<|start_filename|>resources/[scripts]/[Jobs]/[SHERIFF]/rs9000/__resource.lua<|end_filename|>
resource_manifest_version '44febabe-d386-4d18-afbe-5e627f4af937'

ui_page 'nui/index.html'

files {
	'config.json',
	'nui/index.html',
	'nui/index.js',
	'nui/style.css',
	'nui/d-din.otf',
	'nui/dseg7.ttf',
	'Newtonsoft.Json.dll',
}

client_script 'RS9000.net.dll'

<|start_filename|>resources/[scripts]/[Jobs]/[SHERIFF]/rs9000/nui/index.js<|end_filename|>
const elements = {
    radar: document.getElementById('radar'),
    control: document.getElementById('control'),
    speed: document.getElementById('patrol-speed'),
    antennas: {
        front: {
            speed: document.getElementById('front-speed'),
            fast: document.getElementById('front-fast'),
            lamps: {
                same: document.getElementById('front-same'),
                opp: document.getElementById('front-opp'),
                xmit: document.getElementById('front-xmit'),
                fwd: document.getElementById('front-fwd'),
                bwd: document.getElementById('front-bwd'),
            },
        },
        rear: {
            speed: document.getElementById('rear-speed'),
            fast: document.getElementById('rear-fast'),
            lamps: {
                same: document.getElementById('rear-same'),
                opp: document.getElementById('rear-opp'),
                xmit: document.getElementById('rear-xmit'),
                fwd: document.getElementById('rear-fwd'),
                bwd: document.getElementById('rear-bwd'),
            },
        },
    },
    plateDisplay: document.getElementById('plates'),
    plateReader: {
        front: {
            plate: document.getElementById('front-plate'),
            lamps: {
                power: document.getElementById('front-plate-power'),
                lock: document.getElementById('front-plate-lock'),
            },
        },
        rear: {
            plate: document.getElementById('rear-plate'),
            lamps: {
                power: document.getElementById('rear-plate-power'),
                lock: document.getElementById('rear-plate-lock'),
            },
        },
    },
};

const controls = {
    radarPower: document.getElementById('radar-power'),
    radarDisplay: document.getElementById('radar-display'),
    radarBeep: document.getElementById('radar-beep'),
    antennas: {
        front: {
            power: document.getElementById('front-power'),
            mode: document.getElementById('front-mode'),
        },
        rear: {
            power: document.getElementById('rear-power'),
            mode: document.getElementById('rear-mode'),
        },
    },
};

var settings = {
    resourceName: 'rs9000',
    plateReader: false,
}

const messageTypes = {
    initialize: 0,
    heartbeat: 1,
    switchMode: 2,
    displayRadar: 3,
    displayControl: 4,
    radarPower: 5,
    antennaPower: 6,
    radarBeep: 7,
    targetLock: 8,
};

const antennaModes = {
    same: 0,
    opp: 1,
};

const targetDirections = {
    none: 0,
    coming: 1,
    going: 2,
};

function clearDisplays(value, antennas) {
    if (value === undefined) {
        value = '';
    }
    elements.speed.innerText = formatDisplay(value);
    for (let antenna in elements.antennas) {
        if (antennas != undefined && !antennas.includes(antenna)) {
            continue;
        }
        elements.antennas[antenna].speed.innerText = formatDisplay(value);
        elements.antennas[antenna].fast.innerText = formatDisplay(value);
        if (settings.plateReader) {
            elements.plateReader[antenna].plate.innerText = '';
        }
    }
}

function clearLamps(antennaNames) {
    let antennas = elements.antennas;
    for (let antenna in antennas) {
        if (antennaNames != undefined && !antennaNames.includes(antenna)) {
            continue;
        }

        for (let lamp in antennas[antenna].lamps) {
            antennas[antenna].lamps[lamp].classList.remove('lit');
        }

        if (settings.plateReader) {
            for (let lamp in elements.plateReader.lamps) {
                elements.plateReader.lamps[lamp].classList.remove('lit');
            }
        }
    }
}

function setPower(powered) {
    setButtonLamp(controls.radarPower, powered);
    clearDisplays(powered ? 0 : '');
    if (!powered) {
        clearLamps();
    }
}

