Scripts/resources/[standalone]/wk_wars2x/nui/radar.js

1167 lines
32 KiB
JavaScript
Raw Permalink Normal View History

2024-12-29 20:02:43 +00:00
/*-----------------------------------------------------------------------------------------
Wraith ARS 2X
Created by WolfKnight
For discussions, information on future updates, and more, join
my Discord: https://discord.gg/fD4e6WD
MIT License
Copyright (c) 2020-2021 WolfKnight
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-----------------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------------
Variables
------------------------------------------------------------------------------------*/
var uiEdited = false;
// All of the audio file names
const audioNames =
{
// Beeps
beep: "beep.ogg",
xmit_on: "xmit_on.ogg",
xmit_off: "xmit_off.ogg",
done: "done.ogg",
// Verbal lock
front: "front.ogg",
rear: "rear.ogg",
closing: "closing.ogg",
away: "away.ogg",
// Plate reader
plate_hit: "plate_hit.ogg",
// Hmm
speed_alert: "speed_alert.ogg"
}
// Defines which audio needs to play for which direction
const lockAudio =
{
front: {
1: "away",
2: "closing"
},
rear: {
1: "closing",
2: "away"
}
}
// Setup the main const element structure, this way we can easily access elements without having the mess
// that was in the JS file for WraithRS
const elements =
{
radar: $( "#radarFrame" ),
remote: $( "#rc" ),
plateReader: $( "#plateReaderFrame" ),
pwrBtn: $( "#pwrBtn" ),
uiSettingsBtn: $( "#uiSettings" ),
uiSettingsBox: $( "#uiSettingsBox" ),
closeUiBtn: $( "#closeUiSettings" ),
plateReaderBtn: $( "#plateReaderBtn" ),
plateReaderBox: $( "#plateReaderBox" ),
boloText: $( "#boloText" ),
setBoloBtn: $( "#setBoloPlate" ),
clearBoloBtn: $( "#clearBoloPlate" ),
closePrBtn: $( "#closePlateReaderSettings" ),
openHelp: $( "#helpBtn" ),
helpWindow: $( "#helpWindow" ),
helpWeb: $( "#helpWeb" ),
closeHelp: $( "#closeHelp" ),
closeNewUser: $( "#closeNewUserMsg" ),
newUser: $( "#newUser" ),
openQsv: $( "#showQuickStartVideo" ),
qsvWindow: $( "#quickStart" ),
qsvWeb: $( "#quickStartVideo" ),
closeQsv: $( "#closeQuickStart" ),
radarScaling: {
increase: $( "#radarIncreaseScale" ),
decrease: $( "#radarDecreaseScale" ),
display: $( "#radarScaleDisplay" )
},
remoteScaling: {
increase: $( "#remoteIncreaseScale" ),
decrease: $( "#remoteDecreaseScale" ),
display: $( "#remoteScaleDisplay" )
},
plateReaderScaling: {
increase: $( "#readerIncreaseScale" ),
decrease: $( "#readerDecreaseScale" ),
display: $( "#readerScaleDisplay" )
},
plates: {
front: {
plate: $( "#frontPlate" ),
text: $( "#frontPlateText" ),
fill: $( "#frontPlateTextFill" ),
lolite: $( "#frontPlateTextLolite" ),
img: $( "#frontPlateImg" ),
lock: $( "#frontPlateLock" )
},
rear: {
plate: $( "#rearPlate" ),
text: $( "#rearPlateText" ),
fill: $( "#rearPlateTextFill" ),
lolite: $( "#rearPlateTextLolite" ),
img: $( "#rearPlateImg" ),
lock: $( "#rearPlateLock" )
}
},
safezoneSlider: $( "#safezone" ),
safezoneDisplay: $( "#safezoneDisplay" ),
keyLock: {
label: $( "#keyLockLabel" ),
stateLabel: $( "#keyLockStateLabel" )
},
patrolSpeed: $( "#patrolSpeed" ),
antennas: {
front: {
targetSpeed: $( "#frontSpeed" ),
fastSpeed: $( "#frontFastSpeed" ),
dirs: {
fwd: $( "#frontDirAway" ),
bwd: $( "#frontDirTowards" ),
fwdFast: $( "#frontFastDirAway" ),
bwdFast: $( "#frontFastDirTowards" )
},
modes: {
same: $( "#frontSame" ),
opp: $( "#frontOpp" ),
xmit: $( "#frontXmit" )
},
fast: {
fastLabel: $( "#frontFastLabel" ),
lockLabel: $( "#frontFastLockLabel" )
}
},
rear: {
targetSpeed: $( "#rearSpeed" ),
fastSpeed: $( "#rearFastSpeed" ),
dirs: {
fwd: $( "#rearDirTowards" ),
bwd: $( "#rearDirAway" ),
fwdFast: $( "#rearFastDirTowards" ),
bwdFast: $( "#rearFastDirAway" )
},
modes: {
same: $( "#rearSame" ),
opp: $( "#rearOpp" ),
xmit: $( "#rearXmit" )
},
fast: {
fastLabel: $( "#rearFastLabel" ),
lockLabel: $( "#rearFastLockLabel" )
}
}
}
}
// Antenna mode values
const modes =
{
off: 0,
same: 1,
opp: 2,
both: 3
}
// Antenna direction values
const dirs =
{
none: 0,
closing: 1,
away: 2
}
/*------------------------------------------------------------------------------------
Hide elements
------------------------------------------------------------------------------------*/
elements.radar.hide();
elements.remote.hide();
elements.plateReader.hide();
elements.plateReaderBox.hide();
elements.uiSettingsBox.hide();
elements.keyLock.label.hide();
elements.helpWindow.hide();
elements.qsvWindow.hide();
elements.newUser.hide();
// Sets the action for the "UI SETTINGS" button on the remote to open the UI settings box
elements.uiSettingsBtn.click( function() {
setEleVisible( elements.uiSettingsBox, true );
} )
// Sets the action for the "PLATE READER" button on the remote to open the plate reader box
elements.plateReaderBtn.click( function() {
setEleVisible( elements.plateReaderBox, true );
} )
// Sets the action for the "HELP" button on the remote to open the help window and load the web page
elements.openHelp.click( function() {
setEleVisible( elements.helpWindow, true );
loadHelp( true );
} )
// Sets the action for the "Close Help" button under the help window to close the help window and unload the web page
elements.closeHelp.click( function() {
setEleVisible( elements.helpWindow, false );
loadHelp( false );
} )
// Sets the action for the "No" button on the new user popup to close the popup
elements.closeNewUser.click( function() {
setEleVisible( elements.newUser, false );
sendData( "qsvWatched", {} );
} )
// Sets the action for the "Yes" button on the new user popup to open the quick start window and load the video
elements.openQsv.click( function() {
setEleVisible( elements.newUser, false );
setEleVisible( elements.qsvWindow, true );
loadQuickStartVideo( true );
} )
// Sets the action for the "Close Video" button under the quick start window to close the quick start window and unload the video
elements.closeQsv.click( function() {
setEleVisible( elements.qsvWindow, false );
loadQuickStartVideo( false );
sendData( "qsvWatched", {} );
} )
/*------------------------------------------------------------------------------------
Setters
------------------------------------------------------------------------------------*/
// Sets the visibility of an element to the given state
function setEleVisible( ele, state )
{
state ? ele.fadeIn() : ele.fadeOut();
}
// Changes the class of the given element so it looks lit up
function setLight( ant, cat, item, state )
{
// Grab the obj element from the elements table
let obj = elements.antennas[ant][cat][item];
// Either add the active class or remove it
if ( state ) {
obj.addClass( cat == "dirs" ? "active_arrow" : "active" );
} else {
obj.removeClass( cat == "dirs" ? "active_arrow" : "active" );
}
}
// Sets the XMIT state of an antenna based on the passed state, makes the fast box display "HLd"
// when the state is false
function setAntennaXmit( ant, state )
{
// Set the light state of the antenna's XMIT icon
setLight( ant, "modes", "xmit", state );
// Clear the antenna's directional arrows and speeds, display "HLd" in the fast box
if ( !state ) {
clearDirs( ant );
elements.antennas[ant].targetSpeed.html( "¦¦¦" );
elements.antennas[ant].fastSpeed.html( "HLd" );
// Blank the fast box when the antenna is set to transmit
} else {
elements.antennas[ant].fastSpeed.html( "¦¦¦" );
}
}
// Sets the mode lights for the given antenna
function setAntennaMode( ant, mode )
{
// Light up the 'same' led if the given mode is the same mode, otherwise blank it
setLight( ant, "modes", "same", mode == modes.same );
// Light up the 'opp' led if the given mode is the opp mode, otherwise blank it
setLight( ant, "modes", "opp", mode == modes.opp );
}
// Sets the fast light for the given antenna
function setAntennaFastMode( ant, state )
{
// Lighten or dull the fast led based on the given state
setLight( ant, "fast", "fastLabel", state );
}
// Sets the lock light for the given antenna
function setAntennaLock( ant, state )
{
// Lighten or dull the lock led based on the given state
setLight( ant, "fast", "lockLabel", state );
}
// Sets the directional arrows light for the given antenna
function setAntennaDirs( ant, dir, fastDir )
{
// Target forward
setLight( ant, "dirs", "fwd", dir == dirs.closing );
// Target backward
setLight( ant, "dirs", "bwd", dir == dirs.away );
// Fast forward
setLight( ant, "dirs", "fwdFast", fastDir == dirs.closing );
// Fast backward
setLight( ant, "dirs", "bwdFast", fastDir == dirs.away );
}
// sets the plate lock light for the given plate reader
function setPlateLock( cam, state, isBolo )
{
// Get the plate reader lock object
let obj = elements.plates[cam];
// Add or remove the active class
if ( state ) {
obj.lock.addClass( "active" );
// Only flash the plate if it was a BOLO plate
if ( isBolo ) {
// Make the hit plate flash for 3 seconds, acts as a visual aid
obj.plate.addClass( "plate_hit" );
setTimeout( function() {
obj.plate.removeClass( "plate_hit" );
}, 3000 );
}
} else {
obj.lock.removeClass( "active" );
}
}
// Sets the license text and plate image of the given plate reader
function setPlate( cam, plate, index )
{
// Get the plate items
let pl = elements.plates[cam];
// Change the plate image
pl.img.attr( "src", "images/plates/" + index + ".png" );
// Change the plate text colour depending on the plate itself
( index == 1 || index == 2 ) ? pl.fill.removeClass( "plate_blue" ).addClass( "plate_yellow" ) : pl.fill.removeClass( "plate_yellow" ).addClass( "plate_blue" );
// If the plate is black or blue then we hide the lolite effect
( index == 1 || index == 2 ) ? pl.lolite.hide() : pl.lolite.show();
// Update all of the p elements with the new plate
pl.text.find( "p" ).each( function( i, obj ) {
$( this ).html( plate );
} );
}
/*------------------------------------------------------------------------------------
Clearing functions
------------------------------------------------------------------------------------*/
// Clears the same, opp, fast, and lock leds for the given antenna
function clearModes( ant )
{
// Iterate through the modes and clear them
for ( let i in elements.antennas[ant].modes )
{
elements.antennas[ant].modes[i].removeClass( "active" );
}
// Iterate through the fast leds and clear them
for ( let a in elements.antennas[ant].fast )
{
elements.antennas[ant].fast[a].removeClass( "active" );
}
}
// Clears the directional arrows for the given antenna
function clearDirs( ant )
{
// Iterate through the directional arrows and clear them
for ( let i in elements.antennas[ant].dirs )
{
elements.antennas[ant].dirs[i].removeClass( "active_arrow" );
}
}
// Clears all of the elements of the given antenna
function clearAntenna( ant )
{
// Clear the modes
clearModes( ant );
// Clear the directional arrows
clearDirs( ant );
// Blank the target speed box
elements.antennas[ant].targetSpeed.html( "¦¦¦" );
// Blank the fast speed box
elements.antennas[ant].fastSpeed.html( "¦¦¦" );
}
// Clears all the elements on the radar's UI
function clearEverything()
{
// Blank the patrol speed
elements.patrolSpeed.html( "¦¦¦" );
// Blank both the antennas
for ( let i in elements.antennas )
{
clearAntenna( i );
}
}
/*------------------------------------------------------------------------------------
Radar power functions
------------------------------------------------------------------------------------*/
// Simulates the radar unit powering up by lighting all of the elements
function poweringUp()
{
// Set the patrol speed container to be fully lit
elements.patrolSpeed.html( "888" );
// Iterate through the front and rear antenna elements
for ( let i of [ "front", "rear" ] )
{
// Get the antenna object to shorten the target reference
let e = elements.antennas[i];
// Set the target and fast speed box to be fully lit
e.targetSpeed.html( "888" );
e.fastSpeed.html( "888" );
// Iterate through the rest of the antenna's elements
for ( let a of [ "dirs", "modes", "fast" ] )
{
// Iterate through the objects for the current category and add the active class
for ( let obj in e[a] )
{
a == "dirs" ? e[a][obj].addClass( "active_arrow" ) : e[a][obj].addClass( "active" );
}
}
}
}
// Simulates the 'fully powered' state of the radar unit
function poweredUp( fastDisplay )
{
// Completely clear everything
clearEverything();
// Activate the 'fast' led for both antennas, and make sure the xmit led is off
for ( let ant of [ "front", "rear" ] )
{
// Even though the clearEverything() function is called above, we run this so the fast window
// displays 'HLd'
setAntennaXmit( ant, false );
setAntennaFastMode( ant, fastDisplay );
}
}
// Runs the startup process or clears everything, the Lua side calls for the full powered up state
function radarPower( state, override, fastDisplay )
{
state ? ( override ? poweredUp( fastDisplay ) : poweringUp() ) : clearEverything();
}
/*------------------------------------------------------------------------------------
Audio
------------------------------------------------------------------------------------*/
// Plays the given audio file name from the audioNames list at the given volume
function playAudio( name, vol )
{
// Create the new audio object
let audio = new Audio( "sounds/" + audioNames[name] );
// Set the volume
audio.volume = vol;
// Play the audio clip
audio.play();
}
// Plays the verbal lock, this is a separate from the function above as it plays two sounds with a delay
function playLockAudio( ant, dir, vol )
{
// Play the front/rear sound
playAudio( ant, vol );
// If the vehicle was closing or away, play that sound too
if ( dir > 0 )
{
setTimeout( function() {
playAudio( lockAudio[ant][dir], vol );
}, 500 );
}
}
/*------------------------------------------------------------------------------------
Radar updating
------------------------------------------------------------------------------------*/
// Updates patrol speed as well as the speeds and directional arrows for the given antenna
function updateDisplays( ps, ants )
{
// Update the patrol speed
elements.patrolSpeed.html( ps );
// Iterate through the antenna data
for ( let ant in ants )
{
// Make sure there is actually data for the current antenna data
if ( ants[ant] != null ) {
// Grab the antenna element from the elements table
let e = elements.antennas[ant];
// Update the target and fast speeds
e.targetSpeed.html( ants[ant][0].speed );
e.fastSpeed.html( ants[ant][1].speed );
// Update the directional arrows
setAntennaDirs( ant, ants[ant][0].dir, ants[ant][1].dir );
}
}
}
// Updates all of the mode leds on the radar interface
function settingUpdate( ants )
{
for ( let ant in ants )
{
setAntennaXmit( ant, ants[ant].xmit );
setAntennaMode( ant, ants[ant].mode );
setAntennaFastMode( ant, ants[ant].fast );
setAntennaLock( ant, ants[ant].speedLocked );
}
}
/*------------------------------------------------------------------------------------
Misc
------------------------------------------------------------------------------------*/
// Displays the given option text and current option value on the radar
function menu( optionText, option )
{
// Clear everything
clearEverything();
// Set the target and fast box to the option text
elements.antennas.front.targetSpeed.html( optionText[0] );
elements.antennas.front.fastSpeed.html( optionText[1] );
// Set the patrol speed to the value
elements.patrolSpeed.html( option );
}
var keyLockTimeout;
// Makes the key lock label fade in then fade out after 2 seconds
function displayKeyLock( state )
{
let sl = elements.keyLock.stateLabel;
// Set the state label text to enabled or disabled
sl.html( state ? "blocked" : "enabled" );
// Change the colour of the altered text
state ? sl.addClass( "red" ).removeClass( "green" ) : sl.addClass( "green" ).removeClass( "red" );
// Fade in the label
elements.keyLock.label.fadeIn();
// Clear the timeout if it already exists
clearTimeout( keyLockTimeout );
// Make the label fade out after 2 seconds
keyLockTimeout = setTimeout( function() {
elements.keyLock.label.fadeOut();
}, 2000 );
}
// Prepare headers for HTTP requests
$.ajaxSetup({
headers: {
'Content-Type': 'application/json; charset=UTF-8',
},
});
// This function is used to send data back through to the LUA side
function sendData( name, data ) {
$.post( "https://wk_wars2x/" + name, JSON.stringify( data ), function( datab ) {
if ( datab != "ok" ) {
console.log( datab );
}
} );
}
// Sets the ui edited variable to the given state, this is used in the UI save system
function setUiHasBeenEdited( state )
{
uiEdited = state;
}
// Returns if the UI has been edited
function hasUiBeenEdited()
{
return uiEdited;
}
// Gathers the UI data and sends it to the Lua side
function sendSaveData()
{
// Make sure we only collect and send the UI data if it has been edited
if ( hasUiBeenEdited() ) {
let data =
{
remote: {
left: elements.remote.css( "left" ),
top: elements.remote.css( "top" ),
scale: remoteScale
},
radar: {
left: elements.radar.css( "left" ),
top: elements.radar.css( "top" ),
scale: radarScale
},
plateReader: {
left: elements.plateReader.css( "left" ),
top: elements.plateReader.css( "top" ),
scale: readerScale
},
safezone: safezone
}
// Send the data
sendData( "saveUiData", data );
}
}
// Loads the UI settings
function loadUiSettings( data, isSave )
{
// Iterate through "remote", "radar" and "plateReader"
for ( let setting of [ "remote", "radar", "plateReader" ] )
{
let ele = elements[setting];
if ( isSave ) {
// Iterate through the settings
for ( let i of [ "left", "top" ] )
{
// Update the position of the current element
ele.css( i, data[setting][i] );
}
// Set the scale and update the display
setScaleAndDisplay( ele, data[setting].scale, elements[setting + "Scaling"].display );
} else {
// Set the scale and update the display
setScaleAndDisplay( ele, data.scale[setting], elements[setting + "Scaling"].display );
// Get the scaled width and height of the current element
let w = ( ele.outerWidth() * data.scale[setting] );
let h = ( ele.outerHeight() * data.scale[setting] );
// The position of the element then needs to be updated.
switch ( setting ) {
case "remote":
ele.css( "left", "calc( 50% - " + w / 2 + "px )" );
ele.css( "top", "calc( 50% - " + h / 2 + "px )" );
break;
case "radar":
ele.css( "left", "calc( ( 100% - " + data.safezone + "px ) - " + w + "px )" );
ele.css( "top", "calc( ( 100% - " + data.safezone + "px ) - " + h + "px )" );
break;
case "plateReader":
ele.css( "left", "calc( ( 100% - " + data.safezone + "px ) - " + w + "px )" );
ele.css( "top", "calc( 50% - " + h / 2 + "px )" );
break;
default:
break;
}
}
}
// Update the remote, radar and reader scale variables
remoteScale = isSave ? data.remote.scale : data.scale.remote;
radarScale = isSave ? data.radar.scale : data.scale.radar;
readerScale = isSave ? data.plateReader.scale : data.scale.plateReader;
// Set the safezone and update the display
elements.safezoneSlider.val( data.safezone );
elements.safezoneSlider.trigger( "input" );
}
// Sets the on click function for the set BOLO plate button
elements.setBoloBtn.click( function() {
// Grab the value of the text input box
let plate = elements.boloText.val().toUpperCase();
// Gets the amount of whitespace there should be
let spaceAmount = 8 - plate.length;
if ( spaceAmount > 0 )
{
// Splits the amount in half
let split = spaceAmount / 2;
// Calculates how many whitespace characters there should be at the start and end of the string. As GTA
// formats a licence plate string by padding it, with the end of the string being biased compared to
// the start of the string.
let startSpace = Math.floor( split );
let endSpace = Math.ceil( split );
// Add the padding to the string
let text = plate.padStart( plate.length + startSpace );
text = text.padEnd( text.length + endSpace );
// Send the plate to the Lua side
sendData( "setBoloPlate", text );
} else {
sendData( "setBoloPlate", plate );
}
} )
// Sets the on click function for the clear BOLO button
elements.clearBoloBtn.click( function() {
sendData( "clearBoloPlate", null );
} )
// Checks what the user is typing into the plate box
function checkPlateInput( event )
{
// See if what has been typed is a valid key, GTA only seems to like A-Z and 0-9
let valid = /[a-zA-Z0-9 ]/g.test( event.key );
// If the key is not valid, prevent the key from being input into the box
if ( !valid ) {
event.preventDefault();
}
}
// Sets the src of the in-game help element, when true it loads the manual, when false it blanks the element
function loadHelp( state )
{
if ( state ) {
elements.helpWeb.attr( "src", "https://wolfknight98.github.io/wk_wars2x_web/manual.pdf" );
} else {
elements.helpWeb.attr( "src", "about:blank" );
}
}
function loadQuickStartVideo( state )
{
if ( state ) {
elements.qsvWeb.attr( "src", "https://www.youtube-nocookie.com/embed/B-6VD8pXNYE" );
} else {
elements.qsvWeb.attr( "src", "about:blank" );
}
}
/*------------------------------------------------------------------------------------
UI scaling and positioning
This whole bit could most likely be streamlined and made more efficient, it
works for now though. Redo it at a later date.
------------------------------------------------------------------------------------*/
var remoteScale = 1.0;
var remoteMoving = false;
var remoteOffset = [ 0, 0 ];
var radarScale = 1.0;
var radarMoving = false;
var radarOffset = [ 0, 0 ];
var readerScale = 1.0;
var readerMoving = false;
var readerOffset = [ 0, 0 ];
var windowWidth = 0;
var windowHeight = 0;
var safezone = 0;
// Close the UI settings window when the 'Close' button is pressed
elements.closeUiBtn.click( function() {
setEleVisible( elements.uiSettingsBox, false );
} )
// Close the plate reader settings window when the 'Close' button is pressed
elements.closePrBtn.click( function() {
setEleVisible( elements.plateReaderBox, false );
} )
// Set the remote scale buttons to change the remote's scale
elements.remoteScaling.increase.click( function() {
remoteScale = changeEleScale( elements.remote, remoteScale, 0.05, elements.remoteScaling.display );
} )
elements.remoteScaling.decrease.click( function() {
remoteScale = changeEleScale( elements.remote, remoteScale, -0.05, elements.remoteScaling.display );
} )
// Set the radar scale buttons to change the radar's scale
elements.radarScaling.increase.click( function() {
radarScale = changeEleScale( elements.radar, radarScale, 0.05, elements.radarScaling.display );
} )
elements.radarScaling.decrease.click( function() {
radarScale = changeEleScale( elements.radar, radarScale, -0.05, elements.radarScaling.display );
} )
// Set the reader scale buttons to change the reader's scale
elements.plateReaderScaling.increase.click( function() {
readerScale = changeEleScale( elements.plateReader, readerScale, 0.05, elements.plateReaderScaling.display );
} )
elements.plateReaderScaling.decrease.click( function() {
readerScale = changeEleScale( elements.plateReader, readerScale, -0.05, elements.plateReaderScaling.display );
} )
// Remote mouse down and up event
elements.remote.mousedown( function( event ) {
remoteMoving = true;
let offset = $( this ).offset();
remoteOffset = getOffset( offset, event.clientX, event.clientY );
} )
// Radar mouse down and up event
elements.radar.mousedown( function( event ) {
radarMoving = true;
let offset = $( this ).offset();
radarOffset = getOffset( offset, event.clientX, event.clientY );
} )
// Plate reader mouse down and up event
elements.plateReader.mousedown( function( event ) {
readerMoving = true;
let offset = $( this ).offset();
readerOffset = getOffset( offset, event.clientX, event.clientY );
} )
$( document ).mouseup( function( event ) {
// Reset the remote and radar moving variables
remoteMoving = false;
radarMoving = false;
readerMoving = false;
} )
$( document ).mousemove( function( event ) {
let x = event.clientX;
let y = event.clientY;
if ( remoteMoving )
{
event.preventDefault();
calculatePos( elements.remote, x, y, windowWidth, windowHeight, remoteOffset, remoteScale, safezone );
}
if ( radarMoving )
{
event.preventDefault();
calculatePos( elements.radar, x, y, windowWidth, windowHeight, radarOffset, radarScale, safezone );
}
if ( readerMoving )
{
event.preventDefault();
calculatePos( elements.plateReader, x, y, windowWidth, windowHeight, readerOffset, readerScale, safezone );
}
} )
$( window ).resize( function() {
windowWidth = $( this ).width();
windowHeight = $( this ).height();
} )
$( document ).ready( function() {
windowWidth = $( window ).width();
windowHeight = $( window ).height();
} )
elements.safezoneSlider.on( "input", function() {
let val = $( this ).val();
safezone = parseInt( val, 10 );
elements.safezoneDisplay.html( val + "px" );
} )
function calculatePos( ele, x, y, w, h, offset, scale, safezone )
{
let eleWidth = ( ele.outerWidth() * scale );
let eleHeight = ( ele.outerHeight() * scale );
let eleWidthPerct = ( eleWidth / w ) * 100;
let eleHeightPerct = ( eleHeight / h ) * 100;
let eleWidthPerctHalf = eleWidthPerct / 2;
let eleHeightPerctHalf = eleHeightPerct / 2;
let maxWidth = w - eleWidth;
let maxHeight = h - eleHeight;
let left = clamp( x + offset[0], 0 + safezone, maxWidth - safezone );
let top = clamp( y + offset[1], 0 + safezone, maxHeight - safezone );
let leftPos = ( left / w ) * 100;
let topPos = ( top / h ) * 100;
let leftLockGap = leftPos + eleWidthPerctHalf;
let topLockGap = topPos + eleHeightPerctHalf;
// Lock pos check
if ( leftLockGap >= 49.0 && leftLockGap <= 51.0 )
{
leftPos = 50.0 - eleWidthPerctHalf;
}
if ( topLockGap >= 49.0 && topLockGap <= 51.0 )
{
topPos = 50.0 - eleHeightPerctHalf;
}
updatePosition( ele, leftPos, topPos );
setUiHasBeenEdited( true );
}
function updatePosition( ele, left, top )
{
ele.css( "left", left + "%" );
ele.css( "top", top + "%" );
}
function getOffset( offset, x, y )
{
return [
offset.left - x,
offset.top - y
]
}
function changeEleScale( ele, scaleVar, amount, display )
{
// Change the scale of the element and update it's displayer
let scale = changeScale( ele, scaleVar, amount );
display.html( scale.toFixed( 2 ) + "x" );
// Tell the system the UI has been edited
setUiHasBeenEdited( true );
return scale;
}
function changeScale( ele, current, amount )
{
let scale = clamp( current + amount, 0.25, 2.5 );
ele.css( "transform", "scale(" + scale + ")" );
return scale;
}
function setScaleAndDisplay( ele, scale, display )
{
ele.css( "transform", "scale(" + scale + ")" );
display.html( scale.toFixed( 2 ) + "x" );
}
function clamp( num, min, max )
{
return num < min ? min : num > max ? max : num;
}
/*------------------------------------------------------------------------------------
Button click event assigning
------------------------------------------------------------------------------------*/
// This runs when the JS file is loaded, loops through all of the remote buttons and assigns them an onclick function
// elements.remote.find( "button" ).each( function( i, obj ) {
$( "body" ).find( "button, div" ).each( function( i, obj ) {
if ( $( this ).attr( "data-nuitype" ) ) {
$( this ).click( function() {
let type = $( this ).data( "nuitype" );
let value = $( this ).attr( "data-value" ) ? $( this ).data( "value" ) : null;
let mode = $( this ).attr( "data-mode" ) ? $( this ).data( "mode" ) : null;
sendData( type, { value, mode } );
} )
}
} );
/*------------------------------------------------------------------------------------
Close the remote when the user presses the 'Escape' key or the right mouse button
------------------------------------------------------------------------------------*/
function closeRemote()
{
sendData( "closeRemote", {} );
setEleVisible( elements.plateReaderBox, false );
setEleVisible( elements.uiSettingsBox, false );
setEleVisible( elements.helpWindow, false );
setEleVisible( elements.newUser, false );
setEleVisible( elements.qsvWindow, false );
loadHelp( false );
loadQuickStartVideo( false );
setEleVisible( elements.remote, false );
sendSaveData();
}
$( document ).keyup( function( event ) {
if ( event.keyCode == 27 )
{
closeRemote();
}
} );
$( document ).contextmenu( function() {
closeRemote();
} );
/*------------------------------------------------------------------------------------
The main event listener, this is where the NUI messages sent by the LUA side arrive
at, they are then handled properly via a switch/case that runs the relevant code
------------------------------------------------------------------------------------*/
window.addEventListener( "message", function( event ) {
var item = event.data;
var type = event.data._type;
switch ( type ) {
// System events
case "loadUiSettings":
loadUiSettings( item.data, true );
break;
case "setUiDefaults":
loadUiSettings( item.data, false );
break;
case "displayKeyLock":
displayKeyLock( item.state );
break;
case "showNewUser":
setEleVisible( elements.newUser, true );
break;
// Radar events
case "openRemote":
setEleVisible( elements.remote, true );
setUiHasBeenEdited( false );
break;
case "setRadarDisplayState":
setEleVisible( elements.radar, item.state );
break;
case "radarPower":
radarPower( item.state, item.override, item.fast );
break;
case "poweredUp":
poweredUp( item.fast );
break;
case "update":
updateDisplays( item.speed, item.antennas );
break;
case "antennaXmit":
setAntennaXmit( item.ant, item.on );
break;
case "antennaMode":
setAntennaMode( item.ant, item.mode );
break;
case "antennaLock":
setAntennaLock( item.ant, item.state );
break;
case "antennaFast":
setAntennaFastMode( item.ant, item.state );
break;
case "menu":
menu( item.text, item.option );
break;
case "settingUpdate":
settingUpdate( item.antennaData );
break;
// Plate reader events
case "setReaderDisplayState":
setEleVisible( elements.plateReader, item.state );
break;
case "changePlate":
setPlate( item.cam, item.plate, item.index );
break;
case "lockPlate":
setPlateLock( item.cam, item.state, item.isBolo );
break;
// Audio events
case "audio":
playAudio( item.name, item.vol );
break;
case "lockAudio":
playLockAudio( item.ant, item.dir, item.vol );
break;
// default
default:
break;
}
} );