ForgetMeNow / Duolingo Auto Layout

// ==UserScript==
// @name         Duolingo Auto Layout
// @namespace    http://ilnicki.me
// @version      0.1
// @description  The script provide you ability to type right symbols in answer box without switching keyboard layout. 
// @author       ilnicki
// @include      https://www.duolingo.com/*
// @grant        none
// ==/UserScript==
var $dal = {
    char: function(value, shiftValue, altValue, altShiftValue) {
        if(typeof value === "object" && value !== null) {
            this.value = value.val;
            this.shiftValue = value.shift;
            this.altValue = value.alt;
            this.altShiftValue = value.altShift;
        }
        else {
            this.value =  value;
            this.shiftValue = shiftValue;
            this.altValue = altValue;
            this.altShiftValue = altShiftValue;
        }
    },
    charmap: function(charMap, parentMap) {
        this.charMap = charMap;
        this.parentMap = parentMap;
        this.getChar = function(params) {
            if(params.ctrlKey) {
                return null;
            }

            var keyChar = this.charMap[params.keyCode];

            if(keyChar === undefined) {
                if(this.parentMap !== undefined) {
                    return this.parentMap.getChar(params);
                }
            }
            else {
                var char;

                if(params.altKey && params.shiftKey) {
                    char = keyChar.altShiftValue;
                }
                else if(params.altKey) {
                    char = keyChar.altValue;
                }
                else if(params.shiftKey) {
                    char = keyChar.shiftValue;
                }
                else {
                    char = keyChar.value;
                }

                if(char === undefined) {
                    if(this.parentMap !== undefined) {
                        return this.parentMap.getChar(params);
                    }
                }
                else {
                    return char;
                }
            }
        };
    },
    charmaps: {},
    addCharmap: function(mapCode, charMap, parentCode) {
        $dal.charmaps[mapCode] = new $dal.charmap(charMap, $dal.charmaps[parentCode]);
    },
    checkCharmap: function(mapCode) {
        return mapCode !== undefined && $dal.charmaps[mapCode] !== undefined;
    },
    getLocalizedChar: function(params, mapCode) {
        return $dal.charmaps[mapCode].getChar(params);
    },
    applyChar: function(inputElem, char) {
        var start = inputElem.prop("selectionStart");
        var end = inputElem.prop("selectionEnd");
        var text = inputElem.val();
        var before = text.substring(0, start);
        var after  = text.substring(end, text.length);
        inputElem.val(before + char + after);
        inputElem.prop("selectionStart", start + 1);
        inputElem.prop("selectionEnd", start + 1);
    }
};

$dal.addCharmap("en", {
    192: new $dal.char('`', '~'),
    49: new $dal.char('1', '!'),
    50: new $dal.char('2', '@'),
    51: new $dal.char('3', '#'),
    52: new $dal.char('4', '$'),
    53: new $dal.char('5', '%'),
    54: new $dal.char('6', '^'),
    55: new $dal.char('7', '&'),
    56: new $dal.char('8', '*'),
    57: new $dal.char('9', '('),
    48: new $dal.char('0', ')'),
    189: new $dal.char('-', '_'),
    187: new $dal.char('=', '+'),
    81: new $dal.char('q', 'Q'),
    87: new $dal.char('w', 'W'),
    69: new $dal.char('e', 'E'),
    82: new $dal.char('r', 'R'),
    84: new $dal.char('t', 'T'),
    89: new $dal.char('y', 'Y'),
    85: new $dal.char('u', 'U'),
    73: new $dal.char('i', 'I'),
    79: new $dal.char('o', 'O'),
    80: new $dal.char('p', 'P'),
    219: new $dal.char('[', '{'),
    221: new $dal.char(']', '}'),
    226: new $dal.char('\\', '|'),
    65: new $dal.char('a', 'A'),
    83: new $dal.char('s', 'S'),
    68: new $dal.char('d', 'D'),
    70: new $dal.char('f', 'F'),
    71: new $dal.char('g', 'G'),
    72: new $dal.char('h', 'H'),
    74: new $dal.char('j', 'J'),
    75: new $dal.char('k', 'K'),
    76: new $dal.char('l', 'L'),
    186: new $dal.char(';', ':'),
    222: new $dal.char('\'', '"'),
    220: new $dal.char('\\', '|'),
    90: new $dal.char('z', 'Z'),
    88: new $dal.char('x', 'X'),
    67: new $dal.char('c', 'C'),
    86: new $dal.char('v', 'V'),
    66: new $dal.char('b', 'B'),
    78: new $dal.char('n', 'N'),
    77: new $dal.char('m', 'M'),
    188: new $dal.char(',', '<'),
    190: new $dal.char('.', '>'),
    191: new $dal.char('/', '?')
});

