Merge branch 'dev-test' into 'master'

Dev test

Closes #150, #118, #95, and #9

See merge request gridtracker.org/gridtracker!179

If this is changing anything in the UI or operational behavior, please prepare to update the wiki!
merge-requests/180/merge v1.22.0725
Matthew Chambers 2022-07-24 19:56:21 +00:00
commit c1f7f1be41
17 zmienionych plików z 491 dodań i 53 usunięć

Wyświetl plik

@ -107,19 +107,3 @@ Final build results are left in:
# Editing GeoJSON files # Editing GeoJSON files
We've had success using https://vector.rocks/ and then cleaning up the output with https://jsonformatter.org/ We've had success using https://vector.rocks/ and then cleaning up the output with https://jsonformatter.org/
# Hacks
### Roster Column Ordering
We've added internal support for reordering roster columns, but have yet to implement a UI to change these settings.
In the meantime you can:
* Open the roster window, right click on the "More Controls" link on the top right corner and select "Inspect" from the context menu.
* Select the "Console" tab in the Chrome DevTools window that should have appeared.
* Enter `g_rosterSettings.columnOrder` in the Console and press `[return]` to see the current list of columns.
* Enter the following in the Console, changing the values of `columnOrder` to fit your needs: `changeRosterColumnOrder(["Callsign", "Grid", "Spot"]);` and press `[return]`.
Any columns included in this list will be shown before all other columns.

11
debian/changelog vendored
Wyświetl plik

@ -1,3 +1,14 @@
gridtracker (1.22.0725) unstable; urgency=low
- Resolved #9 Call roster columns order can be changed
- Resolved $95 Puts calling/called stations at the top of the call roster if sorting by Wanted
- Resolved #118 Introduce POTA hunting in the call roster
- Resolved #133 Fixes missing CloudLog Station Profile ID
- Resolved #150 Highlights RR73/73 the same as a station calling CQ
- Fixes pattern match for US 1x1 callsigns to match actual FCC rules around them.
- Add WSJT-X/JTDX active instance name to roster window title when operating with multiple instances.
-- Matthew Chambers <nr0q@gridtracker.org> Sun, 24 Jul 2022 19:05:00 -0000
gridtracker (1.22.0503) unstable; urgency=low gridtracker (1.22.0503) unstable; urgency=low
- Increment version for build with correct NWJS version - Increment version for build with correct NWJS version

Wyświetl plik

@ -1,6 +1,6 @@
Name: {{{ git_name name=gridtracker }}} Name: {{{ git_name name=gridtracker }}}
Summary: GridTracker: An amateur radio companion to WSJT-X or JTDX Summary: GridTracker: An amateur radio companion to WSJT-X or JTDX
Version: {{{ git_version lead=1.22.0503 }}} Version: {{{ git_version lead=1.22.0725 }}}
Release: 1%{?dist} Release: 1%{?dist}
BuildArch: noarch BuildArch: noarch
Source0: {{{ git_dir_pack }}} Source0: {{{ git_dir_pack }}}
@ -40,6 +40,14 @@ DESTDIR=${RPM_BUILD_ROOT} make clean
%license %{_docdir}/%{name}/ %license %{_docdir}/%{name}/
%changelog %changelog
* Sun Jul 24 2022 Matthew Chambers <nr0q@gridtracker.org> - 1.22.0725-1
- Resolved #9 Call roster columns order can be changed
- Resolved $95 Puts calling/called stations at the top of the call roster if sorting by Wanted
- Resolved #118 Introduce POTA hunting in the call roster
- Resolved #133 Fixes missing CloudLog Station Profile ID
- Resolved #150 Highlights RR73/73 the same as a station calling CQ
- Fixes pattern match for US 1x1 callsigns to match actual FCC rules around them.
- Add WSJT-X/JTDX active instance name to roster window title when operating with multiple instances.
* Mon May 02 2022 Matthew Chambers <nr0q@gridtracker.org> - 1.22.0503-1 * Mon May 02 2022 Matthew Chambers <nr0q@gridtracker.org> - 1.22.0503-1
- Increment version number for build with correct vesion of NWJS - Increment version number for build with correct vesion of NWJS
* Mon May 02 2022 Matthew Chambers <nr0q@gridtracker.org> - 1.22.0502-1 * Mon May 02 2022 Matthew Chambers <nr0q@gridtracker.org> - 1.22.0502-1

Wyświetl plik

@ -48,8 +48,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
<script src="./lib/callsigns.js" type="text/javascript"></script> <script src="./lib/callsigns.js" type="text/javascript"></script>
<script src="./lib/shadow.js" type="text/javascript"></script> <script src="./lib/shadow.js" type="text/javascript"></script>
<script src="./lib/gtws.js" type="text/javascript"></script> <script src="./lib/gtws.js" type="text/javascript"></script>
<script src="./lib/pota.js" type="text/javascript"></script>
<script src="./lib/gt.js" type="text/javascript"></script> <script src="./lib/gt.js" type="text/javascript"></script>
<script src="./lib/screens.js"></script> <script src="./lib/screens.js" type="text/javascript"></script>
</head> </head>
<body id="mainBody" onload="initialDatabases();"> <body id="mainBody" onload="initialDatabases();">
<div id="startupDiv"> <div id="startupDiv">

Wyświetl plik

