diff --git a/package.nw/lib/gt.js b/package.nw/lib/gt.js index fe6cedf..e4fd898 100644 --- a/package.nw/lib/gt.js +++ b/package.nw/lib/gt.js @@ -11165,13 +11165,18 @@ function checkForNewVersion(showUptoDate) function checkForNewAcks() { - getBuffer( - "http://app.gridtracker.org/acknowledgements.json?lang=" + g_localeString, - updateAcks, - null, - http, - 80 - ); + if (g_mapSettings.offlineMode == false) + { + getBuffer( + "http://app.gridtracker.org/acknowledgements.json", + updateAcks, + null, + "http", + 80 + ); + + setTimeout(downloadAcknowledgements, 8640000); + } } function renderBandActivity() @@ -12725,8 +12730,29 @@ function versionCheck(buffer, flag) function updateAcks(buffer) { - g_acks = JSON.parse(buffer); - fs.writeFileSync("./data/acknowledgements.json", JSON.stringify(g_acks)); + try + { + g_acknowledgedCalls = JSON.parse(buffer); + } + catch (e) + { + // can't write, somethings broke + } +} + +function readAcksFromDisk() +{ + try + { + var fileBuf = fs.readFileSync(g_NWappData + "acknowledgements.json"); + var loadedData = JSON.parse(fileBuf); + // some validation here? + g_acknowledgedCalls = loadedData; + } + catch (e) + { + // file failed to load, probably not downloaded + } } function onExitAppToGoWebsite() diff --git a/package.nw/lib/roster.js b/package.nw/lib/roster.js index 60d90fb..6d98b14 100644 --- a/package.nw/lib/roster.js +++ b/package.nw/lib/roster.js @@ -445,11 +445,1800 @@ function processRoster(roster) function viewRoster() { - var rosterSettings = prepareRosterSettings(); - processRosterFiltering(callRoster, rosterSettings); - processRosterHunting(callRoster, rosterSettings); - renderRoster(callRoster, rosterSettings); - sendAlerts(callRoster, rosterSettings); + var bands = Object(); + var modes = Object(); + + var callMode = g_rosterSettings.callsign; + var onlyHits = false; + var isAwardTracker = false; + + if (callMode == "hits") + { + callMode = "all"; + onlyHits = true; + } + if (referenceNeed.value == LOGBOOK_AWARD_TRACKER) + { + callMode = "all"; + onlyHits = false; + isAwardTracker = true; + g_rosterSettings.huntNeed = "confirmed"; + } + + var canMsg = + window.opener.g_mapSettings.offlineMode == false && + window.opener.g_appSettings.gtShareEnable == "true" && + window.opener.g_appSettings.gtMsgEnable == "true"; + + if (window.opener.g_callsignLookups.lotwUseEnable == true) + { + usesLoTWDiv.style.display = ""; + if (g_rosterSettings.usesLoTW == true) + { + maxLoTW.style.display = ""; + maxLoTWView.style.display = ""; + } + else + { + maxLoTW.style.display = "none"; + maxLoTWView.style.display = "none"; + } + } + else + { + usesLoTWDiv.style.display = "none"; + maxLoTW.style.display = "none"; + maxLoTWView.style.display = "none"; + } + + if (window.opener.g_callsignLookups.eqslUseEnable == true) useseQSLDiv.style.display = ""; + else useseQSLDiv.style.display = "none"; + + if (window.opener.g_callsignLookups.oqrsUseEnable == true) usesOQRSDiv.style.display = ""; + else usesOQRSDiv.style.display = "none"; + + if (g_rosterSettings.columns.Spot == true) onlySpotDiv.style.display = ""; + else onlySpotDiv.style.display = "none"; + + if (callMode == "all") allOnlyNewDiv.style.display = ""; + else allOnlyNewDiv.style.display = "none"; + + var huntIndex, workedIndex, layeredMode; + if (g_rosterSettings.huntNeed == "mixed") + { + huntIndex = g_confirmed; + workedIndex = g_worked; + layeredMode = LAYERED_MODE_FOR[String(g_rosterSettings.reference)]; + } + else if (g_rosterSettings.huntNeed == "worked") + { + huntIndex = g_worked; + workedIndex = false; + layeredMode = false; + } + else if (g_rosterSettings.huntNeed == "confirmed") + { + huntIndex = g_confirmed; + workedIndex = g_worked; + layeredMode = false; + } + else + { + huntIndex = false; + workedIndex = false; + layeredMode = false; + } + + var now = timeNowSec(); + + // First loop, exclude calls, mostly based on "Exceptions" settings + for (var callHash in callRoster) + { + var entry = callRoster[callHash]; + var callObj = entry.callObj; + + var call = entry.DEcall; + + entry.tx = true; + callObj.shouldAlert = false; + callObj.reason = Array(); + callObj.awardReason = "Callsign"; + + if (now - callObj.age > window.opener.g_mapSettings.rosterTime) + { + entry.tx = false; + entry.alerted = false; + callObj.qrz = false; + callObj.reset = true; + continue; + } + if (window.opener.g_instances[callObj.instance].crEnable == false) + { + entry.tx = false; + continue; + } + if (call in g_blockedCalls) + { + entry.tx = false; + continue; + } + if ( + entry.DXcall + " from All" in g_blockedCQ || + entry.DXcall + " from " + window.opener.g_dxccToAltName[callObj.dxcc] in g_blockedCQ + ) + { + entry.tx = false; + continue; + } + if (callObj.dxcc in g_blockedDxcc) + { + entry.tx = false; + continue; + } + if (g_rosterSettings.cqOnly == true && callObj.CQ == false) + { + entry.tx = false; + continue; + } + if (g_rosterSettings.useRegex && g_rosterSettings.callsignRegex.length > 0) + { + try + { + if (!call.match(g_rosterSettings.callsignRegex)) + { + entry.tx = false; + continue; + } + } + catch (e) {} + } + if (g_rosterSettings.requireGrid == true && callObj.grid.length != 4) + { + entry.tx = false; + continue; + } + if (g_rosterSettings.wantMinDB == true && entry.message.SR < g_rosterSettings.minDb) + { + entry.tx = false; + continue; + } + if (g_rosterSettings.wantMaxDT == true && Math.abs(entry.message.DT) > g_rosterSettings.maxDT) + { + entry.tx = false; + continue; + } + if (g_rosterSettings.wantMinFreq == true && entry.message.DF < g_rosterSettings.minFreq) + { + entry.tx = false; + continue; + } + if (g_rosterSettings.wantMaxFreq == true && entry.message.DF > g_rosterSettings.maxFreq) + { + entry.tx = false; + continue; + } + + if (g_rosterSettings.noMsg == true) + { + try + { + if (callObj.msg.match(g_rosterSettings.noMsgValue)) + { + entry.tx = false; + continue; + } + } + catch (e) {} + } + if (g_rosterSettings.onlyMsg == true) + { + try + { + if (!callObj.msg.match(g_rosterSettings.onlyMsgValue)) + { + entry.tx = false; + continue; + } + } + catch (e) {} + } + + if (callObj.dxcc == window.opener.g_myDXCC) + { + if (g_rosterSettings.noMyDxcc == true) + { + entry.tx = false; + continue; + } + } + else + { + if (g_rosterSettings.onlyMyDxcc == true) + { + entry.tx = false; + continue; + } + } + + if (window.opener.g_callsignLookups.lotwUseEnable == true && g_rosterSettings.usesLoTW == true) + { + if (!(call in window.opener.g_lotwCallsigns)) + { + entry.tx = false; + continue; + } + if (g_rosterSettings.maxLoTW < 27) + { + var months = (g_day - window.opener.g_lotwCallsigns[call]) / 30; + if (months > g_rosterSettings.maxLoTW) + { + entry.tx = false; + continue; + } + } + } + + if (window.opener.g_callsignLookups.eqslUseEnable == true && g_rosterSettings.useseQSL == true) + { + if (!(call in window.opener.g_eqslCallsigns)) + { + entry.tx = false; + continue; + } + } + + if (window.opener.g_callsignLookups.oqrsUseEnable == true && g_rosterSettings.usesOQRS == true) + { + if (!(call in window.opener.g_oqrsCallsigns)) + { + entry.tx = false; + continue; + } + } + + if (callMode != "all") + { + if (entry.DXcall == "CQ DX" && callObj.dxcc == window.opener.g_myDXCC) + { + entry.tx = false; + continue; + } + + var hash = hashMaker(call, callObj, g_rosterSettings.reference); + if (callMode == "worked" && hash in g_worked.call) + { + entry.tx = false; + continue; + } + if (callMode == "confirmed" && hash in g_confirmed.call) + { + entry.tx = false; + continue; + } + + if (g_rosterSettings.hunting == "grid") + { + var hash = hashMaker(callObj.grid.substr(0, 4), + callObj, g_rosterSettings.reference); + if (huntIndex && hash in huntIndex.grid) + { + entry.tx = false; + continue; + } + if (callObj.grid.length == 0) + { + entry.tx = false; + continue; + } + continue; + } + if (g_rosterSettings.hunting == "dxcc") + { + var hash = hashMaker(String(callObj.dxcc), + callObj, g_rosterSettings.reference); + + if (huntIndex && (hash in huntIndex.dxcc)) + { + entry.tx = false; + continue; + } + + continue; + } + + if (g_rosterSettings.hunting == "dxccs" && r_currentDXCCs != -1) + { + if (callObj.dxcc != r_currentDXCCs) + { + entry.tx = false; + continue; + } + } + + if (g_rosterSettings.hunting == "wpx") + { + if (String(callObj.px) == null) + { + entry.tx = false; + continue; + } + var hash = hashMaker(String(callObj.px), + callObj, g_rosterSettings.reference); + + if (huntIndex && (hash in huntIndex.px)) + { + entry.tx = false; + continue; + } + + continue; + } + + if (g_rosterSettings.hunting == "cq") + { + var huntTotal = callObj.cqza.length; + if (huntTotal == 0 || !huntIndex) + { + entry.tx = false; + continue; + } + var huntFound = 0; + for (index in callObj.cqza) + { + var hash = hashMaker(callObj.cqza[index], callObj, g_rosterSettings.reference); + + if (hash in huntIndex.cqz) huntFound++; + } + if (huntFound == huntTotal) + { + entry.tx = false; + continue; + } + + continue; + } + + if (g_rosterSettings.hunting == "itu") + { + var huntTotal = callObj.ituza.length; + if (huntTotal == 0 || !huntIndex) + { + entry.tx = false; + continue; + } + var huntFound = 0; + for (index in callObj.ituza) + { + var hash = hashMaker(callObj.ituza[index], callObj, g_rosterSettings.reference); + + if (hash in huntIndex.ituz) huntFound++; + } + if (huntFound == huntTotal) + { + entry.tx = false; + continue; + } + + if (callObj.grid.length == 0) + { + entry.tx = false; + continue; + } + continue; + } + + if (g_rosterSettings.hunting == "usstates" && window.opener.g_callsignLookups.ulsUseEnable == true) + { + var state = callObj.state; + var finalDxcc = callObj.dxcc; + if (finalDxcc == 291 || finalDxcc == 110 || finalDxcc == 6) + { + if (state in window.opener.g_StateData) + { + var hash = hashMaker(state, callObj, g_rosterSettings.reference); + + if (huntIndex && hash in huntIndex.state) + { + entry.tx = false; + continue; + } + } + else entry.tx = false; + } + else entry.tx = false; + + continue; + } + + if (g_rosterSettings.hunting == "usstate" && g_currentUSCallsigns) + { + if (call in g_currentUSCallsigns) + { + // Do Nothing + } + else + { + entry.tx = false; + continue; + } + continue; + } + } + if (isAwardTracker) + { + var tx = false; + var baseHash = hashMaker("", callObj, g_rosterSettings.reference); + + for (var award in g_awardTracker) + { + if (g_awardTracker[award].enable) + { + tx = testAward(award, callObj, baseHash); + if (tx) + { + var x = g_awardTracker[award]; + callObj.awardReason = + g_awards[x.sponsor].awards[x.name].tooltip + + " (" + + g_awards[x.sponsor].sponsor + + ")"; + + break; + } + } + } + entry.tx = tx; + } + } + + var hasGtPin = false; + + var newCallList = Array(); + var inversionAlpha = "DD"; + var row = "#000000"; + var bold = "#000000;font-weight: bold;"; + var unconf = "background-clip:padding-box;box-shadow: 0 0 7px 3px inset "; + var layeredAlpha = "77"; + var layeredInversionAlpha = "66"; + var layeredUnconf = "background-clip:padding-box;box-shadow: 0 0 4px 2px inset "; + var layeredUnconfAlpha = "AA"; + + // Second loop, hunting and highlighting + for (var callHash in callRoster) + { + var entry = callRoster[callHash]; + var callObj = entry.callObj; + + // Special case check for called station + if (callObj.qrz == true && entry.tx == false) + { + // The instance has to be enabled + if (window.opener.g_instances[callObj.instance].crEnable == true) + { + // Calling us, but we wouldn't normally display + // If they are not ignored or we're in a QSO with them, var it through + + if ((!(entry.DEcall in g_blockedCalls) && !(callObj.dxcc in g_blockedDxcc)) || + window.opener.g_instances[callObj.instance].status.DXcall == entry.DEcall) + { + entry.tx = true; + } + } + } + + // Only render entries with `tx == true`, ignore the rest + if (callObj.dxcc != -1 && entry.tx == true) + { + // In layered mode ("Hunting: mixed") the workHashSuffix becomes a more stricter 'live band', + // while the layered suffix is a broader 'mixed band' + var workHashSuffix, layeredHashSuffix; + if (layeredMode) + { + workHashSuffix = hashMaker("", callObj, layeredMode); + layeredHashSuffix = hashMaker("", callObj, g_rosterSettings.reference); + } + else + { + workHashSuffix = hashMaker("", callObj, g_rosterSettings.reference); + layeredHashSuffix = false + } + var workHash = workHashSuffix; // TODO: Remove after replacing all occurrences with Suffix + + var callsign = entry.DEcall; + + callObj.hunting = {} + callObj.callFlags = {} + + var colorObject = Object(); + + var callPointer = callObj.CQ == true ? "cursor:pointer" : ""; + + var didWork = false; + + var call = "#FFFF00"; + var grid = "#00FFFF"; + var calling = "#90EE90"; + var dxcc = "#FFA500"; + var state = "#90EE90"; + var cnty = "#CCDD00"; + var cont = "#00DDDD"; + var cqz = "#DDDDDD"; + var ituz = "#DDDDDD"; + var wpx = "#FFFF00"; + + hasGtPin = false; + var shouldAlert = false; + var callBg, gridBg, callingBg, dxccBg, stateBg, cntyBg, contBg, cqzBg, ituzBg, wpxBg, gtBg; + var callConf, gridConf, callingConf, dxccConf, stateConf, cntyConf, contConf, cqzConf, ituzConf, wpxConf; + + callBg = gridBg = callingBg = dxccBg = stateBg = cntyBg = contBg = cqzBg = ituzBg = wpxBg = gtBg = row; + + callConf = gridConf = callingConf = dxccConf = stateConf = cntyConf = contConf = cqzConf = ituzConf = wpxConf = + ""; + + var hash = callsign + workHashSuffix; + var layeredHash = layeredHashSuffix && (callsign + layeredHashSuffix) + + // Call worked in current logbook settings, regardless of hunting mode + if (hash in g_worked.call) + { + callObj.callFlags.worked = true; + didWork = true; + callConf = `${unconf}${call}${inversionAlpha};`; + + if (hash in g_confirmed.call) + { + callObj.callFlags.confirmed = true; + callPointer = "text-decoration: line-through; "; + callConf = ""; + } + } + + // Calls that have OAMS chat support + if ( + callsign in window.opener.g_gtCallsigns && + window.opener.g_gtCallsigns[callsign] in window.opener.g_gtFlagPins && + window.opener.g_gtFlagPins[window.opener.g_gtCallsigns[callsign]].canmsg == true + ) + { + callObj.callFlags.oams = true; + // grab the CID + colorObject.gt = window.opener.g_gtCallsigns[callsign]; + hasGtPin = true; + } + else + { + colorObject.gt = 0; + } + + // We only do hunt highlighting when showing all entries + // This means "Callsigns: All Traffic", "Callsigns: All Traffic/Only Wanted" and "Logbook: Award Tracker" + // There is no highlighting in other modes + if (callMode == "all") + { + // Skip when "only new calls" + // Questions: Move to the first loop? Why only skip new calls in "all traffic" and not other modes? + if (allOnlyNew.checked == true && didWork && callObj.qrz == false) + { + entry.tx = false; + continue; + } + + // Hunting for callsigns + if (huntCallsign.checked == true) + { + var hash = callsign + workHashSuffix; + var layeredHash = layeredMode && (callsign + layeredHashSuffix) + + if (huntIndex && !(hash in huntIndex.call)) + { + shouldAlert = true; + + callObj.reason.push("call"); + + if (workedIndex && hash in workedIndex.call) + { + if (layeredMode && layeredHash in huntIndex.call) + { + callObj.hunting.call = "worked-and-mixed"; + callConf = `${layeredUnconf}${call}${layeredUnconfAlpha};`; + callBg = `${call}${layeredInversionAlpha}`; + call = bold; + } + // /* Currently we don't have a way to figure out + // * if the call is worked only in this band or also others, + // * so we cannot cover this particular combination + // * and have to default to just showing it as plain "worked" + // */ + // else if (layeredMode && layeredHash in workedIndex.call) + // { + // callObj.hunting.call = "worked-and-mixed-worked"; + // callConf = `${layeredUnconf}${call}${layeredAlpha};`; + // } + else + { + callObj.hunting.call = "worked"; + callConf = `${unconf}${call}${inversionAlpha};`; + } + } + else + { + if (layeredMode && layeredHash in huntIndex.call) + { + callObj.hunting.call = "mixed"; + callBg = `${call}${layeredAlpha};`; + call = bold; + } + else if (layeredMode && layeredHash in workedIndex.call) + { + callObj.hunting.call = "mixed-worked"; + callConf = `${unconf}${call}${layeredAlpha};`; + } + else + { + callObj.hunting.call = "hunted"; + callBg = `${call}${inversionAlpha};`; + call = bold; + } + } + } + } + + // Hunting for "stations calling you" + if (huntQRZ.checked == true && callObj.qrz == true) + { + callObj.callFlags.calling = true + shouldAlert = true; + callObj.reason.push("qrz"); + } + + // Hunting for stations with OAMS + if (huntOAMS.checked == true && hasGtPin == true) + { + callObj.hunting.oams = "hunted"; + shouldAlert = true; + callObj.reason.push("oams"); + } + + // Hunting for grids + if (huntGrid.checked == true && callObj.grid.length > 1) + { + var hash = callObj.grid.substr(0, 4) + workHashSuffix; + var layeredHash = layeredMode && (callObj.grid.substr(0, 4) + layeredHashSuffix) + + if (huntIndex && !(hash in huntIndex.grid)) + { + shouldAlert = true; + + callObj.reason.push("grid"); + + if (workedIndex && hash in workedIndex.grid) + { + if (layeredMode && layeredHash in huntIndex.grid) + { + callObj.hunting.grid = "worked-and-mixed"; + gridConf = `${layeredUnconf}${grid}${layeredUnconfAlpha};`; + gridBg = `${grid}${layeredInversionAlpha}`; + grid = bold; + } + else + { + callObj.hunting.grid = "worked"; + gridConf = `${unconf}${grid}${inversionAlpha};`; + } + } + else + { + if (layeredMode && layeredHash in huntIndex.grid) + { + callObj.hunting.grid = "mixed"; + gridBg = `${grid}${layeredAlpha};`; + grid = bold; + } + else if (layeredMode && layeredHash in workedIndex.grid) + { + callObj.hunting.grid = "mixed-worked"; + gridConf = `${unconf}${grid}${layeredAlpha};`; + } + else + { + callObj.hunting.grid = "hunted"; + gridBg = `${grid}${inversionAlpha};`; + grid = bold; + } + } + } + } + + // Hunting for DXCC + if (huntDXCC.checked == true) + { + var hash = String(callObj.dxcc) + workHashSuffix; + var layeredHash = layeredMode && (String(callObj.dxcc) + layeredHashSuffix) + + if (huntIndex && !(hash in huntIndex.dxcc)) + { + shouldAlert = true; + + callObj.reason.push("dxcc"); + + if (workedIndex && hash in workedIndex.dxcc) + { + if (layeredMode && layeredHash in huntIndex.dxcc) + { + callObj.hunting.dxcc = "worked-and-mixed"; + dxccConf = `${layeredUnconf}${dxcc}${layeredUnconfAlpha};`; + dxccBg = `${dxcc}${layeredInversionAlpha}`; + dxcc = bold; + } + else + { + callObj.hunting.dxcc = "worked"; + dxccConf = `${unconf}${dxcc}${inversionAlpha};`; + } + } + else + { + if (layeredMode && layeredHash in huntIndex.dxcc) + { + callObj.hunting.dxcc = "mixed"; + dxccBg = `${dxcc}${layeredAlpha};`; + dxcc = bold; + } + else if (layeredMode && layeredHash in workedIndex.dxcc) + { + callObj.hunting.dxcc = "mixed-worked"; + dxccConf = `${unconf}${dxcc}${layeredAlpha};`; + } + else + { + callObj.hunting.dxcc = "hunted"; + dxccBg = `${dxcc}${inversionAlpha};`; + dxcc = bold; + } + } + } + } + + // Hunting for US States + if (huntState.checked == true && window.opener.g_callsignLookups.ulsUseEnable == true) + { + var stateSearch = callObj.state; + var finalDxcc = callObj.dxcc; + if (finalDxcc == 291 || finalDxcc == 110 || finalDxcc == 6) + { + if (stateSearch in window.opener.g_StateData) + { + var hash = stateSearch + workHashSuffix; + var layeredHash = layeredMode && (stateSearch + layeredHashSuffix) + + if (huntIndex && !(hash in huntIndex.state)) + { + shouldAlert = true; + + callObj.reason.push("state"); + + if (workedIndex && hash in workedIndex.state) + { + if (layeredMode && layeredHash in huntIndex.state) + { + callObj.hunting.state = "worked-and-mixed"; + stateConf = `${layeredUnconf}${state}${layeredUnconfAlpha};`; + stateBg = `${state}${layeredInversionAlpha}`; + state = bold; + } + else + { + callObj.hunting.state = "worked"; + stateConf = `${unconf}${state}${inversionAlpha};`; + } + } + else + { + if (layeredMode && layeredHash in huntIndex.state) + { + callObj.hunting.state = "mixed"; + stateBg = `${state}${layeredAlpha};`; + state = bold; + } + else if (layeredMode && layeredHash in workedIndex.state) + { + callObj.hunting.state = "mixed-worked"; + stateConf = `${unconf}${state}${layeredAlpha};`; + } + else + { + callObj.hunting.state = "hunted"; + stateBg = `${state}${inversionAlpha};`; + state = bold; + } + } + } + } + } + } + + // Hunting for US Counties + if (huntCounty.checked == true && window.opener.g_callsignLookups.ulsUseEnable == true) + { + var finalDxcc = callObj.dxcc; + if ( + callObj.cnty && + (finalDxcc == 291 || finalDxcc == 110 || finalDxcc == 6 || finalDxcc == 202) && + callObj.cnty.length > 0 + ) + { + var hash = callObj.cnty + (layeredMode ? layeredHashSuffix : workHashSuffix); + + if ((huntIndex && !(hash in huntIndex.cnty)) || callObj.qual == false) + { + if (callObj.qual == false) + { + var counties = window.opener.g_zipToCounty[callObj.zipcode]; + var foundHit = false; + for (var cnt in counties) + { + var hh = counties[cnt] + workHash; + callObj.cnty = counties[cnt]; + if (huntIndex && !(hh in huntIndex.cnty)) + { + foundHit = true; + break; + } + } + if (foundHit) shouldAlert = true; + } + else + { + shouldAlert = true; + } + + if (shouldAlert) + { + callObj.reason.push("cnty"); + + if (workedIndex && hash in workedIndex.cnty) + { + callObj.hunting.cnty = "worked"; + cntyConf = `${unconf}${cnty}${inversionAlpha};`; + } + else + { + callObj.hunting.cnty = "hunted"; + cntyBg = `${cnty}${inversionAlpha}`; + cnty = bold; + } + } + } + } + } + + // Hunting for CQ Zones + if (huntCQz.checked == true) + { + var huntTotal = callObj.cqza.length; + var huntFound = 0, layeredFound = 0, workedFound = 0, layeredWorkedFound = 0; + + for (index in callObj.cqza) + { + var hash = callObj.cqza[index] + workHashSuffix; + var layeredHash = layeredMode && (callObj.cqza[index] + layeredHashSuffix) + + if (huntIndex && hash in huntIndex.cqz) huntFound++; + if (layeredMode && layeredHash in huntIndex.cqz) layeredFound++; + if (workedIndex && hash in workedIndex.cqz) workedFound++; + if (layeredMode && layeredHash in workedIndex.cqz) layeredWorkedFound++; + } + if (huntFound != huntTotal) + { + shouldAlert = true; + callObj.reason.push("cqz"); + + if (workedIndex && workedFound == huntTotal) + { + if (layeredMode && layeredFound == huntTotal) + { + callObj.hunting.cqz = "worked-and-mixed"; + cqzConf = `${layeredUnconf}${cqz}${layeredUnconfAlpha};`; + cqzBg = `${cqz}${layeredInversionAlpha}`; + cqz = bold; + } + else + { + callObj.hunting.cqz = "worked"; + cqzConf = `${unconf}${cqz}${inversionAlpha};`; + } + } + else + { + if (layeredMode && layeredFound == huntTotal) + { + callObj.hunting.cqz = "mixed"; + cqzBg = `${cqz}${layeredAlpha};`; + cqz = bold; + } + else if (layeredMode && layeredWorkedFound == huntTotal) + { + callObj.hunting.cqz = "mixed-worked"; + cqzConf = `${unconf}${cqz}${layeredAlpha};`; + } + else + { + callObj.hunting.cqz = "hunted"; + cqzBg = `${cqz}${inversionAlpha};`; + cqz = bold; + } + } + } + } + + // Hunting for ITU Zones + if (huntITUz.checked == true) + { + var huntTotal = callObj.ituza.length; + var huntFound = 0, layeredFound = 0, workedFound = 0, layeredWorkedFound = 0; + + for (index in callObj.ituza) + { + var hash = callObj.ituza[index] + workHashSuffix; + var layeredHash = layeredMode && (callObj.ituza[index] + layeredHashSuffix) + + if (huntIndex && hash in huntIndex.ituz) huntFound++; + if (layeredMode && layeredHash in huntIndex.ituz) layeredFound++; + if (workedIndex && hash in workedIndex.ituz) workedFound++; + if (layeredMode && layeredHash in workedIndex.ituz) layeredWorkedFound++; + } + if (huntFound != huntTotal) + { + shouldAlert = true; + callObj.reason.push("ituz"); + + if (workedIndex && workedFound == huntTotal) + { + if (layeredMode && layeredFound == huntTotal) + { + callObj.hunting.ituz = "worked-and-mixed"; + ituzConf = `${layeredUnconf}${ituz}${layeredUnconfAlpha};`; + ituzBg = `${ituz}${layeredInversionAlpha}`; + ituz = bold; + } + else + { + callObj.hunting.ituz = "worked"; + ituzConf = `${unconf}${ituz}${inversionAlpha};`; + } + } + else + { + if (layeredMode && layeredFound == huntTotal) + { + callObj.hunting.ituz = "mixed"; + ituzBg = `${ituz}${layeredAlpha};`; + ituz = bold; + } + else if (layeredMode && layeredWorkedFound == huntTotal) + { + callObj.hunting.ituz = "mixed-worked"; + ituzConf = `${unconf}${ituz}${layeredAlpha};`; + } + else + { + callObj.hunting.ituz = "hunted"; + ituzBg = `${ituz}${inversionAlpha};`; + ituz = bold; + } + } + } + } + + // Hunting for WPX (Prefixes) + if (huntPX.checked == true && callObj.px) + { + var hash = String(callObj.px) + workHashSuffix; + var layeredHash = layeredMode && (String(callObj.px) + layeredHashSuffix) + + if (huntIndex && !(hash in huntIndex.px)) + { + shouldAlert = true; + + callObj.reason.push("wpx"); + + if (workedIndex && hash in workedIndex.px) + { + if (layeredMode && layeredHash in huntIndex.px) + { + callObj.hunting.wpx = "worked-and-mixed"; + wpxConf = `${layeredUnconf}${wpx}${layeredUnconfAlpha};`; + wpxBg = `${wpx}${layeredInversionAlpha}`; + wpx = bold; + } + else + { + callObj.hunting.wpx = "worked"; + wpxConf = `${unconf}${wpx}${inversionAlpha};`; + } + } + else + { + if (layeredMode && layeredHash in huntIndex.px) + { + callObj.hunting.wpx = "mixed"; + wpxBg = `${wpx}${layeredAlpha};`; + wpx = bold; + } + else if (layeredMode && layeredHash in workedIndex.px) + { + callObj.hunting.wpx = "mixed-worked"; + wpxConf = `${unconf}${wpx}${layeredAlpha};`; + } + else + { + callObj.hunting.wpx = "hunted"; + wpxBg = `${wpx}${inversionAlpha};`; + wpx = bold; + } + } + } + } + + // Hunting for Continents + if (huntCont.checked == true && callObj.cont) + { + var hash = String(callObj.cont) + workHashSuffix; + var layeredHash = layeredMode && (String(callObj.cont) + layeredHashSuffix) + + if (huntIndex && !(hash in huntIndex.cont)) + { + shouldAlert = true; + + callObj.reason.push("cont"); + + if (workedIndex && hash in workedIndex.cont) + { + if (layeredMode && layeredHash in huntIndex.cont) + { + callObj.hunting.cont = "worked-and-mixed"; + contConf = `${layeredUnconf}${cont}${layeredUnconfAlpha};`; + contBg = `${cont}${layeredInversionAlpha}`; + cont = bold; + } + else + { + callObj.hunting.cont = "worked"; + contConf = `${unconf}${cont}${inversionAlpha};`; + } + } + else + { + if (layeredMode && layeredHash in huntIndex.cont) + { + callObj.hunting.cont = "mixed"; + contBg = `${cont}${layeredAlpha};`; + cont = bold; + } + else if (layeredMode && layeredHash in workedIndex.cont) + { + callObj.hunting.cont = "mixed-worked"; + contConf = `${unconf}${cont}${layeredAlpha};`; + } + else + { + callObj.hunting.cont = "hunted"; + contBg = `${cont}${inversionAlpha};`; + cont = bold; + } + } + } + } + } + + // Station is calling us + if (callObj.DXcall == window.opener.myDEcall) + { + callingBg = "#0000FF" + inversionAlpha; + calling = "#FFFF00;text-shadow: 0px 0px 2px #FFFF00"; + } + else if (callObj.CQ == true && g_rosterSettings.cqOnly == false) + { + callingBg = calling + inversionAlpha; + calling = bold; + } + + // Assemble all styles + colorObject.call = "style='" + callConf + "background-color:" + callBg + ";color:" + + call + ";" + callPointer + "'"; + colorObject.grid = "style='" + gridConf + "background-color:" + gridBg + ";color:" + grid + ";cursor:pointer'"; + 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.cont = "style='" + contConf + "background-color:" + contBg + ";color:" + cont + "'"; + colorObject.cqz = "style='" + cqzConf + "background-color:" + cqzBg + ";color:" + cqz + "'"; + colorObject.ituz = "style='" + ituzConf + "background-color:" + ituzBg + ";color:" + ituz + "'"; + colorObject.px = "style='" + wpxConf + "background-color:" + wpxBg + ";color:" + wpx + "'"; + + // Just in case, don't alert if we worked this callsign alread + if (didWork && shouldAlert) shouldAlert = false; + + callObj.shouldAlert = shouldAlert; + + callObj.style = colorObject; + + if (g_rosterSettings.columns.Spot) + { + callObj.spot = window.opener.getSpotTime( + callObj.DEcall + callObj.mode + callObj.band + callObj.grid + ); + if (callObj.spot == null) + { + callObj.spot = { when: 0, snr: 0 }; + } + } + else + { + callObj.spot = { when: 0, snr: 0 }; + } + + modes[callObj.mode] = true; + bands[callObj.band] = true; + + newCallList.push(callObj); + } + } + + // Show the roster count in the window title + + var totalCount = Object.keys(callRoster).length; + var visibleCount = newCallList.length; + var huntedCount = newCallList.filter(obj => Object.keys(obj.hunting).length > 0).length + var countParts = []; + + if (totalCount != visibleCount) + { + countParts.push(`${totalCount} heard`); + } + + countParts.push(`${visibleCount} in roster`); + + if (huntedCount != visibleCount) + { + countParts.push(`${huntedCount} wanted`); + } + + window.document.title = `Call Roster: ${countParts.join(" • ")}`; + + // Render the roster + + if (g_rosterSettings.compact == false) + { + newCallList.sort(r_sortFunction[g_rosterSettings.lastSortIndex]); + if (g_rosterSettings.lastSortReverse == 1) + { + newCallList.reverse(); + } + } + else + { + // Age sort for now... make this happen Tag + newCallList.sort(r_sortFunction[6]).reverse(); + } + + var showBands = (Object.keys(bands).length > 1) || g_rosterSettings.columns.Band; + var showModes = (Object.keys(modes).length > 1) || g_rosterSettings.columns.Mode; + + var worker = ""; + + // Render the table headers for the regular roster table + if (g_rosterSettings.compact == false) + { + worker = ""; + + worker += ""; + + if (showBands) + { worker += ""; } + + if (showModes) + { worker += ""; } + + worker += ""; + + if (g_rosterSettings.columns.Calling) + { worker += ""; } + + if (g_rosterSettings.columns.Msg) + { worker += ""; } + + if (g_rosterSettings.columns.DXCC) + { worker += ""; } + + if (g_rosterSettings.columns.Flag) + { worker += ""; } + + if (g_rosterSettings.columns.State) + { worker += ""; } + + if (g_rosterSettings.columns.County) + { worker += ""; } + + if (g_rosterSettings.columns.Cont) + { worker += ""; } + + if (g_rosterSettings.columns.dB) + { worker += ""; } + + if (g_rosterSettings.columns.Freq) + { worker += ""; } + + if (g_rosterSettings.columns.DT) + { worker += ""; } + + if (g_rosterSettings.columns.Dist) + { + worker += ""; + } + + if (g_rosterSettings.columns.Azim) + { worker += ""; } + + if (g_rosterSettings.columns.CQz) + { worker += ""; } + + if (g_rosterSettings.columns.ITUz) + { worker += ""; } + + if (g_rosterSettings.columns.PX) + { worker += ""; } + + if (window.opener.g_callsignLookups.lotwUseEnable == true && g_rosterSettings.columns.LoTW) + { worker += ""; } + + if (window.opener.g_callsignLookups.eqslUseEnable == true && g_rosterSettings.columns.eQSL) + { worker += ""; } + + if (window.opener.g_callsignLookups.oqrsUseEnable == true && g_rosterSettings.columns.OQRS) + { worker += ""; } + + if (g_rosterSettings.columns.Spot) + { worker += ""; } + + if (g_rosterSettings.columns.Life) + { worker += ""; } + + if (g_rosterSettings.columns.OAMS) + { worker += ""; } + + if (g_rosterSettings.columns.Age) + { worker += ""; } + } + // No headers for compact roster table + else + { + worker = "
"; + } + + var shouldAlert = 0; + + // Render all rows + for (var x in newCallList) + { + var callObj = newCallList[x]; + + if (callObj.shouldAlert == false && onlyHits == true && callObj.qrz == false) + { continue; } + + var spotString = ""; + if (g_rosterSettings.columns.Spot && callObj.qrz == false) + { + spotString = getSpotString(callObj); + if (g_rosterSettings.onlySpot && spotString == "") continue; + } + var grid = callObj.grid.length > 1 ? callObj.grid.substr(0, 4) : "-"; + + var geo = window.opener.g_worldGeoData[window.opener.g_dxccToGeoData[callObj.dxcc]]; + var cqzone = grid in window.opener.g_gridToCQZone ? window.opener.g_gridToCQZone[grid].join(", ") : "-"; + var ituzone = grid in window.opener.g_gridToITUZone ? window.opener.g_gridToITUZone[grid].join(", ") : "-"; + var thisCall = callObj.DEcall; + + if (thisCall.match("^[A-Z][0-9][A-Z](/w+)?$")) + { callObj.style.call = "class='oneByOne'"; } + if (thisCall == window.opener.g_instances[callObj.instance].status.DXcall) + { + if (window.opener.g_instances[callObj.instance].status.TxEnabled == 1) + { + callObj.style.call = "class='dxCalling'"; + } + else + { + callObj.style.call = "class='dxCaller'"; + } + } + + if (g_rosterSettings.compact == false) + { + var acks = window.opener.g_acknowledgedCalls + + var thisHash = thisCall + callObj.band + callObj.mode; + var callStr = thisCall.formatCallsign() + if (acks[thisCall]) + { + callStr = `${callStr} ` + callObj.awardReason += ` - ${thisCall}: ${acks[thisCall].message}` + } + + worker += "
"; + worker += + ""; + + if (showBands) + { + worker += + ""; + } + if (showModes) + { + var color = "888888"; + if (callObj.mode in g_modeColors) + { color = g_modeColors[callObj.mode]; } + worker += + ""; + } + + worker += + ""; + if (g_rosterSettings.columns.Calling) + { + var lookString = callObj.CQ ? "name='CQ'" : "name='Calling'"; + worker += + ""; + } + if (g_rosterSettings.columns.Msg) + { worker += ""; } + + if (g_rosterSettings.columns.DXCC) + { + worker += + ""; + } + if (g_rosterSettings.columns.Flag) + { + worker += + ""; + } + if (g_rosterSettings.columns.State) + { + worker += + ""; + } + if (g_rosterSettings.columns.County) + { + worker += + ""; + } + if (g_rosterSettings.columns.Cont) + { + worker += + ""; + } + + if (g_rosterSettings.columns.dB) + { + worker += + ""; + } + if (g_rosterSettings.columns.Freq) + { worker += ""; } + if (g_rosterSettings.columns.DT) + { worker += ""; } + if (g_rosterSettings.columns.Dist) + { + worker += + ""; + } + if (g_rosterSettings.columns.Azim) + { + worker += + ""; + } + + if (g_rosterSettings.columns.CQz) + { + worker += + ""; + } + if (g_rosterSettings.columns.ITUz) + { + worker += + ""; + } + + if (g_rosterSettings.columns.PX) + { + worker += + ""; + } + + if ( + window.opener.g_callsignLookups.lotwUseEnable == true && + g_rosterSettings.columns.LoTW + ) + { + if (thisCall in window.opener.g_lotwCallsigns) + { + if (g_rosterSettings.maxLoTW < 27) + { + var months = (g_day - window.opener.g_lotwCallsigns[thisCall]) / 30; + if (months > g_rosterSettings.maxLoTW) + { + worker += + ""; + } + else + { + worker += + ""; + } + } + else + { + worker += + ""; + } + } + else worker += ""; + } + if ( + window.opener.g_callsignLookups.eqslUseEnable == true && + g_rosterSettings.columns.eQSL + ) + { + worker += + ""; + } + if ( + window.opener.g_callsignLookups.oqrsUseEnable == true && + g_rosterSettings.columns.OQRS + ) + { + worker += + ""; + } + + if (g_rosterSettings.columns.Spot) + { + worker += + ""; + } + if (g_rosterSettings.columns.Life) + { + worker += + ""; + } + + if (g_rosterSettings.columns.OAMS) + { + if (callObj.style.gt != 0) + { + if (callObj.reason.includes("oams")) + { + worker += + ""; + } + else + { + worker += + ""; + } + } + else worker += ""; + } + + if (g_rosterSettings.columns.Age) + { + worker += + ""; + } + + worker += ""; + } + else + { + var tt = + callObj.RSTsent + + "㏈, " + + parseInt(callObj.dt * 100) + + "ms, " + + callObj.delta + + "hz" + + (callObj.grid.length ? ", " + callObj.grid : "") + + ", " + + (timeNowSec() - callObj.age).toDHMS(); + worker += + "
"; + worker += + "
" + + thisCall.formatCallsign() + + "
"; + worker += + "
" + + window.opener.g_dxccToAltName[callObj.dxcc] + + "
"; + worker += "
"; + } + + if (g_rosterSettings.realtime == false) + { + var call = callObj.DEcall; + g_scriptReport[call] = Object.assign({}, callObj); + g_scriptReport[call].dxccName = + window.opener.g_dxccToAltName[callObj.dxcc]; + g_scriptReport[call].distance = parseInt( + callObj.distance * + MyCircle.validateRadius(window.opener.distanceUnit.value) + ); + + delete g_scriptReport[call].DEcall; + g_scriptReport[call].rect = null; + delete g_scriptReport[call].rect; + delete g_scriptReport[call].style; + delete g_scriptReport[call].wspr; + delete g_scriptReport[call].qso; + delete g_scriptReport[call].instance; + + if (callMode != "all") + { + g_scriptReport[call].shouldAlert = true; + g_scriptReport[call].reason.push(g_rosterSettings.hunting); + } + } + + if ( + callObj.alerted == false && + callMode == "all" && + callObj.shouldAlert == true + ) + { + callObj.alerted = true; + shouldAlert++; + } + else if (callObj.alerted == false && callMode != "all") + { + callObj.alerted = true; + shouldAlert++; + } + + callObj.shouldAlert = false; + } + + if (g_rosterSettings.compact == false) + { + worker += "
CallsignBandModeGridCallingMsgDXCCFlagStateCountyContdBFreqDTDist(" + + window.opener.distanceUnit.value.toLowerCase() + ")AzimCQzITUzPXLoTWeQSLOQRSSpotLifeOAMSAge
" + + callStr + + "" + + callObj.band + + "" + callObj.mode + "" + + grid + + "" + + callObj.DXcall.formatCallsign() + + "" + callObj.msg + "" + + window.opener.g_dxccToAltName[callObj.dxcc] + "" + + (callObj.state ? callObj.state.substr(3) : "") + + "" + + (callObj.cnty + ? (callObj.qual ? "" : "~ ") + + window.opener.g_cntyToCounty[callObj.cnty] + + (callObj.qual ? "" : " ~") + : "") + + "" + + (callObj.cont ? callObj.cont : "") + + "" + + callObj.RSTsent + + "" + callObj.delta + "" + callObj.dt + "" + + parseInt( + callObj.distance * + MyCircle.validateRadius(window.opener.distanceUnit.value) + ) + + "" + + parseInt(callObj.heading) + + "" + + callObj.cqza.join(",") + + "" + + callObj.ituza.join(",") + + "" + + (callObj.px ? callObj.px : "") + + "?" + + (thisCall in window.opener.g_eqslCallsigns ? "✔" : "") + + "" + + (thisCall in window.opener.g_oqrsCallsigns ? "✔" : "") + + "" + + spotString + + "" + + (timeNowSec() - callObj.life).toDHMS() + + "" + + (timeNowSec() - callObj.age).toDHMS() + + "
"; + RosterTable.innerHTML = worker; + } + else + { + RosterTable.innerHTML = worker + ""; + } + + var dirPath = window.opener.g_scriptDir; + var scriptExists = false; + var script = "cr-alert.sh"; + + try + { + if (fs.existsSync(dirPath)) + { + if (window.opener.g_platform == "windows") + { + script = "cr-alert.bat"; + } + if ( + fs.existsSync(dirPath + script) && + g_rosterSettings.realtime == false + ) + { + scriptExists = true; + scriptIcon.innerHTML = + "
" + + (window.opener.g_crScript == 1 + ? "Script Enabled" + : "Script Disabled") + + "
"; + scriptIcon.style.display = "block"; + } + else + { + scriptIcon.style.display = "none"; + } + } + } + catch (e) {} + + if (shouldAlert > 0) + { + if (window.opener.g_classicAlerts.huntRoster == true) + { + var notify = window.opener.huntRosterNotify.value; + if (notify == "0") + { + var media = window.opener.huntRosterNotifyMedia.value; + if (media != "none") window.opener.playAlertMediaFile(media); + } + else if (notify == "1") + { + window.opener.speakAlertString( + window.opener.huntRosterNotifyWord.value + ); + } + } + + if ( + g_rosterSettings.realtime == false && + scriptExists && + window.opener.g_crScript == 1 + ) + { + try + { + fs.writeFileSync( + dirPath + "cr-alert.json", + JSON.stringify(g_scriptReport, null, 2) + ); + + var thisProc = dirPath + script; + var cp = require("child_process"); + var child = cp.spawn(thisProc, [], { + detached: true, + cwd: dirPath.slice(0, -1), + stdio: ["ignore", "ignore", "ignore"] + }); + child.unref(); + } + catch (e) + { + conosle.log(e); + } + g_scriptReport = Object(); + } + else g_scriptReport = Object(); + } } function realtimeRoster()