Merge branch 'Main-App-Pota-Integration' into 'master'
POTA integration and a slew of bug fixes See merge request gridtracker.org/gridtracker!204 If this is changing anything in the UI or operational behavior, please prepare to update the wiki!merge-requests/206/head
|
@ -26,6 +26,7 @@ module.exports = {
|
|||
semi: 0,
|
||||
"space-before-function-paren": 0,
|
||||
"one-var": 0,
|
||||
"no-trailing-spaces": ["error", { skipBlankLines: true, ignoreComments: true }],
|
||||
|
||||
/* ESLint checks we should consider fixing in the future */
|
||||
eqeqeq: 0,
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
gridtracker (1.22.0930) unstable; urgency=low
|
||||
- Test build for POTA Enhancements
|
||||
|
||||
-- Matthew Chambers <nr0q@gridtracker.org> Mon, 26 Sep 2022 19:00:00 -0000
|
||||
gridtracker (1.22.0903) unstable; urgency=low
|
||||
- Fixed a bug that displayed 1.25m band QSOs incorrectly.
|
||||
- Fixed broken DXCC CQ highlighting and Statistics CQ highlighting.
|
||||
|
|
|
@ -695,6 +695,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
<div id="radarButton" onclick="toggleNexrad()" class="iconButton" title="Toggle US Nexrad">
|
||||
<img src="./img/radar-button.png" class="buttonImg" />
|
||||
</div>
|
||||
<div id="potaButton" onClick="togglePota();" class="iconButton" title="Parks On The Air">
|
||||
<img id="potaImg" src="./img/pota.png" class="buttonImg" />
|
||||
</div>
|
||||
<div id="gridOverlayButton" onclick="toggleAllGrids()" class="iconButton" title="Toggle All Grid Overlay">
|
||||
<img src="./img/grid-overlay.png" class="buttonImg" />
|
||||
</div>
|
||||
|
@ -753,6 +756,22 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
></div>
|
||||
</div>
|
||||
<div id="myFlagtip" class="myFlagtipEnd"></div>
|
||||
<div id="myParktip" style="
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
background-color: black;
|
||||
padding: 2px;
|
||||
text-align: center;
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: -500;
|
||||
left: -1000px;
|
||||
top: -1000px;
|
||||
border: 2px solid white;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
"
|
||||
class="roundBorder"></div>
|
||||
<div
|
||||
id="myTooltip"
|
||||
style="
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"AMTORFEC": false,
|
||||
"AM": true,
|
||||
"ARDOP": false,
|
||||
"ASCII": false,
|
||||
"ASCI": false,
|
||||
"ATV": false,
|
||||
"BPSK31": false,
|
||||
"BPSK63": false,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"AMTORFEC": true,
|
||||
"AM": false,
|
||||
"ARDOP": true,
|
||||
"ASCII": true,
|
||||
"ASCI": true,
|
||||
"ATV": false,
|
||||
"BPSK31": true,
|
||||
"BPSK63": true,
|
||||
|
|
|
@ -172,7 +172,7 @@
|
|||
<div>
|
||||
<label><input type="checkbox" id="huntPX" onchange="wantedChanged(this);" /> WPX</label>
|
||||
</div>
|
||||
<div>
|
||||
<div id="huntingMatrixPotaDiv">
|
||||
<label title="Parks On The Air">
|
||||
<input type="checkbox" id="huntPOTA" onchange="wantedChanged(this);" /> POTA
|
||||
</label>
|
||||
|
@ -201,9 +201,6 @@
|
|||
<div>
|
||||
<label><input type="checkbox" id="huntCont" onchange="wantedChanged(this);" /> Cont</label>
|
||||
</div>
|
||||
<div>
|
||||
<label><input type="checkbox" id="huntRR73" onchange="wantedChanged(this);" /> RR73</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -320,7 +317,10 @@
|
|||
<div id="allOnlyNewDiv">
|
||||
<label><input type="checkbox" id="allOnlyNew" onchange="valuesChanged();" /> Only New Calls</label>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<label><input type="checkbox" id="wantRRCQ" onchange="valuesChanged();" /> RR73 as CQ</label>
|
||||
</div>
|
||||
<div>
|
||||
<label title="No Decodes Containing...">
|
||||
<input type="checkbox" id="noMsg" onchange="valuesChanged();" />
|
||||
|
@ -376,15 +376,14 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="instancesWrapper" class="secondaryControlGroup" >
|
||||
<h3>Instances</h3>
|
||||
<div id="instancesDiv"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</header>
|
||||
|
||||
</header>
|
||||
|
||||
<div id="instancesWrapper">
|
||||
<div id="instancesDiv"></div>
|
||||
</div>
|
||||
|
||||
<main id="RosterTable"></main>
|
||||
</div>
|
||||
|
||||
|
|
Przed Szerokość: | Wysokość: | Rozmiar: 690 B |
Przed Szerokość: | Wysokość: | Rozmiar: 383 B |
Przed Szerokość: | Wysokość: | Rozmiar: 754 B |
Przed Szerokość: | Wysokość: | Rozmiar: 332 B |
Przed Szerokość: | Wysokość: | Rozmiar: 754 B |
Przed Szerokość: | Wysokość: | Rozmiar: 351 B |
Przed Szerokość: | Wysokość: | Rozmiar: 437 B |
Przed Szerokość: | Wysokość: | Rozmiar: 367 B |
Przed Szerokość: | Wysokość: | Rozmiar: 872 B |
Przed Szerokość: | Wysokość: | Rozmiar: 868 B |
Po Szerokość: | Wysokość: | Rozmiar: 37 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 552 B |
Po Szerokość: | Wysokość: | Rozmiar: 2.1 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 7.8 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 17 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 1.0 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 155 KiB |
|
@ -249,14 +249,18 @@ function onAdiLoadComplete(adiBuffer, saveAdifFile, adifFileName, newFile)
|
|||
{
|
||||
isPhone = g_modes_phone[finalMode];
|
||||
}
|
||||
// TODO: Revisit when we support more than one park ID
|
||||
let finalPOTA = findAdiField(activeAdifArray[x], "POTA").toUpperCase();
|
||||
if (finalPOTA.length == 0)
|
||||
{
|
||||
finalPOTA = null;
|
||||
}
|
||||
|
||||
if (finalDXcall != "")
|
||||
{
|
||||
addDeDx(
|
||||
finalGrid,
|
||||
finalDXcall,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
finalDEcall,
|
||||
finalRSTsent,
|
||||
finalTime,
|
||||
|
@ -264,7 +268,7 @@ function onAdiLoadComplete(adiBuffer, saveAdifFile, adifFileName, newFile)
|
|||
finalMode,
|
||||
finalBand,
|
||||
confirmed,
|
||||
false,
|
||||
true,
|
||||
finalRSTrecv,
|
||||
finalDxcc,
|
||||
finalState,
|
||||
|
@ -277,7 +281,8 @@ function onAdiLoadComplete(adiBuffer, saveAdifFile, adifFileName, newFile)
|
|||
isDigital,
|
||||
isPhone,
|
||||
finalIOTA,
|
||||
finalSatName
|
||||
finalSatName,
|
||||
finalPOTA
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -332,9 +337,6 @@ function onAdiLoadComplete(adiBuffer, saveAdifFile, adifFileName, newFile)
|
|||
addDeDx(
|
||||
finalMyGrid,
|
||||
finalDEcall,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
finalDXcall,
|
||||
null,
|
||||
finalTime,
|
||||
|
@ -342,7 +344,7 @@ function onAdiLoadComplete(adiBuffer, saveAdifFile, adifFileName, newFile)
|
|||
finalMode,
|
||||
finalBand,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
finalRSTsent,
|
||||
finalDxcc,
|
||||
null,
|
||||
|
@ -357,9 +359,6 @@ function onAdiLoadComplete(adiBuffer, saveAdifFile, adifFileName, newFile)
|
|||
addDeDx(
|
||||
finalGrid,
|
||||
finalDXcall,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
"-",
|
||||
finalRSTsent,
|
||||
finalTime,
|
||||
|
@ -367,7 +366,7 @@ function onAdiLoadComplete(adiBuffer, saveAdifFile, adifFileName, newFile)
|
|||
finalMode,
|
||||
finalBand,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
null,
|
||||
finalDxcc,
|
||||
null,
|
||||
|
@ -382,9 +381,6 @@ function onAdiLoadComplete(adiBuffer, saveAdifFile, adifFileName, newFile)
|
|||
addDeDx(
|
||||
finalGrid,
|
||||
finalDXcall,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
finalDEcall,
|
||||
finalRSTsent,
|
||||
finalTime,
|
||||
|
@ -392,7 +388,7 @@ function onAdiLoadComplete(adiBuffer, saveAdifFile, adifFileName, newFile)
|
|||
finalMode,
|
||||
finalBand,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
null,
|
||||
finalDxcc,
|
||||
null,
|
||||
|
@ -1822,14 +1818,22 @@ function sendToLogger(ADIF)
|
|||
localMode = record.SUBMODE;
|
||||
}
|
||||
|
||||
let localHash = record.CALL + record.BAND + localMode;
|
||||
if (
|
||||
(!("GRIDSQUARE" in record) || record.GRIDSQUARE.length == 0) &&
|
||||
record.CALL + record.BAND + localMode in g_liveCallsigns
|
||||
localHash in g_liveCallsigns
|
||||
)
|
||||
{
|
||||
record.GRIDSQUARE = g_liveCallsigns[
|
||||
record.CALL + record.BAND + localMode
|
||||
].grid.substr(0, 4);
|
||||
record.GRIDSQUARE = g_liveCallsigns[localHash].grid.substr(0, 4);
|
||||
}
|
||||
|
||||
if (g_potaEnabled == 1 && localHash in g_liveCallsigns && g_liveCallsigns[localHash].pota.length > 0)
|
||||
{
|
||||
let pota = g_liveCallsigns[localHash].pota[0];
|
||||
if (pota != "?-????")
|
||||
{
|
||||
record.POTA = pota;
|
||||
}
|
||||
}
|
||||
|
||||
if ("TX_PWR" in record)
|
||||
|
@ -1922,17 +1926,33 @@ function sendToLogger(ADIF)
|
|||
function finishSendingReport(record, localMode)
|
||||
{
|
||||
let report = "";
|
||||
|
||||
for (let key in record)
|
||||
for (const key in record)
|
||||
{
|
||||
report += "<" + key + ":" + Buffer.byteLength(record[key]) + ">" + record[key] + " ";
|
||||
if (key != "POTA")
|
||||
{
|
||||
report += "<" + key + ":" + Buffer.byteLength(record[key]) + ">" + record[key] + " ";
|
||||
}
|
||||
}
|
||||
report += "<EOR>";
|
||||
|
||||
// this report is for internal use ONLY!
|
||||
let reportWithPota = "";
|
||||
for (const key in record)
|
||||
{
|
||||
reportWithPota += "<" + key + ":" + Buffer.byteLength(record[key]) + ">" + record[key] + " ";
|
||||
}
|
||||
reportWithPota += "<EOR>";
|
||||
|
||||
// Full record dupe check
|
||||
if (report != g_lastReport)
|
||||
{
|
||||
g_lastReport = report;
|
||||
|
||||
if (g_potaEnabled == 1 && "POTA" in record)
|
||||
{
|
||||
reportPotaQSO(record);
|
||||
addLastTraffic("<font style='color:white'>Spotted to POTA</font>");
|
||||
}
|
||||
|
||||
if (
|
||||
g_N1MMSettings.enable == true &&
|
||||
|
@ -1966,7 +1986,7 @@ function finishSendingReport(record, localMode)
|
|||
|
||||
try
|
||||
{
|
||||
onAdiLoadComplete("GT<EOH>" + report);
|
||||
onAdiLoadComplete("GT<EOH>" + reportWithPota);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
|
@ -1978,7 +1998,7 @@ function finishSendingReport(record, localMode)
|
|||
if (logGTqsoCheckBox.checked == true)
|
||||
{
|
||||
var fs = require("fs");
|
||||
fs.appendFileSync(g_qsoLogFile, report + "\r\n");
|
||||
fs.appendFileSync(g_qsoLogFile, reportWithPota + "\r\n");
|
||||
addLastTraffic(
|
||||
"<font style='color:white'>Logged to GridTracker backup</font>"
|
||||
);
|
||||
|
@ -2795,7 +2815,7 @@ function getPostJSONBuffer(
|
|||
});
|
||||
req.on("error", function (err) // eslint-disable-line node/handle-callback-err
|
||||
{
|
||||
if (typeof timeoutCallback != "undefined")
|
||||
if (typeof timeoutCallback === "function")
|
||||
{
|
||||
timeoutCallback(
|
||||
file_url,
|
||||
|
|
|
@ -744,15 +744,13 @@ function lookupUsCallsign(object, writeState = false)
|
|||
{
|
||||
g_ulsDatabase.transaction(function (tx)
|
||||
{
|
||||
var qry = "SELECT * FROM calls where callsign = \"" + object.DEcall + "\"";
|
||||
let qry = "SELECT * FROM calls where callsign = \"" + object.DEcall + "\"";
|
||||
tx.executeSql(
|
||||
qry,
|
||||
[],
|
||||
function (tx, results)
|
||||
{
|
||||
var len = results.rows.length,
|
||||
i;
|
||||
if (len == 1)
|
||||
if (results.rows.length == 1)
|
||||
{
|
||||
if (object.state == null)
|
||||
{
|
||||
|
@ -764,15 +762,16 @@ function lookupUsCallsign(object, writeState = false)
|
|||
{
|
||||
object.state = "US-" + results.rows[0].state;
|
||||
}
|
||||
if (writeState) setState(object);
|
||||
|
||||
if (writeState)
|
||||
{
|
||||
setState(object);
|
||||
}
|
||||
}
|
||||
object.zipcode = String(results.rows[0].zip);
|
||||
if (object.cnty == null)
|
||||
{
|
||||
let request = g_Idb
|
||||
.transaction(["lookups"], "readwrite")
|
||||
.objectStore("lookups")
|
||||
.get(object.DEcall);
|
||||
let request = g_Idb.transaction(["lookups"], "readwrite").objectStore("lookups").get(object.DEcall);
|
||||
|
||||
request.onsuccess = function (event)
|
||||
{
|
||||
|
@ -785,21 +784,40 @@ function lookupUsCallsign(object, writeState = false)
|
|||
if (object.cnty == null && object.zipcode in g_zipToCounty)
|
||||
{
|
||||
var counties = g_zipToCounty[object.zipcode];
|
||||
if (counties.length > 1) object.qual = false;
|
||||
else object.qual = true;
|
||||
if (counties.length > 1)
|
||||
{
|
||||
object.qual = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
object.qual = true;
|
||||
}
|
||||
object.cnty = counties[0];
|
||||
}
|
||||
else object.qual = false;
|
||||
if (writeState) setState(object);
|
||||
else
|
||||
{
|
||||
object.qual = false;
|
||||
}
|
||||
|
||||
if (writeState)
|
||||
{
|
||||
setState(object);
|
||||
}
|
||||
};
|
||||
|
||||
request.onerror = function (event)
|
||||
{
|
||||
object.qual = false;
|
||||
if (writeState) setState(object);
|
||||
if (writeState)
|
||||
{
|
||||
setState(object);
|
||||
}
|
||||
};
|
||||
}
|
||||
if (writeState) setState(object);
|
||||
if (writeState)
|
||||
{
|
||||
setState(object);
|
||||
}
|
||||
}
|
||||
},
|
||||
null
|
||||
|
|
|
@ -47,6 +47,7 @@ var def_appSettings = {
|
|||
gtPropFilter: "mixed",
|
||||
gtMsgEnable: true,
|
||||
gtShareEnable: true,
|
||||
gtSpotEnable: true,
|
||||
heatEnabled: 0,
|
||||
loadAdifAtStartup: false,
|
||||
lookupLoginCq: "",
|
||||
|
@ -74,6 +75,7 @@ var def_appSettings = {
|
|||
myRawFreq: "",
|
||||
myRawGrid: "",
|
||||
pathWidthWeight: 1.0,
|
||||
potaEnabled: 0,
|
||||
pushPinMode: false,
|
||||
qrzPathWidthWeight: 1.2,
|
||||
sixWideMode: 0,
|
||||
|
@ -90,8 +92,7 @@ var def_appSettings = {
|
|||
workingCallsignEnable: false,
|
||||
workingCallsigns: {},
|
||||
workingDateEnable: false,
|
||||
workingDate: 0,
|
||||
gtSpotEnable: true
|
||||
workingDate: 0
|
||||
};
|
||||
|
||||
var def_mapSettings = {
|
||||
|
@ -210,32 +211,38 @@ var def_N1MMSettings = {
|
|||
port: 2333,
|
||||
ip: "127.0.0.1"
|
||||
};
|
||||
|
||||
var def_log4OMSettings = {
|
||||
enable: false,
|
||||
port: 2236,
|
||||
ip: "127.0.0.1"
|
||||
};
|
||||
|
||||
var def_dxkLogSettings = {
|
||||
enable: false,
|
||||
port: 52000,
|
||||
ip: "127.0.0.1"
|
||||
};
|
||||
|
||||
var def_HRDLogbookLogSettings = {
|
||||
enable: false,
|
||||
port: 7826,
|
||||
ip: "127.0.0.1"
|
||||
};
|
||||
|
||||
var def_acLogSettings = {
|
||||
enable: false,
|
||||
port: 1100,
|
||||
ip: "127.0.0.1"
|
||||
};
|
||||
|
||||
var def_trustedQslSettings = {
|
||||
stationFile: "",
|
||||
stationFileValid: false,
|
||||
binaryFile: "",
|
||||
binaryFileValid: false
|
||||
};
|
||||
|
||||
var def_callsignLookups = {
|
||||
lotwUseEnable: true,
|
||||
lotwWeeklyEnable: true,
|
||||
|
|
|
@ -337,7 +337,9 @@ function gtChatUpdateCall(jsmesg)
|
|||
{
|
||||
makeGtPin(g_gtFlagPins[cid]);
|
||||
if (g_gtFlagPins[cid].pin != null)
|
||||
{ g_layerSources.gtflags.addFeature(g_gtFlagPins[cid].pin); }
|
||||
{
|
||||
g_layerSources.gtflags.addFeature(g_gtFlagPins[cid].pin);
|
||||
}
|
||||
}
|
||||
g_gtChatlistChangeCount++;
|
||||
g_gtCallsigns[g_gtFlagPins[cid].call] = cid;
|
||||
|
@ -382,7 +384,9 @@ function makeGtPin(obj)
|
|||
delete obj.pin;
|
||||
obj.pin = null;
|
||||
}
|
||||
|
||||
|
||||
if (obj.src != "GT") return;
|
||||
|
||||
if (typeof obj.grid == "undefined" || obj.grid == null) return;
|
||||
|
||||
if (obj.grid.length != 4 && obj.grid.length != 6) return;
|
||||
|
@ -453,9 +457,13 @@ function gtChatNewList(jsmesg)
|
|||
g_gtFlagPins[cid].dxcc = callsignToDxcc(g_gtFlagPins[cid].call);
|
||||
g_gtFlagPins[cid].live = true;
|
||||
g_gtCallsigns[g_gtFlagPins[cid].call] = cid;
|
||||
|
||||
makeGtPin(g_gtFlagPins[cid]);
|
||||
|
||||
if (g_gtFlagPins[cid].pin != null)
|
||||
{ g_layerSources.gtflags.addFeature(g_gtFlagPins[cid].pin); }
|
||||
{
|
||||
g_layerSources.gtflags.addFeature(g_gtFlagPins[cid].pin);
|
||||
}
|
||||
}
|
||||
}
|
||||
g_gtChatlistChangeCount++;
|
||||
|
|
|
@ -2,66 +2,625 @@
|
|||
// All rights reserved.
|
||||
// See LICENSE for more information.
|
||||
|
||||
var g_potaPlaces = null;
|
||||
var g_potaSpots = null;
|
||||
var g_pota = {
|
||||
parks: {},
|
||||
locations: {},
|
||||
parksTimeout: null,
|
||||
callSchedule: {},
|
||||
parkSchedule: {},
|
||||
scheduleTimeout: null,
|
||||
callSpots: {},
|
||||
parkSpots: {},
|
||||
spotsTimeout: null,
|
||||
mapParks: {},
|
||||
rbnReportTimes: {},
|
||||
rbnFrequency: 600000
|
||||
};
|
||||
|
||||
function ingestPotaPlaces(buffer)
|
||||
var g_potaSpotTemplate = {
|
||||
activator: "",
|
||||
frequency: 0,
|
||||
mode: "",
|
||||
band: "",
|
||||
reference: "",
|
||||
spotTime: 0,
|
||||
spotter: "",
|
||||
comments: "",
|
||||
source: "GT",
|
||||
count: 1,
|
||||
activatorGrid: "",
|
||||
spotterGrid: ""
|
||||
};
|
||||
|
||||
var g_parkTemplate = {
|
||||
feature: null
|
||||
}
|
||||
|
||||
var g_potaUnknownPark = {
|
||||
name: "Unknown park (not yet spotted)",
|
||||
active: "0",
|
||||
entityId: "-1",
|
||||
locationDesc: "??-??",
|
||||
latitude: "0.0",
|
||||
longitude: "0.0",
|
||||
grid: ""
|
||||
};
|
||||
|
||||
var g_gtParkIconActive = new ol.style.Icon({
|
||||
src: "./img/pota_icon_active.png",
|
||||
anchorYUnits: "pixels",
|
||||
anchorXUnits: "pixels",
|
||||
anchor: [10, 19]
|
||||
});
|
||||
|
||||
var g_gtParkIconInactive = new ol.style.Icon({
|
||||
src: "./img/pota_icon_inactive.png",
|
||||
anchorYUnits: "pixels",
|
||||
anchorXUnits: "pixels",
|
||||
anchor: [10, 19]
|
||||
});
|
||||
|
||||
function initPota()
|
||||
{
|
||||
try
|
||||
potaImg.style.filter = g_potaEnabled == 1 ? "" : "grayscale(1)";
|
||||
|
||||
getPotaParks();
|
||||
}
|
||||
|
||||
function togglePota()
|
||||
{
|
||||
g_potaEnabled ^= 1;
|
||||
g_appSettings.potaEnabled = g_potaEnabled;
|
||||
potaImg.style.filter = g_potaEnabled == 1 ? "" : "grayscale(1)";
|
||||
|
||||
saveAppSettings();
|
||||
|
||||
if (g_potaEnabled == 1)
|
||||
{
|
||||
g_potaPlaces = JSON.parse(buffer);
|
||||
getPotaParks();
|
||||
}
|
||||
catch (e)
|
||||
else
|
||||
{
|
||||
// can't write, somethings broke
|
||||
g_layerSources.pota.clear();
|
||||
g_pota.mapParks = {};
|
||||
}
|
||||
goProcessRoster();
|
||||
}
|
||||
|
||||
function redrawParks()
|
||||
{
|
||||
g_layerSources.pota.clear();
|
||||
|
||||
if (g_potaEnabled == 1)
|
||||
{
|
||||
g_pota.mapParks = {};
|
||||
makeParkFeatures();
|
||||
}
|
||||
}
|
||||
|
||||
function getPotaPlaces()
|
||||
function makeParkFeatures()
|
||||
{
|
||||
if (g_mapSettings.offlineMode == false)
|
||||
try
|
||||
{
|
||||
for (const park in g_pota.parkSpots)
|
||||
{
|
||||
if (park in g_pota.parks)
|
||||
{
|
||||
let parkObj = Object.assign({}, g_parkTemplate);
|
||||
for (const call in g_pota.parkSpots[park])
|
||||
{
|
||||
let report = g_pota.parkSpots[park][call];
|
||||
if (parkObj.feature == null && validateMapBandAndMode(report.band, report.mode))
|
||||
{
|
||||
parkObj.feature = iconFeature(ol.proj.fromLonLat([Number(g_pota.parks[park].longitude), Number(g_pota.parks[park].latitude)]), g_gtParkIconActive, 1);
|
||||
parkObj.feature.key = park;
|
||||
parkObj.feature.size = 22;
|
||||
|
||||
g_pota.mapParks[park] = parkObj;
|
||||
g_layerSources.pota.addFeature(parkObj.feature);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
console.log("exception: makeParkFeature " + park);
|
||||
console.log(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
function potaSpotFromDecode(callObj)
|
||||
{
|
||||
for (const i in callObj.pota)
|
||||
{
|
||||
let park = callObj.pota[i];
|
||||
let spotObj = null;
|
||||
|
||||
if (!(callObj.DEcall in g_pota.callSpots))
|
||||
{
|
||||
// new call and park
|
||||
g_pota.callSpots[callObj.DEcall] = [park];
|
||||
}
|
||||
else if (!g_pota.callSpots[callObj.DEcall].includes(park))
|
||||
{
|
||||
// new park
|
||||
g_pota.callSpots[callObj.DEcall].push(park);
|
||||
}
|
||||
|
||||
if (!(park in g_pota.parkSpots))
|
||||
{
|
||||
g_pota.parkSpots[park] = {};
|
||||
g_pota.parkSpots[park][callObj.DEcall] = spotFromCallObj(callObj, park, 0, 0);
|
||||
}
|
||||
else if (!(callObj.DEcall in g_pota.parkSpots[park]))
|
||||
{
|
||||
g_pota.parkSpots[park][callObj.DEcall] = spotFromCallObj(callObj, park, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// update spot
|
||||
g_pota.parkSpots[park][callObj.DEcall] = spotFromCallObj(callObj, park, g_pota.parkSpots[park][callObj.DEcall].count);
|
||||
}
|
||||
|
||||
// may or may not be on screen, so try
|
||||
addParkSpotFeature(park, g_pota.parkSpots[park][callObj.DEcall]);
|
||||
|
||||
let hash = park + callObj.DEcall;
|
||||
if (!(hash in g_pota.rbnReportTimes) || Date.now() > g_pota.rbnReportTimes[hash])
|
||||
{
|
||||
g_pota.rbnReportTimes[hash] = Date.now() + g_pota.rbnFrequency;
|
||||
reportPotaRBN(g_pota.parkSpots[park][callObj.DEcall]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function reportPotaRBN(callSpot)
|
||||
{
|
||||
let report = {
|
||||
activator: callSpot.activator,
|
||||
spotter: myDEcall + "-#",
|
||||
frequency: String(parseInt(callSpot.frequency * 1000)),
|
||||
reference: callSpot.reference,
|
||||
mode: callSpot.mode,
|
||||
source: "RBN",
|
||||
comments: callSpot.comments,
|
||||
activatorGrid: callSpot.activatorGrid,
|
||||
spotterGrid: callSpot.spotterGrid
|
||||
}
|
||||
|
||||
getPostJSONBuffer(
|
||||
"https://api.pota.app/spot",
|
||||
rbnReportResult,
|
||||
null,
|
||||
"https",
|
||||
443,
|
||||
report,
|
||||
10000,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
function reportPotaQSO(record)
|
||||
{
|
||||
let report = {
|
||||
activator: record.CALL,
|
||||
spotter: record.STATION_CALLSIGN,
|
||||
frequency: record.FREQ,
|
||||
reference: record.POTA,
|
||||
mode: record.MODE,
|
||||
source: "GT",
|
||||
comments: record.COMMENT ? record.COMMENT : "",
|
||||
activatorGrid: record.GRIDSQUARE ? record.GRIDSQUARE : "",
|
||||
spotterGrid: record.MY_GRIDSQUARE ? record.MY_GRIDSQUARE : ""
|
||||
}
|
||||
|
||||
if ("SUBMODE" in record)
|
||||
{
|
||||
report.mode = record.SUBMODE;
|
||||
}
|
||||
|
||||
getPostJSONBuffer(
|
||||
"https://api.pota.app/spot",
|
||||
rbnReportResult,
|
||||
null,
|
||||
"https",
|
||||
443,
|
||||
report,
|
||||
10000,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
function rbnReportResult(buffer, flag, cookies)
|
||||
{
|
||||
// It worked! process latest spots!
|
||||
if (g_pota.spotsTimeout)
|
||||
{
|
||||
clearTimeout(g_pota.spotsTimeout);
|
||||
g_pota.spotsTimeout = null;
|
||||
}
|
||||
|
||||
processPotaSpots(String(buffer));
|
||||
|
||||
g_pota.spotsTimeout = setTimeout(getPotaSpots, 300000);
|
||||
}
|
||||
|
||||
function spotFromCallObj(callObj, park, inCount, rbnTime)
|
||||
{
|
||||
let callSpot = {
|
||||
activator: callObj.DEcall,
|
||||
activatorGrid: callObj.grid,
|
||||
spotter: myDEcall + "-#",
|
||||
spotterGrid: myDEGrid,
|
||||
frequency: Number((g_instances[callObj.instance].status.Frequency / 1000000).toFixed(3)),
|
||||
reference: park,
|
||||
mode: callObj.mode,
|
||||
band: callObj.band,
|
||||
spotTime: Date.now(),
|
||||
source: "GT",
|
||||
count: inCount + 1,
|
||||
comments: "GT " + callObj.RSTsent + " dB " + myDEGrid + " via " + myDEcall + "-#"
|
||||
};
|
||||
return callSpot;
|
||||
}
|
||||
|
||||
function addParkSpotFeature(park, report)
|
||||
{
|
||||
let parkObj = Object.assign({}, g_parkTemplate);
|
||||
if (park in g_pota.mapParks)
|
||||
{
|
||||
parkObj = g_pota.mapParks[park];
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pota.mapParks[park] = parkObj;
|
||||
}
|
||||
|
||||
if (parkObj.feature == null && validateMapBandAndMode(report.band, report.mode))
|
||||
{
|
||||
parkObj.feature = iconFeature(ol.proj.fromLonLat([Number(g_pota.parks[park].longitude), Number(g_pota.parks[park].latitude)]), g_gtParkIconActive, 1);
|
||||
parkObj.feature.key = park;
|
||||
parkObj.feature.size = 22;
|
||||
g_layerSources.pota.addFeature(parkObj.feature);
|
||||
}
|
||||
}
|
||||
|
||||
function processPotaParks(buffer)
|
||||
{
|
||||
if (g_potaEnabled == 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
let data = JSON.parse(buffer);
|
||||
let newParks = data.parks;
|
||||
for (const park in newParks)
|
||||
{
|
||||
let locations = newParks[park].locationDesc.split(",");
|
||||
for (const i in locations)
|
||||
{
|
||||
if (locations[i] in data.locations)
|
||||
{
|
||||
locations[i] = data.locations[locations[i]];
|
||||
}
|
||||
}
|
||||
newParks[park].locationDesc = locations.join(", ");
|
||||
}
|
||||
newParks["?-????"] = g_potaUnknownPark;
|
||||
|
||||
g_pota.parks = newParks;
|
||||
g_pota.locations = data.locations;
|
||||
getPotaSchedule();
|
||||
getPotaSpots();
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
// can't write, somethings broke
|
||||
console.log("Failed to load parks!");
|
||||
console.log(e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getPotaParks()
|
||||
{
|
||||
if (g_pota.parksTimeout)
|
||||
{
|
||||
clearTimeout(g_pota.parksTimeout);
|
||||
g_pota.spotsTimeout = null;
|
||||
}
|
||||
|
||||
if (g_mapSettings.offlineMode == false && g_potaEnabled == 1)
|
||||
{
|
||||
getBuffer(
|
||||
"https://storage.googleapis.com/gt_app/pota.json",
|
||||
ingestPotaPlaces,
|
||||
"https://storage.googleapis.com/gt_app/pota.json?cb=" + Date.now(),
|
||||
processPotaParks,
|
||||
null,
|
||||
"https",
|
||||
443
|
||||
);
|
||||
|
||||
setTimeout(getPotaPlaces, 86400000)
|
||||
}
|
||||
|
||||
g_pota.parksTimeout = setTimeout(getPotaParks, 86400000)
|
||||
}
|
||||
|
||||
function ingestPotaSpots(buffer)
|
||||
// This is a shallow copy, don't use with objects that contain other objects or arrays
|
||||
function fillObjectFromTemplate(template, input)
|
||||
{
|
||||
try
|
||||
let object = {};
|
||||
for (const key in template)
|
||||
{
|
||||
g_potaSpots = JSON.parse(buffer);
|
||||
if (key in input)
|
||||
{
|
||||
object[key] = input[key];
|
||||
}
|
||||
else
|
||||
{
|
||||
// missing, use the template value
|
||||
object[key] = template[key];
|
||||
}
|
||||
}
|
||||
catch (e)
|
||||
return object;
|
||||
}
|
||||
|
||||
function uniqueArrayFromArray(input)
|
||||
{
|
||||
let unique = [];
|
||||
input.forEach((c) =>
|
||||
{
|
||||
// can't write, somethings broke
|
||||
if (!unique.includes(c))
|
||||
{
|
||||
unique.push(c);
|
||||
}
|
||||
});
|
||||
return unique;
|
||||
}
|
||||
|
||||
function processPotaSpots(buffer)
|
||||
{
|
||||
if (g_potaEnabled == 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
let spots = JSON.parse(buffer);
|
||||
g_pota.callSpots = {};
|
||||
g_pota.parkSpots = {};
|
||||
for (const spot in spots)
|
||||
{
|
||||
if (spots[spot].reference in g_pota.parks)
|
||||
{
|
||||
let newSpot = fillObjectFromTemplate(g_potaSpotTemplate, spots[spot]);
|
||||
newSpot.spotTime = Date.parse(newSpot.spotTime + "Z");
|
||||
newSpot.frequency = parseInt(newSpot.frequency) / 1000;
|
||||
newSpot.band = newSpot.frequency.formatBand();
|
||||
(g_pota.callSpots[newSpot.activator] = g_pota.callSpots[newSpot.activator] || []).push(newSpot.reference);
|
||||
|
||||
if (!(newSpot.reference in g_pota.parkSpots))
|
||||
{
|
||||
g_pota.parkSpots[newSpot.reference] = {};
|
||||
}
|
||||
if (newSpot.activator in g_pota.parkSpots[newSpot.reference])
|
||||
{
|
||||
g_pota.parkSpots[newSpot.reference][newSpot.activator] = fillObjectFromTemplate(g_pota.parkSpots[newSpot.reference][newSpot.activator], newSpot);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pota.parkSpots[newSpot.reference][newSpot.activator] = newSpot;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log("PotaSpots: unknown park id: " + spots[spot].reference);
|
||||
}
|
||||
}
|
||||
|
||||
// Sanity dedupe checks
|
||||
for (const spot in g_pota.callSpots)
|
||||
{
|
||||
g_pota.callSpots[spot] = uniqueArrayFromArray(g_pota.callSpots[spot]);
|
||||
}
|
||||
|
||||
redrawParks();
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
// can't write, somethings broke
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getPotaSpots()
|
||||
{
|
||||
if (g_mapSettings.offlineMode == false && g_spotsEnabled == 1)
|
||||
if (g_pota.spotsTimeout)
|
||||
{
|
||||
clearTimeout(g_pota.spotsTimeout);
|
||||
g_pota.spotsTimeout = null;
|
||||
}
|
||||
|
||||
if (g_mapSettings.offlineMode == false && g_potaEnabled == 1)
|
||||
{
|
||||
getBuffer(
|
||||
"https://api.pota.app/spot/activator",
|
||||
ingestPotaSpots,
|
||||
processPotaSpots,
|
||||
null,
|
||||
"https",
|
||||
443
|
||||
);
|
||||
}
|
||||
|
||||
setTimeout(getPotaSpots, 300000);
|
||||
g_pota.spotsTimeout = setTimeout(getPotaSpots, 300000);
|
||||
}
|
||||
|
||||
function processPotaSchedule(buffer)
|
||||
{
|
||||
if (g_potaEnabled == 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
let schedules = JSON.parse(buffer);
|
||||
g_pota.callSchedule = {};
|
||||
g_pota.parkSchedule = {};
|
||||
for (const i in schedules)
|
||||
{
|
||||
let newObj = {};
|
||||
newObj.id = schedules[i].reference;
|
||||
newObj.start = Date.parse(schedules[i].startDate + "T" + schedules[i].startTime + "Z");
|
||||
newObj.end = Date.parse(schedules[i].endDate + "T" + schedules[i].endTime + "Z");
|
||||
newObj.frequencies = schedules[i].frequencies;
|
||||
newObj.comments = schedules[i].comments;
|
||||
if (Date.now() < newObj.end)
|
||||
{
|
||||
if (newObj.id in g_pota.parks)
|
||||
{
|
||||
(g_pota.callSchedule[schedules[i].activator] = g_pota.callSchedule[schedules[i].activator] || []).push(newObj);
|
||||
|
||||
newObj = Object.assign({}, newObj);
|
||||
newObj.id = schedules[i].activator;
|
||||
(g_pota.parkSchedule[schedules[i].reference] = g_pota.parkSchedule[schedules[i].reference] || []).push(newObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log("PotaSchedule: unknown park id: " + newObj.id);
|
||||
}
|
||||
}
|
||||
// else it is expired and no longer relevant
|
||||
}
|
||||
|
||||
// Sanity dedupe checks
|
||||
for (const key in g_pota.callSchedule)
|
||||
{
|
||||
g_pota.callSchedule[key] = uniqueArrayFromArray(g_pota.callSchedule[key]);
|
||||
}
|
||||
for (const key in g_pota.parkSchedule)
|
||||
{
|
||||
g_pota.parkSchedule[key] = uniqueArrayFromArray(g_pota.parkSchedule[key]);
|
||||
}
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
// can't write, somethings broke
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function g_sendPotaSpot()
|
||||
function getPotaSchedule()
|
||||
{
|
||||
// if Pota spotting enabled, and we have enough info, send a spot to Pota
|
||||
if (g_pota.scheduleTimeout)
|
||||
{
|
||||
clearTimeout(g_pota.scheduleTimeout);
|
||||
g_pota.scheduleTimeout = null;
|
||||
}
|
||||
|
||||
if (g_mapSettings.offlineMode == false && g_potaEnabled == 1)
|
||||
{
|
||||
getBuffer(
|
||||
"https://api.pota.app/activation",
|
||||
processPotaSchedule,
|
||||
null,
|
||||
"https",
|
||||
443
|
||||
);
|
||||
}
|
||||
g_pota.scheduleTimeout = setTimeout(getPotaSchedule, 900000);
|
||||
}
|
||||
|
||||
var g_lastPark = null;
|
||||
function mouseOverPark(feature)
|
||||
{
|
||||
if (g_lastPark && g_lastPark == feature)
|
||||
{
|
||||
mouseParkMove();
|
||||
return;
|
||||
}
|
||||
g_lastPark = feature;
|
||||
|
||||
createParkTipTable(feature);
|
||||
|
||||
mouseParkMove();
|
||||
|
||||
myParktip.style.zIndex = 499;
|
||||
myParktip.style.display = "block";
|
||||
}
|
||||
|
||||
function mouseOutPark(mouseEvent)
|
||||
{
|
||||
g_lastPark = null;
|
||||
myParktip.style.zIndex = -1;
|
||||
}
|
||||
|
||||
function mouseParkMove()
|
||||
{
|
||||
var positionInfo = myParktip.getBoundingClientRect();
|
||||
var windowWidth = window.innerWidth;
|
||||
|
||||
myParktip.style.left = getMouseX() - (positionInfo.width / 2) + "px";
|
||||
if (windowWidth - getMouseX() < (positionInfo.width / 2))
|
||||
{
|
||||
myParktip.style.left = getMouseX() - (10 + positionInfo.width) + "px";
|
||||
}
|
||||
if (getMouseX() - (positionInfo.width / 2) < 0)
|
||||
{
|
||||
myParktip.style.left = getMouseX() + 10 + "px";
|
||||
}
|
||||
myParktip.style.top = getMouseY() - positionInfo.height - 12 + "px";
|
||||
}
|
||||
|
||||
function createParkTipTable(toolElement)
|
||||
{
|
||||
let worker = "";
|
||||
|
||||
let key = toolElement.key;
|
||||
let now = Date.now();
|
||||
|
||||
worker += "<div style='background-color:#000;color:lightgreen;font-weight:bold;font-size:12px;border:1px solid gray;margin:0px' class='roundBorder'>" +
|
||||
key +
|
||||
" : <font color='cyan'>" + g_pota.parks[key].name + "" +
|
||||
" (<font color='yellow'>" + g_dxccToAltName[Number(g_pota.parks[key].entityId)] + "</font>)" +
|
||||
"</font></br><font color='lightblue'>" + g_pota.parks[key].locationDesc + "</font></div>";
|
||||
|
||||
worker += "<table id='potaSpotsTable' class='darkTable' style='margin: 0 auto;'>";
|
||||
worker += "<tr><th>Activator</th><th>Spotter</th><th>Freq</th><th>Mode</th><th>Count</th><th>When</th><th>Source</th><th>Comment</th></tr>";
|
||||
for (const i in g_pota.parkSpots[key])
|
||||
{
|
||||
if (validateMapBandAndMode(g_pota.parkSpots[key][i].band, g_pota.parkSpots[key][i].mode))
|
||||
{
|
||||
worker += "<tr>";
|
||||
worker += "<td style='color:yellow'>" + g_pota.parkSpots[key][i].activator + "</td>";
|
||||
worker += "<td style='color:cyan'>" + ((g_pota.parkSpots[key][i].spotter == g_pota.parkSpots[key][i].activator) ? "Self" : g_pota.parkSpots[key][i].spotter) + "</td>";
|
||||
worker += "<td style='color:lightgreen' >" + g_pota.parkSpots[key][i].frequency.formatMhz(3, 3) + " <font color='yellow'>(" + g_pota.parkSpots[key][i].band + ")</font></td>";
|
||||
worker += "<td style='color:orange'>" + g_pota.parkSpots[key][i].mode + "</td>";
|
||||
worker += "<td>" + g_pota.parkSpots[key][i].count + "</td>";
|
||||
worker += "<td style='color:lightblue' >" + parseInt((now - g_pota.parkSpots[key][i].spotTime) / 1000).toDHMS() + "</td>";
|
||||
worker += "<td>" + g_pota.parkSpots[key][i].source + "</td>";
|
||||
worker += "<td>" + g_pota.parkSpots[key][i].comments + "</td>";
|
||||
worker += "</tr>";
|
||||
}
|
||||
}
|
||||
worker += "</table>";
|
||||
|
||||
/*
|
||||
buffer += "<div style='background-color:#000;color:#fff;font-size:12px;border:1px solid gray;margin:1px' class='roundBorder'>Activations (scheduled)"
|
||||
buffer += "<table id='potaScheduleTable' class='darkTable' style='margin: 0 auto;'>";
|
||||
buffer += "<tr><th>Activator</th><th>Start</th><th>End</th><th>Frequencies</th><th>Comment</th></tr>";
|
||||
for (const i in g_pota.parkSchedule[key])
|
||||
{
|
||||
let start = g_pota.parkSchedule[key][i].start;
|
||||
let end = g_pota.parkSchedule[key][i].end;
|
||||
if (now < end)
|
||||
{
|
||||
buffer += "<tr>";
|
||||
buffer += "<td style='color:yellow'>" + g_pota.parkSchedule[key][i].id + "</td>";
|
||||
buffer += "<td style='color:lightblue'>" + ((now >= start) ? "<font color='white'>Now</font>" : (userTimeString(start) + "</br><font color='lightgreen'>T- " + Number(start - now).msToDHMS() + "</font>")) + "</td>";
|
||||
buffer += "<td style='color:lightblue'>" + (userTimeString(end) + "</br><font color='orange'>T- " + Number(end - now).msToDHMS() + "</font>") + "</td>";
|
||||
buffer += "<td style='color:lightgreen'>" + g_pota.parkSchedule[key][i].frequencies + "</td>";
|
||||
buffer += "<td>" + g_pota.parkSchedule[key][i].comments.substr(0, 40) + "</td>";
|
||||
buffer += "</tr>";
|
||||
active++;
|
||||
}
|
||||
}
|
||||
*/
|
||||
myParktip.innerHTML = worker;
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -129,6 +129,25 @@ Number.prototype.toDHMS = function ()
|
|||
return val;
|
||||
};
|
||||
|
||||
Number.prototype.msToDHMS = function ()
|
||||
{
|
||||
var seconds = parseInt(this / 1000);
|
||||
var days = Math.floor(seconds / (3600 * 24));
|
||||
seconds -= days * 3600 * 24;
|
||||
var hrs = Math.floor(seconds / 3600);
|
||||
seconds -= hrs * 3600;
|
||||
var mnts = Math.floor(seconds / 60);
|
||||
seconds -= mnts * 60;
|
||||
|
||||
days = days ? days + "d " : "";
|
||||
hrs = hrs ? hrs + "h " : "";
|
||||
mnts = mnts ? mnts + "m " : "";
|
||||
var first = days + hrs + mnts;
|
||||
if (first == "") val = seconds + "s";
|
||||
else val = first + (seconds > 0 ? seconds + "s" : "");
|
||||
return val;
|
||||
};
|
||||
|
||||
Number.prototype.toDHMS15 = function ()
|
||||
{
|
||||
// round to earliest 15 seconds
|
||||
|
|
|
@ -21,6 +21,7 @@ var r_currentDXCCs = -1;
|
|||
var r_callsignManifest = null;
|
||||
var g_rosterSettings = {};
|
||||
var g_day = 0;
|
||||
var g_dayAsString = "0";
|
||||
var g_menu = null;
|
||||
var g_callMenu = null;
|
||||
var g_ageMenu = null;
|
||||
|
@ -72,6 +73,7 @@ var g_defaultSettings = {
|
|||
wantMinDB: false,
|
||||
wantMinFreq: false,
|
||||
wantMaxFreq: false,
|
||||
wantRRCQ: false,
|
||||
maxDT: 0.5,
|
||||
minDb: -25,
|
||||
minFreq: 0,
|
||||
|
@ -91,7 +93,7 @@ var g_defaultSettings = {
|
|||
allOnlyNew: false,
|
||||
useRegex: false,
|
||||
callsignRegex: "",
|
||||
realtime: false,
|
||||
realtime: true,
|
||||
wanted: {
|
||||
huntCallsign: false,
|
||||
huntGrid: true,
|
||||
|
@ -105,13 +107,14 @@ var g_defaultSettings = {
|
|||
huntPX: false,
|
||||
huntPOTA: false,
|
||||
huntQRZ: true,
|
||||
huntOAMS: false,
|
||||
huntRR73: false
|
||||
huntOAMS: false
|
||||
},
|
||||
columns: {
|
||||
Callsign: true,
|
||||
Band: false,
|
||||
Mode: false,
|
||||
Calling: true,
|
||||
Grid: true,
|
||||
Msg: false,
|
||||
DXCC: true,
|
||||
Flag: true,
|
||||
|
@ -137,7 +140,7 @@ var g_defaultSettings = {
|
|||
},
|
||||
reference: 0,
|
||||
controls: true,
|
||||
controlsExtended: false,
|
||||
controlsExtended: true,
|
||||
compact: false,
|
||||
settingProfiles: false,
|
||||
|
||||
|
@ -333,12 +336,13 @@ function viewRoster()
|
|||
function realtimeRoster()
|
||||
{
|
||||
let now = timeNowSec();
|
||||
g_day = now / 86400;
|
||||
|
||||
g_day = parseInt(now / 86400);
|
||||
g_dayAsString = String(g_day);
|
||||
|
||||
if (g_rosterSettings.realtime == false) return;
|
||||
|
||||
let timeCols = document.getElementsByClassName("timeCol");
|
||||
for (let x in timeCols)
|
||||
for (const x in timeCols)
|
||||
{
|
||||
if ((typeof timeCols[x].id != "undefined") && (typeof callRoster[timeCols[x].id.substr(2)] != "undefined"))
|
||||
{
|
||||
|
@ -347,7 +351,7 @@ function realtimeRoster()
|
|||
}
|
||||
}
|
||||
let lifeCols = document.getElementsByClassName("lifeCol");
|
||||
for (let x in lifeCols)
|
||||
for (const x in lifeCols)
|
||||
{
|
||||
if ((typeof lifeCols[x].id != "undefined") && (typeof callRoster[lifeCols[x].id.substr(2)] != "undefined"))
|
||||
{
|
||||
|
@ -358,7 +362,7 @@ function realtimeRoster()
|
|||
if (g_rosterSettings.columns.Spot)
|
||||
{
|
||||
let spotCols = document.getElementsByClassName("spotCol");
|
||||
for (let x in spotCols)
|
||||
for (const x in spotCols)
|
||||
{
|
||||
if ((typeof spotCols[x].id != "undefined") && (typeof callRoster[spotCols[x].id.substr(2)] != "undefined"))
|
||||
{
|
||||
|
@ -448,7 +452,7 @@ function updateInstances()
|
|||
let worker = "";
|
||||
|
||||
let keys = Object.keys(instances).sort();
|
||||
for (let key in keys)
|
||||
for (const key in keys)
|
||||
{
|
||||
let inst = keys[key];
|
||||
let sp = inst.split(" - ");
|
||||
|
@ -544,7 +548,7 @@ function createSelectOptions(
|
|||
{
|
||||
obj = Object.keys(forObject).sort();
|
||||
}
|
||||
for (let k in obj)
|
||||
for (const k in obj)
|
||||
{
|
||||
let opt = obj[k];
|
||||
let option = document.createElement("option");
|
||||
|
@ -622,7 +626,7 @@ function updateAwardList(target = null)
|
|||
|
||||
let keys = Object.keys(g_awardTracker).sort();
|
||||
|
||||
for (let key in keys)
|
||||
for (const key in keys)
|
||||
{
|
||||
let award = g_awardTracker[keys[key]];
|
||||
let rule = g_awards[award.sponsor].awards[award.name].rule;
|
||||
|
@ -842,15 +846,18 @@ function setVisual()
|
|||
if (g_rosterSettings.controlsExtended)
|
||||
{
|
||||
RosterControls.className = "extended";
|
||||
instancesWrapper.style.display = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
RosterControls.className = "normal";
|
||||
instancesWrapper.style.display = "none";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RosterControls.className = "hidden";
|
||||
instancesWrapper.style.display = "none";
|
||||
}
|
||||
|
||||
// Award Hunter
|
||||
|
@ -1010,7 +1017,7 @@ function wantedChanged(element)
|
|||
{
|
||||
g_rosterSettings.columns[t] = true;
|
||||
|
||||
for (let i = 0; i < g_menu.items.length; ++i)
|
||||
for (const i in g_menu.items)
|
||||
{
|
||||
if (
|
||||
typeof g_menu.items[i].checked != "undefined" &&
|
||||
|
@ -1024,7 +1031,7 @@ function wantedChanged(element)
|
|||
writeRosterSettings();
|
||||
|
||||
g_scriptReport = Object();
|
||||
for (let callHash in window.opener.g_callRoster)
|
||||
for (const callHash in window.opener.g_callRoster)
|
||||
{
|
||||
window.opener.g_callRoster[callHash].callObj.alerted = false;
|
||||
}
|
||||
|
@ -1044,6 +1051,7 @@ function valuesChanged()
|
|||
g_rosterSettings.wantMinDB = wantMinDB.checked;
|
||||
g_rosterSettings.wantMinFreq = wantMinFreq.checked;
|
||||
g_rosterSettings.wantMaxFreq = wantMaxFreq.checked;
|
||||
g_rosterSettings.wantRRCQ = wantRRCQ.checked;
|
||||
|
||||
maxDTView.innerHTML = g_rosterSettings.maxDT = maxDT.value;
|
||||
minDbView.innerHTML = g_rosterSettings.minDb = minDb.value;
|
||||
|
@ -1480,6 +1488,7 @@ function openIgnoreEdit()
|
|||
worker += "</table></div>";
|
||||
|
||||
editTables.innerHTML = worker;
|
||||
editView.style.height = (window.innerHeight - 50) + "px";
|
||||
}
|
||||
|
||||
function onMyKeyDown(event)
|
||||
|
@ -1718,23 +1727,25 @@ function init()
|
|||
for (let columnIndex in g_rosterSettings.columnOrder)
|
||||
{
|
||||
let key = g_rosterSettings.columnOrder[columnIndex];
|
||||
if (key != "Callsign")
|
||||
{
|
||||
let itemx = new nw.MenuItem({
|
||||
type: "checkbox",
|
||||
label: key,
|
||||
checked: g_rosterSettings.columns[key],
|
||||
click: function ()
|
||||
{
|
||||
g_rosterSettings.columns[this.label] = this.checked;
|
||||
if (this.label == "Spot")
|
||||
{ window.opener.setRosterSpot(g_rosterSettings.columns.Spot); }
|
||||
writeRosterSettings();
|
||||
window.opener.goProcessRoster();
|
||||
resize();
|
||||
}
|
||||
});
|
||||
|
||||
let itemx = new nw.MenuItem({
|
||||
type: "checkbox",
|
||||
label: key,
|
||||
checked: g_rosterSettings.columns[key],
|
||||
click: function ()
|
||||
{
|
||||
g_rosterSettings.columns[this.label] = this.checked;
|
||||
if (this.label == "Spot")
|
||||
{ window.opener.setRosterSpot(g_rosterSettings.columns.Spot); }
|
||||
writeRosterSettings();
|
||||
window.opener.goProcessRoster();
|
||||
resize();
|
||||
}
|
||||
});
|
||||
|
||||
g_menu.append(itemx);
|
||||
g_menu.append(itemx);
|
||||
}
|
||||
}
|
||||
|
||||
item = new nw.MenuItem({ type: "separator" });
|
||||
|
@ -2027,6 +2038,7 @@ function init()
|
|||
wantMinDB.checked = g_rosterSettings.wantMinDB;
|
||||
wantMinFreq.checked = g_rosterSettings.wantMinFreq;
|
||||
wantMaxFreq.checked = g_rosterSettings.wantMaxFreq;
|
||||
wantRRCQ.checked = g_rosterSettings.wantRRCQ;
|
||||
|
||||
maxDTView.innerHTML = maxDT.value = g_rosterSettings.maxDT;
|
||||
minDbView.innerHTML = minDb.value = g_rosterSettings.minDb;
|
||||
|
@ -2173,8 +2185,11 @@ function handleContextMenu(ev)
|
|||
}
|
||||
}
|
||||
|
||||
let name
|
||||
if (ev.target.tagName == "TD") name = ev.target.getAttribute("name");
|
||||
let name = "";
|
||||
if (ev.target.tagName == "TD")
|
||||
{
|
||||
name = ev.target.getAttribute("name");
|
||||
}
|
||||
|
||||
if (name == "Callsign")
|
||||
{
|
||||
|
@ -2204,7 +2219,7 @@ function handleContextMenu(ev)
|
|||
g_targetITUz = ev.target.parentNode.id;
|
||||
g_ITUzMenu.popup(mouseX, mouseY);
|
||||
}
|
||||
else if (name && name.startsWith("DXCC"))
|
||||
else if (name.startsWith("DXCC"))
|
||||
{
|
||||
let dxcca = name.split("(");
|
||||
let dxcc = parseInt(dxcca[1]);
|
||||
|
@ -2332,29 +2347,9 @@ function processAward(awardName)
|
|||
if (Index > -1) mode.splice(Index, 1);
|
||||
|
||||
test.mode = mode.length > 0;
|
||||
|
||||
test.confirmed =
|
||||
"qsl_req" in
|
||||
g_awards[g_awardTracker[awardName].sponsor].awards[
|
||||
g_awardTracker[awardName].name
|
||||
].rule
|
||||
? g_awards[g_awardTracker[awardName].sponsor].awards[
|
||||
g_awardTracker[awardName].name
|
||||
].rule.qsl_req == "confirmed"
|
||||
: g_awards[g_awardTracker[awardName].sponsor].qsl_req == "confirmed";
|
||||
|
||||
test.confirmed = "qsl_req" in g_awards[g_awardTracker[awardName].sponsor].awards[g_awardTracker[awardName].name].rule ? g_awards[g_awardTracker[awardName].sponsor].awards[g_awardTracker[awardName].name].rule.qsl_req == "confirmed" : g_awards[g_awardTracker[awardName].sponsor].qsl_req == "confirmed";
|
||||
test.look = "confirmed";
|
||||
|
||||
test.qsl_req =
|
||||
"qsl_req" in
|
||||
g_awards[g_awardTracker[awardName].sponsor].awards[
|
||||
g_awardTracker[awardName].name
|
||||
].rule
|
||||
? g_awards[g_awardTracker[awardName].sponsor].awards[
|
||||
g_awardTracker[awardName].name
|
||||
].rule.qsl_req
|
||||
: g_awards[g_awardTracker[awardName].sponsor].qsl_req;
|
||||
|
||||
test.qsl_req = "qsl_req" in g_awards[g_awardTracker[awardName].sponsor].awards[g_awardTracker[awardName].name].rule ? g_awards[g_awardTracker[awardName].sponsor].awards[g_awardTracker[awardName].name].rule.qsl_req : g_awards[g_awardTracker[awardName].sponsor].qsl_req;
|
||||
test.DEcall = "call" in award.rule;
|
||||
test.band = "band" in award.rule && award.rule.band.indexOf("Mixed") == -1;
|
||||
test.dxcc = "dxcc" in award.rule;
|
||||
|
@ -2741,7 +2736,7 @@ function testAcont52band(award, obj, baseHash)
|
|||
|
||||
function scoreAgrids(award, obj)
|
||||
{
|
||||
if (obj.grid)
|
||||
if (obj.grid && obj.grid.length > 0)
|
||||
{
|
||||
let grid = obj.grid.substr(0, 4);
|
||||
|
||||
|
@ -2763,6 +2758,10 @@ function testAgrids(award, obj, baseHash)
|
|||
{
|
||||
return false;
|
||||
}
|
||||
if (!obj.grid || obj.grid.length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -74,6 +74,7 @@ function prepareRosterSettings()
|
|||
}
|
||||
else
|
||||
{
|
||||
console.log("Invalid/Unknown huntNeed");
|
||||
rosterSettings.huntIndex = false;
|
||||
rosterSettings.workedIndex = false;
|
||||
rosterSettings.layeredMode = false;
|
||||
|
|
|
@ -45,18 +45,6 @@ function processRosterFiltering(callRoster, rosterSettings)
|
|||
entry.tx = false;
|
||||
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)
|
||||
{
|
||||
entry.tx = false;
|
||||
|
@ -72,10 +60,21 @@ function processRosterFiltering(callRoster, rosterSettings)
|
|||
entry.tx = false;
|
||||
continue;
|
||||
}
|
||||
if (g_rosterSettings.cqOnly == true && callObj.CQ == false)
|
||||
if (g_rosterSettings.cqOnly == true)
|
||||
{
|
||||
entry.tx = false;
|
||||
continue;
|
||||
if (g_rosterSettings.wantRRCQ)
|
||||
{
|
||||
if (callObj.RR73 == false && callObj.CQ == false)
|
||||
{
|
||||
entry.tx = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (callObj.CQ == false)
|
||||
{
|
||||
entry.tx = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (g_rosterSettings.useRegex && g_rosterSettings.callsignRegex.length > 0)
|
||||
{
|
||||
|
@ -148,13 +147,10 @@ function processRosterFiltering(callRoster, rosterSettings)
|
|||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (g_rosterSettings.onlyMyDxcc == true)
|
||||
{
|
||||
if (g_rosterSettings.onlyMyDxcc == true)
|
||||
{
|
||||
entry.tx = false;
|
||||
continue;
|
||||
}
|
||||
entry.tx = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (window.opener.g_callsignLookups.lotwUseEnable == true && g_rosterSettings.usesLoTW == true)
|
||||
|
@ -380,12 +376,8 @@ function processRosterFiltering(callRoster, rosterSettings)
|
|||
let x = g_awardTracker[award];
|
||||
|
||||
// TODO: Move award reason out of exclusions code?
|
||||
callObj.awardReason =
|
||||
g_awards[x.sponsor].awards[x.name].tooltip +
|
||||
" (" +
|
||||
g_awards[x.sponsor].sponsor +
|
||||
")";
|
||||
|
||||
callObj.awardReason = g_awards[x.sponsor].awards[x.name].tooltip + " (" + g_awards[x.sponsor].sponsor + ")";
|
||||
callObj.reason.push(x.name + " - " + x.sponsor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,12 +15,12 @@ function processRosterHunting(callRoster, rosterSettings, awardTracker)
|
|||
|
||||
const currentYear = new Date().getFullYear();
|
||||
const currentYearSuffix = `’${currentYear - 2000}`;
|
||||
|
||||
const potaEnabled = (window.opener.g_potaEnabled === 1);
|
||||
// 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?
|
||||
|
||||
// Second loop, hunting and highlighting
|
||||
for (let callHash in callRoster)
|
||||
for (const callHash in callRoster)
|
||||
{
|
||||
let entry = callRoster[callHash];
|
||||
let callObj = entry.callObj;
|
||||
|
@ -95,6 +95,8 @@ function processRosterHunting(callRoster, rosterSettings, awardTracker)
|
|||
callConf = gridConf = callingConf = dxccConf = stateConf = cntyConf = contConf = potaConf = cqzConf = ituzConf = wpxConf =
|
||||
"";
|
||||
|
||||
let cntyPointer = (callObj.cnty && callObj.qual == false) ? "cursor: pointer;" : "";
|
||||
|
||||
let hash = callsign + workHashSuffix;
|
||||
let layeredHash = layeredHashSuffix && (callsign + layeredHashSuffix)
|
||||
|
||||
|
@ -131,9 +133,9 @@ function processRosterHunting(callRoster, rosterSettings, awardTracker)
|
|||
}
|
||||
|
||||
// We only do hunt highlighting when showing all entries
|
||||
// This means "Callsigns: All Traffic", "Callsigns: All Traffic/Only Wanted" and "Logbook: Award Tracker"
|
||||
// This means "Callsigns: All Traffic", "Callsigns: All Traffic/Only Wanted"
|
||||
// There is no highlighting in other modes
|
||||
if (rosterSettings.callMode == "all")
|
||||
if (rosterSettings.callMode == "all" && rosterSettings.isAwardTracker == false)
|
||||
{
|
||||
// Skip when "only new calls"
|
||||
// Questions: Move to the first loop? Why only skip new calls in "all traffic" and not other modes?
|
||||
|
@ -163,25 +165,9 @@ function processRosterHunting(callRoster, rosterSettings, awardTracker)
|
|||
callObj.style.call = "class='dxCaller'";
|
||||
}
|
||||
}
|
||||
// award tracker overrides
|
||||
let awardTrackerOverrides = {
|
||||
call: false,
|
||||
grids: false,
|
||||
dxcc: false,
|
||||
states: false,
|
||||
cnty: false,
|
||||
cqz: false,
|
||||
px: false,
|
||||
cont: false
|
||||
};
|
||||
if (g_rosterSettings.reference == LOGBOOK_AWARD_TRACKER) {
|
||||
for (let key in awardTracker) {
|
||||
awardTrackerOverrides[awardTracker[key].rule.type] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Hunting for callsigns
|
||||
if (huntCallsign.checked || awardTrackerOverrides.call)
|
||||
if (huntCallsign.checked)
|
||||
{
|
||||
let hash = callsign + workHashSuffix;
|
||||
let layeredHash = rosterSettings.layeredMode && (callsign + layeredHashSuffix)
|
||||
|
@ -258,7 +244,7 @@ function processRosterHunting(callRoster, rosterSettings, awardTracker)
|
|||
}
|
||||
|
||||
// Hunting for grids
|
||||
if ((huntGrid.checked || awardTrackerOverrides.grids) && callObj.grid.length > 1)
|
||||
if (huntGrid.checked && callObj.grid.length > 1)
|
||||
{
|
||||
let hash = callObj.grid.substr(0, 4) + workHashSuffix;
|
||||
let layeredHash = rosterSettings.layeredMode && (callObj.grid.substr(0, 4) + layeredHashSuffix)
|
||||
|
@ -308,7 +294,7 @@ function processRosterHunting(callRoster, rosterSettings, awardTracker)
|
|||
}
|
||||
|
||||
// Hunting for DXCC
|
||||
if (huntDXCC.checked || awardTrackerOverrides.dxcc)
|
||||
if (huntDXCC.checked)
|
||||
{
|
||||
let hash = String(callObj.dxcc) + workHashSuffix;
|
||||
let layeredHash = rosterSettings.layeredMode && (String(callObj.dxcc) + layeredHashSuffix)
|
||||
|
@ -379,7 +365,7 @@ function processRosterHunting(callRoster, rosterSettings, awardTracker)
|
|||
}
|
||||
|
||||
// Hunting for US States
|
||||
if ((huntState.checked || awardTrackerOverrides.states) && window.opener.g_callsignLookups.ulsUseEnable == true)
|
||||
if (huntState.checked && window.opener.g_callsignLookups.ulsUseEnable == true)
|
||||
{
|
||||
let stateSearch = callObj.state;
|
||||
let finalDxcc = callObj.dxcc;
|
||||
|
@ -437,7 +423,7 @@ function processRosterHunting(callRoster, rosterSettings, awardTracker)
|
|||
}
|
||||
|
||||
// Hunting for US Counties
|
||||
if ((huntCounty.checked || awardTrackerOverrides.cnty) && window.opener.g_callsignLookups.ulsUseEnable == true)
|
||||
if (huntCounty.checked && window.opener.g_callsignLookups.ulsUseEnable == true)
|
||||
{
|
||||
let finalDxcc = callObj.dxcc;
|
||||
if (
|
||||
|
@ -454,7 +440,7 @@ function processRosterHunting(callRoster, rosterSettings, awardTracker)
|
|||
{
|
||||
let counties = window.opener.g_zipToCounty[callObj.zipcode];
|
||||
let foundHit = false;
|
||||
for (let cnt in counties)
|
||||
for (const cnt in counties)
|
||||
{
|
||||
let hh = counties[cnt] + workHash;
|
||||
callObj.cnty = counties[cnt];
|
||||
|
@ -492,71 +478,35 @@ function processRosterHunting(callRoster, rosterSettings, awardTracker)
|
|||
}
|
||||
|
||||
// Hunting for POTAs
|
||||
if (huntPOTA.checked == true && window.opener.g_mapSettings.offlineMode == false && callObj.pota != null)
|
||||
if (potaEnabled && huntPOTA.checked == true && callObj.pota.length > 0)
|
||||
{
|
||||
let huntTotal = callObj.pota.length;
|
||||
let huntFound = 0, layeredFound = 0, workedFound = 0, layeredWorkedFound = 0;
|
||||
let workedFound = 0;
|
||||
|
||||
for (index in callObj.pota)
|
||||
for (const index in callObj.pota)
|
||||
{
|
||||
let hash = callObj.pota[index] + workHashSuffix;
|
||||
let layeredHash = rosterSettings.layeredMode && (callObj.pota[index] + layeredHashSuffix)
|
||||
let hash = g_dayAsString + callsign + callObj.pota[index] + (rosterSettings.layeredMode ? layeredHashSuffix : workHashSuffix);
|
||||
|
||||
// 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 (rosterSettings.workedIndex && hash in rosterSettings.workedIndex.pota) workedFound++;
|
||||
}
|
||||
if (huntFound != huntTotal)
|
||||
if (workedFound != 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;
|
||||
}
|
||||
}
|
||||
|
||||
callObj.hunting.pota = "hunted";
|
||||
potaBg = `${pota}${inversionAlpha};`;
|
||||
pota = bold;
|
||||
}
|
||||
}
|
||||
|
||||
// Hunting for CQ Zones
|
||||
if (huntCQz.checked || awardTrackerOverrides.cqz)
|
||||
if (huntCQz.checked)
|
||||
{
|
||||
let huntTotal = callObj.cqza.length;
|
||||
let huntFound = 0, layeredFound = 0, workedFound = 0, layeredWorkedFound = 0, marathonFound = 0;
|
||||
|
||||
for (index in callObj.cqza)
|
||||
for (const index in callObj.cqza)
|
||||
{
|
||||
let hash = callObj.cqza[index] + workHashSuffix;
|
||||
let layeredHash = rosterSettings.layeredMode && (callObj.cqza[index] + layeredHashSuffix);
|
||||
|
@ -614,7 +564,7 @@ function processRosterHunting(callRoster, rosterSettings, awardTracker)
|
|||
}
|
||||
}
|
||||
|
||||
callObj.cqzSuffix = null
|
||||
callObj.cqzSuffix = null;
|
||||
if (huntMarathon.checked && callObj.hunting.cqz != "hunted" && callObj.hunting.cqz != "worked")
|
||||
{
|
||||
if (marathonFound != huntTotal)
|
||||
|
@ -638,7 +588,7 @@ function processRosterHunting(callRoster, rosterSettings, awardTracker)
|
|||
let huntTotal = callObj.ituza.length;
|
||||
let huntFound = 0, layeredFound = 0, workedFound = 0, layeredWorkedFound = 0;
|
||||
|
||||
for (index in callObj.ituza)
|
||||
for (const index in callObj.ituza)
|
||||
{
|
||||
let hash = callObj.ituza[index] + workHashSuffix;
|
||||
let layeredHash = rosterSettings.layeredMode && (callObj.ituza[index] + layeredHashSuffix)
|
||||
|
@ -692,7 +642,7 @@ function processRosterHunting(callRoster, rosterSettings, awardTracker)
|
|||
}
|
||||
|
||||
// Hunting for WPX (Prefixes)
|
||||
if ((huntPX.checked || awardTrackerOverrides.px) && callObj.px)
|
||||
if (huntPX.checked && callObj.px)
|
||||
{
|
||||
let hash = String(callObj.px) + workHashSuffix;
|
||||
let layeredHash = rosterSettings.layeredMode && (String(callObj.px) + layeredHashSuffix)
|
||||
|
@ -742,7 +692,7 @@ function processRosterHunting(callRoster, rosterSettings, awardTracker)
|
|||
}
|
||||
|
||||
// Hunting for Continents
|
||||
if ((huntCont.checked || awardTrackerOverrides.cont) && callObj.cont)
|
||||
if (huntCont.checked && callObj.cont)
|
||||
{
|
||||
let hash = String(callObj.cont) + workHashSuffix;
|
||||
let layeredHash = rosterSettings.layeredMode && (String(callObj.cont) + layeredHashSuffix)
|
||||
|
@ -798,12 +748,12 @@ function processRosterHunting(callRoster, rosterSettings, awardTracker)
|
|||
callingBg = "#0000FF" + inversionAlpha;
|
||||
calling = "#FFFF00;text-shadow: 0px 0px 2px #FFFF00";
|
||||
}
|
||||
else if (callObj.CQ == true && !g_rosterSettings.cqOnly)
|
||||
else if ((callObj.CQ == true || (g_rosterSettings.wantRRCQ && callObj.RR73 == true)) && !g_rosterSettings.cqOnly)
|
||||
{
|
||||
callingBg = calling + inversionAlpha;
|
||||
calling = bold;
|
||||
// If treating RR73/73 as CQ, soften highlighting to help differentiate foreshadow from an actual CQ
|
||||
if (callObj.DXcall == "RR73" || callObj.DXcall == "73")
|
||||
if (g_rosterSettings.wantRRCQ && callObj.RR73 == true)
|
||||
{
|
||||
callingConf = `${unconf}#90EE90${inversionAlpha};`;
|
||||
calling = `#90EE90${inversionAlpha};`
|
||||
|
@ -818,7 +768,7 @@ function processRosterHunting(callRoster, rosterSettings, awardTracker)
|
|||
colorObject.calling = "style='" + callingConf + "background-color:" + callingBg + ";color:" + calling + "'";
|
||||
colorObject.dxcc = "style='" + dxccConf + "background-color:" + dxccBg + ";color:" + dxcc + "'";
|
||||
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 + ";" + cntyPointer + "'";
|
||||
colorObject.pota = "style='" + potaConf + "background-color:" + potaBg + ";color:" + pota + "'";
|
||||
colorObject.cont = "style='" + contConf + "background-color:" + contBg + ";color:" + cont + "'";
|
||||
colorObject.cqz = "style='" + cqzConf + "background-color:" + cqzBg + ";color:" + cqz + "'";
|
||||
|
|
|
@ -1,21 +1,16 @@
|
|||
function renderNormalRosterHeaders(columns)
|
||||
{
|
||||
let html = "<table id='callTable' class='rosterTable' align=left><thead>"
|
||||
html = html + columns.map(column => renderHeaderForColumn(column)).join("\n")
|
||||
html = html + "</thead><tbody>"
|
||||
let html = "<table id='callTable' class='rosterTable' align=left><thead>";
|
||||
html = html + columns.map(column => renderHeaderForColumn(column)).join("");
|
||||
html = html + "</thead><tbody>";
|
||||
|
||||
return html
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderNormalRosterRow(columns, callObj)
|
||||
{
|
||||
callObj.grid4 = callObj.grid4 || (callObj.grid && callObj.grid.length > 1) ? callObj.grid.substr(0, 4) : "-";
|
||||
callObj.hash = callObj.hash || `${callObj.DEcall}${callObj.band}${callObj.mode}`;
|
||||
|
||||
let html = `<tr id='${callObj.hash}'>`;
|
||||
|
||||
html = html + columns.map(column => renderEntryForColumn(column, callObj)).join("\n")
|
||||
|
||||
html = html + columns.map(column => renderEntryForColumn(column, callObj)).join("");
|
||||
html += "</tr>";
|
||||
|
||||
return html;
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
function renderRoster(callRoster, rosterSettings)
|
||||
{
|
||||
let columnOverrides = {
|
||||
Callsign: true,
|
||||
Grid: true
|
||||
Callsign: true
|
||||
}
|
||||
|
||||
if (window.opener.g_callsignLookups.eqslUseEnable == true)
|
||||
|
@ -34,6 +33,20 @@ function renderRoster(callRoster, rosterSettings)
|
|||
columnOverrides.LoTW = false;
|
||||
}
|
||||
|
||||
if (window.opener.g_potaEnabled === 1)
|
||||
{
|
||||
huntingMatrixPotaDiv.style.display = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
huntingMatrixPotaDiv.style.display = "none";
|
||||
columnOverrides.POTA = false;
|
||||
}
|
||||
|
||||
if (rosterSettings.isAwardTracker)
|
||||
{
|
||||
columnOverrides.Wanted = true;
|
||||
}
|
||||
// dealing with spots
|
||||
if (g_rosterSettings.columns.Spot == true) onlySpotDiv.style.display = "";
|
||||
else onlySpotDiv.style.display = "none";
|
||||
|
|
|
@ -94,8 +94,8 @@ const ROSTER_COLUMNS = {
|
|||
compare: callObjSimpleComparer("grid"),
|
||||
tableData: (callObj) => ({
|
||||
rawAttrs: callObj.style.grid,
|
||||
onClick: `centerOn("${callObj.grid4}")`,
|
||||
html: callObj.grid4
|
||||
onClick: `centerOn("${callObj.grid}")`,
|
||||
html: callObj.grid
|
||||
})
|
||||
},
|
||||
|
||||
|
@ -104,7 +104,7 @@ const ROSTER_COLUMNS = {
|
|||
tableData: (callObj) => ({
|
||||
rawAttrs: callObj.style.calling,
|
||||
name: callObj.CQ ? "CQ" : "Calling",
|
||||
html: callObj.DXcall.formatCallsign()
|
||||
html: (g_rosterSettings.wantRRCQ && callObj.RR73) ? "RR73" : callObj.DXcall.formatCallsign()
|
||||
})
|
||||
},
|
||||
|
||||
|
@ -117,7 +117,7 @@ const ROSTER_COLUMNS = {
|
|||
compare: (a, b) => window.opener.myDxccCompare(a.callObj, b.callObj),
|
||||
tableData: (callObj) => ({
|
||||
title: window.opener.g_worldGeoData[window.opener.g_dxccToGeoData[callObj.dxcc]].pp,
|
||||
name: `${callObj.dxcc}`,
|
||||
name: `DXCC (${callObj.dxcc})`,
|
||||
rawAttrs: callObj.style.dxcc,
|
||||
html: [window.opener.g_dxccToAltName[callObj.dxcc], callObj.dxccSuffix].join(" ")
|
||||
})
|
||||
|
@ -143,6 +143,7 @@ const ROSTER_COLUMNS = {
|
|||
|
||||
County: {
|
||||
// Not sure why this comparison uses substring, but this is what the original code did
|
||||
// Because we're sorting on the county name, the data contains "CO,Adams", we don't want to sort by state.
|
||||
compare: getterSimpleComparer((elem) => elem.callObj.cnty && elem.callObj.cnty.substr(3)),
|
||||
tableData: (callObj) =>
|
||||
{
|
||||
|
@ -151,11 +152,12 @@ const ROSTER_COLUMNS = {
|
|||
rawAttrs: callObj.style.cnty,
|
||||
html: callObj.cnty ? window.opener.g_cntyToCounty[callObj.cnty] : ""
|
||||
}
|
||||
if (callObj.cnty && callObj.qual)
|
||||
if (callObj.cnty && callObj.qual == false)
|
||||
{
|
||||
attrs.title = "ZIP Code matches multiple counties, click to do a full lookup"
|
||||
attrs.onClick = `lookupZip("${callObj.DEcall}", "${callObj.grid4}")`
|
||||
attrs.html = `¿ ${attrs.html} ?`
|
||||
attrs.title = "Matches multiple counties, click to do a full lookup"
|
||||
attrs.onClick = `window.opener.lookupCallsign("${callObj.DEcall}", "${callObj.grid}")`
|
||||
attrs.html = attrs.html + " +" + String(window.opener.g_zipToCounty[callObj.zipcode].length - 1)
|
||||
attrs.style = "cursor: pointer; color: cyan;"
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
|
@ -306,7 +308,7 @@ const ROSTER_COLUMNS = {
|
|||
style: "color: #EEE;",
|
||||
class: "lifeCol",
|
||||
id: `lm${callObj.hash}`,
|
||||
html: (timeNowSec() - callObj.life).toDHMS15()
|
||||
html: (timeNowSec() - callObj.life).toDHMS()
|
||||
})
|
||||
},
|
||||
|
||||
|
@ -340,13 +342,12 @@ const ROSTER_COLUMNS = {
|
|||
},
|
||||
|
||||
Age: {
|
||||
compare: callObjSimpleComparer("time"),
|
||||
compare: callObjSimpleComparer("age"),
|
||||
tableData: (callObj) => ({
|
||||
style: "color: #EEE;",
|
||||
class: "timeCol",
|
||||
id: `tm${callObj.hash}`,
|
||||
title: (timeNowSec() - callObj.age).toDHMS(),
|
||||
html: (timeNowSec() - callObj.age).toDHMS15()
|
||||
html: (timeNowSec() - callObj.age).toDHMS()
|
||||
})
|
||||
},
|
||||
|
||||
|
@ -382,8 +383,8 @@ const ROSTER_COLUMNS = {
|
|||
tableData: (callObj) => ({
|
||||
name: "POTA",
|
||||
rawAttrs: callObj.style.pota,
|
||||
title: callObj.pota ? callObj.pota.name : "",
|
||||
html: callObj.pota ? callObj.pota.reference : ""
|
||||
title: potaColumnHover(callObj),
|
||||
html: potaColumnRef(callObj)
|
||||
})
|
||||
},
|
||||
|
||||
|
@ -397,6 +398,36 @@ const ROSTER_COLUMNS = {
|
|||
}
|
||||
}
|
||||
|
||||
function potaColumnRef(callObj)
|
||||
{
|
||||
if (callObj.pota.length > 0)
|
||||
{
|
||||
let value = callObj.pota[0];
|
||||
if (callObj.pota.length > 1)
|
||||
{
|
||||
value += " +" + String(callObj.pota.length - 1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function potaColumnHover(callObj)
|
||||
{
|
||||
let value = ""
|
||||
for (let i in callObj.pota)
|
||||
{
|
||||
if (callObj.pota[i] in window.opener.g_pota.parks)
|
||||
{
|
||||
value += callObj.pota[i] + " - " + window.opener.g_pota.parks[callObj.pota[i]].name + "\n";
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
WANTED_ORDER = ["call", "qrz", "cont", "dxcc", "cqz", "ituz", "dxccMarathon", "cqzMarathon", "state", "pota", "grid", "cnty", "wpx", "oams"];
|
||||
WANTED_LABELS = {
|
||||
cont: "Continent",
|
||||
|
@ -418,7 +449,19 @@ function wantedColumnParts(callObj, options)
|
|||
{
|
||||
options = options || {};
|
||||
|
||||
if (!callObj.hunting) return [];
|
||||
if (Object.keys(callObj.hunting).length == 0)
|
||||
{
|
||||
// is this an award reason?
|
||||
// Hack until I talk with seb
|
||||
if (callObj.awardReason != "Callsign")
|
||||
{
|
||||
return callObj.reason;
|
||||
}
|
||||
else
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
let parts = [];
|
||||
|
||||
|
|
|
@ -339,7 +339,7 @@ body.roster {
|
|||
|
||||
.secondaryControlGroup .columns {
|
||||
/* column-count: 2; */
|
||||
max-height: 110px;
|
||||
max-height: 135px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
|
@ -373,27 +373,9 @@ body.roster {
|
|||
padding: 2px;
|
||||
}
|
||||
|
||||
#instancesWrapper {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
justify-content: flex-start;
|
||||
min-width: 80px;
|
||||
max-width: 160px;
|
||||
margin-top: 22px;
|
||||
text-align: right;
|
||||
}
|
||||
#instancesWrapper h3 {
|
||||
text-align: left;
|
||||
font-size: 12px;
|
||||
margin: 0px;
|
||||
margin-bottom: 2px;
|
||||
padding: 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#instancesWrapper .button {
|
||||
display: block;
|
||||
display: inline-block;
|
||||
margin: 1px;
|
||||
padding: 2px;
|
||||
border-width: 1px;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "GridTracker",
|
||||
"product_string_do_not_use": "gridtracker",
|
||||
"version": "1.22.0903",
|
||||
"version": "1.22.0930",
|
||||
"betaVersion": "",
|
||||
"description": "GridTracker, an amateur radio companion",
|
||||
"author": "GridTracker.org",
|
||||
|
|