@ -172,6 +172,11 @@
<div> <div>
<label><input type="checkbox" id="huntPX" onchange="wantedChanged(this);" /> WPX</label> <label><input type="checkbox" id="huntPX" onchange="wantedChanged(this);" /> WPX</label>
</div> </div>
<div>
<label title="Parks On The Air">
<input type="checkbox" id="huntPOTA" onchange="wantedChanged(this);" /> POTA
</label>
</div>
<div> <div>
<label title="Off-Air Message Service Users"> <label title="Off-Air Message Service Users">
<input type="checkbox" id="huntOAMS" onchange="wantedChanged(this);" /> OAMS <input type="checkbox" id="huntOAMS" onchange="wantedChanged(this);" /> OAMS
@ -184,6 +189,9 @@
<div> <div>
<label><input type="checkbox" id="huntITUz" onchange="wantedChanged(this);" /> ITUz</label> <label><input type="checkbox" id="huntITUz" onchange="wantedChanged(this);" /> ITUz</label>
</div> </div>
<div>
<label title='CQ DX Marathon'><input type="checkbox" id="huntMarathon" onchange="wantedChanged(this);" /> Marathon</label>
</div>
<div> <div>
<label><input type="checkbox" id="huntState" onchange="wantedChanged(this);" /> State</label> <label><input type="checkbox" id="huntState" onchange="wantedChanged(this);" /> State</label>
</div> </div>

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -1065,6 +1065,10 @@ function addDeDx(
finalSatName = "" finalSatName = ""
) )
{ {
var currentYear = new Date().getFullYear();
var qsoDate = new Date(1970, 0, 1); qsoDate.setSeconds(finalTime);
var isCurrentYear = (qsoDate.getFullYear() == currentYear);
var callsign = null; var callsign = null;
var rect = null; var rect = null;
var worked = false; var worked = false;
@ -1262,6 +1266,10 @@ function addDeDx(
g_tracker.worked.cqz[details.cqz + "dg"] = true; g_tracker.worked.cqz[details.cqz + "dg"] = true;
g_tracker.worked.cqz[details.cqz + band + "dg"] = true; g_tracker.worked.cqz[details.cqz + band + "dg"] = true;
} }
if (isCurrentYear)
{
g_tracker.worked.cqz[`${details.cqz}-${currentYear}`] = true;
}
} }
if (details.dxcc > 0) if (details.dxcc > 0)
@ -1276,6 +1284,10 @@ function addDeDx(
g_tracker.worked.dxcc[sDXCC + "dg"] = true; g_tracker.worked.dxcc[sDXCC + "dg"] = true;
g_tracker.worked.dxcc[sDXCC + band + "dg"] = true; g_tracker.worked.dxcc[sDXCC + band + "dg"] = true;
} }
if (isCurrentYear)
{
g_tracker.worked.dxcc[`${sDXCC}-${currentYear}`] = true;
}
} }
if (details.px) if (details.px)
@ -6945,7 +6957,6 @@ function handleWsjtxDecode(newMessage)
theTimeStamp = theTimeStamp =
timeNowSec() - (timeNowSec() % 86400) + parseInt(newMessage.TM / 1000); timeNowSec() - (timeNowSec() % 86400) + parseInt(newMessage.TM / 1000);
var messageColor = "white"; var messageColor = "white";
if (CQ == true) messageColor = "cyan";
// Break up the decoded message // Break up the decoded message
var decodeWords = newMessage.Msg.split(" ").slice(0, 5); var decodeWords = newMessage.Msg.split(" ").slice(0, 5);
@ -6991,6 +7002,7 @@ function handleWsjtxDecode(newMessage)
CQ = true; CQ = true;
msgDXcallsign = "CQ"; msgDXcallsign = "CQ";
} }
if (decodeWords.length == 4 && CQ == true) if (decodeWords.length == 4 && CQ == true)
{ {
msgDXcallsign += " " + decodeWords[1]; msgDXcallsign += " " + decodeWords[1];
@ -7011,6 +7023,12 @@ function handleWsjtxDecode(newMessage)
msgDEcallsign = decodeWords[1]; msgDEcallsign = decodeWords[1];
} }
if (decodeWords[2] == "RR73")
{
CQ = true;
msgDXcallsign = "RR73";
}
var callsign = null; var callsign = null;
var hash = msgDEcallsign + newMessage.OB + newMessage.OM; var hash = msgDEcallsign + newMessage.OB + newMessage.OM;
@ -7079,6 +7097,7 @@ function handleWsjtxDecode(newMessage)
newCallsign.qso = false; newCallsign.qso = false;
newCallsign.dxcc = callsignToDxcc(newCallsign.DEcall); newCallsign.dxcc = callsignToDxcc(newCallsign.DEcall);
newCallsign.px = null; newCallsign.px = null;
newCallsign.pota = null;
newCallsign.zone = null; newCallsign.zone = null;
newCallsign.vucc_grids = []; newCallsign.vucc_grids = [];
newCallsign.propMode = ""; newCallsign.propMode = "";
@ -7209,6 +7228,11 @@ function handleWsjtxDecode(newMessage)
} }
} }
if (g_potaSpots && g_potaSpots.some(item => item.activator === callsign.DEcall))
{
callsign.pota = g_potaSpots.filter(item => item.activator === callsign.DEcall)[0];
}
if (newMessage.NW) if (newMessage.NW)
{ {
didCustomAlert = processAlertMessage( didCustomAlert = processAlertMessage(
@ -12763,7 +12787,8 @@ function getBuffer(file_url, callback, flag, mode, port, cache = null)
host: url.parse(file_url).host, // eslint-disable-line node/no-deprecated-api host: url.parse(file_url).host, // eslint-disable-line node/no-deprecated-api
port: port, port: port,
followAllRedirects: true, followAllRedirects: true,
path: url.parse(file_url).path // eslint-disable-line node/no-deprecated-api path: url.parse(file_url).path, // eslint-disable-line node/no-deprecated-api
headers: { "User-Agent": gtVersionString }
}; };
http.get(options, function (res) http.get(options, function (res)
@ -13741,7 +13766,9 @@ var g_startupTable = [
[loadLookupDetails, "Callsign Lookup Details Loaded"], [loadLookupDetails, "Callsign Lookup Details Loaded"],
[startupEventsAndTimers, "Set Events and Timers"], [startupEventsAndTimers, "Set Events and Timers"],
[registerHotKeys, "Registered Hotkeys"], [registerHotKeys, "Registered Hotkeys"],
[gtChatSystemInit, "User System Initialized"], [gtChatSystemInit, "Chat System Initialized"],
[getPotaPlaces, "Loading POTA Database"],
[getPotaSpots, "Starting POTA Spots Pump"],
[downloadAcknowledgements, "Contributor Acknowledgements Loaded"], [downloadAcknowledgements, "Contributor Acknowledgements Loaded"],
[postInit, "Finalizing System"] [postInit, "Finalizing System"]
]; ];

Wyświetl plik

@ -0,0 +1,67 @@
// GridTracker Copyright © 2022 GridTracker.org
// All rights reserved.
// See LICENSE for more information.
var g_potaPlaces = null;
var g_potaSpots = null;
function ingestPotaPlaces(buffer)
{
try
{
g_potaPlaces = JSON.parse(buffer);
}
catch (e)
{
// can't write, somethings broke
}
}
function getPotaPlaces()
{
if (g_mapSettings.offlineMode == false)
{
getBuffer(
"https://storage.googleapis.com/gt_app/pota.json",
ingestPotaPlaces,
null,
"https",
443
);
setTimeout(getPotaPlaces, 86400000)
}
}
function ingestPotaSpots(buffer)
{
try
{
g_potaSpots = JSON.parse(buffer);
}
catch (e)
{
// can't write, somethings broke
}
}
function getPotaSpots()
{
if (g_mapSettings.offlineMode == false && g_spotsEnabled == 1)
{
getBuffer(
"https://api.pota.app/spot/activator",
ingestPotaSpots,
null,
"https",
443
);
setTimeout(getPotaSpots, 300000);
}
}
function g_sendPotaSpot()
{
// if Pota spotting enabled, and we have enough info, send a spot to Pota
}

Wyświetl plik

@ -26,6 +26,8 @@ var g_callMenu = null;
var g_ageMenu = null; var g_ageMenu = null;
var g_callingMenu = null; var g_callingMenu = null;
var g_compactMenu = null; var g_compactMenu = null;
var g_menuItemForCurrentColumn = null;
var g_currentColumnName = null;
var g_targetHash = ""; var g_targetHash = "";
var g_clearIgnores = null; var g_clearIgnores = null;
var g_clearIgnoresCall = null; var g_clearIgnoresCall = null;
@ -96,10 +98,12 @@ var g_defaultSettings = {
huntDXCC: true, huntDXCC: true,
huntCQz: false, huntCQz: false,
huntITUz: false, huntITUz: false,
huntMarathon: false,
huntState: false, huntState: false,
huntCounty: false, huntCounty: false,
huntCont: false, huntCont: false,
huntPX: false, huntPX: false,
huntPOTA: false,
huntQRZ: true, huntQRZ: true,
huntOAMS: false huntOAMS: false
}, },
@ -112,6 +116,7 @@ var g_defaultSettings = {
Flag: true, Flag: true,
State: true, State: true,
County: true, County: true,
POTA: false,
Cont: true, Cont: true,
dB: true, dB: true,
Freq: false, Freq: false,
@ -1704,6 +1709,19 @@ function init()
item = new nw.MenuItem({ type: "separator" }); item = new nw.MenuItem({ type: "separator" });
g_menu.append(item); g_menu.append(item);
g_menuItemForCurrentColumn = new nw.MenuItem({
type: "normal",
label: "Move Column Left",
click: function ()
{
moveColumnLeft(g_currentColumnName);
}
})
g_menu.append(g_menuItemForCurrentColumn)
item = new nw.MenuItem({ type: "separator" });
g_menu.append(item);
for (let columnIndex in g_rosterSettings.columnOrder) for (let columnIndex in g_rosterSettings.columnOrder)
{ {
let key = g_rosterSettings.columnOrder[columnIndex]; let key = g_rosterSettings.columnOrder[columnIndex];
@ -2162,7 +2180,9 @@ function handleContextMenu(ev)
} }
} }
let name = ev.target.getAttribute("name"); let name
if (ev.target.tagName == "TD") name = ev.target.getAttribute("name");
if (name == "Callsign") if (name == "Callsign")
{ {
g_targetHash = ev.target.parentNode.id; g_targetHash = ev.target.parentNode.id;
@ -2200,13 +2220,24 @@ function handleContextMenu(ev)
} }
else else
{ {
if (g_rosterSettings.compact == false) if (g_rosterSettings.compact)
{ {
g_menu.popup(mouseX, mouseY); g_compactMenu.popup(mouseX, mouseY);
} }
else else
{ {
g_compactMenu.popup(mouseX, mouseY); if (ev.target.tagName == "TH" && ev.target.getAttribute("name"))
{
g_menuItemForCurrentColumn.enabled = true;
g_currentColumnName = ev.target.getAttribute("name");
}
else
{
g_menuItemForCurrentColumn.enabled = false;
g_currentColumnName = null;
}
g_menu.popup(mouseX, mouseY);
} }
} }
} }

