препиши на луа 
angular.module('beamng.apps')
.directive('classicSpeedo', [function () {
  return {
    template: '<div style="overflow: hidden;"><canvas id="cSpeedo" height="90px"></canvas></div>',
    replace: true,
    restrict: 'EA',
    link: function (scope, element, attrs) {

        var streams = ['electrics'];
        StreamsManager.add(streams);
        scope.$on('$destroy', function () {
            StreamsManager.remove(streams);
        });

        var canvas = element.find("canvas")[0];
        scope.currentTheme = getTheme();
        var values = [];

        scope.$on('streamsUpdate', function (event, streams) {

            canvas.width = element[0].clientWidth;
            canvas.height = canvas.width * 3.3333;

            var w = canvas.width;
            var h = canvas.height;
            var scale = w / 200;

            var speedMs = 0;
            try {
                speedMs = streams.electrics.wheelspeed;
            } catch(e) {}
            if (isNaN(speedMs)) speedMs = streams.electrics.airspeed;
            var speedConverted = UiUnits.speed(speedMs);
            if(speedConverted === null) return;
            var speedUnits = Math.round(speedConverted.val);

            var speedStart;
            //for resetting scale >160
            if (speedUnits > 160) {
                speedStart = 160;
            } else {
                speedStart = 0;
            }

            var ctx = canvas.getContext('2d');

            ctx.scale(scale, scale);

            ctx.clearRect(0,0,200,65);
            ctx.fillStyle = scope.currentTheme.background;
            ctx.fillRect(0, 0, 200, 65);


            if (speedStart === 0){
                ctx.fillStyle = scope.currentTheme.speed1;
            } else {
                ctx.fillStyle = scope.currentTheme.speed2;
            }

            ctx.fillRect(20,10,Math.min(speedUnits-speedStart, 160),25);


            ctx.fillStyle = scope.currentTheme.text;
            //-Numbers
            ctx.font='8px Arial';
            ctx.textAlign="center";

            var interval = 20;
            for (var x=0; x<=160; x+=interval) {
                ctx.fillText(speedStart+x,x+20,48);
            }

            ctx.font='20px Arial';
            ctx.fillText(pad(speedUnits, 3), 100, 30);

            ctx.font='10px Arial';
            ctx.fillText("Speed (" + UiUnits.speed().unit.toUpperCase() + ")", 100, 62);

            //Add Graduations
            //20px/20unit intervals
            ctx.strokeStyle = scope.currentTheme.border;
            for (var x1=20; x1<=180; x1+=interval) {
                ctx.beginPath();
                ctx.moveTo(x1, 35);
                ctx.lineTo(x1, 40);
                ctx.stroke();
            }
            //and 10px/10unit intervals
            for (var x2=30; x2<=180; x2+=interval) {
                ctx.beginPath();
                ctx.moveTo(x2, 35);
                ctx.lineTo(x2, 38);
                ctx.stroke();
            }

            //add border
            ctx.strokeStyle = scope.currentTheme.border;
            ctx.lineWidth = 1;
            ctx.strokeRect(20,10,160,25);
        });

        scope.$on('ClassicUI', function (event, state) {
            if(state == "updateTheme") {
                scope.currentTheme = getTheme();
            }
        });

        function getTheme() {
            var themeStyles = parseInt(getDefaultValue("themeMode", 0));
            var tmpTheme;
            if(themeStyles == 0) {
                tmpTheme = {
                    background: 'rgba(255, 255, 255, 0.9)',
                    text: 'rgba(0, 0, 0, 0.9)',
                    border: 'rgba(0, 0, 0, 0.9)',

                    speed1: 'rgba(0, 0, 255, 0.5)',
                    speed2: 'rgba(255, 0, 255, 0.5)',
                };
            } 
            if(themeStyles == 1) {
                tmpTheme = {
                    background: 'rgba(0, 0, 0, 0.9)',
                    text: 'rgba(255, 255, 255, 0.9)',
                    border: 'rgb(160, 160, 160)',

                    speed1: 'rgba(80, 80, 255, 0.5)',
                    speed2: 'rgba(255, 80, 255, 0.5)',
                };
            }
            if(themeStyles == 2) {
                tmpTheme = {
                    background: 'rgba(0, 0, 0, 0.43)',
                    text: 'rgba(255, 255, 255, 0.9)',
                    border: 'rgb(80, 80, 80)',

                    speed1: 'rgba(80, 80, 255, 0.5)',
                    speed2: 'rgba(255, 80, 255, 0.5)',
                };
            }
            return tmpTheme;
        }

        function pad(num, size) {
            var s = num+"";
            while (s.length < size) s = "0" + s;
            return s;
        }

        function getDefaultValue(type, defVal) {
            return localStorage.getItem("classicUI_" + type) || defVal;
        }
    }
  };
}]);