// helper functions for keyboard /** * Unescape an escaped string * @param str input string such as '\u017c' * @returns */ function unescapeStr(str) { str = str.replace(/\\u{([0-9a-fA-F]+)}/g, (a, b) => String.fromCodePoint(Number.parseInt(b, 16)) ); return str; } function getKeyboardLayers(id) { let q = _KeyboardData.keyboards[id].keyboard3.layers; if (!Array.isArray(q)) { q = [q]; } mogrifyAttrs(q); const keybag = getKeyboardKeys(id); mogrifyLayerList(q, keybag); return q; } function mogrifyLayerList(layerList, keybag) { layerList.forEach(({ layer }) => { layer.forEach(({ row }) => { row.forEach((r) => { r.keys = r.keys.split(" ").map((id) => Object.assign( { id, }, keybag[id] ) ); }); }); }); } function getImportFile(id) { return _KeyboardData.imports[id["@_path"].split("/")[1]]; } function getImportKeys(id) { const imp = getImportFile(id); if (!imp) { throw Error(`Could not load import ${JSON.stringify(id)}`); } return imp.keys.key; } function mogrifyKeys(keys) { // drop @' if (!keys) { return []; } return keys.reduce((p, v) => { // TODO: any other swapping mogrifyAttrs(v); const { id, output } = v; if (output) { v.output = unescapeStr(output); } p[id] = v; return p; }, {}); } function mogrifyAttrs(o) { for (const k of Object.keys(o)) { const ok = o[k]; if (/^@_/.test(k)) { const attr = k.substring(2); o[attr] = ok; delete o[k]; } else if (Array.isArray(ok)) { ok.forEach((e) => mogrifyAttrs(e)); } else if (typeof ok === "object") { mogrifyAttrs(ok); } } return o; } function getKeyboardKeys(id) { const keys = _KeyboardData.keyboards[id].keyboard3.keys.key || []; if (!keys) { throw Error(`No keys for ${id}`); } let imports = [ { // add implied import "@_base": "cldr", "@_path": "45/keys-Latn-implied.xml", }, ...(_KeyboardData.keyboards[id].keyboard3.keys.import || []), ]; const importedKeys = []; for (const fn of imports) { for (const k of getImportKeys(fn)) { importedKeys.push(k); } } return mogrifyKeys([...importedKeys, ...keys]); } function getIds() { return Object.keys(_KeyboardData.keyboards); }