Wyświetl plik

@ -45,6 +45,18 @@ function processRosterFiltering(callRoster, rosterSettings)
entry.tx = false; entry.tx = false;
continue; continue;
} }
if (entry.DXcall == "CQ POTA" && huntPOTA.checked == true)
{
entry.tx = true;
if (callObj.pota == null)
{
callObj.pota = {
reference: "?-????",
name: "Unknown Park"
}
}
continue;
}
if (callObj.ituza in g_blockedITUz) if (callObj.ituza in g_blockedITUz)
{ {
entry.tx = false; entry.tx = false;
@ -378,6 +390,12 @@ function processRosterFiltering(callRoster, rosterSettings)
} }
} }
} }
if (callObj.shouldAlert == false && rosterSettings.onlyHits == true && callObj.qrz == false)
{
tx = false
}
entry.tx = tx; entry.tx = tx;
} }
} }

Wyświetl plik

@ -13,6 +13,9 @@ function processRosterHunting(callRoster, rosterSettings)
let layeredUnconf = "background-clip:padding-box;box-shadow: 0 0 4px 2px inset "; let layeredUnconf = "background-clip:padding-box;box-shadow: 0 0 4px 2px inset ";
let layeredUnconfAlpha = "AA"; let layeredUnconfAlpha = "AA";
const currentYear = new Date().getFullYear();
const currentYearSuffix = `&rsquo;${currentYear - 2000}`;
// TODO: Hunting results might be used to filter, based on the "Callsigns: Only Wanted" option, // TODO: Hunting results might be used to filter, based on the "Callsigns: Only Wanted" option,
// so maybe we can move this loop first, and add a check to the filtering loop? // so maybe we can move this loop first, and add a check to the filtering loop?
@ -62,6 +65,7 @@ function processRosterHunting(callRoster, rosterSettings)
callObj.hunting = {} callObj.hunting = {}
callObj.callFlags = {} callObj.callFlags = {}
callObj.style = callObj.style || {}
let colorObject = Object(); let colorObject = Object();
@ -76,18 +80,19 @@ function processRosterHunting(callRoster, rosterSettings)
let state = "#90EE90"; let state = "#90EE90";
let cnty = "#CCDD00"; let cnty = "#CCDD00";
let cont = "#00DDDD"; let cont = "#00DDDD";
let pota = "#fbb6fc";
let cqz = "#DDDDDD"; let cqz = "#DDDDDD";
let ituz = "#DDDDDD"; let ituz = "#DDDDDD";
let wpx = "#FFFF00"; let wpx = "#FFFF00";
hasGtPin = false; hasGtPin = false;
let shouldAlert = false; let shouldAlert = false;
let callBg, gridBg, callingBg, dxccBg, stateBg, cntyBg, contBg, cqzBg, ituzBg, wpxBg, gtBg; let callBg, gridBg, callingBg, dxccBg, stateBg, cntyBg, contBg, potaBg, cqzBg, ituzBg, wpxBg, gtBg;
let callConf, gridConf, callingConf, dxccConf, stateConf, cntyConf, contConf, cqzConf, ituzConf, wpxConf; let callConf, gridConf, callingConf, dxccConf, stateConf, cntyConf, contConf, potaConf, cqzConf, ituzConf, wpxConf;
callBg = gridBg = callingBg = dxccBg = stateBg = cntyBg = contBg = cqzBg = ituzBg = wpxBg = gtBg = row; callBg = gridBg = callingBg = dxccBg = stateBg = cntyBg = contBg = potaBg = cqzBg = ituzBg = wpxBg = gtBg = row;
callConf = gridConf = callingConf = dxccConf = stateConf = cntyConf = contConf = cqzConf = ituzConf = wpxConf = callConf = gridConf = callingConf = dxccConf = stateConf = cntyConf = contConf = potaConf = cqzConf = ituzConf = wpxConf =
""; "";
let hash = callsign + workHashSuffix; let hash = callsign + workHashSuffix;
@ -138,6 +143,27 @@ function processRosterHunting(callRoster, rosterSettings)
continue; continue;
} }
// Special Calls
if (callObj.DEcall.match("^[A-Z][0-9][A-Z](/w+)?$"))
{
callObj.style.call = "class='oneByOne'";
}
// Entries currently calling or being called by us
if (callObj.DEcall == window.opener.g_instances[callObj.instance].status.DXcall)
{
if (window.opener.g_instances[callObj.instance].status.TxEnabled == 1)
{
callObj.hunting.call = "calling";
callObj.style.call = "class='dxCalling'";
}
else
{
callObj.hunting.call = "caller";
callObj.style.call = "class='dxCaller'";
}
}
// Hunting for callsigns // Hunting for callsigns
if (huntCallsign.checked == true) if (huntCallsign.checked == true)
{ {
@ -202,6 +228,7 @@ function processRosterHunting(callRoster, rosterSettings)
if (huntQRZ.checked == true && callObj.qrz == true) if (huntQRZ.checked == true && callObj.qrz == true)
{ {
callObj.callFlags.calling = true callObj.callFlags.calling = true
callObj.hunting.qrz = "hunted";
shouldAlert = true; shouldAlert = true;
callObj.reason.push("qrz"); callObj.reason.push("qrz");
} }
@ -312,6 +339,27 @@ function processRosterHunting(callRoster, rosterSettings)
} }
} }
} }
callObj.dxccSuffix = null
if (huntMarathon.checked && callObj.hunting.dxcc != "hunted" && callObj.hunting.dxcc != "checked")
{
callObj.reason.push("dxcc-marathon");
let hash = `${callObj.dxcc}-${currentYear}`;
if (rosterSettings.huntIndex && !(hash in rosterSettings.huntIndex.dxcc))
{
if (!rosterSettings.workedIndex || !(hash in rosterSettings.workedIndex.dxcc))
{
callObj.dxccSuffix = currentYearSuffix;
callObj.hunting.dxccMarathon = "hunted";
if (!callObj.hunting.dxcc)
{
dxccConf = `${unconf}${dxcc}${layeredAlpha};`;
}
}
}
}
} }
// Hunting for US States // Hunting for US States
@ -427,21 +475,86 @@ function processRosterHunting(callRoster, rosterSettings)
} }
} }
// Hunting for POTAs
if (huntPOTA.checked == true && window.opener.g_mapSettings.offlineMode == false && callObj.pota != null)
{
let huntTotal = callObj.pota.length;
let huntFound = 0, layeredFound = 0, workedFound = 0, layeredWorkedFound = 0;
for (index in callObj.pota)
{
let hash = callObj.pota[index] + workHashSuffix;
let layeredHash = rosterSettings.layeredMode && (callObj.pota[index] + layeredHashSuffix)
// if (rosterSettings.huntIndex && hash in rosterSettings.huntIndex.pota) layeredFound++;
// if (rosterSettings.layeredMode && layeredHash in rosterSettings.huntIndex.pota) layeredFound++;
// if (rosterSettings.workedIndex && hash in rosterSettings.workedIndex.pota) workedFound++;
// if (rosterSettings.layeredMode && layeredHash in rosterSettings.workedIndex.pota) layeredWorkedFound++;
}
if (huntFound != huntTotal)
{
shouldAlert = true;
callObj.reason.push("pota");
if (rosterSettings.workedIndex && workedFound == huntTotal)
{
if (rosterSettings.layeredMode && layeredFound == huntTotal)
{
callObj.hunting.pota = "worked-and-mixed";
potaConf = `${layeredUnconf}${pota}${layeredUnconfAlpha};`;
potaBg = `${potaBg}${layeredInversionAlpha}`;
pota = bold;
}
else
{
callObj.hunting.pota = "worked";
potaConf = `${unconf}${pota}${inversionAlpha};`;
}
}
else
{
if (rosterSettings.layeredMode && layeredFound == huntTotal)
{
callObj.hunting.pota = "mixed";
potaBg = `${pota}${layeredAlpha};`;
pota = bold;
}
else if (rosterSettings.layeredMode && layeredWorkedFound == huntTotal)
{
callObj.hunting.pota = "mixed-worked";
potaConf = `${unconf}${pota}${layeredAlpha};`;
}
else
{
callObj.hunting.pota = "hunted";
potaBg = `${pota}${inversionAlpha};`;
pota = bold;
}
}
}
}
// Hunting for CQ Zones // Hunting for CQ Zones
if (huntCQz.checked == true) if (huntCQz.checked == true)
{ {
let huntTotal = callObj.cqza.length; let huntTotal = callObj.cqza.length;
let huntFound = 0, layeredFound = 0, workedFound = 0, layeredWorkedFound = 0; let huntFound = 0, layeredFound = 0, workedFound = 0, layeredWorkedFound = 0, marathonFound = 0;
for (index in callObj.cqza) for (index in callObj.cqza)
{ {
let hash = callObj.cqza[index] + workHashSuffix; let hash = callObj.cqza[index] + workHashSuffix;
let layeredHash = rosterSettings.layeredMode && (callObj.cqza[index] + layeredHashSuffix) let layeredHash = rosterSettings.layeredMode && (callObj.cqza[index] + layeredHashSuffix);
let marathonHash = huntMarathon.checked && `${callObj.cqza[index]}-${currentYear}`;
if (rosterSettings.huntIndex && hash in rosterSettings.huntIndex.cqz) huntFound++; if (rosterSettings.huntIndex && hash in rosterSettings.huntIndex.cqz) huntFound++;
if (rosterSettings.layeredMode && layeredHash in rosterSettings.huntIndex.cqz) layeredFound++; if (rosterSettings.layeredMode && layeredHash in rosterSettings.huntIndex.cqz) layeredFound++;
if (rosterSettings.workedIndex && hash in rosterSettings.workedIndex.cqz) workedFound++; if (rosterSettings.workedIndex && hash in rosterSettings.workedIndex.cqz) workedFound++;
if (rosterSettings.layeredMode && layeredHash in rosterSettings.workedIndex.cqz) layeredWorkedFound++; if (rosterSettings.layeredMode && layeredHash in rosterSettings.workedIndex.cqz) layeredWorkedFound++;
if (marathonHash)
{
if (rosterSettings.huntIndex && marathonHash in rosterSettings.huntIndex.cqz) marathonFound++;
else if (rosterSettings.workedIndex && marathonHash in rosterSettings.workedIndex.cqz) marathonFound++;
}
} }
if (huntFound != huntTotal) if (huntFound != huntTotal)
{ {
@ -484,6 +597,23 @@ function processRosterHunting(callRoster, rosterSettings)
} }
} }
} }
callObj.cqzSuffix = null
if (huntMarathon.checked && callObj.hunting.cqz != "hunted" && callObj.hunting.cqz != "worked")
{
if (marathonFound != huntTotal)
{
callObj.reason.push("cqz-marathon");
callObj.cqzSuffix = currentYearSuffix;
callObj.hunting.cqzMarathon = "hunted";
if (!callObj.hunting.cqz)
{
cqzConf = `${unconf}${cqz}${layeredAlpha};`;
}
}
}
} }
// Hunting for ITU Zones // Hunting for ITU Zones
@ -666,6 +796,7 @@ function processRosterHunting(callRoster, rosterSettings)
colorObject.dxcc = "style='" + dxccConf + "background-color:" + dxccBg + ";color:" + dxcc + "'"; colorObject.dxcc = "style='" + dxccConf + "background-color:" + dxccBg + ";color:" + dxcc + "'";
colorObject.state = "style='" + stateConf + "background-color:" + stateBg + ";color:" + state + "'"; colorObject.state = "style='" + stateConf + "background-color:" + stateBg + ";color:" + state + "'";
colorObject.cnty = "style='" + cntyConf + "background-color:" + cntyBg + ";color:" + cnty + "'"; colorObject.cnty = "style='" + cntyConf + "background-color:" + cntyBg + ";color:" + cnty + "'";
colorObject.pota = "style='" + potaConf + "background-color:" + potaBg + ";color:" + pota + "'";
colorObject.cont = "style='" + contConf + "background-color:" + contBg + ";color:" + cont + "'"; colorObject.cont = "style='" + contConf + "background-color:" + contBg + ";color:" + cont + "'";
colorObject.cqz = "style='" + cqzConf + "background-color:" + cqzBg + ";color:" + cqz + "'"; colorObject.cqz = "style='" + cqzConf + "background-color:" + cqzBg + ";color:" + cqz + "'";
colorObject.ituz = "style='" + ituzConf + "background-color:" + ituzBg + ";color:" + ituz + "'"; colorObject.ituz = "style='" + ituzConf + "background-color:" + ituzBg + ";color:" + ituz + "'";

