// GridTracker Copyright © 2023 GridTracker.org // All rights reserved. // See LICENSE for more information. const fs = require("fs"); // var CR is in screen.js CR.developerMode = process.versions["nw-flavor"] == "sdk"; CR.callRoster = {}; CR.blockedCalls = {}; CR.blockedCQ = {}; CR.blockedDxcc = {}; CR.blockedCQz = {}; CR.blockedITUz = {}; CR.scriptReport = {}; CR.worked = {}; CR.confirmed = {}; CR.modes = {}; CR.modes_phone = {}; CR.currentUSCallsigns = null; CR.currentUSState = ""; CR.currentDXCCs = -1; CR.callsignManifest = null; CR.rosterSettings = {}; CR.day = 0; CR.dayAsString = "0"; CR.menu = null; CR.callMenu = null; CR.ageMenu = null; CR.callingMenu = null; CR.compactMenu = null; CR.menuItemForCurrentColumn = null; CR.currentColumnName = null; CR.targetHash = ""; CR.clearIgnores = null; CR.clearIgnoresCall = null; CR.dxccMenu = null; CR.targetDxcc = -1; CR.clearDxccIgnore = null; CR.clearDxccIgnoreMainMenu = null; CR.CQMenu = null; CR.targetCQ = ""; CR.clearCQIgnore = null; CR.clearCQIgnoreMainMenu = null; CR.clearCQzIgnore = null; CR.clearCQzIgnoreMainMenu = null; CR.clearITUzIgnore = null; CR.clearITUzIgnoreMainMenu = null; CR.timerInterval = null; CR.typingInRoster = false; CR.awards = {}; CR.awardTypes = {}; CR.awardTracker = {}; CR.callsignDatabaseDXCC = {}; CR.callsignDatabaseUS = {}; CR.callsignDatabaseUSplus = {}; CR.modeColors = {}; CR.modeColors.FT4 = "1111FF"; CR.modeColors.FT8 = "11FF11"; CR.modeColors.JT4 = "EE1111"; CR.modeColors.JT9 = "7CFC00"; CR.modeColors.JT65 = "E550E5"; CR.modeColors.QRA64 = "FF00FF"; CR.modeColors.MSK144 = "4949FF"; CR.rosterTimeout = null; CR.rosterFocus = false; CR.defaultSettings = { callsign: "all", hunting: "dxcc", huntNeed: "confirmed", requireGrid: false, wantMaxDT: false, wantMinDB: false, wantMinFreq: false, wantMaxFreq: false, wantRRCQ: false, maxDT: 0.5, minDb: -25, minFreq: 0, maxFreq: 3500, noMyDxcc: false, onlyMyDxcc: false, noMsg: false, noMsgValue: "CQ RU", onlyMsg: false, onlyMsgValue: "CQ FD", cqOnly: true, usesLoTW: false, maxLoTW: 27, useseQSL: false, usesOQRS: false, onlySpot: false, allOnlyNew: false, useRegex: false, noUnknownDXCC: true, callsignRegex: "", huntRegexValue: "", realtime: true, wanted: { huntCallsign: false, huntGrid: true, huntDXCC: true, huntCQz: false, huntITUz: false, huntMarathon: false, huntState: false, huntCounty: false, huntCont: false, huntPX: false, huntPOTA: false, huntQRZ: true, huntOAMS: false, huntRegex: false }, columns: { Callsign: true, Band: false, Mode: false, Calling: true, Rig: false, Grid: true, Msg: false, DXCC: true, Flag: true, State: true, County: false, POTA: false, Cont: false, dB: true, Freq: false, DT: false, Dist: false, Azim: true, CQz: false, ITUz: false, PX: false, LoTW: false, eQSL: false, OQRS: false, Spot: false, Life: false, OAMS: true, Age: true, UTC: true }, displayFilters: { brightness: 100, contrast: 100, saturate: 100, invert: 0, grayscale: 0, sepia: 0, huerotate: 0 }, reference: 0, controls: true, controlsExtended: true, compact: false, settingProfiles: false, sortColumn: "Age", sortReverse: true, clearRosterOnBandChange: true, rosterAlwaysOnTop: false, rosterDelayOnFocus: false, rosterDelayTime: 1500, rosterTime: 120, compactEntity: "DXCC" }; CR.def_displayFilters = { brightness: 100, contrast: 100, saturate: 100, invert: 0, sepia: 0, huerotate: 0 }; const LOGBOOK_LIVE_BAND_LIVE_MODE = "0"; const LOGBOOK_LIVE_BAND_MIX_MODE = "1"; const LOGBOOK_LIVE_BAND_DIGI_MODE = "2"; const LOGBOOK_MIX_BAND_LIVE_MODE = "3"; const LOGBOOK_MIX_BAND_MIX_MODE = "4"; const LOGBOOK_MIX_BAND_DIGI_MODE = "5"; const LOGBOOK_AWARD_TRACKER = "6"; const LAYERED_MODE_FOR = {} LAYERED_MODE_FOR[LOGBOOK_MIX_BAND_LIVE_MODE] = LOGBOOK_LIVE_BAND_LIVE_MODE; LAYERED_MODE_FOR[LOGBOOK_MIX_BAND_MIX_MODE] = LOGBOOK_LIVE_BAND_MIX_MODE; LAYERED_MODE_FOR[LOGBOOK_MIX_BAND_DIGI_MODE] = LOGBOOK_LIVE_BAND_DIGI_MODE; document.addEventListener("dragover", function (event) { event.preventDefault(); }); document.addEventListener("drop", function (event) { event.preventDefault(); }); window.addEventListener("message", receiveMessage, false); if (typeof localStorage.blockedCQ == "undefined") { localStorage.blockedCQ = "{}"; } if (typeof localStorage.blockedCQz == "undefined") { localStorage.blockedCQz = "{}"; } if (typeof localStorage.blockedITUz == "undefined") { localStorage.blockedITUz = "{}"; } if (typeof localStorage.awardTracker == "undefined") { localStorage.awardTracker = "{}"; CR.rosterSettings = {}; writeRosterSettings(); } CR.awardTracker = JSON.parse(localStorage.awardTracker); if (typeof localStorage.blockedCalls != "undefined") { CR.blockedCalls = JSON.parse(localStorage.blockedCalls); CR.blockedCQ = JSON.parse(localStorage.blockedCQ); CR.blockedDxcc = JSON.parse(localStorage.blockedDxcc); CR.blockedCQz = JSON.parse(localStorage.blockedCQz); CR.blockedITUz = JSON.parse(localStorage.blockedITUz); } function storeBlocks() { localStorage.blockedCalls = JSON.stringify(CR.blockedCalls); localStorage.blockedCQ = JSON.stringify(CR.blockedCQ); localStorage.blockedDxcc = JSON.stringify(CR.blockedDxcc); localStorage.blockedCQz = JSON.stringify(CR.blockedCQz); localStorage.blockedITUz = JSON.stringify(CR.blockedITUz); } function storeAwardTracker() { localStorage.awardTracker = JSON.stringify(CR.awardTracker); } function loadSettings() { let readSettings = {}; if (typeof localStorage.rosterSettings != "undefined") { readSettings = JSON.parse(localStorage.rosterSettings); } CR.rosterSettings = deepmerge(CR.defaultSettings, readSettings); fixLegacySettings(); writeRosterSettings(); } function fixLegacySettings() { // Not sure why, but Paul Traina added this settings cleanup in August 2020. if ("GT" in CR.rosterSettings.columns) delete CR.rosterSettings.columns.GT; // In January 2022, we refactored roster column sorting if (CR.rosterSettings.lastSortIndex) { CR.rosterSettings.sortColumn = LEGACY_COLUMN_SORT_ID[CR.rosterSettings.lastSortIndex] || "Age"; delete CR.rosterSettings.lastSortIndex; } // In January 2022, we refactored roster column sorting if (CR.rosterSettings.lastSortReverse) { CR.rosterSettings.sortReverse = CR.rosterSettings.lastSortReverse; delete CR.rosterSettings.lastSortReverse; } // In January 2022, we added a `columnOrder` setting, which we need to ensure always includes all columns CR.rosterSettings.columnOrder = validateRosterColumnOrder(CR.rosterSettings.columnOrder); } function writeRosterSettings() { localStorage.rosterSettings = JSON.stringify(CR.rosterSettings); } function isKnownCallsignDXCC(dxcc) { if (dxcc in CR.callsignDatabaseDXCC) return true; return false; } function isKnownCallsignUS(dxcc) { if (dxcc in CR.callsignDatabaseUS) return true; return false; } function isKnownCallsignUSplus(dxcc) { if (dxcc in CR.callsignDatabaseUSplus) return true; return false; } function timeNowSec() { return parseInt(Date.now() / 1000); } function lockNewWindows() { if (typeof nw != "undefined") { let gui = require("nw.gui"); let win = gui.Window.get(); win.on("new-win-policy", function (frame, url, policy) { gui.Shell.openExternal(url); policy.ignore(); }); } } function hashMaker(start, callObj, reference) { if (reference == LOGBOOK_LIVE_BAND_LIVE_MODE) return `${start}${callObj.band}${callObj.mode}`; if (reference == LOGBOOK_AWARD_TRACKER) return `${start}${callObj.band}${callObj.mode}`; if (reference == LOGBOOK_LIVE_BAND_MIX_MODE) return `${start}${callObj.band}`; if (reference == LOGBOOK_LIVE_BAND_DIGI_MODE) return `${start}${callObj.band}dg`; if (reference == LOGBOOK_MIX_BAND_LIVE_MODE) return `${start}${callObj.mode}`; if (reference == LOGBOOK_MIX_BAND_MIX_MODE) return `${start}`; if (reference == LOGBOOK_MIX_BAND_DIGI_MODE) return `${start}dg`; return ""; } function rosterInFocus() { if (CR.rosterSettings.rosterDelayOnFocus) { CR.rosterFocus = true; } } function rosterNoFocus() { CR.rosterFocus = false; if (CR.rosterTimeout != null) { nodeTimers.clearTimeout(CR.rosterTimeout); CR.rosterTimeout = null; viewRoster(); } } function processRoster(roster) { CR.callRoster = roster; if (CR.rosterTimeout != null) { nodeTimers.clearTimeout(CR.rosterTimeout); CR.rosterTimeout = null; } if (CR.rosterFocus) { CR.rosterTimeout = nodeTimers.setTimeout(viewRoster, CR.rosterSettings.rosterDelayTime); rosterDelayDiv.style.display = "inline-block"; } else { viewRoster(); } } function viewRoster() { CR.rosterTimeout = null; rosterDelayDiv.style.display = "none"; let rosterSettings = prepareRosterSettings(); processRosterFiltering(CR.callRoster, rosterSettings); processRosterHunting(CR.callRoster, rosterSettings, CR.awardTracker); renderRoster(CR.callRoster, rosterSettings); sendAlerts(CR.callRoster, rosterSettings); } function realtimeRoster() { let now = timeNowSec(); CR.day = parseInt(now / 86400); CR.dayAsString = String(CR.day); if (CR.rosterSettings.realtime == false) return; let timeCols = document.getElementsByClassName("timeCol"); for (const x in timeCols) { if ((typeof timeCols[x].id != "undefined") && (typeof CR.callRoster[timeCols[x].id.substr(2)] != "undefined")) { let when = now - CR.callRoster[timeCols[x].id.substr(2)].callObj.age; timeCols[x].innerHTML = toDHMS(when); } } let lifeCols = document.getElementsByClassName("lifeCol"); for (const x in lifeCols) { if ((typeof lifeCols[x].id != "undefined") && (typeof CR.callRoster[lifeCols[x].id.substr(2)] != "undefined")) { let when = now - CR.callRoster[lifeCols[x].id.substr(2)].callObj.life; lifeCols[x].innerHTML = toDHMS(when); } } if (CR.rosterSettings.columns.Spot) { let spotCols = document.getElementsByClassName("spotCol"); for (const x in spotCols) { if ((typeof spotCols[x].id != "undefined") && (typeof CR.callRoster[spotCols[x].id.substr(2)] != "undefined")) { spotCols[x].innerHTML = getSpotString(CR.callRoster[spotCols[x].id.substr(2)].callObj); } } } } function getSpotString(callObj) { let result = " "; if (callObj.spot && callObj.spot.when > 0) { when = timeNowSec() - callObj.spot.when; if (when <= window.opener.GT.receptionSettings.viewHistoryTimeSec) { result = toDHM(parseInt(when)); } } if (result != " ") result += " / " + callObj.spot.snr; return result; } function openChatToCid(cid) { window.opener.showMessaging(true, cid); } function initiateQso(thisHash) { window.opener.initiateQso(thisHash); } function callLookup(thisHash, grid) { window.opener.startLookup( CR.callRoster[thisHash].DEcall, CR.callRoster[thisHash].grid ); } function callingLookup(thisHash, grid) { let thisCall = CR.callRoster[thisHash].DXcall; window.opener.startLookup(thisCall, grid); } function callGenMessage(thisHash, grid) { let thisCall = CR.callRoster[thisHash].DEcall; let instance = CR.callRoster[thisHash].callObj.instance; window.opener.startGenMessages(thisCall, grid, instance); } function callingGenMessage(thisHash, grid) { let thisCall = CR.callRoster[thisHash].DXcall; let instance = CR.callRoster[thisHash].callObj.instance; window.opener.startGenMessages(thisCall, grid, instance); } function centerOn(grid) { window.opener.centerOn(grid); } function instanceChange(what) { window.opener.GT.instances[what.id].crEnable = what.checked; viewRoster(); } function updateInstances() { if (window.opener.GT.instancesIndex.length > 1) { let instances = window.opener.GT.instances; let worker = ""; let keys = Object.keys(instances).sort(); for (const key in keys) { let inst = keys[key]; let sp = inst.split(" - "); let shortInst = sp[sp.length - 1].substring(0, 18); let color = "blue"; if (instances[inst].open == false) { color = "purple"; } worker += `
` + ` ${shortInst}
` } instancesDiv.innerHTML = worker; instancesWrapper.style.display = ""; } else { instancesDiv.innerHTML = ""; instancesWrapper.style.display = "none"; } } function processStatus(newMessage) { if (newMessage.Transmitting == 0) { // Not Transmitting if (newMessage.Decoding == 1) { // Decoding txrxdec.style.backgroundColor = "Blue"; txrxdec.style.borderColor = "Cyan"; txrxdec.innerHTML = "DECODE"; } else { txrxdec.style.backgroundColor = "Green"; txrxdec.style.borderColor = "GreenYellow"; txrxdec.innerHTML = "RECEIVE"; } } else { txrxdec.style.backgroundColor = "Red"; txrxdec.style.borderColor = "Orange"; txrxdec.innerHTML = "TRANSMIT"; } } function toTitleCase(str) { return str.replace(/\w\S*/g, function (txt) { return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); }); } function newOption(value, text) { if (typeof text == "undefined") text = value; let option = document.createElement("option"); option.value = value; option.text = text; return option; } function createSelectOptions( selectElementString, selectNameDefault, forObject, altName = null, defaultValue = null, checkSponsor = null ) { let selector = document.getElementById(selectElementString); selector.innerHTML = ""; let option = document.createElement("option"); option.value = defaultValue; option.text = selectNameDefault; option.selected = true; option.disabled = true; option.style.display = "none"; selector.appendChild(option); let obj = null; if (forObject) { obj = Object.keys(forObject).sort(); } for (const k in obj) { let opt = obj[k]; let option = document.createElement("option"); option.value = opt; option.text = altName ? forObject[opt][altName] : opt; if (checkSponsor && opt + "-" + checkSponsor in CR.awardTracker) { option.disabled = true; } selector.appendChild(option); } } function awardSponsorChanged() { awardName.style.display = ""; createSelectOptions( "awardName", "Select Award", CR.awards[awardSponsor.value].awards, "name", null, awardSponsor.value ); } function awardNameChanged() { let awardToAdd = newAwardTrackerObject( awardSponsor.value, awardName.value, true ); let hash = awardToAdd.name + "-" + awardToAdd.sponsor; if (!(hash in CR.awardTracker)) { CR.awardTracker[hash] = awardToAdd; storeAwardTracker(); processAward(hash); updateAwardList(hash); viewRoster(); } createSelectOptions( "awardName", "Select Award", CR.awards[awardToAdd.sponsor].awards, "name", null, awardToAdd.sponsor ); } function updateAwardList(target = null) { let worker = ""; worker += ""; worker += ""; worker += ""; worker += "
NameAwardTrack
"; AwardWantedList.innerHTML = worker; let keys = Object.keys(CR.awardTracker).sort(); for (const key in keys) { let award = CR.awardTracker[keys[key]]; let rule = CR.awards[award.sponsor].awards[award.name].rule; let row = awardTable.insertRow(); row.id = keys[key]; let baseAward = false; let baseCount = 0; let endorseCount = 0; let endorseTotal = 0; let allEndorse = false; let tooltip = CR.awards[award.sponsor].awards[award.name].tooltip + " (" + CR.awards[award.sponsor].sponsor + ")\n"; tooltip += toTitleCase(award.test.qsl_req) + " QSO\n"; for (const mode in award.comp.counts) { tooltip += mode + "\n"; for (const count in award.comp.counts[mode]) { endorseTotal++; if (award.comp.counts[mode][count].per == 100) { baseAward = true; endorseCount++; } if (award.comp.counts[mode][count].num > baseCount) { baseCount = award.comp.counts[mode][count].num; } tooltip += "\t" + award.comp.counts[mode][count].num + "/" + count + " (" + award.comp.counts[mode][count].per + "%)\n"; let wrk = ""; if (Object.keys(award.comp.endorse).length > 0) { for (const band in award.comp.endorse[mode]) { endorseTotal++; if (award.comp.endorse[mode][band][count] == true) { endorseCount++; wrk += band + " "; } } } if (wrk.length > 0) { tooltip += "\t\t" + wrk + "\n"; } } } if (baseCount > 0 && endorseCount == endorseTotal) allEndorse = true; let cell = createCellHtml(row, award.name + " - " + award.sponsor); cell.style.textAlign = "left"; cell.style.color = "lightblue"; createCellHtml( row, (allEndorse ? "" : baseAward ? "" : baseCount > 0 ? "" : ""), tooltip ); createCell(row, "enable", award.enable, award.enable, "Toggle Tracking", true); createCellHtml(row, ""); } } function deleteAwardTracker(sender) { let id = sender.parentNode.parentNode.id; delete CR.awardTracker[id]; storeAwardTracker(); resetAwardAdd(); updateAwardList(); viewRoster(); } function awardCheckboxChanged(sender) { let awardId = sender.target.parentNode.parentNode.id; CR.awardTracker[sender.target.parentNode.parentNode.id][sender.target.name] = sender.target.checked; storeAwardTracker(); viewRoster(); } function awardValueChanged(sender) { let awardId = sender.target.parentNode.parentNode.id; CR.awardTracker[sender.target.parentNode.parentNode.id][sender.target.name] = sender.target.value; storeAwardTracker(); viewRoster(); } function createCell( row, target, value, data = null, title = null, checkbox = false ) { let cell = row.insertCell(); if (data == null) cell.innerHTML = value; if (title) cell.title = title; if (checkbox) { let x = document.createElement("INPUT"); x.setAttribute("type", "checkbox"); x.checked = value; x.name = target; x.addEventListener("change", awardCheckboxChanged); cell.appendChild(x); } else if (data) { cell.appendChild(createAwardSelector(cell, target, value, data)); } return cell; } function createCellHtml(row, html, title = null) { let cell = row.insertCell(); cell.innerHTML = html; if (title) cell.title = title; return cell; } function createAwardSelector(cell, target, value, forObject) { let selector = document.createElement("select"); selector.name = target; selector.value = value; selector.disabled = forObject.length == 1; selector.style.margin = "0px"; selector.style.padding = "1px"; if (selector.disabled) selector.style.cursor = "auto"; selector.addEventListener("change", awardValueChanged); for (const opt in forObject) { let option = document.createElement("option"); option.value = forObject[opt]; if (option.value == "Phone" || option.value == "CW") option.disabled = true; option.text = forObject[opt]; selector.appendChild(option); } return selector; } function resetAwardAdd() { awardName.style.display = "none"; createSelectOptions("awardName", "Select Award", null); createSelectOptions("awardSponsor", "Select Sponsor", CR.awards, "sponsor"); } function openAwardPopup() { awardHunterDiv.style.display = ""; resetAwardAdd(); } function closeAwardPopup() { awardHunterDiv.style.display = "none"; resetAwardAdd(); } function toggleMoreControls() { CR.rosterSettings.controlsExtended = !CR.rosterSettings.controlsExtended; localStorage.rosterSettings = JSON.stringify(CR.rosterSettings); setVisual(); } function setVisual() { huntNeed.style.display = "none"; stateSelect.style.display = "none"; DXCCsSelect.style.display = "none"; if (CR.rosterSettings.controls) { if (CR.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 if (referenceNeed.value == LOGBOOK_AWARD_TRACKER) { HuntModeControls.style.display = "none"; CallsignsControls.style.display = "none"; AwardTrackerControls.style.display = ""; updateAwardList(); } else { for (const key in CR.rosterSettings.wanted) { if (document.getElementById(key)) { document.getElementById(key).checked = CR.rosterSettings.wanted[key]; } } AwardTrackerControls.style.display = "none"; HuntModeControls.style.display = ""; huntMode.style.display = ""; CallsignsControls.style.display = ""; closeAwardPopup(); if (callsignNeed.value == "all" || callsignNeed.value == "hits") { huntingMatrixDiv.style.display = ""; huntNeed.style.display = ""; huntMode.style.display = "none"; } else { huntingMatrixDiv.style.display = "none"; huntMode.style.display = ""; if (huntMode.value != "callsign" && huntMode.value != "usstate" && huntMode.value != "dxccs") { huntNeed.style.display = ""; } if (huntMode.value == "usstate") { stateSelect.style.display = ""; } if (huntMode.value == "usstates") { huntNeed.style.display = ""; } if (huntMode.value == "dxccs") { DXCCsSelect.style.display = ""; } } } if (huntRegex.checked == true) { huntRegexValue.style.display = ""; } else { huntRegexValue.style.display = "none"; } if (wantMaxDT.checked == true) { maxDT.style.display = ""; maxDTView.style.display = ""; } else { maxDT.style.display = "none"; maxDTView.style.display = "none"; } if (wantMinDB.checked == true) { minDb.style.display = ""; minDbView.style.display = ""; } else { minDb.style.display = "none"; minDbView.style.display = "none"; } if (wantMinFreq.checked == true) { minFreq.style.display = ""; minFreqView.style.display = ""; } else { minFreq.style.display = "none"; minFreqView.style.display = "none"; } if (wantMaxFreq.checked == true) { maxFreq.style.display = ""; maxFreqView.style.display = ""; } else { maxFreq.style.display = "none"; maxFreqView.style.display = "none"; } if (useRegex.checked == true) { callsignRegex.style.display = ""; } else { callsignRegex.style.display = "none"; } if (window.opener.GT.callsignLookups.lotwUseEnable == true) { usesLoTWDiv.style.display = ""; if (CR.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.GT.callsignLookups.eqslUseEnable == true) { useseQSLDiv.style.display = ""; } else { useseQSLDiv.style.display = "none"; } if (window.opener.GT.callsignLookups.oqrsUseEnable == true) { usesOQRSDiv.style.display = ""; } else { usesOQRSDiv.style.display = "none"; } if (CR.rosterSettings.columns.Spot == true) { onlySpotDiv.style.display = ""; } else { onlySpotDiv.style.display = "none"; } if (CR.rosterSettings.callsign == "all" || CR.rosterSettings.callsign == "hits") { allOnlyNewDiv.style.display = ""; } else { allOnlyNewDiv.style.display = "none"; } rosterBody.style.display = "block"; resize(); } function wantedChanged(element) { CR.rosterSettings.wanted[element.id] = element.checked; if (element.checked == true) { let t = element.id.replace("hunt", ""); if (t in CR.rosterSettings.columns) { CR.rosterSettings.columns[t] = true; for (const i in CR.menu.items) { if ( typeof CR.menu.items[i].checked != "undefined" && CR.menu.items[i].label == t ) { CR.menu.items[i].checked = true; } } } } setVisual(); writeRosterSettings(); CR.scriptReport = Object(); for (const callHash in window.opener.GT.callRoster) { window.opener.GT.callRoster[callHash].callObj.alerted = false; } viewRoster(); } function valuesChanged() { CR.rosterSettings.callsign = callsignNeed.value; CR.rosterSettings.hunting = huntMode.value; CR.rosterSettings.huntNeed = huntNeed.value; CR.rosterSettings.requireGrid = wantGrid.checked; CR.rosterSettings.wantMaxDT = wantMaxDT.checked; CR.rosterSettings.wantMinDB = wantMinDB.checked; CR.rosterSettings.wantMinFreq = wantMinFreq.checked; CR.rosterSettings.wantMaxFreq = wantMaxFreq.checked; CR.rosterSettings.wantRRCQ = wantRRCQ.checked; maxDTView.innerHTML = CR.rosterSettings.maxDT = maxDT.value; minDbView.innerHTML = CR.rosterSettings.minDb = minDb.value; minFreqView.innerHTML = CR.rosterSettings.minFreq = minFreq.value; maxFreqView.innerHTML = CR.rosterSettings.maxFreq = maxFreq.value; CR.rosterSettings.maxLoTW = maxLoTW.value; maxLoTWView.innerHTML = CR.rosterSettings.maxLoTW < 27 ? toYM(Number(CR.rosterSettings.maxLoTW)) : ""; CR.rosterSettings.maxLoTW = maxLoTW.value; CR.rosterSettings.cqOnly = cqOnly.checked; CR.rosterSettings.noMyDxcc = noMyDxcc.checked; CR.rosterSettings.onlyMyDxcc = onlyMyDxcc.checked; if (noMsg.checked && onlyMsg.checked && noMsgValue.value == onlyMsgValue.value) { if (CR.rosterSettings.noMsg) { noMsg.checked = false; } else { onlyMsg.checked = false; } } CR.rosterSettings.noMsg = noMsg.checked; CR.rosterSettings.onlyMsg = onlyMsg.checked; CR.rosterSettings.noMsgValue = noMsgValue.value; CR.rosterSettings.onlyMsgValue = onlyMsgValue.value; CR.rosterSettings.usesLoTW = usesLoTW.checked; CR.rosterSettings.useseQSL = useseQSL.checked; CR.rosterSettings.usesOQRS = usesOQRS.checked; CR.rosterSettings.onlySpot = onlySpot.checked; CR.rosterSettings.reference = referenceNeed.value; CR.rosterSettings.allOnlyNew = allOnlyNew.checked; CR.rosterSettings.useRegex = useRegex.checked; CR.rosterSettings.callsignRegex = callsignRegex.value; CR.rosterSettings.huntRegexValue = huntRegexValue.value; CR.rosterSettings.noUnknownDXCC = noUnknownDXCC.checked; writeRosterSettings(); CR.scriptReport = Object(); for (const callHash in window.opener.GT.callRoster) { window.opener.GT.callRoster[callHash].callObj.alerted = false; } setVisual(); } function loadFilterSettings() { let filters = ""; for (const filter in CR.rosterSettings.displayFilters) { let slider = document.getElementById("filter" + filter + "Slider"); if (slider) { slider.value = CR.rosterSettings.displayFilters[filter]; let td = document.getElementById("filter" + filter + "Td"); if (filter != "huerotate") { td.innerHTML = slider.value + "%"; filters += filter + "(" + slider.value + "%) "; } else { td.innerHTML = slider.value + " deg"; filters += "hue-rotate(" + slider.value + "deg) "; } } } document.documentElement.style.filter = filters; } function filtersChanged() { for (const filter in CR.rosterSettings.displayFilters) { let slider = document.getElementById("filter" + filter + "Slider"); if (slider) { CR.rosterSettings.displayFilters[filter] = slider.value; } else { // no longer a filter, get rid of it delete CR.rosterSettings.displayFilters[filter]; } } loadFilterSettings(); } function resetFilters() { for (const filter in CR.rosterSettings.displayFilters) { CR.rosterSettings.displayFilters[filter] = CR.def_displayFilters[filter]; } loadFilterSettings(); } function getBuffer(file_url, callback, flag, mode, port, cookie) { let url = require("url"); let http = require(mode); let fileBuffer = null; let options = null; if (cookie != null) { options = { host: url.parse(file_url).host, // eslint-disable-line node/no-deprecated-api port: port, path: url.parse(file_url).path, // eslint-disable-line node/no-deprecated-api headers: { Cookie: cookie } }; } else { options = { host: url.parse(file_url).host, // eslint-disable-line node/no-deprecated-api port: port, path: url.parse(file_url).path // eslint-disable-line node/no-deprecated-api }; } http.get(options, function (res) { // let fsize = res.headers["content-length"]; let cookies = null; if (typeof res.headers["set-cookie"] != "undefined") { cookies = res.headers["set-cookie"]; } res .on("data", function (data) { if (fileBuffer == null) fileBuffer = data; else fileBuffer += data; }) .on("end", function () { if (typeof callback == "function") { // Call it, since we have confirmed it is callable callback(fileBuffer, flag, cookies); } }) .on("error", function () {}); }); } function callsignResult(buffer, flag) { let rawData = JSON.parse(buffer); CR.currentUSState = flag; CR.currentUSCallsigns = Object(); for (const key in rawData.c) CR.currentUSCallsigns[rawData.c[key]] = true; viewRoster(); } function stateChangedValue(what) { if (CR.currentUSState != stateSelect.value && stateSelect.value != "") { CR.currentUSState = stateSelect.value; if (window.opener.GT.mapSettings.offlineMode == false) { let callState = CR.currentUSState.replace("CN-", ""); getBuffer( "https://storage.googleapis.com/gt_app/callsigns/" + callState + ".callsigns.json", callsignResult, CR.currentUSState, "http", 80 ); } else { viewRoster(); CR.currentUSState = ""; CR.currentUSCallsigns = null; stateSelect.value = ""; return; } } if (stateSelect.value == "") { CR.currentUSState = ""; CR.currentUSCallsigns = null; viewRoster(); } } function DXCCsChangedValue(what) { CR.currentDXCCs = DXCCsSelect.value; viewRoster(); } function initDXCCSelector() { let items = Object.keys(window.opener.GT.dxccToAltName).sort(function (a, b) { return window.opener.GT.dxccToAltName[a].localeCompare( window.opener.GT.dxccToAltName[b] ); }); let newSelect = document.getElementById("DXCCsSelect"); for (const i in items) { let key = items[i]; if ( window.opener.GT.dxccInfo[key].geo != "deleted" ) { let option = document.createElement("option"); option.value = key; option.text = window.opener.GT.dxccToAltName[key] + " (" + window.opener.GT.dxccInfo[key].pp + ")"; newSelect.appendChild(option); } } newSelect.oninput = DXCCsChangedValue; } function manifestResult(buffer, flag) { CR.callsignManifest = JSON.parse(buffer); let newSelect = document.getElementById("stateSelect"); for (const key in CR.callsignManifest.cnt) { let option = document.createElement("option"); if (window.opener.GT.enums[key]) { option.value = key; option.text = window.opener.GT.enums[key]; } else { option.value = "CN-" + key; option.text = window.opener.GT.enums["CN-" + key]; } newSelect.appendChild(option); } newSelect.oninput = stateChangedValue; } function receiveMessage(event) {} CR.tracker = {}; function updateWorked() { CR.worked = window.opener.GT.tracker.worked; CR.confirmed = window.opener.GT.tracker.confirmed; CR.modes = window.opener.GT.modes; CR.modes_phone = window.opener.GT.modes_phone; CR.tracker = window.opener.GT.tracker; processAllAwardTrackers(); } function deleteCallsignIgnore(key) { delete CR.blockedCalls[key]; storeBlocks(); openIgnoreEdit(); viewRoster(); } function deleteDxccIgnore(key) { delete CR.blockedDxcc[key]; storeBlocks(); openIgnoreEdit(); viewRoster(); } function deleteCQIgnore(key) { delete CR.blockedCQ[key]; storeBlocks(); openIgnoreEdit(); viewRoster(); } function deleteCQzIgnore(key) { delete CR.blockedCQz[key]; storeBlocks(); openIgnoreEdit(); viewRoster(); } function deleteITUzIgnore(key) { delete CR.blockedITUz[key]; storeBlocks(); openIgnoreEdit(); viewRoster(); } function clearAllCallsignIgnores() { CR.blockedCalls = Object(); storeBlocks(); openIgnoreEdit(); viewRoster(); } function clearAllDxccIgnores() { CR.blockedDxcc = Object(); storeBlocks(); openIgnoreEdit(); viewRoster(); } function clearAllCQIgnores() { CR.blockedCQ = Object(); storeBlocks(); openIgnoreEdit(); viewRoster(); } function clearAllCQzIgnores() { CR.blockedCQz = Object(); storeBlocks(); openIgnoreEdit(); viewRoster(); } function clearAllITUzIgnores() { CR.blockedITUz = Object(); storeBlocks(); openIgnoreEdit(); viewRoster(); } function openSettings() { MainCallRoster.style.display = "block"; settingsDiv.style.display = "block"; editView.style.display = "none"; } function closeSettings() { MainCallRoster.style.display = "block"; settingsDiv.style.display = "none"; editView.style.display = "none"; } function closeEditIgnores() { MainCallRoster.style.display = "block"; editView.style.display = "none"; } function openIgnoreEdit() { MainCallRoster.style.display = "none"; editView.style.display = "inline-block"; let worker = ""; let clearString = "none"; if (Object.keys(CR.blockedCalls).length > 0) { clearString = "Clear All"; } worker += "
" + clearString + ""; Object.keys(CR.blockedCalls) .sort() .forEach(function (key, i) { worker += ""; }); worker += "
Callsigns
" + key + "
"; clearString = "none"; if (Object.keys(CR.blockedCQ).length > 0) { clearString = "Clear All"; } worker += "
" + clearString + ""; Object.keys(CR.blockedCQ) .sort() .forEach(function (key, i) { worker += ""; }); worker += "
CQ
" + key + "
"; clearString = "none"; if (Object.keys(CR.blockedDxcc).length > 0) { clearString = "Clear All"; } worker += "
" + clearString + ""; Object.keys(CR.blockedDxcc) .sort() .forEach(function (key, i) { worker += ""; }); worker += "
DXCCs
" + window.opener.GT.dxccToAltName[key] + " (" + window.opener.GT.dxccInfo[key].pp + ")
"; if (Object.keys(CR.blockedCQz).length > 0) { clearString = "Clear All"; } worker += "
" + clearString + ""; Object.keys(CR.blockedCQz) .sort() .forEach(function (key, i) { worker += ""; }); worker += "
CQ Zones
" + key + "
"; if (Object.keys(CR.blockedITUz).length > 0) { clearString = "Clear All"; } worker += "
" + clearString + ""; Object.keys(CR.blockedITUz) .sort() .forEach(function (key, i) { worker += ""; }); worker += "
ITU Zones
" + key + "
"; editTables.innerHTML = worker; editView.style.height = (window.innerHeight - 45) + "px"; } function onMyKeyDown(event) { if (!CR.typingInRoster) { if (event.code == "KeyS" && event.ctrlKey == true) { openSettings(); } else if (event.code == "KeyR" && event.ctrlKey == true) { resetFilters(); } else { window.opener.onMyKeyDown(event); } } } function blurOnEnter(ele) { if (event.key == "Enter") { ele.blur(); } } function resize() { if (editView.style.display == "inline-block") openIgnoreEdit(); window.opener.goProcessRoster(); } function init() { window.opener.GT.rosterInitialized = true; CR.callsignDatabaseDXCC = window.opener.GT.callsignDatabaseDXCC; CR.callsignDatabaseUS = window.opener.GT.callsignDatabaseUS; CR.callsignDatabaseUSplus = window.opener.GT.callsignDatabaseUSplus; loadAwardJson(); updateWorked(); // addAllAwards(); window.addEventListener("message", receiveMessage, false); lockNewWindows(); if (window.opener.GT.mapSettings.offlineMode == false) { getBuffer( "https://storage.googleapis.com/gt_app/callsigns/manifest.json", manifestResult, null, "http", 80 ); } loadSettings(); loadFilterSettings(); updateInstances(); // callback to addControls(); loadRosterI18n(); setRosterTop(); } // From i18n.js function addControls() { WANTED_LABELS = { cont: $.i18n("rosterColumns.Wanted.cont"), cqz: $.i18n("rosterColumns.Wanted.cqz"), ituz: $.i18n("rosterColumns.Wanted.ituz"), dxcc: $.i18n("rosterColumns.Wanted.dxcc"), dxccMarathon: $.i18n("rosterColumns.Wanted.dxccMarathon"), cqzMarathon: $.i18n("rosterColumns.Wanted.cqzMarathon"), state: $.i18n("rosterColumns.Wanted.state"), grid: $.i18n("rosterColumns.Wanted.grid"), cnty: $.i18n("rosterColumns.Wanted.cnty"), wpx: $.i18n("rosterColumns.Wanted.wpx"), call: $.i18n("rosterColumns.Wanted.call"), regex: $.i18n("rosterColumns.Wanted.regex"), oams: $.i18n("rosterColumns.Wanted.oams"), pota: $.i18n("rosterColumns.Wanted.pota") } window.opener.setRosterSpot(CR.rosterSettings.columns.Spot); for (const key in CR.rosterSettings.wanted) { if (document.getElementById(key)) { document.getElementById(key).checked = CR.rosterSettings.wanted[key]; } } CR.menu = new nw.Menu(); CR.compactMenu = new nw.Menu(); let showControlsText = $.i18n("roster.menu.ShowControls"); let hideControlsText = $.i18n("roster.menu.HideControls"); let item = new nw.MenuItem({ type: "normal", label: CR.rosterSettings.controls ? hideControlsText : showControlsText, click: function () { if (this.label == "Hide Controls") { this.label = showControlsText; CR.rosterSettings.controls = false; } else { this.label = hideControlsText; CR.rosterSettings.controls = true; } CR.compactMenu.items[0].label = CR.rosterSettings.controls ? hideControlsText : showControlsText; localStorage.rosterSettings = JSON.stringify(CR.rosterSettings); setVisual(); } }); CR.menu.append(item); item = new nw.MenuItem({ type: "normal", label: CR.rosterSettings.controls ? hideControlsText : showControlsText, click: function () { if (this.label == hideControlsText) { this.label = showControlsText; CR.rosterSettings.controls = false; } else { this.label = hideControlsText; CR.rosterSettings.controls = true; } CR.menu.items[0].label = CR.rosterSettings.controls ? hideControlsText : showControlsText; localStorage.rosterSettings = JSON.stringify(CR.rosterSettings); setVisual(); } }); CR.compactMenu.append(item); item = new nw.MenuItem({ type: "normal", label: $.i18n("roster.menu.Settings"), click: function () { openSettings(); } }); CR.menu.append(item); item = new nw.MenuItem({ type: "normal", label: $.i18n("roster.menu.Settings"), click: function () { openSettings(); } }); CR.compactMenu.append(item); item = new nw.MenuItem({ type: "normal", label: $.i18n("roster.menu.CompactMode"), click: function () { CR.rosterSettings.compact = true; localStorage.rosterSettings = JSON.stringify(CR.rosterSettings); resize(); } }); CR.menu.append(item); item = new nw.MenuItem({ type: "normal", label: $.i18n("roster.menu.RosterMode"), click: function () { CR.rosterSettings.compact = false; localStorage.rosterSettings = JSON.stringify(CR.rosterSettings); resize(); } }); CR.compactMenu.append(item); CR.callMenu = new nw.Menu(); item = new nw.MenuItem({ type: "normal", label: $.i18n("roster.menu.Lookup"), click: function () { callLookup(CR.targetHash, ""); } }); CR.callMenu.append(item); item = new nw.MenuItem({ type: "normal", label: $.i18n("roster.menu.GenMesg"), click: function () { callGenMessage(CR.targetHash, ""); } }); CR.callMenu.append(item); item = new nw.MenuItem({ type: "separator" }); CR.callMenu.append(item); if (window.opener.GT.pstrotatorSettings.enable) { item = new nw.MenuItem({ type: "normal", label: $.i18n("roster.menu.AimRotator"), click: function () { let target = CR.callRoster[CR.targetHash] window.opener.aimRotator(target, ""); } }); CR.callMenu.append(item); item = new nw.MenuItem({ type: "separator" }); CR.callMenu.append(item); } item = new nw.MenuItem({ type: "normal", label: $.i18n("roster.menu.IgnoreCall"), click: function () { let thisCall = CR.callRoster[CR.targetHash].DEcall; CR.blockedCalls[thisCall] = true; storeBlocks(); viewRoster(); } }); CR.callMenu.append(item); CR.callingMenu = new nw.Menu(); item = new nw.MenuItem({ type: "normal", label: $.i18n("roster.menu.Lookup"), click: function () { callingLookup(CR.targetHash, ""); } }); CR.callingMenu.append(item); item = new nw.MenuItem({ type: "normal", label: $.i18n("roster.menu.GenMesg"), click: function () { callingGenMessage(CR.targetHash, ""); } }); CR.callingMenu.append(item); item = new nw.MenuItem({ type: "separator" }); CR.menu.append(item); if (window.opener.GT.pstrotatorSettings.enable) { item = new nw.MenuItem({ type: "normal", label: $.i18n("roster.menu.AimRotator"), click: function () { let target = CR.callRoster[CR.targetHash] window.opener.aimRotator(target, ""); } }); CR.callingMenu.append(item); item = new nw.MenuItem({ type: "separator" }); CR.callingMenu.append(item); } item = new nw.MenuItem({ type: "checkbox", label: $.i18n("roster.menu.Realtime"), checked: CR.rosterSettings.realtime, click: function () { CR.rosterSettings.realtime = this.checked; writeRosterSettings(); viewRoster(); } }); CR.menu.append(item); item = new nw.MenuItem({ type: "separator" }); CR.menu.append(item); CR.menuItemForCurrentColumn = new nw.MenuItem({ type: "normal", label: $.i18n("roster.menu.MoveLeft"), click: function () { moveColumnLeft(CR.currentColumnName); } }) CR.menu.append(CR.menuItemForCurrentColumn) item = new nw.MenuItem({ type: "separator" }); CR.menu.append(item); for (const columnIndex in CR.rosterSettings.columnOrder) { let key = CR.rosterSettings.columnOrder[columnIndex]; if (key != "Callsign") { let itemx = new nw.MenuItem({ type: "checkbox", label: key, checked: CR.rosterSettings.columns[key], click: function () { CR.rosterSettings.columns[this.label] = this.checked; if (this.label == "Spot") { window.opener.setRosterSpot(CR.rosterSettings.columns.Spot); } writeRosterSettings(); viewRoster(); resize(); } }); CR.menu.append(itemx); } } item = new nw.MenuItem({ type: "separator" }); CR.menu.append(item); CR.clearIgnores = new nw.MenuItem({ type: "normal", label: "Clear Call Ignore", enabled: false, click: function () { CR.blockedCalls = Object(); storeBlocks(); viewRoster(); } }); CR.menu.append(CR.clearIgnores); CR.clearIgnoresCall = new nw.MenuItem({ type: "normal", label: "Clear Ignore", enabled: false, click: function () { CR.blockedCalls = Object(); storeBlocks(); viewRoster(); } }); CR.callMenu.append(CR.clearIgnoresCall); CR.CQMenu = new nw.Menu(); item = new nw.MenuItem({ type: "normal", label: "Ignore CQ from DXCC", click: function () { CR.blockedCQ[ CR.callRoster[CR.targetCQ].DXcall + " from " + window.opener.GT.dxccToAltName[CR.callRoster[CR.targetCQ].callObj.dxcc] ] = true; storeBlocks(); viewRoster(); } }); CR.CQMenu.append(item); item = new nw.MenuItem({ type: "normal", label: "Ignore CQ from All", click: function () { CR.blockedCQ[CR.callRoster[CR.targetCQ].DXcall + " from All"] = true; storeBlocks(); viewRoster(); } }); CR.CQMenu.append(item); CR.clearCQIgnoreMainMenu = new nw.MenuItem({ type: "normal", label: "Clear CQ Ignore", enabled: false, click: function () { CR.blockedCQ = Object(); storeBlocks(); viewRoster(); } }); CR.menu.append(CR.clearCQIgnoreMainMenu); CR.clearCQIgnore = new nw.MenuItem({ type: "normal", label: "Clear Ignore", enabled: false, click: function () { CR.blockedCQ = Object(); storeBlocks(); viewRoster(); } }); CR.CQMenu.append(CR.clearCQIgnore); item = new nw.MenuItem({ type: "normal", label: "Edit Ignores", enabled: true, click: function () { openIgnoreEdit(); } }); CR.CQMenu.append(item); CR.CQzMenu = new nw.Menu(); item = new nw.MenuItem({ type: "normal", label: "Ignore CQ Zone", click: function () { CR.blockedCQz[CR.callRoster[CR.targetCQz].callObj.cqz] = true; storeBlocks(); viewRoster(); } }); CR.CQzMenu.append(item); CR.clearCQzIgnoreMainMenu = new nw.MenuItem({ type: "normal", label: "Clear CQ Zone Ignore", enabled: false, click: function () { CR.blockedCQz = Object(); storeBlocks(); viewRoster(); } }); CR.clearCQzIgnore = new nw.MenuItem({ type: "normal", label: "Clear Ignore", enabled: false, click: function () { CR.blockedCQz = Object(); storeBlocks(); viewRoster(); } }); CR.CQzMenu.append(CR.clearCQzIgnore); CR.CQzMenu.append(CR.clearCQzIgnoreMainMenu); item = new nw.MenuItem({ type: "normal", label: "Edit Ignores", enabled: true, click: function () { openIgnoreEdit(); } }); CR.CQzMenu.append(item); CR.ITUzMenu = new nw.Menu(); item = new nw.MenuItem({ type: "normal", label: "Ignore ITU Zone", click: function () { CR.blockedITUz[CR.callRoster[CR.targetITUz].callObj.ituz] = true; storeBlocks(); viewRoster(); } }); CR.ITUzMenu.append(item); CR.clearITUzIgnoreMainMenu = new nw.MenuItem({ type: "normal", label: "Clear ITU Zone Ignore", enabled: false, click: function () { CR.blockedITUz = Object(); storeBlocks(); viewRoster(); } }); CR.ITUzMenu.append(CR.clearITUzIgnoreMainMenu); CR.clearITUzIgnore = new nw.MenuItem({ type: "normal", label: "Clear Ignore", enabled: false, click: function () { CR.blockedITUz = Object(); storeBlocks(); viewRoster(); } }); CR.ITUzMenu.append(CR.clearITUzIgnore); item = new nw.MenuItem({ type: "normal", label: "Edit Ignores", enabled: true, click: function () { openIgnoreEdit(); } }); CR.ITUzMenu.append(item); CR.dxccMenu = new nw.Menu(); item = new nw.MenuItem({ type: "normal", label: "Ignore DXCC", click: function () { CR.blockedDxcc[CR.targetDxcc] = true; storeBlocks(); viewRoster(); } }); CR.dxccMenu.append(item); CR.clearDxccIgnoreMainMenu = new nw.MenuItem({ type: "normal", label: "Clear DXCC Ignore", enabled: false, click: function () { CR.blockedDxcc = Object(); storeBlocks(); viewRoster(); } }); CR.menu.append(CR.clearDxccIgnoreMainMenu); CR.clearDxccIgnore = new nw.MenuItem({ type: "normal", label: "Clear Ignore", enabled: false, click: function () { CR.blockedDxcc = Object(); storeBlocks(); viewRoster(); } }); CR.dxccMenu.append(CR.clearDxccIgnore); item = new nw.MenuItem({ type: "normal", label: "Edit Ignores", enabled: true, click: function () { openIgnoreEdit(); } }); CR.menu.append(item); item = new nw.MenuItem({ type: "normal", label: "Edit Ignores", enabled: true, click: function () { openIgnoreEdit(); } }); CR.callMenu.append(item); item = new nw.MenuItem({ type: "normal", label: "Edit Ignores", enabled: true, click: function () { openIgnoreEdit(); } }); CR.dxccMenu.append(item); callsignNeed.value = CR.rosterSettings.callsign; huntMode.value = CR.rosterSettings.hunting; huntNeed.value = CR.rosterSettings.huntNeed; wantGrid.checked = CR.rosterSettings.requireGrid; wantMaxDT.checked = CR.rosterSettings.wantMaxDT; wantMinDB.checked = CR.rosterSettings.wantMinDB; wantMinFreq.checked = CR.rosterSettings.wantMinFreq; wantMaxFreq.checked = CR.rosterSettings.wantMaxFreq; wantRRCQ.checked = CR.rosterSettings.wantRRCQ; maxDTView.innerHTML = maxDT.value = CR.rosterSettings.maxDT; minDbView.innerHTML = minDb.value = CR.rosterSettings.minDb; minFreqView.innerHTML = minFreq.value = CR.rosterSettings.minFreq; maxFreqView.innerHTML = maxFreq.value = CR.rosterSettings.maxFreq; maxLoTW.value = CR.rosterSettings.maxLoTW; maxLoTWView.innerHTML = maxLoTW.value < 27 ? toYM(Number(maxLoTW.value)) : ""; cqOnly.checked = CR.rosterSettings.cqOnly; noMyDxcc.checked = CR.rosterSettings.noMyDxcc; onlyMyDxcc.checked = CR.rosterSettings.onlyMyDxcc; noMsg.checked = CR.rosterSettings.noMsg; onlyMsg.checked = CR.rosterSettings.onlyMsg; noMsgValue.value = CR.rosterSettings.noMsgValue; onlyMsgValue.value = CR.rosterSettings.onlyMsgValue; usesLoTW.checked = CR.rosterSettings.usesLoTW; useseQSL.checked = CR.rosterSettings.useseQSL; onlySpot.checked = CR.rosterSettings.onlySpot; usesOQRS.checked = CR.rosterSettings.usesOQRS; referenceNeed.value = CR.rosterSettings.reference; allOnlyNew.checked = CR.rosterSettings.allOnlyNew; useRegex.checked = CR.rosterSettings.useRegex; callsignRegex.value = CR.rosterSettings.callsignRegex; huntRegexValue.value = CR.rosterSettings.huntRegexValue; noUnknownDXCC.checked = CR.rosterSettings.noUnknownDXCC; clearRosterOnBandChange.checked = CR.rosterSettings.clearRosterOnBandChange; rosterAlwaysOnTop.checked = CR.rosterSettings.rosterAlwaysOnTop; rosterDelayOnFocus.checked = CR.rosterSettings.rosterDelayOnFocus; displayDelayOnFocus(); rosterDelayTime.value = CR.rosterSettings.rosterDelayTime; rosterDelayTimeTd.innerHTML = rosterDelayTime.value + "ms"; setRosterTimeView(); for (const column in ROSTER_COLUMNS) { if (column != "Callsign") { let option = newOption(column, column); if (column == CR.rosterSettings.compactEntity) { option.selected = true; } compactEntitySelect.appendChild(option); } } setVisual(); document.addEventListener("keydown", onMyKeyDown, false); initDXCCSelector(); CR.timerInterval = nodeTimers.setInterval(realtimeRoster, 1000); updateInstances(); } function compactEntityChanged() { CR.rosterSettings.compactEntity = compactEntitySelect.value; viewRoster(); } function clearRosterOnBandChangeValueChanged(what) { CR.rosterSettings.clearRosterOnBandChange = clearRosterOnBandChange.checked; writeRosterSettings(); } function rosterDelayOnFocusValueChanged(what) { CR.rosterSettings.rosterDelayOnFocus = rosterDelayOnFocus.checked; displayDelayOnFocus(); writeRosterSettings(); } function displayDelayOnFocus() { if (CR.rosterSettings.rosterDelayOnFocus) { rosterDelayTimeTd.style.display = "block"; rosterDelayTime.style.display = "block"; } else { rosterDelayTimeTd.style.display = "none"; rosterDelayTime.style.display = "none"; } } function changeRosterDelayTime() { CR.rosterSettings.rosterDelayTime = rosterDelayTime.value; rosterDelayTimeTd.innerHTML = rosterDelayTime.value + "ms"; writeRosterSettings(); } function changeRosterTime() { CR.rosterSettings.rosterTime = rosterTime.value; setRosterTimeView(); writeRosterSettings(); viewRoster(); } function changeRosterTop(butt) { CR.rosterSettings.rosterAlwaysOnTop = butt.checked; setRosterTop(); } function setRosterTop() { nw.Window.get().setAlwaysOnTop(CR.rosterSettings.rosterAlwaysOnTop); } function setRosterTimeView() { rosterTime.value = CR.rosterSettings.rosterTime; rosterTimeTd.innerHTML = toDHMS(Number(rosterTime.value)); } function handleContextMenu(ev) { if (editView.style.display == "inline-block") return false; let mouseX = Math.round(ev.x); let mouseY = Math.round(ev.y); let len = Object.keys(CR.blockedCalls).length; if (len > 0) { CR.clearIgnores.enabled = true; CR.clearIgnores.label = "Clear Call Ignore" + (len > 1 ? "s (" + len + ")" : ""); CR.clearIgnoresCall.enabled = true; CR.clearIgnoresCall.label = "Clear Ignore" + (len > 1 ? "s (" + len + ")" : ""); } else { CR.clearIgnores.label = "Clear Call Ignore"; CR.clearIgnores.enabled = false; CR.clearIgnoresCall.label = "Clear Ignore"; CR.clearIgnoresCall.enabled = false; } len = Object.keys(CR.blockedDxcc).length; if (len > 0) { CR.clearDxccIgnoreMainMenu.enabled = true; CR.clearDxccIgnoreMainMenu.label = "Clear DXCC Ignore" + (len > 1 ? "s (" + len + ")" : ""); CR.clearDxccIgnore.enabled = true; CR.clearDxccIgnore.label = "Clear Ignore" + (len > 1 ? "s (" + len + ")" : ""); } else { CR.clearDxccIgnoreMainMenu.label = "Clear DXCC Ignore"; CR.clearDxccIgnoreMainMenu.enabled = false; CR.clearDxccIgnore.label = "Clear Ignore"; CR.clearDxccIgnore.enabled = false; } len = Object.keys(CR.blockedCQ).length; if (len > 0) { CR.clearCQIgnoreMainMenu.enabled = true; CR.clearCQIgnoreMainMenu.label = "Clear CQ Ignore" + (len > 1 ? "s (" + len + ")" : ""); CR.clearCQIgnore.enabled = true; CR.clearCQIgnore.label = "Clear Ignore" + (len > 1 ? "s (" + len + ")" : ""); } else { CR.clearCQIgnoreMainMenu.label = "Clear CQ Ignore"; CR.clearCQIgnoreMainMenu.enabled = false; CR.clearCQIgnore.label = "Clear Ignore"; CR.clearCQIgnore.enabled = false; } len = Object.keys(CR.blockedCQz).length; if (len > 0) { CR.clearCQzIgnoreMainMenu.enabled = true; CR.clearCQzIgnoreMainMenu.label = "Clear CQ Zone Ignore" + (len > 1 ? "s (" + len + ")" : ""); CR.clearCQzIgnore.enabled = true; CR.clearCQzIgnore.label = "Clear Ignore" + (len > 1 ? "s (" + len + ")" : ""); } else { CR.clearCQzIgnoreMainMenu.label = "Clear CQ Zone Ignore"; CR.clearCQzIgnoreMainMenu.enabled = false; CR.clearCQzIgnore.label = "Clear Ignore"; CR.clearCQzIgnore.enabled = false; } len = Object.keys(CR.blockedITUz).length; if (len > 0) { CR.clearITUzIgnoreMainMenu.enabled = true; CR.clearITUzIgnoreMainMenu.label = "Clear ITU Zone Ignore" + (len > 1 ? "s (" + len + ")" : ""); CR.clearITUzIgnore.enabled = true; CR.clearITUzIgnore.label = "Clear Ignore" + (len > 1 ? "s (" + len + ")" : ""); } else { CR.clearITUzIgnoreMainMenu.label = "Clear ITU Zone Ignore"; CR.clearITUzIgnoreMainMenu.enabled = false; CR.clearITUzIgnore.label = "Clear Ignore"; CR.clearITUzIgnore.enabled = false; } if (typeof ev.target != "undefined") { if (CR.developerMode) { if ((ev.target.id == "ShowMoreControlsLink") || (ev.target.id == "ShowFewerControlsLink") || (ev.target.id == "txrxdec")) { // Allow event to bubble up so that NWJS will show the developer menu return true; } } let name = ""; if (ev.target.tagName == "TD" || (CR.rosterSettings.compact && ev.target.tagName == "DIV")) { name = ev.target.getAttribute("name"); } if (name == "Callsign") { CR.targetHash = ev.target.parentNode.id; CR.callMenu.popup(mouseX, mouseY); } else if (name == "Calling") { CR.targetHash = ev.target.parentNode.id; CR.callingMenu.popup(mouseX, mouseY); } else if (name == "CQ") { if (CR.callRoster[ev.target.parentNode.id].DXcall != "CQ") { CR.targetCQ = ev.target.parentNode.id; CR.CQMenu.popup(mouseX, mouseY); } } else if (name == "CQz") { CR.targetCQz = ev.target.parentNode.id; CR.CQzMenu.popup(mouseX, mouseY); } else if (name == "ITUz") { CR.targetITUz = ev.target.parentNode.id; CR.ITUzMenu.popup(mouseX, mouseY); } else if (name && name.startsWith("DXCC")) { let dxcca = name.split("("); let dxcc = parseInt(dxcca[1]); CR.targetDxcc = dxcc; CR.dxccMenu.popup(mouseX, mouseY); } else { if (CR.rosterSettings.compact) { CR.compactMenu.popup(mouseX, mouseY); } else { if (ev.target.tagName == "TH" && ev.target.getAttribute("name")) { CR.menuItemForCurrentColumn.enabled = true; CR.currentColumnName = ev.target.getAttribute("name"); } else { CR.menuItemForCurrentColumn.enabled = false; CR.currentColumnName = null; } CR.menu.popup(mouseX, mouseY); } } } else { if (CR.rosterSettings.compact == false) { CR.menu.popup(mouseX, mouseY); } else { CR.compactMenu.popup(mouseX, mouseY); } } ev.preventDefault(); return false; } function getTypeFromMode(mode) { if (mode in CR.modes) { if (CR.modes[mode] == true) return "Digital"; else if (CR.modes_phone[mode] == true) return "Phone"; } return ""; } function testAward(awardName, obj, baseHash) { if ( CR.awardTracker[awardName].test.dxcc && CR.awardTracker[awardName].rule.dxcc.indexOf(obj.dxcc) == -1 ) { return false; } if ( CR.awardTracker[awardName].test.mode && CR.awardTracker[awardName].rule.mode.indexOf(obj.mode) == -1 ) { return false; } if ( CR.awardTracker[awardName].test.band && CR.awardTracker[awardName].rule.band.indexOf(obj.band) == -1 ) { return false; } if ( CR.awardTracker[awardName].test.DEcall && CR.awardTracker[awardName].rule.call.indexOf(obj.DEcall) == -1 ) { return false; } if ( CR.awardTracker[awardName].test.cont && CR.awardTracker[awardName].rule.cont.indexOf(obj.cont) == -1 ) { return false; } if ( CR.awardTracker[awardName].test.prop && CR.awardTracker[awardName].rule.propMode != obj.propMode ) { return false; } if ( CR.awardTracker[awardName].test.sat && CR.awardTracker[awardName].rule.satName.indexOf(obj.satName) == -1 ) { return false; } return CR.awardTypes[CR.awardTracker[awardName].rule.type].test( CR.awardTracker[awardName], obj, baseHash ); } function processAward(awardName) { let award = CR.awards[CR.awardTracker[awardName].sponsor].awards[ CR.awardTracker[awardName].name ]; CR.awardTracker[awardName].rule = award.rule; let test = (CR.awardTracker[awardName].test = {}); let mode = award.rule.mode.slice(); let Index = mode.indexOf("Mixed"); if (Index > -1) mode.splice(Index, 1); Index = mode.indexOf("Digital"); if (Index > -1) mode.splice(Index, 1); Index = mode.indexOf("Phone"); if (Index > -1) mode.splice(Index, 1); test.mode = mode.length > 0; test.confirmed = "qsl_req" in CR.awards[CR.awardTracker[awardName].sponsor].awards[CR.awardTracker[awardName].name].rule ? CR.awards[CR.awardTracker[awardName].sponsor].awards[CR.awardTracker[awardName].name].rule.qsl_req == "confirmed" : CR.awards[CR.awardTracker[awardName].sponsor].qsl_req == "confirmed"; test.look = "confirmed"; test.qsl_req = "qsl_req" in CR.awards[CR.awardTracker[awardName].sponsor].awards[CR.awardTracker[awardName].name].rule ? CR.awards[CR.awardTracker[awardName].sponsor].awards[CR.awardTracker[awardName].name].rule.qsl_req : CR.awards[CR.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; test.cont = "cont" in award.rule; test.prop = "propMode" in award.rule; test.sat = "satName" in award.rule; CR.awardTracker[awardName].stat = {}; for (const i in window.opener.GT.QSOhash) { let obj = window.opener.GT.QSOhash[i]; if (test.confirmed && !obj.confirmed) continue; if (obj.dxcc < 1) continue; if (test.dxcc && award.rule.dxcc.indexOf(obj.dxcc) == -1) continue; if (test.mode && award.rule.mode.indexOf(obj.mode) == -1) continue; if (test.band && award.rule.band.indexOf(obj.band) == -1) continue; if (test.DEcall && award.rule.call.indexOf(obj.DEcall) == -1) continue; if (test.cont && award.rule.cont.indexOf(obj.cont) == -1) continue; if (test.prop && award.rule.propMode != obj.propMode) continue; if (test.sat && award.rule.satName.indexOf(obj.satName) == -1) continue; CR.awardTypes[award.rule.type].score(CR.awardTracker[awardName], obj); } CR.awardTracker[awardName].comp = CR.awardTypes[award.rule.type].compile( CR.awardTracker[awardName], CR.awardTracker[awardName].stat ); CR.awardTracker[awardName].stat = {}; } function newAwardCountObject() { let statCountObject = {}; statCountObject.bands = {}; statCountObject.bands.Mixed = {}; statCountObject.bands.Digital = {}; statCountObject.bands.Phone = {}; statCountObject.modes = {}; statCountObject.modes.Mixed = {}; statCountObject.modes.Digital = {}; statCountObject.modes.Phone = {}; statCountObject.unique = null; return statCountObject; } function workAwardObject(obj, band, mode, isDigital, isPhone, unique = null) { obj.bands.Mixed[band] = ~~obj.bands.Mixed[band] + 1; if (!(mode in obj.bands)) obj.bands[mode] = {}; obj.bands[mode][band] = ~~obj.bands[mode][band] + 1; obj.modes.Mixed[mode] = ~~obj.modes.Mixed[mode] + 1; if (isDigital) { obj.bands.Digital[band] = ~~obj.bands.Digital[band] + 1; obj.modes.Digital[mode] = ~~obj.modes.Digital[mode] + 1; } if (isPhone) { obj.bands.Phone[band] = ~~obj.bands.Phone[band] + 1; obj.modes.Phone[mode] = ~~obj.modes.Phone[mode] + 1; } if (unique) { if (obj.unique == null) obj.unique = {}; if (!(unique in obj.unique)) obj.unique[unique] = newAwardCountObject(); workAwardObject(obj.unique[unique], band, mode, isDigital, isPhone); } return true; } function buildAwardTypeHandlers() { CR.awardTypes = { IOTA: { name: "Islands On The Air" }, call: { name: "Callsign" }, callarea: { name: "Call Area" }, calls2dxcc: { name: "Stations per DXCC" }, cnty: { name: "County" }, cont: { name: "Continents" }, cont5: { name: "5 Continents" }, cont52band: { name: "5 Continents per Band" }, cqz: { name: "CQ Zone" }, dxcc: { name: "DXCC" }, grids: { name: "Grids" }, numsfx: { name: "Call Area + Suffix" }, px: { name: "Prefix" }, pxa: { name: "Prefixes" }, pxplus: { name: "Special Calls" }, sfx: { name: "Suffix" }, states: { name: "States" }, cont2band: { name: "Continents per Band" }, calls2band: { name: "Stations per Band" }, dxcc2band: { name: "DXCC per Band" }, states2band: { name: "States per Band" } }; CR.awardTypes.IOTA.score = scoreAIOTA; CR.awardTypes.call.score = scoreAcall; CR.awardTypes.callarea.score = scoreAcallarea; CR.awardTypes.calls2dxcc.score = scoreAcalls2dxcc; CR.awardTypes.cnty.score = scoreAcnty; CR.awardTypes.cont.score = scoreAcont; CR.awardTypes.cont5.score = scoreAcont5; CR.awardTypes.cont52band.score = scoreAcont52band; CR.awardTypes.cqz.score = scoreAcqz; CR.awardTypes.dxcc.score = scoreAdxcc; CR.awardTypes.grids.score = scoreAgrids; CR.awardTypes.numsfx.score = scoreAnumsfx; CR.awardTypes.px.score = scoreApx; CR.awardTypes.pxa.score = scoreApxa; CR.awardTypes.pxplus.score = scoreApxplus; CR.awardTypes.sfx.score = scoreAsfx; CR.awardTypes.states.score = scoreAstates; CR.awardTypes.cont2band.score = scoreAcont2band; CR.awardTypes.calls2band.score = scoreAcalls2band; CR.awardTypes.dxcc2band.score = scoreAdxcc2band; CR.awardTypes.states2band.score = scoreAstates2band; CR.awardTypes.IOTA.test = testAIOTA; CR.awardTypes.call.test = testAcall; CR.awardTypes.callarea.test = testAcallarea; CR.awardTypes.calls2dxcc.test = testAcalls2dxcc; CR.awardTypes.cnty.test = testAcnty; CR.awardTypes.cont.test = testAcont; CR.awardTypes.cont5.test = testAcont5; CR.awardTypes.cont52band.test = testAcont52band; CR.awardTypes.cqz.test = testAcqz; CR.awardTypes.dxcc.test = testAdxcc; CR.awardTypes.grids.test = testAgrids; CR.awardTypes.numsfx.test = testAnumsfx; CR.awardTypes.px.test = testApx; CR.awardTypes.pxa.test = testApxa; CR.awardTypes.pxplus.test = testApxplus; CR.awardTypes.sfx.test = testAsfx; CR.awardTypes.states.test = testAstates; CR.awardTypes.cont2band.test = testAcont2band; CR.awardTypes.calls2band.test = testAcalls2band; CR.awardTypes.dxcc2band.test = testAdxcc2band; CR.awardTypes.states2band.test = testAstates; CR.awardTypes.IOTA.compile = singleCompile; CR.awardTypes.call.compile = singleCompile; CR.awardTypes.callarea.compile = singleCompile; CR.awardTypes.calls2dxcc.compile = doubleCompile; CR.awardTypes.cnty.compile = singleCompile; CR.awardTypes.cont.compile = singleCompile; CR.awardTypes.cont5.compile = singleCompile; CR.awardTypes.cont52band.compile = doubleCompile; CR.awardTypes.cqz.compile = singleCompile; CR.awardTypes.dxcc.compile = singleCompile; CR.awardTypes.grids.compile = singleCompile; CR.awardTypes.numsfx.compile = singleCompile; CR.awardTypes.px.compile = singleCompile; CR.awardTypes.pxa.compile = singleCompile; CR.awardTypes.pxplus.compile = singleCompile; CR.awardTypes.sfx.compile = singleCompile; CR.awardTypes.states.compile = singleCompile; CR.awardTypes.cont2band.compile = doubleCompile; CR.awardTypes.calls2band.compile = doubleCompile; CR.awardTypes.dxcc2band.compile = doubleCompile; CR.awardTypes.states2band.compile = doubleCompile; } function scoreAstates(award, obj) { if (obj.state) { if (!(obj.state in award.stat)) { award.stat[obj.state] = newAwardCountObject(); } return workAwardObject( award.stat[obj.state], obj.band, obj.mode, obj.digital, obj.phone ); } return false; } function testAstates(award, obj, baseHash) { // calls with empty state will not match anything in the hash map. so filter those out if (!obj.state || obj.state + baseHash in CR.tracker[award.test.look].state) { return false; } return true; } function scoreAstates2band(award, obj) { if (obj.state) { if (!(obj.band in award.stat)) award.stat[obj.band] = newAwardCountObject(); return workAwardObject( award.stat[obj.band], obj.band, obj.mode, obj.digital, obj.phone, obj.state ); } return false; } function scoreAdxcc(award, obj) { if (!(obj.dxcc in award.stat)) award.stat[obj.dxcc] = newAwardCountObject(); return workAwardObject( award.stat[obj.dxcc], obj.band, obj.mode, obj.digital, obj.phone ); } function testAdxcc(award, obj, baseHash) { if (String(obj.dxcc) + "|" + baseHash in CR.tracker[award.test.look].dxcc) { return false; } return true; } function scoreAcont(award, obj) { if (obj.cont) { let cont = obj.cont; if (cont == "AN") cont = "OC"; if (!(cont in award.stat)) award.stat[cont] = newAwardCountObject(); return workAwardObject( award.stat[cont], obj.band, obj.mode, obj.digital, obj.phone ); } return false; } function testAcont(award, obj, baseHash) { if (obj.cont) { let cont = obj.cont; if (cont == "AN") cont = "OC"; if (cont + baseHash in CR.tracker[award.test.look].cont) { return false; } } return true; } function scoreAcont5(award, obj, baseHash) { if (obj.cont) { let cont = obj.cont; if (cont == "NA" || cont == "SA") cont = "AM"; if (cont == "AN") cont = "OC"; if (!(cont in award.stat)) award.stat[cont] = newAwardCountObject(); return workAwardObject( award.stat[cont], obj.band, obj.mode, obj.digital, obj.phone ); } return false; } function testAcont5(award, obj, baseHash) { if (obj.cont) { let cont = obj.cont; if (cont == "NA" || cont == "SA") cont = "AM"; if (cont == "AN") cont = "OC"; if (cont + baseHash in CR.tracker[award.test.look].cont) { return false; } } return true; } function scoreAcont2band(award, obj) { if (!(obj.band in award.stat)) award.stat[obj.band] = newAwardCountObject(); return workAwardObject( award.stat[obj.band], obj.band, obj.mode, obj.digital, obj.phone, obj.cont ); } function testAcont2band(award, obj, baseHash) { if (obj.cont) { let cont = obj.cont; if (cont == "AN") cont = "OC"; if (cont + baseHash in CR.tracker[award.test.look].cont) { return false; } } return true; } function scoreAcont52band(award, obj) { if (obj.cont) { let cont = obj.cont; if (cont == "NA" || cont == "SA") cont = "AM"; if (cont == "AN") cont = "OC"; if (!(obj.band in award.stat)) award.stat[obj.band] = newAwardCountObject(); return workAwardObject( award.stat[obj.band], obj.band, obj.mode, obj.digital, obj.phone, cont ); } return false; } function testAcont52band(award, obj, baseHash) { if (obj.cont) { let cont = obj.cont; if (cont == "NA" || cont == "SA") cont = "AM"; if (cont == "AN") cont = "OC"; if (cont + baseHash in CR.tracker[award.test.look].cont) { return false; } } return true; } function scoreAgrids(award, obj) { if (obj.grid && obj.grid.length > 0) { let grid = obj.grid.substr(0, 4); if (!(grid in award.stat)) award.stat[grid] = newAwardCountObject(); return workAwardObject( award.stat[grid], obj.band, obj.mode, obj.digital, obj.phone ); } return false; } function testAgrids(award, obj, baseHash) { if (obj.grid && obj.grid + baseHash in CR.tracker[award.test.look].grid) { return false; } if (!obj.grid || obj.grid.length == 0) { return false; } return true; } function scoreAcnty(award, obj) { if (obj.cnty) { if (!(obj.cnty in award.stat)) award.stat[obj.cnty] = newAwardCountObject(); return workAwardObject( award.stat[obj.cnty], obj.band, obj.mode, obj.digital, obj.phone ); } return false; } function testAcnty(award, obj, baseHash) { if (obj.cnty && obj.cnty + baseHash in CR.tracker[award.test.look].cnty) { return false; } return true; } function scoreAcall(award, obj) { let call = obj.DEcall; if (call.indexOf("/") > -1) { if (call.endsWith("/MM")) return false; call = call.replace("/P", "").replace("/R", "").replace("/QRP"); } if (!(call in award.stat)) award.stat[call] = newAwardCountObject(); return workAwardObject( award.stat[call], obj.band, obj.mode, obj.digital, obj.phone ); } function testAcall(award, obj, baseHash) { if (obj.DEcall.indexOf("/") > -1 && obj.DEcall.endsWith("/MM")) return false; if (obj.DEcall + baseHash in CR.tracker[award.test.look].call) { return false; } return true; } function scoreAIOTA(award, obj) { if (obj.IOTA) { let test = CR.awards[award.sponsor].awards[award.name]; if ("IOTA" in test.rule && test.rule.IOTA.indexOf(obj.IOTA) == -1) { return false; } if (!(obj.IOTA in award.stat)) award.stat[obj.IOTA] = newAwardCountObject(); return workAwardObject( award.stat[obj.IOTA], obj.band, obj.mode, obj.digital, obj.phone ); } return false; } // NO IOTA YET function testAIOTA(award, obj, baseHash) { /* if ( obj.IOTA ) { let test = CR.awards[award.sponsor].awards[award.name]; if ( "IOTA" in test.rule && test.rule.IOTA.indexOf(obj.IOTA) == -1 ) return false; } */ return false; } function scoreAcallarea(award, obj) { if (obj.zone != null) { let test = CR.awards[award.sponsor].awards[award.name]; if ("zone" in test.rule && test.rule.zone.indexOf(obj.zone) == -1) { return false; } if (!(obj.zone in award.stat)) award.stat[obj.zone] = newAwardCountObject(); return workAwardObject( award.stat[obj.zone], obj.band, obj.mode, obj.digital, obj.phone ); } return false; } function testAcallarea(award, obj, baseHash) { if (obj.zone != null) { let test = CR.awards[award.sponsor].awards[award.name]; if ("zone" in test.rule && test.rule.zone.indexOf(obj.zone) == -1) { return false; } } return true; } function scoreApx(award, obj) { if (obj.px) { let test = CR.awards[award.sponsor].awards[award.name]; let px = obj.px; if ("px" in test.rule) { px = px.substr(0, test.rule.px[0].length); if (test.rule.px.indexOf(px) == -1) return false; } if (!(px in award.stat)) award.stat[px] = newAwardCountObject(); return workAwardObject( award.stat[px], obj.band, obj.mode, obj.digital, obj.phone ); } return false; } function testApx(award, obj, baseHash) { if (obj.px) { let test = CR.awards[award.sponsor].awards[award.name]; let px = obj.px; if ("px" in test.rule) { px = px.substr(0, test.rule.px[0].length); if (test.rule.px.indexOf(px) == -1) return false; } if (String(obj.px) + baseHash in CR.tracker[award.test.look].px) { return false; } } return true; } function scoreApxa(award, obj) { if (obj.px) { let test = CR.awards[award.sponsor].awards[award.name]; for (const i in test.rule.pxa) { if (test.rule.pxa[i].indexOf(obj.px) > -1) { if (!(i in award.stat)) award.stat[i] = newAwardCountObject(); return workAwardObject( award.stat[i], obj.band, obj.mode, obj.digital, obj.phone ); } } } return false; } function testApxa(award, obj, baseHash) { if (obj.px) { let test = CR.awards[award.sponsor].awards[award.name]; for (const i in test.rule.pxa) { if (test.rule.pxa[i].indexOf(obj.px) > -1) { if (String(obj.px) + baseHash in CR.tracker[award.test.look].px) { return false; } else { return true; } } } } return false; } function scoreAsfx(award, obj) { let test = CR.awards[award.sponsor].awards[award.name]; let suf = obj.DEcall.replace(obj.px, ""); for (const i in test.rule.sfx) { for (const s in test.rule.sfx[i]) { if (suf.indexOf(test.rule.sfx[i][s]) == 0) { if (!(i in award.stat)) award.stat[i] = newAwardCountObject(); return workAwardObject( award.stat[i], obj.band, obj.mode, obj.digital, obj.phone ); } } } return false; } function testAsfx(award, obj, baseHash) { let test = CR.awards[award.sponsor].awards[award.name]; let suf = obj.DEcall.replace(obj.px, ""); for (const i in test.rule.sfx) { for (const s in test.rule.sfx[i]) { if (suf.indexOf(test.rule.sfx[i][s]) == 0) { return false; } } } return true; } function scoreAcalls2dxcc(award, obj) { if (!(obj.dxcc in award.stat)) award.stat[obj.dxcc] = newAwardCountObject(); return workAwardObject( award.stat[obj.dxcc], obj.band, obj.mode, obj.digital, obj.phone, obj.DEcall ); } function testAcalls2dxcc(award, obj, baseHash) { if (obj.DEcall + baseHash in CR.tracker[award.test.look].call) { return false; } return true; } function scoreAcalls2band(award, obj) { if (!(obj.band in award.stat)) award.stat[obj.band] = newAwardCountObject(); return workAwardObject( award.stat[obj.band], obj.band, obj.mode, obj.digital, obj.phone, obj.DEcall ); } function testAcalls2band(award, obj, baseHash) { if (obj.DEcall + baseHash in CR.tracker[award.test.look].call) { return false; } return true; } function scoreAdxcc2band(award, obj) { if (!(obj.band in award.stat)) award.stat[obj.band] = newAwardCountObject(); return workAwardObject( award.stat[obj.band], obj.band, obj.mode, obj.digital, obj.phone, obj.dxcc ); } function testAdxcc2band(award, obj, baseHash) { if (String(obj.dxcc) + "|" + baseHash in CR.tracker[award.test.look].dxcc) { return false; } return true; } function scoreAcqz(award, obj) { if (obj.cqz) { if (!(obj.cqz in award.stat)) award.stat[obj.cqz] = newAwardCountObject(); return workAwardObject( award.stat[obj.cqz], obj.band, obj.mode, obj.digital, obj.phone ); } return false; } function testAcqz(award, obj, baseHash) { // calls with empty cqz will not match anything in the hash map. so filter those out if (!obj.cqz || obj.cqz + "|" + baseHash in CR.tracker[award.test.look].cqz) { return false; } return true; } function scoreAnumsfx(award, obj) { if (obj.px) { let test = CR.awards[award.sponsor].awards[award.name]; let px = obj.px.substr(0, obj.px.length - 1); let suf = obj.DEcall.replace(px, ""); suf = suf.substr(0, test.rule.numsfx[0][0].length); for (const i in test.rule.numsfx) { for (const s in test.rule.numsfx[i]) { if (suf.indexOf(test.rule.numsfx[i][s]) == 0) { if (!(i in award.stat)) award.stat[i] = newAwardCountObject(); return workAwardObject( award.stat[i], obj.band, obj.mode, obj.digital, obj.phone ); } } } } return false; } function testAnumsfx(award, obj) { if (obj.px) { let test = CR.awards[award.sponsor].awards[award.name]; let px = obj.px.substr(0, obj.px.length - 1); let suf = obj.DEcall.replace(px, ""); suf = suf.substr(0, test.rule.numsfx[0][0].length); for (const i in test.rule.numsfx) { for (const s in test.rule.numsfx[i]) { if (suf.indexOf(test.rule.numsfx[i][s]) == 0) { return false; } } } } return true; } function scoreApxplus(award, obj) { let test = CR.awards[award.sponsor].awards[award.name]; if (test.rule.pxplus) { for (const i in test.rule.pxplus) { if (obj.DEcall.indexOf(test.rule.pxplus[i]) == 0) { if (!(i in award.stat)) award.stat[i] = newAwardCountObject(); return workAwardObject( award.stat[i], obj.band, obj.mode, obj.digital, obj.phone ); } } } return false; } function testApxplus(award, obj) { let test = CR.awards[award.sponsor].awards[award.name]; if (test.rule.pxplus) { for (const i in test.rule.pxplus) { if (obj.DEcall.indexOf(test.rule.pxplus[i]) == 0) { return false; } } } return true; } function loadAwardJson() { CR.awards = {}; let fs = require("fs"); if (fs.existsSync("./data/awards.json")) { fileBuf = fs.readFileSync("./data/awards.json"); try { CR.awards = JSON.parse(fileBuf); // fs.writeFileSync("./data/awards.json", JSON.stringify(CR.awards,null,2)); for (const sp in CR.awards) { for (const aw in CR.awards[sp].awards) { if (!("unique" in CR.awards[sp].awards[aw].rule)) { CR.awards[sp].awards[aw].rule.unique = 1; } if (CR.awards[sp].awards[aw].rule.band[0] == "Mixed") { CR.awards[sp].awards[aw].rule.band.shift(); } if (CR.awards[sp].awards[aw].rule.band.length == 0) { CR.awards[sp].awards[aw].rule.band = []; for (let key in CR.awards[sp].mixed) { CR.awards[sp].awards[aw].rule.band.push(CR.awards[sp].mixed[key]); } } if ( CR.awards[sp].awards[aw].rule.endorse.length == 1 && CR.awards[sp].awards[aw].rule.endorse[0] == "Mixed" ) { CR.awards[sp].awards[aw].rule.endorse = []; for (let key in CR.awards[sp].mixed) { CR.awards[sp].awards[aw].rule.endorse.push( CR.awards[sp].mixed[key] ); } } } } buildAwardTypeHandlers(); } catch (e) { alert("Core awards.json : " + e); CR.awards = {}; } } else alert("Missing core awards.json"); } function processAllAwardTrackers() { for (let tracker in CR.awardTracker) { if (!(CR.awardTracker[tracker].sponsor in CR.awards)) { delete CR.awardTracker[tracker]; continue; } if ( !( CR.awardTracker[tracker].name in CR.awards[CR.awardTracker[tracker].sponsor].awards ) ) { delete CR.awardTracker[tracker]; continue; } processAward(tracker); } updateAwardList(); } function newAwardTrackerObject(sponsor, award, enable) { let newAward = {}; newAward.sponsor = sponsor; newAward.name = award; newAward.enable = enable; newAward.mode = CR.awards[sponsor].awards[award].rule.mode[0]; newAward.band = CR.awards[sponsor].awards[award].rule.band[0]; newAward.count = CR.awards[sponsor].awards[award].rule.count[0]; newAward.stat = {}; newAward.comp = {}; newAward.test = {}; return newAward; } function addAllAwards() { for (let sponsor in CR.awards) { for (let award in CR.awards[sponsor].awards) { let awardToAdd = newAwardTrackerObject(sponsor, award, true); let hash = awardToAdd.name + "-" + awardToAdd.sponsor; if (!(hash in CR.awardTracker)) { CR.awardTracker[hash] = awardToAdd; processAward(hash); storeAwardTracker(); } } } updateAwardList(); viewRoster(); } function delAllAwards() { CR.awardTracker = {}; storeAwardTracker(); updateAwardList(); viewRoster(); } function newCompileCountObject() { let compileCountObject = {}; compileCountObject.bands = {}; compileCountObject.modes = {}; compileCountObject.endorse = {}; compileCountObject.counts = {}; return compileCountObject; } function singleCompile(award, obj) { let test = CR.awards[award.sponsor].awards[award.name]; let rule = test.rule; let comp = newCompileCountObject(); for (let mode in rule.mode) { comp.modes[rule.mode[mode]] = 0; comp.bands[rule.mode[mode]] = {}; for (let band in rule.band) { comp.bands[rule.mode[mode]][rule.band[band]] = 0; } for (let key in obj) { if ( rule.mode[mode] in obj[key].bands && Object.keys(obj[key].bands[rule.mode[mode]]).length ) { comp.modes[rule.mode[mode]] += 1; for (let band in rule.band) { if (rule.band[band] in obj[key].bands[rule.mode[mode]]) { comp.bands[rule.mode[mode]][rule.band[band]] += 1; } } } } } for (let mode in comp.modes) { comp.endorse[mode] = {}; comp.counts[mode] = {}; for (let cnts in rule.count) { comp.counts[mode][rule.count[cnts]] = { num: comp.modes[mode], per: parseInt( Math.min(100, (comp.modes[mode] / rule.count[cnts]) * 100.0) ) }; } for (let endorse in rule.endorse) { comp.endorse[mode][rule.endorse[endorse]] = {}; for (let cnts in rule.count) { comp.endorse[mode][rule.endorse[endorse]][rule.count[cnts]] = comp.bands[mode][rule.endorse[endorse]] >= rule.count[cnts]; } } } return comp; } function doubleCompile(award, firstLevel) { let test = CR.awards[award.sponsor].awards[award.name]; let rule = test.rule; for (let k in firstLevel) { firstLevel[k].bands = {}; // firstLevel[k].modes = {}; let obj = singleCompile(award, firstLevel[k].unique); for (let mode in obj.bands) { for (let cnt in test.rule.count) { if (obj.counts[mode][test.rule.count[cnt]].num >= test.rule.unique) { for (let band in obj.bands[mode]) { if (!(mode in firstLevel[k].bands)) firstLevel[k].bands[mode] = {}; if (obj.bands[mode][band] > 0) { firstLevel[k].bands[mode][band] = ~~firstLevel[k].bands[mode][band] + 1; } } } } } /* for ( let mode in obj.modes ) { if ( !(mode in firstLevel[k].modes) ) firstLevel[k].modes[mode] = 0; if ( obj.modes[mode] > 0 ) firstLevel[k].modes[mode] += 1; } */ delete firstLevel[k].unique; firstLevel[k].unique = null; } return singleCompile(award, firstLevel); } function listShortInstances() { let shortInstances = []; if (typeof window.opener.GT.instancesIndex != "undefined" && typeof window.opener.GT.instances != "undefined") { if (window.opener.GT.instancesIndex.length > 1) { let instances = window.opener.GT.instances; let keys = Object.keys(instances).sort(); for (let key in keys) { let inst = keys[key]; let sp = inst.split(" - "); let shortInst = sp[sp.length - 1].substring(0, 18); shortInstances.push(shortInst); } } } return shortInstances; }