From 8439798799388e8a368f470692566a84e8316e40 Mon Sep 17 00:00:00 2001
From: Peter Goodhall <84308+magicbug@users.noreply.github.com>
Date: Mon, 6 Jul 2026 17:18:39 +0100
Subject: [PATCH 1/5] Lazy-load Leaflet and fix QRB modal map
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Leaflet CSS/JS assets are now loaded conditionally in shared headers/footers so map libraries are only included on pages that need them. For the QRB calculator dialog, Leaflet is loaded on demand in `common.js`, and the `newpath` map-rendering function was moved into the QRB view so the modal still works when global map scripts are skipped. Also adds a small HTMX updating indicator to the dashboard’s Today’s QSOs section.
---
application/views/dashboard/index.php | 3 +
application/views/interface_assets/footer.php | 92 +++-------------
application/views/interface_assets/header.php | 11 +-
.../views/interface_assets/mini_header.php | 11 +-
application/views/qrbcalc/index.php | 76 ++++++++++++-
assets/js/sections/common.js | 102 +++++++++++++-----
6 files changed, 182 insertions(+), 113 deletions(-)
diff --git a/application/views/dashboard/index.php b/application/views/dashboard/index.php
index d91af440f..474ad4bc6 100644
--- a/application/views/dashboard/index.php
+++ b/application/views/dashboard/index.php
@@ -43,6 +43,9 @@
+
+ Updating...
+
diff --git a/application/views/interface_assets/footer.php b/application/views/interface_assets/footer.php
index 1417ced15..e7bbb5e5d 100644
--- a/application/views/interface_assets/footer.php
+++ b/application/views/interface_assets/footer.php
@@ -36,14 +36,20 @@
-
-
-
-
-uri->segment(1) == "activators") { ?>
-
+uri->segment(1), [NULL, '', 'dashboard', 'logbook', 'gridmap', 'activated_gridmap', 'qso', 'map', 'activators', 'activatorsmap'], true)
+ || ($this->uri->segment(1) == 'awards' && in_array($this->uri->segment(2), ['cq', 'iota', 'dxcc', 'ffma', 'gridmaster', 'waja', 'was', 'sota', 'pota'], true));
+?>
+
+
+
+
+
+ uri->segment(1) == "activators") { ?>
+
+
+
-
@@ -674,78 +680,6 @@ function printWarning() {
}
};
- function newpath(latlng1, latlng2, locator1, locator2) {
- // If map is already initialized
- var container = L.DomUtil.get('mapqrbcontainer');
-
- if (container != null) {
- container._leaflet_id = null;
- container.remove();
- $("#mapqrb").append('
');
- }
-
- var map = new L.Map('mapqrbcontainer', {
- fullscreenControl: true,
- fullscreenControlOptions: {
- position: 'topleft'
- },
- }).setView([30, 0], 1.5);
-
- // Need to fix so that marker is placed at same place as end of line, but this only needs to be done when longitude is < -170
- if (latlng2[1] < -170) {
- latlng2[1] = parseFloat(latlng2[1]) + 360;
- }
- if (latlng1[1] < -170) {
- latlng1[1] = parseFloat(latlng1[1]) + 360;
- }
-
- map.fitBounds([
- [latlng1[0], latlng1[1]],
- [latlng2[0], latlng2[1]]
- ]);
-
- var maidenhead = L.maidenheadqrb().addTo(map);
-
- var osmUrl = 'optionslib->get_option('option_map_tile_server'); ?>';
- var osmAttrib = 'Map data © OpenStreetMap contributors';
- var osm = new L.TileLayer(osmUrl, {
- minZoom: 1,
- maxZoom: 12,
- attribution: osmAttrib
- });
-
- var redIcon = L.icon({
- iconUrl: icon_dot_url,
- iconSize: [10, 10], // size of the icon
- });
-
- map.addLayer(osm);
-
- var marker = L.marker([latlng1[0], latlng1[1]], {
- closeOnClick: false,
- autoClose: false
- }).addTo(map).bindPopup(locator1);
-
- var marker2 = L.marker([latlng2[0], latlng2[1]], {
- closeOnClick: false,
- autoClose: false
- }).addTo(map).bindPopup(locator2);
-
- const multiplelines = [];
- multiplelines.push(
- new L.LatLng(latlng1[0], latlng1[1]),
- new L.LatLng(latlng2[0], latlng2[1])
- )
-
- const geodesic = L.geodesic(multiplelines, {
- weight: 3,
- opacity: 1,
- color: 'red',
- wrap: false,
- steps: 100
- }).addTo(map);
- }
-
function showActivatorsMap(call, count, grids) {
let re = /,/g;
diff --git a/application/views/interface_assets/header.php b/application/views/interface_assets/header.php
index 6afbc87f0..d79538b0c 100644
--- a/application/views/interface_assets/header.php
+++ b/application/views/interface_assets/header.php
@@ -26,9 +26,16 @@
+ uri->segment(1), [NULL, '', 'dashboard', 'logbook', 'gridmap', 'activated_gridmap', 'qso', 'map', 'activators', 'activatorsmap'], true)
+ || ($this->uri->segment(1) == 'awards' && in_array($this->uri->segment(2), ['cq', 'iota', 'dxcc', 'ffma', 'gridmaster', 'waja', 'was', 'sota', 'pota'], true));
+ ?>
+
-
-
+
+
+
+
uri->segment(1) == "search" && $this->uri->segment(2) == "filter") { ?>
diff --git a/application/views/interface_assets/mini_header.php b/application/views/interface_assets/mini_header.php
index 826745453..456fe5875 100644
--- a/application/views/interface_assets/mini_header.php
+++ b/application/views/interface_assets/mini_header.php
@@ -15,10 +15,15 @@
-
-
+ uri->segment(1), [NULL, '', 'dashboard', 'logbook', 'gridmap', 'activated_gridmap', 'qso', 'map', 'activators', 'activatorsmap'], true)
+ || ($this->uri->segment(1) == 'awards' && in_array($this->uri->segment(2), ['cq', 'iota', 'dxcc', 'ffma', 'gridmaster', 'waja', 'was', 'sota', 'pota'], true));
+ ?>
- '; } ?>
+
+
+
+
diff --git a/application/views/qrbcalc/index.php b/application/views/qrbcalc/index.php
index c85c9f788..89eccc7bf 100644
--- a/application/views/qrbcalc/index.php
+++ b/application/views/qrbcalc/index.php
@@ -22,4 +22,78 @@
-
\ No newline at end of file
+
+
+
\ No newline at end of file
diff --git a/assets/js/sections/common.js b/assets/js/sections/common.js
index c06ec285c..869eab498 100644
--- a/assets/js/sections/common.js
+++ b/assets/js/sections/common.js
@@ -442,35 +442,81 @@ function qso_edit(id) {
});
}
-function spawnQrbCalculator(locator1, locator2) {
- $.ajax({
- url: base_url + 'index.php/qrbcalc',
- type: 'post',
- success: function (html) {
- BootstrapDialog.show({
- title: 'Compute QRB and QTF',
- size: BootstrapDialog.SIZE_WIDE,
- cssClass: 'lookup-dialog',
- nl2br: false,
- message: html,
- onshown: function(dialog) {
- if (locator1 !== undefined) {
- $("#qrbcalc_locator1").val(locator1);
- }
- if (locator2 !== undefined) {
- $("#qrbcalc_locator2").val(locator2);
- calculateQrb();
+function loadLeafletAssets(callback) {
+ if (typeof L !== 'undefined' && typeof L.maidenheadqrb !== 'undefined') {
+ if (typeof callback === 'function') {
+ callback();
+ }
+ return;
+ }
+
+ // Load CSS if not already present
+ if ($('link[href*="' + base_url + 'assets/js/leaflet/leaflet.css"]').length === 0) {
+ $('head').append(' ');
+ }
+ if ($('link[href*="' + base_url + 'assets/js/leaflet/Control.FullScreen.css"]').length === 0) {
+ $('head').append(' ');
+ }
+
+ // Helper to load scripts sequentially
+ function loadScript(src, next) {
+ var script = document.createElement('script');
+ script.type = 'text/javascript';
+ script.src = src;
+ script.onload = next;
+ script.onerror = function() {
+ console.error('Failed to load ' + src);
+ if (typeof next === 'function') {
+ next();
+ }
+ };
+ document.head.appendChild(script);
+ }
+
+ loadScript(base_url + 'assets/js/leaflet/leaflet.js', function() {
+ loadScript(base_url + 'assets/js/leaflet/Control.FullScreen.js', function() {
+ loadScript(base_url + 'assets/js/leaflet/L.Maidenhead.qrb.js', function() {
+ loadScript(base_url + 'assets/js/leaflet/leaflet.geodesic.js', function() {
+ if (typeof callback === 'function') {
+ callback();
}
- },
- buttons: [{
- label: lang_admin_close,
- action: function (dialogItself) {
- dialogItself.close();
- }
- }]
- });
- }
- });
+ });
+ });
+ });
+ });
+}
+
+function spawnQrbCalculator(locator1, locator2) {
+ loadLeafletAssets(function() {
+ $.ajax({
+ url: base_url + 'index.php/qrbcalc',
+ type: 'post',
+ success: function (html) {
+ BootstrapDialog.show({
+ title: 'Compute QRB and QTF',
+ size: BootstrapDialog.SIZE_WIDE,
+ cssClass: 'lookup-dialog',
+ nl2br: false,
+ message: html,
+ onshown: function(dialog) {
+ if (locator1 !== undefined) {
+ $("#qrbcalc_locator1").val(locator1);
+ }
+ if (locator2 !== undefined) {
+ $("#qrbcalc_locator2").val(locator2);
+ calculateQrb();
+ }
+ },
+ buttons: [{
+ label: lang_admin_close,
+ action: function (dialogItself) {
+ dialogItself.close();
+ }
+ }]
+ });
+ }
+ });
+ });
}
function spawnActivatorsMap(call, count, grids) {
From 239add94e1e9c8c22615ace4b489e055e04db748 Mon Sep 17 00:00:00 2001
From: Peter Goodhall <84308+magicbug@users.noreply.github.com>
Date: Wed, 8 Jul 2026 23:00:44 +0100
Subject: [PATCH 2/5] Fix label default checkbox handling
Add the `labeltable` class to the labels table and switch the checkbox handler to a delegated `change` event. The uncheck logic is now scoped to the current table so only labels in the same list are affected.
---
application/views/labels/index.php | 2 +-
assets/js/sections/labels.js | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/application/views/labels/index.php b/application/views/labels/index.php
index 913d0dd27..7bad67eb9 100644
--- a/application/views/labels/index.php
+++ b/application/views/labels/index.php
@@ -98,7 +98,7 @@
-
+
Name
diff --git a/assets/js/sections/labels.js b/assets/js/sections/labels.js
index 01154689a..be2b4e888 100644
--- a/assets/js/sections/labels.js
+++ b/assets/js/sections/labels.js
@@ -1,8 +1,8 @@
-$('.labeltable').on('click', 'input[type="checkbox"]', function() {
+$(document).on('change', '.labeltable input[type="checkbox"]', function() {
var clickedlabelid = $(this).closest('tr').attr("class");
clickedlabelid = clickedlabelid.match(/\d+/)[0];
saveDefault(clickedlabelid);
- $('input:checkbox').not(this).prop('checked', false);
+ $(this).closest('.labeltable').find('input:checkbox').not(this).prop('checked', false);
});
function saveDefault(id) {
From fb9ffdb3ad37ba00f4d41a2658f6fc31d0706b5f Mon Sep 17 00:00:00 2001
From: Peter Goodhall <84308+magicbug@users.noreply.github.com>
Date: Sat, 11 Jul 2026 16:45:29 +0100
Subject: [PATCH 3/5] Enable Leaflet maps in logbookadvanced
Add 'logbookadvanced' controller to the list of routes that require Leaflet map assets. Also conditionally load easyprint.js only when Leaflet is enabled to reduce unnecessary script loading.
#3453 Fixed
---
application/views/interface_assets/footer.php | 6 ++++--
application/views/interface_assets/header.php | 2 +-
application/views/interface_assets/mini_header.php | 2 +-
3 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/application/views/interface_assets/footer.php b/application/views/interface_assets/footer.php
index e7bbb5e5d..9ef4f4818 100644
--- a/application/views/interface_assets/footer.php
+++ b/application/views/interface_assets/footer.php
@@ -37,7 +37,7 @@
uri->segment(1), [NULL, '', 'dashboard', 'logbook', 'gridmap', 'activated_gridmap', 'qso', 'map', 'activators', 'activatorsmap'], true)
+$load_leaflet = in_array($this->uri->segment(1), [NULL, '', 'dashboard', 'logbook', 'logbookadvanced', 'gridmap', 'activated_gridmap', 'qso', 'map', 'activators', 'activatorsmap'], true)
|| ($this->uri->segment(1) == 'awards' && in_array($this->uri->segment(2), ['cq', 'iota', 'dxcc', 'ffma', 'gridmaster', 'waja', 'was', 'sota', 'pota'], true));
?>
@@ -53,7 +53,9 @@
-
+
+
+
diff --git a/application/views/interface_assets/header.php b/application/views/interface_assets/header.php
index d79538b0c..79fe057b6 100644
--- a/application/views/interface_assets/header.php
+++ b/application/views/interface_assets/header.php
@@ -27,7 +27,7 @@
uri->segment(1), [NULL, '', 'dashboard', 'logbook', 'gridmap', 'activated_gridmap', 'qso', 'map', 'activators', 'activatorsmap'], true)
+ $load_leaflet = in_array($this->uri->segment(1), [NULL, '', 'dashboard', 'logbook', 'logbookadvanced', 'gridmap', 'activated_gridmap', 'qso', 'map', 'activators', 'activatorsmap'], true)
|| ($this->uri->segment(1) == 'awards' && in_array($this->uri->segment(2), ['cq', 'iota', 'dxcc', 'ffma', 'gridmaster', 'waja', 'was', 'sota', 'pota'], true));
?>
diff --git a/application/views/interface_assets/mini_header.php b/application/views/interface_assets/mini_header.php
index 456fe5875..9db3a3eff 100644
--- a/application/views/interface_assets/mini_header.php
+++ b/application/views/interface_assets/mini_header.php
@@ -16,7 +16,7 @@
uri->segment(1), [NULL, '', 'dashboard', 'logbook', 'gridmap', 'activated_gridmap', 'qso', 'map', 'activators', 'activatorsmap'], true)
+ $load_leaflet = in_array($this->uri->segment(1), [NULL, '', 'dashboard', 'logbook', 'logbookadvanced', 'gridmap', 'activated_gridmap', 'qso', 'map', 'activators', 'activatorsmap'], true)
|| ($this->uri->segment(1) == 'awards' && in_array($this->uri->segment(2), ['cq', 'iota', 'dxcc', 'ffma', 'gridmaster', 'waja', 'was', 'sota', 'pota'], true));
?>
From ffa9d5017e3820b56196ccca8d3ddc7ab60f2bb9 Mon Sep 17 00:00:00 2001
From: Peter Goodhall <84308+magicbug@users.noreply.github.com>
Date: Sat, 11 Jul 2026 16:55:34 +0100
Subject: [PATCH 4/5] Consolidate callbook display into tab interface
Refactored the callsign profile display from a separate hidden card to an integrated tab-based interface. The new callbook tab includes a responsive grid layout with profile image, external links (QRZ and HamQTH), and metadata fields (name, QTH, locator, IOTA). Added proper HTML escaping for security and improved visual organization with Bootstrap 5 styling.
---
application/views/qso/index.php | 81 +++++++++++++++++++++++++++------
assets/js/sections/qso.js | 60 ++++++++++++++++++++----
2 files changed, 117 insertions(+), 24 deletions(-)
diff --git a/application/views/qso/index.php b/application/views/qso/index.php
index cc083ef88..e48c988fa 100755
--- a/application/views/qso/index.php
+++ b/application/views/qso/index.php
@@ -22,7 +22,59 @@ function switchMode(url) {