remove layout stuff

This commit is contained in:
Jörg Breitbart
2026-05-18 21:15:29 +02:00
parent c0007301c1
commit 2f49f321c1
10 changed files with 0 additions and 951 deletions
-38
View File
@@ -1,38 +0,0 @@
import fs from 'node:fs';
import path from 'node:path';
const DATA = {};
for (let i = 2; i < process.argv.length; ++i) {
const filename = process.argv[i];
const key = path.basename(filename).split('.')[0];
const value = JSON.parse(fs.readFileSync(filename));
DATA[key] = value;
}
let keys = [];
for (const map in DATA) {
keys = keys.concat(Object.keys(DATA[map]));
}
const KEYS = Array.from(new Set(keys)).sort();
const ACC = Object.fromEntries(Object.keys(DATA).map((el, i) => [el, i]));
const MAP = Object.fromEntries(KEYS.map(el => [el, []]));
for (const key of KEYS) {
for (const map in ACC) {
MAP[key].push(DATA[map][key]);
}
}
// minify if all entries are of length 1
for (const key in MAP) {
for (const c of MAP[key]) {
if (!c || c.length !== 1)
break;
}
MAP[key] = MAP[key].join('');
}
const FINAL = {acc: ACC, map: MAP};
console.log(FINAL);
console.log(JSON.stringify(FINAL).length);
-270
View File
@@ -1,270 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Keyboard Layout</title>
<style>
body {
background: #f8f8f8;
font-size: 16px;
}
#keyinput {
border: 1px solid #aaa;
padding: 8px;
display: inline-block;
margin: 2px;
border-radius: 4px;
box-shadow: black 1px 1px 1px;
width: 1ch;
}
span {
border: 1px solid #aaa;
padding: 6px 8px;
display: inline-block;
margin: 2px;
border-radius: 4px;
box-shadow: black 1px 1px 1px;
min-width: 2ch;
text-align: center;
background: #fff;
}
#msg {
color: red;
display: none;
text-align: left;
}
#output {
border: 1px solid #666;
font-size: 12px;
}
</style>
</head>
<body>
<h1>Keyboard Base Layout</h1>
<p>
To record your keyboard base layout (unshifted), press all keys from left to right.<br>
While you can record most keys with this helper, it is enough to press all character producing keys.<br><br>
<span>Esc</span> deletes last key except itself.<br>
<span>Enter</span> starts new key row (Enter itself gets not shown as key).
</p>
<div style="min-height: 200px; margin-top: 20px">
<div>
<span id="Escape">Esc</span><input id="keyinput"/>
</div>
<span id="msg"></span>
</div>
<div style="padding: 20px 5px;">
<label>Filename: <input type="text" id="filename"></label>
<button id="download">Export Mapping</button>
</div>
<pre id="output"></pre>
<script>
const silent = [
'Escape',
'Backspace',
'Tab',
'Enter',
'CapsLock',
'ShiftLeft', 'ShiftRight',
'ControlLeft', 'ControlRight',
'MetaLeft', 'MetaRight',
'AltLeft', 'AltRight',
'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12',
];
const modifier = [
'CapsLock',
'ShiftLeft', 'ShiftRight',
'ControlLeft', 'ControlRight',
'MetaLeft', 'MetaRight',
'AltLeft', 'AltRight',
];
const prevent = ['Tab', 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12'];
const rename = { 'Escape': 'Esc', 'CapsLock': 'CapsL' };
const inp = document.querySelector('#keyinput');
inp.focus();
let current_code = '';
let err = false;
const data = {};
let cl_hold = false;
let dead_code = '';
let charProduced = false;
function getClState(ev) {
if (ev.code !== 'CapsLock') {
cl_hold = ev.getModifierState('CapsLock');
return cl_hold;
}
if (ev.type === 'keydown' && ev.code === 'CapsLock') {
cl_hold = !cl_hold;
return true;
}
return cl_hold;
}
function createSpan(code, value) {
const el = document.createElement('span');
el.innerText = rename[value] ?? value;
el.id = code;
el.style.boxShadow = 'inset black 1px 1px 1px';
inp.parentNode.insertBefore(el, inp);
}
function showMsg(s) {
const el = document.querySelector('#msg');
el.innerText = s;
el.style.display = 'block';
}
function hideMsg() {
const el = document.querySelector('#msg');
el.style.display = 'none';
}
inp.addEventListener('keyup', ev => {
console.log('up', ev);
const cl_state = getClState(ev);
const el = document.querySelector('#'+ev.code);
if (el && ev.code !== 'CapsLock') el.style.boxShadow = 'black 1px 1px 1px';
const cl = document.querySelector('#CapsLock');
if (cl) {
if (cl_state) {
cl.style.boxShadow = 'inset black 1px 1px 1px';
} else {
cl.style.boxShadow = 'black 1px 1px 1px';
}
}
if (charProduced) {
hideMsg();
dead_code = '';
}
if (ev.key === 'Dead' && ev.code !== current_code) {
showMsg('Dead key found, press same key again.');
dead_code = ev.code;
current_code = ev.code;
if (!document.querySelector('#'+current_code)) {
createSpan(current_code, ev.key);
} else {
document.querySelector('#'+current_code).style.boxShadow = 'inset black 1px 1px 1px';
}
}
});
inp.addEventListener('keydown', ev => {
hideMsg();
charProduced = false;
console.log('down', ev);
const cl_state = getClState(ev);
const cl = document.querySelector('#CapsLock');
if (cl) {
if (cl_state) {
cl.style.boxShadow = 'inset black 1px 1px 1px';
} else {
cl.style.boxShadow = 'black 1px 1px 1px';
}
}
if ((ev.code === 'Unidentified' || ev.code === '')) {
if (dead_code) {
current_code = dead_code;
return;
};
err = true;
ev.preventDefault();
ev.stopImmediatePropagation();
showMsg('Unidentified key found.');
return;
}
if (ev.code === 'Escape') {
inp.previousSibling?.remove();
}
if (prevent.includes(ev.code)) {
ev.preventDefault();
ev.stopImmediatePropagation();
ev.stopPropagation();
}
if (current_code == 'CapsLock' && ev.code == 'CapsLock') {
return;
}
if (!modifier.includes(ev.code)) {
if (ev.shiftKey || ev.altKey || ev.ctrlKey || ev.getModifierState('CapsLock') || ev.getModifierState('AltGraph')) {
showMsg('Disable modifier keys.');
err = true;
return;
}
}
if (dead_code) {
if (ev.code !== dead_code) {
showMsg('Dead key found, press same key again.');
err = true;
return;
}
dead_code = '';
}
if (ev.key === 'Dead' && ev.code !== current_code) {
showMsg('Dead key found, press same key again.');
dead_code = ev.code;
}
err =false;
current_code = ev.code;
if (ev.code === 'Enter') {
const el = document.createElement('br');
inp.parentNode.insertBefore(el, inp);
return;
}
if (silent.includes(current_code) || ev.key === 'Dead') {
if (!document.querySelector('#'+current_code)) {
createSpan(current_code, ev.key);
} else {
document.querySelector('#'+current_code).style.boxShadow = 'inset black 1px 1px 1px';
}
}
});
inp.addEventListener('input', ev => {
if (!ev.target.value || err) {
ev.target.value = '';
return;
}
charProduced = true;
data[current_code] = ev.target.value;
let value = ev.target.value;
if (value === ' ') value = '\xa0'.repeat(50);
ev.target.value = '';
if (document.querySelector('#'+current_code)) {
const el = document.querySelector('#'+current_code);
el.innerText = value;
el.style.boxShadow = 'inset black 1px 1px 1px';
} else {
createSpan(current_code, value);
}
let keys = Object.keys(data).sort();
const final = {};
for (const key of keys) final[key] = data[key];
const out = JSON.stringify(final, null, 2);
console.log(out);
document.querySelector('#output').innerText = out;
});
function download() {
let keys = Object.keys(data).sort();
const final = {};
for (const key of keys) final[key] = data[key];
const out = JSON.stringify(final, null, 2);
const blob = new Blob([out], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.setAttribute('href', url);
link.setAttribute('download', document.querySelector('#filename').value || 'data.json');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
}
document.querySelector('#download').addEventListener('click', download);
</script>
</body>
</html>
-50
View File
@@ -1,50 +0,0 @@
{
"Backquote": "^",
"Backslash": "#",
"BracketLeft": "ü",
"BracketRight": "+",
"Comma": ",",
"Digit0": "0",
"Digit1": "1",
"Digit2": "2",
"Digit3": "3",
"Digit4": "4",
"Digit5": "5",
"Digit6": "6",
"Digit7": "7",
"Digit8": "8",
"Digit9": "9",
"Equal": "´",
"IntlBackslash": "<",
"KeyA": "a",
"KeyB": "b",
"KeyC": "c",
"KeyD": "d",
"KeyE": "e",
"KeyF": "f",
"KeyG": "g",
"KeyH": "h",
"KeyI": "i",
"KeyJ": "j",
"KeyK": "k",
"KeyL": "l",
"KeyM": "m",
"KeyN": "n",
"KeyO": "o",
"KeyP": "p",
"KeyQ": "q",
"KeyR": "r",
"KeyS": "s",
"KeyT": "t",
"KeyU": "u",
"KeyV": "v",
"KeyW": "w",
"KeyX": "x",
"KeyY": "z",
"KeyZ": "y",
"Minus": "ß",
"Period": ".",
"Quote": "ä",
"Semicolon": "ö",
"Slash": "-"
}
-50
View File
@@ -1,50 +0,0 @@
{
"Backquote": "º",
"Backslash": "ç",
"BracketLeft": "`",
"BracketRight": "+",
"Comma": ",",
"Digit0": "0",
"Digit1": "1",
"Digit2": "2",
"Digit3": "3",
"Digit4": "4",
"Digit5": "5",
"Digit6": "6",
"Digit7": "7",
"Digit8": "8",
"Digit9": "9",
"Equal": "¡",
"IntlBackslash": "<",
"KeyA": "a",
"KeyB": "b",
"KeyC": "c",
"KeyD": "d",
"KeyE": "e",
"KeyF": "f",
"KeyG": "g",
"KeyH": "h",
"KeyI": "i",
"KeyJ": "j",
"KeyK": "k",
"KeyL": "l",
"KeyM": "m",
"KeyN": "n",
"KeyO": "o",
"KeyP": "p",
"KeyQ": "q",
"KeyR": "r",
"KeyS": "s",
"KeyT": "t",
"KeyU": "u",
"KeyV": "v",
"KeyW": "w",
"KeyX": "x",
"KeyY": "y",
"KeyZ": "z",
"Minus": "'",
"Period": ".",
"Quote": "´",
"Semicolon": "ñ",
"Slash": "-"
}
-50
View File
@@ -1,50 +0,0 @@
{
"Backquote": "|",
"Backslash": "}",
"BracketLeft": "´",
"BracketRight": "+",
"Comma": ",",
"Digit0": "0",
"Digit1": "1",
"Digit2": "2",
"Digit3": "3",
"Digit4": "4",
"Digit5": "5",
"Digit6": "6",
"Digit7": "7",
"Digit8": "8",
"Digit9": "9",
"Equal": "¿",
"IntlBackslash": "<",
"KeyA": "a",
"KeyB": "b",
"KeyC": "c",
"KeyD": "d",
"KeyE": "e",
"KeyF": "f",
"KeyG": "g",
"KeyH": "h",
"KeyI": "i",
"KeyJ": "j",
"KeyK": "k",
"KeyL": "l",
"KeyM": "m",
"KeyN": "n",
"KeyO": "o",
"KeyP": "p",
"KeyQ": "q",
"KeyR": "r",
"KeyS": "s",
"KeyT": "t",
"KeyU": "u",
"KeyV": "v",
"KeyW": "w",
"KeyX": "x",
"KeyY": "y",
"KeyZ": "z",
"Minus": "'",
"Period": ".",
"Quote": "{",
"Semicolon": "ñ",
"Slash": "-"
}
-50
View File
@@ -1,50 +0,0 @@
{
"Backquote": "²",
"Backslash": "*",
"BracketLeft": "^",
"BracketRight": "$",
"Comma": ";",
"Digit0": "à",
"Digit1": "&",
"Digit2": "é",
"Digit3": "\"",
"Digit4": "'",
"Digit5": "(",
"Digit6": "-",
"Digit7": "è",
"Digit8": "_",
"Digit9": "ç",
"Equal": "=",
"IntlBackslash": "<",
"KeyA": "q",
"KeyB": "b",
"KeyC": "c",
"KeyD": "d",
"KeyE": "e",
"KeyF": "f",
"KeyG": "g",
"KeyH": "h",
"KeyI": "i",
"KeyJ": "j",
"KeyK": "k",
"KeyL": "l",
"KeyM": ",",
"KeyN": "n",
"KeyO": "o",
"KeyP": "p",
"KeyQ": "a",
"KeyR": "r",
"KeyS": "s",
"KeyT": "t",
"KeyU": "u",
"KeyV": "v",
"KeyW": "z",
"KeyX": "x",
"KeyY": "y",
"KeyZ": "w",
"Minus": ")",
"Period": ":",
"Quote": "ù",
"Semicolon": "m",
"Slash": "!"
}
-50
View File
@@ -1,50 +0,0 @@
{
"Backquote": "`",
"Backslash": "\\",
"BracketLeft": "[",
"BracketRight": "]",
"Comma": ",",
"Digit0": "0",
"Digit1": "1",
"Digit2": "2",
"Digit3": "3",
"Digit4": "4",
"Digit5": "5",
"Digit6": "6",
"Digit7": "7",
"Digit8": "8",
"Digit9": "9",
"Equal": "=",
"IntlBackslash": "«",
"KeyA": "α",
"KeyB": "β",
"KeyC": "ψ",
"KeyD": "δ",
"KeyE": "ε",
"KeyF": "φ",
"KeyG": "γ",
"KeyH": "η",
"KeyI": "ι",
"KeyJ": "ξ",
"KeyK": "κ",
"KeyL": "λ",
"KeyM": "μ",
"KeyN": "ν",
"KeyO": "ο",
"KeyP": "π",
"KeyQ": ";",
"KeyR": "ρ",
"KeyS": "σ",
"KeyT": "τ",
"KeyU": "θ",
"KeyV": "ω",
"KeyW": "ς",
"KeyX": "χ",
"KeyY": "υ",
"KeyZ": "ζ",
"Minus": "-",
"Period": ".",
"Quote": "'",
"Semicolon": "´",
"Slash": "/"
}
-50
View File
@@ -1,50 +0,0 @@
{
"Backquote": "ё",
"Backslash": "\\",
"BracketLeft": "х",
"BracketRight": "ъ",
"Comma": "б",
"Digit0": "0",
"Digit1": "1",
"Digit2": "2",
"Digit3": "3",
"Digit4": "4",
"Digit5": "5",
"Digit6": "6",
"Digit7": "7",
"Digit8": "8",
"Digit9": "9",
"Equal": "=",
"IntlBackslash": "/",
"KeyA": "ф",
"KeyB": "и",
"KeyC": "с",
"KeyD": "в",
"KeyE": "у",
"KeyF": "а",
"KeyG": "п",
"KeyH": "р",
"KeyI": "ш",
"KeyJ": "о",
"KeyK": "л",
"KeyL": "д",
"KeyM": "ь",
"KeyN": "т",
"KeyO": "щ",
"KeyP": "з",
"KeyQ": "й",
"KeyR": "к",
"KeyS": "ы",
"KeyT": "е",
"KeyU": "г",
"KeyV": "м",
"KeyW": "ц",
"KeyX": "ч",
"KeyY": "н",
"KeyZ": "я",
"Minus": "-",
"Period": "ю",
"Quote": "э",
"Semicolon": "ж",
"Slash": "."
}
-50
View File
@@ -1,50 +0,0 @@
{
"Backquote": "`",
"Backslash": "\\",
"BracketLeft": "[",
"BracketRight": "]",
"Comma": ",",
"Digit0": "0",
"Digit1": "1",
"Digit2": "2",
"Digit3": "3",
"Digit4": "4",
"Digit5": "5",
"Digit6": "6",
"Digit7": "7",
"Digit8": "8",
"Digit9": "9",
"Equal": "=",
"IntlBackslash": "\\",
"KeyA": "a",
"KeyB": "b",
"KeyC": "c",
"KeyD": "d",
"KeyE": "e",
"KeyF": "f",
"KeyG": "g",
"KeyH": "h",
"KeyI": "i",
"KeyJ": "j",
"KeyK": "k",
"KeyL": "l",
"KeyM": "m",
"KeyN": "n",
"KeyO": "o",
"KeyP": "p",
"KeyQ": "q",
"KeyR": "r",
"KeyS": "s",
"KeyT": "t",
"KeyU": "u",
"KeyV": "v",
"KeyW": "w",
"KeyX": "x",
"KeyY": "y",
"KeyZ": "z",
"Minus": "-",
"Period": ".",
"Quote": "'",
"Semicolon": ";",
"Slash": "/"
}
-293
View File
@@ -1,293 +0,0 @@
/**
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*
* Keyboard layout detection.
*/
interface IMap {
acc: {[index: string]: number};
map: {[index: string]: string | string[]};
}
interface IKeyResult {
layouts: string[];
certain: number;
key: string | undefined | (string | undefined)[];
}
/**
* rebuild map: node bin/keyboard_layouts.mjs fixtures/keyboard_layouts/*
*/
const MAP: IMap = {
acc: { DE: 0, ES: 1, ES_LA: 2, FR: 3, GR: 4, RU: 5, US: 6 },
map: {
Backquote: '^º|²`ё`',
Backslash: '#ç}*\\\\\\',
BracketLeft: 'ü`´^[х[',
BracketRight: '+++$]ъ]',
Comma: ',,,;,б,',
Digit0: '000à000',
Digit1: '111&111',
Digit2: '222é222',
Digit3: '333"333',
Digit4: "444'444",
Digit5: '555(555',
Digit6: '666-666',
Digit7: '777è777',
Digit8: '888_888',
Digit9: '999ç999',
Equal: '´¡¿====',
IntlBackslash: '<<<<«/\\',
KeyA: 'aaaqαфa',
KeyB: 'bbbbβиb',
KeyC: 'ccccψсc',
KeyD: 'ddddδвd',
KeyE: 'eeeeεуe',
KeyF: 'ffffφаf',
KeyG: 'ggggγпg',
KeyH: 'hhhhηрh',
KeyI: 'iiiiιшi',
KeyJ: 'jjjjξоj',
KeyK: 'kkkkκлk',
KeyL: 'llllλдl',
KeyM: 'mmm,μьm',
KeyN: 'nnnnνтn',
KeyO: 'ooooοщo',
KeyP: 'ppppπзp',
KeyQ: 'qqqa;йq',
KeyR: 'rrrrρкr',
KeyS: 'ssssσыs',
KeyT: 'ttttτеt',
KeyU: 'uuuuθгu',
KeyV: 'vvvvωмv',
KeyW: 'wwwzςцw',
KeyX: 'xxxxχчx',
KeyY: 'zyyyυнy',
KeyZ: 'yzzwζяz',
Minus: "ß'')---",
Period: '...:.ю.',
Quote: "ä´{ù'э'",
Semicolon: 'öññm´ж;',
Slash: '---!/./'
}
};
export class LayoutDetector {
private _keys: {[index: string]: number} = {};
private _values: (string | string[])[] = [];
private _rec: string[] = [];
private _acc: string[] = Object.keys(MAP.acc);
private _cand: {layout: string; match: number;}[];
private _key: IKeyResult;
constructor() {
let p = 0;
for (const key of Object.keys(MAP.map)) {
this._keys[key] = p++;
this._values.push(MAP.map[key]);
this._rec.push('');
}
this._cand = [...this._acc].map(e => ({layout: e, match: 0 }));
this._key = { layouts: ['US_QWERTY'], certain: 0, key: undefined };
}
/**
* Reset detector.
*/
public reset(): void {
this._rec = this._rec.map(e => '');
}
/**
* Return list of registered layouts.
*/
public get layouts(): string[] {
return Object.keys(MAP.acc);
}
/**
* Return list of supported key codes.
*/
public get codes(): string[] {
return Object.keys(MAP.map);
}
/**
* Get character key for key code and layout.
* The known key codes can be requested with `.codes`,
* the registered layouts with `.layouts`.
*/
public getLayoutKey(code: string, layout: string): string | undefined {
if (MAP.acc[layout] !== undefined && MAP.map[code]) {
return MAP.map[code][MAP.acc[layout]];
}
}
/**
* Feed a keyboard event `ev` to the detector.
*/
public feed(ev: KeyboardEvent): void {
// FIXME: Should we check for OS? (not supported by Safari)
if (ev.shiftKey || ev.ctrlKey || ev.altKey || ev.metaKey
|| ev.getModifierState('AltGraph')
|| ev.getModifierState('CapsLock')
) {
return;
}
const pos = this._keys[ev.code];
if (pos !== undefined) {
if (this._rec[pos] && this._rec[pos] !== ev.key) {
// The key value should never change for the same layout,
// so we treat a sudden change as a layout change.
this.reset();
}
this._rec[pos] = ev.key;
}
}
/**
* Shows all known layouts and their degree of matching mappings
* sorted descending (likely layouts first).
* Ideally there is only one leading layout with a match of 1.
* If the leading match is not 1, then the user uses an
* unknown or custom layout.
*/
public matches(): {layout: string; match: number;}[] {
for (let i = 0; i < this._cand.length; ++i) {
this._cand[i].match = 0;
}
let c = 0;
for (let k = 0; k < this._rec.length; ++k) {
const v = this._rec[k];
if (v) {
c++;
const values = this._values[k];
for (let i = 0; i < this._acc.length; ++i) {
if (v === values[i]) {
this._cand[i].match++;
}
}
}
}
if (!c) {
return this._cand;
}
const sorted = [...this._cand].sort((a, b) => b.match - a.match);
for (let i = 0; i < sorted.length; ++i) {
sorted[i].match /= c;
}
return sorted;
}
/**
* Tries to resolve a key code to a key character.
* If `certain` is 1 then the result matches the listed layouts.
* Ideally only one layout is returned, then the detector has seen enough
* key events in `feed`.
* If multiple layouts are returned but only one key, then the layout is
* not yet fully determined but the key code is already known from `feed`.
* When multiple keys are returned, then the character undetermined
* and the layout needs further resolving with resolve.
* A certainty lesser than 1 can have different reasons:
* - not enough key event fed yet (multiple layouts returned)
* - user has an unknown or custom layout
* If `certain` is 0 the result should not be used as the dector
* has not seen any key events at all.
*/
public getKey(code: string): IKeyResult {
const lm = this.matches();
let candidates = [];
let lastMatch = 0;
for (let i = 0; i < lm.length; ++i) {
if (lm[i].match === 0) {
break;
}
if (lm[i].match === 1) {
lastMatch = 1;
candidates.push(lm[i].layout);
} else if (lm[i].match >= lastMatch) {
lastMatch = lm[i].match;
candidates.push(lm[i].layout);
}
}
if (candidates.length === 1) {
this._key.layouts = candidates;
this._key.certain = lastMatch;
this._key.key = MAP.map[code]?.[MAP.acc[candidates[0]]];
return this._key;
}
if (!candidates.length) {
candidates = [...this._acc];
}
const values = [];
for (let i = 0; i < candidates.length; ++i) {
values.push(MAP.map[code]?.[MAP.acc[candidates[i]]]);
}
const valuesSet = new Set(values);
// if all candidates yield the same char, return it
if (valuesSet.size === 1) {
this._key.layouts = candidates;
this._key.certain = lastMatch;
const [value] = values;
this._key.key = value;
return this._key;
}
// fallthrough
this._key.layouts = candidates;
this._key.certain = lastMatch / valuesSet.size;
this._key.key = values;
return this._key;
}
/**
* Calculate distance to resolve keyboard layout.
* Returns the candicate layouts and a list of keys resolving layout ambiguity.
* The key list is sorted descending by candidate differences for a key code
* (picking a high difference code needs less follow-up steps).
* The user should be asked to press the corresponding key and the key event
* should be fed to `feed`.
* Repeat this process until this method returns only one layout.
*/
public resolve(): any {
const lm = this.matches();
let candidates = [];
let lastMatch = 0;
for (let i = 0; i < lm.length; ++i) {
if (lm[i].match === 0) {
break;
}
if (lm[i].match === 1) {
lastMatch = 1;
candidates.push(lm[i].layout);
} else if (lm[i].match >= lastMatch) {
lastMatch = lm[i].match;
candidates.push(lm[i].layout);
}
}
if (candidates.length === 1) {
return { layouts: candidates, keys: [] };
}
if (!candidates.length) {
candidates = [...this._acc];
}
const acc = candidates.map(e => MAP.acc[e]);
const codes = Object.keys(this._keys);
const values = [];
for (let i = 0; i < this._values.length; ++i) {
const value = new Set();
for (let k = 0; k < acc.length; ++k) {
value.add(this._values[i][k]);
}
const len = (new Set(value)).size;
if (len > 1) {
values.push({code: codes[i], keys: [...value].sort()});
}
}
values.sort((a, b) => b.keys.length - a.keys.length);
return { layouts: candidates, keys: values };
}
}