auto-type popup

This commit is contained in:
antelle 2016-07-24 20:11:25 +03:00
parent 8cd5d2b1d7
commit 6a4c6594a7
12 changed files with 221 additions and 29 deletions

View File

@ -1,9 +1,13 @@
'use strict';
var AutoTypeParser = require('./auto-type-parser'),
var Backbone = require('backbone'),
AutoTypeParser = require('./auto-type-parser'),
AutoTypeHelperFactory = require('./auto-type-helper-factory'),
Launcher = require('../comp/launcher'),
Alerts = require('../comp/alerts'),
AutoTypePopupView = require('../views/auto-type/auto-type-popup-view'),
Logger = require('../util/logger'),
Locale = require('../util/locale'),
Timeouts = require('../const/timeouts');
var logger = new Logger('auto-type');
@ -14,6 +18,43 @@ var AutoType = {
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) {
var sequence = entry.getEffectiveAutoTypeSeq();
logger.debug('Start', sequence);
@ -83,7 +124,8 @@ var AutoType = {
hideWindow: function(callback) {
logger.debug('Hide window');
if (Launcher.hideWindowIfActive()) {
if (Launcher.isAppFocused()) {
Launcher.hideApp();
setTimeout(callback, Timeouts.AutoTypeAfterHide);
} else {
callback();
@ -98,9 +140,36 @@ var AutoType = {
} else {
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;

View File

@ -14,6 +14,9 @@ var Alerts = {
},
alert: function(config) {
if (config.skipIfAlertDisplayed && Alerts.alertDisplayed) {
return null;
}
Alerts.alertDisplayed = true;
var view = new ModalView({ model: config });
view.render();

View File

@ -125,19 +125,22 @@ if (window.process && window.process.versions && window.process.versions.electro
openWindow: function(opts) {
return this.remoteApp().openWindow(opts);
},
hideWindowIfActive: function() {
hideApp: function() {
var app = this.remoteApp();
var win = app.getMainWindow();
var visible = win.isVisible(), focused = win.isFocused();
if (!visible || !focused) {
return false;
}
if (process.platform === 'darwin') {
app.hide();
if (this.canMinimize()) {
app.getMainWindow().minimize();
} 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) {
var ts = logger.ts();

View File

@ -253,6 +253,10 @@ var Locale = {
autoTypeModifiers: 'Modifier keys',
autoTypeKeys: 'Keys',
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!',
appSecWarnBody1: 'You have loaded this app with insecure connection. ' +

View File

@ -16,13 +16,17 @@ var ThemeChanger = {
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]');
if (metaThemeColor) {
metaThemeColor.content = window.getComputedStyle(document.body).backgroundColor;
}
},
getThemeClass: function(theme) {
return 'th-' + theme;
},
setFontSize: function(fontSize) {
document.documentElement.style.fontSize = fontSize ? (12 + fontSize * 2) + 'px' : '';
}

View File

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

View File

@ -67,7 +67,6 @@ var DetailsView = Backbone.View.extend({
this.initScroll();
this.listenTo(Backbone, 'select-entry', this.showEntry);
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-url', this.copyUrl);
this.listenTo(Backbone, 'toggle-settings', this.settingsToggled);
@ -794,17 +793,7 @@ var DetailsView = Backbone.View.extend({
},
autoType: function() {
var entry = this.model;
// AutoType.getActiveWindowTitle(function() {
// console.log(arguments);
// });
AutoType.hideWindow(() => {
AutoType.run(entry);
});
},
autoTypeGlobal: function() {
// TODO
Backbone.emit('auto-type', { entry: this.model });
}
});

View File

@ -0,0 +1,3 @@
.at-popup {
padding: $base-padding;
}

View File

@ -22,6 +22,7 @@
@import "common/tip";
@import "areas/app";
@import "areas/auto-type";
@import "areas/details";
@import "areas/footer";
@import "areas/grp";

View File

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

View File

@ -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}}}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}}}T</span> {{res 'setShAutoTypeGlobal'}}</div>--}}
<div><span class="shortcut {{#if globalIsLarge}}shortcut-large{{/if}}">{{{global}}}T</span> {{res 'setShAutoTypeGlobal'}}</div>
{{/if}}
</div>

View File

@ -88,6 +88,7 @@ app.minimizeApp = function () {
app.getMainWindow = function () {
return mainWindow;
};
app.emitBackboneEvent = emitBackboneEvent;
function checkSingleInstance() {
var shouldQuit = app.makeSingleInstance((/* commandLine, workingDirectory */) => {
@ -112,6 +113,7 @@ function createMainWindow() {
});
setMenu();
mainWindow.loadURL('file://' + htmlPath);
mainWindow.setContentProtection(true);
mainWindow.webContents.on('dom-ready', () => {
setTimeout(() => {
mainWindow.show();
@ -215,8 +217,9 @@ function restoreMainWindowPosition() {
});
}
function emitBackboneEvent(e) {
mainWindow.webContents.executeJavaScript('Backbone.trigger("' + e + '");');
function emitBackboneEvent(e, arg) {
arg = JSON.stringify(arg);
mainWindow.webContents.executeJavaScript(`Backbone.trigger('${e}', ${arg});`);
}
function setMenu() {