Wyświetl plik

@ -97,15 +97,6 @@ function renderRoster(callRoster, rosterSettings)
window.document.title += " | " + listShortInstances().join(" • "); window.document.title += " | " + listShortInstances().join(" • ");
} }
if (g_rosterSettings.compact)
{
sortCallList(visibleCallList, "Age", false);
}
else
{
sortCallList(visibleCallList, g_rosterSettings.sortColumn, g_rosterSettings.sortReverse);
}
let showBands = (Object.keys(rosterSettings.bands).length > 1) || g_rosterSettings.columns.Band; let showBands = (Object.keys(rosterSettings.bands).length > 1) || g_rosterSettings.columns.Band;
let showModes = (Object.keys(rosterSettings.modes).length > 1) || g_rosterSettings.columns.Mode; let showModes = (Object.keys(rosterSettings.modes).length > 1) || g_rosterSettings.columns.Mode;
@ -113,6 +104,15 @@ function renderRoster(callRoster, rosterSettings)
columnOverrides.Mode = showModes columnOverrides.Mode = showModes
const rosterColumns = rosterColumnList(g_rosterSettings.columns, columnOverrides) const rosterColumns = rosterColumnList(g_rosterSettings.columns, columnOverrides)
if (g_rosterSettings.compact)
{
sortCallList(visibleCallList, "Age", false, rosterColumns);
}
else
{
sortCallList(visibleCallList, g_rosterSettings.sortColumn, g_rosterSettings.sortReverse);
}
let worker = g_rosterSettings.compact ? renderCompactRosterHeaders() : renderNormalRosterHeaders(rosterColumns) let worker = g_rosterSettings.compact ? renderCompactRosterHeaders() : renderNormalRosterHeaders(rosterColumns)
// Third loop: render all rows // Third loop: render all rows
@ -142,5 +142,6 @@ function renderRoster(callRoster, rosterSettings)
} }
worker += g_rosterSettings.compact ? renderCompactRosterFooter() : renderNormalRosterFooter() worker += g_rosterSettings.compact ? renderCompactRosterFooter() : renderNormalRosterFooter()
RosterTable.innerHTML = worker; RosterTable.innerHTML = worker;
} }