$dal.addCharmap("de", {
    192: new $dal.char(null, '°'),
    50: new $dal.char({shift: '"', alt: '²'}),
    51: new $dal.char({shift: '§', alt: '³'}),
    54: new $dal.char({shift: '&'}),
    55: new $dal.char({shift: '/', alt: '{'}),
    56: new $dal.char({shift: '(', alt: '['}),
    57: new $dal.char({shift: ')', alt: ']'}),
    48: new $dal.char({shift: '=', alt: '}'}),
    189: new $dal.char('ß', '?', '\\'),
    187: new $dal.char(null, null),
    81: new $dal.char({alt: '@'}),
    69: new $dal.char({alt: '€'}),
    89: new $dal.char('z', 'Z'),
    219: new $dal.char('ü', 'Ü'),
    221: new $dal.char('+', '*', '~'),
    226: new $dal.char('<', '>', '|'),
    186: new $dal.char('ö', 'Ö'),
    222: new $dal.char('ä', 'Ä'),
    220: new $dal.char('#', '\''),
    90: new $dal.char('y', 'Y'),
    77: new $dal.char({alt: 'µ'}),
    188: new $dal.char({shift: ';'}),
    190: new $dal.char({shift: ':'}),
    191: new $dal.char('-', '_')
}, "en");

$dal.addCharmap("ru", {
    192: new $dal.char('ё', 'Ё'),
    50: new $dal.char({shift: '"'}),
    51: new $dal.char({shift:  '№'}),
    52: new $dal.char({shift: ';'}),
    54: new $dal.char({shift: ':'}),
    55: new $dal.char({shift:  '?'}),
    81: new $dal.char('й', 'Й'),
    87: new $dal.char('ц', 'Ц'),
    69: new $dal.char('у', 'У'),
    82: new $dal.char('к', 'К'),
    84: new $dal.char('е', 'Е'),
    89: new $dal.char('н', 'Н'),
    85: new $dal.char('г', 'Г'),
    73: new $dal.char('ш', 'Ш'),
    79: new $dal.char('щ', 'Щ'),
    80: new $dal.char('з', 'З'),
    219: new $dal.char('х', 'Х'),
    221: new $dal.char('ъ', 'Ъ'),
    226: new $dal.char('\\', '/'),
    65: new $dal.char('ф', 'Ф'),
    83: new $dal.char('ы', 'Ы'),
    68: new $dal.char('в', 'В'),
    70: new $dal.char('а', 'А'),
    71: new $dal.char('п', 'П'),
    72: new $dal.char('р', 'Р'),
    74: new $dal.char('о', 'О'),
    75: new $dal.char('л', 'Л'),
    76: new $dal.char('д', 'Д'),
    186: new $dal.char('ж', 'Ж'),
    222: new $dal.char('э', 'Э'),
    220: new $dal.char('\\', '/'),
    90: new $dal.char('я', 'Я'),
    88: new $dal.char('ч', 'Ч'),
    67: new $dal.char('с', 'С'),
    86: new $dal.char('м', 'М'),
    66: new $dal.char('и', 'И'),
    78: new $dal.char('т', 'Т'),
    77: new $dal.char('ь', 'Ь'),
    188: new $dal.char('б', 'Б'),
    190: new $dal.char('ю', 'Ю'),
    191: new $dal.char('.', ',')
}, "en");

$dal.addCharmap("uk", {
    192: new $dal.char('\'', '₴'),
    85: new $dal.char('г', 'Г', 'ґ', 'Ґ'),
    221: new $dal.char('ї', 'Ї'),
    226: new $dal.char('ґ', 'Ґ'),
    83: new $dal.char('і', 'І'),
    222: new $dal.char('є', 'Є')
}, "ru");

$(document).ready(function() {
    $(document).on("keydown" , "#text-input, #word-input", function(e) {
        var el = $(this);
        var lang = el.attr('lang');

        if(!$dal.checkCharmap(lang))
            return;

        var char = $dal.getLocalizedChar(e, lang);

        if(char !== undefined) {
            e.preventDefault();
            if(char !== null) {
                $dal.applyChar(el, char);
            } 
        }
    });
});