mirror of https://github.com/keeweb/keeweb.git
auto-type parser
This commit is contained in:
parent
735ba1fbcf
commit
ee3e9dcb95
|
@ -0,0 +1,131 @@
|
|||
'use strict';
|
||||
|
||||
var AutoTypeRunner = require('./auto-type-runner');
|
||||
|
||||
var AutoTypeParser = function(sequence) {
|
||||
this.sequence = sequence;
|
||||
this.ix = 0;
|
||||
this.states = [];
|
||||
};
|
||||
|
||||
AutoTypeParser.opSepRegex = /[\s:=]+/;
|
||||
|
||||
AutoTypeParser.prototype.parse = function() {
|
||||
var len = this.sequence.length;
|
||||
this.pushState();
|
||||
while (this.ix < len) {
|
||||
var ch = this.sequence[this.ix];
|
||||
switch (ch) {
|
||||
case '{':
|
||||
this.readOp();
|
||||
break;
|
||||
case '+':
|
||||
case '%':
|
||||
case '^':
|
||||
this.readModifier(ch);
|
||||
break;
|
||||
case '(':
|
||||
this.pushState();
|
||||
break;
|
||||
case ')':
|
||||
this.popState();
|
||||
break;
|
||||
case '~':
|
||||
this.addOp('ENTER');
|
||||
break;
|
||||
default:
|
||||
this.addChar(ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (this.states.length !== 0) {
|
||||
throw 'Groups count mismatch';
|
||||
}
|
||||
return new AutoTypeRunner(this.state().ops);
|
||||
};
|
||||
|
||||
AutoTypeParser.prototype.pushState = function() {
|
||||
this.states.unshift({
|
||||
modifiers: null,
|
||||
ops: []
|
||||
});
|
||||
};
|
||||
|
||||
AutoTypeParser.prototype.popState = function() {
|
||||
if (this.states.length <= 1) {
|
||||
throw 'Unexpected ")" at index ' + this.ix;
|
||||
}
|
||||
var state = this.states.shift();
|
||||
this.addState(state);
|
||||
};
|
||||
|
||||
AutoTypeParser.prototype.state = function() {
|
||||
return this.states[0];
|
||||
};
|
||||
|
||||
AutoTypeParser.prototype.readOp = function() {
|
||||
var toIx = this.sequence.indexOf('}', this.ix + 2);
|
||||
if (toIx < 0) {
|
||||
throw 'Mismatched "{" at index ' + this.ix;
|
||||
}
|
||||
var contents = this.sequence.substring(this.ix + 1, toIx);
|
||||
this.ix = toIx + 1;
|
||||
if (contents.length === 1) {
|
||||
this.addChar(contents);
|
||||
return;
|
||||
}
|
||||
var parts = contents.split(AutoTypeParser.opSepRegex, 2);
|
||||
if (parts.length > 1 && parts[0].length && parts[1].length) {
|
||||
var op = parts[1];
|
||||
var sep = contents.substr(op.length, 1);
|
||||
var arg = parts[2];
|
||||
this.addOp(op, sep, arg);
|
||||
} else {
|
||||
this.addOp(contents);
|
||||
}
|
||||
};
|
||||
|
||||
AutoTypeParser.prototype.readModifier = function(modifier) {
|
||||
var state = this.state();
|
||||
if (!state.modifiers) {
|
||||
state.modifiers = {};
|
||||
}
|
||||
if (modifier === '^' && state.modifiers['^']) {
|
||||
state.modifiers['^'] = false;
|
||||
modifier = '^^';
|
||||
}
|
||||
state.modifiers[modifier] = true;
|
||||
};
|
||||
|
||||
AutoTypeParser.prototype.resetModifiers = function() {
|
||||
this.state.modifiers = null;
|
||||
return this.state.modifiers;
|
||||
};
|
||||
|
||||
AutoTypeParser.prototype.addState = function(state) {
|
||||
this.state().ops.push({
|
||||
type: 'group',
|
||||
value: state.ops,
|
||||
mod: this.resetModifiers()
|
||||
});
|
||||
};
|
||||
|
||||
AutoTypeParser.prototype.addChar = function(ch) {
|
||||
this.state().ops.push({
|
||||
type: 'text',
|
||||
value: ch,
|
||||
mod: this.resetModifiers()
|
||||
});
|
||||
};
|
||||
|
||||
AutoTypeParser.prototype.addOp = function(op, sep, arg) {
|
||||
this.state().ops.push({
|
||||
type: 'op',
|
||||
value: op,
|
||||
mod: this.resetModifiers(),
|
||||
sep: sep,
|
||||
arg: arg
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = AutoTypeParser;
|
|
@ -0,0 +1,143 @@
|
|||
'use strict';
|
||||
|
||||
var AutoTypeRunner = function(ops) {
|
||||
this.ops = ops;
|
||||
this.pendingResolves = 0;
|
||||
};
|
||||
|
||||
AutoTypeRunner.PendingResolve = { pending: true };
|
||||
|
||||
AutoTypeRunner.Keys = {
|
||||
tab: 'tab', enter: 'enter', space: 'space',
|
||||
up: 'up', down: 'down', left: 'left', right: 'right', home: 'home', end: 'end', pgup: 'pgup', pgdn: 'pgdn',
|
||||
insert: 'ins', ins: 'ins', delete: 'del', del: 'del', backspace: 'bs', bs: 'bs', bksp: 'bs', esc: 'esc',
|
||||
win: 'win', lwin: 'win', rwin: 'rwin', f1: 'f1', f2: 'f2', f3: 'f3', f4: 'f4', f5: 'f5', f6: 'f6',
|
||||
f7: 'f7', f8: 'f8', f9: 'f9', f10: 'f10', f11: 'f11', f12: 'f12', f13: 'f13', f14: 'f14', f15: 'f15', f16: 'f16',
|
||||
add: 'add', subtract: 'subtract', multiply: 'multiply', divide: 'divide',
|
||||
numpad0: 'n0', numpad1: 'n1', numpad2: 'n2', numpad3: 'n3', numpad4: 'n4',
|
||||
numpad5: 'n5', numpad6: 'n6', numpad7: 'n7', numpad8: 'n8', numpad9: 'n9'
|
||||
};
|
||||
|
||||
AutoTypeRunner.Substitutions = {
|
||||
title: function() {},
|
||||
username: function() {},
|
||||
url: function() {},
|
||||
password: function() {},
|
||||
notes: function() {},
|
||||
group: function() {},
|
||||
totp: function() {},
|
||||
s: function() {},
|
||||
'dt_simple': function() {},
|
||||
'dt_year': function() {},
|
||||
'dt_month': function() {},
|
||||
'dt_day': function() {},
|
||||
'dt_hour': function() {},
|
||||
'dt_minute': function() {},
|
||||
'dt_second': function() {},
|
||||
'dt_utc_simple': function() {},
|
||||
'dt_utc_year': function() {},
|
||||
'dt_utc_month': function() {},
|
||||
'dt_utc_day': function() {},
|
||||
'dt_utc_hour': function() {},
|
||||
'dt_utc_minute': function() {},
|
||||
'dt_utc_second': function() {}
|
||||
};
|
||||
|
||||
AutoTypeRunner.Commands = {
|
||||
wait: function() {},
|
||||
setDelay: function() {}
|
||||
};
|
||||
|
||||
AutoTypeRunner.prototype.resolve = function(entry, callback) {
|
||||
this.entry = entry;
|
||||
this.resolveCallback = callback;
|
||||
try {
|
||||
this.resolveOps(this.ops);
|
||||
if (!this.pendingResolves) {
|
||||
callback();
|
||||
}
|
||||
} catch (e) {
|
||||
return callback(e);
|
||||
}
|
||||
};
|
||||
|
||||
AutoTypeRunner.prototype.resolveOps = function(ops) {
|
||||
for (var i = 0, len = ops.length; i < len; i++) {
|
||||
this.forEachOp(ops[i], this.resolveOp, this);
|
||||
}
|
||||
};
|
||||
|
||||
AutoTypeRunner.prototype.forEachOp = function(op, fn, context) {
|
||||
switch (op.type) {
|
||||
case 'group':
|
||||
return this.resolveOps(op.value);
|
||||
case 'op':
|
||||
return fn.call(context, op);
|
||||
}
|
||||
};
|
||||
|
||||
AutoTypeRunner.prototype.resolveOp = function(op) {
|
||||
if (op.value.length === 1 && !op.sep) {
|
||||
// {x}
|
||||
op.type = 'text';
|
||||
return;
|
||||
}
|
||||
if (op.value.length === 1 && op.sep === ' ') {
|
||||
// {x 3}
|
||||
op.type = 'text';
|
||||
var ch = op.value, text = ch, len = +op.arg;
|
||||
while (text.length < len) {
|
||||
text += ch;
|
||||
}
|
||||
op.value = text;
|
||||
return;
|
||||
}
|
||||
var lowerValue = op.value.toLowerCase();
|
||||
var key = AutoTypeRunner.Keys[lowerValue];
|
||||
if (key) {
|
||||
if (op.sep === ' ' && +op.arg > 0) {
|
||||
// {TAB 3}
|
||||
op.type = 'group';
|
||||
op.value = [];
|
||||
var count = 0;
|
||||
for (var i = 0; i < count; i++) {
|
||||
op.value.push({ type: 'key', value: key });
|
||||
}
|
||||
} else {
|
||||
// {TAB}
|
||||
op.type = 'key';
|
||||
op.value = key;
|
||||
}
|
||||
return;
|
||||
}
|
||||
var substitution = AutoTypeRunner.Substitutions[lowerValue];
|
||||
if (substitution) {
|
||||
op.type = 'text';
|
||||
op.value = substitution(this, op);
|
||||
if (op.value === AutoTypeRunner.PendingResolve) {
|
||||
this.pendingResolves++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch (lowerValue) {
|
||||
case 'clearfield':
|
||||
op.type = 'group';
|
||||
op.value = [{ type: 'key', value: 'a', mod: { '^': true } }, { type: 'key', value: 'bs' }];
|
||||
return;
|
||||
case 'vkey':
|
||||
op.type = 'key';
|
||||
op.value = parseInt(op.value);
|
||||
break;
|
||||
case 'delay':
|
||||
op.type = 'cmd';
|
||||
op.value = op.sep === '=' ? 'setDelay' : 'wait';
|
||||
op.arg = +op.arg;
|
||||
break;
|
||||
}
|
||||
throw 'Bad op: ' + op.value;
|
||||
};
|
||||
|
||||
AutoTypeRunner.prototype.run = function() {
|
||||
};
|
||||
|
||||
module.exports = AutoTypeRunner;
|
|
@ -0,0 +1,38 @@
|
|||
'use strict';
|
||||
|
||||
var AutoTypeParser = require('./auto-type-parser');
|
||||
var Logger = require('../../util/logger');
|
||||
|
||||
var logger = new Logger('auto-type');
|
||||
|
||||
var AutoType = {
|
||||
run: function(entry, sequence, callback) {
|
||||
logger.debug('Start', sequence);
|
||||
try {
|
||||
var parser = new AutoTypeParser(sequence);
|
||||
var runner = parser.parse();
|
||||
logger.debug('Parsed', runner.ops.length);
|
||||
runner.resolve(entry, function(err) {
|
||||
if (err) {
|
||||
logger.error('Error', err);
|
||||
return callback(err);
|
||||
}
|
||||
logger.debug('Running', runner.ops);
|
||||
runner.run(function(err) {
|
||||
if (err) {
|
||||
logger.error('Run error', err);
|
||||
return callback(err);
|
||||
}
|
||||
logger.debug('Complete');
|
||||
return callback();
|
||||
});
|
||||
});
|
||||
runner.run();
|
||||
} catch (ex) {
|
||||
logger.error('Parse error', ex);
|
||||
return callback(ex);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = AutoType;
|
Loading…
Reference in New Issue