From 7030c14c0069ce12eb0dc2f10acf5167b494b684 Mon Sep 17 00:00:00 2001 From: int2001 Date: Fri, 24 Oct 2025 07:33:11 +0000 Subject: [PATCH 01/43] Remoev Bandmap, only list remains --- application/views/bandmap/index.php | 66 ----------------------------- application/views/bandmap/list.php | 11 ----- 2 files changed, 77 deletions(-) delete mode 100644 application/views/bandmap/index.php diff --git a/application/views/bandmap/index.php b/application/views/bandmap/index.php deleted file mode 100644 index c118bf095..000000000 --- a/application/views/bandmap/index.php +++ /dev/null @@ -1,66 +0,0 @@ - - - -
-
-
-

-
- -
-
-
-
- - - - - - - - -
- -
-
-

-

-
-
-
diff --git a/application/views/bandmap/list.php b/application/views/bandmap/list.php index 6ab6a769e..b02ec9a1b 100644 --- a/application/views/bandmap/list.php +++ b/application/views/bandmap/list.php @@ -50,17 +50,6 @@

-
- -
-
From 8735079f8602dca3da1f1c32de4537577792c7e4 Mon Sep 17 00:00:00 2001 From: int2001 Date: Fri, 24 Oct 2025 07:34:27 +0000 Subject: [PATCH 02/43] Remove JS for old Bandmap --- assets/js/sections/bandmap.js | 254 ---------------------------------- 1 file changed, 254 deletions(-) delete mode 100644 assets/js/sections/bandmap.js diff --git a/assets/js/sections/bandmap.js b/assets/js/sections/bandmap.js deleted file mode 100644 index 7f9fa43c7..000000000 --- a/assets/js/sections/bandmap.js +++ /dev/null @@ -1,254 +0,0 @@ -$(function() { - (function(H) { - H.seriesTypes.timeline.prototype.distributeDL = function() { - var series = this, - dataLabelsOptions = series.options.dataLabels, - options, - pointDLOptions, - newOptions = {}, - visibilityIndex = 1, - j = 2, - distance; - - series.points.forEach(function(point, i) { - distance = dataLabelsOptions.distance; - - if (point.visible && !point.isNull) { - options = point.options; - pointDLOptions = point.options.dataLabels; - - if (!series.hasRendered) { - point.userDLOptions = H.merge({}, pointDLOptions); - } - - /* - if (i === j || i === j + 1) { - distance = distance * 2.5 - - if (i === j + 1) { - j += 4 - } - } - */ - if (i % 6 == 0) { distance = distance * 1; } - if (i % 6 == 1) { distance = distance * -1; } - if (i % 6 == 2) { distance = distance * 2; } - if (i % 6 == 3) { distance = distance * -2; } - if (i % 6 == 4) { distance = distance * 3; } - if (i % 6 == 5) { distance = distance * -3; } - - newOptions[series.chart.inverted ? 'x' : 'y'] = distance; - // newOptions[series.chart.inverted ? 'x' : 'y'] = dataLabelsOptions.alternate && (visibilityIndex % 3 != 0) ? -distance : distance; - - options.dataLabels = H.merge(newOptions, point.userDLOptions); - visibilityIndex++; - } - }); - } - }(Highcharts)); - - var bandMapChart; - var color = ifDarkModeThemeReturn('white', 'grey'); - - function render_chart (band,spot_data) { - let chartObject=Highcharts.chart('bandmap', { - chart: { - type: 'timeline', - zoomType: 'x', - inverted: true, - backgroundColor: getBodyBackground(), - height: '800px' - }, - accessibility: { - screenReaderSection: { - beforeChartFormat: '
{chartTitle}
' + - '
{typeDescription}
' + - '
{chartSubtitle}
' + - '
{chartLongdesc}
' + - '
{viewTableButton}
' - }, - point: { - valueDescriptionFormat: '{index}. {point.label}. {point.description}.' - } - }, - xAxis: { - lineColor: color, - visible: true, - type: 'linear', - labels: { - style: { - color: color, - } - } - }, - yAxis: { - visible: false, - }, - title: { - text: band, - style: { - color: color - } - }, - series: [ { data: spot_data } ] - }); - return chartObject; - } - - function SortByQrg(a, b){ - var a = a.frequency; - var b = b.frequency; - return ((a< b) ? -1 : ((a> b) ? 1 : 0)); - } - - function reduce_spots(spotobject) { - let unique=[]; - spotobject.forEach((single) => { - if (!spotobject.find((item) => ((item.spotted == single.spotted) && (item.frequency == single.frequency) && (Date.parse(item.when)>Date.parse(single.when))))) { - unique.push(single); - } - }); - return unique; - } - - function convert2high(spotobject) { - let ret={}; - ret.name=spotobject.spotted; - ret.x=spotobject.frequency; - ret.description=spotobject.frequency + " / "+Math.round( (Date.now() - Date.parse(spotobject.when)) / 1000 / 60)+"min. ago"; - ret.dataLabels={}; - ret.dataLabels.alternate=true; - ret.dataLabels.distance=200; - return ret; - } - - function update_chart(band,maxAgeMinutes) { - if ((band != '') && (band !== undefined)) { - let dxurl = dxcluster_provider + "/spots/" + band + "/" +maxAgeMinutes; - $.ajax({ - url: dxurl, - cache: false, - dataType: "json" - }).done(function(dxspots) { - spots4chart=[]; - if (dxspots.length>0) { - dxspots.sort(SortByQrg); - dxspots=reduce_spots(dxspots); - dxspots.forEach((single) => { - spots4chart.push(convert2high(single)); - }); - } - bandMapChart.title.text=band; - bandMapChart.series[0].setData(spots4chart); - bandMapChart.redraw(); - }); - } - } - - - function set_chart(band, de, maxAgeMinutes) { - if ((band != '') && (band !== undefined)) { - let dxurl = dxcluster_provider + "/spots/" + band + "/" +maxAgeMinutes + "/" + de; - $.ajax({ - url: dxurl, - cache: false, - dataType: "json" - }).done(function(dxspots) { - spots4chart=[]; - if (dxspots.length>0) { - dxspots.sort(SortByQrg); - dxspots=reduce_spots(dxspots); - dxspots.forEach((single) => { - spots4chart.push(convert2high(single)); - }); - } - bandMapChart=render_chart(band,spots4chart); - }); - } - } - - $("#menutoggle").on("click", function() { - if ($('.navbar').is(":hidden")) { - $('.navbar').show(); - $('#dxtabs').show(); - $('#dxtitle').show(); - $('#menutoggle_i').removeClass('fa-arrow-down'); - $('#menutoggle_i').addClass('fa-arrow-up'); - } else { - $('.navbar').hide(); - $('#dxtabs').hide(); - $('#dxtitle').hide(); - $('#menutoggle_i').removeClass('fa-arrow-up'); - $('#menutoggle_i').addClass('fa-arrow-down'); - } - }); - - set_chart($('#band option:selected').val(), $('#decontSelect option:selected').val(), dxcluster_maxage); - setInterval(function () { update_chart($('#band option:selected').val(),dxcluster_maxage); },60000); - $("#band").on("change",function() { - set_chart($('#band option:selected').val(), $('#decontSelect option:selected').val(), dxcluster_maxage); - }); - - $("#decontSelect").on("change",function() { - set_chart($('#band option:selected').val(), $('#decontSelect option:selected').val(), dxcluster_maxage); - }); -}); - -var updateFromCAT = function() { - if($('select.radios option:selected').val() != '0') { - radioID = $('select.radios option:selected').val(); - $.getJSON( base_url + "index.php/radio/json/" + radioID, function( data ) { - - if (data.error) { - if (data.error == 'not_logged_in') { - $(".radio_cat_state" ).remove(); - if($('.radio_login_error').length == 0) { - $('.messages').prepend(''); - } - } - // Put future Errorhandling here - } else { - if($('.radio_login_error').length != 0) { - $(".radio_login_error" ).remove(); - } - var band = frequencyToBand(data.frequency); - - if (band !== $("#band").val()) { - $("#band").val(band); - $("#band").trigger("change"); - } - - var minutes = Math.floor(cat_timeout_interval / 60); - - if(data.updated_minutes_ago > minutes) { - $(".radio_cat_state" ).remove(); - if($('.radio_timeout_error').length == 0) { - $('.messages').prepend(''); - } else { - $('.radio_timeout_error').html('Radio connection timed-out: ' + $('select.radios option:selected').text() + ' data is ' + data.updated_minutes_ago + ' minutes old.'); - } - } else { - $(".radio_timeout_error" ).remove(); - text = 'TX: '+(Math.round(parseInt(data.frequency)/100)/10000).toFixed(4)+' MHz'; - if(data.mode != null) { - text = text+''+data.mode; - } - if(data.power != null && data.power != 0) { - text = text+''+data.power+' W'; - } - if (! $('#radio_cat_state').length) { - $('.messages').prepend(''); - } else { - $('#radio_cat_state').html(text); - } - } - } - }); - } -}; - -// Update frequency every three second -setInterval(updateFromCAT, 3000); - -// If a radios selected from drop down select radio update. -$('.radios').change(updateFromCAT); From 625ffe20ba21cd0875315e24b3a093edaed312eb Mon Sep 17 00:00:00 2001 From: William Goodspeed Date: Fri, 24 Oct 2025 17:46:42 +0800 Subject: [PATCH 03/43] Ignore keyup/input event when composing with IME, and trigger them when finished The current keyup/input didn't handle composing events, which leads to: 1. Repeating input when composing in IME with other language (qso.js); 2. Incorrect sync between the callsign field and the search field (contesting.js). The commit solves it by not doing anything when the user compsoing in IME and firing the event later when the user finished typing. This isn't a big deal on PC but may be troublesome for mobile users, where they often have a default IME on everytime typing in a . Signed-off-by: William Goodspeed --- assets/js/sections/contesting.js | 9 +++++++++ assets/js/sections/qso.js | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/assets/js/sections/contesting.js b/assets/js/sections/contesting.js index b17bd8772..ebcd3fd11 100644 --- a/assets/js/sections/contesting.js +++ b/assets/js/sections/contesting.js @@ -385,8 +385,17 @@ $("#callsign").on( "blur", function() { var scps=[]; +$("#callsign").on("compositionstart", function(){ this.isComposing = true; }); +$("#callsign").on("compositionend", function(e){ + this.isComposing = false; + $(this).trigger("keyup"); +}); + // On Key up check and suggest callsigns $("#callsign").keyup(async function (e) { + // Prevent checking when the user's composing in IME + if (this.isComposing || e.isComposing) return; + var call = $(this).val(); if ((!((e.keyCode == 10 || e.keyCode == 13) && (e.ctrlKey || e.metaKey))) && (call.length >= 3)) { // prevent checking again when pressing CTRL-Enter diff --git a/assets/js/sections/qso.js b/assets/js/sections/qso.js index 8772953b4..598b30452 100644 --- a/assets/js/sections/qso.js +++ b/assets/js/sections/qso.js @@ -99,6 +99,12 @@ $('.qso_panel .qso_eqsl_qslmsg_update').off('click').on('click', function () { $('#charsLeft').text(" "); }); +$("#callsign").on("compositionstart", function(){ this.isComposing = true; }); +$("#callsign").on("compositionend", function(e){ + this.isComposing = false; + $(this).trigger("input"); +}); + $(document).on("keydown", function (e) { if (e.key === "Escape" && $('#callsign').val() != '') { // escape key maps to keycode `27` // console.log("Escape key pressed"); @@ -109,6 +115,9 @@ $(document).on("keydown", function (e) { // Sanitize some input data $('#callsign').on('input', function () { + // Prevent checking when the user's composing in IME + if (this.isComposing) return; + $(this).val($(this).val().replace(/\s/g, '')); $(this).val($(this).val().replace(/0/g, 'Ø')); $(this).val($(this).val().replace(/\./g, '/P')); From ea9f06c80cc25deefee7f91129c34806ebbcc506 Mon Sep 17 00:00:00 2001 From: Szymon Porwolik Date: Mon, 27 Oct 2025 22:54:11 +0100 Subject: [PATCH 04/43] Removed apostrophes from translated message for JavaScript - this was causing a bug --- application/views/qso/award_tabs.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/views/qso/award_tabs.php b/application/views/qso/award_tabs.php index 33753b1eb..647239acf 100644 --- a/application/views/qso/award_tabs.php +++ b/application/views/qso/award_tabs.php @@ -16,7 +16,7 @@ var lang_summary_warning_empty_iota = ''; var lang_summary_warning_empty_dok = ''; var lang_summary_warning_empty_wwff = ''; - var lang_summary_warning_empty_sat = ''; + var lang_summary_warning_empty_sat = ''; var lang_summary_warning_empty_gridsquare = ''; var lang_summary_info_only_first_pota = ''; var lang_summary_info_only_first_gridsquare = ''; From 68f43b33e5ac2780f579fbf60669792f27a17128 Mon Sep 17 00:00:00 2001 From: Szymon Porwolik Date: Tue, 28 Oct 2025 19:14:18 +0100 Subject: [PATCH 05/43] Implemented toast notifications, improved translation --- application/views/interface_assets/footer.php | 4 +++ application/views/qso/index.php | 1 - assets/js/sections/qso.js | 32 +++++++------------ 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/application/views/interface_assets/footer.php b/application/views/interface_assets/footer.php index c010aac2f..159337d63 100644 --- a/application/views/interface_assets/footer.php +++ b/application/views/interface_assets/footer.php @@ -35,6 +35,8 @@ var lang_general_word_error = ""; var lang_general_word_attention = ""; var lang_general_word_warning = ""; + var lang_general_word_success = ""; + var lang_general_word_info = ""; var lang_general_word_cancel = ""; var lang_general_word_ok = ""; var lang_general_word_search = ""; @@ -74,6 +76,8 @@ var lang_qso_note_created = ""; var lang_qso_note_saved = ""; var lang_qso_note_error_saving = ""; + var lang_qso_added = ""; + var lang_qso_added_to_backlog = ""; diff --git a/application/views/qso/index.php b/application/views/qso/index.php index 5cc5e116d..3ddad7c8c 100644 --- a/application/views/qso/index.php +++ b/application/views/qso/index.php @@ -737,7 +737,6 @@ switch ($date_format) {
- - +
+ +
@@ -317,7 +319,7 @@ switch ($date_format) {
- +  
@@ -635,10 +637,10 @@ switch ($date_format) { - + diff --git a/assets/js/sections/qso.js b/assets/js/sections/qso.js index 6ff4b08d1..883a7ff1c 100644 --- a/assets/js/sections/qso.js +++ b/assets/js/sections/qso.js @@ -928,6 +928,7 @@ function reset_fields() { $('#lotw_info').removeClass("lotw_info_orange"); $('#qrz_info').text("").hide(); $('#hamqth_info').text("").hide(); + $('#email_info').html("").addClass('d-none').hide(); $('#dxcc_id').val("").multiselect('refresh'); $('#cqz').val(""); $('#ituz').val(""); @@ -1295,6 +1296,13 @@ $("#callsign").on("focusout", function () { $('#email').val(result.callsign_email); } + // Show email icon if email is available + if (result.callsign_email && result.callsign_email.trim() !== "") { + $('#email_info').html(''); + $('#email_info').attr('title', lang_qso_send_email_to.replace('%s', result.callsign_email)).removeClass('d-none'); + $('#email_info').show(); + } + if ($('#continent').val() == "") { $('#continent').val(result.dxcc.cont); } @@ -2092,6 +2100,19 @@ $("#locator").on("focusout", function () { } }); +// Update email icon when email field changes +$("#email").on("input focusout", function () { + var emailValue = $(this).val().trim(); + if (emailValue !== "") { + $('#email_info').html(''); + $('#email_info').attr('title', lang_qso_send_email_to.replace('%s', emailValue)).removeClass('d-none'); + $('#email_info').show(); + } else { + $('#email_info').addClass('d-none').hide(); + $('#email_info').html(''); + } +}); + $("#ant_path").on("change", function () { if ($("#locator").val().length > 0) { $.ajax({ @@ -2240,6 +2261,7 @@ function resetDefaultQSOFields() { $('#continent').val(""); $("#distance").val(""); $('#email').val(""); + $('#email_info').html("").addClass('d-none').hide(); $('#region').val(""); $('#dxcc_id').val("").multiselect('refresh'); $('#cqz').val(""); From 1012c9bb3ac658d97f2859f03c4db9d8f7a33a1b Mon Sep 17 00:00:00 2001 From: Szymon Porwolik Date: Tue, 28 Oct 2025 19:56:06 +0100 Subject: [PATCH 08/43] Enhance callsign input with loading spinner and disable save button during fetch --- application/views/qso/index.php | 2 +- assets/js/sections/qso.js | 28 +++++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/application/views/qso/index.php b/application/views/qso/index.php index 8326c7692..0aaa30580 100644 --- a/application/views/qso/index.php +++ b/application/views/qso/index.php @@ -154,7 +154,7 @@ switch ($date_format) {
-  " class="fas fa-search"> +  " class="fas fa-search">
diff --git a/assets/js/sections/qso.js b/assets/js/sections/qso.js index 883a7ff1c..fc4aa8edf 100644 --- a/assets/js/sections/qso.js +++ b/assets/js/sections/qso.js @@ -257,6 +257,13 @@ function invalidAntEl() { } $("#qso_input").off('submit').on('submit', function (e) { + e.preventDefault(); + + // Prevent submission if Save button is disabled (fetch in progress) + if ($('#saveQso').prop('disabled')) { + return false; + } + var _submit = true; if ((typeof qso_manual !== "undefined") && (qso_manual == "1")) { if ($('#qso_input input[name="end_time"]').length == 1) { _submit = testTimeOffConsistency(); } @@ -265,7 +272,6 @@ $("#qso_input").off('submit').on('submit', function (e) { var saveQsoButtonText = $("#saveQso").html(); $("#saveQso").html(' ' + saveQsoButtonText + '...').prop('disabled', true); manual_addon = '?manual=' + qso_manual; - e.preventDefault(); $.ajax({ url: base_url + 'index.php/qso' + manual_addon, method: 'POST', @@ -1043,6 +1049,16 @@ function get_note_status(callsign){ $("#callsign").on("focusout", function () { if ($(this).val().length >= 3 && preventLookup == false) { + // Disable Save QSO button and show fetch status + $('#saveQso').prop('disabled', true); + $('#fetch_status').show(); + + // Set timeout to unlock form after 10 seconds + var fetchTimeout = setTimeout(function() { + $('#saveQso').prop('disabled', false); + $('#fetch_status').hide(); + }, 10000); + /* Find and populate DXCC */ $('.callsign-suggest').hide(); @@ -1388,12 +1404,22 @@ $("#callsign").on("focusout", function () { loadAwardTabs(function() { getDxccResult(result.dxcc.adif, convert_case(result.dxcc.entity)); }); + + // Re-enable Save QSO button and hide fetch status + clearTimeout(fetchTimeout); + $('#saveQso').prop('disabled', false); + $('#fetch_status').hide(); } // else { // console.log("Callsigns do not match, skipping lookup"); // console.log("Typed Callsign: " + $('#callsign').val()); // console.log("Returned Callsign: " + result.callsign); // } + }).always(function() { + // Always re-enable button even if there's an error + clearTimeout(fetchTimeout); + $('#saveQso').prop('disabled', false); + $('#fetch_status').hide(); }); } else { // Reset QSO fields From 0549d276501c29de25989bbf5f63c3fe3a93950d Mon Sep 17 00:00:00 2001 From: Szymon Porwolik Date: Tue, 28 Oct 2025 20:00:44 +0100 Subject: [PATCH 09/43] Improved callsign notes rolling down and up --- application/views/qso/index.php | 6 +++--- assets/css/general.css | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/application/views/qso/index.php b/application/views/qso/index.php index 0aaa30580..6f888f8c4 100644 --- a/application/views/qso/index.php +++ b/application/views/qso/index.php @@ -720,16 +720,16 @@ switch ($date_format) {
-
+
diff --git a/assets/css/general.css b/assets/css/general.css index d5638e288..fe33b04a2 100644 --- a/assets/css/general.css +++ b/assets/css/general.css @@ -512,6 +512,16 @@ TD.lotw { color: #ffffff; } +/* Round bottom corners of callsign notes header when collapsed */ +.callsign-notes .card-header { + border-radius: 0.25rem; +} + +.callsign-notes .card-header[aria-expanded="true"] { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + #myTab .nav-link { padding: 8px !important; } From c55e2de6a2b7fea3cea4068b8dfef5d0b3cefb5b Mon Sep 17 00:00:00 2001 From: Szymon Porwolik Date: Tue, 28 Oct 2025 20:03:00 +0100 Subject: [PATCH 10/43] Leaflet now has rounded corners to match rest of the page --- assets/css/general.css | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/assets/css/general.css b/assets/css/general.css index fe33b04a2..b9a017b64 100644 --- a/assets/css/general.css +++ b/assets/css/general.css @@ -532,6 +532,13 @@ TD.lotw { #qsomap { z-index: 1; + border-radius: 0.25rem; + overflow: hidden; +} + +.qso-map { + border-radius: 0.25rem; + overflow: hidden; } #create_station_profile .row { From 592592a866774fe2232b5d6f28055c3118be376e Mon Sep 17 00:00:00 2001 From: Szymon Porwolik Date: Tue, 28 Oct 2025 20:12:21 +0100 Subject: [PATCH 11/43] Callsign notes icon --- application/views/qso/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/views/qso/index.php b/application/views/qso/index.php index 6f888f8c4..d97c8127c 100644 --- a/application/views/qso/index.php +++ b/application/views/qso/index.php @@ -728,7 +728,7 @@ switch ($date_format) { - +
From 5f9494c2d7cb2d43182d25df89cce8dae340fcc2 Mon Sep 17 00:00:00 2001 From: Szymon Porwolik Date: Tue, 28 Oct 2025 20:37:49 +0100 Subject: [PATCH 12/43] Minor translation fixes --- application/views/interface_assets/footer.php | 16 ++++ assets/js/sections/qso.js | 75 +++++++++---------- 2 files changed, 53 insertions(+), 38 deletions(-) diff --git a/application/views/interface_assets/footer.php b/application/views/interface_assets/footer.php index 08f333b91..e0857360a 100644 --- a/application/views/interface_assets/footer.php +++ b/application/views/interface_assets/footer.php @@ -79,6 +79,22 @@ var lang_qso_added = ""; var lang_qso_added_to_backlog = ""; var lang_qso_send_email_to = ""; + var lang_qso_callsign_confirmed = ""; + var lang_qso_callsign_worked = ""; + var lang_qso_callsign_new = ""; + var lang_qso_grid_confirmed = ""; + var lang_qso_grid_worked = ""; + var lang_qso_grid_new = ""; + var lang_qso_delete_fav_confirm = ""; + var lang_qso_dxcc_confirmed = ""; + var lang_qso_dxcc_worked = ""; + var lang_qso_dxcc_new = ""; + var lang_qso_lookup_info = ""; + var lang_qso_lookup_summit_info = ""; + var lang_qso_lookup_reference_info = ""; + var lang_qso_error_loading_bearing = ""; + var lang_qso_gridsquare_formatting = ""; + var lang_qso_gridsquare_help = ""; diff --git a/assets/js/sections/qso.js b/assets/js/sections/qso.js index fc4aa8edf..c002b71f0 100644 --- a/assets/js/sections/qso.js +++ b/assets/js/sections/qso.js @@ -551,7 +551,7 @@ $(document).on("click", "#fav_recall", function (event) { function del_fav(name) { - if (confirm("Are you sure to delete Fav?")) { + if (confirm(lang_qso_delete_fav_confirm)) { $.ajax({ url: base_url + 'index.php/user_options/del_fav', method: 'POST', @@ -846,14 +846,14 @@ function changebadge(entityval) { if (result.confirmed) { $('#callsign_info').addClass("text-bg-success"); - $('#callsign_info').attr('title', 'DXCC was already worked and confirmed in the past on this band and mode!'); + $('#callsign_info').attr('title', lang_qso_dxcc_confirmed); } else if (result.workedBefore) { $('#callsign_info').addClass("text-bg-success"); $('#callsign_info').addClass("lotw_info_orange"); - $('#callsign_info').attr('title', 'DXCC was already worked in the past on this band and mode!'); + $('#callsign_info').attr('title', lang_qso_dxcc_worked); } else { $('#callsign_info').addClass("text-bg-danger"); - $('#callsign_info').attr('title', 'New DXCC, not worked on this band and mode!'); + $('#callsign_info').attr('title', lang_qso_dxcc_new); } }) } else { @@ -867,14 +867,14 @@ function changebadge(entityval) { if (result.confirmed) { $('#callsign_info').addClass("text-bg-success"); - $('#callsign_info').attr('title', 'DXCC was already worked and confirmed in the past on this band and mode!'); + $('#callsign_info').attr('title', lang_qso_dxcc_confirmed); } else if (result.workedBefore) { $('#callsign_info').addClass("text-bg-success"); $('#callsign_info').addClass("lotw_info_orange"); - $('#callsign_info').attr('title', 'DXCC was already worked in the past on this band and mode!'); + $('#callsign_info').attr('title', lang_qso_dxcc_worked); } else { $('#callsign_info').addClass("text-bg-danger"); - $('#callsign_info').attr('title', 'New DXCC, not worked on this band and mode!'); + $('#callsign_info').attr('title', lang_qso_dxcc_new); } }) } @@ -1118,14 +1118,14 @@ $("#callsign").on("focusout", function () { if (result.confirmed) { $('#callsign').addClass("confirmedGrid"); - $('#callsign').attr('title', 'Callsign was already worked and confirmed in the past on this band and mode!'); + $('#callsign').attr('title', lang_qso_callsign_confirmed); } else if (result.workedBefore) { $('#callsign').addClass("workedGrid"); - $('#callsign').attr('title', 'Callsign was already worked in the past on this band and mode!'); + $('#callsign').attr('title', lang_qso_callsign_worked); } else { $('#callsign').addClass("newGrid"); - $('#callsign').attr('title', 'New Callsign!'); + $('#callsign').attr('title', lang_qso_callsign_new); } }) } else { @@ -1140,17 +1140,16 @@ $("#callsign").on("focusout", function () { $('#ham_of_note_link').removeAttr('href'); $('#ham_of_note_line').hide(); - if (result.confirmed) { - $('#callsign').addClass("confirmedGrid"); - $('#callsign').attr('title', 'Callsign was already worked and confirmed in the past on this band and mode!'); - } else if (result.workedBefore) { - $('#callsign').addClass("workedGrid"); - $('#callsign').attr('title', 'Callsign was already worked in the past on this band and mode!'); - } else { - $('#callsign').addClass("newGrid"); - $('#callsign').attr('title', 'New Callsign!'); - } - + if (result.confirmed) { + $('#callsign').addClass("confirmedGrid"); + $('#callsign').attr('title', lang_qso_callsign_confirmed); + } else if (result.workedBefore) { + $('#callsign').addClass("workedGrid"); + $('#callsign').attr('title', lang_qso_callsign_worked); + } else { + $('#callsign').addClass("newGrid"); + $('#callsign').attr('title', lang_qso_callsign_new); + } }) } @@ -1179,10 +1178,10 @@ $("#callsign").on("focusout", function () { $('[data-bs-toggle="tooltip"]').tooltip(); } $('#qrz_info').html(''); - $('#qrz_info').attr('title', 'Lookup ' + callsign + ' info on qrz.com').removeClass('d-none'); + $('#qrz_info').attr('title', lang_qso_lookup_info.replace('%s', callsign).replace('%s', 'qrz.com')).removeClass('d-none'); $('#qrz_info').show(); $('#hamqth_info').html(''); - $('#hamqth_info').attr('title', 'Lookup ' + callsign + ' info on hamqth.com').removeClass('d-none'); + $('#hamqth_info').attr('title', lang_qso_lookup_info.replace('%s', callsign).replace('%s', 'hamqth.com')).removeClass('d-none'); $('#hamqth_info').show(); var $dok_select = $('#darc_dok').selectize(); @@ -1280,13 +1279,13 @@ $("#callsign").on("focusout", function () { if (result.callsign_qra != "" && (result.callsign_geoloc != 'grid' || result.timesWorked > 0)) { if (result.confirmed) { $('#locator').addClass("confirmedGrid"); - $('#locator').attr('title', 'Grid was already worked and confirmed in the past'); + $('#locator').attr('title', lang_qso_grid_confirmed); } else if (result.workedBefore) { $('#locator').addClass("workedGrid"); - $('#locator').attr('title', 'Grid was already worked in the past'); + $('#locator').attr('title', lang_qso_grid_worked); } else { $('#locator').addClass("newGrid"); - $('#locator').attr('title', 'New grid!'); + $('#locator').attr('title', lang_qso_grid_new); } } else { $('#locator').removeClass("workedGrid"); @@ -2025,13 +2024,13 @@ $("#locator").on("input focus", function () { if (result.confirmed) { $('#locator').addClass("confirmedGrid"); - $('#locator').attr('title', 'Grid was already worked and confirmed in the past'); + $('#locator').attr('title', lang_qso_grid_confirmed); } else if (result.workedBefore) { $('#locator').addClass("workedGrid"); - $('#locator').attr('title', 'Grid was already worked in the past'); + $('#locator').attr('title', lang_qso_grid_worked); } else { $('#locator').addClass("newGrid"); - $('#locator').attr('title', 'New grid!'); + $('#locator').attr('title', lang_qso_grid_new); } }) } else { @@ -2044,13 +2043,13 @@ $("#locator").on("input focus", function () { if (result.confirmed) { $('#locator').addClass("confirmedGrid"); - $('#locator').attr('title', 'Grid was already worked and confimred in the past'); + $('#locator').attr('title', lang_qso_grid_confirmed); } else if (result.workedBefore) { $('#locator').addClass("workedGrid"); - $('#locator').attr('title', 'Grid was already worked in the past'); + $('#locator').attr('title', lang_qso_grid_worked); } else { $('#locator').addClass("newGrid"); - $('#locator').attr('title', 'New grid!'); + $('#locator').attr('title', lang_qso_grid_new); } }) @@ -2097,7 +2096,7 @@ $("#locator").on("input focus", function () { $('#locator_info').html(data).fadeIn("slow"); }, error: function () { - $('#locator_info').text("Error loading bearing!").fadeIn("slow"); + $('#locator_info').text(lang_qso_error_loading_bearing).fadeIn("slow"); }, }); $.ajax({ @@ -2153,7 +2152,7 @@ $("#ant_path").on("change", function () { $('#locator_info').html(data).fadeIn("slow"); }, error: function () { - $('#locator_info').text("Error loading bearing!").fadeIn("slow"); + $('#locator_info').text(lang_qso_error_loading_bearing).fadeIn("slow"); }, }); $.ajax({ @@ -2381,7 +2380,7 @@ $(document).ready(function () { clearQrgUnits(); set_qrg(); - $("#locator").popover({ placement: 'top', title: 'Gridsquare Formatting', content: "Enter multiple (4-digit) grids separated with commas. For example: IO77,IO78" }) + $("#locator").popover({ placement: 'top', title: lang_qso_gridsquare_formatting, content: lang_qso_gridsquare_help }) .focus(function () { $('#locator').popover('show'); }) @@ -2469,7 +2468,7 @@ $(document).ready(function () { if (value !== '') { $('#sota_info').show(); $('#sota_info').html(''); - $('#sota_info').attr('title', 'Lookup ' + value + ' summit info on sota.org.uk'); + $('#sota_info').attr('title', lang_qso_lookup_summit_info.replace('%s', value).replace('%s', 'sota.org.uk')); } else { $('#sota_info').hide(); } @@ -2506,7 +2505,7 @@ $(document).ready(function () { if (value !== '') { $('#wwff_info').show(); $('#wwff_info').html(''); - $('#wwff_info').attr('title', 'Lookup ' + value + ' reference info on cqgma.org'); + $('#wwff_info').attr('title', lang_qso_lookup_reference_info.replace('%s', value).replace('%s', 'cqgma.org')); } else { $('#wwff_info').hide(); } @@ -2543,7 +2542,7 @@ $(document).ready(function () { if (value !== '' && value.indexOf(',') === -1) { $('#pota_info').show(); $('#pota_info').html(''); - $('#pota_info').attr('title', 'Lookup ' + value + ' reference info on pota.co'); + $('#pota_info').attr('title', lang_qso_lookup_reference_info.replace('%s', value).replace('%s', 'pota.co')); } else { $('#pota_info').hide(); } From 344365fd8b4271330a1ecf647e8045a32381a7f8 Mon Sep 17 00:00:00 2001 From: Szymon Porwolik Date: Tue, 28 Oct 2025 20:40:33 +0100 Subject: [PATCH 13/43] Tooltips were not properly removed --- assets/js/sections/qso.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assets/js/sections/qso.js b/assets/js/sections/qso.js index c002b71f0..abe34bd15 100644 --- a/assets/js/sections/qso.js +++ b/assets/js/sections/qso.js @@ -946,10 +946,12 @@ function reset_fields() { $("#locator").removeClass("confirmedGrid"); $("#locator").removeClass("workedGrid"); $("#locator").removeClass("newGrid"); + $('#locator').attr('title', ''); $("#callsign").val(""); $("#callsign").removeClass("confirmedGrid"); $("#callsign").removeClass("workedGrid"); $("#callsign").removeClass("newGrid"); + $('#callsign').attr('title', ''); $('#callsign_info').removeClass("text-bg-secondary"); $('#callsign_info').removeClass("text-bg-success"); $('#callsign_info').removeClass("text-bg-danger"); From 16b2e9763e780bf7bfe4107f8b2cb1d92dd8808c Mon Sep 17 00:00:00 2001 From: Szymon Porwolik Date: Tue, 28 Oct 2025 23:10:43 +0100 Subject: [PATCH 14/43] Profile information, translation and bug with the callsign information fetch --- application/controllers/Logbook.php | 53 +++- application/libraries/Qrz.php | 105 +++++--- application/views/interface_assets/footer.php | 17 ++ application/views/qso/index.php | 15 +- assets/css/general.css | 3 + assets/js/sections/qso.js | 236 +++++++++++++++++- 6 files changed, 370 insertions(+), 59 deletions(-) diff --git a/application/controllers/Logbook.php b/application/controllers/Logbook.php index 13e3970d9..6f13af8e1 100644 --- a/application/controllers/Logbook.php +++ b/application/controllers/Logbook.php @@ -162,21 +162,25 @@ class Logbook extends CI_Controller { $measurement_base = $this->session->userdata('user_measurement_base'); } - $return['callsign_name'] = $this->nval($callbook['name'] ?? '', $this->logbook_model->call_name($callsign)); - $return['callsign_qra'] = $this->nval($callbook['gridsquare'] ?? '', $this->logbook_model->call_qra($callsign)); + // Fill in the return array with callbook data, using database values as fallback + // Callbook data takes precedence over database values + // Logic updated to use nval() function to prioritize callbook data + // Added callsign_email field + $return['callsign_name'] = $this->nval($this->logbook_model->call_name($callsign), $callbook['name'] ?? ''); + $return['callsign_qra'] = $this->nval($this->logbook_model->call_qra($callsign), $callbook['gridsquare'] ?? ''); $return['callsign_geoloc'] = $callbook['geoloc'] ?? ''; $return['callsign_distance'] = $this->distance($return['callsign_qra'], $station_id); - $return['callsign_qth'] = $this->nval($callbook['city'] ?? '', $this->logbook_model->call_qth($callsign)); - $return['callsign_iota'] = $this->nval($callbook['iota'] ?? '', $this->logbook_model->call_iota($callsign)); - $return['callsign_email'] = $this->nval($callbook['email'] ?? '', $this->logbook_model->call_email($callsign)); - $return['qsl_manager'] = $this->nval($callbook['qslmgr'] ?? '', $this->logbook_model->call_qslvia($callsign)); - $return['callsign_state'] = $this->nval($callbook['state'] ?? '', $this->logbook_model->call_state($callsign)); - $return['callsign_us_county'] = $this->nval($callbook['us_county'] ?? '', $this->logbook_model->call_us_county($callsign)); - $return['callsign_ituz'] = $this->nval($callbook['ituz'] ?? '', $this->logbook_model->call_ituzone($callsign)); - $return['callsign_cqz'] = $this->nval($callbook['cqz'] ?? '', $this->logbook_model->call_cqzone($callsign)); + $return['callsign_qth'] = $this->nval($this->logbook_model->call_qth($callsign), $callbook['city'] ?? ''); + $return['callsign_iota'] = $this->nval($this->logbook_model->call_iota($callsign), $callbook['iota'] ?? ''); + $return['callsign_email'] = $this->nval($this->logbook_model->call_email($callsign), $callbook['email'] ?? ''); + $return['qsl_manager'] = $this->nval($this->logbook_model->call_qslvia($callsign), $callbook['qslmgr'] ?? ''); + $return['callsign_state'] = $this->nval($this->logbook_model->call_state($callsign), $callbook['state'] ?? ''); + $return['callsign_us_county'] = $this->nval($this->logbook_model->call_us_county($callsign), $callbook['us_county'] ?? ''); + $return['callsign_ituz'] = $this->nval($this->logbook_model->call_ituzone($callsign), $callbook['ituz'] ?? ''); + $return['callsign_cqz'] = $this->nval($this->logbook_model->call_cqzone($callsign), $callbook['cqz'] ?? ''); $return['workedBefore'] = $this->worked_grid_before($return['callsign_qra'], $band, $mode); - $return['confirmed'] = $this->confirmed_grid_before($return['callsign_qra'], $band, $mode); - $return['timesWorked'] = $this->logbook_model->times_worked($lookupcall); + $return['confirmed'] = $this->confirmed_grid_before($return['callsign_qra'], $band, $mode); + $return['timesWorked'] = $this->logbook_model->times_worked($lookupcall); if ($this->session->userdata('user_show_profile_image')) { if (isset($callbook) && isset($callbook['image'])) { @@ -188,6 +192,31 @@ class Logbook extends CI_Controller { } else { $return['image'] = "n/a"; } + + // Additional profile information from QRZ + $return['profile_url'] = $callbook['url'] ?? ''; + $return['profile_class'] = $callbook['class'] ?? ''; + $return['profile_born'] = $callbook['born'] ?? ''; + $return['profile_eqsl'] = $callbook['eqsl'] ?? ''; + $return['profile_lotw'] = $callbook['lotw'] ?? ''; + $return['profile_mqsl'] = $callbook['mqsl'] ?? ''; + $return['profile_fname'] = $callbook['fname'] ?? ''; + $return['profile_name_last'] = $callbook['name_last'] ?? ''; + $return['profile_nickname'] = $callbook['nickname'] ?? ''; + $return['profile_aliases'] = $callbook['aliases'] ?? ''; + $return['profile_p_call'] = $callbook['p_call'] ?? ''; + $return['profile_addr1'] = $callbook['addr1'] ?? ''; + $return['profile_addr2'] = $callbook['addr2'] ?? ''; + $return['profile_state'] = $callbook['state'] ?? ''; + $return['profile_zip'] = $callbook['zip'] ?? ''; + $return['profile_country'] = $callbook['country'] ?? ''; + $return['profile_dxcc'] = $callbook['dxcc'] ?? ''; + $return['profile_lat'] = $callbook['lat'] ?? ''; + $return['profile_lon'] = $callbook['lon'] ?? ''; + $return['profile_efdate'] = $callbook['efdate'] ?? ''; + $return['profile_expdate'] = $callbook['expdate'] ?? ''; + $return['profile_GMTOffset'] = $callbook['GMTOffset'] ?? ''; + $return['profile_qslmgr'] = $callbook['qslmgr'] ?? ''; } if ($return['callsign_qra'] != "" || $return['callsign_qra'] != null) { diff --git a/application/libraries/Qrz.php b/application/libraries/Qrz.php index 3e8d5f21c..a5c237dcc 100644 --- a/application/libraries/Qrz.php +++ b/application/libraries/Qrz.php @@ -91,60 +91,99 @@ class Qrz { return $data['error'] = $xml->Session->Error; } - // Return Required Fields + // Map all QRZ XML fields according to API specification $data['callsign'] = (string)$xml->Callsign->call; - - if ($use_fullname === true) { - $data['name'] = (string)$xml->Callsign->fname. ' ' . (string)$xml->Callsign->name; - } else { - $data['name'] = (string)$xml->Callsign->fname; - } - + $data['xref'] = (string)$xml->Callsign->xref; + $data['aliases'] = (string)$xml->Callsign->aliases; + $data['dxcc'] = (string)$xml->Callsign->dxcc; + $data['fname'] = (string)$xml->Callsign->fname; + $data['name_last'] = (string)$xml->Callsign->name; + $data['addr1'] = (string)$xml->Callsign->addr1; + $data['addr2'] = (string)$xml->Callsign->addr2; + $data['state'] = (string)$xml->Callsign->state; + $data['zip'] = (string)$xml->Callsign->zip; + $data['country'] = (string)$xml->Callsign->country; + $data['ccode'] = (string)$xml->Callsign->ccode; + $data['lat'] = (string)$xml->Callsign->lat; + $data['lon'] = (string)$xml->Callsign->lon; + $data['grid'] = (string)$xml->Callsign->grid; + $data['county'] = (string)$xml->Callsign->county; + $data['fips'] = (string)$xml->Callsign->fips; + $data['land'] = (string)$xml->Callsign->land; + $data['efdate'] = (string)$xml->Callsign->efdate; + $data['expdate'] = (string)$xml->Callsign->expdate; + $data['p_call'] = (string)$xml->Callsign->p_call; + $data['class'] = (string)$xml->Callsign->class; + $data['codes'] = (string)$xml->Callsign->codes; + $data['qslmgr'] = (string)$xml->Callsign->qslmgr; $data['email'] = (string)$xml->Callsign->email; + $data['url'] = (string)$xml->Callsign->url; + $data['u_views'] = (string)$xml->Callsign->u_views; + $data['bio'] = (string)$xml->Callsign->bio; + $data['biodate'] = (string)$xml->Callsign->biodate; + $data['image'] = (string)$xml->Callsign->image; + $data['imageinfo'] = (string)$xml->Callsign->imageinfo; + $data['serial'] = (string)$xml->Callsign->serial; + $data['moddate'] = (string)$xml->Callsign->moddate; + $data['MSA'] = (string)$xml->Callsign->MSA; + $data['AreaCode'] = (string)$xml->Callsign->AreaCode; + $data['TimeZone'] = (string)$xml->Callsign->TimeZone; + $data['GMTOffset'] = (string)$xml->Callsign->GMTOffset; + $data['DST'] = (string)$xml->Callsign->DST; + $data['eqsl'] = (string)$xml->Callsign->eqsl; + $data['mqsl'] = (string)$xml->Callsign->mqsl; + $data['cqzone'] = (string)$xml->Callsign->cqzone; + $data['ituzone'] = (string)$xml->Callsign->ituzone; + $data['born'] = (string)$xml->Callsign->born; + $data['user'] = (string)$xml->Callsign->user; + $data['lotw'] = (string)$xml->Callsign->lotw; + $data['iota'] = (string)$xml->Callsign->iota; + $data['geoloc'] = (string)$xml->Callsign->geoloc; + $data['attn'] = (string)$xml->Callsign->attn; + $data['nickname'] = (string)$xml->Callsign->nickname; + $data['name_fmt'] = (string)$xml->Callsign->name_fmt; + // Build legacy 'name' field for backward compatibility + if ($use_fullname === true) { + $data['name'] = $data['fname']. ' ' . $data['name_last']; + } else { + $data['name'] = $data['fname']; + } // we always give back the name, no matter if reduced data or not $data['name'] = trim($data['name']); // Sanitize gridsquare to allow only up to 8 characters - $unclean_gridsquare = (string)$xml->Callsign->grid; // Get the gridsquare from QRZ convert to string - $clean_gridsquare = strlen($unclean_gridsquare) > 8 ? substr($unclean_gridsquare,0,8) : $unclean_gridsquare; // Trim gridsquare to 8 characters max + $unclean_gridsquare = $data['grid']; + $clean_gridsquare = strlen($unclean_gridsquare) > 8 ? substr($unclean_gridsquare,0,8) : $unclean_gridsquare; - if ($reduced == false) { - - $data['gridsquare'] = $clean_gridsquare; - $data['geoloc'] = (string)$xml->Callsign->geoloc; - $data['city'] = (string)$xml->Callsign->addr2; - $data['lat'] = (string)$xml->Callsign->lat; - $data['long'] = (string)$xml->Callsign->lon; - $data['dxcc'] = (string)$xml->Callsign->dxcc; - $data['state'] = (string)$xml->Callsign->state; - $data['iota'] = (string)$xml->Callsign->iota; - $data['qslmgr'] = (string)$xml->Callsign->qslmgr; - $data['image'] = (string)$xml->Callsign->image; - $data['ituz'] = (string)$xml->Callsign->ituzone; - $data['cqz'] = (string)$xml->Callsign->cqzone; - - if ($xml->Callsign->country == "United States") { - $data['us_county'] = (string)$xml->Callsign->county; - } else { - $data['us_county'] = null; - } + // Map fields for backward compatibility with existing code + $data['gridsquare'] = $clean_gridsquare; + $data['city'] = $data['addr2']; + $data['long'] = $data['lon']; + $data['ituz'] = $data['ituzone']; + $data['cqz'] = $data['cqzone']; + if ($data['country'] == "United States") { + $data['us_county'] = $data['county']; } else { + $data['us_county'] = null; + } + if ($reduced == true) { + // Clear location-specific fields for reduced mode $data['gridsquare'] = ''; $data['city'] = ''; $data['lat'] = ''; $data['long'] = ''; + $data['lon'] = ''; $data['dxcc'] = ''; $data['state'] = ''; $data['iota'] = ''; - $data['qslmgr'] = (string)$xml->Callsign->qslmgr; - $data['image'] = (string)$xml->Callsign->image; $data['us_county'] = ''; $data['ituz'] = ''; $data['cqz'] = ''; - + $data['ituzone'] = ''; + $data['cqzone'] = ''; } } finally { diff --git a/application/views/interface_assets/footer.php b/application/views/interface_assets/footer.php index e0857360a..c06cdb7aa 100644 --- a/application/views/interface_assets/footer.php +++ b/application/views/interface_assets/footer.php @@ -93,6 +93,23 @@ var lang_qso_lookup_summit_info = ""; var lang_qso_lookup_reference_info = ""; var lang_qso_error_loading_bearing = ""; + var lang_qso_profile_aliases = ""; + var lang_qso_profile_previously = ""; + var lang_qso_profile_born = ""; + var lang_qso_profile_years_old = ""; + var lang_qso_profile_license = ""; + var lang_qso_profile_from = ""; + var lang_qso_profile_years = ""; + var lang_qso_profile_expired_on = ""; + var lang_qso_profile_website = ""; + var lang_qso_profile_local_time = ""; + var lang_qso_profile_qsl = ""; + var lang_qso_profile_view_location_maps = ""; + var lang_qso_profile_license_novice = ""; + var lang_qso_profile_license_technician = ""; + var lang_qso_profile_license_general = ""; + var lang_qso_profile_license_advanced = ""; + var lang_qso_profile_license_extra = ""; var lang_qso_gridsquare_formatting = ""; var lang_qso_gridsquare_help = ""; diff --git a/application/views/qso/index.php b/application/views/qso/index.php index d97c8127c..977a3fda4 100644 --- a/application/views/qso/index.php +++ b/application/views/qso/index.php @@ -828,10 +828,19 @@ switch ($date_format) { session->userdata('user_show_profile_image')) { ?>