@@ -1210,6 +1210,13 @@ function showContextMenu(event, mmsi, type, context) {
12101210 } ) ;
12111211 } ) ;
12121212
1213+ // Hide realtime menu items if realtime is disabled
1214+ if ( typeof realtime_enabled === "undefined" || realtime_enabled === false ) {
1215+ document . querySelectorAll ( '.ctx-realtime' ) . forEach ( ( element ) => {
1216+ element . style . display = "none" ;
1217+ } ) ;
1218+ }
1219+
12131220 // we might have made non-android items visible in the context menu, so hide non-android items if needed
12141221 updateAndroid ( ) ;
12151222 updateKiosk ( ) ;
@@ -2019,7 +2026,7 @@ function ToggleFireworks() {
20192026
20202027function StartFireworks ( ) {
20212028 if ( evtSourceMap == null ) {
2022- if ( typeof realtime_enabled === "undefined" || realtime_enabled === false ) {
2029+ if ( ! realtime_enabled ) {
20232030 showDialog ( "Error" , "Cannot run Firework Mode. Please ensure that AIS-catcher is running with -N REALTIME on." ) ;
20242031 return ;
20252032 }
@@ -2958,6 +2965,15 @@ async function fetchShips(noDoubleFetch = true) {
29582965 s . approximate = ( flags >> 5 ) & 1 ;
29592966 s . channels2 = ( flags >> 6 ) & 0b1111 ;
29602967 s . cs_unit = ( flags >> 10 ) & 3 ; // 0=unknown, 1=SOTDMA, 2=Carrier Sense
2968+ s . raim = ( flags >> 12 ) & 3 ; // 0=unknown, 1=false, 2=true
2969+ s . dte = ( flags >> 14 ) & 3 ; // 0=unknown, 1=ready, 2=not ready
2970+ s . assigned = ( flags >> 16 ) & 3 ; // 0=unknown, 1=autonomous, 2=assigned
2971+ s . display = ( flags >> 18 ) & 3 ; // 0=unknown, 1=false, 2=true
2972+ s . dsc = ( flags >> 20 ) & 3 ; // 0=unknown, 1=false, 2=true
2973+ s . band = ( flags >> 22 ) & 3 ; // 0=unknown, 1=false, 2=true
2974+ s . msg22 = ( flags >> 24 ) & 3 ; // 0=unknown, 1=false, 2=true
2975+ s . off_position = ( flags >> 26 ) & 3 ; // 0=unknown, 1=on position, 2=off position
2976+ s . maneuver = ( flags >> 28 ) & 3 ; // 0=not available, 1=no special, 2=special
29612977
29622978 // Check for discrepancies and show error
29632979 if ( s . validated !== s . validated2 ) {
@@ -5491,7 +5507,7 @@ function populateShipcard() {
54915507 document . getElementById ( "shipcard_sources" ) . innerHTML = getStringfromGroup ( ship . group_mask ) ;
54925508
54935509 document . getElementById ( "shipcard_channels" ) . innerHTML = getStringfromChannels ( ship . channels ) ;
5494- document . getElementById ( "shipcard_type" ) . innerHTML = getTypeVal ( ship ) ;
5510+ document . getElementById ( "shipcard_type" ) . innerHTML = getTypeVal ( ship ) + ' <i class="info_icon shipcard-tech-icon" id="shipcard_tech_info" onclick="event.stopPropagation(); toggleTechPopover()" title="Technical details"></i>' ;
54955511 document . getElementById ( "shipcard_shiptype" ) . innerHTML = getShipTypeVal ( ship . shiptype ) ;
54965512 document . getElementById ( "shipcard_status" ) . innerHTML = getStatusVal ( ship ) ;
54975513 document . getElementById ( "shipcard_last_signal" ) . innerHTML = getDeltaTimeVal ( ship . last_signal ) ;
@@ -5507,7 +5523,79 @@ function populateShipcard() {
55075523
55085524 updateShipcardTrackOption ( card_mmsi ) ;
55095525 updateMessageButton ( ) ;
5526+ updateTechDetails ( ship ) ;
5527+
5528+ }
5529+
5530+ function updateTechDetails ( ship ) {
5531+ // Helper to format flag values
5532+ const formatFlag = ( value , trueText = "Yes" , falseText = "No" , unknownText = "-" ) => {
5533+ if ( value === 0 ) return unknownText ;
5534+ if ( value === 1 ) return falseText ;
5535+ if ( value === 2 ) return trueText ;
5536+ return unknownText ;
5537+ } ;
55105538
5539+ // Update RAIM
5540+ document . getElementById ( "tech_raim" ) . textContent = formatFlag ( ship . raim ) ;
5541+
5542+ // Update DTE
5543+ document . getElementById ( "tech_dte" ) . textContent = formatFlag ( ship . dte , "Not Ready" , "Ready" ) ;
5544+
5545+ // Update Assigned Mode
5546+ document . getElementById ( "tech_assigned" ) . textContent = formatFlag ( ship . assigned , "Assigned" , "Autonomous" ) ;
5547+
5548+ // Update Display
5549+ document . getElementById ( "tech_display" ) . textContent = formatFlag ( ship . display ) ;
5550+
5551+ // Update DSC
5552+ document . getElementById ( "tech_dsc" ) . textContent = formatFlag ( ship . dsc ) ;
5553+
5554+ // Update Band
5555+ document . getElementById ( "tech_band" ) . textContent = formatFlag ( ship . band , "Dual" , "Single" ) ;
5556+
5557+ // Update MSG22
5558+ document . getElementById ( "tech_msg22" ) . textContent = formatFlag ( ship . msg22 ) ;
5559+
5560+ // Update Off Position
5561+ document . getElementById ( "tech_off_position" ) . textContent = formatFlag ( ship . off_position , "Off" , "On" ) ;
5562+
5563+ // Update Maneuver
5564+ const maneuverText = ship . maneuver === 0 ? "-" : ship . maneuver === 1 ? "None" : "Special" ;
5565+ document . getElementById ( "tech_maneuver" ) . textContent = maneuverText ;
5566+ }
5567+
5568+ function toggleTechPopover ( ) {
5569+ const popover = document . getElementById ( "tech_popover" ) ;
5570+ const icon = document . getElementById ( "shipcard_tech_info" ) ;
5571+
5572+ if ( popover . style . display === "none" || ! popover . style . display ) {
5573+ // Position popover near the icon
5574+ const iconRect = icon . getBoundingClientRect ( ) ;
5575+ const shipcardRect = document . getElementById ( "shipcard" ) . getBoundingClientRect ( ) ;
5576+
5577+ popover . style . display = "block" ;
5578+ popover . style . left = ( iconRect . left - shipcardRect . left + 20 ) + "px" ;
5579+ popover . style . top = ( iconRect . bottom - shipcardRect . top + 5 ) + "px" ;
5580+
5581+ // Close popover when clicking outside
5582+ setTimeout ( ( ) => {
5583+ document . addEventListener ( "click" , closeTechPopover ) ;
5584+ } , 0 ) ;
5585+ } else {
5586+ popover . style . display = "none" ;
5587+ document . removeEventListener ( "click" , closeTechPopover ) ;
5588+ }
5589+ }
5590+
5591+ function closeTechPopover ( event ) {
5592+ const popover = document . getElementById ( "tech_popover" ) ;
5593+ const icon = document . getElementById ( "shipcard_tech_info" ) ;
5594+
5595+ if ( ! popover . contains ( event . target ) && event . target !== icon ) {
5596+ popover . style . display = "none" ;
5597+ document . removeEventListener ( "click" , closeTechPopover ) ;
5598+ }
55115599}
55125600
55135601function getCategory ( plane ) {
@@ -5847,11 +5935,25 @@ function displayShipcardIcons(type) {
58475935 continue ;
58485936 }
58495937
5850- // Show if within offset range or is More button
5851- const isMoreButton = icon . querySelector ( 'i' ) . classList . contains ( 'more_horiz_icon' ) ;
5852- const isInRange = idx >= shipcardIconOffset [ type ] && idx < shipcardIconOffset [ type ] + shipcardIconMax ;
5853- icon . style . display = ( isInRange || isMoreButton ) ? "flex" : "none" ;
5854- idx ++ ;
5938+ // Check if this is the More button - always show it and don't count it
5939+ const isMoreButton = icon . querySelector ( 'i' ) ?. classList . contains ( 'more_horiz_icon' ) ;
5940+ if ( isMoreButton ) {
5941+ icon . style . display = "flex" ;
5942+ continue ;
5943+ }
5944+
5945+ // Check if realtime option should be hidden
5946+ const isRealtimeDisabled = icon . id === 'shipcard_realtime_option' && ( typeof realtime_enabled === "undefined" || realtime_enabled === false ) ;
5947+
5948+ if ( isRealtimeDisabled ) {
5949+ icon . style . display = "none" ;
5950+ // Don't increment idx, effectively removing it from the visible count
5951+ } else {
5952+ // Show if within offset range
5953+ const isInRange = idx >= shipcardIconOffset [ type ] && idx < shipcardIconOffset [ type ] + shipcardIconMax ;
5954+ icon . style . display = isInRange ? "flex" : "none" ;
5955+ idx ++ ;
5956+ }
58555957 }
58565958}
58575959
@@ -5865,18 +5967,28 @@ function rotateShipcardIcons() {
58655967
58665968function prepareShipcard ( ) {
58675969 // Initialize offset/count objects if needed
5868- shipcardIconOffset = shipcardIconOffset || { ship : 0 , plane : 0 } ;
5970+ if ( ! shipcardIconOffset || typeof shipcardIconOffset !== 'object' ) {
5971+ shipcardIconOffset = { ship : 0 , plane : 0 } ;
5972+ }
58695973 shipcardIconCount = shipcardIconCount || { ship : 0 , plane : 0 } ;
58705974
58715975 // Count icons for each context
58725976 shipcardIconCount . ship = document . querySelectorAll ( '#shipcard_footer > div[data-context-type="ship"]' ) . length ;
58735977 shipcardIconCount . plane = document . querySelectorAll ( '#shipcard_footer > div[data-context-type="plane"]' ) . length ;
58745978
5979+ // Adjust count if realtime is disabled (exclude realtime option from count)
5980+ if ( typeof realtime_enabled === "undefined" || realtime_enabled === false ) {
5981+ const realtimeOption = document . getElementById ( 'shipcard_realtime_option' ) ;
5982+ if ( realtimeOption && realtimeOption . dataset . contextType === 'ship' ) {
5983+ shipcardIconCount . ship -- ;
5984+ }
5985+ }
5986+
58755987 // Add More button for each context if needed
5876- if ( shipcardIconCount . ship > shipcardIconMax + 1 ) {
5988+ if ( shipcardIconCount . ship > shipcardIconMax ) {
58775989 addShipcardItem ( 'more_horiz' , 'More' , 'More options' , 'rotateShipcardIcons()' , 'ship' ) ;
58785990 }
5879- if ( shipcardIconCount . plane > shipcardIconMax + 1 ) {
5991+ if ( shipcardIconCount . plane > shipcardIconMax ) {
58805992 addShipcardItem ( 'more_horiz' , 'More' , 'More options' , 'rotateShipcardIcons()' , 'plane' ) ;
58815993 }
58825994
@@ -7105,7 +7217,7 @@ function clearRealtimeFilter() {
71057217
71067218function openRealtimeForMMSI ( mmsi ) {
71077219 // Check if realtime is enabled
7108- if ( typeof realtime_enabled === "undefined" || ! realtime_enabled ) {
7220+ if ( ! realtime_enabled ) {
71097221 return ;
71107222 }
71117223
@@ -7189,7 +7301,7 @@ function activateTab(b, a) {
71897301
71907302 if ( a == "realtime" ) {
71917303 // Only initialize realtime viewer if realtime is enabled
7192- if ( typeof realtime_enabled !== "undefined" && realtime_enabled ) {
7304+ if ( realtime_enabled ) {
71937305 // Only create a new viewer if one doesn't exist
71947306 if ( ! realtimeViewer ) {
71957307 // Restore saved state if available
@@ -7278,7 +7390,7 @@ function selectTab() {
72787390 if ( settings . tab == "settings" ) settings . tab = "stat" ;
72797391
72807392 // Check if requested tab is disabled and redirect to map
7281- if ( settings . tab == "realtime" && ( typeof realtime_enabled === "undefined" || realtime_enabled === false ) ) {
7393+ if ( settings . tab == "realtime" && ! realtime_enabled ) {
72827394 settings . tab = "map" ;
72837395 }
72847396 if ( settings . tab == "log" && ( typeof log_enabled === "undefined" || log_enabled === false ) ) {
0 commit comments