mirror of https://github.com/keeweb/keeweb.git
auto-type popup
This commit is contained in:
parent
8cd5d2b1d7
commit
6a4c6594a7
|
@ -1,9 +1,13 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var AutoTypeParser = require('./auto-type-parser'),
|
var Backbone = require('backbone'),
|
||||||
|
AutoTypeParser = require('./auto-type-parser'),
|
||||||
AutoTypeHelperFactory = require('./auto-type-helper-factory'),
|
AutoTypeHelperFactory = require('./auto-type-helper-factory'),
|
||||||
Launcher = require('../comp/launcher'),
|
Launcher = require('../comp/launcher'),
|
||||||
|
Alerts = require('../comp/alerts'),
|
||||||
|
AutoTypePopupView = require('../views/auto-type/auto-type-popup-view'),
|
||||||
Logger = require('../util/logger'),
|
Logger = require('../util/logger'),
|
||||||
|
Locale = require('../util/locale'),
|
||||||
Timeouts = require('../const/timeouts');
|
Timeouts = require('../const/timeouts');
|
||||||
|
|
||||||
var logger = new Logger('auto-type');
|
var logger = new Logger('auto-type');
|
||||||
|
@ -14,6 +18,43 @@ var AutoType = {
|
||||||
|
|
||||||
enabled: !!Launcher,
|
enabled: !!Launcher,
|
||||||
|
|
||||||
|
selectEntryView: null,
|
||||||
|
|
||||||
|
init: function() {
|
||||||
|
Backbone.on('auto-type', this.handleEvent.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
handleEvent: function(e) {
|
||||||
|
let entry = e && entry || null;
|
||||||
|
logger.debug('Auto type event', entry);
|
||||||
|
if (entry) {
|
||||||
|
this.hideWindow(() => { this.runAndHandleResult(entry); });
|
||||||
|
} else {
|
||||||
|
if (this.selectEntryView) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Launcher.isAppFocused()) {
|
||||||
|
return Alerts.error({
|
||||||
|
header: Locale.autoTypeError,
|
||||||
|
body: Locale.autoTypeErrorGlobal,
|
||||||
|
skipIfAlertDisplayed: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.selectEntryAndRun();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
runAndHandleResult: function(entry) {
|
||||||
|
this.run(entry, err => {
|
||||||
|
if (err) {
|
||||||
|
Alerts.error({
|
||||||
|
header: Locale.autoTypeError,
|
||||||
|
body: Locale.autoTypeErrorGeneric.replace('{}', err.toString())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
run: function(entry, callback) {
|
run: function(entry, callback) {
|
||||||
var sequence = entry.getEffectiveAutoTypeSeq();
|
var sequence = entry.getEffectiveAutoTypeSeq();
|
||||||
logger.debug('Start', sequence);
|
logger.debug('Start', sequence);
|
||||||
|
@ -83,7 +124,8 @@ var AutoType = {
|
||||||
|
|
||||||
hideWindow: function(callback) {
|
hideWindow: function(callback) {
|
||||||
logger.debug('Hide window');
|
logger.debug('Hide window');
|
||||||
if (Launcher.hideWindowIfActive()) {
|
if (Launcher.isAppFocused()) {
|
||||||
|
Launcher.hideApp();
|
||||||
setTimeout(callback, Timeouts.AutoTypeAfterHide);
|
setTimeout(callback, Timeouts.AutoTypeAfterHide);
|
||||||
} else {
|
} else {
|
||||||
callback();
|
callback();
|
||||||
|
@ -98,9 +140,36 @@ var AutoType = {
|
||||||
} else {
|
} else {
|
||||||
logger.debug('Window title', title, url);
|
logger.debug('Window title', title, url);
|
||||||
}
|
}
|
||||||
return callback(err, title);
|
return callback(err, title, url);
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
selectEntryAndRun: function() {
|
||||||
|
this.getActiveWindowTitle((e, title, url) => {
|
||||||
|
let entries = this.getMatchingEntries(title, url);
|
||||||
|
if (entries.length === 1) {
|
||||||
|
this.runAndHandleResult(entries[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Launcher.hideMainWindow();
|
||||||
|
this.selectEntryView = new AutoTypePopupView().render();
|
||||||
|
this.selectEntryView.on('closed', e => {
|
||||||
|
Launcher.unhideMainWindow();
|
||||||
|
Launcher.hideApp();
|
||||||
|
logger.debug('Popup closed', e.result);
|
||||||
|
this.selectEntryView = null;
|
||||||
|
// this.hideWindow(() => { /* this.runAndHandleResult(e.result); */ });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getMatchingEntries: function() {
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (AutoType.enabled) {
|
||||||
|
AutoType.init();
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = AutoType;
|
module.exports = AutoType;
|
||||||
|
|
|
@ -14,6 +14,9 @@ var Alerts = {
|
||||||
},
|
},
|
||||||
|
|
||||||
alert: function(config) {
|
alert: function(config) {
|
||||||
|
if (config.skipIfAlertDisplayed && Alerts.alertDisplayed) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
Alerts.alertDisplayed = true;
|
Alerts.alertDisplayed = true;
|
||||||
var view = new ModalView({ model: config });
|
var view = new ModalView({ model: config });
|
||||||
view.render();
|
view.render();
|
||||||
|
|
|
@ -125,19 +125,22 @@ if (window.process && window.process.versions && window.process.versions.electro
|
||||||
openWindow: function(opts) {
|
openWindow: function(opts) {
|
||||||
return this.remoteApp().openWindow(opts);
|
return this.remoteApp().openWindow(opts);
|
||||||
},
|
},
|
||||||
hideWindowIfActive: function() {
|
hideApp: function() {
|
||||||
var app = this.remoteApp();
|
var app = this.remoteApp();
|
||||||
var win = app.getMainWindow();
|
if (this.canMinimize()) {
|
||||||
var visible = win.isVisible(), focused = win.isFocused();
|
app.getMainWindow().minimize();
|
||||||
if (!visible || !focused) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (process.platform === 'darwin') {
|
|
||||||
app.hide();
|
|
||||||
} else {
|
} else {
|
||||||
win.minimize();
|
app.hide();
|
||||||
}
|
}
|
||||||
return true;
|
},
|
||||||
|
hideMainWindow: function() {
|
||||||
|
this.remoteApp().getMainWindow().hide();
|
||||||
|
},
|
||||||
|
unhideMainWindow: function() {
|
||||||
|
this.remoteApp().getMainWindow().showInactive();
|
||||||
|
},
|
||||||
|
isAppFocused: function() {
|
||||||
|
return !!this.electron().remote.BrowserWindow.getFocusedWindow();
|
||||||
},
|
},
|
||||||
spawn: function(config) {
|
spawn: function(config) {
|
||||||
var ts = logger.ts();
|
var ts = logger.ts();
|
||||||
|
|
|
@ -253,6 +253,10 @@ var Locale = {
|
||||||
autoTypeModifiers: 'Modifier keys',
|
autoTypeModifiers: 'Modifier keys',
|
||||||
autoTypeKeys: 'Keys',
|
autoTypeKeys: 'Keys',
|
||||||
autoTypeLink: 'more...',
|
autoTypeLink: 'more...',
|
||||||
|
autoTypeError: 'Auto-type error',
|
||||||
|
autoTypeErrorGeneric: 'There was an error performing auto-type: {}',
|
||||||
|
autoTypeErrorGlobal: 'To use system-wide shortcut, please focus the app where you want to type your password',
|
||||||
|
autoTypePopup: 'KeeWeb: Auto Type',
|
||||||
|
|
||||||
appSecWarn: 'Not Secure!',
|
appSecWarn: 'Not Secure!',
|
||||||
appSecWarnBody1: 'You have loaded this app with insecure connection. ' +
|
appSecWarnBody1: 'You have loaded this app with insecure connection. ' +
|
||||||
|
|
|
@ -16,13 +16,17 @@ var ThemeChanger = {
|
||||||
document.body.classList.remove(cls);
|
document.body.classList.remove(cls);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
document.body.classList.add('th-' + theme);
|
document.body.classList.add(this.getThemeClass(theme));
|
||||||
var metaThemeColor = document.head.querySelector('meta[name=theme-color]');
|
var metaThemeColor = document.head.querySelector('meta[name=theme-color]');
|
||||||
if (metaThemeColor) {
|
if (metaThemeColor) {
|
||||||
metaThemeColor.content = window.getComputedStyle(document.body).backgroundColor;
|
metaThemeColor.content = window.getComputedStyle(document.body).backgroundColor;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getThemeClass: function(theme) {
|
||||||
|
return 'th-' + theme;
|
||||||
|
},
|
||||||
|
|
||||||
setFontSize: function(fontSize) {
|
setFontSize: function(fontSize) {
|
||||||
document.documentElement.style.fontSize = fontSize ? (12 + fontSize * 2) + 'px' : '';
|
document.documentElement.style.fontSize = fontSize ? (12 + fontSize * 2) + 'px' : '';
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Backbone = require('backbone');
|
||||||
|
const Launcher = require('../../comp/launcher');
|
||||||
|
const Locale = require('../../util/locale');
|
||||||
|
const ThemeChanger = require('../../util/theme-changer');
|
||||||
|
const Keys = require('../../const/keys');
|
||||||
|
const AppSettingsModel = require('../../models/app-settings-model');
|
||||||
|
|
||||||
|
class AutoTypePopupView {
|
||||||
|
constructor() {
|
||||||
|
this.template = require('templates/auto-type/popup.hbs');
|
||||||
|
this.popupWindow = null;
|
||||||
|
this.result = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let themeClass = ThemeChanger.getThemeClass(AppSettingsModel.instance.get('theme'));
|
||||||
|
let styleSheet = document.styleSheets[0];
|
||||||
|
let css = styleSheet.ownerNode.textContent;
|
||||||
|
if (!css) {
|
||||||
|
// dev mode, external stylesheet
|
||||||
|
css = _.map(styleSheet.rules, rule => rule.cssText).join('\n');
|
||||||
|
}
|
||||||
|
let html = this.template({
|
||||||
|
themeClass: themeClass,
|
||||||
|
css: css
|
||||||
|
});
|
||||||
|
|
||||||
|
this.popupWindow = Launcher.openWindow({
|
||||||
|
show: false,
|
||||||
|
width: 600,
|
||||||
|
minWidth: 600,
|
||||||
|
height: 300,
|
||||||
|
minHeight: 300,
|
||||||
|
minimizable: false,
|
||||||
|
maximizable: false,
|
||||||
|
alwaysOnTop: true,
|
||||||
|
fullscreenable: false,
|
||||||
|
title: Locale.autoTypePopup,
|
||||||
|
modal: true
|
||||||
|
// icon: TODO
|
||||||
|
});
|
||||||
|
this.popupWindow.on('closed', () => this.remove());
|
||||||
|
this.popupWindow.on('blur', () => this.popupWindow.close());
|
||||||
|
this.popupWindow.on('ready-to-show', () => this.popupWindow.show());
|
||||||
|
this.popupWindow.loadURL('data:text/html;charset=utf-8,' + encodeURI(html));
|
||||||
|
this.popupWindow.webContents.executeJavaScript('(' + this.init.toString() + ')()');
|
||||||
|
|
||||||
|
Backbone.on('auto-type-popup-keydown', e => this.keydown(e));
|
||||||
|
Backbone.on('auto-type-popup-keypress', e => this.keypress(e));
|
||||||
|
Backbone.on('auto-type-popup-select', e => this.select(e));
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove() {
|
||||||
|
if (this.popupWindow) {
|
||||||
|
this.popupWindow = null;
|
||||||
|
Backbone.off('auto-type-popup-keydown');
|
||||||
|
Backbone.off('auto-type-popup-keypress');
|
||||||
|
Backbone.off('auto-type-popup-select');
|
||||||
|
this.trigger('closed', { result: this.result });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
// note: this function will be executed in popup
|
||||||
|
function emitBackboneEvent(name, arg) {
|
||||||
|
window.require('electron').remote.app.emitBackboneEvent('auto-type-popup-' + name, arg);
|
||||||
|
}
|
||||||
|
document.body.addEventListener('keydown', e => {
|
||||||
|
emitBackboneEvent('keydown', {keyCode: e.keyCode});
|
||||||
|
});
|
||||||
|
document.body.addEventListener('keypress', e => {
|
||||||
|
emitBackboneEvent('keypress', {keyCode: e.keyCode, text: e.key});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
keydown(e) {
|
||||||
|
if (e.keyCode === Keys.DOM_VK_ESCAPE) {
|
||||||
|
return this.popupWindow.close();
|
||||||
|
} else if (e.keyCode === Keys.DOM_VK_ENTER || e.keyCode === Keys.DOM_VK_RETURN) {
|
||||||
|
this.result = 'entry';
|
||||||
|
return this.popupWindow.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keypress(e) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
select(e) {
|
||||||
|
this.result = e.result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_.extend(AutoTypePopupView.prototype, Backbone.Events);
|
||||||
|
|
||||||
|
module.exports = AutoTypePopupView;
|
|
@ -67,7 +67,6 @@ var DetailsView = Backbone.View.extend({
|
||||||
this.initScroll();
|
this.initScroll();
|
||||||
this.listenTo(Backbone, 'select-entry', this.showEntry);
|
this.listenTo(Backbone, 'select-entry', this.showEntry);
|
||||||
this.listenTo(Backbone, 'copy-password', this.copyPassword);
|
this.listenTo(Backbone, 'copy-password', this.copyPassword);
|
||||||
this.listenTo(Backbone, 'auto-type', this.autoTypeGlobal);
|
|
||||||
this.listenTo(Backbone, 'copy-user', this.copyUserName);
|
this.listenTo(Backbone, 'copy-user', this.copyUserName);
|
||||||
this.listenTo(Backbone, 'copy-url', this.copyUrl);
|
this.listenTo(Backbone, 'copy-url', this.copyUrl);
|
||||||
this.listenTo(Backbone, 'toggle-settings', this.settingsToggled);
|
this.listenTo(Backbone, 'toggle-settings', this.settingsToggled);
|
||||||
|
@ -794,17 +793,7 @@ var DetailsView = Backbone.View.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
autoType: function() {
|
autoType: function() {
|
||||||
var entry = this.model;
|
Backbone.emit('auto-type', { entry: this.model });
|
||||||
// AutoType.getActiveWindowTitle(function() {
|
|
||||||
// console.log(arguments);
|
|
||||||
// });
|
|
||||||
AutoType.hideWindow(() => {
|
|
||||||
AutoType.run(entry);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
autoTypeGlobal: function() {
|
|
||||||
// TODO
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
.at-popup {
|
||||||
|
padding: $base-padding;
|
||||||
|
}
|
|
@ -22,6 +22,7 @@
|
||||||
@import "common/tip";
|
@import "common/tip";
|
||||||
|
|
||||||
@import "areas/app";
|
@import "areas/app";
|
||||||
|
@import "areas/auto-type";
|
||||||
@import "areas/details";
|
@import "areas/details";
|
||||||
@import "areas/footer";
|
@import "areas/footer";
|
||||||
@import "areas/grp";
|
@import "areas/grp";
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>{{res 'autoTypePopup'}}</title>
|
||||||
|
<style>{{{css}}}</style>
|
||||||
|
</head>
|
||||||
|
<body class="{{themeClass}} at-popup">
|
||||||
|
Hello!
|
||||||
|
{{res 'autoTypePopup'}}
|
||||||
|
<button class="btn-silent">Select</button>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -24,6 +24,6 @@
|
||||||
<div><span class="shortcut {{#if globalIsLarge}}shortcut-large{{/if}}">{{{global}}}C</span> {{res 'setShCopyPassGlobal'}}</div>
|
<div><span class="shortcut {{#if globalIsLarge}}shortcut-large{{/if}}">{{{global}}}C</span> {{res 'setShCopyPassGlobal'}}</div>
|
||||||
<div><span class="shortcut {{#if globalIsLarge}}shortcut-large{{/if}}">{{{global}}}B</span> {{res 'setShCopyUserGlobal'}}</div>
|
<div><span class="shortcut {{#if globalIsLarge}}shortcut-large{{/if}}">{{{global}}}B</span> {{res 'setShCopyUserGlobal'}}</div>
|
||||||
<div><span class="shortcut {{#if globalIsLarge}}shortcut-large{{/if}}">{{{global}}}U</span> {{res 'setShCopyUrlGlobal'}}</div>
|
<div><span class="shortcut {{#if globalIsLarge}}shortcut-large{{/if}}">{{{global}}}U</span> {{res 'setShCopyUrlGlobal'}}</div>
|
||||||
{{!--<div><span class="shortcut {{#if globalIsLarge}}shortcut-large{{/if}}">{{{global}}}T</span> {{res 'setShAutoTypeGlobal'}}</div>--}}
|
<div><span class="shortcut {{#if globalIsLarge}}shortcut-large{{/if}}">{{{global}}}T</span> {{res 'setShAutoTypeGlobal'}}</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -88,6 +88,7 @@ app.minimizeApp = function () {
|
||||||
app.getMainWindow = function () {
|
app.getMainWindow = function () {
|
||||||
return mainWindow;
|
return mainWindow;
|
||||||
};
|
};
|
||||||
|
app.emitBackboneEvent = emitBackboneEvent;
|
||||||
|
|
||||||
function checkSingleInstance() {
|
function checkSingleInstance() {
|
||||||
var shouldQuit = app.makeSingleInstance((/* commandLine, workingDirectory */) => {
|
var shouldQuit = app.makeSingleInstance((/* commandLine, workingDirectory */) => {
|
||||||
|
@ -112,6 +113,7 @@ function createMainWindow() {
|
||||||
});
|
});
|
||||||
setMenu();
|
setMenu();
|
||||||
mainWindow.loadURL('file://' + htmlPath);
|
mainWindow.loadURL('file://' + htmlPath);
|
||||||
|
mainWindow.setContentProtection(true);
|
||||||
mainWindow.webContents.on('dom-ready', () => {
|
mainWindow.webContents.on('dom-ready', () => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
mainWindow.show();
|
mainWindow.show();
|
||||||
|
@ -215,8 +217,9 @@ function restoreMainWindowPosition() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function emitBackboneEvent(e) {
|
function emitBackboneEvent(e, arg) {
|
||||||
mainWindow.webContents.executeJavaScript('Backbone.trigger("' + e + '");');
|
arg = JSON.stringify(arg);
|
||||||
|
mainWindow.webContents.executeJavaScript(`Backbone.trigger('${e}', ${arg});`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setMenu() {
|
function setMenu() {
|
||||||
|
|
Loading…
Reference in New Issue