Merge branch 'magicbug:dev' into dev

这个提交包含在:
Steven Dodd 2025-08-11 13:59:51 +01:00 提交者 GitHub
当前提交 342ffd42e6
找不到此签名对应的密钥
GPG 密钥 ID: B5690EEEBB952194
共有 8 个文件被更改,包括 702 次插入95 次删除

查看文件

@ -4857,6 +4857,7 @@ class Logbook_model extends CI_Model
$plot = array('lat' => 0, 'lng' => 0, 'html' => '', 'label' => '', 'flag' => '', 'confirmed' => 'N'); $plot = array('lat' => 0, 'lng' => 0, 'html' => '', 'label' => '', 'flag' => '', 'confirmed' => 'N');
$plot['label'] = $row->COL_CALL; $plot['label'] = $row->COL_CALL;
$plot['callsign'] = $row->COL_CALL;
$flag = strtolower($CI->dxccflag->getISO($row->COL_DXCC)); $flag = strtolower($CI->dxccflag->getISO($row->COL_DXCC));
$plot['flag'] = '<span data-bs-toggle="tooltip" title="' . ucwords(strtolower(($row->name==null?"- NONE -":$row->name))) . '"><span class="fi fi-' . $flag .'"></span></span> '; $plot['flag'] = '<span data-bs-toggle="tooltip" title="' . ucwords(strtolower(($row->name==null?"- NONE -":$row->name))) . '"><span class="fi fi-' . $flag .'"></span></span> ';
$plot['html'] = ($row->COL_GRIDSQUARE != null ? "<b>Grid:</b> " . $row->COL_GRIDSQUARE . "<br />" : ""); $plot['html'] = ($row->COL_GRIDSQUARE != null ? "<b>Grid:</b> " . $row->COL_GRIDSQUARE . "<br />" : "");

查看文件

@ -88,10 +88,20 @@
} }
} }
// Initialize bandcalls array with all bands set to 0
foreach ($bands as $band) {
$bandcalls[$band] = 0;
}
foreach ($bandunique as $band) { foreach ($bandunique as $band) {
$bandcalls[$band->band] = $band->calls; $bandcalls[$band->band] = $band->calls;
} }
// Initialize modecalls array with all modes set to 0
foreach ($modes as $mode) {
$modecalls[$mode] = 0;
}
foreach ($modeunique as $mode) { foreach ($modeunique as $mode) {
//if ($mode->col_submode == null) { //if ($mode->col_submode == null) {
if ($mode->col_submode == null || $mode->col_submode == "") { if ($mode->col_submode == null || $mode->col_submode == "") {

查看文件

@ -784,6 +784,26 @@ if ($this->session->userdata('user_id') != null) {
}); });
// Form "submit" // // Form "submit" //
$('.custom-map-QSOs .btn_submit_map_custom').off('click').on('click', function() { $('.custom-map-QSOs .btn_submit_map_custom').off('click').on('click', function() {
// Show loading spinner and disable button
const $button = $(this);
const $spinner = $('#load-spinner');
$button.prop('disabled', true);
$spinner.removeClass('d-none');
// Hide any previous alerts
$('.warningOnSubmit').hide();
$('#map-success-alert').addClass('d-none');
// Failsafe timeout to prevent stuck spinner (60 seconds)
const failsafeTimeout = setTimeout(function() {
console.warn('Map loading timed out - forcing spinner hide');
$button.prop('disabled', false);
$spinner.addClass('d-none');
$('.warningOnSubmit .warningOnSubmit_txt').text('Map loading timed out. Please try again.');
$('.warningOnSubmit').show();
}, 60000);
var customdata = { var customdata = {
'dataPost': { 'dataPost': {
'date_from': $('.custom-map-QSOs input[name="from"]').val(), 'date_from': $('.custom-map-QSOs input[name="from"]').val(),
@ -795,6 +815,58 @@ if ($this->session->userdata('user_id') != null) {
}, },
'map_id': '#custommap' 'map_id': '#custommap'
}; };
// Add success and error callbacks to the customdata
customdata.onSuccess = function(plotjson) {
console.log('Map loading success callback called with data:', plotjson);
// Clear the failsafe timeout
clearTimeout(failsafeTimeout);
try {
// Update statistics
if (typeof updateMapStatistics === 'function') {
console.log('Calling updateMapStatistics function');
updateMapStatistics(plotjson);
} else {
console.warn('updateMapStatistics function not found');
}
// Show success message
const qsoCount = plotjson['markers'] ? plotjson['markers'].length : 0;
console.log('QSO count:', qsoCount);
$('#qso-count-display').text(`Loaded ${qsoCount} QSOs successfully`);
$('#map-success-alert').removeClass('d-none');
setTimeout(() => {
$('#map-success-alert').addClass('d-none');
}, 3000);
} catch (error) {
console.error('Error in success callback:', error);
$('.warningOnSubmit .warningOnSubmit_txt').text('Map loaded but encountered an error displaying statistics.');
$('.warningOnSubmit').show();
} finally {
// Always re-enable button and hide spinner
console.log('Re-enabling button and hiding spinner');
$button.prop('disabled', false);
$spinner.addClass('d-none');
}
};
customdata.onError = function() {
console.log('Map loading error callback called');
// Clear the failsafe timeout
clearTimeout(failsafeTimeout);
$('.warningOnSubmit .warningOnSubmit_txt').text('Failed to load map data. Please try again.');
$('.warningOnSubmit').show();
// Re-enable button and hide spinner
$button.prop('disabled', false);
$spinner.addClass('d-none');
};
initplot(qso_loc, customdata); initplot(qso_loc, customdata);
}) })
}); });
@ -806,6 +878,10 @@ if ($this->session->userdata('user_id') != null) {
success: function(data) { success: function(data) {
document.getElementById('from').value = data; document.getElementById('from').value = data;
document.getElementById('to').value = new Date().toISOString().split('T')[0]; document.getElementById('to').value = new Date().toISOString().split('T')[0];
// Update the date range display
if (typeof updateDateRangeDisplay === 'function') {
updateDateRangeDisplay();
}
}, },
error: function() {}, error: function() {},
}); });

查看文件