function setAntennaPower(antennaName, powered, mode) {
    clearLamps([antennaName]);
    setLamp(elements.antennas, antennaName, 'xmit', powered);
    if (settings.plateReader) {
        setLamp(elements.plateReader, antennaName, 'power', powered);
    }
    if (powered) {
        switchMode(antennaName, mode);
    }
    clearDisplays(powered ? 0 : '', [antennaName]);

    setButtonLamp(controls.antennas[antennaName].power, powered);
}

function setAntennaDirection(antennas) {
    for (let i in antennas) {
        let a = antennas[i];
        setLamp(elements.antennas, a.name, 'fwd', a.dir == targetDirections.going);
        setLamp(elements.antennas, a.name, 'bwd', a.dir == targetDirections.coming);
    }
}

function init(data) {
    if (data.resourceName !== undefined) {
        settings.resourceName = data.resourceName;
    }
    settings.plateReader = data.plateReader === undefined ? false : data.plateReader;
}

function formatDisplay(n) {
    // NOTE: the 7-segment font we're using uses '!' as the empty digit character
    var v = n.toString();
    var s = '';
    for (var i = 0; i < 3 - v.length; i++) {
        s += '!';
    }
    return s + v;
}

function setDisplays(speed, antennas) {
    elements.speed.innerText = formatDisplay(speed);
    antennas.forEach(function (a) {
        var els = elements.antennas[a.name];
        if (els != null) {
            els.speed.innerText = formatDisplay(a.speed);
            els.fast.innerText = formatDisplay(a.fast);
        }
    });
}

function heartbeat(data) {
    setDisplays(data.speed, data.antennas);
    setAntennaDirection(data.antennas);
}

function switchMode(antennaName, mode) {
    setLamp(elements.antennas, antennaName, 'same', mode == antennaModes.same);
    setLamp(elements.antennas, antennaName, 'opp', mode == antennaModes.opp);
}

function setButtonLamp(el, enabled) {
    if (enabled) {
        el.classList.add('lit');
    } else {
        el.classList.remove('lit');
    }
}

function setLamp(antennas, antennaName, lampName, enabled) {
    var el = antennas[antennaName].lamps[lampName];
    if (el == null) {
        return;
    }
    if (enabled) {
        el.classList.add('lit');
    } else {
        el.classList.remove('lit');
    }
}

function setPlateDisplay(antennaName, plate) {
    let value = plate;
    if (value === undefined) {
        value = '';
    }
    elements.plateReader[antennaName].plate.innerText = value;
}

function setDisplay(el, display) {
    if (el == null) {
        return;
    }
    if (display) {
        el.style.display = 'block';
    } else {
        el.style.display = 'none';
    }
}

function sendMessage(action, data) {
    let body = null;
    if (data != null) {
        body = { data: data };
    }
    $.post('http://' + settings.resourceName + '/' + action, JSON.stringify(body), function (response) {
        console.log(response);
    });
}

window.addEventListener('message', function(e) {
    var item = e.data;

    switch (item.type) {
        case messageTypes.initialize:
            init(item.data);
            break;
        case messageTypes.heartbeat:
            heartbeat(item.data);
            break;
        case messageTypes.switchMode:
            switchMode(item.data.name, item.data.mode);
            break;
        case messageTypes.displayRadar:
            setDisplay(elements.radar, item.data);
            if (settings.plateReader) {
                setDisplay(elements.plateDisplay, item.data)
            }
            break;
        case messageTypes.displayControl:
            setDisplay(elements.control, item.data);
            break;
        case messageTypes.radarPower:
            setPower(item.data);
            break;
        case messageTypes.antennaPower:
            setAntennaPower(item.data.name, item.data.powered, item.data.mode);
            break;
        case messageTypes.radarBeep:
            setButtonLamp(controls.radarBeep, item.data);
            break;
        case messageTypes.targetLock:
            if (settings.plateReader) {
                setLamp(elements.plateReader, item.data.name, 'lock', item.data.locked);
                setPlateDisplay(item.data.name, item.data.plate);
            }
            if (!item.data.locked) {
                clearDisplays(0, [item.data.name]);
            }
            break;
    }
});

const buttons = document.getElementsByTagName('button');

for (var i = 0; i < buttons.length; i++) {
    let event = buttons[i].getAttribute('data-event');
    if (event === undefined || event == '') {
        continue;
    }
    buttons[i].onclick = function () {
        sendMessage(this.getAttribute('data-event'), this.getAttribute('data-value'));
    }
}

