mirror of https://github.com/keeweb/keeweb.git
Merge branch 'upstream/develop' into develop
# Conflicts: # app/scripts/app.js
This commit is contained in:
commit
bda4cbf2ad
|
@ -39,7 +39,6 @@ module.exports = function(grunt) {
|
|||
const data = fs.readFileSync('tmp/fonts/' + fontFile, 'base64');
|
||||
const src = 'url(data:application/font-woff;charset=utf-8;base64,{data}) format(\'woff\')'
|
||||
.replace('{data}', data);
|
||||
// var src = 'url(\'../fonts/fontawesome-webfont.woff\') format(\'woff\')';
|
||||
rule.nodes = rule.nodes.filter(n => n.prop !== 'src');
|
||||
rule.append({ prop: 'src', value: src });
|
||||
});
|
||||
|
@ -102,7 +101,7 @@ module.exports = function(grunt) {
|
|||
{ test: /\.json$/, loader: 'json-loader' },
|
||||
{ test: /argon2-asm\.min\.js$/, loader: 'raw-loader' },
|
||||
{ test: /argon2\.wasm$/, loader: 'base64-loader' },
|
||||
{ test: /argon2\.min\.js/, loader: 'raw-loader' } // exports-loader?Module
|
||||
{ test: /argon2\.min\.js/, loader: 'raw-loader' }
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
|
|
|
@ -72,7 +72,7 @@ ready(() => {
|
|||
if (configParam) {
|
||||
appModel.loadConfig(configParam, err => {
|
||||
SettingsManager.setBySettings(appModel.settings);
|
||||
if (err) {
|
||||
if (err && !appModel.settings.get('cacheConfigSettings')) {
|
||||
showSettingsLoadError();
|
||||
reject(err);
|
||||
} else {
|
||||
|
|
|
@ -2,10 +2,26 @@
|
|||
|
||||
const AppSettingsModel = require('../models/app-settings-model');
|
||||
|
||||
const Libs = {
|
||||
backbone: require('backbone'),
|
||||
_: require('underscore'),
|
||||
underscore: require('underscore'),
|
||||
$: require('jquery'),
|
||||
jquery: require('jquery'),
|
||||
kdbxweb: require('kdbxweb'),
|
||||
hbs: require('hbs'),
|
||||
pikaday: require('pikaday'),
|
||||
filesaver: require('filesaver'),
|
||||
qrcode: require('qrcode')
|
||||
};
|
||||
|
||||
const ExportApi = {
|
||||
settings: {
|
||||
get: function(key) { return key ? AppSettingsModel.instance.get(key) : AppSettingsModel.instance.toJSON(); },
|
||||
set: function(key, value) { AppSettingsModel.instance.set(key, value); }
|
||||
},
|
||||
require: function(module) {
|
||||
return Libs[module] || require('../' + module);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -231,6 +231,7 @@
|
|||
"detClone": "Make a copy",
|
||||
"detClonedName": "Copy",
|
||||
"detAutoType": "Auto-type",
|
||||
"detAutoTypeSettings": "Auto-type settings",
|
||||
"detAutoTypeEnabled": "Enable auto-type for this entry",
|
||||
"detAutoTypeSequence": "Keystrokes",
|
||||
"detAutoTypeInput": "Input",
|
||||
|
|
|
@ -68,6 +68,7 @@ const AppModel = Backbone.Model.extend({
|
|||
response = JSON.parse(response);
|
||||
} catch (e) {
|
||||
this.appLogger.error('Error parsing response', e, response);
|
||||
return callback(true);
|
||||
}
|
||||
}
|
||||
if (!response.settings) {
|
||||
|
@ -87,6 +88,9 @@ const AppModel = Backbone.Model.extend({
|
|||
applyUserConfig(config) {
|
||||
this.settings.set(config.settings);
|
||||
if (config.files) {
|
||||
if (config.showOnlyFilesFromConfig) {
|
||||
this.fileInfos.reset();
|
||||
}
|
||||
config.files
|
||||
.filter(file => file && file.storage && file.name && file.path &&
|
||||
!this.fileInfos.getMatch(file.storage, file.name, file.path))
|
||||
|
|
|
@ -29,12 +29,15 @@ const AppSettingsModel = Backbone.Model.extend({
|
|||
fontSize: 0,
|
||||
tableViewColumns: null,
|
||||
generatorPresets: null,
|
||||
cacheConfigSettings: false,
|
||||
|
||||
canOpen: true,
|
||||
canOpenDemo: true,
|
||||
canOpenSettings: true,
|
||||
canCreate: true,
|
||||
canImportXml: true,
|
||||
canRemoveLatest: true,
|
||||
|
||||
dropbox: true,
|
||||
webdav: true,
|
||||
gdrive: true,
|
||||
|
@ -69,6 +72,9 @@ const AppSettingsModel = Backbone.Model.extend({
|
|||
if (data.rememberKeyFiles === true) {
|
||||
data.rememberKeyFiles = 'data';
|
||||
}
|
||||
if (data.versionWarningShown) {
|
||||
delete data.versionWarningShown;
|
||||
}
|
||||
},
|
||||
|
||||
save: function() {
|
||||
|
|
|
@ -90,6 +90,14 @@ const AppView = Backbone.View.extend({
|
|||
KeyHandler.onKey(Keys.DOM_VK_F12, this.openDevTools, this, KeyHandler.SHORTCUT_ACTION);
|
||||
|
||||
setInterval(this.syncAllByTimer.bind(this), Timeouts.AutoSync);
|
||||
|
||||
this.setWindowClass();
|
||||
},
|
||||
|
||||
setWindowClass: function() {
|
||||
if (window.chrome && window.chrome.webstore) {
|
||||
this.$el.addClass('chrome');
|
||||
}
|
||||
},
|
||||
|
||||
render: function () {
|
||||
|
|
|
@ -251,7 +251,7 @@ const DetailsView = Backbone.View.extend({
|
|||
}
|
||||
moreOptions.push({value: 'otp', icon: 'clock-o', text: Locale.detSetupOtp});
|
||||
if (AutoType.enabled) {
|
||||
moreOptions.push({value: 'auto-type', icon: 'keyboard-o', text: Locale.detAutoType});
|
||||
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();
|
||||
|
@ -774,6 +774,9 @@ const DetailsView = Backbone.View.extend({
|
|||
}
|
||||
options.push({ value: 'det-add-new', icon: 'plus', text: Locale.detMenuAddNewField });
|
||||
options.push({ value: 'det-clone', icon: 'clone', text: Locale.detClone });
|
||||
if (AutoType.enabled) {
|
||||
options.push({ value: 'det-auto-type', icon: 'keyboard-o', text: Locale.detAutoType });
|
||||
}
|
||||
Backbone.trigger('show-context-menu', _.extend(e, { options }));
|
||||
},
|
||||
|
||||
|
@ -791,6 +794,9 @@ const DetailsView = Backbone.View.extend({
|
|||
case 'det-clone':
|
||||
this.clone();
|
||||
break;
|
||||
case 'det-auto-type':
|
||||
this.autoType();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const FieldViewText = require('./field-view-text');
|
||||
const Keys = require('../../const/keys');
|
||||
|
||||
const FieldViewAutocomplete = FieldViewText.extend({
|
||||
endEdit: function(newVal, extra) {
|
||||
|
@ -8,6 +9,7 @@ const FieldViewAutocomplete = FieldViewText.extend({
|
|||
this.autocomplete.remove();
|
||||
this.autocomplete = null;
|
||||
}
|
||||
delete this.selectedCopmletionIx;
|
||||
FieldViewText.prototype.endEdit.call(this, newVal, extra);
|
||||
},
|
||||
|
||||
|
@ -20,6 +22,7 @@ const FieldViewAutocomplete = FieldViewText.extend({
|
|||
left: fieldRect.left,
|
||||
width: fieldRect.width - 2
|
||||
});
|
||||
delete this.selectedCopmletionIx;
|
||||
this.autocomplete.mousedown(this.autocompleteClick.bind(this));
|
||||
if (this.input.val()) {
|
||||
this.autocomplete.hide();
|
||||
|
@ -34,10 +37,44 @@ const FieldViewAutocomplete = FieldViewText.extend({
|
|||
FieldViewText.prototype.fieldValueInput.call(this, e);
|
||||
},
|
||||
|
||||
fieldValueKeydown: function(e) {
|
||||
switch (e.which) {
|
||||
case Keys.DOM_VK_UP:
|
||||
this.moveAutocomplete(false);
|
||||
e.preventDefault();
|
||||
break;
|
||||
case Keys.DOM_VK_DOWN:
|
||||
this.moveAutocomplete(true);
|
||||
e.preventDefault();
|
||||
break;
|
||||
case Keys.DOM_VK_RETURN:
|
||||
const selectedItem = this.autocomplete.find('.details__field-autocomplete-item--selected').text();
|
||||
if (selectedItem) {
|
||||
this.input.val(selectedItem);
|
||||
this.endEdit(selectedItem);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
delete this.selectedCopmletionIx;
|
||||
}
|
||||
FieldViewText.prototype.fieldValueKeydown.call(this, e);
|
||||
},
|
||||
|
||||
moveAutocomplete: function(next) {
|
||||
const completions = this.model.getCompletions(this.input.val());
|
||||
if (typeof this.selectedCopmletionIx === 'number') {
|
||||
this.selectedCopmletionIx = (completions.length + this.selectedCopmletionIx + (next ? 1 : -1)) % completions.length;
|
||||
} else {
|
||||
this.selectedCopmletionIx = next ? 0 : completions.length - 1;
|
||||
}
|
||||
this.updateAutocomplete();
|
||||
},
|
||||
|
||||
updateAutocomplete: function() {
|
||||
const completions = this.model.getCompletions(this.input.val());
|
||||
const completionsHtml = completions.map(item => {
|
||||
return '<div class="details__field-autocomplete-item">' + _.escape(item) + '</div>';
|
||||
const completionsHtml = completions.map((item, ix) => {
|
||||
const sel = ix === this.selectedCopmletionIx ? 'details__field-autocomplete-item--selected' : '';
|
||||
return '<div class="details__field-autocomplete-item ' + sel + '">' + _.escape(item) + '</div>';
|
||||
}).join('');
|
||||
this.autocomplete.html(completionsHtml);
|
||||
this.autocomplete.toggle(!!completionsHtml);
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
const Backbone = require('backbone');
|
||||
const kdbxweb = require('kdbxweb');
|
||||
const OpenConfigView = require('./open-config-view');
|
||||
const AppSettingsModel = require('../models/app-settings-model');
|
||||
const Keys = require('../const/keys');
|
||||
const Alerts = require('../comp/alerts');
|
||||
const SecureInput = require('../comp/secure-input');
|
||||
|
@ -94,21 +93,6 @@ const OpenView = Backbone.View.extend({
|
|||
});
|
||||
this.inputEl = this.$el.find('.open__pass-input');
|
||||
this.passwordInput.setElement(this.inputEl);
|
||||
|
||||
if (!this.versionWarningShown) {
|
||||
// TODO: remove in v1.5
|
||||
this.versionWarningShown = AppSettingsModel.instance.get('versionWarningShown');
|
||||
if (!this.versionWarningShown) {
|
||||
AppSettingsModel.instance.set('versionWarningShown', true);
|
||||
this.versionWarningShown = true;
|
||||
if (this.model.fileInfos.length) {
|
||||
Alerts.info({
|
||||
header: 'KeeWeb updated',
|
||||
body: 'There was a major update of KeeWeb. Please make sure you have a backup of your password files!'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
|
|
|
@ -185,6 +185,7 @@
|
|||
height: $details-field-line-height;
|
||||
width: 100%;
|
||||
@include th { color: text-color(); }
|
||||
.chrome & { padding-bottom: 1px; } // TODO: find a better cross-browser way to do it
|
||||
}
|
||||
}
|
||||
&-value {
|
||||
|
@ -240,6 +241,9 @@
|
|||
height: 20px;
|
||||
.details__field--protected & { font-family: $monospace-font-family; }
|
||||
}
|
||||
>input {
|
||||
.chrome & { padding-bottom: 1px; } // TODO: find a better cross-browser way to do it
|
||||
}
|
||||
>textarea {
|
||||
display: block;
|
||||
resize: none;
|
||||
|
@ -527,6 +531,12 @@
|
|||
display: inline-block;
|
||||
word-break: break-all;
|
||||
@include area-selectable(bottom);
|
||||
&--selected {
|
||||
@include th {
|
||||
background-color: secondary-background-color();
|
||||
border-bottom: selected-hover-border();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
10
bower.json
10
bower.json
|
@ -26,12 +26,12 @@
|
|||
"baron": "2.2.2",
|
||||
"bourbon": "4.2.7",
|
||||
"dropbox": "keeweb/dropbox-js#0ac0efdc2711eece73f6ac044459e1fd0d5e9390",
|
||||
"font-awesome": "4.6.3",
|
||||
"kdbxweb": "a4d58631f2c98667e0de2f46915db2dd79bddd07",
|
||||
"normalize.css": "4.2.0",
|
||||
"pikaday": "1.4.0",
|
||||
"font-awesome": "4.7.0",
|
||||
"kdbxweb": "1.0.0",
|
||||
"normalize.css": "5.0.0",
|
||||
"pikaday": "1.5.1",
|
||||
"FileSaver.js": "eligrey/FileSaver.js",
|
||||
"jquery": "3.1.0",
|
||||
"jquery": "3.1.1",
|
||||
"jsqrcode": "antelle/jsqrcode#0.1.3",
|
||||
"argon2-browser": "0.0.5"
|
||||
}
|
||||
|
|
|
@ -2,6 +2,11 @@ Release notes
|
|||
-------------
|
||||
##### v1.5.0 (TBD)
|
||||
`+` file path hint in recent files list
|
||||
`+` cacheConfigSettings config option
|
||||
`+` keyboard-accessible autocomplete
|
||||
`+` entry auto-type context menu
|
||||
`+` `kw.require` now provides access to modules
|
||||
`+` showOnlyFilesFromConfig config option
|
||||
|
||||
##### v1.4.0 (2017-02-04)
|
||||
KDBX4 format support and minor improvements
|
||||
|
|
|
@ -38,12 +38,15 @@
|
|||
"gdriveClientId": null,
|
||||
|
||||
"onedrive": true,
|
||||
"onedriveClientId": null
|
||||
"onedriveClientId": null,
|
||||
|
||||
"cacheConfigSettings": false
|
||||
},
|
||||
"files": [{
|
||||
"storage": "webdav",
|
||||
"name": "My file",
|
||||
"path": "webdav-url",
|
||||
"options": { "user": "", "password": "" }
|
||||
}]
|
||||
}],
|
||||
"showOnlyFilesFromConfig": false
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue