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 @@
- +
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) {
Name