Wyświetl plik

@ -12,6 +12,8 @@ function renderHeaderForColumn(column)
let attrs = (columnInfo && columnInfo.tableHeader && columnInfo.tableHeader()) || {} let attrs = (columnInfo && columnInfo.tableHeader && columnInfo.tableHeader()) || {}
attrs.name = column
attrs.html = attrs.html || column attrs.html = attrs.html || column
if (columnInfo.compare) if (columnInfo.compare)
@ -62,11 +64,20 @@ function setRosterSorting(column)
window.opener.goProcessRoster(); window.opener.goProcessRoster();
} }
function sortCallList(callList, sortColumn, sortReverse) function sortCallList(callList, sortColumn, sortReverse, columns)
{ {
const columnInfo = ROSTER_COLUMNS[sortColumn] const columnInfo = ROSTER_COLUMNS[sortColumn]
callList.sort((columnInfo && columnInfo.compare) || ROSTER_COLUMNS.Age.compare) const comparerList = [
(columnInfo && columnInfo.compare) || ROSTER_COLUMNS.Age.compare,
columns && columns.includes("Spot") && ROSTER_COLUMNS.Spot.compare,
columns && columns.includes("dB") && ROSTER_COLUMNS.dB.compare,
columns && columns.includes("Age") && ROSTER_COLUMNS.Age.compare,
columns && columns.includes("Life") && ROSTER_COLUMNS.Life.compare,
columns && columns.includes("Callsign") && ROSTER_COLUMNS.Callsign.compare
]
callList.sort(multiColumnComparer(comparerList))
if (sortReverse) if (sortReverse)
{ {
@ -74,16 +85,36 @@ function sortCallList(callList, sortColumn, sortReverse)
} }
} }
const multiColumnComparer = (comparers) => (a, b) =>
{
let result = 0;
for (let i in comparers)
{
result = comparers[i] && comparers[i](a, b);
if (result) return result;
}
return 0;
}
function validateRosterColumnOrder(columns) function validateRosterColumnOrder(columns)
{ {
let correctedColumnOrder = (columns || DEFAULT_COLUMN_ORDER || []).slice(); let correctedColumnOrder = (columns || DEFAULT_COLUMN_ORDER || []).slice();
// Aappend columns not included in the suggested list.
DEFAULT_COLUMN_ORDER.forEach(column => DEFAULT_COLUMN_ORDER.forEach(column =>
{ {
if (!correctedColumnOrder.includes(column)) correctedColumnOrder.push(column); if (!correctedColumnOrder.includes(column)) correctedColumnOrder.push(column);
}) })
// Exclude any unexpected values
correctedColumnOrder = correctedColumnOrder.filter(column => !!ROSTER_COLUMNS[column]) correctedColumnOrder = correctedColumnOrder.filter(column => !!ROSTER_COLUMNS[column])
// Ensure the first three columns are always the same
correctedColumnOrder = correctedColumnOrder.filter(column => column != "Callsign" && column != "Band" && column != "Mode");
correctedColumnOrder.unshift("Mode");
correctedColumnOrder.unshift("Band");
correctedColumnOrder.unshift("Callsign");
return correctedColumnOrder; return correctedColumnOrder;
} }
@ -93,3 +124,15 @@ function changeRosterColumnOrder(columns)
writeRosterSettings(); writeRosterSettings();
window.opener.goProcessRoster(); window.opener.goProcessRoster();
} }
function moveColumnLeft(column)
{
const columns = rosterColumnList(g_rosterSettings.columns, { Callsign: true, Grid: true });
const pos = columns.indexOf(column);
if (pos > 1)
{
columns[pos] = columns[pos - 1];
columns[pos - 1] = column;
}
changeRosterColumnOrder(columns);
}

