// GridTracker Copyright © 2023 GridTracker.org // All rights reserved. // See LICENSE for more information. GT.fromDirectCallNoFileDialog = false; GT.selectStartupLink = null; function dragOverHandler(ev) { // Prevent default behavior (Prevent file from being opened) ev.preventDefault(); } function dropHandler(ev) { // Prevent default behavior (Prevent file from being opened) ev.preventDefault(); if (ev.dataTransfer.items) { // Use DataTransferItemList interface to access the file(s) for (var i = 0; i < ev.dataTransfer.items.length; i++) { // If dropped items aren't files, reject them Entry = ev.dataTransfer.items[i].webkitGetAsEntry(); if (Entry && "isFile" in Entry && Entry.isFile == true) { var filename = ev.dataTransfer.items[i].getAsFile().path; var test = filename.toLowerCase(); var valid = test.endsWith(".adi") ? true : test.endsWith(".adif") ? true : test.endsWith(".log") ? true : !!test.endsWith(".txt"); if (valid && fs.existsSync(filename)) { onAdiLoadComplete(fs.readFileSync(filename, "UTF-8"), false); } } } } } function dateToISO8601(dString, tZone) { var retDate = ""; var tZone = (typeof tZone !== "undefined") ? tZone : "Z"; var dateParts = dString.match(/(\d{4}-\d{2}-\d{2})(\s+(\d{2}:\d{2}:\d{2}))?/); if (dateParts !== null) { retDate = dateParts[1] if ((typeof dateParts[3]) !== "undefined") { retDate += "T" + dateParts[3] + ".000" + tZone; } else { retDate += "T00:00:00.000" + tZone; } } return retDate; } function findAdiField(row, field) { var value = ""; var regex = new RegExp("<" + field + ":", "i"); var firstSplitArray = row.split(regex); if (firstSplitArray && firstSplitArray.length == 2) { var secondSplitArray = firstSplitArray[1].split(">"); if (secondSplitArray.length > 1) { var newLenSearch = secondSplitArray[0].split(":"); var newLen = newLenSearch[0]; value = secondSplitArray[1].slice(0, newLen); } } return value; } function onAdiLoadComplete(adiBuffer, saveAdifFile, adifFileName, newFile) { var rawAdiBuffer = ""; if (typeof adiBuffer == "object") rawAdiBuffer = String(adiBuffer); else rawAdiBuffer = adiBuffer; var activeAdifArray = Array(); var activeAdifLogMode = true; var eQSLfile = false; var lotwTimestampUpdated = false; if (rawAdiBuffer.indexOf("PSKReporter") > -1) activeAdifLogMode = false; if (rawAdiBuffer.indexOf("Received eQSLs") > -1) eQSLfile = true; if (rawAdiBuffer.length > 1) { var regex = new RegExp("", "ig"); rawAdiBuffer = replaceAll(rawAdiBuffer, regex, ""); } if (rawAdiBuffer.length > 1) { var regex = new RegExp("", "i"); activeAdifArray = rawAdiBuffer.split(regex); } for (var x = 0; x < activeAdifArray.length; x++) { if (activeAdifArray[x].length > 3) { if (activeAdifLogMode) { var appLoTW_RXQSO = findAdiField(activeAdifArray[x], "APP_LOTW_RXQSO"); if (appLoTW_RXQSO != "") { var dRXQSO = Date.parse(appLoTW_RXQSO); var dLastLOTW_QSO = Date.parse(GT.adifLogSettings.lastFetch.lotw_qso); if ((isNaN(dRXQSO) == false) && (isNaN(dLastLOTW_QSO) == false) && (dRXQSO > dLastLOTW_QSO)) { // add a second dRXQSO += 1000; var date = new Date(dRXQSO); // create Date object var timestring = date.toISOString(); GT.adifLogSettings.lastFetch.lotw_qso = timestring.slice(0, 10) + " " + timestring.slice(11, 19); lotwTimestampUpdated = true; } } var appLoTW_RXQSL = findAdiField(activeAdifArray[x], "APP_LOTW_RXQSL"); if (appLoTW_RXQSL != "") { var dRXQSL = Date.parse(appLoTW_RXQSL); var dLastLOTW_QSL = Date.parse(GT.adifLogSettings.lastFetch.lotw_qsl); if ((isNaN(dRXQSL) == false) && (isNaN(dLastLOTW_QSL) == false) && (dRXQSL > dLastLOTW_QSL)) { // add a second dRXQSL += 1000; var date = new Date(dRXQSL); // create Date object var timestring = date.toISOString(); GT.adifLogSettings.lastFetch.lotw_qsl = timestring.slice(0, 10) + " " + timestring.slice(11, 19); lotwTimestampUpdated = true; } } var finalDEcall = findAdiField(activeAdifArray[x], "STATION_CALLSIGN").replace("_", "/"); if (finalDEcall == "") { finalDEcall = GT.appSettings.myCall; } if (GT.appSettings.workingCallsignEnable && !(finalDEcall in GT.appSettings.workingCallsigns)) { // not in the working callsigns, move to next continue; } var dateVal = findAdiField(activeAdifArray[x], "QSO_DATE"); var timeVal = findAdiField(activeAdifArray[x], "TIME_ON"); var dateTime = new Date( Date.UTC( dateVal.substr(0, 4), parseInt(dateVal.substr(4, 2)) - 1, dateVal.substr(6, 2), timeVal.substr(0, 2), timeVal.substr(2, 2), timeVal.substr(4, 2) ) ); var finalTime = parseInt(dateTime.getTime() / 1000); if (GT.appSettings.workingDateEnable && finalTime < GT.appSettings.workingDate) { // Not after our working date continue; } var confirmed = false; var finalDXcall = findAdiField(activeAdifArray[x], "CALL").replace("_", "/"); var finalGrid = findAdiField(activeAdifArray[x], "GRIDSQUARE").toUpperCase(); var vuccGrids = findAdiField(activeAdifArray[x], "VUCC_GRIDS").toUpperCase(); var finalVucc = []; var finalRSTsent = findAdiField(activeAdifArray[x], "RST_SENT"); var finalRSTrecv = findAdiField(activeAdifArray[x], "RST_RCVD"); var finalBand = findAdiField(activeAdifArray[x], "BAND").toLowerCase(); if (finalBand == "" || finalBand == "oob") { finalBand = formatBand(Number(findAdiField(activeAdifArray[x], "FREQ"))); } var finalState = findAdiField(activeAdifArray[x], "STATE").toUpperCase(); if (finalState.length == 0) finalState = null; var finalPropMode = findAdiField(activeAdifArray[x], "PROP_MODE").toUpperCase(); var finalSatName = findAdiField(activeAdifArray[x], "SAT_NAME").toUpperCase(); var finalCont = findAdiField(activeAdifArray[x], "CONT").toUpperCase(); if (finalCont.length == 0) { finalCont = null; } var finalCnty = findAdiField(activeAdifArray[x], "CNTY").toUpperCase(); if (finalCnty.length == 0) { finalCnty = null; } else { // GT references internally with NO spaces, this is important finalCnty = replaceAll(finalCnty, " ", ""); } var finalMode = findAdiField(activeAdifArray[x], "MODE").toUpperCase(); var subMode = findAdiField(activeAdifArray[x], "SUBMODE"); if (subMode == "FT4" && (finalMode == "MFSK" || finalMode == "DATA")) { // Internal assigment only finalMode = "FT4" } if (subMode == "Q65" && (finalMode == "MFSK" || finalMode == "DATA")) { // Internal assigment only finalMode = "Q65" } if (subMode == "JS8" && finalMode == "MFSK") { // Internal assigment only finalMode = "JS8"; } var finalMsg = findAdiField(activeAdifArray[x], "COMMENT"); var finalQslMsg = findAdiField(activeAdifArray[x], "QSLMSG"); var finalQslMsgIntl = findAdiField(activeAdifArray[x], "QSLMSG_INTL"); if (finalQslMsg.length > 1) { finalMsg = finalQslMsg; } if (finalQslMsgIntl.length > 1 && finalMsg == "") { finalMsg = finalQslMsgIntl; } var finalDxcc = Number(findAdiField(activeAdifArray[x], "DXCC")); if (finalDxcc == 0) { finalDxcc = Number(callsignToDxcc(finalDXcall)); } if (!(finalDxcc in GT.dxccInfo)) { finalDxcc = Number(callsignToDxcc(finalDXcall)); } // If my callsign isn't present, it must be for me anyway var finalCqZone = findAdiField(activeAdifArray[x], "CQZ"); if (finalCqZone.length == 1) { finalCqZone = "0" + finalCqZone; } if (parseInt(finalCqZone) < 1 || parseInt(finalCqZone) > 40) { finalCqZone = ""; } finalCqZone = String(finalCqZone); var finalItuZone = findAdiField(activeAdifArray[x], "ITUZ"); if (finalItuZone.length == 1) finalItuZone = "0" + finalItuZone; if (parseInt(finalItuZone) < 1 || parseInt(finalItuZone) > 90) { finalItuZone = ""; } finalItuZone = String(finalItuZone); var finalIOTA = findAdiField(activeAdifArray[x], "IOTA").toUpperCase(); var qrzConfirmed = findAdiField(activeAdifArray[x], "APP_QRZLOG_STATUS").toUpperCase(); var lotwConfirmed1 = findAdiField(activeAdifArray[x], "QSL_RCVD").toUpperCase(); var lotw_qsl_rcvd = findAdiField(activeAdifArray[x], "LOTW_QSL_RCVD").toUpperCase(); var eqsl_qsl_rcvd = findAdiField(activeAdifArray[x], "EQSL_QSL_RCVD").toUpperCase(); if (qrzConfirmed == "C" || lotw_qsl_rcvd == "Y" || lotw_qsl_rcvd == "V" || lotwConfirmed1 == "Y" || eqsl_qsl_rcvd == "Y" || eqsl_qsl_rcvd == "V" || eQSLfile == true) { confirmed = true; } finalGrid = finalGrid.substr(0, 6); if (!validateGridFromString(finalGrid)) finalGrid = ""; if (finalGrid == "" && vuccGrids != "") { finalVucc = vuccGrids.split(","); finalGrid = finalVucc[0]; finalVucc.shift(); } var isDigital = false; var isPhone = false; if (finalMode in GT.modes) { isDigital = GT.modes[finalMode]; } if (finalMode in GT.modes_phone) { isPhone = GT.modes_phone[finalMode]; } // TODO: Revisit when we support more than one park ID var finalPOTA = findAdiField(activeAdifArray[x], "POTA").toUpperCase(); if (finalPOTA.length == 0) { finalPOTA = null; } if (finalDXcall != "") { addDeDx( finalGrid, finalDXcall, finalDEcall, finalRSTsent, finalTime, finalMsg, finalMode, finalBand, confirmed, true, finalRSTrecv, finalDxcc, finalState, finalCont, finalCnty, finalCqZone, finalItuZone, finalVucc, finalPropMode, isDigital, isPhone, finalIOTA, finalSatName, finalPOTA ); } } else { var finalMyGrid = findAdiField( activeAdifArray[x], "MY_GRIDSQUARE" ).toUpperCase(); var finalGrid = findAdiField( activeAdifArray[x], "GRIDSQUARE" ).toUpperCase(); var finalDXcall = findAdiField(activeAdifArray[x], "CALL"); var finalDEcall = findAdiField(activeAdifArray[x], "OPERATOR"); var finalRSTsent = findAdiField(activeAdifArray[x], "APP_PSKREP_SNR"); var dateVal = findAdiField(activeAdifArray[x], "QSO_DATE"); var timeVal = findAdiField(activeAdifArray[x], "TIME_ON"); var finalMode = findAdiField(activeAdifArray[x], "MODE"); var finalBand = formatBand(Number(findAdiField(activeAdifArray[x], "FREQ"))); var finalMsg = "-"; var finalDxcc = Number(findAdiField(activeAdifArray[x], "DXCC")); if (finalDxcc == 0) { if (finalDXcall == GT.appSettings.myCall) finalDxcc = callsignToDxcc(finalDEcall); else finalDxcc = callsignToDxcc(finalDXcall); } finalGrid = finalGrid.substr(0, 6); var dateTime = new Date( Date.UTC( dateVal.substr(0, 4), parseInt(dateVal.substr(4, 2)) - 1, dateVal.substr(6, 2), timeVal.substr(0, 2), timeVal.substr(2, 2), timeVal.substr(4, 2) ) ); var finalTime = parseInt(dateTime.getTime() / 1000); if ( finalGrid != "" && finalDXcall != "" && validateGridFromString(finalGrid) ) { if (finalDXcall == GT.appSettings.myCall) { addDeDx( finalMyGrid, finalDEcall, finalDXcall, null, finalTime, finalMsg, finalMode, finalBand, false, false, finalRSTsent, finalDxcc, null, null, null, null, null ); } else if (finalDEcall == GT.appSettings.myCall) { addDeDx( finalGrid, finalDXcall, "-", finalRSTsent, finalTime, finalMsg, finalMode, finalBand, false, false, null, finalDxcc, null, null, null, null, null ); } else { addDeDx( finalGrid, finalDXcall, finalDEcall, finalRSTsent, finalTime, finalMsg, finalMode, finalBand, false, false, null, finalDxcc, null, null, null, null, null ); } } } } } if (lotwTimestampUpdated) { saveLogSettings(); } redrawGrids(); updateCountStats(); updateLogbook(); if (GT.fromDirectCallNoFileDialog == false) { GT.fileSelector.setAttribute("type", ""); GT.fileSelector.setAttribute("type", "file"); GT.fileSelector.setAttribute("accept", ".adi,"); GT.fileSelector.value = null; } GT.fromDirectCallNoFileDialog = false; updateRosterWorked(); goProcessRoster(); } function clubLogCallback(buffer, flag, cookie) { var rawAdiBuffer = String(buffer); if (rawAdiBuffer.indexOf("Invalid login") > -1) { if (flag) clubTestResult.innerHTML = "Invalid"; } else if (buffer == null) { if (flag) clubTestResult.innerHTML = "Unknown Error"; } else { if (flag) clubTestResult.innerHTML = "Passed"; else { GT.fromDirectCallNoFileDialog = true; rawAdiBuffer = cleanAndPrepADIF("clublog.adif", rawAdiBuffer); tryToWriteAdifToDocFolder("clublog.adif", rawAdiBuffer); onAdiLoadComplete(rawAdiBuffer, true, "clublog.adif", true); } } } GT.isGettingClub = false; function grabClubLog(test) { if (GT.isGettingClub == false) { if (test) clubTestResult.innerHTML = "Testing"; var postData = { email: clubEmail.value, password: clubPassword.value, call: clubCall.value }; getAPostBuffer( "https://clublog.org/getadif.php", clubLogCallback, test, "https", 443, postData, ClubLogImg, "GT.isGettingClub" ); } } function tryToWriteAdifToDocFolder(filename, buffer, append = false) { var finalFile = GT.appData + GT.dirSeperator + filename; try { if (append == false) { fs.writeFileSync(finalFile, buffer); return buffer; } else { fs.appendFileSync(finalFile, buffer); return fs.readFileSync(finalFile); } } catch (e) { return false; } } function cleanAndPrepADIF(name, adiBuffer, reverse = false, noheader = false) { var rawAdiBuffer = adiBuffer; var regex = new RegExp("", "i"); rawAdiBuffer = rawAdiBuffer.replace(regex, ""); regex = new RegExp("", "i"); var adiArray = rawAdiBuffer.split(regex); var activeAdifArray = Array(); var activeAdifLogMode = true; var finalBuffer = ""; if (noheader == false) finalBuffer = name + "\r\n"; if (adiArray.length > 1) { regex = new RegExp("", "i"); activeAdifArray = adiArray[1].split(regex); if (reverse == false) { for (var x = 0; x < activeAdifArray.length - 1; x++) { var row = activeAdifArray[x].replace(/[\n\r]/g, ""); if (row.length > 0) finalBuffer += row + "\r\n"; } } else { for (var x = activeAdifArray.length - 1; x > -1; x--) { var row = activeAdifArray[x].replace(/[\n\r]/g, ""); if (row.length > 0) finalBuffer += row + "\r\n"; } } } return finalBuffer; } function addZero(i) { if (i < 10) { i = "0" + i; } return i; } function getUTCString(d) { var Y = d.getUTCFullYear(); var M = addZero(d.getUTCMonth() + 1); var D = addZero(d.getUTCDate()); var h = addZero(d.getUTCHours()); var m = addZero(d.getUTCMinutes()); var s = addZero(d.getUTCSeconds()); return Y + "-" + M + "-" + D + " " + h + ":" + m + ":" + s; } function lotwCallback(buffer, flag, cookies, url) { var rawAdiBuffer = String(buffer); if (rawAdiBuffer.indexOf("password incorrect") > -1) { if (flag) { lotwTestResult.innerHTML = "Invalid"; } } else { if (flag) { lotwTestResult.innerHTML = "Passed"; } else { var shouldAppend = true; var adiFileName = "LogbookOfTheWorld.adif"; GT.fromDirectCallNoFileDialog = true; // Extract header showing type of call // var lotwQSHeader = rawAdiBuffer.match(/^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})/m); rawAdiBuffer = cleanAndPrepADIF( adiFileName, rawAdiBuffer, true, shouldAppend ); rawAdiBuffer = tryToWriteAdifToDocFolder( adiFileName, rawAdiBuffer, shouldAppend ); onAdiLoadComplete(rawAdiBuffer, true, adiFileName, true); if (url.indexOf("qso_qsl=no") != -1) { // This is the QSO download, fire the QSL download now nodeTimers.setTimeout(grabLoTWQSL, 500); } } } } function shouldWeAppendInsteadOfCreate(filename) { var finalFile = GT.appData + GT.dirSeperator + filename; try { if (fs.existsSync(finalFile)) return true; else return false; } catch (e) { return false; } } function tryToDeleteLog(filename) { var finalFile = GT.appData + GT.dirSeperator + filename; try { if (fs.existsSync(finalFile)) { fs.unlinkSync(finalFile); } } catch (e) {} } GT.lotwCount = 0; GT.isGettingLOTW = false; GT.lotwTest = false; function grabLOtWLog(test) { var lastQSLDateString = ""; if (test == true && GT.isGettingLOTW == false) { lotwTestResult.innerHTML = "Testing"; lastQSLDateString = "&qso_qsosince=2100-01-01"; // Fetch Test Results getABuffer( "https://lotw.arrl.org/lotwuser/lotwreport.adi?login=" + lotwLogin.value + "&password=" + encodeURIComponent(lotwPassword.value) + "&qso_query=1&qso_qsl=no&qso_qsldetail=yes&qso_withown=yes" + lastQSLDateString, lotwCallback, test, "https", 443, lotwLogImg, "GT.isGettingLOTW", 150000 ); } if (test == false) { loadLoTWLogFile(); nodeTimers.setTimeout(grabLoTWQSO, 500); } } function grabLoTWQSO() { var dLoTWQSO = Date.parse(dateToISO8601(GT.adifLogSettings.lastFetch.lotw_qso, "Z")); if (GT.isGettingLOTW == false && isNaN(dLoTWQSO) == false) { // Fetch QSOs lastQSLDateString = "&qso_qsorxsince=" + GT.adifLogSettings.lastFetch.lotw_qso; getABuffer( "https://lotw.arrl.org/lotwuser/lotwreport.adi?login=" + lotwLogin.value + "&password=" + encodeURIComponent(lotwPassword.value) + "&qso_query=1&qso_qsl=no&qso_qsldetail=yes&qso_withown=yes" + lastQSLDateString, lotwCallback, false, "https", 443, lotwLogImg, "GT.isGettingLOTW", 120000 ); } } function grabLoTWQSL() { var dLoTWQSL = Date.parse(dateToISO8601(GT.adifLogSettings.lastFetch.lotw_qsl, "Z")); // Don't grab if the last QSL was less than 5 minutes ago if (GT.isGettingLOTW == false && isNaN(dLoTWQSL) == false) { lastQSLDateString = "&qso_qslsince=" + GT.adifLogSettings.lastFetch.lotw_qsl; getABuffer( "https://lotw.arrl.org/lotwuser/lotwreport.adi?login=" + lotwLogin.value + "&password=" + encodeURIComponent(lotwPassword.value) + "&qso_query=1&qso_qsl=yes&qso_qsldetail=yes&qso_withown=yes" + lastQSLDateString, lotwCallback, false, "https", 443, lotwLogImg, "GT.isGettingLOTW", 120000 ); } } function qrzCallback(buffer, flag) { if (buffer.indexOf("invalid api key") > -1) { if (flag) qrzTestResult.innerHTML = "Invalid"; } else { if (flag) { qrzTestResult.innerHTML = "Passed"; } else { GT.fromDirectCallNoFileDialog = true; var htmlString = String(buffer).replace(/</g, "<"); htmlString = htmlString.replace(/>/g, ">"); htmlString = htmlString.replace("ADIF=", "QRZ\r\n"); htmlString = cleanAndPrepADIF("qrz.adif", htmlString); tryToWriteAdifToDocFolder("qrz.adif", htmlString); onAdiLoadComplete(htmlString, true, "qrz.adif", true); } } } GT.isGettingQRZCom = false; function grabQrzComLog(test) { if (GT.isGettingQRZCom == false) { var action = "FETCH"; if (test) { qrzTestResult.innerHTML = "Testing"; action = "STATUS"; } getABuffer( "https://logbook.qrz.com/api?KEY=" + qrzApiKey.value + "&ACTION=" + action, qrzCallback, test, "https", 443, qrzLogImg, "GT.isGettingQRZCom", null ); } } function ValidateQrzApi(inputText) { inputText.value = inputText.value.toUpperCase().trim(); if (inputText.value.length == 19) { var passed = false; var dashcount = 0; for (var i = 0; i < inputText.value.length; i++) { if (inputText.value[i] == "-") dashcount++; } if (dashcount == 3) { passed = true; } if (passed) { inputText.style.color = "#FF0"; inputText.style.backgroundColor = "green"; return true; } else { inputText.style.color = "white"; inputText.style.backgroundColor = "red"; return false; } } else { inputText.style.color = "white"; inputText.style.backgroundColor = "red"; return false; } } function ValidateText(inputText) { if (inputText.value.length > 0) { inputText.style.color = "#FF0"; inputText.style.backgroundColor = "green"; return true; } else { inputText.style.color = "white"; inputText.style.backgroundColor = "red"; return false; } } function pskCallback(buffer, flag) { GT.fromDirectCallNoFileDialog = true; onAdiLoadComplete(buffer, false); } GT.isGettingPsk = false; function grabPsk24() { if (GT.isGettingPsk == true) return; if (GT.appSettings.myCall.length > 0 && GT.appSettings.myCall != "NOCALL") { var days = 1; if (pskImg.src == 1) days = 7; getABuffer( "https://pskreporter.info/cgi-bin/pskdata.pl?adif=1&days=" + days + "&receiverCallsign=" + GT.appSettings.myCall.toLowerCase(), pskCallback, null, "https", 443, pskImg, "GT.isGettingPsk" ); } } function adifMenuCheckBoxChanged(what) { GT.adifLogSettings.menu[what.id] = what.checked; var menuItem = what.id + "Div"; if (what.checked == true) { document.getElementById(menuItem).style.display = "inline-block"; } else { document.getElementById(menuItem).style.display = "none"; } localStorage.adifLogSettings = JSON.stringify(GT.adifLogSettings); if (what == buttonAdifCheckBox) setAdifStartup(loadAdifCheckBox); } function adifStartupCheckBoxChanged(what) { GT.adifLogSettings.startup[what.id] = what.checked; localStorage.adifLogSettings = JSON.stringify(GT.adifLogSettings); if (what == loadAdifCheckBox) setAdifStartup(loadAdifCheckBox); } function adifLogQsoCheckBoxChanged(what) { GT.adifLogSettings.qsolog[what.id] = what.checked; if (what.id == "logLOTWqsoCheckBox") { if (what.checked == true) { lotwUpload.style.display = "inline-block"; trustedTestButton.style.display = "inline-block"; } else { lotwUpload.style.display = "none"; trustedTestButton.style.display = "none"; } } localStorage.adifLogSettings = JSON.stringify(GT.adifLogSettings); } function adifNicknameCheckBoxChanged(what) { GT.adifLogSettings.nickname[what.id] = what.checked; if (what.id == "nicknameeQSLCheckBox") { if (what.checked == true) { eQSLNickname.style.display = "inline-block"; } else { eQSLNickname.style.display = "none"; } } localStorage.adifLogSettings = JSON.stringify(GT.adifLogSettings); } function adifTextValueChange(what) { what.value = what.value.trim(); GT.adifLogSettings.text[what.id] = what.value; localStorage.adifLogSettings = JSON.stringify(GT.adifLogSettings); } GT.fileSelector = document.createElement("input"); GT.fileSelector.setAttribute("type", "file"); GT.fileSelector.setAttribute("accept", ".adi,.adif"); GT.fileSelector.onchange = function () { if (this.files && this.files[0]) { var path = this.value.replace(this.files[0].name, ""); GT.fileSelector.setAttribute("nwworkingdir", path); var reader = new FileReader(); reader.onload = function (e) { if (e.target.error == null) { onAdiLoadComplete(e.target.result, false); } }; reader.readAsText(this.files[0]); } }; function adifLoadDialog() { var exists = GT.fileSelector.getAttribute("nwworkingdir"); GT.fileSelector.setAttribute("nwworkingdir", GT.appData); GT.fileSelector.click(); return false; } GT.startupFileSelector = document.createElement("input"); GT.startupFileSelector.setAttribute("type", "file"); GT.startupFileSelector.setAttribute("accept", ".adi,.adif"); GT.startupFileSelector.onchange = function () { if (this.files && this.files[0]) { for (var i in GT.startupLogs) { if (this.value == GT.startupLogs[i].file) return; } var newObject = Object(); newObject.name = this.files[0].name; newObject.file = this.value; GT.startupLogs.push(newObject); localStorage.startupLogs = JSON.stringify(GT.startupLogs); var path = this.value.replace(this.files[0].name, ""); GT.startupFileSelector.setAttribute("nwworkingdir", path); setAdifStartup(loadAdifCheckBox); } }; function start_and_end(str) { if (str.length > 31) { return ( str.substr(0, 16) + " ... " + str.substr(str.length - 15, str.length) ); } return str; } function setFileSelectors() { GT.selectStartupLink = document.getElementById("selectAdifButton"); GT.selectStartupLink.onclick = function () { let exists = GT.startupFileSelector.getAttribute("nwworkingdir"); if (exists == null) { if (GT.workingIniPath.length > 1) { GT.startupFileSelector.setAttribute("nwworkingdir", GT.appData); } } GT.startupFileSelector.click(); return false; }; selectTqsl = document.getElementById("selectTQSLButton"); selectTqsl.onclick = function () { GT.tqslFileSelector.click(); return false; }; lotwUpload.prepend(selectTqsl); } GT.tqslFileSelector = document.createElement("input"); GT.tqslFileSelector.setAttribute("type", "file"); GT.tqslFileSelector.setAttribute("accept", "*"); GT.tqslFileSelector.onchange = function () { if (this.files && this.files[0]) { GT.trustedQslSettings.binaryFile = this.files[0].path; var fs = require("fs"); if ( fs.existsSync(GT.trustedQslSettings.binaryFile) && (GT.trustedQslSettings.binaryFile.endsWith("tqsl.exe") || GT.trustedQslSettings.binaryFile.endsWith("tqsl")) ) { GT.trustedQslSettings.binaryFileValid = true; } else GT.trustedQslSettings.binaryFileValid = false; if (GT.trustedQslSettings.binaryFileValid == true) { tqslFileDiv.style.backgroundColor = "blue"; } else { tqslFileDiv.style.backgroundColor = "red"; } tqslFileDiv.innerHTML = "" + start_and_end(this.files[0].path) + ""; localStorage.trustedQslSettings = JSON.stringify(GT.trustedQslSettings); } }; function loadGtQSOLogFile() { var fs = require("fs"); if (fs.existsSync(GT.qsoLogFile)) { GT.fromDirectCallNoFileDialog = true; onAdiLoadComplete(fs.readFileSync(GT.qsoLogFile), false); } } function getFilesizeInBytes(filename) { var fs = require("fs"); var stats = fs.statSync(filename); var fileSizeInBytes = stats.size; return fileSizeInBytes; } function loadLoTWLogFile() { var fs = require("fs"); if (fs.existsSync(GT.LoTWLogFile) && getFilesizeInBytes(GT.LoTWLogFile) > 0) { GT.fromDirectCallNoFileDialog = true; onAdiLoadComplete(fs.readFileSync(GT.LoTWLogFile), false); } else { if (GT.appSettings.workingDateEnable == true && GT.appSettings.workingDate > 0) { var workDate = new Date(0); workDate.setSeconds(GT.appSettings.workingDate); var finalDate = workDate.getUTCFullYear() + "-" + padNumber(workDate.getUTCMonth() + 1) + "-" + padNumber(workDate.getUTCDate()); GT.adifLogSettings.lastFetch.lotw_qso = finalDate; GT.adifLogSettings.lastFetch.lotw_qsl = finalDate; } else { // We have no history, so our dates are not valid any more GT.adifLogSettings.lastFetch.lotw_qso = "1970-01-01"; GT.adifLogSettings.lastFetch.lotw_qsl = "1970-01-01"; } } } function loadWsjtLogFile() { var fs = require("fs"); if (fs.existsSync(GT.workingIniPath + "wsjtx_log.adi")) { GT.fromDirectCallNoFileDialog = true; onAdiLoadComplete(fs.readFileSync(GT.workingIniPath + "wsjtx_log.adi"), false); } } function findTrustedQSLPaths() { var process = require("process"); var base = null; if (GT.trustedQslSettings.stationFileValid == true) { // double check the presence of the station_data; if (!fs.existsSync(GT.trustedQslSettings.stationFile)) { GT.trustedQslSettings.stationFileValid = false; } } if (GT.trustedQslSettings.stationFileValid == false) { if (GT.platform == "windows") { base = process.env.APPDATA + "\\TrustedQSL\\station_data"; if (fs.existsSync(base)) { GT.trustedQslSettings.stationFile = base; GT.trustedQslSettings.stationFileValid = true; } else { base = process.env.LOCALAPPDATA + "\\TrustedQSL\\station_data"; if (fs.existsSync(base)) { GT.trustedQslSettings.stationFile = base; GT.trustedQslSettings.stationFileValid = true; } } } else { base = process.env.HOME + "/.tqsl/station_data"; if (fs.existsSync(base)) { GT.trustedQslSettings.stationFile = base; GT.trustedQslSettings.stationFileValid = true; } } } if (GT.trustedQslSettings.stationFileValid == true) { var validate = false; var option = document.createElement("option"); option.value = ""; option.text = "Select a Station"; lotwStation.appendChild(option); var buffer = fs.readFileSync(GT.trustedQslSettings.stationFile, "UTF-8"); parser = new DOMParser(); xmlDoc = parser.parseFromString(buffer, "text/xml"); var x = xmlDoc.getElementsByTagName("StationData"); for (var i = 0; i < x.length; i++) { option = document.createElement("option"); option.value = x[i].getAttribute("name"); option.text = x[i].getAttribute("name"); if (option.value == GT.adifLogSettings.text.lotwStation) { option.selected = true; validate = true; } lotwStation.appendChild(option); } if (validate) { ValidateText(lotwStation); } } if (GT.trustedQslSettings.binaryFileValid == true) { // double check the presence of the TrustedQSL binary; if (!fs.existsSync(GT.trustedQslSettings.binaryFile)) { GT.trustedQslSettings.binaryFileValid = false; } } if (GT.trustedQslSettings.binaryFileValid == false || GT.platform == "mac") { if (GT.platform == "windows") { base = process.env["ProgramFiles(x86)"] + "\\TrustedQSL\\tqsl.exe"; if (fs.existsSync(base)) { GT.trustedQslSettings.binaryFile = base; GT.trustedQslSettings.binaryFileValid = true; } } else if (GT.platform == "mac") { base = "/Applications/TrustedQSL/tqsl.app/Contents/MacOS/tqsl"; if (fs.existsSync(base)) { GT.trustedQslSettings.binaryFile = base; GT.trustedQslSettings.binaryFileValid = true; } else { base = process.env.HOME + "/Applications/TrustedQSL/tqsl.app/Contents/MacOS/tqsl"; if (fs.existsSync(base)) { GT.trustedQslSettings.binaryFile = base; GT.trustedQslSettings.binaryFileValid = true; } else { base = process.env.HOME + "/Applications/tqsl.app/Contents/MacOS/tqsl"; if (fs.existsSync(base)) { GT.trustedQslSettings.binaryFile = base; GT.trustedQslSettings.binaryFileValid = true; } else { base = "/Applications/tqsl.app/Contents/MacOS/tqsl"; if (fs.existsSync(base)) { GT.trustedQslSettings.binaryFile = base; GT.trustedQslSettings.binaryFileValid = true; } else { base = process.env.HOME + "/Desktop/TrustedQSL/tqsl.app/Contents/MacOS/tqsl"; if (fs.existsSync(base)) { GT.trustedQslSettings.binaryFile = base; GT.trustedQslSettings.binaryFileValid = true; } else { base = process.env.HOME + "/Applications/Ham Radio/tqsl.app/Contents/MacOS/tqsl"; if (fs.existsSync(base)) { GT.trustedQslSettings.binaryFile = base; GT.trustedQslSettings.binaryFileValid = true; } } } } } } } else if (GT.platform == "linux") { base = "/usr/bin/tqsl"; if (fs.existsSync(base)) { GT.trustedQslSettings.binaryFile = base; GT.trustedQslSettings.binaryFileValid = true; } else { base = "/usr/local/bin/tqsl"; if (fs.existsSync(base)) { GT.trustedQslSettings.binaryFile = base; GT.trustedQslSettings.binaryFileValid = true; } } } } localStorage.trustedQslSettings = JSON.stringify(GT.trustedQslSettings); } function startupAdifLoadFunction() { var fs = require("fs"); for (var i in GT.startupLogs) { try { if (fs.existsSync(GT.startupLogs[i].file)) { GT.fromDirectCallNoFileDialog = true; onAdiLoadComplete(fs.readFileSync(GT.startupLogs[i].file), false); } } catch (e) {} } } function setAdifStartup(checkbox) { if (GT.trustedQslSettings.binaryFile == null) { GT.trustedQslSettings.binaryFile = ""; } if ( GT.trustedQslSettings.binaryFile.endsWith("tqsl.exe") || GT.trustedQslSettings.binaryFile.endsWith("tqsl") ) { GT.trustedQslSettings.binaryFileValid = true; } else GT.trustedQslSettings.binaryFileValid = false; if (GT.trustedQslSettings.binaryFileValid == true) { tqslFileDiv.style.backgroundColor = "blue"; } else { tqslFileDiv.style.backgroundColor = "red"; } tqslFileDiv.innerHTML = "" + start_and_end(GT.trustedQslSettings.binaryFile) + ""; if (buttonAdifCheckBox.checked || loadAdifCheckBox.checked) { var worker = ""; if (GT.startupLogs.length > 0) { worker += ""; for (var i in GT.startupLogs) { worker += ""; } worker += "
" + GT.startupLogs[i].name + "
"; } else { worker = "No file(s) selected"; } startupLogFileDiv.innerHTML = worker; selectFileOnStartupDiv.style.display = "block"; } else { startupLogFileDiv.innerHTML = "No file(s) selected"; GT.startupFileSelector.setAttribute("type", ""); GT.startupFileSelector.setAttribute("type", "file"); GT.startupFileSelector.setAttribute("accept", ".adi*"); GT.startupFileSelector.value = null; selectFileOnStartupDiv.style.display = "none"; } } function removeStartupLog(i) { if (i in GT.startupLogs) { GT.startupLogs.splice(i, 1); localStorage.startupLogs = JSON.stringify(GT.startupLogs); setAdifStartup(loadAdifCheckBox); } } function startupAdifLoadCheck() { logEventMedia.value = GT.alertSettings.logEventMedia; loadWsjtLogFile(); if (loadGTCheckBox.checked == true) loadGtQSOLogFile(); if (loadAdifCheckBox.checked == true && GT.startupLogs.length > 0) { startupAdifLoadFunction(); } if (GT.mapSettings.offlineMode == false) { if (GT.appSettings.gtFlagImgSrc == 1) showGtFlags(); if (loadLOTWCheckBox.checked == true) { grabLOtWLog(false); } if (loadQRZCheckBox.checked == true) grabQrzComLog(false); if (loadClubCheckBox.checked == true) grabClubLog(false); if (loadPsk24CheckBox.checked == true) grabPsk24(); } } function getABuffer(file_url, callback, flag, mode, port, imgToGray, stringOfFlag, timeoutX) { var url = require("url"); var http = require(mode); var fileBuffer = null; var options = 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 method: "get" }; if (typeof stringOfFlag != "undefined") window[stringOfFlag] = true; if (typeof imgToGray != "undefined") { imgToGray.parentNode.style.background = "linear-gradient(grey 0%, black 0% 100% )"; imgToGray.style.webkitFilter = "invert(100%) grayscale(1)"; } var req = http.request(options, function (res) { var fsize = res.headers["content-length"]; var 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; if (typeof imgToGray != "undefined") { var percent = 0; if (fsize > 0) percent = parseInt((fileBuffer.length / fsize) * 100); else percent = parseInt(((fileBuffer.length / 100000) * 100) % 100); imgToGray.parentNode.style.background = "linear-gradient(grey " + percent + "%, black " + Number(percent + 10) + "% 100% )"; } }) .on("end", function () { if (typeof stringOfFlag != "undefined") { window[stringOfFlag] = false; } if (typeof imgToGray != "undefined") { imgToGray.parentNode.style.background = ""; imgToGray.style.webkitFilter = ""; } if (typeof callback == "function") { // Call it, since we have confirmed it is callable callback(fileBuffer, flag, cookies, file_url); } }) .on("error", function () { if (typeof stringOfFlag != "undefined") { window[stringOfFlag] = false; } if (typeof imgToGray != "undefined") { imgToGray.parentNode.style.background = ""; imgToGray.style.webkitFilter = ""; } }); }); req.on("socket", function (socket) { socket.on("timeout", function () { req.abort(); }); }); req.on("error", function () { if (typeof stringOfFlag != "undefined") { window[stringOfFlag] = false; } if (typeof imgToGray != "undefined") { imgToGray.parentNode.style.background = ""; imgToGray.style.webkitFilter = ""; } }); req.end(); } function getAPostBuffer( file_url, callback, flag, mode, port, theData, imgToGray, stringOfFlag ) { var querystring = require("querystring"); var postData = querystring.stringify(theData); var url = require("url"); var http = require(mode); var fileBuffer = null; var 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 method: "post", headers: { "Content-Type": "application/x-www-form-urlencoded", "Content-Length": postData.length } }; window[stringOfFlag] = true; if (typeof imgToGray != "undefined") { imgToGray.parentNode.style.background = "linear-gradient(grey 0%, black 0% 100% )"; imgToGray.style.webkitFilter = "invert(100%) grayscale(1)"; } var req = http.request(options, function (res) { var fsize = res.headers["content-length"]; var 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; if (typeof imgToGray != "undefined") { var percent = 0; if (fsize > 0) percent = parseInt((fileBuffer.length / fsize) * 100); else percent = parseInt(((fileBuffer.length / 100000) * 100) % 100); imgToGray.parentNode.style.background = "linear-gradient(grey " + percent + "%, black " + Number(percent + 10) + "% 100% )"; } }) .on("end", function () { if (typeof callback == "function") { // Call it, since we have confirmed it is callable callback(fileBuffer, flag, cookies); window[stringOfFlag] = false; if (typeof imgToGray != "undefined") { imgToGray.parentNode.style.background = ""; imgToGray.style.webkitFilter = ""; } } }) .on("error", function () { window[stringOfFlag] = false; if (typeof imgToGray != "undefined") { imgToGray.parentNode.style.background = ""; imgToGray.style.webkitFilter = ""; } }); }); req.on("socket", function (socket) { socket.on("timeout", function () { req.abort(); }); }); req.on("error", function (err) // eslint-disable-line node/handle-callback-err { window[stringOfFlag] = false; if (typeof imgToGray != "undefined") { imgToGray.parentNode.style.background = ""; imgToGray.style.webkitFilter = ""; } }); req.write(postData); req.end(); } function sendUdpMessage(msg, length, port, address) { var dgram = require("dgram"); var socket = dgram.createSocket({ type: "udp4", reuseAddr: true }); socket.send(msg, 0, length, port, address, (err) => // eslint-disable-line node/handle-callback-err { socket.close(); }); } function sendTcpMessage(msg, length, port, address) { var net = require("net"); var client = new net.Socket(); client.setTimeout(30000); client.connect(port, address, function () { client.write(Buffer.from(msg, "utf-8")); }); client.on("close", function () {}); } function valueToAdiField(field, value) { var adi = "<" + field + ":"; adi += Buffer.byteLength(String(value)) + ">"; adi += String(value) + " "; return adi; } function pad(value) { if (value < 10) { return "0" + value; } else { return value; } } function HMSfromMilli(milli) { var seconds = parseInt(milli / 1000); var days = Math.floor(seconds / (3600 * 24)); seconds -= days * 3600 * 24; var hrs = Math.floor(seconds / 3600); seconds -= hrs * 3600; var mnts = Math.floor(seconds / 60); seconds -= mnts * 60; val = String(pad(hrs)) + String(pad(mnts)) + String(pad(seconds)); return String(val); } function colonHMSfromMilli(milli) { var seconds = parseInt(milli / 1000); var days = Math.floor(seconds / (3600 * 24)); seconds -= days * 3600 * 24; var hrs = Math.floor(seconds / 3600); seconds -= hrs * 3600; var mnts = Math.floor(seconds / 60); seconds -= mnts * 60; val = String(pad(hrs)) + ":" + String(pad(mnts)) + ":" + String(pad(seconds)); return String(val); } function colonHMSfromSeconds(secondsIn) { var seconds = secondsIn; var days = Math.floor(seconds / (3600 * 24)); seconds -= days * 3600 * 24; var hrs = Math.floor(seconds / 3600); seconds -= hrs * 3600; var mnts = Math.floor(seconds / 60); seconds -= mnts * 60; val = String(pad(hrs)) + ":" + String(pad(mnts)) + ":" + String(pad(seconds)); return String(val); } function convertToDate(julian) { var DAY = 86400000; var HALF_DAY = DAY / 2; var UNIX_EPOCH_JULIAN_DATE = 2440587.5; var UNIX_EPOCH_JULIAN_DAY = 2440587; return new Date((Number(julian) - UNIX_EPOCH_JULIAN_DATE) * DAY); } const CLk = "25bc718451a71954cb6d0d1b50541dd45d4ba148"; GT.lastReport = ""; GT.oldStyleLogMessage = null; function oldSendToLogger() { var newMessage = Object.assign({}, GT.oldStyleLogMessage); var band = formatBand(Number(newMessage.Frequency / 1000000)); if ( newMessage.DXGrid.length == 0 && newMessage.DXCall + band + newMessage.MO in GT.liveCallsigns ) { newMessage.DXGrid = GT.liveCallsigns[ newMessage.DXCall + band + newMessage.MO ].grid.substr(0, 4); } var report = ""; report += valueToAdiField( "BAND", formatBand(Number(newMessage.Frequency / 1000000)) ); report += valueToAdiField("CALL", newMessage.DXCall.toUpperCase()); report += valueToAdiField( "FREQ", Number(newMessage.Frequency / 1000000).toFixed(6) ); report += valueToAdiField("MODE", newMessage.MO.toUpperCase()); var date = convertToDate(parseInt(newMessage.DateOn)); var dataString = date.getUTCFullYear() + ("0" + (date.getUTCMonth() + 1)).slice(-2) + ("0" + date.getUTCDate()).slice(-2); report += valueToAdiField("QSO_DATE", dataString); report += valueToAdiField("TIME_ON", HMSfromMilli(newMessage.TimeOn)); date = convertToDate(parseInt(newMessage.DateOff)); dataString = date.getUTCFullYear() + ("0" + (date.getUTCMonth() + 1)).slice(-2) + ("0" + date.getUTCDate()).slice(-2); report += valueToAdiField("QSO_DATE_OFF", dataString); report += valueToAdiField("TIME_OFF", HMSfromMilli(newMessage.TimeOff)); report += valueToAdiField("RST_RCVD", newMessage.ReportRecieved); report += valueToAdiField("RST_SENT", newMessage.ReportSend); report += valueToAdiField("TX_PWR", parseInt(newMessage.TXPower)); report += valueToAdiField("GRIDSQUARE", newMessage.DXGrid); if (newMessage.Comments.length > 0) { report += valueToAdiField("COMMENT", newMessage.Comments); } if (newMessage.Name.length > 0) { report += valueToAdiField("NAME", newMessage.Name); } if (newMessage.Operatorcall.length > 0) { report += valueToAdiField("OPERATOR", newMessage.Operatorcall); } if (newMessage.Mycall.length > 0) { report += valueToAdiField("STATION_CALLSIGN", newMessage.Mycall); } else if (GT.appSettings.myCall != "NOCALL" && GT.appSettings.myCall.length > 0) { report += valueToAdiField("STATION_CALLSIGN", GT.appSettings.myCall); } if (newMessage.Mygrid.length > 0) { report += valueToAdiField("MY_GRIDSQUARE", newMessage.Mygrid); } else if (GT.appSettings.myGrid.length > 1) { report += valueToAdiField("MY_GRIDSQUARE", GT.appSettings.myGrid); } report += ""; sendToLogger(report); } GT.adifLookupMap = { name: "NAME", iota: "IOTA", sota: "SOTA_REF", continent: "CONT", cqzone: "CQZ", ituzone: "ITUZ", email: "EMAIL", county: "CNTY", state: "STATE" }; function sendToLogger(ADIF) { var regex = new RegExp("", "i"); var record = parseADIFRecord(ADIF.split(regex)[1]); var localMode = record.MODE; if (localMode == "MFSK" && "SUBMODE" in record) { localMode = record.SUBMODE; } var localHash = record.CALL + record.BAND + localMode; if ( (!("GRIDSQUARE" in record) || record.GRIDSQUARE.length == 0) && localHash in GT.liveCallsigns ) { record.GRIDSQUARE = GT.liveCallsigns[localHash].grid.substr(0, 4); } if (GT.appSettings.potaEnabled == 1 && localHash in GT.liveCallsigns && GT.liveCallsigns[localHash].pota) { if (GT.liveCallsigns[localHash].pota != "?-????") { record.POTA = GT.liveCallsigns[localHash].pota; } } if ("TX_PWR" in record) { record.TX_PWR = String(parseInt(record.TX_PWR)); } if ( (!("STATION_CALLSIGN" in record) || record.STATION_CALLSIGN.length == 0) && GT.appSettings.myCall != "NOCALL" && GT.appSettings.myCall.length > 0 ) { record.STATION_CALLSIGN = GT.appSettings.myCall; } if ( (!("MY_GRIDSQUARE" in record) || record.MY_GRIDSQUARE.length == 0) && GT.appSettings.myGrid.length > 1 ) { record.MY_GRIDSQUARE = GT.appSettings.myGrid; } if (!("DXCC" in record)) { var dxcc = callsignToDxcc(record.CALL); if (dxcc == -1) dxcc = 0; record.DXCC = String(dxcc); } // Tag: This is going to bite us in the butt later, but leaving it alone. if (!("COUNTRY" in record) && Number(record.DXCC) > 0) { record.COUNTRY = GT.dxccToADIFName[Number(record.DXCC)]; } if (GT.appSettings.lookupMerge == true) { var request = GT.Idb .transaction(["lookups"], "readwrite") .objectStore("lookups") .get(record.CALL); request.onsuccess = function (event) { if (request.result) { var lookup = request.result; for (var key in lookup) { if (key in GT.adifLookupMap) { record[GT.adifLookupMap[key]] = lookup[key]; } } if ("GRIDSQUARE" in record && "grid" in lookup) { if ( record.GRIDSQUARE.substr(0, 4) == lookup.grid.substr(0, 4) ) { record.GRIDSQUARE = lookup.grid; } } if ( GT.appSettings.lookupMissingGrid && "grid" in lookup && (!("GRIDSQUARE" in record) || record.GRIDSQUARE.length == 0) ) { record.GRIDSQUARE = lookup.grid; } } finishSendingReport(record, localMode); }; request.onerror = function (event) { finishSendingReport(record, localMode); }; } else { finishSendingReport(record, localMode); } } function finishSendingReport(record, localMode) { var report = ""; for (const key in record) { if (key != "POTA") { report += "<" + key + ":" + Buffer.byteLength(record[key]) + ">" + record[key] + " "; } } report += ""; // this report is for internal use ONLY! var reportWithPota = ""; for (const key in record) { reportWithPota += "<" + key + ":" + Buffer.byteLength(record[key]) + ">" + record[key] + " "; } reportWithPota += ""; // Full record dupe check if (report != GT.lastReport) { GT.lastReport = report; if (GT.appSettings.potaEnabled == 1 && "POTA" in record) { reportPotaQSO(record); addLastTraffic("Spotted to POTA"); } if ( GT.N1MMSettings.enable == true && GT.N1MMSettings.port > 1024 && GT.N1MMSettings.ip.length > 4 ) { sendUdpMessage( report, report.length, parseInt(GT.N1MMSettings.port), GT.N1MMSettings.ip ); addLastTraffic("Logged to N1MM"); } if ( GT.log4OMSettings.enable == true && GT.log4OMSettings.port > 1024 && GT.log4OMSettings.ip.length > 4 ) { sendUdpMessage( "ADD " + report, report.length + 4, parseInt(GT.log4OMSettings.port), GT.log4OMSettings.ip ); addLastTraffic("Logged to Log4OM"); } try { onAdiLoadComplete("GT" + reportWithPota); } catch (e) { addLastTraffic("Exception Internal Log"); } try { // Log worthy if (logGTqsoCheckBox.checked == true) { var fs = require("fs"); fs.appendFileSync(GT.qsoLogFile, reportWithPota + "\r\n"); addLastTraffic( "Logged to GridTracker backup" ); } } catch (e) { console.log(e); addLastTraffic( "Exception GridTracker backup" ); } try { sendQrzLogEntry(report); } catch (e) { addLastTraffic("Exception QRZ Log"); } try { sendClubLogEntry(report); } catch (e) { addLastTraffic("Exception ClubLog Log"); } try { sendHrdLogEntry(report); } catch (e) { addLastTraffic("Exception HrdLog.net Log"); } try { sendCloudlogEntry(report); } catch (e) { addLastTraffic("Exception Cloudlog Log"); } if ( GT.acLogSettings.enable == true && GT.acLogSettings.port > 0 && GT.acLogSettings.ip.length > 4 ) { try { sendACLogMessage(record, GT.acLogSettings.port, GT.acLogSettings.ip); addLastTraffic("Logged to N3FJP"); } catch (e) { addLastTraffic("Exception N3FJP Log"); } } if ( GT.dxkLogSettings.enable == true && GT.dxkLogSettings.port > 0 && GT.dxkLogSettings.ip.length > 4 ) { try { sendDXKeeperLogMessage( report, GT.dxkLogSettings.port, GT.dxkLogSettings.ip ); addLastTraffic("Logged to DXKeeper"); } catch (e) { addLastTraffic("Exception DXKeeper Log"); } } if ( GT.HRDLogbookLogSettings.enable == true && GT.HRDLogbookLogSettings.port > 0 && GT.HRDLogbookLogSettings.ip.length > 4 ) { try { sendHRDLogbookEntry( record, GT.HRDLogbookLogSettings.port, GT.HRDLogbookLogSettings.ip ); addLastTraffic( "Logged to HRD Logbook" ); } catch (e) { addLastTraffic("Exception HRD Log"); } } try { sendLotwLogEntry(report); } catch (e) { addLastTraffic("Exception LoTW Log"); } try { sendHamCQEntry(report); } catch (e) { addLastTraffic("Exception HamCQ Log"); } if ( logeQSLQSOCheckBox.checked == true && nicknameeQSLCheckBox.checked == true && eQSLNickname.value.trim().length > 0 ) { record.APP_EQSL_QTH_NICKNAME = eQSLNickname.value.trim(); report = ""; for (var key in record) { report += "<" + key + ":" + Buffer.byteLength(record[key]) + ">" + record[key] + " "; } report += ""; } try { sendeQSLEntry(report); } catch (e) { addLastTraffic("Exception LoTW Log"); } try { alertLogMessage(); } catch (e) { addLastTraffic("Exception Alert Log"); } if (lookupCloseLog.checked == true) { try { openLookupWindow(false); } catch (e) { addLastTraffic("Exception Hide Lookup"); } } } return report; } function alertLogMessage() { if (logEventMedia.value != "none") { playAlertMediaFile(logEventMedia.value); } } function eqslCallback(buffer, flag) { var result = String(buffer); if (flag) { if (result.indexOf("No such Username/Password found") != -1) { eQSLTestResult.innerHTML = "Bad
Password
or
Nickname"; logeQSLQSOCheckBox.checked = false; adifLogQsoCheckBoxChanged(logeQSLQSOCheckBox); } else if (result.indexOf("No such Callsign found") != -1) { eQSLTestResult.innerHTML = "Unknown
Callsign"; logeQSLQSOCheckBox.checked = false; adifLogQsoCheckBoxChanged(logeQSLQSOCheckBox); } else if (result.indexOf("Your ADIF log file has been built") > -1 || result.indexOf("You have no log entries") > -1) { eQSLTestResult.innerHTML = "Passed"; } else if (result.indexOf("specify the desired User by using the QTHNickname") != -1) { eQSLTestResult.innerHTML = "QTH Nickname
Needed"; } else { eQSLTestResult.innerHTML = "Unknown
Error"; logeQSLQSOCheckBox.checked = false; adifLogQsoCheckBoxChanged(logeQSLQSOCheckBox); } } else { if (result.indexOf("Error: No match on eQSL_User/eQSL_Pswd") != -1) { addLastTraffic( "Fail log eQSL.cc (credentials)" ); } if ( result.indexOf("specify the desired User by using the QTHNickname") != -1 ) { addLastTraffic( "Fail log eQSL.cc (nickname)" ); } else if (result.indexOf("Result: 0 out of 1 records") != -1) { addLastTraffic("Fail log eQSL.cc (dupe)"); } else if (result.indexOf("Result: 1 out of 1 records added") != -1) { addLastTraffic("Logged to eQSL.cc"); } else { addLastTraffic("Fail log eQSL.cc (?)"); } } } function eQSLTest(test) { if (GT.mapSettings.offlineMode == true) return; eQSLTestResult.innerHTML = "Testing"; var fUrl = "https://www.eQSL.cc/qslcard/DownloadInBox.cfm?UserName=" + encodeURIComponent(eQSLUser.value) + "&Password=" + encodeURIComponent(eQSLPassword.value) + "&RcvdSince=2020101"; if (nicknameeQSLCheckBox.checked == true) { fUrl += "&QTHNickname=" + encodeURIComponent(eQSLNickname.value); } getABuffer(fUrl, eqslCallback, true, "https", 443); } function sendeQSLEntry(report) { if (GT.mapSettings.offlineMode == true) return; if (logeQSLQSOCheckBox.checked == true) { var pid = "GridTracker"; var pver = String(gtVersion); var header = "" + pid + "\r\n"; header += "" + pver + "\r\n"; header += "\r\n"; var eReport = encodeURIComponent(header + report); var fUrl = "https://www.eQSL.cc/qslcard/importADIF.cfm?ADIFData=" + eReport + "&EQSL_USER=" + encodeURIComponent(eQSLUser.value) + "&EQSL_PSWD=" + encodeURIComponent(eQSLPassword.value); getABuffer(fUrl, eqslCallback, false, "https", 443); } } function testTrustedQSL(test) { if (GT.mapSettings.offlineMode == true) { lotwTestResult.innerHTML = "Currently
offline"; return; } if ( logLOTWqsoCheckBox.checked == true && GT.trustedQslSettings.binaryFileValid == true && GT.trustedQslSettings.stationFileValid == true && lotwStation.value.length > 0 ) { lotwTestResult.innerHTML = "Testing Upload"; var child_process = require("child_process"); var options = Array(); options.push("-q"); options.push("-v"); child_process.execFile( GT.trustedQslSettings.binaryFile, options, (error, stdout, stderr) => { if (error) { lotwTestResult.innerHTML = "Error encountered"; } lotwTestResult.innerHTML = stderr; } ); } else { var worker = ""; if (GT.trustedQslSettings.binaryFileValid == false) { worker += "Invalid tqsl executable
"; } if (GT.trustedQslSettings.stationFileValid == false) { worker += "TrustQSL not installed
"; } if (!ValidateText(lotwTrusted)) worker += "TQSL Password missing
"; if (!ValidateText(lotwStation)) worker += "Select Station
"; lotwTestResult.innerHTML = worker; } } GT.trustTempPath = ""; function sendLotwLogEntry(report) { if (GT.mapSettings.offlineMode == true) return; if ( logLOTWqsoCheckBox.checked == true && GT.trustedQslSettings.binaryFileValid == true && GT.trustedQslSettings.stationFileValid == true && lotwStation.value.length > 0 ) { var header = "Generated " + userTimeString(null) + " for " + GT.appSettings.myCall + "\r\n\r\n"; var pid = "GridTracker"; var pver = String(gtVersion); header += "" + pid + "\r\n"; header += "" + pver + "\r\n"; header += "\r\n"; var finalLog = header + report + "\r\n"; GT.trustTempPath = os.tmpdir() + GT.dirSeperator + unique(report) + ".adif"; fs.writeFileSync(GT.trustTempPath, finalLog); var child_process = require("child_process"); var options = Array(); options.push("-a"); options.push("all"); options.push("-l"); options.push(lotwStation.value); if (lotwTrusted.value.length > 0) { options.push("-p"); options.push(lotwTrusted.value); } options.push("-q"); options.push("-x"); options.push("-d"); options.push("-u"); options.push(GT.trustTempPath); child_process.execFile( GT.trustedQslSettings.binaryFile, options, (error, stdout, stderr) => // eslint-disable-line node/handle-callback-err { if (stderr.indexOf("Final Status: Success") < 0) { addLastTraffic("Fail log to TQSL"); } else { addLastTraffic("Logged to TQSL"); } fs.unlinkSync(GT.trustTempPath); } ); } } function n1mmLoggerChanged() { GT.N1MMSettings.enable = buttonN1MMCheckBox.checked; GT.N1MMSettings.ip = N1MMIpInput.value; GT.N1MMSettings.port = N1MMPortInput.value; localStorage.N1MMSettings = JSON.stringify(GT.N1MMSettings); } function log4OMLoggerChanged() { GT.log4OMSettings.enable = buttonLog4OMCheckBox.checked; GT.log4OMSettings.ip = log4OMIpInput.value; GT.log4OMSettings.port = log4OMPortInput.value; localStorage.log4OMSettings = JSON.stringify(GT.log4OMSettings); } function acLogLoggerChanged() { GT.acLogSettings.enable = buttonacLogCheckBox.checked; GT.acLogSettings.ip = acLogIpInput.value; GT.acLogSettings.port = acLogPortInput.value; localStorage.acLogSettings = JSON.stringify(GT.acLogSettings); } function dxkLogLoggerChanged() { GT.dxkLogSettings.enable = buttondxkLogCheckBox.checked; GT.dxkLogSettings.ip = dxkLogIpInput.value; GT.dxkLogSettings.port = dxkLogPortInput.value; localStorage.dxkLogSettings = JSON.stringify(GT.dxkLogSettings); } function hrdLogbookLoggerChanged() { GT.HRDLogbookLogSettings.enable = buttonHrdLogbookCheckBox.checked; GT.HRDLogbookLogSettings.ip = hrdLogbookIpInput.value; GT.HRDLogbookLogSettings.port = hrdLogbookPortInput.value; localStorage.HRDLogbookLogSettings = JSON.stringify(GT.HRDLogbookLogSettings); } function CloudUrlErrorCallback( file_url, callback, flag, mode, port, theData, timeoutMs, timeoutCallback, message ) { CloudlogTestResult.innerHTML = message; } function CloudlogSendLogResult(buffer, flag) { if (flag && flag == true) { if (buffer) { if (buffer.indexOf("missing api key") > -1) { CloudlogTestResult.innerHTML = "API Key Invalid"; } else if (buffer.indexOf("created") > -1) { CloudlogTestResult.innerHTML = "Passed"; } else { CloudlogTestResult.innerHTML = "Invalid Response"; } } else { CloudlogTestResult.innerHTML = "Invalid Response"; } } else { if (buffer && buffer.indexOf("created") > -1) { addLastTraffic("Logged to Cloudlog"); } else addLastTraffic("Fail log to Cloudlog"); } } function qrzSendLogResult(buffer, flag) { if (typeof buffer != "undefined" && buffer != null) { var data = String(buffer); var kv = data.split("&"); if (kv.length > 0) { var arrData = Object(); for (var x in kv) { var split = kv[x].split("="); arrData[split[0]] = split[1]; } if (!("RESULT" in arrData)) { addLastTraffic("(Unknown Response)"); addLastTraffic("Fail log to QRZ.com"); } else if (arrData.RESULT != "OK") { if (arrData.RESULT == "FAIL" && "REASON" in arrData) { addLastTraffic("(" + arrData.REASON + ")"); } else if (arrData.RESULT == "AUTH") { addLastTraffic("(Invalid Auth)"); } else { addLastTraffic("Unknown Result (" + arrData.RESULT + ")"); } addLastTraffic("Fail log to QRZ.com"); } else { addLastTraffic("Logged to QRZ.com"); } } else { addLastTraffic("(Missing Response)"); addLastTraffic("Fail log to QRZ.com"); } } else { addLastTraffic("(No Response)"); addLastTraffic("Fail log to QRZ.com"); } } function postDialogRetryCallback( file_url, callback, flag, mode, port, theData, timeoutMs, timeoutCallback, who ) { if (window.confirm("Error sending QSO to " + who + ", retry?")) { getPostBuffer( file_url, callback, flag, mode, port, theData, timeoutMs, postDialogRetryCallback, who ); } } function postRetryErrorCallaback( file_url, callback, flag, mode, port, theData, timeoutMs, timeoutCallback, who ) { getPostBuffer( file_url, callback, flag, mode, port, theData, timeoutMs, postDialogRetryCallback, who ); } function sendQrzLogEntry(report) { if (GT.mapSettings.offlineMode == true) return; if (logQRZqsoCheckBox.checked == true && ValidateQrzApi(qrzApiKey)) { var postData = { KEY: qrzApiKey.value, ACTION: "INSERT", ADIF: report }; getPostBuffer( "https://logbook.qrz.com/api", qrzSendLogResult, null, "https", 443, postData, 30000, postRetryErrorCallaback, "QRZ.com" ); } } function clubLogQsoResult(buffer, flag) { addLastTraffic("Logged to ClubLog.org"); } function sendClubLogEntry(report) { if (GT.mapSettings.offlineMode == true) return; if (logClubqsoCheckBox.checked == true) { if (typeof nw != "undefined") { var postData = { email: clubEmail.value, password: clubPassword.value, callsign: clubCall.value, adif: report, api: CLk }; getPostBuffer( "https://clublog.org/realtime.php", clubLogQsoResult, null, "https", 443, postData, 30000, postRetryErrorCallaback, "ClubLog.org" ); } } } function sendCloudlogEntry(report) { if (GT.mapSettings.offlineMode == true) return; if (logCloudlogQSOCheckBox.checked == true) { var postData = { key: CloudlogAPI.value, station_profile_id: CloudlogStationProfileID.value, type: "adif", string: report }; getPostJSONBuffer( CloudlogURL.value, CloudlogSendLogResult, null, "https", 80, postData, 10000, CloudUrlErrorCallback, "Failed to Send" ); } } function hrdSendLogResult(buffer, flag) { if (flag && flag == true) { if (buffer.indexOf("Unknown user") > -1) { HRDLogTestResult.innerHTML = "Failed"; logHRDLOGqsoCheckBox.checked = false; adifLogQsoCheckBoxChanged(logHRDLOGqsoCheckBox); } else HRDLogTestResult.innerHTML = "Passed"; } else { if (buffer.indexOf("Unknown user") == -1) { addLastTraffic("Logged to HRDLOG.net"); } else { addLastTraffic("Fail log to HRDLOG.net"); } } } function sendHrdLogEntry(report) { if (GT.mapSettings.offlineMode == true) return; if (logHRDLOGqsoCheckBox.checked == true) { if (typeof nw != "undefined") { var postData = { Callsign: HRDLOGCallsign.value, Code: HRDLOGUploadCode.value, App: "GridTracker " + gtVersion, ADIFData: report }; getPostBuffer( "https://www.hrdlog.net/NewEntry.aspx", hrdSendLogResult, null, "https", 443, postData, 30000, postRetryErrorCallaback, "HRDLog.net" ); } } } function hrdCredentialTest(test) { if (test && test == true) { HRDLogTestResult.innerHTML = "Testing"; var postData = { Callsign: HRDLOGCallsign.value, Code: HRDLOGUploadCode.value }; getPostBuffer( "https://www.hrdlog.net/NewEntry.aspx", hrdSendLogResult, test, "https", 443, postData ); } } function CloudlogTest(test) { if (test && test == true) { if (ValidateText(CloudlogURL) && ValidateText(CloudlogAPI) && ValidateText(CloudlogStationProfileID)) { CloudlogTestResult.innerHTML = "Testing"; var postData = { key: CloudlogAPI.value, station_profile_id: CloudlogStationProfileID.value, type: "adif", string: "" }; getPostJSONBuffer( CloudlogURL.value, CloudlogSendLogResult, test, "https", 80, postData, 10000, CloudUrlErrorCallback, "No Response
or
Timeout" ); } else { CloudlogTestResult.innerHTML = "Missing Fields
Test Aborted"; } } } function HamCQTest(test) { if (test && test == true) { HamCQTestResult.innerHTML = "Testing"; var postData = { key: HamCQApiKey.value }; getPostJSONBuffer( "https://www.hamcq.cn/v1/logbook?from=gridtracker", HamCQSendResult, test, "https", 443, postData, 10000, HamCQErrorCallback, "Failed" ); } } function HamCQErrorCallback( file_url, callback, flag, mode, port, theData, timeoutMs, timeoutCallback, message ) { HamCQTestResult.innerHTML = message; } function HamCQSendResult(buffer, flag) { if (flag && flag == true) { if (buffer) { if (buffer.indexOf("Invalid") > -1) { HamCQTestResult.innerHTML = "Invalid"; } else if (buffer.indexOf("Pass") > -1) { HamCQTestResult.innerHTML = "Passed"; } else { HamCQTestResult.innerHTML = "Unknown Error"; } } else { HamCQTestResult.innerHTML = "Resp Err"; } } } function sendHamCQEntry(report) { if (GT.mapSettings.offlineMode == true) return; if (logHamCQqsoCheckBox.checked == true) { if (typeof nw != "undefined") { var postData = { key: HamCQApiKey.value, app: "GridTracker " + gtVersion, adif: report }; getPostBuffer( "https://www.hamcq.cn/v1/logbook?from=gridtracker", HamCQSendResult, null, "https", 443, postData, 30000, postRetryErrorCallaback, "HamCQ.cn" ); } } } function getPostJSONBuffer( file_url, callback, flag, mode, port, theData, timeoutMs, timeoutCallback, who ) { try { var postData = JSON.stringify(theData); var url = require("url"); var protocol = url.parse(file_url).protocol; // eslint-disable-line node/no-deprecated-api var http = require(protocol.replace(":", "")); var fileBuffer = null; var options = { host: url.parse(file_url).hostname, // eslint-disable-line node/no-deprecated-api port: url.parse(file_url).port, // eslint-disable-line node/no-deprecated-api path: url.parse(file_url).path, // eslint-disable-line node/no-deprecated-api method: "post", headers: { "Content-Type": "application/json", "Content-Length": postData.length, "User-Agent": gtUserAgent, "x-user-agent": gtUserAgent } }; var req = http.request(options, function (res) { var fsize = res.headers["content-length"]; var 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 () {}); }); if (typeof timeoutMs == "number" && timeoutMs > 0) { req.on("socket", function (socket) { socket.setTimeout(timeoutMs); socket.on("timeout", function () { req.abort(); }); }); req.on("error", function (err) // eslint-disable-line node/handle-callback-err { if (typeof timeoutCallback == "function") { timeoutCallback( file_url, callback, flag, mode, 80, theData, timeoutMs, timeoutCallback, who ); } }); } req.write(postData); req.end(); } catch (e) { if (typeof timeoutCallback != "undefined") { timeoutCallback( file_url, callback, flag, mode, 80, theData, timeoutMs, timeoutCallback, "Invalid Url" ); } } } function valueToXmlField(field, value) { var adi = "<" + field + ">"; adi += String(value); adi += ""; return adi; } function aclUpdateControlValue(control, value) { return ( valueToXmlField( "CMD", "" + valueToXmlField("CONTROL", control) + valueToXmlField("VALUE", value) ) + "\r\n" ); } function aclAction(action) { return ( valueToXmlField("CMD", "" + valueToXmlField("VALUE", action)) + "\r\n" ); } function adifField(record, key) { if (key in record) return record[key].trim(); else return ""; } function sendACLogMessage(record, port, address) { var report = ""; report += aclAction("CLEAR"); report += aclUpdateControlValue("TXTENTRYBAND", adifField(record, "BAND")); report += aclUpdateControlValue("TXTENTRYCALL", adifField(record, "CALL")); report += aclAction("CALLTAB"); report += aclUpdateControlValue( "TXTENTRYFREQUENCY", adifField(record, "FREQ") ); if (adifField(record, "SUBMODE").length > 0) { report += aclUpdateControlValue( "TXTENTRYMODE", adifField(record, "SUBMODE") ); } else { report += aclUpdateControlValue("TXTENTRYMODE", adifField(record, "MODE")); } var date = adifField(record, "QSO_DATE"); var dataString = date.substr(0, 4) + "/" + date.substr(4, 2) + "/" + date.substr(6); report += aclUpdateControlValue("TXTENTRYDATE", dataString); var timeVal = adifField(record, "TIME_ON"); var whenString = timeVal.substr(0, 2) + ":" + timeVal.substr(2, 2) + ":" + timeVal.substr(4, 2); report += aclUpdateControlValue("TXTENTRYTIMEON", whenString); timeVal = adifField(record, "TIME_OFF"); whenString = timeVal.substr(0, 2) + ":" + timeVal.substr(2, 2) + ":" + timeVal.substr(4, 2); report += aclUpdateControlValue("TXTENTRYTIMEOFF", whenString); report += aclUpdateControlValue( "TXTENTRYRSTR", adifField(record, "RST_RCVD") ); report += aclUpdateControlValue( "TXTENTRYRSTS", adifField(record, "RST_SENT") ); report += aclUpdateControlValue("TXTENTRYPOWER", adifField(record, "TX_PWR")); report += aclUpdateControlValue( "TXTENTRYGRID", adifField(record, "GRIDSQUARE") ); report += aclUpdateControlValue( "TXTENTRYCOMMENTS", adifField(record, "COMMENT") ); report += aclUpdateControlValue("TXTENTRYNAMER", adifField(record, "NAME")); report += aclUpdateControlValue("TXTENTRYIOTA", adifField(record, "IOTA")); report += aclUpdateControlValue( "TXTENTRYCONTINENT", adifField(record, "CONT") ); report += aclUpdateControlValue("TXTENTRYITUZ", adifField(record, "ITUZ")); report += aclUpdateControlValue("TXTENTRYCQZONE", adifField(record, "CQZ")); report += aclUpdateControlValue( "TXTENTRYCOUNTYR", replaceAll(adifField(record, "CNTY"), ", ", ",") ); var sentSpcNum = false; if (adifField(record, "SRX").length > 0) { report += aclUpdateControlValue( "TXTENTRYSERIALNOR", adifField(record, "SRX") ); } else if (adifField(record, "CONTEST_ID").length > 0) { report += aclUpdateControlValue( "TXTENTRYSPCNUM", adifField(record, "SRX_STRING") ); sentSpcNum = true; report += aclUpdateControlValue( "TXTENTRYCLASS", adifField(record, "CLASS") ); report += aclUpdateControlValue( "TXTENTRYSECTION", adifField(record, "ARRL_SECT") ); } if (adifField(record, "STATE").length > 0) { report += aclUpdateControlValue( "TXTENTRYSTATE", adifField(record, "STATE") ); if (sentSpcNum == false) { report += aclUpdateControlValue( "TXTENTRYSPCNUM", adifField(record, "STATE") ); } } report += aclAction("ENTER"); sendTcpMessage(report, report.length, port, address); } function sendDXKeeperLogMessage(newMessage, port, address) { var report = ""; report += valueToAdiField("command", "log"); report += valueToAdiField("parameters", newMessage); report += "\r\n"; sendTcpMessage(report, report.length, Number(port) + 1, address); } function parseADIFRecord(adif) { var regex = new RegExp("", "i"); var newLine = adif.split(regex); var line = newLine[0].trim(); // Catch the naughty case of someone sending two records at the same time var record = {}; // because strings are not escaped for adif.. ie: :'s and <'s .. we have to walk from left to right // cheesy, but damn i'm tired of parsing things var x = 0; while (line.length > 0) { while (line.charAt(0) != "<" && line.length > 0) { line = line.substr(1); } if (line.length > 0) { line = line.substr(1); var where = line.indexOf(":"); if (where != -1) { var fieldName = line.substr(0, where).toUpperCase(); line = line.substr(fieldName.length + 1); var fieldLength = parseInt(line); var end = line.indexOf(">"); if (end > 0) { line = line.substr(end + 1); var fieldValue = line.substr(0, fieldLength); line = line.substr(fieldLength); record[fieldName] = fieldValue; } } } } return record; } function sendHRDLogbookEntry(report, port, address) { var command = "ver\rdb add {"; var items = Object.assign({}, report); items.FREQ = items.FREQ.split(".").join(""); for (var item in items) { command += item + "=\"" + items[item] + "\" "; } command += "}\rexit\r"; sendTcpMessage(command, command.length, Number(port), address); }