pull/1233/head
antelle 4 years ago
parent 176c2a6edd
commit fa4ff0b0c3

@ -1,7 +1,7 @@
{
"tabWidth": 4,
"singleQuote": true,
"printWidth": 120,
"printWidth": 100,
"trailingComma": "none",
"quoteProps": "preserve"
}

@ -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: '<html', replacement: '<html manifest="manifest.appcache"' }] },
options: {
replacements: [
{ pattern: '<html', replacement: '<html manifest="manifest.appcache"' }
]
},
files: { 'dist/index.html': 'dist/index.html' }
},
'desktop-html': {
options: { replacements: [{ pattern: ' manifest="manifest.appcache"', replacement: '' }] },
options: {
replacements: [{ pattern: ' manifest="manifest.appcache"', replacement: '' }]
},
files: { 'tmp/desktop/app/index.html': 'dist/index.html' }
},
'desktop-public-key': {
@ -187,7 +201,13 @@ module.exports = function(grunt) {
{
pattern: "'@@PUBLIC_KEY_CONTENT'",
replacement:
'`' + fs.readFileSync('app/resources/public-key.pem', { encoding: 'utf8' }).trim() + '`'
'`' +
fs
.readFileSync('app/resources/public-key.pem', {
encoding: 'utf8'
})
.trim() +
'`'
}
]
},
@ -195,7 +215,12 @@ module.exports = function(grunt) {
},
'cordova-html': {
options: {
replacements: [{ pattern: '<script', replacement: '<script src="cordova.js"></script><script' }]
replacements: [
{
pattern: '<script',
replacement: '<script src="cordova.js"></script><script'
}
]
},
files: { 'tmp/cordova/app/index.html': 'dist/index.html' }
}
@ -305,7 +330,10 @@ module.exports = function(grunt) {
level: 6
},
'desktop-update': {
options: { archive: 'dist/desktop/UpdateDesktop.zip', comment: zipCommentPlaceholder },
options: {
archive: 'dist/desktop/UpdateDesktop.zip',
comment: zipCommentPlaceholder
},
files: [{ cwd: 'tmp/desktop/update', src: '**', expand: true, nonull: true }]
},
'win32-x64': {
@ -341,7 +369,12 @@ module.exports = function(grunt) {
window: { size: { width: 658, height: 498 } },
contents: [
{ x: 438, y: 344, type: 'link', path: '/Applications' },
{ x: 192, y: 344, type: 'file', path: 'tmp/desktop/KeeWeb-darwin-x64/KeeWeb.app' }
{
x: 192,
y: 344,
type: 'file',
path: 'tmp/desktop/KeeWeb-darwin-x64/KeeWeb.app'
}
]
},
app: {
@ -479,7 +512,11 @@ module.exports = function(grunt) {
desktop: {
options: {
file: 'dist/desktop/UpdateDesktop.zip',
expected: ['app.asar', 'helper/darwin/KeeWebHelper', 'helper/win32/KeeWebHelper.exe'],
expected: [
'app.asar',
'helper/darwin/KeeWebHelper',
'helper/win32/KeeWebHelper.exe'
],
expectedCount: 7,
publicKey: 'app/resources/public-key.pem'
}
@ -505,8 +542,10 @@ module.exports = function(grunt) {
files: {
'tmp/desktop/KeeWeb-win32-x64/KeeWeb.exe': 'KeeWeb',
'tmp/desktop/KeeWeb-win32-x64/ffmpeg.dll': '',
'tmp/desktop/KeeWeb-win32-x64/libEGL.dll': 'ANGLE libEGL Dynamic Link Library',
'tmp/desktop/KeeWeb-win32-x64/libGLESv2.dll': 'ANGLE libGLESv2 Dynamic Link Library',
'tmp/desktop/KeeWeb-win32-x64/libEGL.dll':
'ANGLE libEGL Dynamic Link Library',
'tmp/desktop/KeeWeb-win32-x64/libGLESv2.dll':
'ANGLE libGLESv2 Dynamic Link Library',
'tmp/desktop/KeeWeb-win32-x64/osmesa.dll': ''
}
}
@ -516,8 +555,10 @@ module.exports = function(grunt) {
files: {
'tmp/desktop/KeeWeb-win32-ia32/KeeWeb.exe': 'KeeWeb',
'tmp/desktop/KeeWeb-win32-ia32/ffmpeg.dll': '',
'tmp/desktop/KeeWeb-win32-ia32/libEGL.dll': 'ANGLE libEGL Dynamic Link Library',
'tmp/desktop/KeeWeb-win32-ia32/libGLESv2.dll': 'ANGLE libGLESv2 Dynamic Link Library',
'tmp/desktop/KeeWeb-win32-ia32/libEGL.dll':
'ANGLE libEGL Dynamic Link Library',
'tmp/desktop/KeeWeb-win32-ia32/libGLESv2.dll':
'ANGLE libGLESv2 Dynamic Link Library',
'tmp/desktop/KeeWeb-win32-ia32/osmesa.dll': ''
}
}
@ -557,7 +598,10 @@ module.exports = function(grunt) {
sign: 'dist/desktop/Verify.sign.sha256'
},
files: {
'dist/desktop/Verify.sha256': ['dist/desktop/KeeWeb-*', 'dist/desktop/UpdateDesktop.zip']
'dist/desktop/Verify.sha256': [
'dist/desktop/KeeWeb-*',
'dist/desktop/UpdateDesktop.zip'
]
}
}
}

@ -1,40 +1,74 @@
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>KeeWeb</title>
<meta name="application-name" content="KeeWeb">
<meta name="kw-signature" content="">
<meta name="kw-config" content="(no-config)">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="KeeWeb">
<meta name="theme-color" content="#6386ec">
<meta name="msapplication-config" content="browserconfig.xml">
<meta name="msapplication-TileColor" content="#6386ec">
<link rel="apple-touch-icon" sizes="180x180" href="icons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png">
<link rel="mask-icon" href="icons/safari-pinned-tab.svg" color="#6386ec">
<link rel="apple-touch-startup-image" href="icons/splash-640x1136.png" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/splash-750x1294.png" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/splash-1242x2148.png" media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/splash-1125x2436.png" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/splash-1536x2048.png" media="(min-device-width: 768px) and (max-device-width: 1024px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/splash-1668x2224.png" media="(min-device-width: 834px) and (max-device-width: 834px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="apple-touch-startup-image" href="icons/splash-2048x2732.png" media="(min-device-width: 1024px) and (max-device-width: 1024px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="manifest" href="manifest.json">
<link rel="stylesheet" href="css/app.css?__inline=true" />
<script src="js/vendor.js?__inline=true"></script>
<script src="js/app.js?__inline=true"></script>
<script src="js/runtime.js?__inline=true"></script>
</head>
<body class="th-d">
<noscript>
<h1>KeeWeb</h1>
<p>KeeWeb is a password manager written in JavaScript. Please enable JavaScript to run it.</p>
</noscript>
</body>
<head lang="en">
<meta charset="UTF-8" />
<title>KeeWeb</title>
<meta name="application-name" content="KeeWeb" />
<meta name="kw-signature" content="" />
<meta name="kw-config" content="(no-config)" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="KeeWeb" />
<meta name="theme-color" content="#6386ec" />
<meta name="msapplication-config" content="browserconfig.xml" />
<meta name="msapplication-TileColor" content="#6386ec" />
<link rel="apple-touch-icon" sizes="180x180" href="icons/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png" />
<link rel="mask-icon" href="icons/safari-pinned-tab.svg" color="#6386ec" />
<link
rel="apple-touch-startup-image"
href="icons/splash-640x1136.png"
media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="icons/splash-750x1294.png"
media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="icons/splash-1242x2148.png"
media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="icons/splash-1125x2436.png"
media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="icons/splash-1536x2048.png"
media="(min-device-width: 768px) and (max-device-width: 1024px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="icons/splash-1668x2224.png"
media="(min-device-width: 834px) and (max-device-width: 834px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="icons/splash-2048x2732.png"
media="(min-device-width: 1024px) and (max-device-width: 1024px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link rel="manifest" href="manifest.json" />
<link rel="stylesheet" href="css/app.css?__inline=true" />
<script src="js/vendor.js?__inline=true"></script>
<script src="js/app.js?__inline=true"></script>
<script src="js/runtime.js?__inline=true"></script>
</head>
<body class="th-d">
<noscript>
<h1>KeeWeb</h1>
<p>
KeeWeb is a password manager written in JavaScript. Please enable JavaScript to run
it.
</p>
</noscript>
</body>
</html>

@ -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"
}
]
}

@ -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) {

@ -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' });
};

@ -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':

@ -42,7 +42,10 @@ const AppRightsChecker = {
'<br/>' +
Locale.appRightsAlertBody2 +
`: <pre>${command}</pre>`,
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();

@ -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';

@ -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
}
];
},

@ -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();

@ -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);
},

@ -16,7 +16,10 @@ const OtpQrReader = {
read: function() {
let screenshotKey = FeatureDetector.screenshotToClipboardShortcut();
if (screenshotKey) {
screenshotKey = Locale.detSetupOtpAlertBodyWith.replace('{}', '<code>' + screenshotKey + '</code>');
screenshotKey = Locale.detSetupOtpAlertBodyWith.replace(
'{}',
'<code>' + screenshotKey + '</code>'
);
}
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 + '<pre class="modal__pre">' + _.escape(err.toString()) + '</pre>'
Locale.detOtpQrWrongBody +
'<pre class="modal__pre">' +
_.escape(err.toString()) +
'</pre>'
});
}
} catch (e) {

@ -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);
}

@ -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');

@ -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'
});
}
});
},

@ -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"],

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -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);

@ -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);
});
}

@ -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;
}
});

@ -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();
},

@ -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();

@ -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'
}
};
},

@ -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;

@ -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<