@dosaygo/browserbox
Version:
BrowserBox - embeddable remote browser isolation. A WebView for the open web, an unrestricted IFRAME element, a CORS-proxy, and reverse proxy. And secure document viewer. All produced by DOSAYGO.
201 lines (174 loc) • 7.07 kB
JavaScript
// creating this is diabolical. it should be an interview question.
import fs from 'fs';
import _keyDefinitions from '../src/public/kbd.js';
/**
* Corrects the key definitions according to the given specification.
*
* @param {Object} definitions - The original key definitions.
* @returns {Object} The corrected key definitions.
*/
// Correct key definitions
const correctKeyDefinitions = (definitions) => {
for (const [key, value] of Object.entries(definitions)) {
if (isSpecialHandlingKey(key)) {
continue;
}
const significantKey = extractSignificantKey(key);
if (!value.shiftKeyCode && !value.text) {
value.keyCode = significantKey.charCodeAt(0);
}
if (!value.text && value.key !== significantKey) {
value.key = significantKey;
}
// Pass original value to retain the original code for non-alphanumeric keys
const expectedCode = generateExpectedCode(significantKey, value);
if (value.code !== expectedCode) {
value.code = expectedCode;
}
const correctShiftKey = calculateShiftKey(significantKey);
if (correctShiftKey && value.shiftKey !== correctShiftKey) {
value.shiftKey = correctShiftKey;
}
}
return definitions;
};
/**
* Checks if a key requires special handling.
*
* @param {string} key - The key to check.
* @returns {boolean} True if the key is a special handling key, false otherwise.
*/
const isSpecialHandlingKey = (key) => {
const specialHandlingKeys = [
'Power', 'Eject', 'Abort', 'Help', 'Backspace', 'Tab', 'Numpad5', 'NumpadEnter',
'Enter', '\r', '\n', 'ShiftLeft', 'ShiftRight', 'ControlLeft', 'ControlRight',
'AltLeft', 'AltRight', 'Pause', 'CapsLock', 'Escape', 'Convert', 'NonConvert',
'Space', 'Numpad9', 'PageUp', 'Numpad3', 'PageDown', 'End', 'Numpad1', 'Home',
'Numpad7', 'ArrowLeft', 'Numpad4', 'Numpad8', 'ArrowUp', 'ArrowRight', 'Numpad6',
'Numpad2', 'ArrowDown', 'Select', 'Open', 'PrintScreen', 'Insert', 'Numpad0',
'Delete', 'NumpadDecimal', 'MetaLeft', 'MetaRight', 'ContextMenu', 'NumpadMultiply',
'NumpadAdd', 'NumpadSubtract', 'NumpadDivide', 'F1', 'F2', 'F3', 'F4', 'F5', 'F6',
'F7', 'F8', 'F9', 'F10', 'F11', 'F12', 'F13', 'F14', 'F15', 'F16', 'F17', 'F18',
'F19', 'F20', 'F21', 'F22', 'F23', 'F24', 'NumLock', 'ScrollLock', 'AudioVolumeMute',
'AudioVolumeDown', 'AudioVolumeUp', 'MediaTrackNext', 'MediaTrackPrevious',
'MediaStop', 'MediaPlayPause', 'Semicolon', 'Equal', 'NumpadEqual', 'Comma',
'Minus', 'Period', 'Slash', 'Backquote', 'BracketLeft', 'Backslash', 'BracketRight',
'Quote', 'AltGraph', 'Props', 'Cancel', 'Clear', 'Shift', 'Control', 'Alt',
'Accept', 'ModeChange', 'Print', 'Execute', '\u0000', 'Meta', 'Attn', 'CrSel',
'ExSel', 'EraseEof', 'Play', 'ZoomOut'
];
return specialHandlingKeys.includes(key);
};
/**
* Extracts the significant part of the key.
*
* @param {string} key - The original key.
* @returns {string} The significant part of the key.
*/
const extractSignificantKey = (key) => {
return key.startsWith('Key') || key.startsWith('Digit')
? key.slice(3)
: key;
};
/**
* Generates the expected code based on the significant key.
*
* @param {string} significantKey - The significant part of the key.
* @param {Object} originalValue - The original value of the key definition.
* @returns {string} The expected code.
*/
const generateExpectedCode = (significantKey, originalValue) => {
if (/^[a-zA-Z]$/.test(significantKey)) {
return `Key${significantKey.toUpperCase()}`;
} else if (/^[0-9]$/.test(significantKey)) {
return `Digit${significantKey}`;
} else {
// For non-alphanumeric keys, retain the original code if it exists
return originalValue.code || undefined;
}
};
/**
* Calculates the shiftKey value based on the significant key.
*
* @param {string} significantKey - The significant part of the key.
* @returns {string|null} The shiftKey value, or null if not applicable.
*/
const calculateShiftKey = (significantKey) => {
if (/^[a-z]$/.test(significantKey)) {
return significantKey.toUpperCase();
} else if (/^[A-Z]$/.test(significantKey)) {
return significantKey;
} else if (/^[0-9]$/.test(significantKey)) {
const shiftSymbols = ")!@#$%^&*(";
return shiftSymbols[parseInt(significantKey, 10)];
}
return undefined;
};
/**
* Formats the corrected definitions and writes them to a file.
*
* @param {Object} definitions - The corrected key definitions.
* @param {string} filename - The file to write to.
*/
const formatSpecialKey = (entry, key) => {
if (entry.code === 'NumpadDecimal' && entry.keyCode === 46) {
// Handle the special case for NumpadDecimal
key = '\\u0000';
entry.key = '\\u0000'
} else if (key === '\r') {
key = '\\r';
} else if (key === '\n') {
key = '\\n';
} else if (key == '\'') {
key = '\\\'';
} else if (key == '\\') {
key = '\\\\';
}
return key;
};
const formatAndWriteDefinitions = (definitions, filename) => {
const sortedKeys = Object.keys(definitions).sort();
const formattedOutput = sortedKeys.map(key => {
const entry = definitions[key];
// Handle special keys like '\u0000', '\r', '\n'
const formattedKey = formatSpecialKey(entry, key);
const formattedEntry = Object.entries(entry)
.map(([field, value]) => {
if (field === 'key' && entry.code === 'NumpadDecimal' && entry.keyCode == 46 ) {
return `${field}: '${'\\'}${'u'}${'0000'}'`; // Write \u0000 as a literal, not escaped
} else if (typeof value === 'string') {
// Properly escape special characters in values
value = value
.replace(/\\/g, '\\\\') // Escape backslashes
.replace(/'/g, '\\\'') // Escape single quotes
.replace(/\r/g, '\\r') // Replace carriage return with '\r'
.replace(/\n/g, '\\n'); // Replace newline with '\n'
return `${field}: '${value}'`;
} else {
return `${field}: ${value}`;
}
})
.join(', ');
// Format the key properly, ensuring special cases are handled
const quotedKey = formattedKey.startsWith('\\u') ? `'${formattedKey}'` : `'${formattedKey}'`;
return ` ${quotedKey}: { ${formattedEntry} }`;
});
const outputContent = `const keys = {\n${formattedOutput.join(',\n')}\n};\n\nexport default keys;`;
// Write the formatted output to the file
fs.writeFileSync(filename, outputContent, 'utf8');
};
/**
* Determines if a key needs quotes based on its characters.
*
* @param {string} key - The key to check.
* @returns {boolean} True if the key needs quotes, false otherwise.
*/
const isKeyNeedingQuotes = (key) => {
return !/^[a-zA-Z_]\w*$/.test(key);
};
// Correct the key definitions
const correctedKeyDefinitions = correctKeyDefinitions(_keyDefinitions);
// Write the corrected definitions to a new file
formatAndWriteDefinitions(correctedKeyDefinitions, 'kbd2.js');
console.log("Corrected key definitions written to 'kbd2.js'");