Wyświetl plik

@ -1,6 +1,6 @@
const DEFAULT_COLUMN_ORDER = [ const DEFAULT_COLUMN_ORDER = [
"Callsign", "Band", "Mode", "Grid", "Calling", "Msg", "Callsign", "Band", "Mode", "Calling", "Wanted", "Grid", "Msg",
"DXCC", "Flag", "State", "County", "Cont", "POTA", "DXCC", "Flag", "State", "County", "Cont",
"dB", "Freq", "DT", "Dist", "Azim", "dB", "Freq", "DT", "Dist", "Azim",
"CQz", "ITUz", "PX", "CQz", "ITUz", "PX",
"LoTW", "eQSL", "OQRS", "LoTW", "eQSL", "OQRS",
@ -30,6 +30,7 @@ const getterSimpleComparer = (getter) => (a, b) =>
{ {
const aVal = getter(a); const aVal = getter(a);
const bVal = getter(b); const bVal = getter(b);
if (aVal == null) return 1; if (aVal == null) return 1;
if (bVal == null) return -1; if (bVal == null) return -1;
if (aVal > bVal) return 1; if (aVal > bVal) return 1;
@ -62,7 +63,7 @@ const ROSTER_COLUMNS = {
html: html = callObj.DEcall.formatCallsign() html: html = callObj.DEcall.formatCallsign()
} }
let acks = window.opener.g_acknowledgedCalls; let acks = window.opener.g_acknowledgedCalls || {};
if (acks[callObj.DEcall]) if (acks[callObj.DEcall])
{ {
attrs.html = `${attrs.html} <span class='acknowledged'><img class='ackBadge' src='${acks[callObj.DEcall].badge}'></span>` attrs.html = `${attrs.html} <span class='acknowledged'><img class='ackBadge' src='${acks[callObj.DEcall].badge}'></span>`
@ -116,9 +117,9 @@ const ROSTER_COLUMNS = {
compare: (a, b) => window.opener.myDxccCompare(a.callObj, b.callObj), compare: (a, b) => window.opener.myDxccCompare(a.callObj, b.callObj),
tableData: (callObj) => ({ tableData: (callObj) => ({
title: window.opener.g_worldGeoData[window.opener.g_dxccToGeoData[callObj.dxcc]].pp, title: window.opener.g_worldGeoData[window.opener.g_dxccToGeoData[callObj.dxcc]].pp,
name: `DXCC (${callObj.dxcc})`, name: `${callObj.dxcc}`,
rawAttrs: callObj.style.dxcc, rawAttrs: callObj.style.dxcc,
html: window.opener.g_dxccToAltName[callObj.dxcc] html: [window.opener.g_dxccToAltName[callObj.dxcc], callObj.dxccSuffix].join("&nbsp;")
}) })
}, },
@ -215,7 +216,7 @@ const ROSTER_COLUMNS = {
tableData: (callObj) => ({ tableData: (callObj) => ({
name: "CQz", name: "CQz",
rawAttrs: callObj.style.cqz, rawAttrs: callObj.style.cqz,
html: callObj.cqza.join(",") html: [callObj.cqza.join(","), callObj.cqzSuffix].join("&nbsp;")
}) })
}, },
@ -374,5 +375,104 @@ const ROSTER_COLUMNS = {
id: `sp${callObj.hash}`, id: `sp${callObj.hash}`,
html: getSpotString(callObj) html: getSpotString(callObj)
}) })
},
POTA: {
compare: false,
tableData: (callObj) => ({
name: "POTA",
rawAttrs: callObj.style.pota,
title: callObj.pota ? callObj.pota.name : "",
html: callObj.pota ? callObj.pota.reference : ""
})
},
Wanted: {
compare: (a, b) => wantedColumnComparer(a.callObj, b.callObj),
tableData: (callObj) => ({
class: "wantedCol",
title: wantedColumnParts(callObj).map(entry => `${entry}`).join("\n"),
html: wantedColumnParts(callObj).join(" - ", { html: true })
})
} }
} }
WANTED_ORDER = ["call", "qrz", "cont", "dxcc", "cqz", "ituz", "dxccMarathon", "cqzMarathon", "state", "pota", "grid", "cnty", "wpx", "oams"];
WANTED_LABELS = {
cont: "Continent",
cqz: "CQ Zone",
ituz: "ITU Zone",
dxcc: "DXCC",
dxccMarathon: "Marathon DXCC",
cqzMarathon: "Marathon CQ Zone",
state: "State",
grid: "Grid",
cnty: "County",
wpx: "WPX",
call: "Call",
oams: "OAMS",
pota: "POTA"
}
function wantedColumnParts(callObj, options)
{
options = options || {};
if (!callObj.hunting) return [];
let parts = [];
WANTED_ORDER.forEach(field =>
{
let wanted = callObj.hunting[field];
if (wanted == "calling") { parts.push("Calling"); }
// else if (wanted == "caller") { parts.push("Called"); }
else if (wanted == "hunted" && field == "qrz") { parts.push("Caller"); }
else if (wanted == "hunted" && field == "oams") { parts.push("OAMS User"); }
else if (wanted == "hunted") { parts.push(`${options.html ? "<b>" : ""}New ${WANTED_LABELS[field]}${options.html ? "<b>" : ""}`); }
else if (wanted == "worked") { parts.push(`Worked ${WANTED_LABELS[field]}`); }
else if (wanted == "mixed") { parts.push(`${callObj.band} ${WANTED_LABELS[field]}`); }
else if (wanted == "mixed-worked") { parts.push(`${callObj.band} ${WANTED_LABELS[field]}`); parts.push(`Worked ${WANTED_LABELS[field]}`); }
else if (wanted == "worked-and-mixed") { parts.push(`Worked ${callObj.band} ${WANTED_LABELS[field]}`); }
})
if (parts[0] == "Calling" && parts[1] == "Caller")
{
parts.shift(); parts.shift();
parts.unshift(`${options.html ? "<b>" : ""}Working${options.html ? "<b>" : ""}`);
}
return parts;
}
function wantedColumnWeighter(callObj, field)
{
let wanted = callObj.hunting[field];
// We use negative numbers so that sorting is "reversed" by default, placing most interesting items up top.
if (wanted == "calling" || wanted == "caller") return -10;
else if (wanted == "hunted") return -5;
else if (wanted == "worked") return -4;
else if (wanted == "mixed") return -3;
else if (wanted == "mixed-worked") return -2;
else if (wanted == "worked-and-mixed") return -1;
else return 0;
}
function wantedColumnComparer(a, b)
{
if (!a.hunting) return 1;
if (!b.hunting) return -1;
for (const index in WANTED_ORDER)
{
const field = WANTED_ORDER[index];
const aWeight = wantedColumnWeighter(a, field);
const bWeight = wantedColumnWeighter(b, field);
if (aWeight < bWeight) return 1;
if (aWeight > bWeight) return -1;
}
return 0;
}

