2017-01-31 07:50:28 +01:00
|
|
|
const Backbone = require('backbone');
|
|
|
|
const AutoTypeParser = require('./auto-type-parser');
|
|
|
|
const AutoTypeFilter = require('./auto-type-filter');
|
|
|
|
const AutoTypeHelperFactory = require('./auto-type-helper-factory');
|
|
|
|
const Launcher = require('../comp/launcher');
|
|
|
|
const Alerts = require('../comp/alerts');
|
|
|
|
const AutoTypeSelectView = require('../views/auto-type/auto-type-select-view');
|
|
|
|
const Logger = require('../util/logger');
|
|
|
|
const Locale = require('../util/locale');
|
|
|
|
const Timeouts = require('../const/timeouts');
|
|
|
|
|
|
|
|
const logger = new Logger('auto-type');
|
|
|
|
const clearTextAutoTypeLog = localStorage.autoTypeDebug;
|
|
|
|
|
|
|
|
const AutoType = {
|
2016-04-09 20:58:22 +02:00
|
|
|
helper: AutoTypeHelperFactory.create(),
|
2017-01-31 23:09:35 +01:00
|
|
|
enabled: !!(Launcher && Launcher.autoTypeSupported),
|
2016-07-24 22:57:12 +02:00
|
|
|
selectEntryView: false,
|
2016-08-07 19:34:31 +02:00
|
|
|
pendingEvent: null,
|
2016-08-18 22:01:08 +02:00
|
|
|
running: false,
|
2016-04-23 16:50:40 +02:00
|
|
|
|
2016-08-07 19:34:31 +02:00
|
|
|
init(appModel) {
|
2016-07-24 22:57:12 +02:00
|
|
|
if (!this.enabled) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.appModel = appModel;
|
2016-08-07 19:34:31 +02:00
|
|
|
Backbone.on('auto-type', this.handleEvent, this);
|
|
|
|
Backbone.on('main-window-blur main-window-will-close', this.resetPendingEvent, this);
|
2016-07-24 19:11:25 +02:00
|
|
|
},
|
|
|
|
|
2016-08-07 19:34:31 +02:00
|
|
|
handleEvent(e) {
|
2017-01-31 07:50:28 +01:00
|
|
|
const entry = e && e.entry || null;
|
2016-07-24 19:11:25 +02:00
|
|
|
logger.debug('Auto type event', entry);
|
2016-08-18 22:01:08 +02:00
|
|
|
if (this.running) {
|
|
|
|
logger.debug('Already running, skipping event');
|
|
|
|
return;
|
|
|
|
}
|
2016-07-24 19:11:25 +02:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2016-08-07 19:34:31 +02:00
|
|
|
runAndHandleResult(entry) {
|
2016-07-24 19:11:25 +02:00
|
|
|
this.run(entry, err => {
|
|
|
|
if (err) {
|
|
|
|
Alerts.error({
|
|
|
|
header: Locale.autoTypeError,
|
|
|
|
body: Locale.autoTypeErrorGeneric.replace('{}', err.toString())
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2016-08-07 19:34:31 +02:00
|
|
|
run(entry, callback) {
|
2016-08-18 22:01:08 +02:00
|
|
|
this.running = true;
|
2017-01-31 07:50:28 +01:00
|
|
|
const sequence = entry.getEffectiveAutoTypeSeq();
|
2016-04-08 17:40:00 +02:00
|
|
|
logger.debug('Start', sequence);
|
2017-01-31 07:50:28 +01:00
|
|
|
const ts = logger.ts();
|
2016-04-08 17:40:00 +02:00
|
|
|
try {
|
2017-01-31 07:50:28 +01:00
|
|
|
const parser = new AutoTypeParser(sequence);
|
|
|
|
const runner = parser.parse();
|
2016-07-17 13:30:38 +02:00
|
|
|
logger.debug('Parsed', this.printOps(runner.ops));
|
|
|
|
runner.resolve(entry, err => {
|
2016-04-08 17:40:00 +02:00
|
|
|
if (err) {
|
2016-08-18 22:01:08 +02:00
|
|
|
this.running = false;
|
2016-04-08 23:14:09 +02:00
|
|
|
logger.error('Resolve error', err);
|
2016-04-23 17:05:33 +02:00
|
|
|
return callback && callback(err);
|
2016-04-08 17:40:00 +02:00
|
|
|
}
|
2016-07-17 13:30:38 +02:00
|
|
|
logger.debug('Resolved', this.printOps(runner.ops));
|
2016-04-23 21:35:30 +02:00
|
|
|
if (entry.autoTypeObfuscation) {
|
2016-04-09 14:55:27 +02:00
|
|
|
try {
|
|
|
|
runner.obfuscate();
|
|
|
|
} catch (e) {
|
2016-08-18 22:01:08 +02:00
|
|
|
this.running = false;
|
2016-04-09 14:55:27 +02:00
|
|
|
logger.error('Obfuscate error', e);
|
2016-04-23 17:05:33 +02:00
|
|
|
return callback && callback(e);
|
2016-04-09 14:55:27 +02:00
|
|
|
}
|
2016-04-10 09:31:08 +02:00
|
|
|
logger.debug('Obfuscated');
|
2016-04-09 10:41:52 +02:00
|
|
|
}
|
2016-07-17 13:30:38 +02:00
|
|
|
runner.run(err => {
|
2016-08-18 22:01:08 +02:00
|
|
|
this.running = false;
|
2016-04-08 17:40:00 +02:00
|
|
|
if (err) {
|
|
|
|
logger.error('Run error', err);
|
2016-04-23 17:05:33 +02:00
|
|
|
return callback && callback(err);
|
2016-04-08 17:40:00 +02:00
|
|
|
}
|
2016-04-23 17:05:33 +02:00
|
|
|
logger.debug('Complete', logger.ts(ts));
|
|
|
|
return callback && callback();
|
2016-04-08 17:40:00 +02:00
|
|
|
});
|
|
|
|
});
|
|
|
|
} catch (ex) {
|
2016-08-18 22:01:08 +02:00
|
|
|
this.running = false;
|
2016-04-08 17:40:00 +02:00
|
|
|
logger.error('Parse error', ex);
|
2016-04-23 17:05:33 +02:00
|
|
|
return callback && callback(ex);
|
2016-04-08 17:40:00 +02:00
|
|
|
}
|
2016-04-09 11:17:01 +02:00
|
|
|
},
|
|
|
|
|
2016-08-07 19:34:31 +02:00
|
|
|
validate(entry, sequence, callback) {
|
2016-04-23 16:50:40 +02:00
|
|
|
try {
|
2017-01-31 07:50:28 +01:00
|
|
|
const parser = new AutoTypeParser(sequence);
|
|
|
|
const runner = parser.parse();
|
2016-04-23 16:50:40 +02:00
|
|
|
runner.resolve(entry, callback);
|
|
|
|
} catch (ex) {
|
|
|
|
return callback(ex);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2016-08-07 19:34:31 +02:00
|
|
|
printOps(ops) {
|
2016-04-09 11:17:01 +02:00
|
|
|
return '[' + ops.map(this.printOp, this).join(',') + ']';
|
|
|
|
},
|
|
|
|
|
2016-08-07 19:34:31 +02:00
|
|
|
printOp(op) {
|
2017-01-31 07:50:28 +01:00
|
|
|
const mod = op.mod ? Object.keys(op.mod).join('') : '';
|
2016-04-09 11:17:01 +02:00
|
|
|
if (op.type === 'group') {
|
|
|
|
return mod + this.printOps(op.value);
|
|
|
|
}
|
|
|
|
if (op.type === 'text') {
|
2017-01-31 07:50:28 +01:00
|
|
|
let value = op.value;
|
2016-04-09 11:28:14 +02:00
|
|
|
if (!clearTextAutoTypeLog) {
|
|
|
|
value = value.replace(/./g, '*');
|
|
|
|
}
|
|
|
|
return mod + value;
|
2016-04-09 11:17:01 +02:00
|
|
|
}
|
|
|
|
return mod + op.type + ':' + op.value;
|
2016-04-09 20:58:22 +02:00
|
|
|
},
|
|
|
|
|
2016-08-07 19:34:31 +02:00
|
|
|
hideWindow(callback) {
|
2016-04-09 20:58:22 +02:00
|
|
|
logger.debug('Hide window');
|
2016-07-24 19:11:25 +02:00
|
|
|
if (Launcher.isAppFocused()) {
|
|
|
|
Launcher.hideApp();
|
2016-04-09 20:58:22 +02:00
|
|
|
setTimeout(callback, Timeouts.AutoTypeAfterHide);
|
|
|
|
} else {
|
|
|
|
callback();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2016-08-07 19:34:31 +02:00
|
|
|
getActiveWindowTitle(callback) {
|
2016-04-09 20:58:22 +02:00
|
|
|
logger.debug('Get window title');
|
2016-07-17 13:30:38 +02:00
|
|
|
return this.helper.getActiveWindowTitle((err, title, url) => {
|
2016-04-09 20:58:22 +02:00
|
|
|
if (err) {
|
|
|
|
logger.error('Error get window title', err);
|
|
|
|
} else {
|
2016-04-09 22:19:56 +02:00
|
|
|
logger.debug('Window title', title, url);
|
2016-04-09 20:58:22 +02:00
|
|
|
}
|
2016-07-24 19:11:25 +02:00
|
|
|
return callback(err, title, url);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2016-08-07 19:34:31 +02:00
|
|
|
selectEntryAndRun() {
|
2016-07-24 19:11:25 +02:00
|
|
|
this.getActiveWindowTitle((e, title, url) => {
|
2017-01-31 07:50:28 +01:00
|
|
|
const filter = new AutoTypeFilter({title, url}, this.appModel);
|
|
|
|
const evt = { filter };
|
2016-08-07 19:34:31 +02:00
|
|
|
if (!this.appModel.files.hasOpenFiles()) {
|
|
|
|
this.pendingEvent = evt;
|
|
|
|
this.appModel.files.once('update', this.processPendingEvent, this);
|
|
|
|
logger.debug('auto-type event delayed');
|
2016-08-18 23:26:35 +02:00
|
|
|
this.focusMainWindow();
|
2016-08-07 19:34:31 +02:00
|
|
|
} else {
|
|
|
|
this.processEventWithFilter(evt);
|
2016-07-24 19:11:25 +02:00
|
|
|
}
|
2016-08-07 19:34:31 +02:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2016-08-18 23:26:35 +02:00
|
|
|
focusMainWindow() {
|
|
|
|
setTimeout(() => Launcher.showMainWindow(), Timeouts.RedrawInactiveWindow);
|
|
|
|
},
|
|
|
|
|
2016-08-07 19:34:31 +02:00
|
|
|
processEventWithFilter(evt) {
|
2017-01-31 07:50:28 +01:00
|
|
|
const entries = evt.filter.getEntries();
|
2016-08-07 19:34:31 +02:00
|
|
|
if (entries.length === 1) {
|
2016-08-24 18:19:59 +02:00
|
|
|
this.hideWindow(() => {
|
|
|
|
this.runAndHandleResult(entries.at(0));
|
|
|
|
});
|
2016-08-07 19:34:31 +02:00
|
|
|
return;
|
|
|
|
}
|
2016-08-18 23:26:35 +02:00
|
|
|
this.focusMainWindow();
|
2016-08-07 19:34:31 +02:00
|
|
|
this.selectEntryView = new AutoTypeSelectView({
|
|
|
|
model: { filter: evt.filter }
|
|
|
|
}).render();
|
|
|
|
this.selectEntryView.on('result', result => {
|
|
|
|
logger.debug('Entry selected', result);
|
|
|
|
this.selectEntryView.off('result');
|
|
|
|
this.selectEntryView.remove();
|
|
|
|
this.selectEntryView = null;
|
|
|
|
this.hideWindow(() => {
|
|
|
|
if (result) {
|
|
|
|
this.runAndHandleResult(result);
|
2016-07-24 22:57:12 +02:00
|
|
|
}
|
2016-07-24 19:11:25 +02:00
|
|
|
});
|
2016-04-09 20:58:22 +02:00
|
|
|
});
|
2016-08-07 19:34:31 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
resetPendingEvent() {
|
|
|
|
if (this.pendingEvent) {
|
|
|
|
this.pendingEvent = null;
|
|
|
|
this.appModel.files.off('update', this.processPendingEvent, this);
|
|
|
|
logger.debug('auto-type event cancelled');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
processPendingEvent() {
|
|
|
|
if (!this.pendingEvent) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
logger.debug('processing pending auto-type event');
|
2017-01-31 07:50:28 +01:00
|
|
|
const evt = this.pendingEvent;
|
2016-08-07 19:34:31 +02:00
|
|
|
this.appModel.files.off('update', this.processPendingEvent, this);
|
|
|
|
this.pendingEvent = null;
|
|
|
|
this.processEventWithFilter(evt);
|
2016-04-08 17:40:00 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = AutoType;
|