196 lines
No EOL
6.7 KiB
JavaScript
196 lines
No EOL
6.7 KiB
JavaScript
const LINE_START = 'Event: time';
|
|
|
|
const ACTION_KEYUP = { id: 0, action: 'keyup' };
|
|
const ACTION_KEYDOWN = { id: 1, action: 'keydown' };
|
|
const ACTION_KEYHOLD = { id: 2, action: 'keyhold' };
|
|
|
|
const VARIABLE_KEY = '{{ key }}';
|
|
const VARIABLE_TYPE = '{{ type }}';
|
|
|
|
class Keyfilter {
|
|
constructor(keys, combos) {
|
|
if ((keys === undefined || keys.length === 0) && (combos === undefined || combos.length === 0)) {
|
|
return;
|
|
}
|
|
this.actions = new Map();
|
|
for (var index = 0; index < keys.length; index++) {
|
|
this.setAction(keys[index]);
|
|
}
|
|
this.currentCombo = undefined;
|
|
}
|
|
setAction(config) {
|
|
var type = ACTION_KEYDOWN;
|
|
switch (config.type.toLowerCase()) {
|
|
case ACTION_KEYUP.action:
|
|
type = ACTION_KEYUP;
|
|
break;
|
|
case ACTION_KEYHOLD.action:
|
|
type = ACTION_KEYHOLD;
|
|
break;
|
|
}
|
|
this.actions.set(config.key.toUpperCase(),
|
|
{
|
|
type: type,
|
|
event: config.event,
|
|
command: config.command,
|
|
args: config.args,
|
|
combo: config.combo,
|
|
delay: function () {
|
|
if (config.combo === undefined) {
|
|
return config.delay;
|
|
}
|
|
return config.delay || 1000;
|
|
}(),
|
|
}
|
|
);
|
|
}
|
|
filter(input) {
|
|
if (input === undefined || input.length === 0) {
|
|
return;
|
|
}
|
|
input = input.toString();
|
|
var lines = input.split("\n");
|
|
for (var index = 0; index < lines.length; index++) {
|
|
var line = lines[index];
|
|
if (line.length === 0) {
|
|
continue;
|
|
}
|
|
if (!line.startsWith(LINE_START)) {
|
|
continue;
|
|
}
|
|
const parsedEvent = this.parseLine(line);
|
|
if (parsedEvent === undefined) {
|
|
continue;
|
|
}
|
|
for (var [key, event] of this.actions) {
|
|
if (this.currentCombo === undefined && !this.isParsedEventValid(key, event, parsedEvent)) {
|
|
continue;
|
|
}
|
|
if (this.isStartOfCombo(key, event)) {
|
|
return this.getFilterResult(key, event, 'combo', this.currentCombo);
|
|
}
|
|
if (this.isPartOfCombo(parsedEvent)) {
|
|
if (parsedEvent.ignore) {
|
|
continue;
|
|
}
|
|
const result = this.getFilterResult(key, event, 'combo', this.currentCombo);
|
|
if (this.hasComboFinished()) {
|
|
this.resetCurrentCombo();
|
|
}
|
|
return result;
|
|
}
|
|
if (!this.isParsedEventValid(key, event, parsedEvent)) {
|
|
continue;
|
|
}
|
|
if (this.shouldBeDelayed(event)) {
|
|
return this.getFilterResult(key, event, 'delayed', true);
|
|
}
|
|
event.captured = new Date().getTime();
|
|
return this.getFilterResult(key, event);
|
|
}
|
|
}
|
|
}
|
|
isParsedEventValid(key, value, parsed) {
|
|
return key === parsed.key && (value.event === undefined || value.event === parsed.event) && value.type.id === parsed.type;
|
|
}
|
|
getFilterResult(key, event, extraName, extra) {
|
|
if (key === undefined || event === undefined) {
|
|
return;
|
|
}
|
|
let result = this.replaceVariables(
|
|
{
|
|
key: key,
|
|
type: event.type.action,
|
|
command: event.command,
|
|
args: event.args,
|
|
delay: event.delay
|
|
}
|
|
)
|
|
if (extraName !== undefined && extra !== undefined) {
|
|
result[extraName] = extra;
|
|
}
|
|
return result;
|
|
}
|
|
parseLine(line) {
|
|
try {
|
|
const parts = line.split(',');
|
|
const event = parts[1].substring(parts[1].indexOf('(') + 1, parts[1].lastIndexOf(')'));
|
|
const key = parts[2].substring(parts[2].indexOf('(') + 1, parts[2].indexOf(')'));
|
|
const type = parseInt(parts[3].split(' ').pop());
|
|
return { event: event, key: key, type: type };
|
|
} catch (err) {
|
|
return;
|
|
}
|
|
}
|
|
replaceVariables(filtered) {
|
|
for (var index = 0; index < filtered.args.length; index++) {
|
|
filtered.args[index] = filtered.args[index].replace(VARIABLE_KEY, filtered.key);
|
|
filtered.args[index] = filtered.args[index].replace(VARIABLE_TYPE, filtered.type);
|
|
}
|
|
return filtered;
|
|
}
|
|
shouldBeDelayed(event) {
|
|
if (event.delay === undefined || event.delay === 0 || event.captured === undefined) {
|
|
return false;
|
|
}
|
|
return new Date().getTime() - event.captured < event.delay;
|
|
}
|
|
isStartOfCombo(key, event) {
|
|
if (this.currentCombo !== undefined || event.combo === undefined) {
|
|
return false;
|
|
}
|
|
this.currentCombo = {
|
|
key: key,
|
|
type: event.type,
|
|
start: new Date().getTime(),
|
|
done: [key],
|
|
remaining: JSON.parse(JSON.stringify(event.combo)),
|
|
finished: this.hasComboFinished()
|
|
};
|
|
return true;
|
|
}
|
|
isPartOfCombo(parsedEvent) {
|
|
if (this.hasComboTimedOut()) {
|
|
this.resetCurrentCombo();
|
|
}
|
|
if (this.currentCombo === undefined) {
|
|
return false;
|
|
}
|
|
if (this.currentCombo.done.includes(parsedEvent.key)) {
|
|
parsedEvent.ignore = true;
|
|
return true;
|
|
}
|
|
if (this.currentCombo.type.id !== parsedEvent.type) {
|
|
return false;
|
|
}
|
|
if (this.currentCombo.remaining[0].toUpperCase() !== parsedEvent.key) {
|
|
return false;
|
|
}
|
|
this.currentCombo.key = parsedEvent.key;
|
|
this.currentCombo.done.push(parsedEvent.key);
|
|
this.currentCombo.remaining.shift();
|
|
this.currentCombo.finished = this.hasComboFinished();
|
|
return true;
|
|
}
|
|
hasComboFinished() {
|
|
return this.currentCombo !== undefined &&
|
|
this.currentCombo.remaining !== undefined &&
|
|
this.currentCombo.remaining.length === 0;
|
|
}
|
|
hasComboTimedOut() {
|
|
return this.currentCombo !== undefined &&
|
|
this.currentCombo.delay !== undefined &&
|
|
this.currentCombo.start !== undefined &&
|
|
new Date().getTime() - this.currentCombo.start > this.currentCombo.delay;
|
|
}
|
|
resetCurrentCombo() {
|
|
this.currentCombo = undefined;
|
|
}
|
|
isValid() {
|
|
return this.actions != undefined && this.actions.size > 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
module.exports = Keyfilter; |