Support multiple comma-separated grids (VUCC) across SimpleFLE. Updated Logbook_model to accept vucc_grids and prefer it over single locator when present. Frontend changes: parse and detect single vs multiple grids, add Grid column to preview table, send both `locator` and `vucc_grids` to the save endpoint, tighten time regex, add qsotime variable, include X band in satellite mode handling, and clear satellite state when switching to a regular band. Documentation and help updated: improved syntax help and added full docs/SimpleFLE.md explaining grids, VUCC usage, satellite handling and other SimpleFLE features.
Introduce an HTMX-driven continents results component and refactor the continents page/JS to load/filter results dynamically. Added Continents::component_continent_results() and a new view (continents/component_results.php) which renders the chart/table container and exposes filter params via data attributes. Updated application/views/continents/index.php to use a filter card with an HTMX-enabled form that posts to the new component endpoint and injects results into #continentResults. Reworked assets/js/sections/continents.js to centralize AJAX logic into continentsRender(), add setContinentsLoading(), handle HTMX afterSwap to initialize results, improve error/info messaging, and implement a JS reset button that triggers an HTMX submit. Overall this enables incremental loading of continent stats and simplifies the previous full-form AJAX flow.
Introduce an HTMX-powered results component and refactor Timeplotter flow: add component_timeplot_results controller action that supplies band/dxcc/cqzone defaults and renders a new timeplotter/component_results view. Revamp the index view to use Bootstrap cards, a filter form wired to HTMX (hx-post/hx-target/hx-trigger) and a results container that auto-loads on page load. Refactor assets/js/sections/timeplot.js: separate loading state, implement timeplotRender/timeplot helper functions, improve AJAX error handling, and add renderTimeplotFromComponent + htmx:afterSwap hook to initialize charts when the component is swapped in. Also update getTimes controller to rely on the model to output JSON directly.
Introduce a new partial view and controller endpoint for rendering accumulated-results as an HTMX component, and refactor the accumulated statistics page and JS. Changes include: adding component_accumulated_results() in the Accumulated controller and a new view (accumulate/component_results.php) exposing filter params via data-attributes; updating accumulate/index.php to use a card-based layout, improved form markup, and HTMX attributes to load the results component; and a major rewrite of assets/js/sections/accumulatedstatistics.js to modularize logic (getAwardText, setAccumulateLoading, accumulateRender), improve loading/error handling, rebuild chart/table markup, update DataTables usage, and hook into htmx:afterSwap to render the chart when the component is swapped in. Overall this enables partial updates, better UX, and cleaner client-side code.
Change htmx.ajax swap option from 'outerHTML' to 'innerHTML' when updating the #qso-last-table. This ensures only the container's contents are replaced (preserving the container element and any bindings/listeners) and avoids DOM reattachment issues.
Add an optional preserveDxccState parameter to resetDefaultQSOFields to keep DXCC-related UI state (country, callsign_info text/title/class, dxcc_id, CQ zone, ITU zone) when requested, preventing badge/country flicker. Update the callsign focusout flow to use this option. Also remove unnecessary .trigger('change') calls on dxcc_id assignments to avoid redundant change events and adjust field clearing/restoration order accordingly.
Add a cached DXCC lookup and a jsondxcc endpoint to speed up country resolution and reduce DB load (file-cache with 1h TTL). Introduce cached_dxcc_lookup(), build_confirmation_where(), and callsign_status() in the Logbook controller; refactor confirmed_grid_before() and worked_grid_before() to accept logbook relationships and confirmation prefs to avoid redundant queries. Use a new Logbook_model::get_recent_callsign_details() to populate recent name/gridsquare/qth/iota/qsl/state/county values in a single query. Update JS to better manage lookup state, invalidate in-flight requests, and perform a debounced quick DXCC lookup to improve UX. Tweak previous-contacts pagination to a compact sliding window and remove a redundant UI note.
Prevent the logbook auto-refresh from overwriting callsign lookup results by introducing a previousContactsLookupMode flag and pauseAutoRefresh()/resumeAutoRefresh() helpers that remove/restore the htmx hx-trigger on the qso list element. Clear and hide partial_view on reset and resume auto-refresh there. Show an explicit "no past QSOs" info message when a callsign is found but has no previous QSOs, and ensure the previous-contacts panel state is set deterministically. Also harden CAT radioID checking to treat undefined and falsy values as invalid. Changes touch qso.js and the footer view.
Treat lookup.partial defensively (ensure it's a string) before injecting into #partial_view, and determine whether to show previous-contacts panel based on trimmed content instead of always enabling it. Clear #partial_view when hiding the panel to avoid stale content, and simplify the htmx init check to rely on the presence of content rather than visibility. These changes prevent accidental HTML insertion and make panel toggling deterministic.
Hide the #partial_view container by default and only reveal the previous-contacts panel when it contains non-empty HTML. Invoke getDxccResult after injecting the partial result so the DXX summary is fetched and displayed. Add handling to reset QSO fields and switch back to the Previous Contacts tab when the callsign is cleared, and remove duplicated/unused code in the callsign focusout handler. This prevents showing an empty lookup panel and cleans up the logic around loading lookup details.
After saving a QSO, switch to the QSP tab if present (using bootstrap.Tab.show()). Enhance reset_fields() to clear wwff and pota info (HTML and title) and safely clear the sota_ref Selectize control (if initialized) instead of using .val(""), preventing leftover values/tooltips and avoiding errors when Selectize isn't present.
Add setPreviousContactsPanelState(showLookupDetails) to centralize show/hide logic for #qso-last-table, its trailing <small>, and #partial_view. Replace inline show/hide calls with this helper, update resetToPreviousContactsTab to use it, and add an htmx:afterSwap listener to reapply the visibility state after HTMX updates the previous contacts markup. This ensures the lookup details and default previous-contacts table remain consistent across dynamic updates.
Add a suppressNextResetHandler flag to avoid running the reset event handler when the QSO form is reset programmatically (set the flag before calling qsoFormElement.reset()). Introduce clearCatTrackedFieldState() to remove stored 'catValue' data from frequency/satellite/mode/power fields and call it from reset_fields(). Also initialize suppressNextResetHandler and short-circuit the '#qso_input' reset handler when the flag is set to prevent unintended UI side effects.
Add request/version tracking to CAT JSON handling to avoid applying stale responses when radio selection changes. Introduce catRequestCounter, lastProcessedCatRequest and catSelectionContextVersion, compare request IDs and context version before updating UI, and increment context version on radio selection change. Also add a syncFromSelectedRadioAfterReset helper in qso.js (with a throttled user notice) and call it after form reset and on escape-based reset so the form prefers live CAT values from the selected radio.
Before resetting the QSO form, capture the user's current operating context (band, mode, satellite name/mode, propagation). After qsoForm.reset() and the built-in reset handlers run, reapply those captured values (with a 150ms timeout) so the UI doesn't revert to server-session defaults or the previous QSO values. Also call setRst(mode) if available to update RST for the restored mode.
Ensure satellite fields are cleared whenever propagation mode is not 'SAT' and when the QSO form is reset. In Qso.php, sat_name and sat_mode are set to empty strings if prop_mode != 'SAT' (added in two handling points). In assets/js/sections/qso.js, added clearSatelliteFields() to clear sat_name, sat_mode, satellite_modes_list and related state, call it from reset_fields(), and invoke the form reset flow to ensure no stale satellite data remains after resetting or changing propagation mode.
When a QSO is saved, preserve the selected mode, satellite name, and satellite mode so the user doesn't have to reselect them for the next entry. Capture savedMode, savedSatName, and savedSatMode into postSaveDefaults and call a new reapplyPostSaveDefaults() after reset_fields(). Added reapplyPostSaveDefaults() to restore band, mode, sat_name, sat_mode and call setRst() if available.
When resetting QSO fields, reapply the default RST for the current mode. The change calls setRst($('.mode').val()) if the setRst function exists, ensuring mode-specific defaults (e.g., CW => 599) are restored after a form reset to avoid stale RST values.
Add escapeNoticeValue to sanitize user-provided strings (&, <, >, ", ') to prevent XSS in notice messages. Use a new savedBand variable and update saveMessage assembly to include both callsign and band when available, with fallbacks for when only one is present. Changes are in assets/js/sections/qso.js.
Introduce an AJAX-based QSO save flow: add QSO::ajax_saveqso() in the controller to validate input, set session/cookies, create the QSO and return JSON success/error payloads. Update the QSO view to include a data-ajax-save-url on the form and add a notice-alerts container location. Enhance the qso.js client: add isSubmitting guard, showQsoNotice helper, intercept form submit to POST serialized form data to the AJAX endpoint, handle success (reset form, show notice, refresh recent QSOs via htmx), display validation/server errors, and restore UI state when complete. These changes avoid full page reloads on save and provide inline validation feedback.
Introduce a frontend QRA utilities module and switch QSO locator/bearing/distance logic to run client-side. Added assets/js/qra-utils.js (ported from the PHP Qra library), injected measurement_base and station_gridsquares into the QSO view, and updated application/controllers/Qso.php to provide the measurement_base for the frontend. Footer now loads qra-utils.js before qso.js. Modified assets/js/sections/qso.js to debounce locator input, place map markers from local grid→lat/lng math, and compute bearing/distance via QraUtils (removes several AJAX calls to server endpoints). This reduces server round-trips and keeps frontend behavior consistent with the PHP implementation.
Add/adjust known FT8/FT4 spot frequencies and tighten band mode detection ranges. New FT8/FT4 frequencies were added across multiple bands (160m, 80m, 30m, 20m, 17m, 15m, 12m, 10m, 6m, etc.). Mode boundary logic was refined for 160m, 80m, 60m (added), 40m, 6m, 4m, 2m and 70cm to better reflect common CW/DIGI/SSB allocations and specific FT8 spots. These changes improve automatic mode detection from frequency values.
Add UI controls for mode and "New DXCC" filtering to the DX Cluster and QSO views, persist selections in localStorage, and wire them into existing spot filtering and rendering logic (including auto-unhide of RBN when CW is selected). Extract shared helper functions into a new assets/js/dxcluster-utils.js (DXCluster) providing isRbnSpot, getBandFromFrequency, detectModeFromFrequency, and detectMode, and update views to use these helpers. Also adjust badge logic to prioritize truly new DXCC, include the new script, and ensure spots are held until worked status is known for new/DXCC-band filtering.
Replace jQuery $(function() {}) with document.addEventListener('DOMContentLoaded') in the QSO view and harden selectize usage in assets/js/sections/qso.js. Add existence checks (for elements and selectize instances) before accessing .selectize to avoid null/undefined dereferences, and consolidate selectize clearing logic. These changes improve robustness when selectize or jQuery readiness assumptions aren't met and prevent runtime errors.
Always render the DOK field wrapper but mark it with data-user-hidden when the field is disabled for the user, and make toggleDokField skip showing the field in that case. Add defensive checks around Selectize accesses in assets/js/sections/qso.js (ensure $select[0] exists and dok_selectize is non-null before calling methods) to avoid JS errors when the DOK input is absent or not initialized. Changes touch application/views/qso/index.php and assets/js/sections/qso.js to prevent runtime errors and correctly honor user-hidden DOK configuration.
Adjust indentation in assets/js/sections/qso.js so the $('#dxcc_id').val(...) line is properly nested within the surrounding block. This is a whitespace/readability fix only and introduces no functional change.
Call toggleDokField()/toggleUsaFields() on jQuery ready and ensure programmatic updates to #dxcc_id fire change handlers. Replaced the previous DOMContentLoaded init with a jQuery $(function()) call to initialise the toggle state, added .trigger('change') where #dxcc_id is set programmatically, and invoke the toggle functions from the dxcc change handler to keep UI fields in sync.
Move the small badge elements (callsign_info, locator_info_contest_dxcc, distance_contest_dxcc) out of the callsign input column and into a new row below the distance field (div#callsign-badge-row) with a min-height to preserve layout. In contesting.js, replace table.columns.adjust().draw(false) with table.draw(false) in updateTableColumns to avoid an extra column adjustment and reduce redraw overhead.
Rework contest QSO form layout and add collapsible panels for session settings and extra fields (name/comment/copy-exchange). Add prominent reset/save buttons, inline chevrons to indicate collapse state, and a logbook search input. In assets/js/sections/contesting.js ensure DataTables are destroyed before DOM updates, initialize DataTable with a compact dom, preserve initial search from the new logbook search field, add a keyup handler to drive table filtering, and fix column render targets/padding. Also remove a redundant search/order call after logging a QSO to avoid duplicate operations.
Add a contest stats card to the contesting view and implement client-side logic to compute and display session QSOs, per-band counts, and recent rate. Hide the callsign suggestion box by default and show/hide it based on lookup results; add a debounced duplicate-worked check while typing and visual success/error styling on the callsign input. Mark duplicate QSOs in the table (table-warning) by counting identical call|band|mode entries, and add updateTableColumns/updateContestStats helpers to toggle DataTable columns by exchange type and refresh contest stats when the QSO table is rebuilt. Also include small resets to clear suggestion UI and styling when fields are reset.
Bump migration version to 262 and add Migration_tag_2_8_10 which updates the stored app version to 2.8.10 and forces the version info dialog by resetting the user option. The migration includes a down() to revert the version to 2.8.9. Also update L.Terminator.js to generate three longitude-shifted polygon copies (offsets -360, 0, +360) so the night/day terminator overlay tiles correctly across world copies when the map is zoomed out.
Add a full Cabrillo export workflow and harden Cabrillo/QSO formatting. Introduces a modal UI to export contest logs (new button + modal form with fields for location, category time, operators, club, soapbox, date range and other Cabrillo categories). Controller updates pass the new fields to the export action. Cabrilloformat library extended to accept and emit LOCATION and CATEGORY-TIME, improve header field ordering and presence checks, map ADIF modes to the five Cabrillo modes (CW/PH/FM/RY/DG), fix a band label (2.4G -> 2.3G), and emit placeholders for missing received exchanges to preserve column alignment. Contesting_model: more robust date parsing with UTC fallback, ensure session QSO marker only persists when timestamp valid, and build start timestamp when LIVE mode omits start_date/start_time. Frontend JS: setSession() now returns the ajax promise so callers can await it; several callers updated to await setSession and re-fetch session data before refreshing the QSO table; restore full table search on callsign blur and when suggestions are cleared. Misc: small form/input fixes (club field type, default overlay option) and additional server-supplied data loaded into the contesting view (active station id, contest session, station profile). These changes add required Cabrillo fields for certain contests and make exports and session handling more reliable.
Adjust terminator/greyline visual style and default options. Changed colors to #4a6fa5 (stroke) and #001f3f (fill), increased stroke weight to 1.5, lowered fillOpacity to 0.18, and changed resolution from 2 to 1. These edits were applied both where the greyline is created in the footer view and in the L.Terminator default options to keep behavior and appearance consistent.
Add a day/night greyline (terminator) overlay to the dashboard map.
- New assets/js/leaflet/L.Terminator.js: a Leaflet polygon plugin that computes the solar terminator for a given time and exposes L.terminator().
- Load the terminator script in the footer and register a Greyline overlay layer with start/stop interval updates (refreshes every minute). Updates are managed when the overlay is added/removed or when the map unloads to avoid orphaned timers.
- Expose the greyline as a toggleable overlay in the map layer control and add markers to a dedicated markersLayer overlay (instead of adding them directly to the map).
- Add a user preference checkbox to application/views/user/edit.php (Enable Dashboard Map Greyline Layer) and wire saving/reading of the dashboard_map_greyline option in application/controllers/User.php and application/controllers/Dashboard.php. Default behavior is enabled when not set.
This change lets users enable/disable the visual day/night terminator on the dashboard map and ensures proper lifecycle handling for updates and marker layering.
Implement server-side pagination for previous contacts and add a DXCC Summary tab. Controller Qso now uses last_custom_paginated and exposes total_rows/total_pages/current_page/limit. Logbook_model gained last_custom_paginated and last_custom_count to support paged queries. The previous_contacts view was updated with HTMX pagination controls and wrapping for a scrollable table; qso/index was refactored to a tabbed UI (Previous Contacts / DXCC Summary). Lookup result display was improved: responsive table, sticky header, consolidation of LSB/USB into SSB and filtering out empty modes. JS and CSS updates load DXCC HTML into the new tab, hide legacy dxccsummary elements, and add form reset / Escape handling to return focus to the previous contacts tab.
Add normalization helpers and update callhistory renderers to avoid showing redundant membership numbers or names when they are contained in or equal to other fields. Introduces normalizeCallhistoryText and qsoCallhistoryNormalizeText, converts values to strings, trims and lowercases them for comparisons, and only appends #exch1 or name when they would be distinct from the organization label or each other. Applies the change to assets/js/sections/contesting.js and assets/js/sections/qso.js to clean up callhistory display logic.
Introduce a new Call History feature: adds Callhistory controller, Callhistory_model, migration (create callhistory_files table) and view for uploading/managing call history files. Implements CSV/TXT upload handling, storage per-user, activation/priority controls, checksum, and server-side lookup that scans active files for matching callsigns. Integrates UI: header menu link, contesting and QSO views show call history results inline, and JavaScript to perform lookups and render results with copy-to-SIG functionality. Also updates .gitignore to exclude uploaded callhistory directory and bumps migration_version to 261.
Set deterministic tab sequence for contest logging fields by exchange type, start focus on callsign, and end navigation at Save QSO for faster keyboard entry.
Enable createOnBlur and selectOnTab on selectize instances for SOTA, WWFF and POTA reference fields in both the QSO form and the QSO edit dialog (assets/js/sections/qso.js and assets/js/sections/common.js). This improves UX by allowing new entries to be created on blur and enabling selection with the Tab key.
Keep callsign lookup retriggered on correction while preventing silent data loss.
Add a Bootstrap modal that lists conflicting fields and allows keep existing, replace selected, or replace all.
Fix stale lookup race handling with request IDs and correct callsign comparison guard to avoid assignment bug.
Make callsign-change resets non-destructive for user-entered fields while still refreshing DXCC/metadata and past-QSO context.
Mark certain dropdowns (Gridmaster, Other Export Options, Third Party Logbooks) with a new dropdown-submenu-left class in header.php so submenus can open to the left. Update general.css to style .dropdown-submenu and .dropdown-submenu-left positioning, refine .navbar .dropdown-menu appearance, and improve dropdown item spacing, hover effects and submenu chevron styling for better alignment and visual consistency (prevents overflow and improves UX).
Introduces band, mode, and confirmed-only filters to the Worked All Britain (WAB) award view and details AJAX endpoint. Updates the controller, model, and view logic to support filtering, and adds new localized strings for WAB award UI elements in multiple languages. Also ensures the correct script is loaded for the WAB section and improves the Bands model to check for field existence before filtering by award.
Moved the comment reset to the beginning of each line iteration in handleInput(), ensuring comments are cleared before processing each row. This prevents comments from persisting across lines and fixes potential data carryover issues.
Introduces the ability to add comments to QSOs by enclosing text in angle brackets, updates the UI to display a new Comment column, and documents the feature in the syntax help. The JavaScript logic is updated to extract, display, and save comments with each QSO.