@ -1,35 +1,156 @@
<link rel="stylesheet" href="<?php echo base_url(); ?>assets/css/map-enhancements.css">
<style>
.callsign-label-text {
background: rgba(0, 0, 0, 0.8) !important;
color: white !important;
font-family: Arial, sans-serif !important;
font-weight: bold !important;
padding: 1px 3px !important;
border-radius: 2px !important;
border: 1px solid #fff !important;
white-space: nowrap !important;
text-align: center !important;
box-shadow: 0 1px 2px rgba(0,0,0,0.5) !important;
display: inline-block !important;
line-height: 1 !important;
margin: 0 !important;
min-width: auto !important;
width: auto !important;
height: auto !important;
transform-origin: center !important;
font-size: 12px !important; /* Ensure consistent font size */
}
</style>
<div class="container custom-map-QSOs"> <div class="container custom-map-QSOs">
<br> <br>
<h2><?php echo $logbook_name; echo strpos($logbook_name, 'logbook') ? '' : ' logbook'; ?> QSOs (Custom Dates)</h2>
<!-- Enhanced Header Section -->
<div class="row">
<div class="col-12">
<div class="card border-0 mb-4">
<div class="card-body rounded">
<div class="d-flex align-items-center justify-content-between">
<div>
<h2 class="mb-1">
<i class="fas fa-map-marked-alt me-2"></i>
<?php echo $logbook_name; echo strpos($logbook_name, 'logbook') ? '' : ' logbook'; ?> QSOs (Custom Dates)
</h2>
</div>
</div>
</div>
</div>
</div>
</div>
<?php if ($this->session->flashdata('notice')) { ?> <?php if ($this->session->flashdata('notice')) { ?>
<div class="alert alert-info" role="alert"> <div class="alert alert-info" role="alert">
<i class="fas fa-info-circle me-2"></i>
<?php echo $this->session->flashdata('notice'); ?> <?php echo $this->session->flashdata('notice'); ?>
</div> </div>
<?php } ?> <?php } ?>
<!-- Filter Controls Section -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-filter me-2"></i>Filter Controls
</h5>
</div>
<div class="card-body">
<form method="post" action="<?php echo site_url('map/custom'); ?>"> <form method="post" action="<?php echo site_url('map/custom'); ?>">
<!-- Quick Action Buttons -->
<div class="row mb-4">
<div class="col-12">
<div class="card border-0">
<div class="card-body py-3">
<h6 class="card-title mb-3">
<i class="fas fa-bolt text-warning me-2"></i>Quick Filters
</h6>
<div class="row g-2">
<div class="col-6 col-md-2">
<button type="button" class="btn btn-outline-primary btn-sm w-100" onclick="setDateRange('today')">
<i class="fas fa-calendar-day me-1"></i>Today
</button>
</div>
<div class="col-6 col-md-2">
<button type="button" class="btn btn-outline-primary btn-sm w-100" onclick="setDateRange('week')">
<i class="fas fa-calendar-week me-1"></i>This Week
</button>
</div>
<div class="col-6 col-md-2">
<button type="button" class="btn btn-outline-primary btn-sm w-100" onclick="setDateRange('month')">
<i class="fas fa-calendar-alt me-1"></i>This Month
</button>
</div>
<div class="col-6 col-md-2">
<button type="button" class="btn btn-outline-primary btn-sm w-100" onclick="setDateRange('year')">
<i class="fas fa-calendar me-1"></i>This Year
</button>
</div>
<div class="col-6 col-md-2">
<button type="button" class="btn btn-outline-primary btn-sm w-100" onclick="get_oldest_qso_date()">
<i class="fas fa-history me-1"></i>All Time
</button>
</div>
<div class="col-6 col-md-2">
<button type="button" class="btn btn-outline-danger btn-sm w-100" onclick="clearFilters()">
<i class="fas fa-eraser me-1"></i>Reset
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Date Range Section -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-body">
<h6 class="card-title text-primary mb-3">
<i class="fas fa-calendar-range me-2"></i>Date Range
</h6>
<div class="row"> <div class="row">
<div class="mb-3 col-md-3"> <div class="col-md-6">
<label for="from"><?php echo lang('gen_from_date') . ": " ?></label> <label for="from" class="form-label fw-semibold">
<input name="from" id="from" type="date" class="form-control w-auto" value="<?php echo $date_from; ?>"> <i class="fas fa-play text-success me-1"></i><?php echo lang('gen_from_date') ?>
</label>
<input name="from" id="from" type="date" class="form-control" value="<?php echo $date_from; ?>">
</div>
<div class="col-md-6">
<label for="to" class="form-label fw-semibold">
<i class="fas fa-stop text-danger me-1"></i><?php echo lang('gen_to_date') ?>
</label>
<input name="to" id="to" type="date" class="form-control" value="<?php echo $date_to; ?>" max="<?php echo $date_to; ?>">
</div>
</div>
</div> </div>
<div class="mb-3 col-md-3">
<label for="to"><?php echo lang('gen_to_date') . ": " ?></label>
<input name="to" id="to" type="date" class="form-control w-auto" value="<?php echo $date_to; ?>" max="<?php echo $date_to; ?>">
</div> </div>
<div class="mb-3 col-md-3 d-flex align-items-end">
<input class="btn btn-secondary" type="button" value="<?php echo lang('set_log_to_full_dates'); ?>" onclick="get_oldest_qso_date();">
</div> </div>
</div> </div>
<!-- Advanced Filters Section -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-body">
<h6 class="card-title text-secondary mb-3">
<i class="fas fa-sliders-h me-2"></i>Advanced Filters
</h6>
<div class="row"> <div class="row">
<div class="mb-3 col-md-3"> <div class="col-md-4 mb-3">
<label for="band">Band</label> <label for="band2" class="form-label fw-semibold">
Band
</label>
<select id="band2" name="band" class="form-select"> <select id="band2" name="band" class="form-select">
<option value="All" <?php if ($this->input->post('band') == "All" || $this->input->method() !== 'post') echo ' selected'; ?>>Every band</option> <option value="All" <?php if ($this->input->post('band') == "All" || $this->input->method() !== 'post') echo ' selected'; ?>>
<i class="fas fa-asterisk"></i> Every band
</option>
<?php foreach ($worked_bands as $band) { <?php foreach ($worked_bands as $band) {
echo '<option value="' . $band . '"'; echo '<option value="' . $band . '"';
if ($this->input->post('band') == $band) echo ' selected'; if ($this->input->post('band') == $band) echo ' selected';
@ -38,10 +159,14 @@
</select> </select>
</div> </div>
<div class="mb-3 col-md-3"> <div class="col-md-4 mb-3">
<label for="mode">Mode</label> <label for="mode" class="form-label fw-semibold">
Mode
</label>
<select id="mode" name="mode" class="form-select"> <select id="mode" name="mode" class="form-select">
<option value="All" <?php if ($this->input->post('mode') == "All" || $this->input->method() !== 'post') echo ' selected'; ?>><?php echo lang('general_word_all') ?></option> <option value="All" <?php if ($this->input->post('mode') == "All" || $this->input->method() !== 'post') echo ' selected'; ?>>
<?php echo lang('general_word_all') ?>
</option>
<?php <?php
foreach ($modes as $mode) { foreach ($modes as $mode) {
if ($mode->submode ?? '' == '') { if ($mode->submode ?? '' == '') {
@ -51,6 +176,7 @@
?> ?>
</select> </select>
</div> </div>
<?php <?php
// Sort translated propagation modes alphabetically // Sort translated propagation modes alphabetically
$prop_modes = ['AS' => lang('gen_hamradio_propagation_AS'), $prop_modes = ['AS' => lang('gen_hamradio_propagation_AS'),
@ -73,35 +199,334 @@
'TR' => lang('gen_hamradio_propagation_TR')]; 'TR' => lang('gen_hamradio_propagation_TR')];
asort($prop_modes); asort($prop_modes);
?> ?>
<div class="mb-3 col-md-3"> <div class="col-md-4 mb-3">
<label for="selectPropagation">Propagation Mode</label> <label for="selectPropagation" class="form-label fw-semibold">
Propagation Mode
</label>
<select class="form-select" id="selectPropagation" name="prop_mode"> <select class="form-select" id="selectPropagation" name="prop_mode">
<option value="All" <?php if ($this->input->post('prop_mode') == "All" || $this->input->method() !== 'post') echo ' selected'; ?>><?php echo lang('general_word_all') ?></option> <option value="All" <?php if ($this->input->post('prop_mode') == "All" || $this->input->method() !== 'post') echo ' selected'; ?>>
<option value="" <?php if ($this->input->post('prop_mode') == "" && $this->input->method() == 'post') echo ' selected'; ?>><?php echo lang('general_word_undefined') ?></option> <?php echo lang('general_word_all') ?>
</option>
<option value="" <?php if ($this->input->post('prop_mode') == "" && $this->input->method() == 'post') echo ' selected'; ?>>
<?php echo lang('general_word_undefined') ?>
</option>
<?php <?php
foreach($prop_modes as $key => $label) { foreach($prop_modes as $key => $label) {
echo '<option value="' . $key . '">'; echo '<option value="' . $key . '"';
if ($this->input->post('prop_mode') == $key) echo ' selected'; if ($this->input->post('prop_mode') == $key) echo ' selected';
echo $label . '</option>'; echo '>' . $label . '</option>';
} }
?> ?>
</select> </select>
</div> </div>
</div> </div>
<div class="row">
<div class="mb-2 col-md-2">
<input class="btn btn-primary btn_submit_map_custom" type="button" value="Load Map">
</div> </div>
<div class="mb-4 col-md-4"> </div>
<div class="alert alert-danger warningOnSubmit" style="display:none;"><span><i class="fas fa-times-circle"></i></span> <span class="warningOnSubmit_txt ms-1">Error</span></div> </div>
</div>
<!-- Action Buttons and Status -->
<div class="row">
<div class="col-md-6">
<button class="btn btn-primary btn_submit_map_custom me-3" type="button">
<i class="fas fa-map-marker-alt me-2"></i>Load Map
<span class="spinner-border spinner-border-sm ms-2 d-none" id="load-spinner" role="status">
<span class="visually-hidden">Loading...</span>
</span>
</button>
<button class="btn btn-outline-info" type="button" onclick="exportMap()">
<i class="fas fa-download me-2"></i>Export Map
</button>
</div>
<div class="col-md-6">
<div class="alert alert-danger warningOnSubmit" style="display:none;">
<i class="fas fa-exclamation-triangle me-2"></i>
<span class="warningOnSubmit_txt">Error loading map data</span>
</div>
<div class="alert alert-success d-none" id="map-success-alert">
<i class="fas fa-check-circle me-2"></i>
<span id="qso-count-display">Map loaded successfully</span>
</div>
</div> </div>
</div> </div>
</form> </form>
</div>
</div>
</div> </div>
<!-- Map --> <!-- Map Container -->
<div id="custommap" class="map-leaflet mt-2" style="width: 100%; height: 1000px;"></div> <div class="card">
<div class="card-header py-3">
<div class="row align-items-center">
<div class="col-md-4">
<h5 class="mb-0">
<i class="fas fa-globe-americas me-2"></i>World Map View
</h5>
<small class="text-muted">Showing: <strong id="active-logbook-display"><?php echo $logbook_name ?></strong></small>
</div>
<div class="col-md-4">
<div class="d-flex align-items-center justify-content-center">
<div class="me-4">
<small class="text-muted">QSOs Displayed:</small>
<span class="badge bg-primary ms-1" id="qso-count">0</span>
</div>
<div class="alert alert-success mb-0 py-1 px-2 d-none" id="map-status">
<small><i class="fas fa-check-circle me-1"></i>Map loaded successfully</small>
</div>
</div>
</div>
<div class="col-md-4 text-end">
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-outline-success" onclick="fitMapToMarkers()" title="Zoom to fit all QSOs">
<i class="fas fa-expand-arrows-alt"></i>
</button>
<button type="button" class="btn btn-outline-success" onclick="toggleGridSquares()" title="Toggle grid squares">
<i class="fas fa-th"></i>
</button>
<button type="button" class="btn btn-outline-success" onclick="toggleCallsignLabels()" title="Toggle callsign labels" id="callsign-labels-btn">
<i class="fas fa-tags"></i>
</button>
<button type="button" class="btn btn-outline-success" onclick="toggleFullscreen()" title="Toggle fullscreen">
<i class="fas fa-expand"></i>
</button>
</div>
</div>
</div>
</div>
<div class="card-body p-0">
<div id="custommap" class="map-leaflet" style="width: 100%; height: 1000px;"></div>
</div>
</div>
<div class="alert alert-success" role="alert">Showing QSOs for Custom Dates for Active Logbook <?php echo $logbook_name ?></div> <!-- Map Legend -->
<div class="row mt-3">
<div class="col-12">
<div class="card border-0">
<div class="card-body py-2">
<div class="row align-items-center">
<div class="col-md-6">
<small class="text-muted fw-semibold me-3">Legend:</small>
<span class="me-3">
<i class="fas fa-dot-circle text-danger me-1"></i>
<small>QSO</small>
</span>
<span class="me-3">
<i class="fas fa-home text-primary me-1"></i>
<small>Home Station</small>
</span>
</div>
<div class="col-md-6 text-end">
<small class="text-muted">
<i class="fas fa-info-circle me-1"></i>
Click on markers for QSO details Use
<i class="fas fa-tags"></i> to toggle callsign labels
</small>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// Enhanced JavaScript functionality for improved UX
document.addEventListener('DOMContentLoaded', function() {
// Add event listeners for date changes - removed updateDateRangeDisplay calls
// since the date-range-display element doesn't exist
});
function setDateRange(period) {
const today = new Date();
const fromInput = document.getElementById('from');
const toInput = document.getElementById('to');
let fromDate, toDate = today;
switch (period) {
case 'today':
fromDate = new Date(today);
break;
case 'week':
fromDate = new Date(today);
fromDate.setDate(today.getDate() - 7);
break;
case 'month':
// Create first day of current month
fromDate = new Date(today.getFullYear(), today.getMonth(), 1);
console.log('Month calculation - Today:', today.toDateString(), 'First of month:', fromDate.toDateString());
break;
case 'year':
fromDate = new Date(today.getFullYear(), 0, 1);
break;
}
if (fromDate) {
// Use local date formatting to avoid timezone issues
const formatDate = (date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
};
fromInput.value = formatDate(fromDate);
toInput.value = formatDate(today);
}
}
function clearFilters() {
document.getElementById('band2').selectedIndex = 0;
document.getElementById('mode').selectedIndex = 0;
document.getElementById('selectPropagation').selectedIndex = 0;
const today = new Date().toISOString().split('T')[0];
document.getElementById('from').value = today;
document.getElementById('to').value = today;
}
function exportMap() {
// This would integrate with the existing Leaflet print functionality
if (typeof map !== 'undefined' && map._container) {
// Trigger the existing print functionality
alert('Map export functionality - integrate with existing Leaflet print plugin');
}
}
function fitMapToMarkers() {
if (typeof map !== 'undefined' && plotlayers.length > 0) {
const group = new L.featureGroup(plotlayers);
map.fitBounds(group.getBounds().pad(0.1));
}
}
function toggleGridSquares() {
if (typeof maidenhead !== 'undefined') {
if (map.hasLayer(maidenhead)) {
map.removeLayer(maidenhead);
} else {
map.addLayer(maidenhead);
}
}
}
function toggleFullscreen() {
const mapContainer = document.getElementById('custommap');
if (!document.fullscreenElement) {
mapContainer.requestFullscreen().then(() => {
mapContainer.style.height = '100vh';
if (typeof map !== 'undefined') {
map.invalidateSize();
}
});
} else {
document.exitFullscreen().then(() => {
mapContainer.style.height = '1000px'; // Restore original height
if (typeof map !== 'undefined') {
map.invalidateSize();
}
});
}
}
// Global variable to track if callsign labels are shown
let callsignLabelsVisible = false;
let callsignLabels = [];
function toggleCallsignLabels() {
const button = document.getElementById('callsign-labels-btn');
if (!callsignLabelsVisible) {
// Show callsign labels
if (typeof plotlayers !== 'undefined' && plotlayers.length > 0) {
plotlayers.forEach(function(marker) {
if (marker.data && marker.data.callsign) {
// Only create labels when callsign is specifically provided
const callsign = marker.data.callsign;
// Try a different approach - create a DivIcon instead of tooltip
const labelIcon = L.divIcon({
className: 'callsign-label-icon',
html: `<div class="callsign-label-text">${callsign}</div>`,
iconSize: [0, 0], // No fixed size, let CSS handle it
iconAnchor: [0, -5] // Position above the marker with minimal offset
});
// Create a separate marker for the label
const labelMarker = L.marker(marker.getLatLng(), {
icon: labelIcon,
interactive: false,
zIndexOffset: 1000
});
map.addLayer(labelMarker);
callsignLabels.push(labelMarker);
}
});
callsignLabelsVisible = true;
button.classList.remove('btn-outline-success');
button.classList.add('btn-success');
}
} else {
// Hide callsign labels
callsignLabels.forEach(function(labelMarker) {
map.removeLayer(labelMarker);
});
callsignLabels = [];
callsignLabelsVisible = false;
button.classList.remove('btn-success');
button.classList.add('btn-outline-success');
}
}
// Update statistics when map loads
function updateMapStatistics(plotjson) {
if (plotjson && plotjson.markers) {
const qsoCountElement = document.getElementById('qso-count');
if (qsoCountElement) {
qsoCountElement.textContent = plotjson.markers.length;
}
// Count unique countries and gridsquares
const countries = new Set();
const gridsquares = new Set();
plotjson.markers.forEach(marker => {
if (marker.country) countries.add(marker.country);
if (marker.gridsquare) gridsquares.add(marker.gridsquare);
});
// Only update if elements exist
const countryCountElement = document.getElementById('country-count');
if (countryCountElement) {
countryCountElement.textContent = countries.size;
}
const gridCountElement = document.getElementById('grid-count');
if (gridCountElement) {
gridCountElement.textContent = gridsquares.size;
}
// Show success message
const mapStatusElement = document.getElementById('map-status');
if (mapStatusElement) {
mapStatusElement.classList.remove('d-none');
setTimeout(() => {
mapStatusElement.classList.add('d-none');
}, 3000);
}
// Reapply callsign labels if they were previously enabled
if (callsignLabelsVisible) {
// Reset state and toggle to reapply labels to new markers
callsignLabelsVisible = false;
callsignLabels = [];
setTimeout(() => {
toggleCallsignLabels();
}, 100); // Small delay to ensure markers are fully rendered
}
}
}
</script>

查看文件

@ -17,7 +17,7 @@ if ($qsoarray) {
foreach ($value as $key => $val) { foreach ($value as $key => $val) {
echo '<td>' . $val . '</td>'; echo '<td>' . $val . '</td>';
} }
echo '<th>' . $modetotal[$mode] . '</th>'; echo '<th>' . (isset($modetotal[$mode]) ? $modetotal[$mode] : 0) . '</th>';
echo '</tr>'; echo '</tr>';
} }
echo '</tbody><tfoot><tr><th>'.lang('statistics_total').'</th>'; echo '</tbody><tfoot><tr><th>'.lang('statistics_total').'</th>';

查看文件

@ -17,13 +17,13 @@ if ($qsoarray) {
foreach ($value as $key => $val) { foreach ($value as $key => $val) {
echo '<td>' . $val . '</td>'; echo '<td>' . $val . '</td>';
} }
echo '<th>' . $modeunique[$mode] . '</th>'; echo '<th>' . (isset($modeunique[$mode]) ? $modeunique[$mode] : 0) . '</th>';
echo '</tr>'; echo '</tr>';
} }
echo '</tbody><tfoot><tr><th>'.lang('statistics_total').'</th>'; echo '</tbody><tfoot><tr><th>'.lang('statistics_total').'</th>';
foreach($bands as $band) { foreach($bands as $band) {
echo '<th>' . $bandunique[$band] . '</th>'; echo '<th>' . (isset($bandunique[$band]) ? $bandunique[$band] : 0) . '</th>';
} }
echo '<th>' . $total->calls . '</th>'; echo '<th>' . $total->calls . '</th>';
echo '</tr></tfoot></table>'; echo '</tr></tfoot></table>';

查看文件

@ -0,0 +1,74 @@
/* Custom Map Interface Enhancements */
/* Ensure grid square labels stay together (fix for separated letters) */
.leaflet-marker-icon.my-div-icon,
.my-div-icon .grid-text,
.my-div-icon .grid-text font,
span.grid-text,
span.grid-text > font {
letter-spacing: 0 !important;
word-spacing: 0 !important;
white-space: nowrap !important;
font-family: monospace !important;
display: inline-block !important;
}
/* Map container enhancements */
#custommap {
border-radius: 0 0 0.375rem 0.375rem;
overflow: hidden;
transition: height 0.3s ease;
width: 100%;
height: 1000px !important; /* Maintain original screenshot-friendly height */
}
/* Fullscreen map styling */
#custommap:fullscreen {
border-radius: 0;
}
/* Statistics badges */
.custom-map-QSOs .badge {
font-size: 0.85em;
padding: 0.5em 0.75em;
}
/* Legend styling */
.custom-map-QSOs .fas {
width: 1em;
text-align: center;
}
/* Responsive improvements */
@media (max-width: 768px) {
.custom-map-QSOs .card-body {
padding: 1rem 0.5rem;
}
.custom-map-QSOs .btn-group .btn {
padding: 0.375rem 0.5rem;
font-size: 0.8rem;
}
/* Keep map height at 1000px even on mobile for screenshots */
#custommap {
height: 1000px !important;
min-height: 1000px !important;
}
}
/* Animation for statistics updates */
.custom-map-QSOs .badge {
transition: all 0.3s ease;
}
.custom-map-QSOs .badge.updated {
animation: pulse 0.5s ease-in-out;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}

查看文件

@ -65,12 +65,25 @@ function initplot(_url_qso, options={}) {
} }
function askForPlots(_url_qso, options={}) { function askForPlots(_url_qso, options={}) {
console.log('askForPlots called with URL:', _url_qso, 'options:', options);
removeMarkers(); removeMarkers();
if (typeof options.dataPost !== "undefined") { _dataPost = options.dataPost; } else { _dataPost = {}; } if (typeof options.dataPost !== "undefined") { _dataPost = options.dataPost; } else { _dataPost = {}; }
$.ajax({ $.ajax({
url: _url_qso, type: 'POST', dataType: 'json', data: _dataPost, url: _url_qso,
error: function() { console.log('[ERROR] ajax askForPlots() function return error.'); }, type: 'POST',
dataType: 'json',
data: _dataPost,
timeout: 50000, // 50 second timeout
error: function(xhr, status, error) {
console.log('[ERROR] ajax askForPlots() function return error:', status, error, xhr);
// Call custom error callback if provided
if (typeof options.onError === 'function') {
console.log('Calling custom error callback');
options.onError();
}
},
success: function(plotjson) { success: function(plotjson) {
console.log('askForPlots AJAX success, plotjson:', plotjson);
if ((typeof plotjson['markers'] !== "undefined")&&(plotjson['markers'].length>0)) { if ((typeof plotjson['markers'] !== "undefined")&&(plotjson['markers'].length>0)) {
for (i=0;i<plotjson['markers'].length;i++) { createPlots(plotjson['markers'][i]); } for (i=0;i<plotjson['markers'].length;i++) { createPlots(plotjson['markers'][i]); }
} }
@ -80,6 +93,14 @@ function askForPlots(_url_qso, options={}) {
$.each(iconsList, function(icon, data){ $.each(iconsList, function(icon, data){
$(options.map_id+' .cspot_'+icon).addClass(data.icon).css("color",data.color); $(options.map_id+' .cspot_'+icon).addClass(data.icon).css("color",data.color);
}); });
// Call custom success callback if provided
if (typeof options.onSuccess === 'function') {
console.log('Calling custom success callback');
options.onSuccess(plotjson);
} else {
console.warn('No custom success callback provided');
}
} }
}); });
} }