document.onkeyup = function (e) {
    if (e.keyCode == 27) { // Esc
        sendMessage('close', null);
    }
}

<|start_filename|>resources/[scripts]/[Jobs]/[SHERIFF]/rs9000/nui/style.css<|end_filename|>
@font-face {
  font-family: 'DSEG7Classic';
  src: url('dseg7.ttf');
}

@font-face {
  font-family: 'D-DIN Bold';
  src: url('d-din.otf');
}

html, body {
  margin: 0;
  padding: 0;
  font-family: 'D-DIN Bold';
}

#radar {
  width: 390px;
  height: 155px;
  position: absolute;
  bottom: 10px;
  right: 10px;
  background-color: #0a0a0a;
  border: 5px solid #000;
  border-radius: 10px;
}

.primary {
  display: inline-block;
}

.patrol {
  display: inline-block;
  position: absolute;
  margin-top: 30px;
  margin-left: 10px;
}

.label {
  letter-spacing: 0.05em;
  display: block;
  color: #c0c0c0;
  font-weight: bold;
  font-size: 10pt;
  text-transform: uppercase;
  text-align: center;
}

.label.logo {
  font-size: 14pt;
}

.label.dim {
  color: #666;
}

#radar .front .lamps, #radar .front .arrows {
  margin-top: 15px;
}

#radar .rear .lamps, #radar .rear .arrows {
  margin-bottom: 15px;
}

.lamps {
  list-style-type: none;
  padding: 0;
  margin: 0;
  color: #600;
  line-height: 15px;
  font-weight: bold;
  font-size: 8pt;
  text-transform: uppercase;
  text-align: right;
}

.lit {
  color: #e00;
}

.display {
  display: inline-block;
  width: 2.5em;
  height: 1.2em;
  font-size: 28pt;
  line-height: 45px;
  margin: 2px;
  font-family: 'DSEG7Classic';
  border: 3px solid #000;
}

.display::after {
  position: absolute;
  content: '888';
}

.display span {
  z-index: 100;
  position: absolute;
}

.display.orange span { color: #f79e00; }
.display.orange { color: #592c00; background-color: #3d1c00; }

.display.red span { color: #ff0000; }
.display.red { color: #350000; background-color: #230303; }

.display.green span { color: #18f400; }
.display.green { color: #0e3800; background-color: #072600; }

.display.plate {
  display: block;
  width: 6em;
  height: 1.2em;
  line-height: 1.1em;
  font-size: 16pt;
  border: 2px solid #000;
  font-family: Consolas;
  letter-spacing: 0.1em;
}

.display.plate::after {
  position: absolute;
  content: '';
}

.display.plate span {
  margin-left: 0.4em;
}

.arrows {
  width: 15px;
}

.arrow {
  font-size: 14pt;
  color: #500;
  display: block;
}

.arrow.lit {
  color: #e00;
}

#control {
  padding: 10px;
  border: 3px solid #000;
  border-radius: 10px;
  background-color: #0a0a0a;
  text-transform: uppercase;
  position: absolute;
  right: 10px;
  bottom: 185px;
}

#control .btn-grid button.lit {
    background-color: #620000;
    color: #ff0000;
}

#control .btn-grid td {
  width: 50px;
  height: 50px;
}

#control .btn-grid label {
  color: #aaa;
}

#control .btn-grid .btn-red {
  color: #530000;
  background-color: #180000;
}

#control button {
  width: 50px;
  height: 50px;
  border: 2px solid #000;
  text-align: center;
  font-family: 'D-DIN Bold';
  font-weight: bold;
  font-size: 9pt;
  text-transform: uppercase;
  background-color: #111;
  color: #aaa;
  overflow-wrap: normal;
}

#control button:focus {
    border: 2px solid #000;
}

#control .btn-block {
    display: block;
    width: 100%;
}

#control .btn-sm {
    height: 25px;
}

#plates {
  width: 180px;
  height: 100px;
  position: absolute;
  bottom: 10px;
  right: 420px;
  background-color: #0a0a0a;
  border: 5px solid #000;
  border-radius: 10px;
  z-index: -1;
}

#plates .label {
  margin-left: 0.25em;
  line-height: 1.5em;
}

#plates .lamps li {
  width: 3em;
}