diff --git a/.prettierrc b/.prettierrc index b28a7fcb..6dc5529b 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,7 +1,7 @@ { "tabWidth": 4, "singleQuote": true, - "printWidth": 120, + "printWidth": 100, "trailingComma": "none", "quoteProps": "preserve" } diff --git a/Gruntfile.js b/Gruntfile.js index 04c8b948..ec7ac606 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -22,7 +22,8 @@ module.exports = function(grunt) { const year = date.getFullYear(); const minElectronVersionForUpdate = '4.0.1'; const zipCommentPlaceholderPart = 'zip_comment_placeholder_that_will_be_replaced_with_hash'; - const zipCommentPlaceholder = zipCommentPlaceholderPart + '.'.repeat(512 - zipCommentPlaceholderPart.length); + const zipCommentPlaceholder = + zipCommentPlaceholderPart + '.'.repeat(512 - zipCommentPlaceholderPart.length); const electronVersion = pkg.dependencies.electron.replace(/^\D/, ''); grunt.initConfig({ @@ -124,7 +125,8 @@ module.exports = function(grunt) { }, 'desktop-darwin-installer': { cwd: 'package/osx/KeeWeb Installer.app', - dest: 'tmp/desktop/KeeWeb-darwin-x64/KeeWeb.app/Contents/Installer/KeeWeb Installer.app', + dest: + 'tmp/desktop/KeeWeb-darwin-x64/KeeWeb.app/Contents/Installer/KeeWeb Installer.app', src: '**', expand: true, nonull: true, @@ -167,18 +169,30 @@ module.exports = function(grunt) { manifest: { options: { replacements: [ - { pattern: '# YYYY-MM-DD:v0.0.0', replacement: '# ' + dt + ':v' + pkg.version }, - { pattern: '# updmin:v0.0.0', replacement: '# updmin:v' + minElectronVersionForUpdate } + { + pattern: '# YYYY-MM-DD:v0.0.0', + replacement: '# ' + dt + ':v' + pkg.version + }, + { + pattern: '# updmin:v0.0.0', + replacement: '# updmin:v' + minElectronVersionForUpdate + } ] }, files: { 'dist/manifest.appcache': 'app/manifest.appcache' } }, 'manifest-html': { - options: { replacements: [{ pattern: ' - - - KeeWeb - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + KeeWeb + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/manifest/manifest.json b/app/manifest/manifest.json index 1e1460f4..693835d3 100644 --- a/app/manifest/manifest.json +++ b/app/manifest/manifest.json @@ -1,21 +1,21 @@ { - "name": "KeeWeb", - "short_name": "KeeWeb", - "description": "Free cross-platform password manager compatible with KeePass", - "display": "standalone", - "orientation": "any", - "theme_color": "#6386ec", - "background_color": "#6386ec", - "icons": [ - { - "src": "icons/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "icons/android-chrome-512x512.png", - "sizes": "512x512", - "type": "image/png" - } - ] + "name": "KeeWeb", + "short_name": "KeeWeb", + "description": "Free cross-platform password manager compatible with KeePass", + "display": "standalone", + "orientation": "any", + "theme_color": "#6386ec", + "background_color": "#6386ec", + "icons": [ + { + "src": "icons/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ] } diff --git a/app/scripts/app.js b/app/scripts/app.js index e9de38ab..2d6fddd5 100644 --- a/app/scripts/app.js +++ b/app/scripts/app.js @@ -116,7 +116,8 @@ ready(() => { function showApp() { return Promise.resolve().then(() => { - const skipHttpsWarning = localStorage.skipHttpsWarning || appModel.settings.get('skipHttpsWarning'); + const skipHttpsWarning = + localStorage.skipHttpsWarning || appModel.settings.get('skipHttpsWarning'); const protocolIsInsecure = ['https:', 'file:', 'app:'].indexOf(location.protocol) < 0; const hostIsInsecure = location.hostname !== 'localhost'; if (protocolIsInsecure && hostIsInsecure && !skipHttpsWarning) { diff --git a/app/scripts/auto-type/auto-type-filter.js b/app/scripts/auto-type/auto-type-filter.js index bc155164..efd8a817 100644 --- a/app/scripts/auto-type/auto-type-filter.js +++ b/app/scripts/auto-type/auto-type-filter.js @@ -21,7 +21,9 @@ AutoTypeFilter.prototype.getEntries = function() { if (!this.ignoreWindowInfo) { entries = entries.filter(e => e[1]); } - entries = entries.sort((x, y) => (x[1] === y[1] ? x[0].title.localeCompare(y[0].title) : y[1] - x[1])); + entries = entries.sort((x, y) => + x[1] === y[1] ? x[0].title.localeCompare(y[0].title) : y[1] - x[1] + ); entries = entries.map(p => p[0]); return new EntryCollection(entries, { comparator: 'none' }); }; diff --git a/app/scripts/auto-type/auto-type-runner.js b/app/scripts/auto-type/auto-type-runner.js index 17a68e1a..e88099c5 100644 --- a/app/scripts/auto-type/auto-type-runner.js +++ b/app/scripts/auto-type/auto-type-runner.js @@ -299,7 +299,14 @@ AutoTypeRunner.prototype.getEntryGroupName = function() { AutoTypeRunner.prototype.dt = function(part) { switch (part) { case 'simple': - return this.dt('Y') + this.dt('M') + this.dt('D') + this.dt('h') + this.dt('m') + this.dt('s'); + return ( + this.dt('Y') + + this.dt('M') + + this.dt('D') + + this.dt('h') + + this.dt('m') + + this.dt('s') + ); case 'Y': return this.now.getFullYear().toString(); case 'M': @@ -320,7 +327,14 @@ AutoTypeRunner.prototype.dt = function(part) { AutoTypeRunner.prototype.udt = function(part) { switch (part) { case 'simple': - return this.udt('Y') + this.udt('M') + this.udt('D') + this.udt('h') + this.udt('m') + this.udt('s'); + return ( + this.udt('Y') + + this.udt('M') + + this.udt('D') + + this.udt('h') + + this.udt('m') + + this.udt('s') + ); case 'Y': return this.now.getUTCFullYear().toString(); case 'M': diff --git a/app/scripts/comp/app-rights-checker.js b/app/scripts/comp/app-rights-checker.js index f00cf8fd..fae77b0d 100644 --- a/app/scripts/comp/app-rights-checker.js +++ b/app/scripts/comp/app-rights-checker.js @@ -42,7 +42,10 @@ const AppRightsChecker = { '
' + Locale.appRightsAlertBody2 + `:
${command}
`, - buttons: [{ result: 'skip', title: Locale.alertDoNotAsk, error: true }, Alerts.buttons.ok], + buttons: [ + { result: 'skip', title: Locale.alertDoNotAsk, error: true }, + Alerts.buttons.ok + ], success: result => { if (result === 'skip') { this.dontAskAnymore(); diff --git a/app/scripts/comp/feature-tester.js b/app/scripts/comp/feature-tester.js index 2ac830ad..37f4d236 100644 --- a/app/scripts/comp/feature-tester.js +++ b/app/scripts/comp/feature-tester.js @@ -11,7 +11,9 @@ const FeatureTester = { checkWebAssembly() { try { - const module = new global.WebAssembly.Module(Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00)); + const module = new global.WebAssembly.Module( + Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00) + ); return new global.WebAssembly.Instance(module) instanceof global.WebAssembly.Instance; } catch (e) { throw 'WebAssembly is not supported'; @@ -29,7 +31,10 @@ const FeatureTester = { .importKey(kdbxweb.ByteUtils.hexToBytes(key)) .then(() => { return aesCbc - .encrypt(kdbxweb.ByteUtils.hexToBytes(data), kdbxweb.ByteUtils.hexToBytes(iv)) + .encrypt( + kdbxweb.ByteUtils.hexToBytes(data), + kdbxweb.ByteUtils.hexToBytes(iv) + ) .then(res => { if (kdbxweb.ByteUtils.bytesToHex(res) !== exp) { throw 'AES is not working properly'; diff --git a/app/scripts/comp/generator-presets.js b/app/scripts/comp/generator-presets.js index e6027531..28535a75 100644 --- a/app/scripts/comp/generator-presets.js +++ b/app/scripts/comp/generator-presets.js @@ -3,13 +3,26 @@ const Locale = require('../util/locale'); const GeneratorPresets = { get defaultPreset() { - return { name: 'Default', title: Locale.genPresetDefault, length: 16, upper: true, lower: true, digits: true }; + return { + name: 'Default', + title: Locale.genPresetDefault, + length: 16, + upper: true, + lower: true, + digits: true + }; }, get builtIn() { return [ this.defaultPreset, - { name: 'Pronounceable', title: Locale.genPresetPronounceable, length: 10, lower: true, upper: true }, + { + name: 'Pronounceable', + title: Locale.genPresetPronounceable, + length: 10, + lower: true, + upper: true + }, { name: 'Med', title: Locale.genPresetMed, @@ -21,11 +34,37 @@ const GeneratorPresets = { brackets: true, ambiguous: true }, - { name: 'Long', title: Locale.genPresetLong, length: 32, upper: true, lower: true, digits: true }, + { + name: 'Long', + title: Locale.genPresetLong, + length: 32, + upper: true, + lower: true, + digits: true + }, { name: 'Pin4', title: Locale.genPresetPin4, length: 4, digits: true }, - { name: 'Mac', title: Locale.genPresetMac, length: 17, upper: true, digits: true, special: true }, - { name: 'Hash128', title: Locale.genPresetHash128, length: 32, lower: true, digits: true }, - { name: 'Hash256', title: Locale.genPresetHash256, length: 64, lower: true, digits: true } + { + name: 'Mac', + title: Locale.genPresetMac, + length: 17, + upper: true, + digits: true, + special: true + }, + { + name: 'Hash128', + title: Locale.genPresetHash128, + length: 32, + lower: true, + digits: true + }, + { + name: 'Hash256', + title: Locale.genPresetHash256, + length: 64, + lower: true, + digits: true + } ]; }, diff --git a/app/scripts/comp/key-handler.js b/app/scripts/comp/key-handler.js index 2cbb75ff..991f8aec 100644 --- a/app/scripts/comp/key-handler.js +++ b/app/scripts/comp/key-handler.js @@ -16,7 +16,13 @@ const KeyHandler = { $(document).bind('keydown', this.keydown.bind(this)); this.shortcuts[Keys.DOM_VK_A] = [ - { handler: this.handleAKey, thisArg: this, shortcut: this.SHORTCUT_ACTION, modal: true, noPrevent: true } + { + handler: this.handleAKey, + thisArg: this, + shortcut: this.SHORTCUT_ACTION, + modal: true, + noPrevent: true + } ]; }, onKey: function(key, handler, thisArg, shortcut, modal, noPrevent) { @@ -107,7 +113,10 @@ const KeyHandler = { IdleTracker.regUserAction(); }, handleAKey: function(e) { - if (e.target.tagName.toLowerCase() === 'input' && ['password', 'text'].indexOf(e.target.type) >= 0) { + if ( + e.target.tagName.toLowerCase() === 'input' && + ['password', 'text'].indexOf(e.target.type) >= 0 + ) { e.stopImmediatePropagation(); } else { e.preventDefault(); diff --git a/app/scripts/comp/launcher-cordova.js b/app/scripts/comp/launcher-cordova.js index d9937df9..41a017e5 100644 --- a/app/scripts/comp/launcher-cordova.js +++ b/app/scripts/comp/launcher-cordova.js @@ -92,7 +92,9 @@ const Launcher = { reader.onerror = callback; reader.onloadend = () => { const contents = new Uint8Array(reader.result); - callback(encoding ? String.fromCharCode.apply(null, contents) : contents); + callback( + encoding ? String.fromCharCode.apply(null, contents) : contents + ); }; reader.readAsArrayBuffer(file); }, diff --git a/app/scripts/comp/otp-qr-reader.js b/app/scripts/comp/otp-qr-reader.js index 8088abc1..c3cbcd42 100644 --- a/app/scripts/comp/otp-qr-reader.js +++ b/app/scripts/comp/otp-qr-reader.js @@ -16,7 +16,10 @@ const OtpQrReader = { read: function() { let screenshotKey = FeatureDetector.screenshotToClipboardShortcut(); if (screenshotKey) { - screenshotKey = Locale.detSetupOtpAlertBodyWith.replace('{}', '' + screenshotKey + ''); + screenshotKey = Locale.detSetupOtpAlertBodyWith.replace( + '{}', + '' + screenshotKey + '' + ); } const pasteKey = FeatureDetector.isMobile ? '' @@ -95,7 +98,10 @@ const OtpQrReader = { }, pasteEvent: function(e) { - const item = _.find(e.clipboardData.items, item => item.kind === 'file' && item.type.indexOf('image') !== -1); + const item = _.find( + e.clipboardData.items, + item => item.kind === 'file' && item.type.indexOf('image') !== -1 + ); if (!item) { logger.debug('Paste without file'); return; @@ -135,7 +141,10 @@ const OtpQrReader = { Alerts.error({ header: Locale.detOtpQrWrong, body: - Locale.detOtpQrWrongBody + '' + Locale.detOtpQrWrongBody + + '' }); } } catch (e) { diff --git a/app/scripts/comp/popup-notifier.js b/app/scripts/comp/popup-notifier.js index b9b6a243..a2e33615 100644 --- a/app/scripts/comp/popup-notifier.js +++ b/app/scripts/comp/popup-notifier.js @@ -85,12 +85,22 @@ const PopupNotifier = { win.close(); win = null; }); - win.webContents.on('did-fail-load', (e, errorCode, errorDescription, validatedUrl, isMainFrame) => { - this.logger.debug('did-fail-load', e, errorCode, errorDescription, validatedUrl, isMainFrame); - this.deferCheckClosed(win); - win.close(); - win = null; - }); + win.webContents.on( + 'did-fail-load', + (e, errorCode, errorDescription, validatedUrl, isMainFrame) => { + this.logger.debug( + 'did-fail-load', + e, + errorCode, + errorDescription, + validatedUrl, + isMainFrame + ); + this.deferCheckClosed(win); + win.close(); + win = null; + } + ); win.once('page-title-updated', () => { setTimeout(() => { if (win) { @@ -100,7 +110,10 @@ const PopupNotifier = { }, Timeouts.PopupWaitTime); }); win.on('closed', () => { - setTimeout(PopupNotifier.triggerClosed.bind(PopupNotifier, win), Timeouts.CheckWindowClosed); + setTimeout( + PopupNotifier.triggerClosed.bind(PopupNotifier, win), + Timeouts.CheckWindowClosed + ); win = null; }); win.loadURL(url); @@ -109,7 +122,10 @@ const PopupNotifier = { }, isOwnUrl(url) { - return url.lastIndexOf(Links.WebApp, 0) === 0 || url.lastIndexOf(location.origin + location.pathname, 0) === 0; + return ( + url.lastIndexOf(Links.WebApp, 0) === 0 || + url.lastIndexOf(location.origin + location.pathname, 0) === 0 + ); }, processReturnToApp: function(url) { @@ -127,7 +143,10 @@ const PopupNotifier = { checkClosed: function(win) { if (win.closed) { - setTimeout(PopupNotifier.triggerClosed.bind(PopupNotifier, win), Timeouts.CheckWindowClosed); + setTimeout( + PopupNotifier.triggerClosed.bind(PopupNotifier, win), + Timeouts.CheckWindowClosed + ); } else { PopupNotifier.deferCheckClosed(win); } diff --git a/app/scripts/comp/single-instance-checker.js b/app/scripts/comp/single-instance-checker.js index 78c5bcca..93fb40a4 100644 --- a/app/scripts/comp/single-instance-checker.js +++ b/app/scripts/comp/single-instance-checker.js @@ -20,7 +20,10 @@ const SingleInstanceChecker = { return; } if (e.key === LocalStorageKeyName && e.newValue !== instanceKey) { - SingleInstanceChecker.setKey(LocalStorageResponseKeyName, instanceKey + Math.random().toString()); + SingleInstanceChecker.setKey( + LocalStorageResponseKeyName, + instanceKey + Math.random().toString() + ); } else if (e.key === LocalStorageResponseKeyName && e.newValue.indexOf(instanceKey) < 0) { window.removeEventListener('storage', SingleInstanceChecker.storageChanged); Backbone.trigger('second-instance'); diff --git a/app/scripts/comp/updater.js b/app/scripts/comp/updater.js index 7541e39e..fc416d03 100644 --- a/app/scripts/comp/updater.js +++ b/app/scripts/comp/updater.js @@ -41,7 +41,10 @@ const Updater = { init: function() { this.scheduleNextCheck(); if (!Launcher && window.applicationCache) { - window.applicationCache.addEventListener('updateready', this.checkAppCacheUpdateReady.bind(this)); + window.applicationCache.addEventListener( + 'updateready', + this.checkAppCacheUpdateReady.bind(this) + ); this.checkAppCacheUpdateReady(); } }, @@ -75,7 +78,9 @@ const Updater = { // additional protection from broken program logic, to ensure that auto-checks are not performed more than once an hour const diffMs = new Date() - this.updateCheckDate; if (isNaN(diffMs) || diffMs < 1000 * 60 * 60) { - logger.error('Prevented update check; last check was performed at ' + this.updateCheckDate); + logger.error( + 'Prevented update check; last check was performed at ' + this.updateCheckDate + ); this.scheduleNextCheck(); return; } @@ -91,7 +96,11 @@ const Updater = { logger.info('Update check: ' + (match ? match[0] : 'unknown')); if (!match) { const errMsg = 'No version info found'; - UpdateModel.instance.set({ status: 'error', lastCheckDate: dt, lastCheckError: errMsg }); + UpdateModel.instance.set({ + status: 'error', + lastCheckDate: dt, + lastCheckError: errMsg + }); UpdateModel.instance.save(); this.scheduleNextCheck(); return; @@ -121,7 +130,12 @@ const Updater = { } if (!startedByUser && this.getAutoUpdateType() === 'install') { this.update(startedByUser); - } else if (SemVer.compareVersions(UpdateModel.instance.get('lastVersion'), RuntimeInfo.version) > 0) { + } else if ( + SemVer.compareVersions( + UpdateModel.instance.get('lastVersion'), + RuntimeInfo.version + ) > 0 + ) { UpdateModel.instance.set('updateStatus', 'found'); } }, @@ -172,7 +186,10 @@ const Updater = { this.extractAppUpdate(filePath, err => { if (err) { logger.error('Error extracting update', err); - UpdateModel.instance.set({ updateStatus: 'error', updateError: 'Error extracting update' }); + UpdateModel.instance.set({ + updateStatus: 'error', + updateError: 'Error extracting update' + }); } else { UpdateModel.instance.set({ updateStatus: 'ready', updateError: null }); if (!startedByUser) { @@ -186,7 +203,10 @@ const Updater = { }, error: function(e) { logger.error('Error downloading update', e); - UpdateModel.instance.set({ updateStatus: 'error', updateError: 'Error downloading update' }); + UpdateModel.instance.set({ + updateStatus: 'error', + updateError: 'Error downloading update' + }); } }); }, diff --git a/app/scripts/locales/base.json b/app/scripts/locales/base.json index 23dd1842..1093f4fa 100644 --- a/app/scripts/locales/base.json +++ b/app/scripts/locales/base.json @@ -13,7 +13,20 @@ "November", "December" ], - "monthsShort": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], + "monthsShort": [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" + ], "weekdays": ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], "weekdaysShort": ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], diff --git a/app/scripts/locales/de-DE.json b/app/scripts/locales/de-DE.json index 6b148477..e688d517 100644 --- a/app/scripts/locales/de-DE.json +++ b/app/scripts/locales/de-DE.json @@ -1,567 +1,551 @@ { - "months": [ - "Januar", - "Februar", - "März", - "April", - "Mai", - "Juni", - "Juli", - "August", - "September", - "Oktober", - "November", - "Dezember" - ], - "monthsShort": [ - "Jan", - "Feb", - "Mär", - "Apr", - "Mai", - "Jun", - "Jul", - "Aug", - "Sep", - "Okt", - "Nov", - "Dez" - ], - "weekdays": [ - "Sonntag", - "Montag", - "Dienstag", - "Mittwoch", - "Donnerstag", - "Freitag", - "Samstag" - ], - "weekdaysShort": [ - "So", - "Mo", - "Di", - "Mi", - "Do", - "Fr", - "Sa" - ], - "retToApp": "zurück zur App", - "name": "Name", - "icon": "Icon", - "title": "Titel", - "password": "Passwort", - "user": "Benutzer", - "website": "Website", - "tags": "Tags", - "notes": "Notizen", - "entry": "Eintrag", - "group": "Gruppe", - "noTitle": "Keinen Titel", - "or": "oder", - "history": "Verlauf", - "template": "Template", - "notImplemented": "Nicht implementiert", - "saveChanges": "Änderungen speichern", - "discardChanges": "Änderungen verwerfen", - "advanced": "Erweiterte Einstellungen", - "shortcuts": "Tastenkürzel", - "help": "Hilfe", - "settings": "Einstellungen", - "plugins": "Plugins", - "cache": "Cache", - "file": "Datei", - "webdav": "WebDAV", - "dropbox": "Dropbox", - "gdrive": "Google Drive", - "onedrive": "OneDrive", - "menuAllItems": "Alle Einträge", - "menuColors": "Farben", - "menuTrash": "Papierkorb", - "menuSetGeneral": "Allgemein", - "menuSetAbout": "Info", - "menuAlertNoTags": "Keine Tags", - "menuAlertNoTagsBody": "Neue Tags können durch das Bearbeiten von Einträgen (Abschnitt “Tags”) hinzugefügt werden.", - "menuEmptyTrash": "Leere Papierkorb", - "menuEmptyTrashAlert": "Papierkorb leeren?", - "menuEmptyTrashAlertBody": "Die Einträge können nicht wiederhergestellt werden", - "menuItemCollapsed": "Doppelklick zum Aufklappen", - "alertYes": "Ja", - "alertNo": "Nein", - "alertOk": "OK", - "alertCancel": "Abbrechen", - "alertSignIn": "Anmelden", - "alertCopy": "Kopieren", - "alertClose": "Schließen", - "alertDoNotAsk": "Nicht mehr nachfragen", - "appBeta": "ACHTUNG: Beta-Version, nur für Vorschau-Zwecke", - "footerOpen": "Öffnen / Neu", - "footerSyncError": "Synchronisierungsfehler", - "footerTitleGen": "Generieren", - "footerTitleLock": "Sperren", - "genLen": "Länge", - "genNewPass": "Neues Passwort", - "genPresetDefault": "Standardeinstellung", - "genPresetDerived": "unverändert", - "genPresetPronounceable": "aussprechbar", - "genPresetMed": "durchschnittlich lang", - "genPresetLong": "lang", - "genPresetPin4": "4-stellige PIN", - "genPresetMac": "MAC-Adresse", - "genPresetHash128": "128-Bit Hash", - "genPresetHash256": "256-Bit Hash", - "grpTitle": "Gruppe", - "grpSearch": "Suche für Einträge in dieser Gruppe aktivieren", - "grpAutoType": "Auto-Type aktivieren", - "grpAutoTypeSeq": "Auto-Type Abfolge", - "grpAutoTypeSeqDefault": "Standard Auto-Type Abfolge verwenden", - "grpTrash": "Gruppe und alle enthaltenen Einträge löschen", - "tagTitle": "Tag", - "tagTrash": "Diesen Tag von allen Einträgen entfernen", - "tagRename": "Umbenennen", - "tagTrashQuestion": "Diesen Tag von allen Einträgen entfernen?", - "tagTrashQuestionBody": "Der Tag wird von allen Einträgen entfernt. Diese Aktion kann nicht rückgängig gemacht werden.", - "tagExists": "Tag existiert bereits", - "tagExistsBody": "Ein Tag mit diesem Namen ist bereits vorhanden. Bitte wählen Sie einen anderen Namen.", - "tagBadName": "Ungültiger Name", - "tagBadNameBody": "Der Tag-Name darf nicht die folgenden Zeichen enthalten: {}. Bitte entfernen Sie sie.", - "genPsTitle": "Generator-Voreinstellungen", - "genPsCreate": "Neue Voreinstellung", - "genPsDelete": "Voreinstellung löschen", - "genPsNew": "Voreinstellung", - "genPsEnabled": "In Voreinstellungs-Liste anzeigen", - "genPsDefault": "Standardmäßig ausgewählt", - "genPsDefaultLength": "Standardlänge", - "genPsUpper": "Lateinische Großbuchstaben", - "genPsLower": "Lateinische Kleinbuchstaben", - "genPsDigits": "Ziffern", - "genPsSpecial": "Sonderzeichen", - "genPsBrackets": "Klammern", - "genPsHigh": "Erweiterte ASCII-Zeichen", - "genPsAmbiguous": "Ähnliche Zeichen", - "genPsInclude": "Zusätzliche Symbole angeben", - "genPsExample": "Beispiel für ein generiertes Passwort", - "keyChangeTitleRemote": "Hauptschlüssel geändert", - "keyChangeMessageRemote": "Der Hauptschlüssel für diese Datenbank wurde geändert. Bitte geben Sie das neue Passwort ein.", - "keyChangeTitleExpired": "Hauptschlüssel abgelaufen", - "keyChangeMessageExpired": "Der Hauptschlüssel für diese Datenkbank ist abgelaufen. Bitte geben Sie einen neuen Schlüssel ein.", - "keyChangeRepeatPassword": "Passwort (Wiederholung)", - "keyEnter": "Enter", - "iconFavTitle": "Website-Icon herunterladen und verwenden", - "iconSelCustom": "Eigenes Icon auswählen", - "listEmptyTitle": "Leer", - "listEmptyAdd": "Zum Anlegen neuer Einträge den obigen {} Button benutzen", - "listGroup": "Gruppe", - "listNoWebsite": "keine Website", - "listNoUser": "kein Benutzer", - "listNoAttachments": "keine Anhänge", - "listAddTemplateHeader": "Templates", - "listAddTemplateBody1": "Templates ermöglichen es, Einträge mit nur einem Klick zu erstellen. Fügen Sie etwas zum Template-Eintrag hinzu und klicken Sie auf {}, um dieses Template zu benutzen.", - "listAddTemplateBody2": "Sie können Ihre Templates in der Gruppe “{}” finden.", - "searchAddNew": "Hinzufügen", - "searchSort": "Sortierung", - "searchCreated": "Erstelldatum", - "searchUpdated": "Änderungsdatum", - "searchAttachments": "Anhänge", - "searchAZ": "A {} Z", - "searchZA": "Z {} A", - "searchON": "Alt {} Neu", - "searchNO": "Neu {} Alt", - "searchShiftClickOr": "Umschalt-Klick oder", - "searchAdvTitle": "Erweiterte Suche", - "searchSearchIn": "Durchsuchen", - "searchOther": "Andere Felder", - "searchProtect": "Geschützte Felder", - "searchOptions": "Suchoptionen", - "searchCase": "Groß/klein", - "searchRegex": "RegEx", - "openOpen": "Öffnen", - "openNew": "Neu", - "openMore": "Mehr", - "openDemo": "Demo", - "openXml": "XML importieren", - "openCaps": "Feststelltaste ist aktiviert", - "openClickToOpen": "Hier klicken, um eine Datei zu öffnen", - "openKeyFile": "Schlüsseldatei", - "openKeyFileDropbox": "(von Dropbox)", - "openDropHere": "Dateien hier ablegen", - "openFailedRead": "Konnte die Datei nicht lesen", - "openNothingFound": "Nichts gefunden", - "openNothingFoundBody": "Es konnten keine kompatiblen Dateien gefunden werden.", - "openSelectFile": "Datei auswählen", - "openSelectFileBody": "Wählen Sie eine Datei zum Öffnen aus", - "openPassFor": "Passwort für", - "openRemoveLastQuestion": "Lokale Datei löschen?", - "openRemoveLastQuestionBody": "Die Datei, die Sie löschen möchten, ist nur innerhalb der App gespeichert. Möchten Sie sie unwiederbringlich löschen?", - "openRemoveLastQuestionModBody": "Die Datei, die Sie löschen möchten, beinhaltet lokale Änderungen. Möchten Sie diese Änderungen verwerfen und die Datei löschen?", - "openLocalFile": "Lokale Datei", - "openLocalFileBody": "Sie sind dabei, eine Datei zu öffnen, die innerhalb der App gespeichert wird. Wenn Sie an dieser Datei Änderungen vornehmen, werden diese nicht im Dateisystem gespeichert. Um die Datei mit den Änderungen zu erhalten, können Sie sie aus den Einstellungen heraus exportieren.", - "openLocalFileDontShow": "Nicht wieder anzeigen", - "openWrongFile": "Inkompatibles Dateiformat", - "openWrongFileBody": "Dieses Dateiformat ist nicht kompatibel. Die App unterstützt KeePass-Datenbanken im kdbx-Format.", - "openKdbFileBody": "Sie versuchen, eine alte KeePass-Datenbank im kdb-Format zu öffnen. Diese App unterstützt nur das neuere kdbx-Format. Bitte verwenden Sie KeePass v2, um diese Datei umzuwandeln.", - "openConfigHeader": "{} Einstellungen", - "openUrl": "URL", - "openUrlDesc": "https://server/pfad/datei.kdbx, oder einfach datei.kdbx", - "openUser": "Benutzername", - "openUserDesc": "WebDAV Server Benutzername (falls erforderlich)", - "openUserPlaceholder": "kein Benutzername", - "openPass": "Passwort", - "openPassDesc": "WebDAV Server-Passwort (nicht das KDBX-Datei-Passwort)", - "openPassPlaceholder": "kein Passwort", - "openConfigError": "Fehler: {}", - "openConfigErrorNotFound": "Datei nicht gefunden", - "openError": "Fehler", - "openErrorDescription": "Beim Öffnen der Datei ist ein Fehler aufgetreten", - "openErrorFileNotFound": "Datei nicht gefunden", - "openListErrorBody": "Dateien konnten nicht geladen werden", - "openShowAllFiles": "Alle Dateien anzeigen", - "detAttDownload": "Umschalttaste + Klick auf den Anhang-Button zum Herunterladen oder", - "detAttDelToRemove": "Backspace zum entfernen", - "detEmpty": "Ihre Passwörter werden hier angezeigt", - "detGroupRestore": "Ziehen Sie diese Gruppe in eine andere Gruppe als den Papierkorb, um sie wiederherzustellen.", - "detHistoryClickPoint": "Klicken Sie auf einen der Punkte im Verlauf um den entsprechenden Zustand des Eintrags anzuzeigen", - "detHistoryReturn": "zurück zum Eintrag", - "detHistoryRevert": "Auf diese Version zurücksetzen", - "detHistoryDel": "Version löschen", - "detHistoryEmpty": "leer", - "detHistoryModified": "geändert", - "detHistoryRec": "Version", - "detHistoryRecs": "Versionen", - "detHistoryVersion": "Version", - "detHistorySaved": "Gespeichert", - "detHistoryNoTitle": "keine Bezeichnung", - "detHistoryCurState": "aktuelle Version", - "detHistoryCurUnsavedState": "aktuelle ungespeicherte Version", - "detHistoryRevertAlert": "Zu diesem Stand zurückkehren?", - "detHistoryRevertAlertBody": "Ihr aktueller Stand wird in der Historie gesichert.", - "detHistoryDeleteAlert": "Diesen Stand löschen?", - "detHistoryDeleteAlertBody": "Sie können ihn nicht wiederherstellen.", - "detHistoryDiscardChangesAlert": "Änderung verwerfen?", - "detHistoryDiscardChangesAlertBody": "Ungesicherte Änderungen sind unwiderruflich verloren.", - "detBackToList": "zurück zur Liste", - "detSetIconColor": "Farbe ändern", - "detSetIcon": "Icon ändern", - "detDropAttachments": "Anhänge hier ablegen", - "detDelEntry": "Löschen", - "detDelEntryPerm": "Dauerhaft löschen", - "detExpires": "Ablaufdatum", - "detExpired": "abgelaufen", - "detGroup": "Gruppe", - "detCreated": "Erstellt", - "detUpdated": "Geändert", - "detNetField": "Neues Feld", - "detAttachments": "Anhänge", - "detDelFromTrash": "Aus dem Papierkorb löschen?", - "detDelFromTrashBody": "Es wird keine Möglichkeit zur Wiederherstellung geben.", - "detDelFromTrashBodyHint": "Um alle Dateien aus dem Papierkorb zu entfernen, klicken Sie auf das “Papierkorb” leeren-Icon im Papierkorb-Menüpunkt.", - "detFieldCopied": "Kopiert", - "detFieldCopiedTime": "Kopiert für {} Sekunden", - "detCopyHint": "Sie können den Inhalt eines Feldes kopieren, indem Sie auf seine Beschriftung klicken", - "detMore": "mehr", - "detClickToAddField": "Klicken, um ein neues Feld hinzuzufügen", - "detMenuAddNewField": "Neues Feld hinzufügen", - "detMenuShowEmpty": "Leere Felder anzeigen", - "detMenuHideEmpty": "Leere Felder ausblenden", - "detMenuAddField": "{} hinzufügen", - "detMenuCopyPassword": "Passwort kopieren", - "detMenuCopyUser": "Benutzer kopieren", - "detSetupOtp": "Einweg-Passwörter", - "detClone": "Kopie erstellen", - "detClonedName": "Kopie", - "detAutoType": "Auto-Type", - "detAutoTypeSettings": "Auto-Type Einstellungen", - "detAutoTypeEnabled": "Auto-Type für diesen Eintrag aktivieren", - "detAutoTypeSequence": "Tastensequenz", - "detAutoTypeInput": "Eingabe", - "detAutoTypeShortcutsDesc": "{} oder {} während die App inaktiv ist", - "detAutoTypeObfuscation": "Echte Tastenanschläge mit zufälligen mischen", - "detAutoTypeWindow": "Fenster", - "detAutoTypeInputWindow": "Fenster-Titel", - "detSetupOtpAlert": "QR-Code einscannen", - "detSetupOtpAlertBody": "Bitte kopieren Sie den QR-Code, der auf der Autorisierungsseite angezeigt wird.", - "detSetupOtpAlertBody1": "1. Gehen Sie zur Autorisierungsseite", - "detSetupOtpAlertBody2": "2. Erstellen Sie einen Screenshot des QR-Codes {}", - "detSetupOtpAlertBody3": "3. Fügen Sie ihn hier ein {}", - "detSetupOtpAlertBody3Mobile": "3. Markieren Sie ihn oder scannen Sie ihn mit Ihrer Kamera mit dem untenstehenden Markieren/Scannen-Button", - "detSetupOtpAlertBody4": "Wenn Sie den Code nicht einscannen können, klicken Sie bitte auf “Code manuell eingeben”", - "detSetupOtpManualButton": "Code manuell eingeben", - "detSetupOtpScanButton": "Markieren/Scannen", - "detSetupOtpAlertBodyWith": "mit {}", - "detOtpImageError": "Fehler beim Einlesen des Bildes", - "detOtpImageErrorBody": "Entschuldigung, wir konnten das Bildformat nicht lesen. Bitte kontaktieren Sie den Hersteller mit Einzelheiten zu diesem Fehler.", - "detOtpImageReading": "QR-Code wird eingelesen...", - "detOtpQrError": "QR-Code Lesefehler", - "detOtpQrErrorBody": "Der QR-Code konnte leider nicht eingelesen werden. Bitte versuchen Sie es noch einmal oder kontaktieren Sie den Hersteller mit Einzelheiten zum aufgetretenen Fehler.", - "detOtpQrWrong": "Fehlerhafter QR-Code", - "detOtpQrWrongBody": "Ihr QR-Code wurde erfolgreich eingelesen, enthält jedoch kein Einweg-Passwort.", - "detLockField": "Dieses Feld sperren, damit der Inhalt nicht durchsuchbar ist und angezeigt wird. Um ihn anzuzeigen, muss er angeklickt werden.", - "detUnlockField": "Dieses Feld entsperren. Dies führt dazu, dass der Inhalt direkt angezeigt wird und durchsuchbar ist.", - "autoTypeEntryFields": "Feld-Platzhalter", - "autoTypeModifiers": "Hilfstasten", - "autoTypeKeys": "Tasten", - "autoTypeLink": "mehr...", - "autoTypeError": "Auto-Type Fehler", - "autoTypeErrorGeneric": "Während der Ausführung von Auto-Type ist ein Fehler aufgetreten: {}", - "autoTypeErrorGlobal": "Um globale Tastenkürzel zu nutzen, wechseln Sie bitte zu der Anwendung, in der Sie das Passwort eingeben wollen.", - "autoTypeErrorNotInstalled": "{} ist nicht installiert", - "autoTypeHeader": "Auto-Type: Auswählen", - "autoTypeMsgNoWindow": "Wir konnten den Titel des aktiven Fensters leider nicht auslesen. Fangen Sie einfach an zu tippen, um nach dem richtigen Eintrag zu suchen.", - "autoTypeMsgMatchedByWindow": "Passwort auswählen für {}", - "autoTypeNoMatches": "keine passenden Einträge", - "autoTypeSelectionHint": "Die Auto-Type Abfolge eintippen", - "autoTypeSelectionHintAction": "Nur das Passwort eintippen", - "autoTypeSelectionHintOpt": "Nur den Benutzernamen eintippen", - "appSecWarn": "Unsichere Verbindung!", - "appSecWarnBody1": "Sie haben diese App über eine ungesicherte Verbindung geöffnet, Sie könnten beobachtet und Ihre Passwörter könnten gestohlen werden. Wir empfehlen Ihnen dringend, die App hier nicht weiter zu nutzen, wenn Sie nicht genau wissen, was Sie tun.", - "appSecWarnBody2": "Ihre Datenbank ist zwar verschlüsselt, aber es kann nicht sichergestellt werden, dass die App nicht auf dem Web vom Server zu Ihrem Browser manipuliert wurde.", - "appSecWarnBtn": "Ich habe das Risiko verstanden, trotzdem fortfahren", - "appUnsavedWarn": "Ungespeicherte Änderungen!", - "appUnsavedWarnBody": "Sie haben ungespeicherte Änderungen. Wenn Sie die App schließen, gehen diese verloren.", - "appDontExitBtn": "App nicht verlassen", - "appCannotLockAutoInit": "Die App kann nicht gesperrt werden, da automatisches Speichern deaktiviert ist.", - "appCannotLock": "Sie haben ungespeicherte Änderungen, die verloren gehen. Fortfahren?", - "appAutoSave": "Änderungen automatisch speichern", - "appSaveError": "Fehler beim Speichern", - "appSaveErrorBody": "Automatisches Speichern fehlgeschlagen", - "appSaveErrorBodyMul": "Automatisches Speichern fehlgeschlagen:", - "appSettingsError": "Fehler beim Laden der App", - "appSettingsErrorBody": "Es gab einen Fehler beim Laden der App-Einstellungen. Bitte überprüfen Sie die App URL oder kontaktieren Sie Ihren Systemadministrator.", - "appNotSupportedError": "Ihr Browser wird nicht unterstützt.", - "appTabWarn": "Zu viele Tabs", - "appTabWarnBody": "KeeWeb kann nicht in mehreren Browser-Tabs gleichzeitig genutzt werden, bitte schließen Sie diesen Tab.", - "appRightsAlert": "Schreibschutz für KeeWeb wird eingerichtet", - "appRightsAlertBody1": "Ihre KeeWeb-Anwendung ist nicht gegen Änderungen von außen gesichert. Um diesen Schreibschutz einzurichten, benötigt die App Administratorrechte.", - "appRightsAlertBody2": "Sie möchten der App keine Administratorrechte erteilen? Mit folgendem Befehl können Sie den Schreibschutz auch von Hand im Terminal aktivieren", - "setGenTitle": "Allgemeine Einstellungen", - "setGenUpdate": "Update", - "setGenNewVersion": "Eine neue Version der App ist verfügbar und wurde heruntergeladen", - "setGenReleaseNotes": "Versionshinweise anzeigen", - "setGenReloadToUpdate": "Seite aktualisieren, um das Update durchzuführen", - "setGenUpdateManual": "Eine neue Version ist verfügbar. Sie wird nach Updates suchen und sie automatisch installieren, ein automatisches Update von Ihrer Version ist aber nicht möglich.", - "setGenDownloadUpdate": "Update herunterladen", - "setGenUpdateAuto": "Automatisch herunterladen und installieren", - "setGenUpdateCheck": "Auf Updates überprüfen aber nicht installieren", - "setGenNoUpdate": "Nie nach Updates suchen", - "setGenUpdateChecking": "Suche nach Updates", - "setGenCheckUpdate": "Nach Updates suchen", - "setGenErrorChecking": "Updatesuche fehlgeschlagen", - "setGenLastCheckSuccess": "Letzte erfolgreiche Überprüfung am {}", - "setGenLastCheckVer": "Die aktuelle Version war {}", - "setGenCheckedAt": "Überprüft am", - "setGenLatestVer": "Sie haben die aktuellste Version", - "setGenNewVer": "Neue Version {} verfügbar, veröffentlicht am", - "setGenDownloadingUpdate": "Update wird heruntergeladen...", - "setGenExtractingUpdate": "Update wird entpackt...", - "setGenCheckErr": "Beim Herunterladen der neuen Version ist ein Fehler aufgetreten", - "setGenNeverChecked": "Nie nach Updates gesucht", - "setGenRestartToUpdate": "App neu starten, um das Update durchzuführen", - "setGenDownloadAndRestart": "Update herunterladen und App neu starten", - "setGenAppearance": "Oberfläche", - "setGenTheme": "Theme", - "setGenThemeFb": "Flat blue", - "setGenThemeDb": "Dark brown", - "setGenThemeWh": "White", - "setGenThemeTe": "Terminal", - "setGenThemeHc": "High contrast", - "setGenThemeSd": "Solarized dark", - "setGenThemeSl": "Solarized light", - "setGenLocale": "Sprache", - "setGenLocOther": "Andere Sprachen sind als Plugins verfügbar.", - "setGenFontSize": "Schriftgröße", - "setGenFontSizeNormal": "Standard", - "setGenFontSizeLarge": "Groß", - "setGenFontSizeLargest": "Größte", - "setGenTitlebarStyle": "Fenster-Stil (erfordert Neustart des Programms)", - "setGenTitlebarStyleDefault": "Standard", - "setGenTitlebarStyleHidden": "Eigener Titel", - "setGenTitlebarStyleHiddenInset": "Eigener Titel, verschiebbares Fenster", - "setGenShowSubgroups": "Einträge aus allen Untergruppen anzeigen", - "setGenTableView": "Einträge in Tabellenansicht anzeigen", - "setGenColorfulIcons": "Eigene Icons in der Listenansicht farbig anzeigen", - "setGenFunction": "Arbeitsweise", - "setGenAutoSyncOnClose": "Beim Schließen speichern und synchronisieren", - "setGenAutoSyncTimer": "Regelmäßig speichern und synchronisieren", - "setGenAutoSyncTimerOff": "Abgeschaltet", - "setGenAutoSyncTimerInterval": "Alle {} Minuten", - "setGenRememberKeyFiles": "Schlüsseldateien merken", - "setGenNoRememberKeyFiles": "Nicht merken", - "setGenRememberKeyFilesData": "Im internen App-Speicher ablegen", - "setGenRememberKeyFilesPath": "Nur Speicherort der Schlüsseldateien merken", - "setGenLockInactive": "Automatisch sperren, wenn die App inaktiv ist", - "setGenNoAutoLock": "Nicht automatisch sperren", - "setGenLockMinutes": "Nach {} Minuten", - "setGenLockHour": "In einer Stunde", - "setGenLockHours": "Nach {} Stunden", - "setGenLockDay": "Nach einem Tag", - "setGenClearClip": "Zwischenablage nach dem Kopieren leeren", - "setGenNoClear": "Nicht leeren", - "setGenClearSeconds": "In {} Sekunden", - "setGenClearMinute": "Nach einer Minute", - "setGenMinInstead": "App beim Schließen stattdessen minimieren", - "setGenLock": "Automatisch sperren", - "setGenLockMinimize": "Beim Minimieren automatisch sperren", - "setGenLockCopy": "Nach dem Kopieren eines Passworts", - "setGenLockAutoType": "Bei Auto-Type", - "setGenLockOrSleep": "Bei Aktivierung von Bildschirmsperre oder Ruhezustand", - "setGenStorage": "Synchronisierung", - "setGenShowAdvanced": "Erweiterte Einstellungen anzeigen", - "setGenDevTools": "Entwicklerwerkzeuge anzeigen", - "setGenTryBeta": "Beta-Version bis zum Schließen der App ausprobieren", - "setGenTryBetaWarning": "Ungespeicherte Dateien", - "setGenTryBetaWarningBody": "Bitte speichern Sie alle Dateien und Klicken Sie erneut auf diesen Button", - "setGenShowAppLogs": "App-Logs anzeigen", - "setFilePath": "Dateipfad", - "setFileStorage": "Diese Datei wird von {} geladen.", - "setFileIntl": "Diese Datei ist im internen App-Speicher abgelegt", - "setFileLocalHint": "Sie wollen nahtlos mit lokalen Dateien arbeiten?", - "setFileDownloadApp": "Laden Sie die Desktop-App herunter", - "setFileSave": "Speichern", - "setFileSaveTo": "Speichern unter...", - "setFileClose": "Sperren", - "setFileSync": "Synchronisierung", - "setFileSyncVerb": "Synchronisieren", - "setFileSaveToXml": "XML", - "setFileLastSync": "Letzte Synchronisierung", - "setFileLastSyncUnknown": "unbekannt", - "setFileSyncInProgress": "Synchronisierung läuft", - "setFileSyncError": "Synchronisierungsfehler", - "setFilePass": "Master-Passwort", - "setFileConfirmPass": "Master-Passwort bestätigen", - "setFilePassChange": "Um das Passwort zu ändern, geben Sie es im Bestätigungsfeld an", - "setFilePassChanged": "Das Passwort wurde geändert. Lassen Sie das Feld leer, um das bisherige Passwort zu behalten.", - "setFilePassNotMatch": "Die Passwörter stimmen nicht überein, bitte geben Sie sie erneut ein", - "setFileKeyFile": "Schlüsseldatei", - "setFileSelKeyFile": "Schlüsseldatei auswählen", - "setFileNames": "Namen", - "setFileDefUser": "Standard-Benutzername", - "setFileEnableTrash": "Papierkorb aktivieren", - "setFileHistLen": "Anzahl der im Verlauf gespeicherten Versionen", - "setFileHistSize": "Maximalgröße des Verlaufs pro Datei in MB", - "setFileBackups": "Sicherungen", - "setFileBackupEnable": "Diese Datei sichern", - "setFileBackupPath": "Sicherungspfad", - "setFileBackupTime": "Sicherungen anlegen", - "setFileBackupNow": "Jetzt sichern", - "setFileBackupNowWorking": "Sichern...", - "setFileBackupError": "Sicherungsfehler", - "setFileBackupErrorDescription": "Fehler beim Schreiben der Sicherungsdatei", - "setFileBackupErrorIsDir": "Der Sicherungspfad ist ungültig", - "setFileBackupErrorIsDirDescription": "Der Sicherungspfad scheint ein Verzeichnis zu sein. Bitte geben Sie stattdessen einen Dateinamen ein.", - "setFileBackupOnSave": "Immer, wenn die Datei gespeichert wird", - "setFileBackupDaily": "Täglich", - "setFileBackupWeekly": "Wöchentlich", - "setFileBackupMonthly": "Monatlich", - "setFileBackupManually": "Manuell, keine automatische Sicherung", - "setFileRounds": "Verschlüsselungs-Runden", - "setFileKdfParams": "Schlüsselableitungsfunktion", - "setFileKdfParamsIter": "Iterationen", - "setFileKdfParamsMem": "Speicher in KB", - "setFileKdfParamsPar": "Parallelismus", - "setFileKeyChangeForce": "Hinweis zum Ändern des Schlüssels nach (Tagen)", - "setFileUseKeyFile": "Schlüsseldatei benutzen", - "setFileUseGenKeyFile": "Generierte Schlüsseldatei benutzen", - "setFileUseOldKeyFile": "Alte Schlüsseldatei benutzen", - "setFileGenKeyFile": "Neue Schlüsseldatei generieren", - "setFileDontUseKeyFile": "Keine Schlüsseldatei benutzen", - "setFileEmptyPass": "Leeres Passwort", - "setFileEmptyPassBody": "Eine Datenbank ohne Passwort zu speichern macht sie vollkommen ungeschützt. Möchten Sie sie trotzdem speichern?", - "setFileSaveError": "Fehler beim Speichern", - "setFileSaveErrorBody": "Schreibfehler beim Speichern", - "setFileAlreadyExists": "Existiert bereits", - "setFileAlreadyExistsBody": "Die Datei {} existiert bereits. Überschreiben?", - "setFileUnsaved": "Ungespeicherte Änderungen", - "setFileUnsavedBody": "Es gibt ungespeicherte Änderungen in dieser Datei", - "setFileCloseNoSave": "Sperren und Änderungen verwerfen", - "setFileDontClose": "Nicht sperren", - "setShTitle": "Tastenkürzel", - "setShShowAll": "alle Einträge anzeigen", - "setShColors": "mit Farben markierte Einträge anzeigen", - "setShTrash": "Papierkorb öffnen", - "setShFind": "Suchfeld aktivieren; oder: einfach anfangen, den Suchbegriff zu tippen", - "setShClearSearch": "Suchfeld leeren", - "setShCopyPass": "Passwort bzw. ausgewähltes Feld kopieren", - "setShCopyUser": "Benutzername kopieren", - "setShCopyUrl": "Website kopieren", - "setShAutoType": "Auto-Type für ausgewählten Eintrag", - "setShPrev": "zum vorherigen Eintrag gehen", - "setShNext": "zum nächsten Eintrag gehen", - "setShCreateEntry": "Neuer Eintrag", - "setShOpen": "öffnen / neu", - "setShSave": "alle Dateien speichern", - "setShGen": "Passwort generieren", - "setShSet": "App-Einstellungen", - "setShCopyPassGlobal": "Passwort kopieren (wenn die App im Hintergrund ist)", - "setShCopyUserGlobal": "Usernamen kopieren (wenn die App im Hintergrund ist)", - "setShCopyUrlGlobal": "Website kopieren (wenn die App im Hintergrund ist)", - "setShAutoTypeGlobal": "Auto-Type (wenn die App im Hintergrund ist)", - "setShLock": "Datenbank sperren", - "setPlInstallTitle": "Neue Plugins installieren", - "setPlInstallDesc": "KeeWeb-Plugins fügen Funktionen, Themes und Sprachen zu KeeWeb hinzu. Plugins werden mit denselben Rechten ausgeführt wie KeeWeb selbst, sie können auf all Ihre Passwörter zugreifen oder diese ändern. Installieren Sie niemals Plugins, denen Sie nicht vertrauen.", - "setPlInstallLabel": "Plugin-URL", - "setPlInstallBtn": "Installieren", - "setPlInstallBtnProgress": "Installiere", - "setPlUninstallBtn": "Deinstallieren", - "setPlDisableBtn": "Deaktivieren", - "setPlEnableBtn": "Aktivieren", - "setPlUpdateBtn": "Aktualisieren", - "setPlLocaleBtn": "Diese Sprache aktivieren", - "setPlThemeBtn": "Dieses Theme aktivieren", - "setPlJs": "Code", - "setPlCss": "Stile", - "setPlLoc": "Sprache", - "setPlCreatedBy": "Erstellt von {}", - "setPlLoadTime": "geladen in {}", - "setPlLastUpdate": "Letzte Prüfung auf Updates", - "setPlLoadError": "Fehler beim Laden des Plugins", - "setPlGalleryLoading": "Plugins werden geladen, bitte warten Sie einen Moment", - "setPlGalleryLoadError": "Fehler beim Laden der Plugins", - "setPlInstallUrlTitle": "Plugin von einer URL hinzufügen", - "setPlInstallUrlDesc": "Wenn das Plugin nicht in der Galerie verfügbar ist, können Sie es manuell von einer URL installieren", - "setPlOfficial": "Offizielles KeeWeb-Plugin", - "setPlSearch": "Nach Plugins suchen", - "setPlDevelop": "Eigene Plugins entwickeln?", - "setPlDevelopStart": "Hier entlang", - "setPlTranslate": "Oder {}", - "setPlTranslateLink": "übersetzen Sie die App in Ihre Sprache", - "setPlAutoUpdate": "Automatisch aktualisieren", - "setPlLoadGallery": "Plugin-Galerie laden", - "setAboutTitle": "Über", - "setAboutBuilt": "Diese App wurde mit den folgenden Werkzeugen erstellt", - "setAboutLic": "Lizenz", - "setAboutLicComment": "Die App selbst und alle enthaltenen Komponenten, die nicht Public Domain sind, stehen unter der MIT-Lizenz.", - "setAboutFirst": "Dies ist eine Open-Source App, erstellt von {}", - "setAboutSecond": "und lizenziert unter {}.", - "setAboutSource": "Quellcode und Tickets befinden sich auf {}.", - "setHelpFormat": "Dateiformat", - "setHelpFormatBody": "Dies ist eine Portierung der {} App, programmiert mit Webtechnologien. Sie ist kompatibel mit Dateien im KeePass-Format (kdbx). Sie können solche Dateien (Passwort-Datenbanken) entweder mit KeePass oder mit dieser App erstellen. Das Dateiformat ist zu 100% kompatibel und sollte in beiden Anwendungen nutzbar sein.", - "setHelpProblems": "Probleme?", - "setHelpProblems1": "Falls etwas schief geht, {}", - "setHelpProblems2": "oder {}", - "setHelpOpenIssue": "eröffnen Sie bitte ein Ticket auf GitHub (englisch)", - "setHelpContactLink": "kontaktieren Sie einen Entwickler direkt", - "setHelpAppInfo": "App-Informationen", - "setHelpOtherPlatforms": "Andere Plattformen", - "setHelpDesktopApps": "Deskop-Apps", - "setHelpWebApp": "Web-App", - "setHelpUpdates": "Neuigkeiten", - "setHelpTwitter": "Twitter", - "dropboxSetupDesc": "Etwas Konfiguration ist notwendig, um Dropbox in einer selbst gehosteten Web-App zu verwenden. Bitte erstellen Sie eine eigene Dropbox-App und tragen Sie ihren App-Schlüssel unten ein.", - "dropboxAppKey": "Dropbox App-Schlüssel", - "dropboxAppKeyDesc": "Kopieren Sie den Schlüssel aus Ihrer Dropbox-App (Entwicklereinstellungen)", - "dropboxFolder": "App-Ordner", - "dropboxFolderDesc": "Wenn Ihre App mit der gesamten Dropbox verlinkt ist (anstatt mit einem bestimmten Ordner), geben Sie hier den Pfad zum Ordner mit Ihren KDBX-Dateien an.", - "dropboxFolderSettingsDesc": "Wählen Sie einen Ordner in Ihrer Dropbox, in dem die Dateien gespeichert werden (standardmäßig das Stammverzeichnis)", - "dropboxFolderPlaceholder": "Standard-Ordner", - "dropboxLink": "App verlinken mit", - "dropboxLinkApp": "App-Ordner (Apps/KeeWeb)", - "dropboxLinkFull": "Gesamte Dropbox oder beliebiger Ordner", - "dropboxLinkCustom": "Eigene Dropbox-App", - "gdriveSharedWithMe": "Mit mir geteilt", - "webdavSaveMethod": "Speichermethode", - "webdavSaveMove": "Eine temporäre Datei hochladen und verschieben", - "webdavSavePut": "kdbx-Datei mittels PUT überschreiben", - "launcherSave": "Passwort-Datenbank speichern", - "launcherFileFilter": "KeePass-Dateien", - "authPopupRequired": "Pop-Ups blockiert", - "authPopupRequiredBody": "Bitte erlauben Sie Pop-Ups für diese App in Ihrem Browser." -} \ No newline at end of file + "months": [ + "Januar", + "Februar", + "März", + "April", + "Mai", + "Juni", + "Juli", + "August", + "September", + "Oktober", + "November", + "Dezember" + ], + "monthsShort": [ + "Jan", + "Feb", + "Mär", + "Apr", + "Mai", + "Jun", + "Jul", + "Aug", + "Sep", + "Okt", + "Nov", + "Dez" + ], + "weekdays": ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"], + "weekdaysShort": ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"], + "retToApp": "zurück zur App", + "name": "Name", + "icon": "Icon", + "title": "Titel", + "password": "Passwort", + "user": "Benutzer", + "website": "Website", + "tags": "Tags", + "notes": "Notizen", + "entry": "Eintrag", + "group": "Gruppe", + "noTitle": "Keinen Titel", + "or": "oder", + "history": "Verlauf", + "template": "Template", + "notImplemented": "Nicht implementiert", + "saveChanges": "Änderungen speichern", + "discardChanges": "Änderungen verwerfen", + "advanced": "Erweiterte Einstellungen", + "shortcuts": "Tastenkürzel", + "help": "Hilfe", + "settings": "Einstellungen", + "plugins": "Plugins", + "cache": "Cache", + "file": "Datei", + "webdav": "WebDAV", + "dropbox": "Dropbox", + "gdrive": "Google Drive", + "onedrive": "OneDrive", + "menuAllItems": "Alle Einträge", + "menuColors": "Farben", + "menuTrash": "Papierkorb", + "menuSetGeneral": "Allgemein", + "menuSetAbout": "Info", + "menuAlertNoTags": "Keine Tags", + "menuAlertNoTagsBody": "Neue Tags können durch das Bearbeiten von Einträgen (Abschnitt “Tags”) hinzugefügt werden.", + "menuEmptyTrash": "Leere Papierkorb", + "menuEmptyTrashAlert": "Papierkorb leeren?", + "menuEmptyTrashAlertBody": "Die Einträge können nicht wiederhergestellt werden", + "menuItemCollapsed": "Doppelklick zum Aufklappen", + "alertYes": "Ja", + "alertNo": "Nein", + "alertOk": "OK", + "alertCancel": "Abbrechen", + "alertSignIn": "Anmelden", + "alertCopy": "Kopieren", + "alertClose": "Schließen", + "alertDoNotAsk": "Nicht mehr nachfragen", + "appBeta": "ACHTUNG: Beta-Version, nur für Vorschau-Zwecke", + "footerOpen": "Öffnen / Neu", + "footerSyncError": "Synchronisierungsfehler", + "footerTitleGen": "Generieren", + "footerTitleLock": "Sperren", + "genLen": "Länge", + "genNewPass": "Neues Passwort", + "genPresetDefault": "Standardeinstellung", + "genPresetDerived": "unverändert", + "genPresetPronounceable": "aussprechbar", + "genPresetMed": "durchschnittlich lang", + "genPresetLong": "lang", + "genPresetPin4": "4-stellige PIN", + "genPresetMac": "MAC-Adresse", + "genPresetHash128": "128-Bit Hash", + "genPresetHash256": "256-Bit Hash", + "grpTitle": "Gruppe", + "grpSearch": "Suche für Einträge in dieser Gruppe aktivieren", + "grpAutoType": "Auto-Type aktivieren", + "grpAutoTypeSeq": "Auto-Type Abfolge", + "grpAutoTypeSeqDefault": "Standard Auto-Type Abfolge verwenden", + "grpTrash": "Gruppe und alle enthaltenen Einträge löschen", + "tagTitle": "Tag", + "tagTrash": "Diesen Tag von allen Einträgen entfernen", + "tagRename": "Umbenennen", + "tagTrashQuestion": "Diesen Tag von allen Einträgen entfernen?", + "tagTrashQuestionBody": "Der Tag wird von allen Einträgen entfernt. Diese Aktion kann nicht rückgängig gemacht werden.", + "tagExists": "Tag existiert bereits", + "tagExistsBody": "Ein Tag mit diesem Namen ist bereits vorhanden. Bitte wählen Sie einen anderen Namen.", + "tagBadName": "Ungültiger Name", + "tagBadNameBody": "Der Tag-Name darf nicht die folgenden Zeichen enthalten: {}. Bitte entfernen Sie sie.", + "genPsTitle": "Generator-Voreinstellungen", + "genPsCreate": "Neue Voreinstellung", + "genPsDelete": "Voreinstellung löschen", + "genPsNew": "Voreinstellung", + "genPsEnabled": "In Voreinstellungs-Liste anzeigen", + "genPsDefault": "Standardmäßig ausgewählt", + "genPsDefaultLength": "Standardlänge", + "genPsUpper": "Lateinische Großbuchstaben", + "genPsLower": "Lateinische Kleinbuchstaben", + "genPsDigits": "Ziffern", + "genPsSpecial": "Sonderzeichen", + "genPsBrackets": "Klammern", + "genPsHigh": "Erweiterte ASCII-Zeichen", + "genPsAmbiguous": "Ähnliche Zeichen", + "genPsInclude": "Zusätzliche Symbole angeben", + "genPsExample": "Beispiel für ein generiertes Passwort", + "keyChangeTitleRemote": "Hauptschlüssel geändert", + "keyChangeMessageRemote": "Der Hauptschlüssel für diese Datenbank wurde geändert. Bitte geben Sie das neue Passwort ein.", + "keyChangeTitleExpired": "Hauptschlüssel abgelaufen", + "keyChangeMessageExpired": "Der Hauptschlüssel für diese Datenkbank ist abgelaufen. Bitte geben Sie einen neuen Schlüssel ein.", + "keyChangeRepeatPassword": "Passwort (Wiederholung)", + "keyEnter": "Enter", + "iconFavTitle": "Website-Icon herunterladen und verwenden", + "iconSelCustom": "Eigenes Icon auswählen", + "listEmptyTitle": "Leer", + "listEmptyAdd": "Zum Anlegen neuer Einträge den obigen {} Button benutzen", + "listGroup": "Gruppe", + "listNoWebsite": "keine Website", + "listNoUser": "kein Benutzer", + "listNoAttachments": "keine Anhänge", + "listAddTemplateHeader": "Templates", + "listAddTemplateBody1": "Templates ermöglichen es, Einträge mit nur einem Klick zu erstellen. Fügen Sie etwas zum Template-Eintrag hinzu und klicken Sie auf {}, um dieses Template zu benutzen.", + "listAddTemplateBody2": "Sie können Ihre Templates in der Gruppe “{}” finden.", + "searchAddNew": "Hinzufügen", + "searchSort": "Sortierung", + "searchCreated": "Erstelldatum", + "searchUpdated": "Änderungsdatum", + "searchAttachments": "Anhänge", + "searchAZ": "A {} Z", + "searchZA": "Z {} A", + "searchON": "Alt {} Neu", + "searchNO": "Neu {} Alt", + "searchShiftClickOr": "Umschalt-Klick oder", + "searchAdvTitle": "Erweiterte Suche", + "searchSearchIn": "Durchsuchen", + "searchOther": "Andere Felder", + "searchProtect": "Geschützte Felder", + "searchOptions": "Suchoptionen", + "searchCase": "Groß/klein", + "searchRegex": "RegEx", + "openOpen": "Öffnen", + "openNew": "Neu", + "openMore": "Mehr", + "openDemo": "Demo", + "openXml": "XML importieren", + "openCaps": "Feststelltaste ist aktiviert", + "openClickToOpen": "Hier klicken, um eine Datei zu öffnen", + "openKeyFile": "Schlüsseldatei", + "openKeyFileDropbox": "(von Dropbox)", + "openDropHere": "Dateien hier ablegen", + "openFailedRead": "Konnte die Datei nicht lesen", + "openNothingFound": "Nichts gefunden", + "openNothingFoundBody": "Es konnten keine kompatiblen Dateien gefunden werden.", + "openSelectFile": "Datei auswählen", + "openSelectFileBody": "Wählen Sie eine Datei zum Öffnen aus", + "openPassFor": "Passwort für", + "openRemoveLastQuestion": "Lokale Datei löschen?", + "openRemoveLastQuestionBody": "Die Datei, die Sie löschen möchten, ist nur innerhalb der App gespeichert. Möchten Sie sie unwiederbringlich löschen?", + "openRemoveLastQuestionModBody": "Die Datei, die Sie löschen möchten, beinhaltet lokale Änderungen. Möchten Sie diese Änderungen verwerfen und die Datei löschen?", + "openLocalFile": "Lokale Datei", + "openLocalFileBody": "Sie sind dabei, eine Datei zu öffnen, die innerhalb der App gespeichert wird. Wenn Sie an dieser Datei Änderungen vornehmen, werden diese nicht im Dateisystem gespeichert. Um die Datei mit den Änderungen zu erhalten, können Sie sie aus den Einstellungen heraus exportieren.", + "openLocalFileDontShow": "Nicht wieder anzeigen", + "openWrongFile": "Inkompatibles Dateiformat", + "openWrongFileBody": "Dieses Dateiformat ist nicht kompatibel. Die App unterstützt KeePass-Datenbanken im kdbx-Format.", + "openKdbFileBody": "Sie versuchen, eine alte KeePass-Datenbank im kdb-Format zu öffnen. Diese App unterstützt nur das neuere kdbx-Format. Bitte verwenden Sie KeePass v2, um diese Datei umzuwandeln.", + "openConfigHeader": "{} Einstellungen", + "openUrl": "URL", + "openUrlDesc": "https://server/pfad/datei.kdbx, oder einfach datei.kdbx", + "openUser": "Benutzername", + "openUserDesc": "WebDAV Server Benutzername (falls erforderlich)", + "openUserPlaceholder": "kein Benutzername", + "openPass": "Passwort", + "openPassDesc": "WebDAV Server-Passwort (nicht das KDBX-Datei-Passwort)", + "openPassPlaceholder": "kein Passwort", + "openConfigError": "Fehler: {}", + "openConfigErrorNotFound": "Datei nicht gefunden", + "openError": "Fehler", + "openErrorDescription": "Beim Öffnen der Datei ist ein Fehler aufgetreten", + "openErrorFileNotFound": "Datei nicht gefunden", + "openListErrorBody": "Dateien konnten nicht geladen werden", + "openShowAllFiles": "Alle Dateien anzeigen", + "detAttDownload": "Umschalttaste + Klick auf den Anhang-Button zum Herunterladen oder", + "detAttDelToRemove": "Backspace zum entfernen", + "detEmpty": "Ihre Passwörter werden hier angezeigt", + "detGroupRestore": "Ziehen Sie diese Gruppe in eine andere Gruppe als den Papierkorb, um sie wiederherzustellen.", + "detHistoryClickPoint": "Klicken Sie auf einen der Punkte im Verlauf um den entsprechenden Zustand des Eintrags anzuzeigen", + "detHistoryReturn": "zurück zum Eintrag", + "detHistoryRevert": "Auf diese Version zurücksetzen", + "detHistoryDel": "Version löschen", + "detHistoryEmpty": "leer", + "detHistoryModified": "geändert", + "detHistoryRec": "Version", + "detHistoryRecs": "Versionen", + "detHistoryVersion": "Version", + "detHistorySaved": "Gespeichert", + "detHistoryNoTitle": "keine Bezeichnung", + "detHistoryCurState": "aktuelle Version", + "detHistoryCurUnsavedState": "aktuelle ungespeicherte Version", + "detHistoryRevertAlert": "Zu diesem Stand zurückkehren?", + "detHistoryRevertAlertBody": "Ihr aktueller Stand wird in der Historie gesichert.", + "detHistoryDeleteAlert": "Diesen Stand löschen?", + "detHistoryDeleteAlertBody": "Sie können ihn nicht wiederherstellen.", + "detHistoryDiscardChangesAlert": "Änderung verwerfen?", + "detHistoryDiscardChangesAlertBody": "Ungesicherte Änderungen sind unwiderruflich verloren.", + "detBackToList": "zurück zur Liste", + "detSetIconColor": "Farbe ändern", + "detSetIcon": "Icon ändern", + "detDropAttachments": "Anhänge hier ablegen", + "detDelEntry": "Löschen", + "detDelEntryPerm": "Dauerhaft löschen", + "detExpires": "Ablaufdatum", + "detExpired": "abgelaufen", + "detGroup": "Gruppe", + "detCreated": "Erstellt", + "detUpdated": "Geändert", + "detNetField": "Neues Feld", + "detAttachments": "Anhänge", + "detDelFromTrash": "Aus dem Papierkorb löschen?", + "detDelFromTrashBody": "Es wird keine Möglichkeit zur Wiederherstellung geben.", + "detDelFromTrashBodyHint": "Um alle Dateien aus dem Papierkorb zu entfernen, klicken Sie auf das “Papierkorb” leeren-Icon im Papierkorb-Menüpunkt.", + "detFieldCopied": "Kopiert", + "detFieldCopiedTime": "Kopiert für {} Sekunden", + "detCopyHint": "Sie können den Inhalt eines Feldes kopieren, indem Sie auf seine Beschriftung klicken", + "detMore": "mehr", + "detClickToAddField": "Klicken, um ein neues Feld hinzuzufügen", + "detMenuAddNewField": "Neues Feld hinzufügen", + "detMenuShowEmpty": "Leere Felder anzeigen", + "detMenuHideEmpty": "Leere Felder ausblenden", + "detMenuAddField": "{} hinzufügen", + "detMenuCopyPassword": "Passwort kopieren", + "detMenuCopyUser": "Benutzer kopieren", + "detSetupOtp": "Einweg-Passwörter", + "detClone": "Kopie erstellen", + "detClonedName": "Kopie", + "detAutoType": "Auto-Type", + "detAutoTypeSettings": "Auto-Type Einstellungen", + "detAutoTypeEnabled": "Auto-Type für diesen Eintrag aktivieren", + "detAutoTypeSequence": "Tastensequenz", + "detAutoTypeInput": "Eingabe", + "detAutoTypeShortcutsDesc": "{} oder {} während die App inaktiv ist", + "detAutoTypeObfuscation": "Echte Tastenanschläge mit zufälligen mischen", + "detAutoTypeWindow": "Fenster", + "detAutoTypeInputWindow": "Fenster-Titel", + "detSetupOtpAlert": "QR-Code einscannen", + "detSetupOtpAlertBody": "Bitte kopieren Sie den QR-Code, der auf der Autorisierungsseite angezeigt wird.", + "detSetupOtpAlertBody1": "1. Gehen Sie zur Autorisierungsseite", + "detSetupOtpAlertBody2": "2. Erstellen Sie einen Screenshot des QR-Codes {}", + "detSetupOtpAlertBody3": "3. Fügen Sie ihn hier ein {}", + "detSetupOtpAlertBody3Mobile": "3. Markieren Sie ihn oder scannen Sie ihn mit Ihrer Kamera mit dem untenstehenden Markieren/Scannen-Button", + "detSetupOtpAlertBody4": "Wenn Sie den Code nicht einscannen können, klicken Sie bitte auf “Code manuell eingeben”", + "detSetupOtpManualButton": "Code manuell eingeben", + "detSetupOtpScanButton": "Markieren/Scannen", + "detSetupOtpAlertBodyWith": "mit {}", + "detOtpImageError": "Fehler beim Einlesen des Bildes", + "detOtpImageErrorBody": "Entschuldigung, wir konnten das Bildformat nicht lesen. Bitte kontaktieren Sie den Hersteller mit Einzelheiten zu diesem Fehler.", + "detOtpImageReading": "QR-Code wird eingelesen...", + "detOtpQrError": "QR-Code Lesefehler", + "detOtpQrErrorBody": "Der QR-Code konnte leider nicht eingelesen werden. Bitte versuchen Sie es noch einmal oder kontaktieren Sie den Hersteller mit Einzelheiten zum aufgetretenen Fehler.", + "detOtpQrWrong": "Fehlerhafter QR-Code", + "detOtpQrWrongBody": "Ihr QR-Code wurde erfolgreich eingelesen, enthält jedoch kein Einweg-Passwort.", + "detLockField": "Dieses Feld sperren, damit der Inhalt nicht durchsuchbar ist und angezeigt wird. Um ihn anzuzeigen, muss er angeklickt werden.", + "detUnlockField": "Dieses Feld entsperren. Dies führt dazu, dass der Inhalt direkt angezeigt wird und durchsuchbar ist.", + "autoTypeEntryFields": "Feld-Platzhalter", + "autoTypeModifiers": "Hilfstasten", + "autoTypeKeys": "Tasten", + "autoTypeLink": "mehr...", + "autoTypeError": "Auto-Type Fehler", + "autoTypeErrorGeneric": "Während der Ausführung von Auto-Type ist ein Fehler aufgetreten: {}", + "autoTypeErrorGlobal": "Um globale Tastenkürzel zu nutzen, wechseln Sie bitte zu der Anwendung, in der Sie das Passwort eingeben wollen.", + "autoTypeErrorNotInstalled": "{} ist nicht installiert", + "autoTypeHeader": "Auto-Type: Auswählen", + "autoTypeMsgNoWindow": "Wir konnten den Titel des aktiven Fensters leider nicht auslesen. Fangen Sie einfach an zu tippen, um nach dem richtigen Eintrag zu suchen.", + "autoTypeMsgMatchedByWindow": "Passwort auswählen für {}", + "autoTypeNoMatches": "keine passenden Einträge", + "autoTypeSelectionHint": "Die Auto-Type Abfolge eintippen", + "autoTypeSelectionHintAction": "Nur das Passwort eintippen", + "autoTypeSelectionHintOpt": "Nur den Benutzernamen eintippen", + "appSecWarn": "Unsichere Verbindung!", + "appSecWarnBody1": "Sie haben diese App über eine ungesicherte Verbindung geöffnet, Sie könnten beobachtet und Ihre Passwörter könnten gestohlen werden. Wir empfehlen Ihnen dringend, die App hier nicht weiter zu nutzen, wenn Sie nicht genau wissen, was Sie tun.", + "appSecWarnBody2": "Ihre Datenbank ist zwar verschlüsselt, aber es kann nicht sichergestellt werden, dass die App nicht auf dem Web vom Server zu Ihrem Browser manipuliert wurde.", + "appSecWarnBtn": "Ich habe das Risiko verstanden, trotzdem fortfahren", + "appUnsavedWarn": "Ungespeicherte Änderungen!", + "appUnsavedWarnBody": "Sie haben ungespeicherte Änderungen. Wenn Sie die App schließen, gehen diese verloren.", + "appDontExitBtn": "App nicht verlassen", + "appCannotLockAutoInit": "Die App kann nicht gesperrt werden, da automatisches Speichern deaktiviert ist.", + "appCannotLock": "Sie haben ungespeicherte Änderungen, die verloren gehen. Fortfahren?", + "appAutoSave": "Änderungen automatisch speichern", + "appSaveError": "Fehler beim Speichern", + "appSaveErrorBody": "Automatisches Speichern fehlgeschlagen", + "appSaveErrorBodyMul": "Automatisches Speichern fehlgeschlagen:", + "appSettingsError": "Fehler beim Laden der App", + "appSettingsErrorBody": "Es gab einen Fehler beim Laden der App-Einstellungen. Bitte überprüfen Sie die App URL oder kontaktieren Sie Ihren Systemadministrator.", + "appNotSupportedError": "Ihr Browser wird nicht unterstützt.", + "appTabWarn": "Zu viele Tabs", + "appTabWarnBody": "KeeWeb kann nicht in mehreren Browser-Tabs gleichzeitig genutzt werden, bitte schließen Sie diesen Tab.", + "appRightsAlert": "Schreibschutz für KeeWeb wird eingerichtet", + "appRightsAlertBody1": "Ihre KeeWeb-Anwendung ist nicht gegen Änderungen von außen gesichert. Um diesen Schreibschutz einzurichten, benötigt die App Administratorrechte.", + "appRightsAlertBody2": "Sie möchten der App keine Administratorrechte erteilen? Mit folgendem Befehl können Sie den Schreibschutz auch von Hand im Terminal aktivieren", + "setGenTitle": "Allgemeine Einstellungen", + "setGenUpdate": "Update", + "setGenNewVersion": "Eine neue Version der App ist verfügbar und wurde heruntergeladen", + "setGenReleaseNotes": "Versionshinweise anzeigen", + "setGenReloadToUpdate": "Seite aktualisieren, um das Update durchzuführen", + "setGenUpdateManual": "Eine neue Version ist verfügbar. Sie wird nach Updates suchen und sie automatisch installieren, ein automatisches Update von Ihrer Version ist aber nicht möglich.", + "setGenDownloadUpdate": "Update herunterladen", + "setGenUpdateAuto": "Automatisch herunterladen und installieren", + "setGenUpdateCheck": "Auf Updates überprüfen aber nicht installieren", + "setGenNoUpdate": "Nie nach Updates suchen", + "setGenUpdateChecking": "Suche nach Updates", + "setGenCheckUpdate": "Nach Updates suchen", + "setGenErrorChecking": "Updatesuche fehlgeschlagen", + "setGenLastCheckSuccess": "Letzte erfolgreiche Überprüfung am {}", + "setGenLastCheckVer": "Die aktuelle Version war {}", + "setGenCheckedAt": "Überprüft am", + "setGenLatestVer": "Sie haben die aktuellste Version", + "setGenNewVer": "Neue Version {} verfügbar, veröffentlicht am", + "setGenDownloadingUpdate": "Update wird heruntergeladen...", + "setGenExtractingUpdate": "Update wird entpackt...", + "setGenCheckErr": "Beim Herunterladen der neuen Version ist ein Fehler aufgetreten", + "setGenNeverChecked": "Nie nach Updates gesucht", + "setGenRestartToUpdate": "App neu starten, um das Update durchzuführen", + "setGenDownloadAndRestart": "Update herunterladen und App neu starten", + "setGenAppearance": "Oberfläche", + "setGenTheme": "Theme", + "setGenThemeFb": "Flat blue", + "setGenThemeDb": "Dark brown", + "setGenThemeWh": "White", + "setGenThemeTe": "Terminal", + "setGenThemeHc": "High contrast", + "setGenThemeSd": "Solarized dark", + "setGenThemeSl": "Solarized light", + "setGenLocale": "Sprache", + "setGenLocOther": "Andere Sprachen sind als Plugins verfügbar.", + "setGenFontSize": "Schriftgröße", + "setGenFontSizeNormal": "Standard", + "setGenFontSizeLarge": "Groß", + "setGenFontSizeLargest": "Größte", + "setGenTitlebarStyle": "Fenster-Stil (erfordert Neustart des Programms)", + "setGenTitlebarStyleDefault": "Standard", + "setGenTitlebarStyleHidden": "Eigener Titel", + "setGenTitlebarStyleHiddenInset": "Eigener Titel, verschiebbares Fenster", + "setGenShowSubgroups": "Einträge aus allen Untergruppen anzeigen", + "setGenTableView": "Einträge in Tabellenansicht anzeigen", + "setGenColorfulIcons": "Eigene Icons in der Listenansicht farbig anzeigen", + "setGenFunction": "Arbeitsweise", + "setGenAutoSyncOnClose": "Beim Schließen speichern und synchronisieren", + "setGenAutoSyncTimer": "Regelmäßig speichern und synchronisieren", + "setGenAutoSyncTimerOff": "Abgeschaltet", + "setGenAutoSyncTimerInterval": "Alle {} Minuten", + "setGenRememberKeyFiles": "Schlüsseldateien merken", + "setGenNoRememberKeyFiles": "Nicht merken", + "setGenRememberKeyFilesData": "Im internen App-Speicher ablegen", + "setGenRememberKeyFilesPath": "Nur Speicherort der Schlüsseldateien merken", + "setGenLockInactive": "Automatisch sperren, wenn die App inaktiv ist", + "setGenNoAutoLock": "Nicht automatisch sperren", + "setGenLockMinutes": "Nach {} Minuten", + "setGenLockHour": "In einer Stunde", + "setGenLockHours": "Nach {} Stunden", + "setGenLockDay": "Nach einem Tag", + "setGenClearClip": "Zwischenablage nach dem Kopieren leeren", + "setGenNoClear": "Nicht leeren", + "setGenClearSeconds": "In {} Sekunden", + "setGenClearMinute": "Nach einer Minute", + "setGenMinInstead": "App beim Schließen stattdessen minimieren", + "setGenLock": "Automatisch sperren", + "setGenLockMinimize": "Beim Minimieren automatisch sperren", + "setGenLockCopy": "Nach dem Kopieren eines Passworts", + "setGenLockAutoType": "Bei Auto-Type", + "setGenLockOrSleep": "Bei Aktivierung von Bildschirmsperre oder Ruhezustand", + "setGenStorage": "Synchronisierung", + "setGenShowAdvanced": "Erweiterte Einstellungen anzeigen", + "setGenDevTools": "Entwicklerwerkzeuge anzeigen", + "setGenTryBeta": "Beta-Version bis zum Schließen der App ausprobieren", + "setGenTryBetaWarning": "Ungespeicherte Dateien", + "setGenTryBetaWarningBody": "Bitte speichern Sie alle Dateien und Klicken Sie erneut auf diesen Button", + "setGenShowAppLogs": "App-Logs anzeigen", + "setFilePath": "Dateipfad", + "setFileStorage": "Diese Datei wird von {} geladen.", + "setFileIntl": "Diese Datei ist im internen App-Speicher abgelegt", + "setFileLocalHint": "Sie wollen nahtlos mit lokalen Dateien arbeiten?", + "setFileDownloadApp": "Laden Sie die Desktop-App herunter", + "setFileSave": "Speichern", + "setFileSaveTo": "Speichern unter...", + "setFileClose": "Sperren", + "setFileSync": "Synchronisierung", + "setFileSyncVerb": "Synchronisieren", + "setFileSaveToXml": "XML", + "setFileLastSync": "Letzte Synchronisierung", + "setFileLastSyncUnknown": "unbekannt", + "setFileSyncInProgress": "Synchronisierung läuft", + "setFileSyncError": "Synchronisierungsfehler", + "setFilePass": "Master-Passwort", + "setFileConfirmPass": "Master-Passwort bestätigen", + "setFilePassChange": "Um das Passwort zu ändern, geben Sie es im Bestätigungsfeld an", + "setFilePassChanged": "Das Passwort wurde geändert. Lassen Sie das Feld leer, um das bisherige Passwort zu behalten.", + "setFilePassNotMatch": "Die Passwörter stimmen nicht überein, bitte geben Sie sie erneut ein", + "setFileKeyFile": "Schlüsseldatei", + "setFileSelKeyFile": "Schlüsseldatei auswählen", + "setFileNames": "Namen", + "setFileDefUser": "Standard-Benutzername", + "setFileEnableTrash": "Papierkorb aktivieren", + "setFileHistLen": "Anzahl der im Verlauf gespeicherten Versionen", + "setFileHistSize": "Maximalgröße des Verlaufs pro Datei in MB", + "setFileBackups": "Sicherungen", + "setFileBackupEnable": "Diese Datei sichern", + "setFileBackupPath": "Sicherungspfad", + "setFileBackupTime": "Sicherungen anlegen", + "setFileBackupNow": "Jetzt sichern", + "setFileBackupNowWorking": "Sichern...", + "setFileBackupError": "Sicherungsfehler", + "setFileBackupErrorDescription": "Fehler beim Schreiben der Sicherungsdatei", + "setFileBackupErrorIsDir": "Der Sicherungspfad ist ungültig", + "setFileBackupErrorIsDirDescription": "Der Sicherungspfad scheint ein Verzeichnis zu sein. Bitte geben Sie stattdessen einen Dateinamen ein.", + "setFileBackupOnSave": "Immer, wenn die Datei gespeichert wird", + "setFileBackupDaily": "Täglich", + "setFileBackupWeekly": "Wöchentlich", + "setFileBackupMonthly": "Monatlich", + "setFileBackupManually": "Manuell, keine automatische Sicherung", + "setFileRounds": "Verschlüsselungs-Runden", + "setFileKdfParams": "Schlüsselableitungsfunktion", + "setFileKdfParamsIter": "Iterationen", + "setFileKdfParamsMem": "Speicher in KB", + "setFileKdfParamsPar": "Parallelismus", + "setFileKeyChangeForce": "Hinweis zum Ändern des Schlüssels nach (Tagen)", + "setFileUseKeyFile": "Schlüsseldatei benutzen", + "setFileUseGenKeyFile": "Generierte Schlüsseldatei benutzen", + "setFileUseOldKeyFile": "Alte Schlüsseldatei benutzen", + "setFileGenKeyFile": "Neue Schlüsseldatei generieren", + "setFileDontUseKeyFile": "Keine Schlüsseldatei benutzen", + "setFileEmptyPass": "Leeres Passwort", + "setFileEmptyPassBody": "Eine Datenbank ohne Passwort zu speichern macht sie vollkommen ungeschützt. Möchten Sie sie trotzdem speichern?", + "setFileSaveError": "Fehler beim Speichern", + "setFileSaveErrorBody": "Schreibfehler beim Speichern", + "setFileAlreadyExists": "Existiert bereits", + "setFileAlreadyExistsBody": "Die Datei {} existiert bereits. Überschreiben?", + "setFileUnsaved": "Ungespeicherte Änderungen", + "setFileUnsavedBody": "Es gibt ungespeicherte Änderungen in dieser Datei", + "setFileCloseNoSave": "Sperren und Änderungen verwerfen", + "setFileDontClose": "Nicht sperren", + "setShTitle": "Tastenkürzel", + "setShShowAll": "alle Einträge anzeigen", + "setShColors": "mit Farben markierte Einträge anzeigen", + "setShTrash": "Papierkorb öffnen", + "setShFind": "Suchfeld aktivieren; oder: einfach anfangen, den Suchbegriff zu tippen", + "setShClearSearch": "Suchfeld leeren", + "setShCopyPass": "Passwort bzw. ausgewähltes Feld kopieren", + "setShCopyUser": "Benutzername kopieren", + "setShCopyUrl": "Website kopieren", + "setShAutoType": "Auto-Type für ausgewählten Eintrag", + "setShPrev": "zum vorherigen Eintrag gehen", + "setShNext": "zum nächsten Eintrag gehen", + "setShCreateEntry": "Neuer Eintrag", + "setShOpen": "öffnen / neu", + "setShSave": "alle Dateien speichern", + "setShGen": "Passwort generieren", + "setShSet": "App-Einstellungen", + "setShCopyPassGlobal": "Passwort kopieren (wenn die App im Hintergrund ist)", + "setShCopyUserGlobal": "Usernamen kopieren (wenn die App im Hintergrund ist)", + "setShCopyUrlGlobal": "Website kopieren (wenn die App im Hintergrund ist)", + "setShAutoTypeGlobal": "Auto-Type (wenn die App im Hintergrund ist)", + "setShLock": "Datenbank sperren", + "setPlInstallTitle": "Neue Plugins installieren", + "setPlInstallDesc": "KeeWeb-Plugins fügen Funktionen, Themes und Sprachen zu KeeWeb hinzu. Plugins werden mit denselben Rechten ausgeführt wie KeeWeb selbst, sie können auf all Ihre Passwörter zugreifen oder diese ändern. Installieren Sie niemals Plugins, denen Sie nicht vertrauen.", + "setPlInstallLabel": "Plugin-URL", + "setPlInstallBtn": "Installieren", + "setPlInstallBtnProgress": "Installiere", + "setPlUninstallBtn": "Deinstallieren", + "setPlDisableBtn": "Deaktivieren", + "setPlEnableBtn": "Aktivieren", + "setPlUpdateBtn": "Aktualisieren", + "setPlLocaleBtn": "Diese Sprache aktivieren", + "setPlThemeBtn": "Dieses Theme aktivieren", + "setPlJs": "Code", + "setPlCss": "Stile", + "setPlLoc": "Sprache", + "setPlCreatedBy": "Erstellt von {}", + "setPlLoadTime": "geladen in {}", + "setPlLastUpdate": "Letzte Prüfung auf Updates", + "setPlLoadError": "Fehler beim Laden des Plugins", + "setPlGalleryLoading": "Plugins werden geladen, bitte warten Sie einen Moment", + "setPlGalleryLoadError": "Fehler beim Laden der Plugins", + "setPlInstallUrlTitle": "Plugin von einer URL hinzufügen", + "setPlInstallUrlDesc": "Wenn das Plugin nicht in der Galerie verfügbar ist, können Sie es manuell von einer URL installieren", + "setPlOfficial": "Offizielles KeeWeb-Plugin", + "setPlSearch": "Nach Plugins suchen", + "setPlDevelop": "Eigene Plugins entwickeln?", + "setPlDevelopStart": "Hier entlang", + "setPlTranslate": "Oder {}", + "setPlTranslateLink": "übersetzen Sie die App in Ihre Sprache", + "setPlAutoUpdate": "Automatisch aktualisieren", + "setPlLoadGallery": "Plugin-Galerie laden", + "setAboutTitle": "Über", + "setAboutBuilt": "Diese App wurde mit den folgenden Werkzeugen erstellt", + "setAboutLic": "Lizenz", + "setAboutLicComment": "Die App selbst und alle enthaltenen Komponenten, die nicht Public Domain sind, stehen unter der MIT-Lizenz.", + "setAboutFirst": "Dies ist eine Open-Source App, erstellt von {}", + "setAboutSecond": "und lizenziert unter {}.", + "setAboutSource": "Quellcode und Tickets befinden sich auf {}.", + "setHelpFormat": "Dateiformat", + "setHelpFormatBody": "Dies ist eine Portierung der {} App, programmiert mit Webtechnologien. Sie ist kompatibel mit Dateien im KeePass-Format (kdbx). Sie können solche Dateien (Passwort-Datenbanken) entweder mit KeePass oder mit dieser App erstellen. Das Dateiformat ist zu 100% kompatibel und sollte in beiden Anwendungen nutzbar sein.", + "setHelpProblems": "Probleme?", + "setHelpProblems1": "Falls etwas schief geht, {}", + "setHelpProblems2": "oder {}", + "setHelpOpenIssue": "eröffnen Sie bitte ein Ticket auf GitHub (englisch)", + "setHelpContactLink": "kontaktieren Sie einen Entwickler direkt", + "setHelpAppInfo": "App-Informationen", + "setHelpOtherPlatforms": "Andere Plattformen", + "setHelpDesktopApps": "Deskop-Apps", + "setHelpWebApp": "Web-App", + "setHelpUpdates": "Neuigkeiten", + "setHelpTwitter": "Twitter", + "dropboxSetupDesc": "Etwas Konfiguration ist notwendig, um Dropbox in einer selbst gehosteten Web-App zu verwenden. Bitte erstellen Sie eine eigene Dropbox-App und tragen Sie ihren App-Schlüssel unten ein.", + "dropboxAppKey": "Dropbox App-Schlüssel", + "dropboxAppKeyDesc": "Kopieren Sie den Schlüssel aus Ihrer Dropbox-App (Entwicklereinstellungen)", + "dropboxFolder": "App-Ordner", + "dropboxFolderDesc": "Wenn Ihre App mit der gesamten Dropbox verlinkt ist (anstatt mit einem bestimmten Ordner), geben Sie hier den Pfad zum Ordner mit Ihren KDBX-Dateien an.", + "dropboxFolderSettingsDesc": "Wählen Sie einen Ordner in Ihrer Dropbox, in dem die Dateien gespeichert werden (standardmäßig das Stammverzeichnis)", + "dropboxFolderPlaceholder": "Standard-Ordner", + "dropboxLink": "App verlinken mit", + "dropboxLinkApp": "App-Ordner (Apps/KeeWeb)", + "dropboxLinkFull": "Gesamte Dropbox oder beliebiger Ordner", + "dropboxLinkCustom": "Eigene Dropbox-App", + "gdriveSharedWithMe": "Mit mir geteilt", + "webdavSaveMethod": "Speichermethode", + "webdavSaveMove": "Eine temporäre Datei hochladen und verschieben", + "webdavSavePut": "kdbx-Datei mittels PUT überschreiben", + "launcherSave": "Passwort-Datenbank speichern", + "launcherFileFilter": "KeePass-Dateien", + "authPopupRequired": "Pop-Ups blockiert", + "authPopupRequiredBody": "Bitte erlauben Sie Pop-Ups für diese App in Ihrem Browser." +} diff --git a/app/scripts/locales/fr-FR.json b/app/scripts/locales/fr-FR.json index f589d34d..035f9a55 100644 --- a/app/scripts/locales/fr-FR.json +++ b/app/scripts/locales/fr-FR.json @@ -1,567 +1,551 @@ { - "months": [ - "Janvier", - "Février", - "Mars", - "Avril", - "Mai", - "Juin", - "Juillet", - "Août", - "Septembre", - "Octobre", - "Novembre", - "Décembre" - ], - "monthsShort": [ - "Janv", - "Févr", - "Mars", - "Avr", - "Mai", - "Juin", - "Juil", - "Août", - "Sept", - "Oct", - "Nov", - "Déc" - ], - "weekdays": [ - "Dimanche", - "Lundi", - "Mardi", - "Mercredi", - "Jeudi", - "Vendredi", - "Samedi" - ], - "weekdaysShort": [ - "Dim", - "Lun", - "Mar", - "Mer", - "Jeu", - "Ven", - "Sam" - ], - "retToApp": "retour vers l'appli", - "name": "nom", - "icon": "icône", - "title": "titre", - "password": "mot de passe", - "user": "utilisateur", - "website": "site web", - "tags": "tags", - "notes": "notes", - "entry": "entrée", - "group": "groupe", - "noTitle": "aucun titre", - "or": "ou", - "history": "historique", - "template": "modèle", - "notImplemented": "Non Implémenté", - "saveChanges": "Sauver les modifs", - "discardChanges": "Annuler les modifs", - "advanced": "Avancé", - "shortcuts": "Raccourcis", - "help": "Aide", - "settings": "Paramètres", - "plugins": "Modules", - "cache": "cache", - "file": "fichier", - "webdav": "WebDAV", - "dropbox": "Dropbox", - "gdrive": "Google Drive", - "onedrive": "OneDrive", - "menuAllItems": "Tous les items", - "menuColors": "Couleurs", - "menuTrash": "Corbeille", - "menuSetGeneral": "Général", - "menuSetAbout": "A propos", - "menuAlertNoTags": "Aucun tag", - "menuAlertNoTagsBody": "Vous pouvez ajouter de nouveaux tags en éditant les champs, dans la section tags.", - "menuEmptyTrash": "Vider la Corbeille", - "menuEmptyTrashAlert": "Vider la Corbeille?", - "menuEmptyTrashAlertBody": "Vous ne pourrez plus récupérer ces éléments", - "menuItemCollapsed": "Double-cliquer pour déplier", - "alertYes": "Oui", - "alertNo": "Non", - "alertOk": "OK", - "alertCancel": "Annuler", - "alertSignIn": "Connexion", - "alertCopy": "Copier", - "alertClose": "Fermer", - "alertDoNotAsk": "Ne plus me le redemander", - "appBeta": "ATTENTION: version beta, aperçu seulement", - "footerOpen": "Ouvrir/Nouveau", - "footerSyncError": "Erreur de synchro", - "footerTitleGen": "Générer", - "footerTitleLock": "Verrouiller", - "genLen": "Longueur", - "genNewPass": "Nouveau mot de passe", - "genPresetDefault": "réglages par défaut", - "genPresetDerived": "comme ancien mot de passe", - "genPresetPronounceable": "prononçable", - "genPresetMed": "longueur moyenne", - "genPresetLong": "long", - "genPresetPin4": "code à 4 chiffres", - "genPresetMac": "adresse MAC", - "genPresetHash128": "hash 128-bit", - "genPresetHash256": "hash 256-bit", - "grpTitle": "Groupe", - "grpSearch": "Activer la recherche dans les entrées de ce groupe", - "grpAutoType": "Activer l'auto-complétion", - "grpAutoTypeSeq": "Séquence auto-complétion", - "grpAutoTypeSeqDefault": "Utiliser la séquence d'auto-complétion par défaut", - "grpTrash": "Supprimer le groupe et toutes ses entrées", - "tagTitle": "Tag", - "tagTrash": "Supprimer le tag de toutes les entrées", - "tagRename": "Renommer", - "tagTrashQuestion": "Supprimer le tag de toutes les entrées ?", - "tagTrashQuestionBody": "Ce tag va être supprimé de toutes les entrées. Il n'y aura pas de moyen facile pour le remettre.", - "tagExists": "Ce tag existe déjà", - "tagExistsBody": "Un tag existe déjà avec ce nom. Merci de choisir un autre nom.", - "tagBadName": "Nom invalide", - "tagBadNameBody": "Un nom de tag ne peut pas contenir les caractères {}. Merci de les supprimer.", - "genPsTitle": "Générateur de préréglage", - "genPsCreate": "Nouveau préréglage", - "genPsDelete": "Supprimer préréglage", - "genPsNew": "préréglage", - "genPsEnabled": "Afficher dans la liste des préréglages", - "genPsDefault": "Sélectionné par défaut", - "genPsDefaultLength": "Longueur par défaut", - "genPsUpper": "Lettres latines majuscules", - "genPsLower": "Lettres latines minuscules", - "genPsDigits": "Chiffres", - "genPsSpecial": "Caractères spéciaux", - "genPsBrackets": "Parenthèses", - "genPsHigh": "Caractères ASCII hauts", - "genPsAmbiguous": "Caractères ambigus", - "genPsInclude": "Caractères additionnels à inclure", - "genPsExample": "Exemple de mot de passe généré", - "keyChangeTitleRemote": "Clé maître changée", - "keyChangeMessageRemote": "La clé maître a été changée pour cette base de donnée. Merci d'entrer une nouvelle clé", - "keyChangeTitleExpired": "Clé maître expirée", - "keyChangeMessageExpired": "La clé maître pour cette base de donnée est expirée. Merci d'entrer une nouvelle clé", - "keyChangeRepeatPassword": "Mot de passe, encore une fois", - "keyEnter": "Entrée", - "iconFavTitle": "Télécharger et utiliser le favicon du site web", - "iconSelCustom": "Sélectionner un icône personnalisé", - "listEmptyTitle": "Vide", - "listEmptyAdd": "ajouter avec le bouton {} au-dessus", - "listGroup": "Groupe", - "listNoWebsite": "aucun site web", - "listNoUser": "aucun utilisateur", - "listNoAttachments": "aucune pièce-jointe", - "listAddTemplateHeader": "Templates", - "listAddTemplateBody1": "Les modèles (templates) vous permettent de créer des nouvelles entrées en un clic. Ajouter quelque chose à l'entrée du modèle et ensuite cliquer de nouveau sur {} pour utiliser ce modèle.", - "listAddTemplateBody2": "Vous pouvez toujours retrouver vos modèles dans le groupe {}.", - "searchAddNew": "Ajouter Nouveau", - "searchSort": "Trier", - "searchCreated": "Créé", - "searchUpdated": "Mis à jour", - "searchAttachments": "Pièce-jointes", - "searchAZ": "A {} Z", - "searchZA": "Z {} A", - "searchON": "Ancien {} Nouveau", - "searchNO": "Nouveau {} Ancien", - "searchShiftClickOr": "maj-click ou", - "searchAdvTitle": "Afficher/masquer recherche avancée", - "searchSearchIn": "Rechercher dans", - "searchOther": "Autres champs", - "searchProtect": "Champs sécurisés", - "searchOptions": "Options", - "searchCase": "Respecte la casse", - "searchRegex": "RegEx", - "openOpen": "Ouvrir", - "openNew": "Nouveau", - "openMore": "Plus", - "openDemo": "Démo", - "openXml": "Importer XML", - "openCaps": "Majuscules activés", - "openClickToOpen": "Cliquer pour ouvrir un fichier", - "openKeyFile": "fichier clé", - "openKeyFileDropbox": "(depuis DropBox)", - "openDropHere": "déposez les fichiers ici", - "openFailedRead": "Échec lors de la lecture du fichier", - "openNothingFound": "Aucun résultat", - "openNothingFoundBody": "Aucun fichier ne peut être ouvert.", - "openSelectFile": "Sélectionnez un fichier", - "openSelectFileBody": "Sélectionnez le fichier que vous voudriez ouvrir", - "openPassFor": "Mot de passe pour", - "openRemoveLastQuestion": "Supprimer le fichier local ?", - "openRemoveLastQuestionBody": "Le fichier que vous voulez supprimer est sauvegardé dans l'appli. Le supprimer de façon définitive ?", - "openRemoveLastQuestionModBody": "Le fichier que vous voulez supprimer a des modifications locales. Le supprimer et annuler ces modifications ?", - "openLocalFile": "Fichier local", - "openLocalFileBody": "Vous allez ouvrir un fichier qui sera stocké à l'intérieur de l'app. Les modifications apportées ne seront pas sauvegardés vers le système de fichiers. Pour obtenir le fichier avec vos données, exporter à partir des paramètres.", - "openLocalFileDontShow": "Ne plus afficher", - "openWrongFile": "Fichier invalide", - "openWrongFileBody": "Ce format de fichier n'est pas supporté. Cette application fonctionne avec des fichiers de base de donnée KeePass (kdbx).", - "openKdbFileBody": "Vous essayez d'ouvrir un ancien fichier de base de donnée KeePass (KDB). Cette application supporte uniquement le nouveaux format (kdbx), merci d'utiliser KeePass 2 afin de convertir celui-ci.", - "openConfigHeader": "{} Paramètres", - "openUrl": "URL", - "openUrlDesc": "https://serveur/chemin/fichier.kdbx, ou seulement fichier.kdbx", - "openUser": "Nom d'utilisateur", - "openUserDesc": "Nom d'utilisateur du serveur WebDAV (si requis)", - "openUserPlaceholder": "aucun nom d'utilisateur", - "openPass": "Mot de passe", - "openPassDesc": "Mot de passe du serveur WebDAV (ce n'est pas votre mot de passe de fichier)", - "openPassPlaceholder": "aucun mot de passe", - "openConfigError": "Erreur: {}", - "openConfigErrorNotFound": "Aucun fichier trouvé", - "openError": "Erreur", - "openErrorDescription": "Une erreur est survenue à l'ouverture du fichier", - "openErrorFileNotFound": "Fichier non trouvé", - "openListErrorBody": "Erreur au chargement de la liste des fichiers", - "openShowAllFiles": "Tous les fichiers", - "detAttDownload": "Cliquer sur le bouton de pièce-jointe en pressant la touche Maj ou", - "detAttDelToRemove": "Supprimer pour enlever", - "detEmpty": "Vos mot de passe seront affichés ici", - "detGroupRestore": "Pour restaurer ce groupe, veuillez le glisser-déposer dans un des groupes hors de la corbeille", - "detHistoryClickPoint": "Cliquer sur une entrée de l'historique pour voir son état", - "detHistoryReturn": "Retour vers l'entrée", - "detHistoryRevert": "Revenir à cet état", - "detHistoryDel": "Supprimer cet état", - "detHistoryEmpty": "vide", - "detHistoryModified": "modifié", - "detHistoryRec": "archive", - "detHistoryRecs": "archives", - "detHistoryVersion": "Version", - "detHistorySaved": "Sauvegardé", - "detHistoryNoTitle": "sans titre", - "detHistoryCurState": "état actuel", - "detHistoryCurUnsavedState": "état actuel non sauvegardé", - "detHistoryRevertAlert": "Revenir à cet état de l'historique ?", - "detHistoryRevertAlertBody": "L'état courant sera sauvegardé dans l'historique.", - "detHistoryDeleteAlert": "Supprimer cet état de l'historique ?", - "detHistoryDeleteAlertBody": "Vous ne pourrez plus le restaurer.", - "detHistoryDiscardChangesAlert": "Oublier les changements fait à cette entrée ?", - "detHistoryDiscardChangesAlertBody": "Les changements non sauvegardés seront définitivement perdus.", - "detBackToList": "retour vers liste", - "detSetIconColor": "Changer de couleur", - "detSetIcon": "Changer d'icône", - "detDropAttachments": "Déposer une pièce-jointe ici", - "detDelEntry": "Supprimer", - "detDelEntryPerm": "Supprimer définitivement", - "detExpires": "Expire", - "detExpired": "expirée", - "detGroup": "Groupe", - "detCreated": "Créé le", - "detUpdated": "Mis à jour", - "detNetField": "Nouveau champ", - "detAttachments": "Pièces-jointes", - "detDelFromTrash": "Supprimer de la Corbeille ?", - "detDelFromTrashBody": "Vous ne pourrez plus le récupérer.", - "detDelFromTrashBodyHint": "Pour supprimer tous les éléments de la Corbeille, cliquez l’icône \"Vider la corbeille\" du menu Corbeille.", - "detFieldCopied": "Copié", - "detFieldCopiedTime": "Copié pendant {} secondes", - "detCopyHint": "Vous pouvez copier la valeur du champ en cliquant sur son titre", - "detMore": "plus", - "detClickToAddField": "cliquez pour ajouter un nouveau champ", - "detMenuAddNewField": "Ajouter nouveau champ", - "detMenuShowEmpty": "Montrer les champs vides", - "detMenuHideEmpty": "Cacher les champs vides", - "detMenuAddField": "Ajouter {}", - "detMenuCopyPassword": "Copier mot de passe", - "detMenuCopyUser": "Copier nom", - "detSetupOtp": "Mots de passe à usage unique", - "detClone": "Dupliquer", - "detClonedName": "Copier", - "detAutoType": "Saisie auto", - "detAutoTypeSettings": "Paramètres saisie auto", - "detAutoTypeEnabled": "Activer la saisie automatique pour cette entrée", - "detAutoTypeSequence": "Frappes", - "detAutoTypeInput": "Entrée", - "detAutoTypeShortcutsDesc": "{} ou {} tant que l'appli est inactive", - "detAutoTypeObfuscation": "Mélanger touches réelles et aléatoire", - "detAutoTypeWindow": "Fenêtre", - "detAutoTypeInputWindow": "Titre de fenêtre", - "detSetupOtpAlert": "Scanner le code QR", - "detSetupOtpAlertBody": "Veuillez copier le code QR qui est affiché sur la page d'autorisation", - "detSetupOtpAlertBody1": "1. aller sur la page d'autorisation", - "detSetupOtpAlertBody2": "2. faire une capture d'écran du code QR {}", - "detSetupOtpAlertBody3": "3. coller la ici {}", - "detSetupOtpAlertBody3Mobile": "3. Selectionner ou scanner avec votre appareil photo en utilisant Selectionner/Scan ci-dessous", - "detSetupOtpAlertBody4": "Si vous ne pouvez pas scanner le code, cliquer sur Entrer le code manuellement", - "detSetupOtpManualButton": "Entrer le code manuellement", - "detSetupOtpScanButton": "Selectionner/Scan", - "detSetupOtpAlertBodyWith": "avec {}", - "detOtpImageError": "Erreur de lecture de l'image", - "detOtpImageErrorBody": "Désolé, le format de l'image ne peut pas être lu, merci de contacter les auteurs de l'application avec le détail de l'erreur.", - "detOtpImageReading": "Lecture du code QR...", - "detOtpQrError": "Erreur lecture du code QR", - "detOtpQrErrorBody": "Désolé le QR code ne peut pas être lu, essayez à nouveau ou contacter les auteurs de l'application avec le détail de l'erreur.", - "detOtpQrWrong": "Code QR incorrect", - "detOtpQrWrongBody": "Votre QR code a été scanné avec succès mais il ne contient pas d'information pour un mot de passe à usage unique.", - "detLockField": "Verrouiller ce champ, ainsi son contenu ne sera pas visible ni ne pourra être cherché. Visualiser son contenu exigera de le cliquer explicitement.", - "detUnlockField": "Déverrouiller ce champ, rendant son contenu immédiatement visible et pouvant être cherché", - "autoTypeEntryFields": "Champs", - "autoTypeModifiers": "Touches modificatrices", - "autoTypeKeys": "Clés", - "autoTypeLink": "plus...", - "autoTypeError": "Erreur saisie auto", - "autoTypeErrorGeneric": "Il y a eu une erreur lors de la saisie automatique : {}", - "autoTypeErrorGlobal": "Pour utiliser le raccourci, sélectionner dans l'application là où vous voulez saisir le mot de passe", - "autoTypeErrorNotInstalled": "{} n'est pas installé", - "autoTypeHeader": "Saisie auto : Selectionner", - "autoTypeMsgNoWindow": "Nous n'avons pas pu obtenir le titre de la fenêtre active, commencez à taper pour rechercher", - "autoTypeMsgMatchedByWindow": "Sélectionner un mot de passe pour {}", - "autoTypeNoMatches": "aucun", - "autoTypeSelectionHint": "Saisissez la séquence automatique", - "autoTypeSelectionHintAction": "Entrez seulement le mot de passe", - "autoTypeSelectionHintOpt": "Entrez seulement le compte", - "appSecWarn": "Non sécurisé !", - "appSecWarnBody1": "Vous avez chargé cette appli avec une connexion non sécurisée. Quelqu'un peut vous observer et voler vos mots de passe. Nous vous recommandons fortement d'arrêter cette connexion, à moins que vous ne compreniez exactement ce que vous faites.", - "appSecWarnBody2": "Oui, votre base est chiffrée mais personne ne peut garantir que l'application n'a pas été modifiée avant d'arriver à vous.", - "appSecWarnBtn": "Je comprends les risques, continuer", - "appUnsavedWarn": "Modifications non sauvegardées", - "appUnsavedWarnBody": "Vous avez des fichiers non sauvegardés, si vous fermez l'appli, les changements seront perdus.", - "appDontExitBtn": "Ne pas quitter", - "appCannotLockAutoInit": "L'appli ne peut pas être verrouillée car la sauvegarde automatique est désactivée.", - "appCannotLock": "Vous avez des modifications non sauvegardées qui seront perdus. Continuer ?", - "appAutoSave": "Sauver les modifications automatiquement", - "appSaveError": "Sauver l'erreur", - "appSaveErrorBody": "Echec sauvegarde auto du fichier", - "appSaveErrorBodyMul": "Echec sauvegarde auto des fichiers", - "appSettingsError": "Erreur lancement appli", - "appSettingsErrorBody": "Il y a eu une erreur au chargement des paramètres. Merci de revérifier l'URL de l'application ou contacter votre administrateur.", - "appNotSupportedError": "Votre navigateur n'est pas supporté.", - "appTabWarn": "Trop d'onglets", - "appTabWarnBody": "KeeWeb ne peut être utilisé dans 2 onglets de navigateurs simultanément. Fermer cet onglet, s'il vous plait.", - "appRightsAlert": "Protéger l'appli KeeWeb", - "appRightsAlertBody1": "Votre application KeeWeb est accessible en écriture. Nous demandons les droits administrateurs pour le mettre en écriture uniquement pour les administrateurs.", - "appRightsAlertBody2": "Vous ne voulez pas donner les permissions ? Vous pouvez le faire vous-même à partir d'un terminal", - "setGenTitle": "Paramètres", - "setGenUpdate": "Mettre à jour", - "setGenNewVersion": "Une nouvelle version a été téléchargée", - "setGenReleaseNotes": "Voir les notes de version", - "setGenReloadToUpdate": "Recharger pour mettre à jour", - "setGenUpdateManual": "Une nouvelle version est disponible. Elle vérifiera les mises à jour et les installera automatiquement, mais une mise à jour automatique à partir de votre version n'est pas possible.", - "setGenDownloadUpdate": "Télécharger mise à jour", - "setGenUpdateAuto": "Télécharger et installer automatiquement", - "setGenUpdateCheck": "Vérifier mais ne pas installer", - "setGenNoUpdate": "Ne jamais vérifier les mises à jour", - "setGenUpdateChecking": "Vérification des mises à jour", - "setGenCheckUpdate": "Vérifier les mises à jour", - "setGenErrorChecking": "Erreur vérification des mises à jour", - "setGenLastCheckSuccess": "Dernière vérification réussie fut le {}", - "setGenLastCheckVer": "la dernière version était {}", - "setGenCheckedAt": "Vérifié le", - "setGenLatestVer": "vous utilisez la dernière version", - "setGenNewVer": "nouvelle version {} disponible", - "setGenDownloadingUpdate": "Téléchargement de la mise à jour...", - "setGenExtractingUpdate": "Décompression de la mise à jour...", - "setGenCheckErr": "Une erreur est intervenue durant le téléchargement de la mise à jour", - "setGenNeverChecked": "Ne jamais vérifier les mises à jour", - "setGenRestartToUpdate": "Redémarrer pour mettre à jour", - "setGenDownloadAndRestart": "Télécharger la mise à jour et redémarrer", - "setGenAppearance": "Apparence", - "setGenTheme": "Thème", - "setGenThemeFb": "Bleu plat", - "setGenThemeDb": "Marron foncé", - "setGenThemeWh": "Blanc", - "setGenThemeTe": "Terminal", - "setGenThemeHc": "Contraste élevé", - "setGenThemeSd": "Solarized dark", - "setGenThemeSl": "Solarized light", - "setGenLocale": "Langue", - "setGenLocOther": "d'autres langues sont disponibles en tant que modules", - "setGenFontSize": "Taille de police", - "setGenFontSizeNormal": "Normal", - "setGenFontSizeLarge": "Large", - "setGenFontSizeLargest": "Très large", - "setGenTitlebarStyle": "Style de fenêtres (redémarrage requis)", - "setGenTitlebarStyleDefault": "Par défaut", - "setGenTitlebarStyleHidden": "Titre modifié", - "setGenTitlebarStyleHiddenInset": "Titre modifié, fénêtre déplaçable", - "setGenShowSubgroups": "Afficher les entrées de tous les sous groupes", - "setGenTableView": "Voir les entrées en liste", - "setGenColorfulIcons": "Icônes en couleur dans la liste", - "setGenFunction": "Opérations", - "setGenAutoSyncOnClose": "Sauvegarde et synchro automatique à la fermeture", - "setGenAutoSyncTimer": "Sauvegarde et synchro automatique régulièrement", - "setGenAutoSyncTimerOff": "Désactivé", - "setGenAutoSyncTimerInterval": "Toutes les {} minute(s)", - "setGenRememberKeyFiles": "Se souvenir des fichiers clé", - "setGenNoRememberKeyFiles": "Ne pas se souvenir", - "setGenRememberKeyFilesData": "Sauvegarder dans le stockage interne de l'appli", - "setGenRememberKeyFilesPath": "Se souvenir seulement des chemins des fichiers", - "setGenLockInactive": "si l'app est inactive", - "setGenNoAutoLock": "Ne pas verrouiller automatiquement", - "setGenLockMinutes": "Dans {} minutes", - "setGenLockHour": "Dans une heure", - "setGenLockHours": "Dans {} heures", - "setGenLockDay": "Dans 1 jour", - "setGenClearClip": "Vider le presse-papier après une copie", - "setGenNoClear": "Ne pas effacer", - "setGenClearSeconds": "Dans {} secondes", - "setGenClearMinute": "Dans une minute", - "setGenMinInstead": "Réduire l'application au lieu de fermer", - "setGenLock": "Verrou Auto", - "setGenLockMinimize": "quand l'appli est minimisée", - "setGenLockCopy": "sur copie de m.d.p.", - "setGenLockAutoType": "sur saisie auto", - "setGenLockOrSleep": "Quand l'ordinateur est verrouillé ou mis en sommeil", - "setGenStorage": "Stockage", - "setGenShowAdvanced": "Afficher les paramètres avancés", - "setGenDevTools": "Afficher les outils de développement", - "setGenTryBeta": "Essayer une fois la version beta ", - "setGenTryBetaWarning": "Fichiers non sauvegardés", - "setGenTryBetaWarningBody": "Sauvegarder tous les fichiers et re-cliquer ce bouton", - "setGenShowAppLogs": "Voir les logs", - "setFilePath": "Chemin", - "setFileStorage": "Le fichier est ouvert de {}.", - "setFileIntl": "Le fichier est stocké en interne de l'appli", - "setFileLocalHint": "Voulez-vous travailler avec des fichiers locaux ?", - "setFileDownloadApp": "Télécharger l'application", - "setFileSave": "Enregistrer", - "setFileSaveTo": "Enregistrer sous", - "setFileClose": "Fermer", - "setFileSync": "Sync", - "setFileSyncVerb": "Synchroniser", - "setFileSaveToXml": "XML", - "setFileLastSync": "Dernière synchro", - "setFileLastSyncUnknown": "inconnu", - "setFileSyncInProgress": "synchronisation en cours", - "setFileSyncError": "Sync erreur", - "setFilePass": "Mot de passe principal", - "setFileConfirmPass": "Confirmer le mot de passe Maître", - "setFilePassChange": "pour changer le mot de passe, merci de le saisir dans le champ Confirmation", - "setFilePassChanged": "Le mot de passe a été changé ; laissez le champ vide pour utiliser l'ancien mot de passe", - "setFilePassNotMatch": "les mots de passe ne correspondent pas, veuillez réessayer", - "setFileKeyFile": "Fichier clé", - "setFileSelKeyFile": "Sélectionner fichier clé", - "setFileNames": "Noms", - "setFileDefUser": "Nom par défaut", - "setFileEnableTrash": "Activer corbeille", - "setFileHistLen": "Longueur de l'historique, conserve les derniers enregistrements", - "setFileHistSize": "Taille de l'historique, Mo par fichier", - "setFileBackups": "Sauvegardes", - "setFileBackupEnable": "Sauvegarder ce fichier", - "setFileBackupPath": "Chemin", - "setFileBackupTime": "Lancer sauvegardes ", - "setFileBackupNow": "Sauvegarder...", - "setFileBackupNowWorking": "En cours...", - "setFileBackupError": "Erreur sauvegarde", - "setFileBackupErrorDescription": "Erreur à l'écriture du fichier", - "setFileBackupErrorIsDir": "Chemin de sauvegarde invalide", - "setFileBackupErrorIsDirDescription": "Le chemin de sauvegarde semble pointer sur un répertoire. Merci de spécifier un fichier dans un répertoire à la place.", - "setFileBackupOnSave": "A chaque fois que je sauvegarde", - "setFileBackupDaily": "Quotidien", - "setFileBackupWeekly": "Chaque semaine", - "setFileBackupMonthly": "Chaque mois", - "setFileBackupManually": "Manuellement, pas de sauvegarde automatique", - "setFileRounds": "Cycles de chiffrement de la clé", - "setFileKdfParams": "Fonction de dérivation de clé", - "setFileKdfParamsIter": "Itérations", - "setFileKdfParamsMem": "Mémoire, Ko", - "setFileKdfParamsPar": "Parallèlisme", - "setFileKeyChangeForce": "Demander à changer la clé après (jours)", - "setFileUseKeyFile": "Utiliser fichier clé", - "setFileUseGenKeyFile": "Utiliser fichier clé généré", - "setFileUseOldKeyFile": "Utiliser ancien fichier clé", - "setFileGenKeyFile": "Générer nouveau fichier clé", - "setFileDontUseKeyFile": "Ne pas utiliser fichier clé", - "setFileEmptyPass": "Mot de passe vide", - "setFileEmptyPassBody": "Sauvegarder la base de données avec un mode de passe vide la laisse sans aucune protection. Voulez-vous vraiment faire cela ?", - "setFileSaveError": "Erreur sauvegarde", - "setFileSaveErrorBody": "Erreur sauvegarde dans le fichier", - "setFileAlreadyExists": "Existe déjà", - "setFileAlreadyExistsBody": "Le fichier {} existe déjà. L'écraser ?", - "setFileUnsaved": "Modif non sauvegardées", - "setFileUnsavedBody": "Il y a des modifications non sauvegardées dans ce fichier", - "setFileCloseNoSave": "Fermer et perdre les modifs", - "setFileDontClose": "Ne pas fermer", - "setShTitle": "Raccourcis", - "setShShowAll": "montrer tout", - "setShColors": "montrer les champs en couleur", - "setShTrash": "aller à Corbeille", - "setShFind": "rechercher ou juste commencer à taper", - "setShClearSearch": "effacer recherche", - "setShCopyPass": "copier le mot de passe ou le champ sélectionné", - "setShCopyUser": "copier le nom", - "setShCopyUrl": "copier le site", - "setShAutoType": "saisie automatique pour cette entrée", - "setShPrev": "aller au précédent", - "setShNext": "aller au prochain", - "setShCreateEntry": "créer une entrée", - "setShOpen": "ouvrir/nouveau", - "setShSave": "sauver tous les fichiers", - "setShGen": "générer un mot de passe", - "setShSet": "paramètres", - "setShCopyPassGlobal": "copier le mot de passe (quand l'appli est en arrière-plan)", - "setShCopyUserGlobal": "copier l'utilisateur (quand l'appli est en arrière-plan)", - "setShCopyUrlGlobal": "copier le site web (quand l'appli est en arrière-plan)", - "setShAutoTypeGlobal": "saisie automatique (quand l'appli est en arrière-plan)", - "setShLock": "verrouiller la base", - "setPlInstallTitle": "Installer de nouveaux modules", - "setPlInstallDesc": "Les modules (plugins) de KeeWeb ajoutent des fonctionnalités, thèmes et langues à KeeWeb. Les modules sont lancés avec les même privilèges que KeeWeb : ils peuvent accéder et gérer tous vos mots de passe. Ne jamais installer de modules auxquels vous ne faites pas confiance.", - "setPlInstallLabel": "URL du module", - "setPlInstallBtn": "Installer", - "setPlInstallBtnProgress": "Installation", - "setPlUninstallBtn": "Désintaller", - "setPlDisableBtn": "Désactiver", - "setPlEnableBtn": "Activer", - "setPlUpdateBtn": "Mettre à jour", - "setPlLocaleBtn": "Changer pour cette langue", - "setPlThemeBtn": "Changer pour ce thème", - "setPlJs": "code", - "setPlCss": "styles", - "setPlLoc": "langue", - "setPlCreatedBy": "Créé par {}", - "setPlLoadTime": "{} pour charger", - "setPlLastUpdate": "Dernière vérification des mises à jour", - "setPlLoadError": "erreur au chargement du module", - "setPlGalleryLoading": "Chargement des modules, patientez un instant", - "setPlGalleryLoadError": "erreur au chargement des modules", - "setPlInstallUrlTitle": "Ajouter un module à partir d'une URL", - "setPlInstallUrlDesc": "Si un module n'est pas dans le catalogue, vous pouvez l'installer manuellement à partir d'une URL.", - "setPlOfficial": "Module officiel KeeWeb", - "setPlSearch": "Rechercher des modules", - "setPlDevelop": "Vous souhaitez développer vos propres modules ?", - "setPlDevelopStart": "Commencez ici", - "setPlTranslate": "Ou vous pouvez {}", - "setPlTranslateLink": "traduire l'appli dans votre langue", - "setPlAutoUpdate": "Mise à jour automatique", - "setPlLoadGallery": "Charger le catalogue", - "setAboutTitle": "A propos", - "setAboutBuilt": "Cette appli est construite avec ces outils géniaux", - "setAboutLic": "Licence", - "setAboutLicComment": "Cette application et tous les composants qu'elle contient qui ne sont pas dans le domaine public sont sous licence MIT", - "setAboutFirst": "Ceci est une application open-source créée par {}", - "setAboutSecond": " et sous licence {}.", - "setAboutSource": "Le code source et les problèmes sont sur {}.", - "setHelpFormat": "Format de fichier", - "setHelpFormatBody": "Ceci est un portage de l'appli {} développée avec les technologies web. Il interprète les fichiers au format KeePass (kdbx). Vous pouvez créer de tels fichiers (bases de données de mots de passe) soit avec KeePass, soit avec cette appli. Le format de fichier est 100% compatible et devrait être compris par les 2 applications.", - "setHelpProblems": "Un problème ?", - "setHelpProblems1": "Si quelque chose ne va pas, merci de {}", - "setHelpProblems2": "ou {}", - "setHelpOpenIssue": "faire connaître le problème sur GitHub", - "setHelpContactLink": "contacter directement un développeur", - "setHelpAppInfo": "Information", - "setHelpOtherPlatforms": "Autres plateformes", - "setHelpDesktopApps": "Applis Desktop", - "setHelpWebApp": "Appli web", - "setHelpUpdates": "Mises à jour", - "setHelpTwitter": "Appli twitter", - "dropboxSetupDesc": "Une configuration particulière est nécessaire pour utiliser Dropbox dans une application auto-hébergée. Merci de créer votre propre application Dropbox et d'inscrire sa clé ci-dessous.", - "dropboxAppKey": "Clé Dropbox", - "dropboxAppKeyDesc": "Copier la clé de votre appli Dropbox (Réglages développeur)", - "dropboxFolder": "Dossier Appli", - "dropboxFolderDesc": "Si votre appli est lié à Dropbox (pas un dossier), définir le dossier avec vos fichiers Kdbx içi", - "dropboxFolderSettingsDesc": "Sélectionner un dossier dans votre Dropbox où les fichiers sont stockés (à la racine par défaut)", - "dropboxFolderPlaceholder": "dossier par défaut", - "dropboxLink": "Lier l'appli à", - "dropboxLinkApp": "Dossier de l'appli (Apps/KeeWeb)", - "dropboxLinkFull": "Tout Dropbox ou n'importe quel dossier", - "dropboxLinkCustom": "Votre appli Dropbox", - "gdriveSharedWithMe": "Partagé avec moi", - "webdavSaveMethod": "Méthode de sauvegarde", - "webdavSaveMove": "Uploader un fichier temporaire et le déplacer", - "webdavSavePut": "Ecraser le fichier kdbx avec PUT", - "launcherSave": "Sauvegarder base des mots de passe", - "launcherFileFilter": "Fichiers Keepass", - "authPopupRequired": "Les pop-up sont bloquées", - "authPopupRequiredBody": "Merci d'autoriser les popups pour cette appli dans votre navigateur ou essayez à nouveau." -} \ No newline at end of file + "months": [ + "Janvier", + "Février", + "Mars", + "Avril", + "Mai", + "Juin", + "Juillet", + "Août", + "Septembre", + "Octobre", + "Novembre", + "Décembre" + ], + "monthsShort": [ + "Janv", + "Févr", + "Mars", + "Avr", + "Mai", + "Juin", + "Juil", + "Août", + "Sept", + "Oct", + "Nov", + "Déc" + ], + "weekdays": ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"], + "weekdaysShort": ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"], + "retToApp": "retour vers l'appli", + "name": "nom", + "icon": "icône", + "title": "titre", + "password": "mot de passe", + "user": "utilisateur", + "website": "site web", + "tags": "tags", + "notes": "notes", + "entry": "entrée", + "group": "groupe", + "noTitle": "aucun titre", + "or": "ou", + "history": "historique", + "template": "modèle", + "notImplemented": "Non Implémenté", + "saveChanges": "Sauver les modifs", + "discardChanges": "Annuler les modifs", + "advanced": "Avancé", + "shortcuts": "Raccourcis", + "help": "Aide", + "settings": "Paramètres", + "plugins": "Modules", + "cache": "cache", + "file": "fichier", + "webdav": "WebDAV", + "dropbox": "Dropbox", + "gdrive": "Google Drive", + "onedrive": "OneDrive", + "menuAllItems": "Tous les items", + "menuColors": "Couleurs", + "menuTrash": "Corbeille", + "menuSetGeneral": "Général", + "menuSetAbout": "A propos", + "menuAlertNoTags": "Aucun tag", + "menuAlertNoTagsBody": "Vous pouvez ajouter de nouveaux tags en éditant les champs, dans la section tags.", + "menuEmptyTrash": "Vider la Corbeille", + "menuEmptyTrashAlert": "Vider la Corbeille?", + "menuEmptyTrashAlertBody": "Vous ne pourrez plus récupérer ces éléments", + "menuItemCollapsed": "Double-cliquer pour déplier", + "alertYes": "Oui", + "alertNo": "Non", + "alertOk": "OK", + "alertCancel": "Annuler", + "alertSignIn": "Connexion", + "alertCopy": "Copier", + "alertClose": "Fermer", + "alertDoNotAsk": "Ne plus me le redemander", + "appBeta": "ATTENTION: version beta, aperçu seulement", + "footerOpen": "Ouvrir/Nouveau", + "footerSyncError": "Erreur de synchro", + "footerTitleGen": "Générer", + "footerTitleLock": "Verrouiller", + "genLen": "Longueur", + "genNewPass": "Nouveau mot de passe", + "genPresetDefault": "réglages par défaut", + "genPresetDerived": "comme ancien mot de passe", + "genPresetPronounceable": "prononçable", + "genPresetMed": "longueur moyenne", + "genPresetLong": "long", + "genPresetPin4": "code à 4 chiffres", + "genPresetMac": "adresse MAC", + "genPresetHash128": "hash 128-bit", + "genPresetHash256": "hash 256-bit", + "grpTitle": "Groupe", + "grpSearch": "Activer la recherche dans les entrées de ce groupe", + "grpAutoType": "Activer l'auto-complétion", + "grpAutoTypeSeq": "Séquence auto-complétion", + "grpAutoTypeSeqDefault": "Utiliser la séquence d'auto-complétion par défaut", + "grpTrash": "Supprimer le groupe et toutes ses entrées", + "tagTitle": "Tag", + "tagTrash": "Supprimer le tag de toutes les entrées", + "tagRename": "Renommer", + "tagTrashQuestion": "Supprimer le tag de toutes les entrées ?", + "tagTrashQuestionBody": "Ce tag va être supprimé de toutes les entrées. Il n'y aura pas de moyen facile pour le remettre.", + "tagExists": "Ce tag existe déjà", + "tagExistsBody": "Un tag existe déjà avec ce nom. Merci de choisir un autre nom.", + "tagBadName": "Nom invalide", + "tagBadNameBody": "Un nom de tag ne peut pas contenir les caractères {}. Merci de les supprimer.", + "genPsTitle": "Générateur de préréglage", + "genPsCreate": "Nouveau préréglage", + "genPsDelete": "Supprimer préréglage", + "genPsNew": "préréglage", + "genPsEnabled": "Afficher dans la liste des préréglages", + "genPsDefault": "Sélectionné par défaut", + "genPsDefaultLength": "Longueur par défaut", + "genPsUpper": "Lettres latines majuscules", + "genPsLower": "Lettres latines minuscules", + "genPsDigits": "Chiffres", + "genPsSpecial": "Caractères spéciaux", + "genPsBrackets": "Parenthèses", + "genPsHigh": "Caractères ASCII hauts", + "genPsAmbiguous": "Caractères ambigus", + "genPsInclude": "Caractères additionnels à inclure", + "genPsExample": "Exemple de mot de passe généré", + "keyChangeTitleRemote": "Clé maître changée", + "keyChangeMessageRemote": "La clé maître a été changée pour cette base de donnée. Merci d'entrer une nouvelle clé", + "keyChangeTitleExpired": "Clé maître expirée", + "keyChangeMessageExpired": "La clé maître pour cette base de donnée est expirée. Merci d'entrer une nouvelle clé", + "keyChangeRepeatPassword": "Mot de passe, encore une fois", + "keyEnter": "Entrée", + "iconFavTitle": "Télécharger et utiliser le favicon du site web", + "iconSelCustom": "Sélectionner un icône personnalisé", + "listEmptyTitle": "Vide", + "listEmptyAdd": "ajouter avec le bouton {} au-dessus", + "listGroup": "Groupe", + "listNoWebsite": "aucun site web", + "listNoUser": "aucun utilisateur", + "listNoAttachments": "aucune pièce-jointe", + "listAddTemplateHeader": "Templates", + "listAddTemplateBody1": "Les modèles (templates) vous permettent de créer des nouvelles entrées en un clic. Ajouter quelque chose à l'entrée du modèle et ensuite cliquer de nouveau sur {} pour utiliser ce modèle.", + "listAddTemplateBody2": "Vous pouvez toujours retrouver vos modèles dans le groupe {}.", + "searchAddNew": "Ajouter Nouveau", + "searchSort": "Trier", + "searchCreated": "Créé", + "searchUpdated": "Mis à jour", + "searchAttachments": "Pièce-jointes", + "searchAZ": "A {} Z", + "searchZA": "Z {} A", + "searchON": "Ancien {} Nouveau", + "searchNO": "Nouveau {} Ancien", + "searchShiftClickOr": "maj-click ou", + "searchAdvTitle": "Afficher/masquer recherche avancée", + "searchSearchIn": "Rechercher dans", + "searchOther": "Autres champs", + "searchProtect": "Champs sécurisés", + "searchOptions": "Options", + "searchCase": "Respecte la casse", + "searchRegex": "RegEx", + "openOpen": "Ouvrir", + "openNew": "Nouveau", + "openMore": "Plus", + "openDemo": "Démo", + "openXml": "Importer XML", + "openCaps": "Majuscules activés", + "openClickToOpen": "Cliquer pour ouvrir un fichier", + "openKeyFile": "fichier clé", + "openKeyFileDropbox": "(depuis DropBox)", + "openDropHere": "déposez les fichiers ici", + "openFailedRead": "Échec lors de la lecture du fichier", + "openNothingFound": "Aucun résultat", + "openNothingFoundBody": "Aucun fichier ne peut être ouvert.", + "openSelectFile": "Sélectionnez un fichier", + "openSelectFileBody": "Sélectionnez le fichier que vous voudriez ouvrir", + "openPassFor": "Mot de passe pour", + "openRemoveLastQuestion": "Supprimer le fichier local ?", + "openRemoveLastQuestionBody": "Le fichier que vous voulez supprimer est sauvegardé dans l'appli. Le supprimer de façon définitive ?", + "openRemoveLastQuestionModBody": "Le fichier que vous voulez supprimer a des modifications locales. Le supprimer et annuler ces modifications ?", + "openLocalFile": "Fichier local", + "openLocalFileBody": "Vous allez ouvrir un fichier qui sera stocké à l'intérieur de l'app. Les modifications apportées ne seront pas sauvegardés vers le système de fichiers. Pour obtenir le fichier avec vos données, exporter à partir des paramètres.", + "openLocalFileDontShow": "Ne plus afficher", + "openWrongFile": "Fichier invalide", + "openWrongFileBody": "Ce format de fichier n'est pas supporté. Cette application fonctionne avec des fichiers de base de donnée KeePass (kdbx).", + "openKdbFileBody": "Vous essayez d'ouvrir un ancien fichier de base de donnée KeePass (KDB). Cette application supporte uniquement le nouveaux format (kdbx), merci d'utiliser KeePass 2 afin de convertir celui-ci.", + "openConfigHeader": "{} Paramètres", + "openUrl": "URL", + "openUrlDesc": "https://serveur/chemin/fichier.kdbx, ou seulement fichier.kdbx", + "openUser": "Nom d'utilisateur", + "openUserDesc": "Nom d'utilisateur du serveur WebDAV (si requis)", + "openUserPlaceholder": "aucun nom d'utilisateur", + "openPass": "Mot de passe", + "openPassDesc": "Mot de passe du serveur WebDAV (ce n'est pas votre mot de passe de fichier)", + "openPassPlaceholder": "aucun mot de passe", + "openConfigError": "Erreur: {}", + "openConfigErrorNotFound": "Aucun fichier trouvé", + "openError": "Erreur", + "openErrorDescription": "Une erreur est survenue à l'ouverture du fichier", + "openErrorFileNotFound": "Fichier non trouvé", + "openListErrorBody": "Erreur au chargement de la liste des fichiers", + "openShowAllFiles": "Tous les fichiers", + "detAttDownload": "Cliquer sur le bouton de pièce-jointe en pressant la touche Maj ou", + "detAttDelToRemove": "Supprimer pour enlever", + "detEmpty": "Vos mot de passe seront affichés ici", + "detGroupRestore": "Pour restaurer ce groupe, veuillez le glisser-déposer dans un des groupes hors de la corbeille", + "detHistoryClickPoint": "Cliquer sur une entrée de l'historique pour voir son état", + "detHistoryReturn": "Retour vers l'entrée", + "detHistoryRevert": "Revenir à cet état", + "detHistoryDel": "Supprimer cet état", + "detHistoryEmpty": "vide", + "detHistoryModified": "modifié", + "detHistoryRec": "archive", + "detHistoryRecs": "archives", + "detHistoryVersion": "Version", + "detHistorySaved": "Sauvegardé", + "detHistoryNoTitle": "sans titre", + "detHistoryCurState": "état actuel", + "detHistoryCurUnsavedState": "état actuel non sauvegardé", + "detHistoryRevertAlert": "Revenir à cet état de l'historique ?", + "detHistoryRevertAlertBody": "L'état courant sera sauvegardé dans l'historique.", + "detHistoryDeleteAlert": "Supprimer cet état de l'historique ?", + "detHistoryDeleteAlertBody": "Vous ne pourrez plus le restaurer.", + "detHistoryDiscardChangesAlert": "Oublier les changements fait à cette entrée ?", + "detHistoryDiscardChangesAlertBody": "Les changements non sauvegardés seront définitivement perdus.", + "detBackToList": "retour vers liste", + "detSetIconColor": "Changer de couleur", + "detSetIcon": "Changer d'icône", + "detDropAttachments": "Déposer une pièce-jointe ici", + "detDelEntry": "Supprimer", + "detDelEntryPerm": "Supprimer définitivement", + "detExpires": "Expire", + "detExpired": "expirée", + "detGroup": "Groupe", + "detCreated": "Créé le", + "detUpdated": "Mis à jour", + "detNetField": "Nouveau champ", + "detAttachments": "Pièces-jointes", + "detDelFromTrash": "Supprimer de la Corbeille ?", + "detDelFromTrashBody": "Vous ne pourrez plus le récupérer.", + "detDelFromTrashBodyHint": "Pour supprimer tous les éléments de la Corbeille, cliquez l’icône \"Vider la corbeille\" du menu Corbeille.", + "detFieldCopied": "Copié", + "detFieldCopiedTime": "Copié pendant {} secondes", + "detCopyHint": "Vous pouvez copier la valeur du champ en cliquant sur son titre", + "detMore": "plus", + "detClickToAddField": "cliquez pour ajouter un nouveau champ", + "detMenuAddNewField": "Ajouter nouveau champ", + "detMenuShowEmpty": "Montrer les champs vides", + "detMenuHideEmpty": "Cacher les champs vides", + "detMenuAddField": "Ajouter {}", + "detMenuCopyPassword": "Copier mot de passe", + "detMenuCopyUser": "Copier nom", + "detSetupOtp": "Mots de passe à usage unique", + "detClone": "Dupliquer", + "detClonedName": "Copier", + "detAutoType": "Saisie auto", + "detAutoTypeSettings": "Paramètres saisie auto", + "detAutoTypeEnabled": "Activer la saisie automatique pour cette entrée", + "detAutoTypeSequence": "Frappes", + "detAutoTypeInput": "Entrée", + "detAutoTypeShortcutsDesc": "{} ou {} tant que l'appli est inactive", + "detAutoTypeObfuscation": "Mélanger touches réelles et aléatoire", + "detAutoTypeWindow": "Fenêtre", + "detAutoTypeInputWindow": "Titre de fenêtre", + "detSetupOtpAlert": "Scanner le code QR", + "detSetupOtpAlertBody": "Veuillez copier le code QR qui est affiché sur la page d'autorisation", + "detSetupOtpAlertBody1": "1. aller sur la page d'autorisation", + "detSetupOtpAlertBody2": "2. faire une capture d'écran du code QR {}", + "detSetupOtpAlertBody3": "3. coller la ici {}", + "detSetupOtpAlertBody3Mobile": "3. Selectionner ou scanner avec votre appareil photo en utilisant Selectionner/Scan ci-dessous", + "detSetupOtpAlertBody4": "Si vous ne pouvez pas scanner le code, cliquer sur Entrer le code manuellement", + "detSetupOtpManualButton": "Entrer le code manuellement", + "detSetupOtpScanButton": "Selectionner/Scan", + "detSetupOtpAlertBodyWith": "avec {}", + "detOtpImageError": "Erreur de lecture de l'image", + "detOtpImageErrorBody": "Désolé, le format de l'image ne peut pas être lu, merci de contacter les auteurs de l'application avec le détail de l'erreur.", + "detOtpImageReading": "Lecture du code QR...", + "detOtpQrError": "Erreur lecture du code QR", + "detOtpQrErrorBody": "Désolé le QR code ne peut pas être lu, essayez à nouveau ou contacter les auteurs de l'application avec le détail de l'erreur.", + "detOtpQrWrong": "Code QR incorrect", + "detOtpQrWrongBody": "Votre QR code a été scanné avec succès mais il ne contient pas d'information pour un mot de passe à usage unique.", + "detLockField": "Verrouiller ce champ, ainsi son contenu ne sera pas visible ni ne pourra être cherché. Visualiser son contenu exigera de le cliquer explicitement.", + "detUnlockField": "Déverrouiller ce champ, rendant son contenu immédiatement visible et pouvant être cherché", + "autoTypeEntryFields": "Champs", + "autoTypeModifiers": "Touches modificatrices", + "autoTypeKeys": "Clés", + "autoTypeLink": "plus...", + "autoTypeError": "Erreur saisie auto", + "autoTypeErrorGeneric": "Il y a eu une erreur lors de la saisie automatique : {}", + "autoTypeErrorGlobal": "Pour utiliser le raccourci, sélectionner dans l'application là où vous voulez saisir le mot de passe", + "autoTypeErrorNotInstalled": "{} n'est pas installé", + "autoTypeHeader": "Saisie auto : Selectionner", + "autoTypeMsgNoWindow": "Nous n'avons pas pu obtenir le titre de la fenêtre active, commencez à taper pour rechercher", + "autoTypeMsgMatchedByWindow": "Sélectionner un mot de passe pour {}", + "autoTypeNoMatches": "aucun", + "autoTypeSelectionHint": "Saisissez la séquence automatique", + "autoTypeSelectionHintAction": "Entrez seulement le mot de passe", + "autoTypeSelectionHintOpt": "Entrez seulement le compte", + "appSecWarn": "Non sécurisé !", + "appSecWarnBody1": "Vous avez chargé cette appli avec une connexion non sécurisée. Quelqu'un peut vous observer et voler vos mots de passe. Nous vous recommandons fortement d'arrêter cette connexion, à moins que vous ne compreniez exactement ce que vous faites.", + "appSecWarnBody2": "Oui, votre base est chiffrée mais personne ne peut garantir que l'application n'a pas été modifiée avant d'arriver à vous.", + "appSecWarnBtn": "Je comprends les risques, continuer", + "appUnsavedWarn": "Modifications non sauvegardées", + "appUnsavedWarnBody": "Vous avez des fichiers non sauvegardés, si vous fermez l'appli, les changements seront perdus.", + "appDontExitBtn": "Ne pas quitter", + "appCannotLockAutoInit": "L'appli ne peut pas être verrouillée car la sauvegarde automatique est désactivée.", + "appCannotLock": "Vous avez des modifications non sauvegardées qui seront perdus. Continuer ?", + "appAutoSave": "Sauver les modifications automatiquement", + "appSaveError": "Sauver l'erreur", + "appSaveErrorBody": "Echec sauvegarde auto du fichier", + "appSaveErrorBodyMul": "Echec sauvegarde auto des fichiers", + "appSettingsError": "Erreur lancement appli", + "appSettingsErrorBody": "Il y a eu une erreur au chargement des paramètres. Merci de revérifier l'URL de l'application ou contacter votre administrateur.", + "appNotSupportedError": "Votre navigateur n'est pas supporté.", + "appTabWarn": "Trop d'onglets", + "appTabWarnBody": "KeeWeb ne peut être utilisé dans 2 onglets de navigateurs simultanément. Fermer cet onglet, s'il vous plait.", + "appRightsAlert": "Protéger l'appli KeeWeb", + "appRightsAlertBody1": "Votre application KeeWeb est accessible en écriture. Nous demandons les droits administrateurs pour le mettre en écriture uniquement pour les administrateurs.", + "appRightsAlertBody2": "Vous ne voulez pas donner les permissions ? Vous pouvez le faire vous-même à partir d'un terminal", + "setGenTitle": "Paramètres", + "setGenUpdate": "Mettre à jour", + "setGenNewVersion": "Une nouvelle version a été téléchargée", + "setGenReleaseNotes": "Voir les notes de version", + "setGenReloadToUpdate": "Recharger pour mettre à jour", + "setGenUpdateManual": "Une nouvelle version est disponible. Elle vérifiera les mises à jour et les installera automatiquement, mais une mise à jour automatique à partir de votre version n'est pas possible.", + "setGenDownloadUpdate": "Télécharger mise à jour", + "setGenUpdateAuto": "Télécharger et installer automatiquement", + "setGenUpdateCheck": "Vérifier mais ne pas installer", + "setGenNoUpdate": "Ne jamais vérifier les mises à jour", + "setGenUpdateChecking": "Vérification des mises à jour", + "setGenCheckUpdate": "Vérifier les mises à jour", + "setGenErrorChecking": "Erreur vérification des mises à jour", + "setGenLastCheckSuccess": "Dernière vérification réussie fut le {}", + "setGenLastCheckVer": "la dernière version était {}", + "setGenCheckedAt": "Vérifié le", + "setGenLatestVer": "vous utilisez la dernière version", + "setGenNewVer": "nouvelle version {} disponible", + "setGenDownloadingUpdate": "Téléchargement de la mise à jour...", + "setGenExtractingUpdate": "Décompression de la mise à jour...", + "setGenCheckErr": "Une erreur est intervenue durant le téléchargement de la mise à jour", + "setGenNeverChecked": "Ne jamais vérifier les mises à jour", + "setGenRestartToUpdate": "Redémarrer pour mettre à jour", + "setGenDownloadAndRestart": "Télécharger la mise à jour et redémarrer", + "setGenAppearance": "Apparence", + "setGenTheme": "Thème", + "setGenThemeFb": "Bleu plat", + "setGenThemeDb": "Marron foncé", + "setGenThemeWh": "Blanc", + "setGenThemeTe": "Terminal", + "setGenThemeHc": "Contraste élevé", + "setGenThemeSd": "Solarized dark", + "setGenThemeSl": "Solarized light", + "setGenLocale": "Langue", + "setGenLocOther": "d'autres langues sont disponibles en tant que modules", + "setGenFontSize": "Taille de police", + "setGenFontSizeNormal": "Normal", + "setGenFontSizeLarge": "Large", + "setGenFontSizeLargest": "Très large", + "setGenTitlebarStyle": "Style de fenêtres (redémarrage requis)", + "setGenTitlebarStyleDefault": "Par défaut", + "setGenTitlebarStyleHidden": "Titre modifié", + "setGenTitlebarStyleHiddenInset": "Titre modifié, fénêtre déplaçable", + "setGenShowSubgroups": "Afficher les entrées de tous les sous groupes", + "setGenTableView": "Voir les entrées en liste", + "setGenColorfulIcons": "Icônes en couleur dans la liste", + "setGenFunction": "Opérations", + "setGenAutoSyncOnClose": "Sauvegarde et synchro automatique à la fermeture", + "setGenAutoSyncTimer": "Sauvegarde et synchro automatique régulièrement", + "setGenAutoSyncTimerOff": "Désactivé", + "setGenAutoSyncTimerInterval": "Toutes les {} minute(s)", + "setGenRememberKeyFiles": "Se souvenir des fichiers clé", + "setGenNoRememberKeyFiles": "Ne pas se souvenir", + "setGenRememberKeyFilesData": "Sauvegarder dans le stockage interne de l'appli", + "setGenRememberKeyFilesPath": "Se souvenir seulement des chemins des fichiers", + "setGenLockInactive": "si l'app est inactive", + "setGenNoAutoLock": "Ne pas verrouiller automatiquement", + "setGenLockMinutes": "Dans {} minutes", + "setGenLockHour": "Dans une heure", + "setGenLockHours": "Dans {} heures", + "setGenLockDay": "Dans 1 jour", + "setGenClearClip": "Vider le presse-papier après une copie", + "setGenNoClear": "Ne pas effacer", + "setGenClearSeconds": "Dans {} secondes", + "setGenClearMinute": "Dans une minute", + "setGenMinInstead": "Réduire l'application au lieu de fermer", + "setGenLock": "Verrou Auto", + "setGenLockMinimize": "quand l'appli est minimisée", + "setGenLockCopy": "sur copie de m.d.p.", + "setGenLockAutoType": "sur saisie auto", + "setGenLockOrSleep": "Quand l'ordinateur est verrouillé ou mis en sommeil", + "setGenStorage": "Stockage", + "setGenShowAdvanced": "Afficher les paramètres avancés", + "setGenDevTools": "Afficher les outils de développement", + "setGenTryBeta": "Essayer une fois la version beta ", + "setGenTryBetaWarning": "Fichiers non sauvegardés", + "setGenTryBetaWarningBody": "Sauvegarder tous les fichiers et re-cliquer ce bouton", + "setGenShowAppLogs": "Voir les logs", + "setFilePath": "Chemin", + "setFileStorage": "Le fichier est ouvert de {}.", + "setFileIntl": "Le fichier est stocké en interne de l'appli", + "setFileLocalHint": "Voulez-vous travailler avec des fichiers locaux ?", + "setFileDownloadApp": "Télécharger l'application", + "setFileSave": "Enregistrer", + "setFileSaveTo": "Enregistrer sous", + "setFileClose": "Fermer", + "setFileSync": "Sync", + "setFileSyncVerb": "Synchroniser", + "setFileSaveToXml": "XML", + "setFileLastSync": "Dernière synchro", + "setFileLastSyncUnknown": "inconnu", + "setFileSyncInProgress": "synchronisation en cours", + "setFileSyncError": "Sync erreur", + "setFilePass": "Mot de passe principal", + "setFileConfirmPass": "Confirmer le mot de passe Maître", + "setFilePassChange": "pour changer le mot de passe, merci de le saisir dans le champ Confirmation", + "setFilePassChanged": "Le mot de passe a été changé ; laissez le champ vide pour utiliser l'ancien mot de passe", + "setFilePassNotMatch": "les mots de passe ne correspondent pas, veuillez réessayer", + "setFileKeyFile": "Fichier clé", + "setFileSelKeyFile": "Sélectionner fichier clé", + "setFileNames": "Noms", + "setFileDefUser": "Nom par défaut", + "setFileEnableTrash": "Activer corbeille", + "setFileHistLen": "Longueur de l'historique, conserve les derniers enregistrements", + "setFileHistSize": "Taille de l'historique, Mo par fichier", + "setFileBackups": "Sauvegardes", + "setFileBackupEnable": "Sauvegarder ce fichier", + "setFileBackupPath": "Chemin", + "setFileBackupTime": "Lancer sauvegardes ", + "setFileBackupNow": "Sauvegarder...", + "setFileBackupNowWorking": "En cours...", + "setFileBackupError": "Erreur sauvegarde", + "setFileBackupErrorDescription": "Erreur à l'écriture du fichier", + "setFileBackupErrorIsDir": "Chemin de sauvegarde invalide", + "setFileBackupErrorIsDirDescription": "Le chemin de sauvegarde semble pointer sur un répertoire. Merci de spécifier un fichier dans un répertoire à la place.", + "setFileBackupOnSave": "A chaque fois que je sauvegarde", + "setFileBackupDaily": "Quotidien", + "setFileBackupWeekly": "Chaque semaine", + "setFileBackupMonthly": "Chaque mois", + "setFileBackupManually": "Manuellement, pas de sauvegarde automatique", + "setFileRounds": "Cycles de chiffrement de la clé", + "setFileKdfParams": "Fonction de dérivation de clé", + "setFileKdfParamsIter": "Itérations", + "setFileKdfParamsMem": "Mémoire, Ko", + "setFileKdfParamsPar": "Parallèlisme", + "setFileKeyChangeForce": "Demander à changer la clé après (jours)", + "setFileUseKeyFile": "Utiliser fichier clé", + "setFileUseGenKeyFile": "Utiliser fichier clé généré", + "setFileUseOldKeyFile": "Utiliser ancien fichier clé", + "setFileGenKeyFile": "Générer nouveau fichier clé", + "setFileDontUseKeyFile": "Ne pas utiliser fichier clé", + "setFileEmptyPass": "Mot de passe vide", + "setFileEmptyPassBody": "Sauvegarder la base de données avec un mode de passe vide la laisse sans aucune protection. Voulez-vous vraiment faire cela ?", + "setFileSaveError": "Erreur sauvegarde", + "setFileSaveErrorBody": "Erreur sauvegarde dans le fichier", + "setFileAlreadyExists": "Existe déjà", + "setFileAlreadyExistsBody": "Le fichier {} existe déjà. L'écraser ?", + "setFileUnsaved": "Modif non sauvegardées", + "setFileUnsavedBody": "Il y a des modifications non sauvegardées dans ce fichier", + "setFileCloseNoSave": "Fermer et perdre les modifs", + "setFileDontClose": "Ne pas fermer", + "setShTitle": "Raccourcis", + "setShShowAll": "montrer tout", + "setShColors": "montrer les champs en couleur", + "setShTrash": "aller à Corbeille", + "setShFind": "rechercher ou juste commencer à taper", + "setShClearSearch": "effacer recherche", + "setShCopyPass": "copier le mot de passe ou le champ sélectionné", + "setShCopyUser": "copier le nom", + "setShCopyUrl": "copier le site", + "setShAutoType": "saisie automatique pour cette entrée", + "setShPrev": "aller au précédent", + "setShNext": "aller au prochain", + "setShCreateEntry": "créer une entrée", + "setShOpen": "ouvrir/nouveau", + "setShSave": "sauver tous les fichiers", + "setShGen": "générer un mot de passe", + "setShSet": "paramètres", + "setShCopyPassGlobal": "copier le mot de passe (quand l'appli est en arrière-plan)", + "setShCopyUserGlobal": "copier l'utilisateur (quand l'appli est en arrière-plan)", + "setShCopyUrlGlobal": "copier le site web (quand l'appli est en arrière-plan)", + "setShAutoTypeGlobal": "saisie automatique (quand l'appli est en arrière-plan)", + "setShLock": "verrouiller la base", + "setPlInstallTitle": "Installer de nouveaux modules", + "setPlInstallDesc": "Les modules (plugins) de KeeWeb ajoutent des fonctionnalités, thèmes et langues à KeeWeb. Les modules sont lancés avec les même privilèges que KeeWeb : ils peuvent accéder et gérer tous vos mots de passe. Ne jamais installer de modules auxquels vous ne faites pas confiance.", + "setPlInstallLabel": "URL du module", + "setPlInstallBtn": "Installer", + "setPlInstallBtnProgress": "Installation", + "setPlUninstallBtn": "Désintaller", + "setPlDisableBtn": "Désactiver", + "setPlEnableBtn": "Activer", + "setPlUpdateBtn": "Mettre à jour", + "setPlLocaleBtn": "Changer pour cette langue", + "setPlThemeBtn": "Changer pour ce thème", + "setPlJs": "code", + "setPlCss": "styles", + "setPlLoc": "langue", + "setPlCreatedBy": "Créé par {}", + "setPlLoadTime": "{} pour charger", + "setPlLastUpdate": "Dernière vérification des mises à jour", + "setPlLoadError": "erreur au chargement du module", + "setPlGalleryLoading": "Chargement des modules, patientez un instant", + "setPlGalleryLoadError": "erreur au chargement des modules", + "setPlInstallUrlTitle": "Ajouter un module à partir d'une URL", + "setPlInstallUrlDesc": "Si un module n'est pas dans le catalogue, vous pouvez l'installer manuellement à partir d'une URL.", + "setPlOfficial": "Module officiel KeeWeb", + "setPlSearch": "Rechercher des modules", + "setPlDevelop": "Vous souhaitez développer vos propres modules ?", + "setPlDevelopStart": "Commencez ici", + "setPlTranslate": "Ou vous pouvez {}", + "setPlTranslateLink": "traduire l'appli dans votre langue", + "setPlAutoUpdate": "Mise à jour automatique", + "setPlLoadGallery": "Charger le catalogue", + "setAboutTitle": "A propos", + "setAboutBuilt": "Cette appli est construite avec ces outils géniaux", + "setAboutLic": "Licence", + "setAboutLicComment": "Cette application et tous les composants qu'elle contient qui ne sont pas dans le domaine public sont sous licence MIT", + "setAboutFirst": "Ceci est une application open-source créée par {}", + "setAboutSecond": " et sous licence {}.", + "setAboutSource": "Le code source et les problèmes sont sur {}.", + "setHelpFormat": "Format de fichier", + "setHelpFormatBody": "Ceci est un portage de l'appli {} développée avec les technologies web. Il interprète les fichiers au format KeePass (kdbx). Vous pouvez créer de tels fichiers (bases de données de mots de passe) soit avec KeePass, soit avec cette appli. Le format de fichier est 100% compatible et devrait être compris par les 2 applications.", + "setHelpProblems": "Un problème ?", + "setHelpProblems1": "Si quelque chose ne va pas, merci de {}", + "setHelpProblems2": "ou {}", + "setHelpOpenIssue": "faire connaître le problème sur GitHub", + "setHelpContactLink": "contacter directement un développeur", + "setHelpAppInfo": "Information", + "setHelpOtherPlatforms": "Autres plateformes", + "setHelpDesktopApps": "Applis Desktop", + "setHelpWebApp": "Appli web", + "setHelpUpdates": "Mises à jour", + "setHelpTwitter": "Appli twitter", + "dropboxSetupDesc": "Une configuration particulière est nécessaire pour utiliser Dropbox dans une application auto-hébergée. Merci de créer votre propre application Dropbox et d'inscrire sa clé ci-dessous.", + "dropboxAppKey": "Clé Dropbox", + "dropboxAppKeyDesc": "Copier la clé de votre appli Dropbox (Réglages développeur)", + "dropboxFolder": "Dossier Appli", + "dropboxFolderDesc": "Si votre appli est lié à Dropbox (pas un dossier), définir le dossier avec vos fichiers Kdbx içi", + "dropboxFolderSettingsDesc": "Sélectionner un dossier dans votre Dropbox où les fichiers sont stockés (à la racine par défaut)", + "dropboxFolderPlaceholder": "dossier par défaut", + "dropboxLink": "Lier l'appli à", + "dropboxLinkApp": "Dossier de l'appli (Apps/KeeWeb)", + "dropboxLinkFull": "Tout Dropbox ou n'importe quel dossier", + "dropboxLinkCustom": "Votre appli Dropbox", + "gdriveSharedWithMe": "Partagé avec moi", + "webdavSaveMethod": "Méthode de sauvegarde", + "webdavSaveMove": "Uploader un fichier temporaire et le déplacer", + "webdavSavePut": "Ecraser le fichier kdbx avec PUT", + "launcherSave": "Sauvegarder base des mots de passe", + "launcherFileFilter": "Fichiers Keepass", + "authPopupRequired": "Les pop-up sont bloquées", + "authPopupRequiredBody": "Merci d'autoriser les popups pour cette appli dans votre navigateur ou essayez à nouveau." +} diff --git a/app/scripts/mixins/copyable.js b/app/scripts/mixins/copyable.js index 555157e7..934bb75c 100644 --- a/app/scripts/mixins/copyable.js +++ b/app/scripts/mixins/copyable.js @@ -18,7 +18,9 @@ const Copyable = { this.hideFieldCopyTip(); const fieldLabel = e.source.labelEl; const clipboardTime = e.copyRes.seconds; - const msg = clipboardTime ? Locale.detFieldCopiedTime.replace('{}', clipboardTime) : Locale.detFieldCopied; + const msg = clipboardTime + ? Locale.detFieldCopiedTime.replace('{}', clipboardTime) + : Locale.detFieldCopied; let tip; if (!this.isHidden()) { tip = Tip.createTip(fieldLabel[0], { @@ -36,7 +38,10 @@ const Copyable = { tip.hide(); } this.fieldCopyTip = null; - if (e.source.model.name === '$Password' && AppSettingsModel.instance.get('lockOnCopy')) { + if ( + e.source.model.name === '$Password' && + AppSettingsModel.instance.get('lockOnCopy') + ) { setTimeout(() => { Backbone.trigger('lock-workspace'); }, Timeouts.BeforeAutoLock); diff --git a/app/scripts/models/app-model.js b/app/scripts/models/app-model.js index 83c45937..ff6961fd 100644 --- a/app/scripts/models/app-model.js +++ b/app/scripts/models/app-model.js @@ -84,7 +84,11 @@ const AppModel = Backbone.Model.extend({ this.appLogger.error('Invalid app config, no settings section', response); return reject('Invalid app config, no settings section'); } - this.appLogger.info('Loaded app config from', configLocation, this.appLogger.ts(ts)); + this.appLogger.info( + 'Loaded app config from', + configLocation, + this.appLogger.ts(ts) + ); resolve(response); }); xhr.addEventListener('error', () => { @@ -197,7 +201,13 @@ const AppModel = Backbone.Model.extend({ this.menu.tagsSection.set('scrollable', true); this.menu.tagsSection.setItems( this.tags.map(tag => { - return { title: tag, icon: 'tag', filterKey: 'tag', filterValue: tag, editable: true }; + return { + title: tag, + icon: 'tag', + filterKey: 'tag', + filterValue: tag, + editable: true + }; }) ); } else { @@ -341,12 +351,15 @@ const AppModel = Backbone.Model.extend({ completeUserNames: function(part) { const userNames = {}; this.files.forEach(file => { - file.forEachEntry({ text: part, textLower: part.toLowerCase(), advanced: { user: true } }, entry => { - const userName = entry.user; - if (userName) { - userNames[userName] = (userNames[userName] || 0) + 1; + file.forEachEntry( + { text: part, textLower: part.toLowerCase(), advanced: { user: true } }, + entry => { + const userName = entry.user; + if (userName) { + userNames[userName] = (userNames[userName] || 0) + 1; + } } - }); + ); }); const matches = _.pairs(userNames); matches.sort((x, y) => y[1] - x[1]); @@ -483,11 +496,20 @@ const AppModel = Backbone.Model.extend({ if (cacheRev && storage.stat) { logger.info('Stat file'); storage.stat(params.path, params.opts, (err, stat) => { - if (fileInfo && storage.name !== 'file' && (err || (stat && stat.rev === cacheRev))) { - logger.info('Open file from cache because ' + (err ? 'stat error' : 'it is latest'), err); + if ( + fileInfo && + storage.name !== 'file' && + (err || (stat && stat.rev === cacheRev)) + ) { + logger.info( + 'Open file from cache because ' + (err ? 'stat error' : 'it is latest'), + err + ); this.openFileFromCache(params, callback, fileInfo); } else if (stat) { - logger.info('Open file from storage (' + stat.rev + ', local ' + cacheRev + ')'); + logger.info( + 'Open file from storage (' + stat.rev + ', local ' + cacheRev + ')' + ); storageLoad(); } else { logger.info('Stat error', err); @@ -534,7 +556,10 @@ const AppModel = Backbone.Model.extend({ params.keyFileName = fileInfo.get('keyFileName'); if (this.settings.get('rememberKeyFiles') === 'data') { params.keyFileData = FileModel.createKeyFileWithHash(fileInfo.get('keyFileHash')); - } else if (this.settings.get('rememberKeyFiles') === 'path' && fileInfo.get('keyFilePath')) { + } else if ( + this.settings.get('rememberKeyFiles') === 'path' && + fileInfo.get('keyFilePath') + ) { params.keyFilePath = fileInfo.get('keyFilePath'); if (Storage.file.enabled) { needLoadKeyFile = true; @@ -731,7 +756,11 @@ const AppModel = Backbone.Model.extend({ const storage = options.storage || file.get('storage'); let path = options.path || file.get('path'); const opts = options.opts || file.get('opts'); - if (storage && Storage[storage].getPathForName && (!path || storage !== file.get('storage'))) { + if ( + storage && + Storage[storage].getPathForName && + (!path || storage !== file.get('storage')) + ) { path = Storage[storage].getPathForName(file.get('name')); } const optionsForLogging = _.clone(options); @@ -856,7 +885,8 @@ const AppModel = Backbone.Model.extend({ }; const saveToStorage = data => { logger.info('Save data to storage'); - const storageRev = fileInfo.get('storage') === storage ? fileInfo.get('rev') : undefined; + const storageRev = + fileInfo.get('storage') === storage ? fileInfo.get('rev') : undefined; Storage[storage].save( path, opts, @@ -927,7 +957,10 @@ const AppModel = Backbone.Model.extend({ if (!e) { file.set('dirty', false); } - logger.info('Saved to cache, exit with error', err || 'no error'); + logger.info( + 'Saved to cache, exit with error', + err || 'no error' + ); complete(err); }); } diff --git a/app/scripts/models/entry-model.js b/app/scripts/models/entry-model.js index 9a0df724..67542ed5 100644 --- a/app/scripts/models/entry-model.js +++ b/app/scripts/models/entry-model.js @@ -114,7 +114,9 @@ const EntryModel = Backbone.Model.extend({ this.customIcon = null; this.customIconId = null; if (this.entry.customIcon) { - this.customIcon = IconUrl.toDataUrl(this.file.db.meta.customIcons[this.entry.customIcon]); + this.customIcon = IconUrl.toDataUrl( + this.file.db.meta.customIcons[this.entry.customIcon] + ); this.customIconId = this.entry.customIcon.toString(); } }, @@ -130,7 +132,8 @@ const EntryModel = Backbone.Model.extend({ _buildAutoType: function() { this.autoTypeEnabled = this.entry.autoType.enabled; this.autoTypeObfuscation = - this.entry.autoType.obfuscation === kdbxweb.Consts.AutoTypeObfuscationOptions.UseClipboard; + this.entry.autoType.obfuscation === + kdbxweb.Consts.AutoTypeObfuscationOptions.UseClipboard; this.autoTypeSequence = this.entry.autoType.defaultSequence; this.autoTypeWindows = this.entry.autoType.items.map(this._convertAutoTypeItem); }, @@ -201,8 +204,12 @@ const EntryModel = Backbone.Model.extend({ !filter || ((!filter.tagLower || this.searchTags.indexOf(filter.tagLower) >= 0) && (!filter.textLower || - (filter.advanced ? this.matchesAdv(filter) : this.searchText.indexOf(filter.textLower) >= 0)) && - (!filter.color || (filter.color === true && this.searchColor) || this.searchColor === filter.color) && + (filter.advanced + ? this.matchesAdv(filter) + : this.searchText.indexOf(filter.textLower) >= 0)) && + (!filter.color || + (filter.color === true && this.searchColor) || + this.searchColor === filter.color) && (!filter.autoType || this.autoTypeEnabled)) ); }, @@ -701,8 +708,10 @@ const EntryModel = Backbone.Model.extend({ _.forEach(ranking, rankingEntry => { if (this._getFieldString(rankingEntry.field).toLowerCase() !== '') { const calculatedRank = - Ranking.getStringRank(searchString, this._getFieldString(rankingEntry.field).toLowerCase()) * - rankingEntry.multiplicator; + Ranking.getStringRank( + searchString, + this._getFieldString(rankingEntry.field).toLowerCase() + ) * rankingEntry.multiplicator; rank += calculatedRank; } }); diff --git a/app/scripts/models/file-model.js b/app/scripts/models/file-model.js index ad1cc829..8f6bacfa 100644 --- a/app/scripts/models/file-model.js +++ b/app/scripts/models/file-model.js @@ -71,8 +71,14 @@ const FileModel = Backbone.Model.extend({ callback(); }) .catch(err => { - if (err.code === kdbxweb.Consts.ErrorCodes.InvalidKey && password && !password.byteLength) { - logger.info('Error opening file with empty password, try to open with null password'); + if ( + err.code === kdbxweb.Consts.ErrorCodes.InvalidKey && + password && + !password.byteLength + ) { + logger.info( + 'Error opening file with empty password, try to open with null password' + ); return this.open(null, fileData, keyFileData, callback); } logger.error('Error opening file', err.code, err.message, err); @@ -139,7 +145,9 @@ const FileModel = Backbone.Model.extend({ openDemo: function(callback) { const password = kdbxweb.ProtectedValue.fromString('demo'); const credentials = new kdbxweb.Credentials(password); - const demoFile = kdbxweb.ByteUtils.arrayToBuffer(kdbxweb.ByteUtils.base64ToBytes(demoFileData)); + const demoFile = kdbxweb.ByteUtils.arrayToBuffer( + kdbxweb.ByteUtils.base64ToBytes(demoFileData) + ); kdbxweb.Kdbx.load(demoFile, credentials).then(db => { this.db = db; this.set('name', 'Demo'); @@ -334,7 +342,9 @@ const FileModel = Backbone.Model.extend({ forEachEntry: function(filter, callback) { let top = this; if (filter.trash) { - top = this.getGroup(this.db.meta.recycleBinUuid ? this.subId(this.db.meta.recycleBinUuid.id) : null); + top = this.getGroup( + this.db.meta.recycleBinUuid ? this.subId(this.db.meta.recycleBinUuid.id) : null + ); } else if (filter.group) { top = this.getGroup(filter.group); } @@ -359,11 +369,15 @@ const FileModel = Backbone.Model.extend({ }, getTrashGroup: function() { - return this.db.meta.recycleBinEnabled ? this.getGroup(this.subId(this.db.meta.recycleBinUuid.id)) : null; + return this.db.meta.recycleBinEnabled + ? this.getGroup(this.subId(this.db.meta.recycleBinUuid.id)) + : null; }, getEntryTemplatesGroup: function() { - return this.db.meta.entryTemplatesGroup ? this.getGroup(this.subId(this.db.meta.entryTemplatesGroup.id)) : null; + return this.db.meta.entryTemplatesGroup + ? this.getGroup(this.subId(this.db.meta.entryTemplatesGroup.id)) + : null; }, createEntryTemplatesGroup: function() { @@ -618,7 +632,9 @@ const FileModel = Backbone.Model.extend({ addCustomIcon: function(iconData) { const uuid = kdbxweb.KdbxUuid.random(); - this.db.meta.customIcons[uuid] = kdbxweb.ByteUtils.arrayToBuffer(kdbxweb.ByteUtils.base64ToBytes(iconData)); + this.db.meta.customIcons[uuid] = kdbxweb.ByteUtils.arrayToBuffer( + kdbxweb.ByteUtils.base64ToBytes(iconData) + ); return uuid.toString(); }, diff --git a/app/scripts/models/group-model.js b/app/scripts/models/group-model.js index 06cf1f56..5d6a75ef 100644 --- a/app/scripts/models/group-model.js +++ b/app/scripts/models/group-model.js @@ -124,7 +124,8 @@ const GroupModel = MenuItemModel.extend({ let result = true; this.get('items').forEach(group => { if (group.matches(filter)) { - result = callback(group) !== false && group.forEachGroup(callback, filter) !== false; + result = + callback(group) !== false && group.forEachGroup(callback, filter) !== false; } }); return result; @@ -260,7 +261,9 @@ const GroupModel = MenuItemModel.extend({ }, getParentEffectiveAutoTypeSeq: function() { - return this.parentGroup ? this.parentGroup.getEffectiveAutoTypeSeq() : DefaultAutoTypeSequence; + return this.parentGroup + ? this.parentGroup.getEffectiveAutoTypeSeq() + : DefaultAutoTypeSequence; }, isEntryTemplatesGroup: function() { @@ -315,7 +318,12 @@ const GroupModel = MenuItemModel.extend({ }, moveToTop: function(object) { - if (!object || object.id === this.id || object.file !== this.file || !(object instanceof GroupModel)) { + if ( + !object || + object.id === this.id || + object.file !== this.file || + !(object instanceof GroupModel) + ) { return; } this.file.setModified(); diff --git a/app/scripts/models/menu/menu-model.js b/app/scripts/models/menu/menu-model.js index 10d246c1..41d1e029 100644 --- a/app/scripts/models/menu/menu-model.js +++ b/app/scripts/models/menu/menu-model.js @@ -17,7 +17,13 @@ const MenuModel = Backbone.Model.extend({ initialize: function() { this.menus = {}; this.allItemsSection = new MenuSectionModel([ - { locTitle: 'menuAllItems', icon: 'th-large', active: true, shortcut: Keys.DOM_VK_A, filterKey: '*' } + { + locTitle: 'menuAllItems', + icon: 'th-large', + active: true, + shortcut: Keys.DOM_VK_A, + filterKey: '*' + } ]); this.allItemsItem = this.allItemsSection.get('items').models[0]; this.groupsSection = new GroupsMenuModel(); @@ -49,7 +55,11 @@ const MenuModel = Backbone.Model.extend({ Colors.AllColors.forEach(color => { this.colorsSection .get('items') - .models[0].addOption({ cls: 'fa ' + color + '-color', value: color, filterValue: color }); + .models[0].addOption({ + cls: 'fa ' + color + '-color', + value: color, + filterValue: color + }); }); this.menus.app = new MenuSectionCollection([ this.allItemsSection, @@ -65,9 +75,15 @@ const MenuModel = Backbone.Model.extend({ this.shortcutsSection = new MenuSectionModel([ { locTitle: 'shortcuts', icon: 'keyboard-o', page: 'shortcuts' } ]); - this.pluginsSection = new MenuSectionModel([{ locTitle: 'plugins', icon: 'puzzle-piece', page: 'plugins' }]); - this.aboutSection = new MenuSectionModel([{ locTitle: 'menuSetAbout', icon: 'info', page: 'about' }]); - this.helpSection = new MenuSectionModel([{ locTitle: 'help', icon: 'question', page: 'help' }]); + this.pluginsSection = new MenuSectionModel([ + { locTitle: 'plugins', icon: 'puzzle-piece', page: 'plugins' } + ]); + this.aboutSection = new MenuSectionModel([ + { locTitle: 'menuSetAbout', icon: 'info', page: 'about' } + ]); + this.helpSection = new MenuSectionModel([ + { locTitle: 'help', icon: 'question', page: 'help' } + ]); this.filesSection = new MenuSectionModel(); this.filesSection.set({ scrollable: true, grow: true }); this.menus.settings = new MenuSectionCollection([ @@ -93,7 +109,10 @@ const MenuModel = Backbone.Model.extend({ }, this); if (sections === this.menus.app) { this.colorsItem.get('options').forEach(opt => opt.set('active', opt === sel.option)); - const selColor = sel.item === this.colorsItem && sel.option ? sel.option.get('value') + '-color' : ''; + const selColor = + sel.item === this.colorsItem && sel.option + ? sel.option.get('value') + '-color' + : ''; this.colorsItem.set('cls', 'menu__item-colors ' + selColor); const filterKey = sel.item.get('filterKey'); const filterValue = (sel.option || sel.item).get('filterValue'); @@ -101,7 +120,10 @@ const MenuModel = Backbone.Model.extend({ filter[filterKey] = filterValue; Backbone.trigger('set-filter', filter); } else if (sections === this.menus.settings) { - Backbone.trigger('set-page', { page: sel.item.get('page'), file: sel.item.get('file') }); + Backbone.trigger('set-page', { + page: sel.item.get('page'), + file: sel.item.get('file') + }); } }, @@ -186,7 +208,11 @@ const MenuModel = Backbone.Model.extend({ title: Format.capFirst(Locale.tags), icon: 'tags', defaultItem: true, - disabled: { header: Locale.menuAlertNoTags, body: Locale.menuAlertNoTagsBody, icon: 'tags' } + disabled: { + header: Locale.menuAlertNoTags, + body: Locale.menuAlertNoTagsBody, + icon: 'tags' + } }; }, diff --git a/app/scripts/plugins/plugin-gallery.js b/app/scripts/plugins/plugin-gallery.js index db080fd0..e3f6822e 100644 --- a/app/scripts/plugins/plugin-gallery.js +++ b/app/scripts/plugins/plugin-gallery.js @@ -38,7 +38,10 @@ const PluginGallery = { this.loading = false; this.loadError = !gallery; if (gallery) { - this.logger.debug(`Loaded ${gallery.plugins.length} plugins`, this.logger.ts(ts)); + this.logger.debug( + `Loaded ${gallery.plugins.length} plugins`, + this.logger.ts(ts) + ); this.gallery = gallery; this.saveGallery(gallery); } @@ -50,7 +53,10 @@ const PluginGallery = { verifySignature(gallery) { const dataToVerify = JSON.stringify(gallery, null, 2).replace(gallery.signature, ''); - return SignatureVerifier.verify(kdbxweb.ByteUtils.stringToBytes(dataToVerify), gallery.signature) + return SignatureVerifier.verify( + kdbxweb.ByteUtils.stringToBytes(dataToVerify), + gallery.signature + ) .then(isValid => { if (isValid) { return gallery; diff --git a/app/scripts/plugins/plugin-manager.js b/app/scripts/plugins/plugin-manager.js index 0fb13461..80cbe488 100644 --- a/app/scripts/plugins/plugin-manager.js +++ b/app/scripts/plugins/plugin-manager.js @@ -164,7 +164,9 @@ const PluginManager = Backbone.Model.extend({ return Promise.resolve(); } const anotherVersion = this.get('autoUpdateAppVersion') !== RuntimeInfo.version; - const wasLongAgo = !this.get('autoUpdateDate') || Date.now() - this.get('autoUpdateDate') > this.UpdateInterval; + const wasLongAgo = + !this.get('autoUpdateDate') || + Date.now() - this.get('autoUpdateDate') > this.UpdateInterval; const autoUpdateRequired = anotherVersion || wasLongAgo; if (!autoUpdateRequired) { return; @@ -194,7 +196,9 @@ const PluginManager = Backbone.Model.extend({ }); let enabled = desc.enabled; if (enabled) { - const galleryPlugin = gallery ? gallery.plugins.find(pl => pl.manifest.name === desc.manifest.name) : null; + const galleryPlugin = gallery + ? gallery.plugins.find(pl => pl.manifest.name === desc.manifest.name) + : null; const expectedPublicKey = galleryPlugin ? galleryPlugin.manifest.publicKey : SignatureVerifier.getPublicKey(); diff --git a/app/scripts/plugins/plugin.js b/app/scripts/plugins/plugin.js index b11e16ff..828c2aed 100644 --- a/app/scripts/plugins/plugin.js +++ b/app/scripts/plugins/plugin.js @@ -99,7 +99,12 @@ const Plugin = Backbone.Model.extend( if (manifest.manifestVersion !== '0.1.0') { return 'Invalid manifest version ' + manifest.manifestVersion; } - if (!manifest.author || !manifest.author.email || !manifest.author.name || !manifest.author.url) { + if ( + !manifest.author || + !manifest.author.email || + !manifest.author.name || + !manifest.author.url + ) { return 'Invalid plugin author'; } if (!manifest.url) { @@ -108,7 +113,10 @@ const Plugin = Backbone.Model.extend( if (!manifest.publicKey) { return 'No plugin public key'; } - if (!this.get('skipSignatureValidation') && manifest.publicKey !== SignatureVerifier.getPublicKey()) { + if ( + !this.get('skipSignatureValidation') && + manifest.publicKey !== SignatureVerifier.getPublicKey() + ) { return 'Public key mismatch'; } if (!manifest.resources || !Object.keys(manifest.resources).length) { @@ -116,7 +124,9 @@ const Plugin = Backbone.Model.extend( } if ( manifest.resources.loc && - (!manifest.locale || !manifest.locale.title || !/^[a-z]{2}(-[A-Z]{2})?$/.test(manifest.locale.name)) + (!manifest.locale || + !manifest.locale.title || + !/^[a-z]{2}(-[A-Z]{2})?$/.test(manifest.locale.name)) ) { return 'Bad plugin locale'; } @@ -160,7 +170,9 @@ const Plugin = Backbone.Model.extend( ); this.resources = {}; const ts = this.logger.ts(); - const results = Object.keys(manifest.resources).map(res => this.loadResource(res, local)); + const results = Object.keys(manifest.resources).map(res => + this.loadResource(res, local) + ); return Promise.all(results) .catch(() => { throw 'Error loading plugin resources'; @@ -328,7 +340,10 @@ const Plugin = Backbone.Model.extend( } } if (badSelectors.length) { - this.logger.error('Themes must not add rules outside theme namespace. Bad selectors:', badSelectors); + this.logger.error( + 'Themes must not add rules outside theme namespace. Bad selectors:', + badSelectors + ); throw 'Invalid theme'; } }, @@ -425,7 +440,8 @@ const Plugin = Backbone.Model.extend( if (!settings) { settings = {}; } - settings[key.replace(settingPrefix, '')] = AppSettingsModel.instance.attributes[key]; + settings[key.replace(settingPrefix, '')] = + AppSettingsModel.instance.attributes[key]; } } if (settings) { @@ -461,7 +477,10 @@ const Plugin = Backbone.Model.extend( disable() { const manifest = this.get('manifest'); - this.logger.info('Disabling plugin with resources', Object.keys(manifest.resources).join(', ')); + this.logger.info( + 'Disabling plugin with resources', + Object.keys(manifest.resources).join(', ') + ); this.set('status', this.STATUS_UNINSTALLING); const ts = this.logger.ts(); return Promise.resolve().then(() => { @@ -491,15 +510,26 @@ const Plugin = Backbone.Model.extend( const manifest = this.get('manifest'); const newManifest = newPlugin.get('manifest'); if (manifest.version === newManifest.version) { - this.set({ status: prevStatus, updateCheckDate: Date.now(), updateError: null }); + this.set({ + status: prevStatus, + updateCheckDate: Date.now(), + updateError: null + }); this.logger.info(`v${manifest.version} is the latest plugin version`); return; } - this.logger.info(`Updating plugin from v${manifest.version} to v${newManifest.version}`); - const error = newPlugin.validateManifest() || this.validateUpdatedManifest(newManifest); + this.logger.info( + `Updating plugin from v${manifest.version} to v${newManifest.version}` + ); + const error = + newPlugin.validateManifest() || this.validateUpdatedManifest(newManifest); if (error) { this.logger.error('Manifest validation error', error); - this.set({ status: prevStatus, updateCheckDate: Date.now(), updateError: error }); + this.set({ + status: prevStatus, + updateCheckDate: Date.now(), + updateError: error + }); throw 'Plugin validation error: ' + error; } this.uninstallPluginCode(); @@ -527,7 +557,11 @@ const Plugin = Backbone.Model.extend( throw err; }); } else { - this.set({ status: prevStatus, updateCheckDate: Date.now(), updateError: err }); + this.set({ + status: prevStatus, + updateCheckDate: Date.now(), + updateError: err + }); throw err; } }); @@ -555,7 +589,9 @@ const Plugin = Backbone.Model.extend( if (settings instanceof Array) { return settings.map(setting => { setting = _.clone(setting); - const value = AppSettingsModel.instance.get(settingsPrefix + setting.name); + const value = AppSettingsModel.instance.get( + settingsPrefix + setting.name + ); if (value !== undefined) { setting.value = value; } diff --git a/app/scripts/presenters/entry-presenter.js b/app/scripts/presenters/entry-presenter.js index 08e8d97b..62bbdce8 100644 --- a/app/scripts/presenters/entry-presenter.js +++ b/app/scripts/presenters/entry-presenter.js @@ -27,7 +27,9 @@ EntryPresenter.prototype = { return this.entry ? this.entry.customIcon : undefined; }, get color() { - return this.entry ? this.entry.color || (this.entry.customIcon ? this.noColor : undefined) : undefined; + return this.entry + ? this.entry.color || (this.entry.customIcon ? this.noColor : undefined) + : undefined; }, get title() { return this.entry ? this.entry.title : this.group.get('title'); @@ -76,7 +78,10 @@ EntryPresenter.prototype = { case 'updated': return this.updated; case 'attachments': - return this.entry.attachments.map(a => a.title).join(', ') || '(' + Locale.listNoAttachments + ')'; + return ( + this.entry.attachments.map(a => a.title).join(', ') || + '(' + Locale.listNoAttachments + ')' + ); default: return this.user || this.notes || this.url; } diff --git a/app/scripts/storage/storage-base.js b/app/scripts/storage/storage-base.js index 001fe2c5..b2f02348 100644 --- a/app/scripts/storage/storage-base.js +++ b/app/scripts/storage/storage-base.js @@ -72,7 +72,10 @@ _.extend(StorageBase.prototype, { } else { config.tryNum = (config.tryNum || 0) + 1; if (config.tryNum >= MaxRequestRetries) { - this.logger.info('Too many authorize attempts, fail request', config.url); + this.logger.info( + 'Too many authorize attempts, fail request', + config.url + ); return config.error && config.error('unauthorized', xhr); } this.logger.info('Repeat request, try #' + config.tryNum, config.url); diff --git a/app/scripts/storage/storage-dropbox.js b/app/scripts/storage/storage-dropbox.js index 36b879c0..4b66082e 100644 --- a/app/scripts/storage/storage-dropbox.js +++ b/app/scripts/storage/storage-dropbox.js @@ -197,7 +197,10 @@ const StorageDropbox = StorageBase.extend({ }, _encodeJsonHttpHeader(json) { - return json.replace(/[\u007f-\uffff]/g, c => '\\u' + ('000' + c.charCodeAt(0).toString(16)).slice(-4)); + return json.replace( + /[\u007f-\uffff]/g, + c => '\\u' + ('000' + c.charCodeAt(0).toString(16)).slice(-4) + ); }, _apiCall: function(args) { @@ -209,7 +212,9 @@ const StorageDropbox = StorageBase.extend({ let headers; let data = args.data; if (args.apiArg) { - headers = { 'Dropbox-API-Arg': this._encodeJsonHttpHeader(JSON.stringify(args.apiArg)) }; + headers = { + 'Dropbox-API-Arg': this._encodeJsonHttpHeader(JSON.stringify(args.apiArg)) + }; if (args.data) { headers['Content-Type'] = 'application/octet-stream'; } @@ -274,7 +279,12 @@ const StorageDropbox = StorageBase.extend({ } else if (stat['.tag'] === 'folder') { stat = { folder: true }; } - this.logger.debug('Stated', path, stat.folder ? 'folder' : stat.rev, this.logger.ts(ts)); + this.logger.debug( + 'Stated', + path, + stat.folder ? 'folder' : stat.rev, + this.logger.ts(ts) + ); if (callback) { callback(null, stat); } diff --git a/app/scripts/storage/storage-gdrive.js b/app/scripts/storage/storage-gdrive.js index da1ff990..971895b1 100644 --- a/app/scripts/storage/storage-gdrive.js +++ b/app/scripts/storage/storage-gdrive.js @@ -32,7 +32,9 @@ const StorageGDrive = StorageBase.extend({ const ts = this.logger.ts(); const url = this._baseUrl + - '/files/{id}/revisions/{rev}?alt=media'.replace('{id}', path).replace('{rev}', stat.rev); + '/files/{id}/revisions/{rev}?alt=media' + .replace('{id}', path) + .replace('{rev}', stat.rev); this._xhr({ url: url, responseType: 'arraybuffer', @@ -94,7 +96,9 @@ const StorageGDrive = StorageBase.extend({ const isNew = path.lastIndexOf(NewFileIdPrefix, 0) === 0; let url; if (isNew) { - url = this._baseUrlUpload + '/files?uploadType=multipart&fields=id,headRevisionId'; + url = + this._baseUrlUpload + + '/files?uploadType=multipart&fields=id,headRevisionId'; const fileName = path.replace(NewFileIdPrefix, '') + '.kdbx'; const boundry = 'b' + Date.now() + 'x' + Math.round(Math.random() * 1000000); data = new Blob( @@ -137,7 +141,10 @@ const StorageGDrive = StorageBase.extend({ if (!newRev) { return callback && callback('save error: no rev'); } - return callback && callback(null, { rev: newRev, path: isNew ? response.id : null }); + return ( + callback && + callback(null, { rev: newRev, path: isNew ? response.id : null }) + ); }, error: err => { this.logger.error('Save error', path, err, this.logger.ts(ts)); @@ -154,12 +161,20 @@ const StorageGDrive = StorageBase.extend({ return callback && callback(err); } this.logger.debug('List'); - let query = dir === 'shared' ? 'sharedWithMe=true' : dir ? `"${dir}" in parents` : '"root" in parents'; + let query = + dir === 'shared' + ? 'sharedWithMe=true' + : dir + ? `"${dir}" in parents` + : '"root" in parents'; query += ' and trashed=false'; const url = this._baseUrl + '/files?fields={fields}&q={q}&pageSize=1000' - .replace('{fields}', encodeURIComponent('files(id,name,mimeType,headRevisionId)')) + .replace( + '{fields}', + encodeURIComponent('files(id,name,mimeType,headRevisionId)') + ) .replace('{q}', encodeURIComponent(query)); const ts = this.logger.ts(); this._xhr({ @@ -225,7 +240,10 @@ const StorageGDrive = StorageBase.extend({ _getOAuthConfig: function() { let clientId = this.appSettings.get('gdriveClientId'); if (!clientId) { - clientId = location.origin.indexOf('localhost') >= 0 ? GDriveClientId.Local : GDriveClientId.Production; + clientId = + location.origin.indexOf('localhost') >= 0 + ? GDriveClientId.Local + : GDriveClientId.Production; } return { scope: 'https://www.googleapis.com/auth/drive', diff --git a/app/scripts/storage/storage-onedrive.js b/app/scripts/storage/storage-onedrive.js index 214e655d..2391c192 100644 --- a/app/scripts/storage/storage-onedrive.js +++ b/app/scripts/storage/storage-onedrive.js @@ -43,7 +43,13 @@ const StorageOneDrive = StorageBase.extend({ const downloadUrl = response['@microsoft.graph.downloadUrl']; let rev = response.eTag; if (!downloadUrl || !response.eTag) { - this.logger.debug('Load error', path, 'no download url', response, this.logger.ts(ts)); + this.logger.debug( + 'Load error', + path, + 'no download url', + response, + this.logger.ts(ts) + ); return callback && callback('no download url'); } this._xhr({ @@ -233,7 +239,10 @@ const StorageOneDrive = StorageBase.extend({ _getClientId: function() { let clientId = this.appSettings.get('onedriveClientId'); if (!clientId) { - clientId = location.origin.indexOf('localhost') >= 0 ? OneDriveClientId.Local : OneDriveClientId.Production; + clientId = + location.origin.indexOf('localhost') >= 0 + ? OneDriveClientId.Local + : OneDriveClientId.Production; } return clientId; }, @@ -254,7 +263,10 @@ const StorageOneDrive = StorageBase.extend({ popupWindow.webContents.on('did-finish-load', e => { const webContents = e.sender.webContents; const url = webContents.getURL(); - if (url && url.startsWith('https://login.microsoftonline.com/common/oauth2/v2.0/authorize')) { + if ( + url && + url.startsWith('https://login.microsoftonline.com/common/oauth2/v2.0/authorize') + ) { // click the login button mentioned in #821 const script = `const selector = '[role="button"][aria-describedby="tileError loginHeader"]'; if (document.querySelectorAll(selector).length === 1) document.querySelector(selector).click()`; diff --git a/app/scripts/storage/storage-webdav.js b/app/scripts/storage/storage-webdav.js index 5bf722a6..58f1e482 100644 --- a/app/scripts/storage/storage-webdav.js +++ b/app/scripts/storage/storage-webdav.js @@ -146,23 +146,46 @@ const StorageWebDav = StorageBase.extend({ (err, xhr, stat) => { if (err) { that._request( - _.defaults({ op: 'Save:delete', method: 'DELETE', path: tmpPath }, saveOpts) + _.defaults( + { + op: 'Save:delete', + method: 'DELETE', + path: tmpPath + }, + saveOpts + ) ); return cb(err, xhr, stat); } if (stat.rev !== rev) { - that.logger.debug('Save error', path, 'rev conflict', stat.rev, rev); + that.logger.debug( + 'Save error', + path, + 'rev conflict', + stat.rev, + rev + ); that._request( - _.defaults({ op: 'Save:delete', method: 'DELETE', path: tmpPath }, saveOpts) + _.defaults( + { + op: 'Save:delete', + method: 'DELETE', + path: tmpPath + }, + saveOpts + ) ); return cb({ revConflict: true }, xhr, stat); } let movePath = path; if (movePath.indexOf('://') < 0) { if (movePath.indexOf('/') === 0) { - movePath = location.protocol + '//' + location.host + movePath; + movePath = + location.protocol + '//' + location.host + movePath; } else { - movePath = location.href.replace(/\?(.*)/, '').replace(/[^/]*$/, movePath); + movePath = location.href + .replace(/\?(.*)/, '') + .replace(/[^/]*$/, movePath); } } that._request( @@ -239,7 +262,9 @@ const StorageWebDav = StorageBase.extend({ const password = opts.password; let encpass = ''; for (let i = 0; i < password.length; i++) { - encpass += String.fromCharCode(password.charCodeAt(i) ^ fileId.charCodeAt(i % fileId.length)); + encpass += String.fromCharCode( + password.charCodeAt(i) ^ fileId.charCodeAt(i % fileId.length) + ); } result.encpass = btoa(encpass); } @@ -253,7 +278,9 @@ const StorageWebDav = StorageBase.extend({ const encpass = atob(opts.encpass); let password = ''; for (let i = 0; i < encpass.length; i++) { - password += String.fromCharCode(encpass.charCodeAt(i) ^ fileId.charCodeAt(i % fileId.length)); + password += String.fromCharCode( + encpass.charCodeAt(i) ^ fileId.charCodeAt(i % fileId.length) + ); } result.password = password; } @@ -271,7 +298,12 @@ const StorageWebDav = StorageBase.extend({ const xhr = new XMLHttpRequest(); xhr.addEventListener('load', () => { if ([200, 201, 204].indexOf(xhr.status) < 0) { - that.logger.debug(config.op + ' error', config.path, xhr.status, that.logger.ts(ts)); + that.logger.debug( + config.op + ' error', + config.path, + xhr.status, + that.logger.ts(ts) + ); let err; switch (xhr.status) { case 404: @@ -292,14 +324,20 @@ const StorageWebDav = StorageBase.extend({ } const rev = xhr.getResponseHeader('Last-Modified'); if (!rev && !config.nostat) { - that.logger.debug(config.op + ' error', config.path, 'no headers', that.logger.ts(ts)); + that.logger.debug( + config.op + ' error', + config.path, + 'no headers', + that.logger.ts(ts) + ); if (callback) { callback('No Last-Modified header', xhr); callback = null; } return; } - const completedOpName = config.op + (config.op.charAt(config.op.length - 1) === 'e' ? 'd' : 'ed'); + const completedOpName = + config.op + (config.op.charAt(config.op.length - 1) === 'e' ? 'd' : 'ed'); that.logger.debug(completedOpName, config.path, rev, that.logger.ts(ts)); if (callback) { callback(null, xhr, rev ? { rev: rev } : null); @@ -323,7 +361,10 @@ const StorageWebDav = StorageBase.extend({ xhr.open(config.method, config.path); xhr.responseType = 'arraybuffer'; if (config.user) { - xhr.setRequestHeader('Authorization', 'Basic ' + btoa(config.user + ':' + config.password)); + xhr.setRequestHeader( + 'Authorization', + 'Basic ' + btoa(config.user + ':' + config.password) + ); } if (config.headers) { _.forEach(config.headers, (value, header) => { diff --git a/app/scripts/util/color.js b/app/scripts/util/color.js index f35d881f..e340444b 100644 --- a/app/scripts/util/color.js +++ b/app/scripts/util/color.js @@ -79,7 +79,9 @@ Color.prototype.toRgba = function() { }; Color.prototype.toHsla = function() { - return `hsla(${Math.round(this.h * 100)},${Math.round(this.s * 100)}%,${Math.round(this.l * 100)}%,${this.a})`; + return `hsla(${Math.round(this.h * 100)},${Math.round(this.s * 100)}%,${Math.round( + this.l * 100 + )}%,${this.a})`; }; Color.prototype.distanceTo = function(color) { diff --git a/app/scripts/util/feature-detector.js b/app/scripts/util/feature-detector.js index 7401794e..49bc102f 100644 --- a/app/scripts/util/feature-detector.js +++ b/app/scripts/util/feature-detector.js @@ -12,7 +12,9 @@ const FeatureDetector = { isPopup: !!(window.parent !== window.top || window.opener), isStandalone: !!navigator.standalone, isFrame: window.top !== window, - isSelfHosted: !isDesktop && !/^http(s?):\/\/((localhost:8085)|((app|beta)\.keeweb\.info))/.test(location.href), + isSelfHosted: + !isDesktop && + !/^http(s?):\/\/((localhost:8085)|((app|beta)\.keeweb\.info))/.test(location.href), needFixClicks: /Edge\/14/.test(navigator.appVersion), actionShortcutSymbol: function(formatting) { @@ -22,7 +24,11 @@ const FeatureDetector = { return this.isMac ? '⌥' : formatting ? 'alt + ' : 'alt-'; }, globalShortcutSymbol: function(formatting) { - return this.isMac ? '⌃⌥' : formatting ? 'shift+alt+' : 'shift-alt-'; + return this.isMac + ? '⌃⌥' + : formatting + ? 'shift+alt+' + : 'shift-alt-'; }, globalShortcutIsLarge: function() { return !this.isMac; diff --git a/app/scripts/util/format.js b/app/scripts/util/format.js index 60410c30..3f7b3f8e 100644 --- a/app/scripts/util/format.js +++ b/app/scripts/util/format.js @@ -32,7 +32,9 @@ const Format = { if (typeof dt === 'number') { dt = new Date(dt); } - return dt ? dt.getDate() + ' ' + Locale.monthsShort[dt.getMonth()] + ' ' + dt.getFullYear() : ''; + return dt + ? dt.getDate() + ' ' + Locale.monthsShort[dt.getMonth()] + ' ' + dt.getFullYear() + : ''; }, capFirst: function(str) { if (!str) { diff --git a/app/scripts/util/icon-url.js b/app/scripts/util/icon-url.js index 9c2b01aa..a10b0f65 100644 --- a/app/scripts/util/icon-url.js +++ b/app/scripts/util/icon-url.js @@ -2,7 +2,9 @@ const kdbxweb = require('kdbxweb'); const IconUrl = { toDataUrl: function(iconData) { - return iconData ? 'data:image/png;base64,' + kdbxweb.ByteUtils.bytesToBase64(iconData) : null; + return iconData + ? 'data:image/png;base64,' + kdbxweb.ByteUtils.bytesToBase64(iconData) + : null; } }; diff --git a/app/scripts/util/kdbxweb-init.js b/app/scripts/util/kdbxweb-init.js index 00653e75..177acea8 100644 --- a/app/scripts/util/kdbxweb-init.js +++ b/app/scripts/util/kdbxweb-init.js @@ -86,7 +86,8 @@ const KdbxwebInit = { worker.terminate(); KdbxwebInit.runtimeModule = null; if (!e.data || e.data.error || !e.data.hash) { - const ex = (e.data && e.data.error) || 'unexpected error'; + const ex = + (e.data && e.data.error) || 'unexpected error'; logger.error('Worker error', ex); reject(ex); } diff --git a/app/scripts/util/password-generator.js b/app/scripts/util/password-generator.js index 5f4ffdef..18230a97 100644 --- a/app/scripts/util/password-generator.js +++ b/app/scripts/util/password-generator.js @@ -8,7 +8,8 @@ const PasswordGenerator = { digits: '123456789', special: '!@#$%^&*_+-=,./?;:`"~\'\\', brackets: '(){}[]<>', - high: '¡¢£¤¥¦§©ª«¬®¯°±²³´µ¶¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ', + high: + '¡¢£¤¥¦§©ª«¬®¯°±²³´µ¶¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ', ambiguous: 'O0oIl' }, diff --git a/app/scripts/util/phonetic.js b/app/scripts/util/phonetic.js index b4b495ca..a927001a 100644 --- a/app/scripts/util/phonetic.js +++ b/app/scripts/util/phonetic.js @@ -169,7 +169,12 @@ function addSyllable(wordObj) { } else { wordObj.lastSkippedPre = true; } - wordObj.word += getNextPhonetic(PHONETIC_MID, PHONETIC_MID_SIMPLE_LENGTH, wordObj, first && wordObj.lastSkippedPre); + wordObj.word += getNextPhonetic( + PHONETIC_MID, + PHONETIC_MID_SIMPLE_LENGTH, + wordObj, + first && wordObj.lastSkippedPre + ); if (wordObj.lastSkippedPre || compound) { wordObj.word += getNextPhonetic(PHONETIC_POST, PHONETIC_POST_SIMPLE_LENGTH, wordObj); wordObj.lastSkippedPost = false; @@ -219,8 +224,12 @@ function getOptions(overrides) { overrides = overrides || {}; options.length = overrides.length || 16; options.seed = overrides.seed || Math.random(); - options.phoneticSimplicity = overrides.phoneticSimplicity ? Math.max(overrides.phoneticSimplicity, 1) : 5; - options.compoundSimplicity = overrides.compoundSimplicity ? Math.max(overrides.compoundSimplicity, 1) : 5; + options.phoneticSimplicity = overrides.phoneticSimplicity + ? Math.max(overrides.phoneticSimplicity, 1) + : 5; + options.compoundSimplicity = overrides.compoundSimplicity + ? Math.max(overrides.compoundSimplicity, 1) + : 5; return options; } diff --git a/app/scripts/util/signature-verifier.js b/app/scripts/util/signature-verifier.js index 7e040148..57bebb19 100644 --- a/app/scripts/util/signature-verifier.js +++ b/app/scripts/util/signature-verifier.js @@ -54,7 +54,9 @@ const SignatureVerifier = { getPublicKey() { if (!this.publicKey) { - this.publicKey = publicKey.match(/-+BEGIN PUBLIC KEY-+([\s\S]+?)-+END PUBLIC KEY-+/)[1].replace(/\s+/g, ''); + this.publicKey = publicKey + .match(/-+BEGIN PUBLIC KEY-+([\s\S]+?)-+END PUBLIC KEY-+/)[1] + .replace(/\s+/g, ''); } return this.publicKey; } diff --git a/app/scripts/util/tip.js b/app/scripts/util/tip.js index 3982a076..0937e5aa 100644 --- a/app/scripts/util/tip.js +++ b/app/scripts/util/tip.js @@ -173,7 +173,10 @@ Tip.hideTip = function(el) { Tip.updateTip = function(el, props) { if (el._tip) { el._tip.hide(); - _.extend(el._tip, _.pick(props, ['title', 'placement', 'fast', 'showTimeout', 'hideTimeout'])); + _.extend( + el._tip, + _.pick(props, ['title', 'placement', 'fast', 'showTimeout', 'hideTimeout']) + ); } }; diff --git a/app/scripts/views/app-view.js b/app/scripts/views/app-view.js index 5c748d86..ff9a36c2 100644 --- a/app/scripts/views/app-view.js +++ b/app/scripts/views/app-view.js @@ -189,7 +189,11 @@ const AppView = Backbone.View.extend({ }, updateApp: function() { - if (UpdateModel.instance.get('updateStatus') === 'ready' && !Launcher && !this.model.files.hasOpenFiles()) { + if ( + UpdateModel.instance.get('updateStatus') === 'ready' && + !Launcher && + !this.model.files.hasOpenFiles() + ) { window.location.reload(); } }, @@ -308,7 +312,9 @@ const AppView = Backbone.View.extend({ }, showFileSettings: function(e) { - const menuItem = this.model.menu.filesSection.get('items').find(item => item.get('file').cid === e.fileId); + const menuItem = this.model.menu.filesSection + .get('items') + .find(item => item.get('file').cid === e.fileId); if (this.views.settings) { if (this.views.settings.file === menuItem.get('file')) { this.showEntries(); @@ -538,7 +544,10 @@ const AppView = Backbone.View.extend({ if (--pendingCallbacks === 0) { if (errorFiles.length && that.model.files.hasDirtyFiles()) { if (!Alerts.alertDisplayed) { - const alertBody = errorFiles.length > 1 ? Locale.appSaveErrorBodyMul : Locale.appSaveErrorBody; + const alertBody = + errorFiles.length > 1 + ? Locale.appSaveErrorBodyMul + : Locale.appSaveErrorBody; Alerts.error({ header: Locale.appSaveError, body: alertBody + ' ' + errorFiles.join(', ') diff --git a/app/scripts/views/auto-type/auto-type-select-view.js b/app/scripts/views/auto-type/auto-type-select-view.js index 0b1676f4..b06f993e 100644 --- a/app/scripts/views/auto-type/auto-type-select-view.js +++ b/app/scripts/views/auto-type/auto-type-select-view.js @@ -32,12 +32,30 @@ const AutoTypePopupView = Backbone.View.extend({ setupKeys() { KeyHandler.onKey(Keys.DOM_VK_ESCAPE, this.escPressed, this, false, true); KeyHandler.onKey(Keys.DOM_VK_RETURN, this.enterPressed, this, false, true); - KeyHandler.onKey(Keys.DOM_VK_RETURN, this.actionEnterPressed, this, KeyHandler.SHORTCUT_ACTION, true); - KeyHandler.onKey(Keys.DOM_VK_RETURN, this.optEnterPressed, this, KeyHandler.SHORTCUT_OPT, true); + KeyHandler.onKey( + Keys.DOM_VK_RETURN, + this.actionEnterPressed, + this, + KeyHandler.SHORTCUT_ACTION, + true + ); + KeyHandler.onKey( + Keys.DOM_VK_RETURN, + this.optEnterPressed, + this, + KeyHandler.SHORTCUT_OPT, + true + ); KeyHandler.onKey(Keys.DOM_VK_UP, this.upPressed, this, false, true); KeyHandler.onKey(Keys.DOM_VK_DOWN, this.downPressed, this, false, true); KeyHandler.onKey(Keys.DOM_VK_BACK_SPACE, this.backSpacePressed, this, false, true); - KeyHandler.onKey(Keys.DOM_VK_O, this.openKeyPressed, this, KeyHandler.SHORTCUT_ACTION, true); + KeyHandler.onKey( + Keys.DOM_VK_O, + this.openKeyPressed, + this, + KeyHandler.SHORTCUT_ACTION, + true + ); KeyHandler.on('keypress:auto-type', this.keyPressed.bind(this)); KeyHandler.setModal('auto-type'); }, @@ -180,7 +198,10 @@ const AutoTypePopupView = Backbone.View.extend({ backSpacePressed() { if (this.model.filter.text) { - this.model.filter.text = this.model.filter.text.substr(0, this.model.filter.text.length - 1); + this.model.filter.text = this.model.filter.text.substr( + 0, + this.model.filter.text.length - 1 + ); this.render(); } }, diff --git a/app/scripts/views/details/details-history-view.js b/app/scripts/views/details/details-history-view.js index b4b2da38..312e59c5 100644 --- a/app/scripts/views/details/details-history-view.js +++ b/app/scripts/views/details/details-history-view.js @@ -121,7 +121,9 @@ const DetailsHistoryView = Backbone.View.extend({ showRecord: function(ix) { this.activeIx = ix; this.record = this.timeline[ix].rec; - this.timelineEl.find('.details__history-timeline-item').removeClass('details__history-timeline-item--active'); + this.timelineEl + .find('.details__history-timeline-item') + .removeClass('details__history-timeline-item--active'); this.timelineEl .find('.details__history-timeline-item[data-id="' + ix + '"]') .addClass('details__history-timeline-item--active'); @@ -129,7 +131,9 @@ const DetailsHistoryView = Backbone.View.extend({ this.bodyEl.html(''); const colorCls = this.record.color ? this.record.color + '-color' : ''; this.fieldViews.push( - new FieldViewReadOnly({ model: { name: 'Rev', title: Locale.detHistoryVersion, value: ix + 1 } }) + new FieldViewReadOnly({ + model: { name: 'Rev', title: Locale.detHistoryVersion, value: ix + 1 } + }) ); this.fieldViews.push( new FieldViewReadOnly({ @@ -162,27 +166,47 @@ const DetailsHistoryView = Backbone.View.extend({ ); this.fieldViews.push( new FieldViewReadOnly({ - model: { name: '$UserName', title: Format.capFirst(Locale.user), value: this.record.user } + model: { + name: '$UserName', + title: Format.capFirst(Locale.user), + value: this.record.user + } }) ); this.fieldViews.push( new FieldViewReadOnly({ - model: { name: '$Password', title: Format.capFirst(Locale.password), value: this.record.password } + model: { + name: '$Password', + title: Format.capFirst(Locale.password), + value: this.record.password + } }) ); this.fieldViews.push( new FieldViewReadOnly({ - model: { name: '$URL', title: Format.capFirst(Locale.website), value: this.record.url } + model: { + name: '$URL', + title: Format.capFirst(Locale.website), + value: this.record.url + } }) ); this.fieldViews.push( new FieldViewReadOnly({ - model: { name: '$Notes', title: Format.capFirst(Locale.notes), value: this.record.notes } + model: { + name: '$Notes', + title: Format.capFirst(Locale.notes), + value: this.record.notes + } }) ); this.fieldViews.push( new FieldViewReadOnly({ - model: { name: 'Tags', title: Format.capFirst(Locale.tags), value: this.record.tags.join(', ') } + model: { + name: 'Tags', + title: Format.capFirst(Locale.tags), + value: this.record.tags.join(', ') + } }) ); this.fieldViews.push( @@ -198,7 +222,9 @@ const DetailsHistoryView = Backbone.View.extend({ this.record.fields, function(value, field) { this.fieldViews.push( - new FieldViewReadOnly({ model: { name: '$' + field, title: field, value: value } }) + new FieldViewReadOnly({ + model: { name: '$' + field, title: field, value: value } + }) ); }, this @@ -223,7 +249,12 @@ const DetailsHistoryView = Backbone.View.extend({ buttons.find('.details__history-button-delete').toggle(ix < this.history.length - 1); buttons .find('.details__history-button-discard') - .toggle((this.record.unsaved && ix === this.history.length - 1 && this.history.length > 1) || false); + .toggle( + (this.record.unsaved && + ix === this.history.length - 1 && + this.history.length > 1) || + false + ); }, timelineItemClick: function(e) { @@ -254,13 +285,15 @@ const DetailsHistoryView = Backbone.View.extend({ })); const period = lastRec.updated - firstRec.updated; const format = this.getDateFormat(period); - this.labels = this.getLabels(firstRec.updated.getTime(), lastRec.updated.getTime(), format.round).map( - label => ({ - pos: (label - firstRec.updated) / (lastRec.updated - firstRec.updated), - val: label, - text: format.format(new Date(label)) - }) - ); + this.labels = this.getLabels( + firstRec.updated.getTime(), + lastRec.updated.getTime(), + format.round + ).map(label => ({ + pos: (label - firstRec.updated) / (lastRec.updated - firstRec.updated), + val: label, + text: format.format(new Date(label)) + })); }, getDateFormat: function(period) { diff --git a/app/scripts/views/details/details-view.js b/app/scripts/views/details/details-view.js index fa8c3b17..37fb53f3 100644 --- a/app/scripts/views/details/details-view.js +++ b/app/scripts/views/details/details-view.js @@ -74,22 +74,53 @@ const DetailsView = Backbone.View.extend({ this.listenTo(Backbone, 'set-locale', this.render); this.listenTo(OtpQrReader, 'qr-read', this.otpCodeRead); this.listenTo(OtpQrReader, 'enter-manually', this.otpEnterManually); - KeyHandler.onKey(Keys.DOM_VK_C, this.copyPasswordFromShortcut, this, KeyHandler.SHORTCUT_ACTION, false, true); + KeyHandler.onKey( + Keys.DOM_VK_C, + this.copyPasswordFromShortcut, + this, + KeyHandler.SHORTCUT_ACTION, + false, + true + ); KeyHandler.onKey(Keys.DOM_VK_B, this.copyUserName, this, KeyHandler.SHORTCUT_ACTION); KeyHandler.onKey(Keys.DOM_VK_U, this.copyUrl, this, KeyHandler.SHORTCUT_ACTION); if (AutoType.enabled) { KeyHandler.onKey(Keys.DOM_VK_T, this.autoType, this, KeyHandler.SHORTCUT_ACTION); } - KeyHandler.onKey(Keys.DOM_VK_DELETE, this.deleteKeyPress, this, KeyHandler.SHORTCUT_ACTION, false, true); - KeyHandler.onKey(Keys.DOM_VK_BACK_SPACE, this.deleteKeyPress, this, KeyHandler.SHORTCUT_ACTION, false, true); + KeyHandler.onKey( + Keys.DOM_VK_DELETE, + this.deleteKeyPress, + this, + KeyHandler.SHORTCUT_ACTION, + false, + true + ); + KeyHandler.onKey( + Keys.DOM_VK_BACK_SPACE, + this.deleteKeyPress, + this, + KeyHandler.SHORTCUT_ACTION, + false, + true + ); }, remove: function() { KeyHandler.offKey(Keys.DOM_VK_C, this.copyPassword, this); KeyHandler.offKey(Keys.DOM_VK_B, this.copyUserName, this); KeyHandler.offKey(Keys.DOM_VK_U, this.copyUrl, this); - KeyHandler.offKey(Keys.DOM_VK_DELETE, this.deleteKeyPress, this, KeyHandler.SHORTCUT_ACTION); - KeyHandler.offKey(Keys.DOM_VK_BACK_SPACE, this.deleteKeyPress, this, KeyHandler.SHORTCUT_ACTION); + KeyHandler.offKey( + Keys.DOM_VK_DELETE, + this.deleteKeyPress, + this, + KeyHandler.SHORTCUT_ACTION + ); + KeyHandler.offKey( + Keys.DOM_VK_BACK_SPACE, + this.deleteKeyPress, + this, + KeyHandler.SHORTCUT_ACTION + ); this.removeFieldViews(); Backbone.View.prototype.remove.call(this); }, @@ -321,7 +352,10 @@ const DetailsView = Backbone.View.extend({ if (hideEmptyFields) { const value = fieldView.model.value(); if (!value || value.length === 0 || value.byteLength === 0) { - if (this.model.isJustCreated && ['$UserName', '$Password'].indexOf(fieldView.model.name) >= 0) { + if ( + this.model.isJustCreated && + ['$UserName', '$Password'].indexOf(fieldView.model.name) >= 0 + ) { return; // don't hide user for new records } fieldView.hide(); @@ -385,15 +419,35 @@ const DetailsView = Backbone.View.extend({ }); } }, this); - moreOptions.push({ value: 'add-new', icon: 'plus', text: Locale.detMenuAddNewField }); - moreOptions.push({ value: 'toggle-empty', icon: 'eye', text: Locale.detMenuShowEmpty }); + moreOptions.push({ + value: 'add-new', + icon: 'plus', + text: Locale.detMenuAddNewField + }); + moreOptions.push({ + value: 'toggle-empty', + icon: 'eye', + text: Locale.detMenuShowEmpty + }); } else { - moreOptions.push({ value: 'add-new', icon: 'plus', text: Locale.detMenuAddNewField }); - moreOptions.push({ value: 'toggle-empty', icon: 'eye-slash', text: Locale.detMenuHideEmpty }); + moreOptions.push({ + value: 'add-new', + icon: 'plus', + text: Locale.detMenuAddNewField + }); + moreOptions.push({ + value: 'toggle-empty', + icon: 'eye-slash', + text: Locale.detMenuHideEmpty + }); } moreOptions.push({ value: 'otp', icon: 'clock-o', text: Locale.detSetupOtp }); if (AutoType.enabled) { - moreOptions.push({ value: 'auto-type', icon: 'keyboard-o', text: Locale.detAutoTypeSettings }); + moreOptions.push({ + value: 'auto-type', + icon: 'keyboard-o', + text: Locale.detAutoTypeSettings + }); } moreOptions.push({ value: 'clone', icon: 'clone', text: Locale.detClone }); const rect = this.moreView.labelEl[0].getBoundingClientRect(); @@ -561,7 +615,8 @@ const DetailsView = Backbone.View.extend({ } if (!window.getSelection().toString()) { const fieldValue = editView.value; - const fieldText = fieldValue && fieldValue.isProtected ? fieldValue.getText() : fieldValue; + const fieldText = + fieldValue && fieldValue.isProtected ? fieldValue.getText() : fieldValue; if (!fieldText) { return; } @@ -701,7 +756,10 @@ const DetailsView = Backbone.View.extend({ e.preventDefault(); e.stopPropagation(); const dt = e.originalEvent.dataTransfer; - if (!dt.types || (dt.types.indexOf ? dt.types.indexOf('Files') === -1 : !dt.types.contains('Files'))) { + if ( + !dt.types || + (dt.types.indexOf ? dt.types.indexOf('Files') === -1 : !dt.types.contains('Files')) + ) { dt.dropEffect = 'none'; return; } @@ -896,7 +954,11 @@ const DetailsView = Backbone.View.extend({ deleteFromTrash: function() { Alerts.yesno({ header: Locale.detDelFromTrash, - body: Locale.detDelFromTrashBody + '

' + Locale.detDelFromTrashBodyHint + '

', + body: + Locale.detDelFromTrashBody + + '

' + + Locale.detDelFromTrashBodyHint + + '

', icon: 'minus-circle', success: () => { this.model.deleteFromTrash(); @@ -913,8 +975,16 @@ const DetailsView = Backbone.View.extend({ const canCopy = document.queryCommandSupported('copy'); const options = []; if (canCopy) { - options.push({ value: 'det-copy-password', icon: 'clipboard', text: Locale.detMenuCopyPassword }); - options.push({ value: 'det-copy-user', icon: 'clipboard', text: Locale.detMenuCopyUser }); + options.push({ + value: 'det-copy-password', + icon: 'clipboard', + text: Locale.detMenuCopyPassword + }); + options.push({ + value: 'det-copy-user', + icon: 'clipboard', + text: Locale.detMenuCopyUser + }); } options.push({ value: 'det-add-new', icon: 'plus', text: Locale.detMenuAddNewField }); options.push({ value: 'det-clone', icon: 'clone', text: Locale.detClone }); diff --git a/app/scripts/views/fields/field-view-autocomplete.js b/app/scripts/views/fields/field-view-autocomplete.js index cf1a221a..e81fddb6 100644 --- a/app/scripts/views/fields/field-view-autocomplete.js +++ b/app/scripts/views/fields/field-view-autocomplete.js @@ -46,7 +46,9 @@ const FieldViewAutocomplete = FieldViewText.extend({ e.preventDefault(); break; case Keys.DOM_VK_RETURN: - const selectedItem = this.autocomplete.find('.details__field-autocomplete-item--selected').text(); + const selectedItem = this.autocomplete + .find('.details__field-autocomplete-item--selected') + .text(); if (selectedItem) { this.input.val(selectedItem); this.endEdit(selectedItem); @@ -62,7 +64,8 @@ const FieldViewAutocomplete = FieldViewText.extend({ const completions = this.model.getCompletions(this.input.val()); if (typeof this.selectedCopmletionIx === 'number') { this.selectedCopmletionIx = - (completions.length + this.selectedCopmletionIx + (next ? 1 : -1)) % completions.length; + (completions.length + this.selectedCopmletionIx + (next ? 1 : -1)) % + completions.length; } else { this.selectedCopmletionIx = next ? 0 : completions.length - 1; } @@ -73,8 +76,17 @@ const FieldViewAutocomplete = FieldViewText.extend({ const completions = this.model.getCompletions(this.input.val()); const completionsHtml = completions .map((item, ix) => { - const sel = ix === this.selectedCopmletionIx ? 'details__field-autocomplete-item--selected' : ''; - return '
' + _.escape(item) + '
'; + const sel = + ix === this.selectedCopmletionIx + ? 'details__field-autocomplete-item--selected' + : ''; + return ( + '
' + + _.escape(item) + + '
' + ); }) .join(''); this.autocomplete.html(completionsHtml); diff --git a/app/scripts/views/fields/field-view-history.js b/app/scripts/views/fields/field-view-history.js index ff9e5770..e0758f44 100644 --- a/app/scripts/views/fields/field-view-history.js +++ b/app/scripts/views/fields/field-view-history.js @@ -6,7 +6,10 @@ const FieldViewHistory = FieldView.extend({ if (!value.length) { return Locale.detHistoryEmpty; } - let text = value.length + ' ' + (value.length % 10 === 1 ? Locale.detHistoryRec : Locale.detHistoryRecs); + let text = + value.length + + ' ' + + (value.length % 10 === 1 ? Locale.detHistoryRec : Locale.detHistoryRecs); if (value.unsaved) { text += ' (' + Locale.detHistoryModified + ')'; } diff --git a/app/scripts/views/fields/field-view-tags.js b/app/scripts/views/fields/field-view-tags.js index d4d0020e..0ecba264 100644 --- a/app/scripts/views/fields/field-view-tags.js +++ b/app/scripts/views/fields/field-view-tags.js @@ -38,7 +38,9 @@ const FieldViewTags = FieldViewText.extend({ startEdit: function() { FieldViewText.prototype.startEdit.call(this); const fieldRect = this.input[0].getBoundingClientRect(); - this.tagsAutocomplete = $('
').appendTo('body'); + this.tagsAutocomplete = $('
').appendTo( + 'body' + ); this.tagsAutocomplete.css({ top: fieldRect.bottom, left: fieldRect.left, @@ -59,7 +61,10 @@ const FieldViewTags = FieldViewText.extend({ const last = tags[tags.length - 1]; const isLastPart = last && this.model.tags.indexOf(last) < 0; return this.model.tags.filter(tag => { - return tags.indexOf(tag) < 0 && (!isLastPart || tag.toLowerCase().indexOf(last.toLowerCase()) >= 0); + return ( + tags.indexOf(tag) < 0 && + (!isLastPart || tag.toLowerCase().indexOf(last.toLowerCase()) >= 0) + ); }); }, diff --git a/app/scripts/views/fields/field-view-text.js b/app/scripts/views/fields/field-view-text.js index b5d80da2..29e2fc08 100644 --- a/app/scripts/views/fields/field-view-text.js +++ b/app/scripts/views/fields/field-view-text.js @@ -85,7 +85,10 @@ const FieldViewText = FieldView.extend({ } else { const fieldRect = this.input[0].getBoundingClientRect(); this.gen = new GeneratorView({ - model: { pos: { left: fieldRect.left, top: fieldRect.bottom }, password: this.value } + model: { + pos: { left: fieldRect.left, top: fieldRect.bottom }, + password: this.value + } }).render(); this.gen.once('remove', this.generatorClosed.bind(this)); this.gen.once('result', this.generatorResult.bind(this)); diff --git a/app/scripts/views/fields/field-view.js b/app/scripts/views/fields/field-view.js index 72046998..50a03a51 100644 --- a/app/scripts/views/fields/field-view.js +++ b/app/scripts/views/fields/field-view.js @@ -140,7 +140,8 @@ const FieldView = Backbone.View.extend({ } else { textEqual = _.isEqual(this.value, newVal); } - const protectedEqual = (newVal && newVal.isProtected) === (this.value && this.value.isProtected); + const protectedEqual = + (newVal && newVal.isProtected) === (this.value && this.value.isProtected); const nameChanged = extra && extra.newField; let arg; if (newVal !== undefined && (!textEqual || !protectedEqual || nameChanged)) { diff --git a/app/scripts/views/footer-view.js b/app/scripts/views/footer-view.js index aaaff15f..055b039a 100644 --- a/app/scripts/views/footer-view.js +++ b/app/scripts/views/footer-view.js @@ -19,7 +19,14 @@ const FooterView = Backbone.View.extend({ initialize: function() { this.views = {}; - KeyHandler.onKey(Keys.DOM_VK_L, this.lockWorkspace, this, KeyHandler.SHORTCUT_ACTION, false, true); + KeyHandler.onKey( + Keys.DOM_VK_L, + this.lockWorkspace, + this, + KeyHandler.SHORTCUT_ACTION, + false, + true + ); KeyHandler.onKey(Keys.DOM_VK_G, this.genPass, this, KeyHandler.SHORTCUT_ACTION); KeyHandler.onKey(Keys.DOM_VK_O, this.openFile, this, KeyHandler.SHORTCUT_ACTION); KeyHandler.onKey(Keys.DOM_VK_S, this.saveAll, this, KeyHandler.SHORTCUT_ACTION); @@ -35,7 +42,8 @@ const FooterView = Backbone.View.extend({ this.renderTemplate( { files: this.model.files, - updateAvailable: ['ready', 'found'].indexOf(UpdateModel.instance.get('updateStatus')) >= 0 + updateAvailable: + ['ready', 'found'].indexOf(UpdateModel.instance.get('updateStatus')) >= 0 }, { plain: true } ); @@ -67,7 +75,9 @@ const FooterView = Backbone.View.extend({ const bodyRect = document.body.getBoundingClientRect(); const right = bodyRect.right - rect.right; const bottom = bodyRect.bottom - rect.top; - const generator = new GeneratorView({ model: { copy: true, pos: { right: right, bottom: bottom } } }).render(); + const generator = new GeneratorView({ + model: { copy: true, pos: { right: right, bottom: bottom } } + }).render(); generator.once('remove', () => { delete this.views.gen; }); diff --git a/app/scripts/views/generator-presets-view.js b/app/scripts/views/generator-presets-view.js index a64c0175..5ac028bb 100644 --- a/app/scripts/views/generator-presets-view.js +++ b/app/scripts/views/generator-presets-view.js @@ -62,15 +62,17 @@ const GeneratorPresetsView = Backbone.View.extend({ const rangeOverride = { high: '¡¢£¤¥¦§©ª«¬®¯°±¹²´µ¶»¼÷¿ÀÖîü...' }; - return ['Upper', 'Lower', 'Digits', 'Special', 'Brackets', 'High', 'Ambiguous'].map(name => { - const nameLower = name.toLowerCase(); - return { - name: nameLower, - title: Locale['genPs' + name], - enabled: sel[nameLower], - sample: rangeOverride[nameLower] || PasswordGenerator.charRanges[nameLower] - }; - }); + return ['Upper', 'Lower', 'Digits', 'Special', 'Brackets', 'High', 'Ambiguous'].map( + name => { + const nameLower = name.toLowerCase(); + return { + name: nameLower, + title: Locale['genPs' + name], + enabled: sel[nameLower], + sample: rangeOverride[nameLower] || PasswordGenerator.charRanges[nameLower] + }; + } + ); }, getPreset: function(name) { diff --git a/app/scripts/views/generator-view.js b/app/scripts/views/generator-view.js index 4d4ac6db..f2c66d16 100644 --- a/app/scripts/views/generator-view.js +++ b/app/scripts/views/generator-view.js @@ -24,7 +24,34 @@ const GeneratorView = Backbone.View.extend({ 'click .gen__btn-refresh': 'newPass' }, - valuesMap: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 26, 28, 30, 32, 48, 64], + valuesMap: [ + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 22, + 24, + 26, + 28, + 30, + 32, + 48, + 64 + ], presets: null, preset: null, @@ -40,7 +67,11 @@ const GeneratorView = Backbone.View.extend({ render: function() { const canCopy = document.queryCommandSupported('copy'); - const btnTitle = this.model.copy ? (canCopy ? Locale.alertCopy : Locale.alertClose) : Locale.alertOk; + const btnTitle = this.model.copy + ? canCopy + ? Locale.alertCopy + : Locale.alertClose + : Locale.alertOk; this.renderTemplate({ btnTitle: btnTitle, showToggleButton: this.model.copy, @@ -57,7 +88,10 @@ const GeneratorView = Backbone.View.extend({ createPresets: function() { this.presets = GeneratorPresets.enabled; - if (this.model.password && (!this.model.password.isProtected || this.model.password.byteLength)) { + if ( + this.model.password && + (!this.model.password.isProtected || this.model.password.byteLength) + ) { const derivedPreset = { name: 'Derived', title: Locale.genPresetDerived }; _.extend(derivedPreset, PasswordGenerator.deriveOpts(this.model.password)); this.presets.splice(0, 0, derivedPreset); diff --git a/app/scripts/views/icon-select-view.js b/app/scripts/views/icon-select-view.js index 8dc3b018..485b5f38 100644 --- a/app/scripts/views/icon-select-view.js +++ b/app/scripts/views/icon-select-view.js @@ -57,7 +57,9 @@ const IconSelectView = Backbone.View.extend({ } this.downloadingFavicon = true; this.$el.find('.icon-select__icon-download>i').addClass('fa-spinner fa-spin'); - this.$el.find('.icon-select__icon-download').removeClass('icon-select__icon--download-error'); + this.$el + .find('.icon-select__icon-download') + .removeClass('icon-select__icon--download-error'); const url = this.getIconUrl(true); const img = document.createElement('img'); img.crossOrigin = 'Anonymous'; @@ -87,7 +89,10 @@ const IconSelectView = Backbone.View.extend({ if (!this.model.url) { return null; } - let url = this.model.url.replace(/([^\/:]\/.*)?$/, match => (match && match[0]) + '/favicon.ico'); + let url = this.model.url.replace( + /([^\/:]\/.*)?$/, + match => (match && match[0]) + '/favicon.ico' + ); if (url.indexOf('://') < 0) { url = 'http://' + url; } @@ -120,7 +125,9 @@ const IconSelectView = Backbone.View.extend({ reader.readAsDataURL(file); } else { this.$el.find('.icon-select__icon-select img').remove(); - this.$el.find('.icon-select__icon-select').removeClass('icon-select__icon--custom-selected'); + this.$el + .find('.icon-select__icon-select') + .removeClass('icon-select__icon--custom-selected'); } }, diff --git a/app/scripts/views/key-change-view.js b/app/scripts/views/key-change-view.js index d6e6e84c..3e40b025 100644 --- a/app/scripts/views/key-change-view.js +++ b/app/scripts/views/key-change-view.js @@ -33,10 +33,14 @@ const KeyChangeView = Backbone.View.extend({ fileName: this.model.file.get('name'), keyFileName: this.model.file.get('keyFileName'), title: this.model.expired ? Locale.keyChangeTitleExpired : Locale.keyChangeTitleRemote, - message: this.model.expired ? Locale.keyChangeMessageExpired : Locale.keyChangeMessageRemote, + message: this.model.expired + ? Locale.keyChangeMessageExpired + : Locale.keyChangeMessageRemote, repeat: repeat }); - this.$el.find('.key-change__keyfile-name').text(this.keyFileName ? ': ' + this.keyFileName : ''); + this.$el + .find('.key-change__keyfile-name') + .text(this.keyFileName ? ': ' + this.keyFileName : ''); this.inputEl = this.$el.find('.key-change__pass'); this.passwordInput.reset(); this.passwordInput.setElement(this.inputEl); diff --git a/app/scripts/views/list-search-view.js b/app/scripts/views/list-search-view.js index dff2f357..bd7c9011 100644 --- a/app/scripts/views/list-search-view.js +++ b/app/scripts/views/list-search-view.js @@ -83,7 +83,11 @@ const ListSearchView = Backbone.View.extend({ icon: 'sort-numeric-desc', loc: () => Locale.searchUpdated + ' ' + this.addArrow(Locale.searchNO) }, - { value: '-attachments', icon: 'sort-amount-desc', loc: () => Locale.searchAttachments }, + { + value: '-attachments', + icon: 'sort-amount-desc', + loc: () => Locale.searchAttachments + }, { value: '-rank', icon: 'sort-numeric-desc', loc: () => Locale.searchRank } ]; this.sortIcons = {}; @@ -306,7 +310,9 @@ const ListSearchView = Backbone.View.extend({ if (this.views.searchDropdown) { this.views.searchDropdown.remove(); this.views.searchDropdown = null; - this.$el.find('.list__search-btn-sort,.list__search-btn-new').removeClass('sel--active'); + this.$el + .find('.list__search-btn-sort,.list__search-btn-new') + .removeClass('sel--active'); } }, @@ -366,12 +372,18 @@ const ListSearchView = Backbone.View.extend({ options.push({ value: id, icon: tmpl.entry.icon, - text: hasMultipleFiles ? tmpl.file.get('name') + ' / ' + tmpl.entry.title : tmpl.entry.title + text: hasMultipleFiles + ? tmpl.file.get('name') + ' / ' + tmpl.entry.title + : tmpl.entry.title }); this.entryTemplates[id] = tmpl; }); options.sort(Comparators.stringComparator('text', true)); - options.push({ value: 'tmpl', icon: 'sticky-note-o', text: Format.capFirst(Locale.template) }); + options.push({ + value: 'tmpl', + icon: 'sticky-note-o', + text: Format.capFirst(Locale.template) + }); return options; }, diff --git a/app/scripts/views/list-view.js b/app/scripts/views/list-view.js index 051ff238..f83fb7e1 100644 --- a/app/scripts/views/list-view.js +++ b/app/scripts/views/list-view.js @@ -81,7 +81,11 @@ const ListView = Backbone.View.extend({ const itemTemplate = this.getItemTemplate(); const itemsTemplate = this.getItemsTemplate(); const noColor = AppSettingsModel.instance.get('colorfulIcons') ? '' : 'grayscale'; - const presenter = new EntryPresenter(this.getDescField(), noColor, this.model.activeEntryId); + const presenter = new EntryPresenter( + this.getDescField(), + noColor, + this.model.activeEntryId + ); const columns = {}; this.tableColumns.forEach(col => { if (col.enabled) { @@ -310,7 +314,9 @@ const ListView = Backbone.View.extend({ }, saveTableColumnsEnabled() { - const tableViewColumns = this.tableColumns.filter(column => column.enabled).map(column => column.name); + const tableViewColumns = this.tableColumns + .filter(column => column.enabled) + .map(column => column.name); AppSettingsModel.instance.set('tableViewColumns', tableViewColumns); } }); diff --git a/app/scripts/views/menu/menu-item-view.js b/app/scripts/views/menu/menu-item-view.js index 23a8cff8..56b75372 100644 --- a/app/scripts/views/menu/menu-item-view.js +++ b/app/scripts/views/menu/menu-item-view.js @@ -91,7 +91,8 @@ const MenuItemView = Backbone.View.extend({ }, changeIcon: function(model, icon) { - this.iconEl[0].className = 'menu__item-icon fa ' + (icon ? 'fa-' + icon : 'menu__item-icon--no-icon'); + this.iconEl[0].className = + 'menu__item-icon fa ' + (icon ? 'fa-' + icon : 'menu__item-icon--no-icon'); }, changeActive: function(model, active) { diff --git a/app/scripts/views/modal-view.js b/app/scripts/views/modal-view.js index 9c0d0228..ed2036a6 100644 --- a/app/scripts/views/modal-view.js +++ b/app/scripts/views/modal-view.js @@ -77,7 +77,9 @@ const ModalView = Backbone.View.extend({ }, closeWithResult: function(result) { - const checked = this.model.checkbox ? this.$el.find('#modal__check').is(':checked') : undefined; + const checked = this.model.checkbox + ? this.$el.find('#modal__check').is(':checked') + : undefined; this.trigger('result', result, checked); this.$el.addClass('modal--hidden'); this.undelegateEvents(); diff --git a/app/scripts/views/open-config-view.js b/app/scripts/views/open-config-view.js index 90008ef8..37a21a04 100644 --- a/app/scripts/views/open-config-view.js +++ b/app/scripts/views/open-config-view.js @@ -71,7 +71,9 @@ const OpenConfigView = Backbone.View.extend({ setError: function(err) { const errText = - err && err.notFound ? Locale.openConfigErrorNotFound : Locale.openConfigError.replace('{}', err); + err && err.notFound + ? Locale.openConfigErrorNotFound + : Locale.openConfigError.replace('{}', err); this.$el.find('.open__config-error').text(errText); } }); diff --git a/app/scripts/views/open-view.js b/app/scripts/views/open-view.js index abd1bf89..51b013f1 100644 --- a/app/scripts/views/open-view.js +++ b/app/scripts/views/open-view.js @@ -383,7 +383,10 @@ const OpenView = Backbone.View.extend({ body: fileInfo.get('modified') ? Locale.openRemoveLastQuestionModBody : Locale.openRemoveLastQuestionBody, - buttons: [{ result: 'yes', title: Locale.alertYes }, { result: '', title: Locale.alertNo }], + buttons: [ + { result: 'yes', title: Locale.alertYes }, + { result: '', title: Locale.alertNo } + ], success: () => { this.removeFile(id); } @@ -442,7 +445,10 @@ const OpenView = Backbone.View.extend({ e.preventDefault(); e.stopPropagation(); const dt = e.originalEvent.dataTransfer; - if (!dt.types || (dt.types.indexOf ? dt.types.indexOf('Files') === -1 : !dt.types.contains('Files'))) { + if ( + !dt.types || + (dt.types.indexOf ? dt.types.indexOf('Files') === -1 : !dt.types.contains('Files')) + ) { dt.dropEffect = 'none'; return; } @@ -498,7 +504,11 @@ const OpenView = Backbone.View.extend({ .toLowerCase() === 'key' ); if (dataFile) { - this.setFile(dataFile, keyFile, dataFile.path ? null : this.showLocalFileAlert.bind(this)); + this.setFile( + dataFile, + keyFile, + dataFile.path ? null : this.showLocalFileAlert.bind(this) + ); } }, @@ -604,7 +614,9 @@ const OpenView = Backbone.View.extend({ this.inputEl.attr('disabled', 'disabled'); this.busy = true; this.params.password = this.passwordInput.value; - this.afterPaint(this.model.openFile.bind(this.model, this.params, this.openDbComplete.bind(this))); + this.afterPaint( + this.model.openFile.bind(this.model, this.params, this.openDbComplete.bind(this)) + ); }, openDbComplete: function(err) { @@ -624,7 +636,11 @@ const OpenView = Backbone.View.extend({ } Alerts.error({ header: Locale.openError, - body: Locale.openErrorDescription + '' + body: + Locale.openErrorDescription + + '' }); } } else { @@ -701,7 +717,10 @@ const OpenView = Backbone.View.extend({ Alerts.error({ header: Locale.openError, body: - Locale.openListErrorBody + '' + Locale.openListErrorBody + + '' }); } return; @@ -786,7 +805,10 @@ const OpenView = Backbone.View.extend({ }, storage.getOpenConfig() ); - this.views.openConfig = new OpenConfigView({ el: this.$el.find('.open__config-wrap'), model: config }).render(); + this.views.openConfig = new OpenConfigView({ + el: this.$el.find('.open__config-wrap'), + model: config + }).render(); this.views.openConfig.on('cancel', this.closeConfig.bind(this)); this.views.openConfig.on('apply', this.applyConfig.bind(this)); this.$el.find('.open__pass-area').addClass('hide'); @@ -868,7 +890,10 @@ const OpenView = Backbone.View.extend({ moveOpenFileSelection: function(steps) { const lastOpenFiles = this.getLastOpenFiles(); - if (this.currentSelectedIndex + steps >= 0 && this.currentSelectedIndex + steps <= lastOpenFiles.length - 1) { + if ( + this.currentSelectedIndex + steps >= 0 && + this.currentSelectedIndex + steps <= lastOpenFiles.length - 1 + ) { this.currentSelectedIndex = this.currentSelectedIndex + steps; } diff --git a/app/scripts/views/settings/settings-file-view.js b/app/scripts/views/settings/settings-file-view.js index 76377b05..30f2cee6 100644 --- a/app/scripts/views/settings/settings-file-view.js +++ b/app/scripts/views/settings/settings-file-view.js @@ -51,7 +51,11 @@ const SettingsFileView = Backbone.View.extend({ appModel: null, initialize: function() { - this.listenTo(this.model, 'change:syncing change:syncError change:syncDate', this.deferRender); + this.listenTo( + this.model, + 'change:syncing change:syncError change:syncDate', + this.deferRender + ); }, render: function() { @@ -90,25 +94,34 @@ const SettingsFileView = Backbone.View.extend({ recycleBinEnabled: this.model.get('recycleBinEnabled'), backupEnabled: backup && backup.enabled, backupStorage: backup && backup.storage, - backupPath: (backup && backup.path) || DefaultBackupPath.replace('{name}', this.model.get('name')), + backupPath: + (backup && backup.path) || + DefaultBackupPath.replace('{name}', this.model.get('name')), backupSchedule: backup ? backup.schedule : DefaultBackupSchedule, historyMaxItems: this.model.get('historyMaxItems'), historyMaxSize: Math.round(this.model.get('historyMaxSize') / 1024 / 1024), keyEncryptionRounds: this.model.get('keyEncryptionRounds'), - keyChangeForce: this.model.get('keyChangeForce') > 0 ? this.model.get('keyChangeForce') : null, + keyChangeForce: + this.model.get('keyChangeForce') > 0 ? this.model.get('keyChangeForce') : null, kdfParameters: this.kdfParametersToUi(this.model.get('kdfParameters')), storageProviders: storageProviders, canBackup: canBackup }); if (!this.model.get('created')) { - this.$el.find('.settings__file-master-pass-warning').toggle(this.model.get('passwordChanged')); - this.$el.find('#settings__file-master-pass-warning-text').text(Locale.setFilePassChanged); + this.$el + .find('.settings__file-master-pass-warning') + .toggle(this.model.get('passwordChanged')); + this.$el + .find('#settings__file-master-pass-warning-text') + .text(Locale.setFilePassChanged); } this.renderKeyFileSelect(); }, kdfParametersToUi: function(kdfParameters) { - return kdfParameters ? _.extend({}, kdfParameters, { memory: Math.round(kdfParameters.memory / 1024) }) : null; + return kdfParameters + ? _.extend({}, kdfParameters, { memory: Math.round(kdfParameters.memory / 1024) }) + : null; }, renderKeyFileSelect: function() { @@ -219,7 +232,8 @@ const SettingsFileView = Backbone.View.extend({ if (err) { Alerts.error({ header: Locale.setFileSaveError, - body: Locale.setFileSaveErrorBody + ' ' + path + ': \n' + err + body: + Locale.setFileSaveErrorBody + ' ' + path + ': \n' + err }); } }); @@ -299,12 +313,17 @@ const SettingsFileView = Backbone.View.extend({ } const expName = this.model.get('name').toLowerCase(); const existingFile = _.find(files, file => { - return !file.dir && UrlUtil.getDataFileName(file.name).toLowerCase() === expName; + return ( + !file.dir && UrlUtil.getDataFileName(file.name).toLowerCase() === expName + ); }); if (existingFile) { Alerts.yesno({ header: Locale.setFileAlreadyExists, - body: Locale.setFileAlreadyExistsBody.replace('{}', this.model.escape('name')), + body: Locale.setFileAlreadyExistsBody.replace( + '{}', + this.model.escape('name') + ), success: () => { this.model.set('syncing', true); storage.remove(existingFile.path, err => { @@ -404,7 +423,9 @@ const SettingsFileView = Backbone.View.extend({ this.$el.find('.settings__file-master-pass-warning').hide(); } else { this.$el.find('#settings__file-confirm-master-pass-group').show(); - this.$el.find('#settings__file-master-pass-warning-text').text(Locale.setFilePassChange); + this.$el + .find('#settings__file-master-pass-warning-text') + .text(Locale.setFilePassChange); if (!this.model.get('created')) { this.$el.find('.settings__file-master-pass-warning').show(); } @@ -437,11 +458,15 @@ const SettingsFileView = Backbone.View.extend({ const masterPassword = this.$el.find('#settings__file-master-pass').val(); const confirmPassword = e.target.value; if (masterPassword === confirmPassword) { - this.$el.find('#settings__file-master-pass-warning-text').text(Locale.setFilePassChanged); + this.$el + .find('#settings__file-master-pass-warning-text') + .text(Locale.setFilePassChanged); this.$el.find('.settings__file-confirm-master-pass-warning').hide(); this.model.setPassword(kdbxweb.ProtectedValue.fromString(confirmPassword)); } else { - this.$el.find('#settings__file-master-pass-warning-text').text(Locale.setFilePassChange); + this.$el + .find('#settings__file-master-pass-warning-text') + .text(Locale.setFilePassChange); this.$el.find('.settings__file-confirm-master-pass-warning').show(); this.model.resetPassword(); } @@ -550,7 +575,11 @@ const SettingsFileView = Backbone.View.extend({ } Alerts.error({ title: title, - body: description + '' + body: + description + + '' }); } }); diff --git a/app/scripts/views/settings/settings-general-view.js b/app/scripts/views/settings/settings-general-view.js index 8c085fcc..bfa2b4eb 100644 --- a/app/scripts/views/settings/settings-general-view.js +++ b/app/scripts/views/settings/settings-general-view.js @@ -143,12 +143,22 @@ const SettingsGeneralView = Backbone.View.extend({ Format.dtStr(UpdateModel.instance.get('lastSuccessCheckDate')) ) + ': ' + - Locale.setGenLastCheckVer.replace('{}', UpdateModel.instance.get('lastVersion')); + Locale.setGenLastCheckVer.replace( + '{}', + UpdateModel.instance.get('lastVersion') + ); } return errMsg; case 'ok': - let msg = Locale.setGenCheckedAt + ' ' + Format.dtStr(UpdateModel.instance.get('lastCheckDate')) + ': '; - const cmp = SemVer.compareVersions(RuntimeInfo.version, UpdateModel.instance.get('lastVersion')); + let msg = + Locale.setGenCheckedAt + + ' ' + + Format.dtStr(UpdateModel.instance.get('lastCheckDate')) + + ': '; + const cmp = SemVer.compareVersions( + RuntimeInfo.version, + UpdateModel.instance.get('lastVersion') + ); if (cmp >= 0) { msg += Locale.setGenLatestVer; } else { @@ -196,7 +206,9 @@ const SettingsGeneralView = Backbone.View.extend({ const locale = e.target.value; if (locale === '...') { e.target.value = AppSettingsModel.instance.get('locale') || 'en'; - this.appModel.menu.select({ item: this.appModel.menu.pluginsSection.get('items').first() }); + this.appModel.menu.select({ + item: this.appModel.menu.pluginsSection.get('items').first() + }); return; } AppSettingsModel.instance.set('locale', locale); @@ -322,12 +334,16 @@ const SettingsGeneralView = Backbone.View.extend({ if (storage) { storage.setEnabled(e.target.checked); AppSettingsModel.instance.set(storage.name, storage.enabled); - this.$el.find('.settings__general-' + storage.name).toggleClass('hide', !e.target.checked); + this.$el + .find('.settings__general-' + storage.name) + .toggleClass('hide', !e.target.checked); } }, showAdvancedSettings: function() { - this.$el.find('.settings__general-show-advanced, .settings__general-advanced').toggleClass('hide'); + this.$el + .find('.settings__general-show-advanced, .settings__general-advanced') + .toggleClass('hide'); this.scrollToBottom(); }, @@ -352,7 +368,9 @@ const SettingsGeneralView = Backbone.View.extend({ if (this.views.logView) { this.views.logView.remove(); } - this.views.logView = new SettingsLogsView({ el: this.$el.find('.settings__general-advanced') }).render(); + this.views.logView = new SettingsLogsView({ + el: this.$el.find('.settings__general-advanced') + }).render(); this.scrollToBottom(); }, diff --git a/app/scripts/views/settings/settings-plugins-view.js b/app/scripts/views/settings/settings-plugins-view.js index bce6bfe3..06b43112 100644 --- a/app/scripts/views/settings/settings-plugins-view.js +++ b/app/scripts/views/settings/settings-plugins-view.js @@ -38,7 +38,11 @@ const SettingsPluginsView = Backbone.View.extend({ initialize() { this.listenTo(PluginManager, 'change', this.render.bind(this)); - this.listenTo(Backbone, 'plugin-gallery-load-complete', this.pluginGalleryLoadComplete.bind(this)); + this.listenTo( + Backbone, + 'plugin-gallery-load-complete', + this.pluginGalleryLoadComplete.bind(this) + ); }, render() { @@ -103,10 +107,16 @@ const SettingsPluginsView = Backbone.View.extend({ if (plugin.manifest.desktop && !RuntimeInfo.launcher) { return false; } - if (plugin.manifest.versionMin && SemVer.compareVersions(plugin.manifest.versionMin, RuntimeInfo.version) > 0) { + if ( + plugin.manifest.versionMin && + SemVer.compareVersions(plugin.manifest.versionMin, RuntimeInfo.version) > 0 + ) { return false; } - if (plugin.manifest.versionMax && SemVer.compareVersions(plugin.manifest.versionMax, RuntimeInfo.version) > 0) { + if ( + plugin.manifest.versionMax && + SemVer.compareVersions(plugin.manifest.versionMax, RuntimeInfo.version) > 0 + ) { return false; } return true; diff --git a/app/scripts/views/tag-view.js b/app/scripts/views/tag-view.js index c73d5600..a50f60e5 100644 --- a/app/scripts/views/tag-view.js +++ b/app/scripts/views/tag-view.js @@ -38,7 +38,10 @@ const TagView = Backbone.View.extend({ return; } if (/[;,:]/.test(title)) { - Alerts.error({ header: Locale.tagBadName, body: Locale.tagBadNameBody.replace('{}', '`,`, `;`, `:`') }); + Alerts.error({ + header: Locale.tagBadName, + body: Locale.tagBadNameBody.replace('{}', '`,`, `;`, `:`') + }); return; } if (this.appModel.tags.some(t => t.toLowerCase() === title.toLowerCase())) { diff --git a/app/styles/areas/_details.scss b/app/styles/areas/_details.scss index cb64d439..7f495304 100644 --- a/app/styles/areas/_details.scss +++ b/app/styles/areas/_details.scss @@ -279,7 +279,8 @@ } transition: background-color $slow-transition-out, border-color $slow-transition-out; .details__field--edit[active-mobile-action] & { - transition: background-color $slow-transition-in, border-color $slow-transition-in; + transition: background-color $slow-transition-in, + border-color $slow-transition-in; } .details__field--edit[active-mobile-action='apply'] & { @include th { diff --git a/app/styles/areas/_list.scss b/app/styles/areas/_list.scss index f7bf507a..fae4203b 100644 --- a/app/styles/areas/_list.scss +++ b/app/styles/areas/_list.scss @@ -175,22 +175,34 @@ &--custom { vertical-align: text-bottom; &.yellow { - @include filter(grayscale(1) sepia(1) hue-rotate(20deg) brightness(1.17) saturate(5.7)); + @include filter( + grayscale(1) sepia(1) hue-rotate(20deg) brightness(1.17) saturate(5.7) + ); } &.green { - @include filter(grayscale(1) sepia(1) hue-rotate(55deg) brightness(1.01) saturate(4.9)); + @include filter( + grayscale(1) sepia(1) hue-rotate(55deg) brightness(1.01) saturate(4.9) + ); } &.red { - @include filter(grayscale(1) sepia(1) hue-rotate(316deg) brightness(1.1) saturate(6)); + @include filter( + grayscale(1) sepia(1) hue-rotate(316deg) brightness(1.1) saturate(6) + ); } &.orange { - @include filter(grayscale(1) sepia(1) hue-rotate(355deg) brightness(0.92) saturate(5)); + @include filter( + grayscale(1) sepia(1) hue-rotate(355deg) brightness(0.92) saturate(5) + ); } &.blue { - @include filter(grayscale(1) sepia(1) hue-rotate(180deg) brightness(0.9) saturate(5)); + @include filter( + grayscale(1) sepia(1) hue-rotate(180deg) brightness(0.9) saturate(5) + ); } &.violet { - @include filter(grayscale(1) sepia(1) hue-rotate(238deg) brightness(1) saturate(6.2)); + @include filter( + grayscale(1) sepia(1) hue-rotate(238deg) brightness(1) saturate(6.2) + ); } } } diff --git a/app/styles/base/_theme-vars.scss b/app/styles/base/_theme-vars.scss index 2cdaee09..c7b3ddba 100644 --- a/app/styles/base/_theme-vars.scss +++ b/app/styles/base/_theme-vars.scss @@ -2,7 +2,12 @@ @return map-merge( $t, ( - muted-color: mix(map-get($t, medium-color), map-get($t, background-color), map-get($t, mute-percent)), + muted-color: + mix( + map-get($t, medium-color), + map-get($t, background-color), + map-get($t, mute-percent) + ), muted-color-border: mix( map-get($t, medium-color), @@ -29,13 +34,20 @@ base-border-color: mix(map-get($t, medium-color), map-get($t, background-color), 50%), accent-border-color: mix(map-get($t, medium-color), map-get($t, background-color), 65%), light-border-color: - mix(map-get($t, medium-color), map-get($t, background-color), map-get($t, light-border-percent)), + mix( + map-get($t, medium-color), + map-get($t, background-color), + map-get($t, light-border-percent) + ), form-box-shadow-color-focus: lightness-alpha(map-get($t, action-color), -5%, -0.3), form-box-shadow-color-focus-error: lightness-alpha(map-get($t, error-color), -5%, -0.3), dropdown-box-shadow-color: rgba(map-get($t, medium-color), 0.05), - secondary-background-color: mix(map-get($t, medium-color), map-get($t, background-color), 10%), - intermediate-background-color: mix(map-get($t, medium-color), map-get($t, background-color), 3%), - intermediate-pressed-background-color: mix(map-get($t, medium-color), map-get($t, background-color), 2.6%), + secondary-background-color: + mix(map-get($t, medium-color), map-get($t, background-color), 10%), + intermediate-background-color: + mix(map-get($t, medium-color), map-get($t, background-color), 3%), + intermediate-pressed-background-color: + mix(map-get($t, medium-color), map-get($t, background-color), 2.6%), disabled-background-color: shade(map-get($t, background-color), 5%), action-background-color-focus: shade(map-get($t, action-color), 20%), action-background-color-focus-tr: rgba(shade(map-get($t, action-color), 20%), 0.1), diff --git a/app/styles/base/_variables.scss b/app/styles/base/_variables.scss index 930ea501..7fc4ffc8 100644 --- a/app/styles/base/_variables.scss +++ b/app/styles/base/_variables.scss @@ -1,6 +1,7 @@ // Typography -$base-font-family: -apple-system, 'BlinkMacSystemFont', 'Helvetica Neue', 'Helvetica', 'Roboto', 'Arial', - 'Microsoft YaHei', '微软雅黑', 'PingFang SC', 'Hiragino Sans GB', 'STXihei', '华文细黑', sans-serif; +$base-font-family: -apple-system, 'BlinkMacSystemFont', 'Helvetica Neue', 'Helvetica', 'Roboto', + 'Arial', 'Microsoft YaHei', '微软雅黑', 'PingFang SC', 'Hiragino Sans GB', 'STXihei', '华文细黑', + sans-serif; $heading-font-family: $base-font-family; $monospace-font-family: 'SFMono-Regular', Monaco, Consolas, 'Lucida Console', monospace; diff --git a/build/tasks/grunt-sign-archive.js b/build/tasks/grunt-sign-archive.js index 6f1cd18e..2d64565b 100644 --- a/build/tasks/grunt-sign-archive.js +++ b/build/tasks/grunt-sign-archive.js @@ -12,7 +12,9 @@ module.exports = function(grunt) { const data = file.slice(0, ix); sign(grunt, data).then(signature => { signature = Buffer.from(signature.toString('hex'), 'binary'); - if (signature.byteLength !== Buffer.from(this.options().signature, 'binary').byteLength) { + if ( + signature.byteLength !== Buffer.from(this.options().signature, 'binary').byteLength + ) { grunt.warn('Bad signature length'); return; } diff --git a/build/tasks/grunt-sign-dist.js b/build/tasks/grunt-sign-dist.js index 8a1a6e8a..69521c17 100644 --- a/build/tasks/grunt-sign-dist.js +++ b/build/tasks/grunt-sign-dist.js @@ -27,8 +27,14 @@ module.exports = function(grunt) { grunt.log.writeln(basename); } - grunt.file.write(file.dest, results.map(line => `${line.digest} *${line.basename}`).join('\n')); - grunt.file.write(opt.sign, results.map(line => `${line.signature} *${line.basename}`).join('\n')); + grunt.file.write( + file.dest, + results.map(line => `${line.digest} *${line.basename}`).join('\n') + ); + grunt.file.write( + opt.sign, + results.map(line => `${line.signature} *${line.basename}`).join('\n') + ); } done(); diff --git a/build/tasks/grunt-sign-exe.js b/build/tasks/grunt-sign-exe.js index dd7d5f1d..e8ac63a1 100644 --- a/build/tasks/grunt-sign-exe.js +++ b/build/tasks/grunt-sign-exe.js @@ -24,32 +24,38 @@ const fs = require('fs'); module.exports = function(grunt) { - grunt.registerMultiTask('sign-exe', 'Signs exe file with authenticode certificate', async function() { - const opt = this.options(); - const done = this.async(); - if (opt.pvk) { - const keytar = require('keytar'); - keytar - .getPassword(opt.keytarPasswordService, opt.keytarPasswordAccount) - .then(password => { - if (!password) { - return grunt.warn('Code sign password not found'); - } - const promises = Object.keys(opt.files).map(file => signFile(file, opt.files[file], opt, password)); - Promise.all(promises).then(done); - }) - .catch(e => { - grunt.warn('Code sign error: ' + e); - }); - } else { - const sign = require('../util/sign'); - const pin = await sign.getPin(); - for (const file of Object.keys(opt.files)) { - await signFile(file, opt.files[file], opt, pin); + grunt.registerMultiTask( + 'sign-exe', + 'Signs exe file with authenticode certificate', + async function() { + const opt = this.options(); + const done = this.async(); + if (opt.pvk) { + const keytar = require('keytar'); + keytar + .getPassword(opt.keytarPasswordService, opt.keytarPasswordAccount) + .then(password => { + if (!password) { + return grunt.warn('Code sign password not found'); + } + const promises = Object.keys(opt.files).map(file => + signFile(file, opt.files[file], opt, password) + ); + Promise.all(promises).then(done); + }) + .catch(e => { + grunt.warn('Code sign error: ' + e); + }); + } else { + const sign = require('../util/sign'); + const pin = await sign.getPin(); + for (const file of Object.keys(opt.files)) { + await signFile(file, opt.files[file], opt, pin); + } + done(); } - done(); } - }); + ); function signFile(file, name, opt, password) { const signedFile = file + '.sign'; diff --git a/build/tasks/grunt-sign-html.js b/build/tasks/grunt-sign-html.js index fba779c2..1134b6e1 100644 --- a/build/tasks/grunt-sign-html.js +++ b/build/tasks/grunt-sign-html.js @@ -24,7 +24,9 @@ module.exports = function(grunt) { }) .catch(e => { if (e === 'Cannot find PIN') { - grunt.warn('Error signing app html. To build without sign, please launch grunt with --skip-sign.'); + grunt.warn( + 'Error signing app html. To build without sign, please launch grunt with --skip-sign.' + ); } else { grunt.warn('Sign error: ' + e); } diff --git a/build/tasks/grunt-validate-desktop-update.js b/build/tasks/grunt-validate-desktop-update.js index 45aab479..d20161d1 100644 --- a/build/tasks/grunt-validate-desktop-update.js +++ b/build/tasks/grunt-validate-desktop-update.js @@ -1,54 +1,63 @@ module.exports = function(grunt) { - grunt.registerMultiTask('validate-desktop-update', 'Validates desktop update package', function() { - const path = require('path'); - const crypto = require('crypto'); - const fs = require('fs'); - const done = this.async(); - const StreamZip = require(path.resolve(__dirname, '../../desktop/node_modules/node-stream-zip')); - const zip = new StreamZip({ file: this.options().file, storeEntries: true }); - const expFiles = this.options().expected; - const expFilesCount = this.options().expectedCount; - const publicKey = fs.readFileSync(this.options().publicKey, 'binary'); - const zipFileData = fs.readFileSync(this.options().file); - zip.on('error', err => { - grunt.warn(err); - }); - zip.on('ready', () => { - let valid = true; - if (!zip.comment) { - grunt.warn('No comment in ZIP'); - return; - } - if (zip.comment.length !== 512) { - grunt.warn('Bad comment length in ZIP'); - return; - } - const verify = crypto.createVerify('RSA-SHA256'); - verify.write(zipFileData.slice(0, zip.centralDirectory.headerOffset + 22)); - verify.end(); - const signature = Buffer.from(zip.comment, 'hex'); - if (!verify.verify(publicKey, signature)) { - grunt.warn('Invalid ZIP signature'); - return; - } - if (zip.entriesCount !== expFilesCount) { - grunt.warn(`ZIP contains ${zip.entriesCount} entries, expected ${expFilesCount}`); - valid = false; - } - expFiles.forEach(entry => { - try { - if (!zip.entryDataSync(entry)) { - grunt.warn('Corrupted entry in desktop update archive: ' + entry); - valid = false; - } - } catch (e) { - grunt.warn('Entry not found in desktop update archive: ' + entry); + grunt.registerMultiTask( + 'validate-desktop-update', + 'Validates desktop update package', + function() { + const path = require('path'); + const crypto = require('crypto'); + const fs = require('fs'); + const done = this.async(); + const StreamZip = require(path.resolve( + __dirname, + '../../desktop/node_modules/node-stream-zip' + )); + const zip = new StreamZip({ file: this.options().file, storeEntries: true }); + const expFiles = this.options().expected; + const expFilesCount = this.options().expectedCount; + const publicKey = fs.readFileSync(this.options().publicKey, 'binary'); + const zipFileData = fs.readFileSync(this.options().file); + zip.on('error', err => { + grunt.warn(err); + }); + zip.on('ready', () => { + let valid = true; + if (!zip.comment) { + grunt.warn('No comment in ZIP'); + return; + } + if (zip.comment.length !== 512) { + grunt.warn('Bad comment length in ZIP'); + return; + } + const verify = crypto.createVerify('RSA-SHA256'); + verify.write(zipFileData.slice(0, zip.centralDirectory.headerOffset + 22)); + verify.end(); + const signature = Buffer.from(zip.comment, 'hex'); + if (!verify.verify(publicKey, signature)) { + grunt.warn('Invalid ZIP signature'); + return; + } + if (zip.entriesCount !== expFilesCount) { + grunt.warn( + `ZIP contains ${zip.entriesCount} entries, expected ${expFilesCount}` + ); valid = false; } + expFiles.forEach(entry => { + try { + if (!zip.entryDataSync(entry)) { + grunt.warn('Corrupted entry in desktop update archive: ' + entry); + valid = false; + } + } catch (e) { + grunt.warn('Entry not found in desktop update archive: ' + entry); + valid = false; + } + }); + if (valid) { + done(); + } }); - if (valid) { - done(); - } - }); - }); + } + ); }; diff --git a/desktop/app.js b/desktop/app.js index 95c75b39..b180035b 100644 --- a/desktop/app.js +++ b/desktop/app.js @@ -25,7 +25,9 @@ const tempUserDataPath = path.join(userDataDir, 'temp'); const tempUserDataPathRand = Date.now().toString() + Math.random().toString(); const systemNotificationIds = []; -let htmlPath = process.argv.filter(arg => arg.startsWith('--htmlpath=')).map(arg => arg.replace('--htmlpath=', ''))[0]; +let htmlPath = process.argv + .filter(arg => arg.startsWith('--htmlpath=')) + .map(arg => arg.replace('--htmlpath=', ''))[0]; if (!htmlPath) { htmlPath = 'file://' + path.join(__dirname, 'index.html'); } @@ -335,7 +337,10 @@ function setMenu() { }, { label: 'Window', - submenu: [{ accelerator: 'CmdOrCtrl+M', role: 'minimize' }, { accelerator: 'Command+W', role: 'close' }] + submenu: [ + { accelerator: 'CmdOrCtrl+M', role: 'minimize' }, + { accelerator: 'Command+W', role: 'close' } + ] } ]; const menu = electron.Menu.buildFromTemplate(template); @@ -406,15 +411,21 @@ function subscribePowerEvents() { emitBackboneEvent('power-monitor-resume'); }); if (process.platform === 'darwin') { - const id = electron.systemPreferences.subscribeNotification('com.apple.screenIsLocked', () => { - emitBackboneEvent('os-lock'); - }); + const id = electron.systemPreferences.subscribeNotification( + 'com.apple.screenIsLocked', + () => { + emitBackboneEvent('os-lock'); + } + ); systemNotificationIds.push(id); } } function setEnv() { - if (process.platform === 'linux' && ['Pantheon', 'Unity:Unity7'].indexOf(process.env.XDG_CURRENT_DESKTOP) !== -1) { + if ( + process.platform === 'linux' && + ['Pantheon', 'Unity:Unity7'].indexOf(process.env.XDG_CURRENT_DESKTOP) !== -1 + ) { // https://github.com/electron/electron/issues/9046 process.env.XDG_CURRENT_DESKTOP = 'Unity'; } @@ -509,8 +520,10 @@ function coerceMainWindowPositionToConnectedDisplay() { // 160px width and 2/3s the title bar height should be enough that the user can grab it for (let i = 0; i < displays.length; ++i) { const workArea = displays[i].workArea; - const overlapWidth = Math.min(tbRight, workArea.x + workArea.width) - Math.max(tbLeft, workArea.x); - const overlapHeight = Math.min(tbBottom, workArea.y + workArea.height) - Math.max(tbTop, workArea.y); + const overlapWidth = + Math.min(tbRight, workArea.x + workArea.width) - Math.max(tbLeft, workArea.x); + const overlapHeight = + Math.min(tbBottom, workArea.y + workArea.height) - Math.max(tbTop, workArea.y); if (overlapWidth >= 160 && 3 * overlapHeight >= 2 * (tbBottom - tbTop)) return; } // If we get here, no display contains a big enough strip of the title bar diff --git a/desktop/main.js b/desktop/main.js index 784e71b5..72516b21 100644 --- a/desktop/main.js +++ b/desktop/main.js @@ -22,11 +22,15 @@ try { } catch (e) {} if (userPackageStat) { const packageStat = fs.statSync(appFilePath); - const userPackageStatTime = Math.max(userPackageStat.mtime.getTime(), userPackageStat.ctime.getTime()); + const userPackageStatTime = Math.max( + userPackageStat.mtime.getTime(), + userPackageStat.ctime.getTime() + ); const packageStatTime = Math.max(packageStat.mtime.getTime(), packageStat.ctime.getTime()); if (userPackageStatTime > packageStatTime) { let versionLocal = require('./package.json').version; - let versionUserData = require(path.join(userDataAppArchivePath, 'package.json')).version; + let versionUserData = require(path.join(userDataAppArchivePath, 'package.json')) + .version; versionLocal = versionLocal.split('.'); versionUserData = versionUserData.split('.'); for (let i = 0; i < versionLocal.length; i++) { diff --git a/plugins/examples/settings/plugin.js b/plugins/examples/settings/plugin.js index 4a4bce7c..b4662047 100644 --- a/plugins/examples/settings/plugin.js +++ b/plugins/examples/settings/plugin.js @@ -5,25 +5,32 @@ */ module.exports.getSettings = function() { - return [{ - name: 'MyText', - label: 'Text setting', - type: 'text', - maxlength: 20, - placeholder: 'Please enter something', - value: '' - }, { - name: 'MySel', - label: 'Select setting', - type: 'select', - options: [{value: 'apple', label: 'Green apple'}, {value: 'banana', label: 'Yellow banana'}], - value: 'banana' - }, { - name: 'MyCheckbox', - label: 'Checkbox setting', - type: 'checkbox', - value: true - }]; + return [ + { + name: 'MyText', + label: 'Text setting', + type: 'text', + maxlength: 20, + placeholder: 'Please enter something', + value: '' + }, + { + name: 'MySel', + label: 'Select setting', + type: 'select', + options: [ + { value: 'apple', label: 'Green apple' }, + { value: 'banana', label: 'Yellow banana' } + ], + value: 'banana' + }, + { + name: 'MyCheckbox', + label: 'Checkbox setting', + type: 'checkbox', + value: true + } + ]; }; module.exports.setSettings = function(changes) { @@ -32,9 +39,7 @@ module.exports.setSettings = function(changes) { // 1. when any of settings fields is modified by user // 2. after plugin startup, with saved values // only changed settings will be passed - // example: { MyText: 'value', MySel: 'selected-value', MyCheckbox: true } }; -module.exports.uninstall = function() { -}; +module.exports.uninstall = function() {}; diff --git a/plugins/keeweb-plugin/keeweb-plugin.js b/plugins/keeweb-plugin/keeweb-plugin.js index 4cc90a94..236084fe 100644 --- a/plugins/keeweb-plugin/keeweb-plugin.js +++ b/plugins/keeweb-plugin/keeweb-plugin.js @@ -18,9 +18,15 @@ const pkg = require('./package.json'); const op = args.shift(); const bumpVersion = args.some(arg => arg === '--bump-version'); -const privateKeyPath = args.filter(arg => arg.startsWith('--private-key=')).map(arg => arg.replace('--private-key=', ''))[0]; -const signerModule = args.filter(arg => arg.startsWith('--signer-module=')).map(arg => arg.replace('--signer-module=', ''))[0]; -const serverPort = args.filter(arg => arg.startsWith('--port=')).map(arg => arg.replace('--port=', ''))[0]; +const privateKeyPath = args + .filter(arg => arg.startsWith('--private-key=')) + .map(arg => arg.replace('--private-key=', ''))[0]; +const signerModule = args + .filter(arg => arg.startsWith('--signer-module=')) + .map(arg => arg.replace('--signer-module=', ''))[0]; +const serverPort = args + .filter(arg => arg.startsWith('--port=')) + .map(arg => arg.replace('--port=', ''))[0]; showBanner(); @@ -83,19 +89,24 @@ function signPlugin(packageName) { }); }); } - signPromise.then(changed => { - if (changed) { - if (bumpVersion) { - manifest.version = manifest.version.replace(/\d+$/, v => +v + 1); + signPromise + .then(changed => { + if (changed) { + if (bumpVersion) { + manifest.version = manifest.version.replace(/\d+$/, v => +v + 1); + } + fs.writeFileSync( + path.join(packageName, 'manifest.json'), + JSON.stringify(manifest, null, 2) + ); + console.log('Done, package manifest updated'); + } else { + console.log('No changes'); } - fs.writeFileSync(path.join(packageName, 'manifest.json'), JSON.stringify(manifest, null, 2)); - console.log('Done, package manifest updated'); - } else { - console.log('No changes'); - } - }).catch(e => { - console.error('Error', e); - }); + }) + .catch(e => { + console.error('Error', e); + }); } function signResource(packageName, fileName) { @@ -104,7 +115,10 @@ function signResource(packageName, fileName) { if (signerModule) { return require(signerModule)(data); } else { - const privateKey = fs.readFileSync(privateKeyPath || path.join(packageName, 'private_key.pem'), 'binary'); + const privateKey = fs.readFileSync( + privateKeyPath || path.join(packageName, 'private_key.pem'), + 'binary' + ); return Promise.resolve().then(() => { const sign = crypto.createSign('RSA-SHA256'); sign.write(data); @@ -151,14 +165,20 @@ function servePlugin(packageName) { } else { https.get('https://app.keeweb.info', kwRes => { if (kwRes.statusCode !== 200) { - console.error('Error loading https://app.keeweb.info: HTTP status ' + kwRes.statusCode); + console.error( + 'Error loading https://app.keeweb.info: HTTP status ' + kwRes.statusCode + ); res.writeHead(500); - return res.end('Error loading https://app.keeweb.info: HTTP status ' + kwRes.statusCode); + return res.end( + 'Error loading https://app.keeweb.info: HTTP status ' + kwRes.statusCode + ); } const data = []; kwRes.on('data', chunk => data.push(chunk)); kwRes.on('end', () => { - keeWebHtmlCached = Buffer.concat(data).toString('utf8').replace('(no-config)', 'config.json'); + keeWebHtmlCached = Buffer.concat(data) + .toString('utf8') + .replace('(no-config)', 'config.json'); serveKeeWebHtml(res); }); kwRes.on('error', e => { @@ -177,38 +197,43 @@ function servePlugin(packageName) { res.writeHead(200); res.end(`{"settings":{},"plugins":[{"url":"/"}]}`); }; - https.createServer(options, (req, res) => { - console.log('GET', req.connection.remoteAddress, req.url); - const filePath = path.resolve(packageName, '.' + req.url.replace(/\.\./g, '').replace(/\?.*/, '')); - const packagePath = path.resolve(packageName); - if (!filePath.startsWith(packagePath)) { - res.writeHead(404); - res.end('Not found'); - return; - } - if (req.url === '/') { - return serveKeeWebHtml(res); - } else if (req.url === '/manifest.appcache') { - return serveManifestAppCache(res); - } else if (req.url === '/config.json') { - return serveConfig(res); - } - fs.readFile(filePath, (err, data) => { - if (err) { + https + .createServer(options, (req, res) => { + console.log('GET', req.connection.remoteAddress, req.url); + const filePath = path.resolve( + packageName, + '.' + req.url.replace(/\.\./g, '').replace(/\?.*/, '') + ); + const packagePath = path.resolve(packageName); + if (!filePath.startsWith(packagePath)) { res.writeHead(404); res.end('Not found'); - } else { - res.setHeader('Access-Control-Allow-Origin', '*'); - res.setHeader('Access-Control-Allow-Credentials', true); - res.setHeader('Access-Control-Allow-Methods', 'GET'); - res.setHeader('Content-type', 'text/plain'); - res.writeHead(200); - res.end(data); + return; } - }); - }).listen(port); + if (req.url === '/') { + return serveKeeWebHtml(res); + } else if (req.url === '/manifest.appcache') { + return serveManifestAppCache(res); + } else if (req.url === '/config.json') { + return serveConfig(res); + } + fs.readFile(filePath, (err, data) => { + if (err) { + res.writeHead(404); + res.end('Not found'); + } else { + res.setHeader('Access-Control-Allow-Origin', '*'); + res.setHeader('Access-Control-Allow-Credentials', true); + res.setHeader('Access-Control-Allow-Methods', 'GET'); + res.setHeader('Content-type', 'text/plain'); + res.writeHead(200); + res.end(data); + } + }); + }) + .listen(port); console.log(`Open this URL in your browser or add it to KeeWeb: https://127.0.0.1:${port}`); - console.log('If you see a browser warning about an unsafe website, click Proceed, it\'s safe.'); + console.log("If you see a browser warning about an unsafe website, click Proceed, it's safe."); } function getPackageArg() { diff --git a/util/copy-languages.js b/util/copy-languages.js index 847ed95e..7eb53b87 100644 --- a/util/copy-languages.js +++ b/util/copy-languages.js @@ -3,5 +3,8 @@ const fs = require('fs'); const langs = ['de-DE', 'fr-FR']; for (const lang of langs) { - fs.writeFileSync(`app/scripts/locales/${lang}.json`, fs.readFileSync(`../keeweb-plugins/docs/translations/${lang}/${lang}.json`)); + fs.writeFileSync( + `app/scripts/locales/${lang}.json`, + fs.readFileSync(`../keeweb-plugins/docs/translations/${lang}/${lang}.json`) + ); } diff --git a/util/prettier.sh b/util/prettier.sh new file mode 100755 index 00000000..8ecd923d --- /dev/null +++ b/util/prettier.sh @@ -0,0 +1,11 @@ +prettier --write \ + 'app/**/*.js' \ + 'app/**/*.scss' \ + 'app/**/*.json' \ + 'app/**/*.html' \ + 'build/**/*.js' \ + 'desktop/**/*.js' \ + 'plugins/**/*.js' \ + 'util/**/*.js' \ + '*.js' \ + 'package.json' diff --git a/webpack.config.js b/webpack.config.js index e3cd5504..4f7a817e 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -83,18 +83,26 @@ function config(grunt, mode = 'production') { replacements: [ { pattern: /@@VERSION/g, - replacement: () => pkg.version + (grunt.option('beta') ? '-beta' : '') + replacement: () => + pkg.version + (grunt.option('beta') ? '-beta' : '') + }, + { + pattern: /@@BETA/g, + replacement: () => (grunt.option('beta') ? '1' : '') }, - { pattern: /@@BETA/g, replacement: () => (grunt.option('beta') ? '1' : '') }, { pattern: /@@DATE/g, replacement: () => dt }, { pattern: /@@COMMIT/g, - replacement: () => grunt.config.get('gitinfo.local.branch.current.shortSHA') + replacement: () => + grunt.config.get('gitinfo.local.branch.current.shortSHA') } ] }) }, - { test: /baron(\.min)?\.js$/, loader: 'exports-loader?baron; delete window.baron;' }, + { + test: /baron(\.min)?\.js$/, + loader: 'exports-loader?baron; delete window.baron;' + }, { test: /pikaday\.js$/, loader: 'uglify-loader' }, { test: /handlebars/, loader: 'strip-sourcemap-loader' }, {