Wyświetl plik

@ -310,11 +310,11 @@ body.roster {
} }
#huntingMatrixDiv { #huntingMatrixDiv {
flex: 0.5; flex: 0.75;
} }
#exceptionDiv { #exceptionDiv {
flex: 1.5; flex: 1.25;
} }
.secondaryControlGroup h3 { .secondaryControlGroup h3 {
@ -557,4 +557,12 @@ table.rosterTable thead th:first-child {
.ackBadge { .ackBadge {
padding: 0; padding: 0;
width: 1.5em; width: 1.5em;
} }
.wantedCol {
max-width: 160px;
overflow: hidden;
text-overflow:
ellipsis;
white-space: nowrap;
}

Wyświetl plik

@ -1,10 +1,10 @@
{ {
"name": "GridTracker", "name": "GridTracker",
"product_string_do_not_use": "gridtracker", "product_string_do_not_use": "gridtracker",
"version": "1.22.0503", "version": "1.22.0725",
"betaVersion": "", "betaVersion": "",
"description": "GridTracker, an amateur radio companion", "description": "GridTracker, an amateur radio companion",
"author": "Stephen Loomis (N0TTL) and GridTracker.org", "author": "GridTracker.org",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"main": "GridTracker.html", "main": "GridTracker.html",
"window": { "window": {