First Typing Practice
#controllers/index.py
import config
from bottle import Bottle, template, static_file

class Index(Bottle):
    def __init__(self):
        super().__init__()
        self.route('/static/images/<filename>', callback=self.loadImage)
        self.route('/static/styles/<filename>', callback=self.loadStyle)
        self.route('/static/scripts/<filename>', callback=self.loadScript)
        self.route('/static/fonts/<filename>', callback=self.loadFont)

        self.route('/', callback=self.index)
        self.route('/lesson/<id:int>', callback=self.lesson)
        self.route('/practice/<id:int>', callback=self.practice)

    def loadImage(self, filename):
        return static_file(filename, root='./public/images')

    def loadStyle(self, filename):
        return static_file(filename, root='./public/css')

    def loadScript(self, filename):
        return static_file(filename, root='./public/js')

    def loadFont(self, filename):
        return static_file(filename, root='./public/fonts')

    def index(self):
        config.kdict['blogTitle'] = "រៀន​វាយ​អក្សរ​ខ្មែរ"
        return template('index', data=config.kdict)

    def lesson(self, id):
        config.kdict['blogTitle'] = 'មេរៀន​ទី '+config.kdict['KhmerNumber'][id]
        return template('lessons/lesson'+str(id), data=config.kdict)

    def practice(self, id):
        config.kdict['blogTitle'] = 'លំហាត់ទី '+config.kdict['KhmerNumber'][id]
        return template('practices/practice'+str(id), data=config.kdict)
//public/js/practice1.js
class Typing{
  constructor(){
    $(".keyboard-base .key").on({
      keypress: function(event){
        if(event.which != 32)
          var key = String.fromCharCode(event.which).toUpperCase();
        else if(event.which == 32)
          var key = "Space";
        
        typing.checkKey(key)
      } 
    });

    this.letters = [['A','កូន​ដៃ','ា'],['S','នាង​ដៃ','ស'],['D','ចង្អុលកណ្តាល','ដ'],['F','ចង្អុលដៃ','ថ'],['G','ចង្អុលដៃ','ង'],
    ['H','ចង្អុល​ដៃ​ស្តាំ','ហ'],['J','ចង្អុល​ដៃ​ស្តាំ','្'],['K','ចង្អុលកណ្តាល','ក'],['L','នាងដៃ','ល'],[';','កូនដៃ','ើ'],
    ["'",'កូនដៃ','់'],['Space','មេដៃស្តាំ','']];
    
    this.counter = Math.floor(Math.random() * (this.letters).length);
    
    this.nextKey = this.letters[this.counter][0];
    this.setColor(this.nextKey);
  }

  setColor(nextKey){
    var keys = $(".keyboard-base").children();
    for(var index in keys){
      if(keys[index].innerHTML == nextKey){
        keys[index].focus();
        $(keys[index]).css({'color':'teal'});
      }

      if(this.pressedKey && (keys[index].innerHTML == this.pressedKey)){
        $(keys[index]).css({'color':'black'});
      }
    }
    
    $('#letter').html(this.letters[this.counter][2]);
  }

  checkKey(key){
    if(key == this.nextKey){
      this.counter = Math.floor(Math.random() * (this.letters).length);

      this.pressedKey = this.nextKey;
      this.nextKey = this.letters[this.counter][0];
      this.setColor(this.nextKey);
    }
  }
}//end of class
  

GitHub: https://khmerweb-typing.herokuapp.com
Heroku: https://khmerweb-typing.herokuapp.com/