33 Commits
Author SHA1 Message Date
n3mel cb50dd36fc Upload files to "General" 2026-09-05 11:59:03 -04:00
n3mel 7ce6c272aa Upload files to "Weather" 2026-09-05 11:55:23 -04:00
n3mel 692ee001fd Upload files to "ICS" 2026-09-05 11:54:49 -04:00
n3mel b5c84266f5 Upload files to "General" 2026-09-05 11:54:22 -04:00
n3mel 4a7715f0d0 Upload files to "/" 2026-09-05 11:53:59 -04:00
n3mel 14d1370309 Delete index.html 2026-09-05 11:53:37 -04:00
n3mel fab1ec4ad9 Delete directory 'Weather' 2026-09-05 11:53:31 -04:00
n3mel 72a723b4e4 Delete directory 'ICS' 2026-09-05 11:53:24 -04:00
n3mel f3fbed6f6c Delete directory 'General' 2026-09-05 11:53:08 -04:00
n3mel bbf7262015 Upload files to "Weather" 2026-09-05 10:17:56 -04:00
n3mel 8898a54389 Upload files to "General" 2026-09-05 10:17:30 -04:00
n3mel 992ec209ee Upload files to "ICS" 2026-09-05 10:16:48 -04:00
n3mel c3e33663c2 Delete directory 'Weather' 2026-09-05 10:16:09 -04:00
n3mel fe5995bc4b Delete directory 'General' 2026-09-05 10:15:58 -04:00
n3mel 00a0e3dff3 Delete directory 'ICS' 2026-09-05 10:15:51 -04:00
n3mel 91b7f645e8 Upload files to "Weather" 2026-09-04 20:41:12 -04:00
n3mel 1136f4938e Delete directory 'Weather' 2026-09-04 20:40:50 -04:00
n3mel c4dab8c353 Upload files to "General" 2026-09-04 20:40:37 -04:00
n3mel a7bb741270 Delete directory 'General' 2026-09-04 20:40:14 -04:00
n3mel c0a142b714 Upload files to "ICS" 2026-09-04 20:39:32 -04:00
n3mel 45558b0467 Delete directory 'ICS' 2026-09-04 20:38:56 -04:00
n3mel 42d2a6c8e0 Upload files to "ICS" 2026-09-04 20:38:38 -04:00
n3mel 9ae0eb17b2 Delete ICS/incident_status.html 2026-09-04 20:38:12 -04:00
n3mel bfa5594a11 Delete ICS/ics_214.html 2026-09-04 20:38:08 -04:00
n3mel b4189ae7cd Delete ICS/ics_213.html 2026-09-04 20:38:00 -04:00
n3mel 494d6eaa8c Delete ICS/ics_205.html 2026-09-04 20:37:56 -04:00
n3mel 3d8e3753cd Delete ICS/damage_assessment.html 2026-09-04 20:37:43 -04:00
n3mel 1ca50d13c7 Upload files to "Weather" 2026-09-04 20:32:16 -04:00
n3mel b6de970ec9 Upload files to "ICS" 2026-09-04 20:31:38 -04:00
n3mel 4641b41b5f Upload files to "General" 2026-09-04 20:31:08 -04:00
n3mel b38c784de6 Upload files to "General" 2026-09-04 19:43:10 -04:00
n3mel a818f4d46e Delete General/welfare_radiogram.html 2026-09-04 19:42:48 -04:00
n3mel 2ffdea626c Upload files to "General" 2026-09-04 18:10:03 -04:00
10 changed files with 820 additions and 607 deletions
+18 -56
View File
@@ -34,23 +34,12 @@
pre { background: #181c20; color: #39ff14; padding: 14px; border-radius: 6px; overflow-x: auto; font-family: "Courier New", Courier, monospace; font-size: 0.83rem; line-height: 1.32; min-height: 150px; } pre { background: #181c20; color: #39ff14; padding: 14px; border-radius: 6px; overflow-x: auto; font-family: "Courier New", Courier, monospace; font-size: 0.83rem; line-height: 1.32; min-height: 150px; }
.nav-bar { margin-bottom: 12px; font-size: 0.85rem; } .nav-bar { margin-bottom: 12px; font-size: 0.85rem; }
.nav-bar a { color: #00695c; text-decoration: none; font-weight: bold; } .nav-bar a { color: #00695c; text-decoration: none; font-weight: bold; }
.save-modal-backdrop { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.55); z-index: 99999; justify-content: center; align-items: center; }
/* Modal Save Dialog Window */
.save-modal-backdrop {
display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.55); z-index: 99999; justify-content: center; align-items: center;
}
.save-modal-backdrop.active { display: flex; } .save-modal-backdrop.active { display: flex; }
.save-modal-window { .save-modal-window { background: #ffffff; padding: 24px 28px; border-radius: 10px; width: 92%; max-width: 480px; box-shadow: 0 10px 30px rgba(0,0,0,0.3); border: 1px solid #cbd5e1; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }
background: #ffffff; padding: 24px 28px; border-radius: 10px; width: 92%; max-width: 480px;
box-shadow: 0 10px 30px rgba(0,0,0,0.3); border: 1px solid #cbd5e1; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.save-modal-window h3 { margin: 0 0 8px 0; font-size: 1.25rem; color: #1e293b; font-weight: bold; } .save-modal-window h3 { margin: 0 0 8px 0; font-size: 1.25rem; color: #1e293b; font-weight: bold; }
.save-modal-window p { margin: 0 0 16px 0; font-size: 0.88rem; color: #64748b; line-height: 1.4; } .save-modal-window p { margin: 0 0 16px 0; font-size: 0.88rem; color: #64748b; line-height: 1.4; }
.save-modal-window input[type="text"] { .save-modal-window input[type="text"] { width: 100%; padding: 10px 12px; font-size: 0.95rem; border: 2px solid #94a3b8; border-radius: 6px; margin-bottom: 20px; font-family: inherit; box-sizing: border-box; text-transform: none; }
width: 100%; padding: 10px 12px; font-size: 0.95rem; border: 2px solid #94a3b8; border-radius: 6px;
margin-bottom: 20px; font-family: inherit; box-sizing: border-box; text-transform: none;
}
.save-modal-window input[type="text"]:focus { border-color: #2563eb; outline: none; } .save-modal-window input[type="text"]:focus { border-color: #2563eb; outline: none; }
.save-modal-actions { display: flex; gap: 10px; justify-content: flex-end; } .save-modal-actions { display: flex; gap: 10px; justify-content: flex-end; }
.save-modal-btn { padding: 9px 18px; font-size: 0.9rem; font-weight: bold; border-radius: 6px; border: none; cursor: pointer; } .save-modal-btn { padding: 9px 18px; font-size: 0.9rem; font-weight: bold; border-radius: 6px; border: none; cursor: pointer; }
@@ -58,7 +47,6 @@
.save-modal-btn-confirm:hover { background: #1d4ed8; } .save-modal-btn-confirm:hover { background: #1d4ed8; }
.save-modal-btn-cancel { background: #e2e8f0; color: #334155; } .save-modal-btn-cancel { background: #e2e8f0; color: #334155; }
.save-modal-btn-cancel:hover { background: #cbd5e1; } .save-modal-btn-cancel:hover { background: #cbd5e1; }
</style> </style>
</head> </head>
<body> <body>
@@ -190,6 +178,19 @@
<pre id="outputPre">Fill in the fields above and click "Generate Packet ASCII"...</pre> <pre id="outputPre">Fill in the fields above and click "Generate Packet ASCII"...</pre>
</div> </div>
<!-- Custom File Name Modal Window -->
<div id="saveModalBackdrop" class="save-modal-backdrop">
<div class="save-modal-window">
<h3>Save Form as HTML</h3>
<p>Enter a name for the saved offline HTML file:</p>
<input type="text" id="saveModalFileName" spellcheck="false">
<div class="save-modal-actions">
<button type="button" class="save-modal-btn save-modal-btn-cancel" id="saveModalCancelBtn">Cancel</button>
<button type="button" class="save-modal-btn save-modal-btn-confirm" id="saveModalSaveBtn">Save File</button>
</div>
</div>
</div>
<script> <script>
function addCheckinRow() { function addCheckinRow() {
const container = document.getElementById('rosterContainer'); const container = document.getElementById('rosterContainer');
@@ -287,25 +288,6 @@
} }
} }
});
});
const pre = document.getElementById('outputPre');
if (pre) pre.textContent = pre.textContent;
const doctype = '<!DOCTYPE html>\n';
const htmlContent = doctype + document.documentElement.outerHTML;
const blob = new Blob([htmlContent], { type: 'text/html;charset=utf-8' });
const url = URL.createObjectURL(blob);
const now = new Date();
const dStr = now.toISOString().slice(0, 10);
const defaultName = 'net_roster_' + dStr + '.html';
let chosenName = prompt('Enter filename to save as:', defaultName);
if (!chosenName) return; // User cancelled
if (!chosenName.toLowerCase().endsWith('.html') && !chosenName.toLowerCase().endsWith('.txt')) {
chosenName += '.html';
}
function copyASCII() { function copyASCII() {
const txt = document.getElementById('outputPre').textContent; const txt = document.getElementById('outputPre').textContent;
if (navigator.clipboard && navigator.clipboard.writeText) { if (navigator.clipboard && navigator.clipboard.writeText) {
@@ -339,13 +321,11 @@
document.getElementById('btnNetNow').addEventListener('click', setUTC); document.getElementById('btnNetNow').addEventListener('click', setUTC);
document.getElementById('btnAddCheckin').addEventListener('click', addCheckinRow); document.getElementById('btnAddCheckin').addEventListener('click', addCheckinRow);
document.getElementById('btnGen').addEventListener('click', generateASCII); document.getElementById('btnGen').addEventListener('click', generateASCII);
document.getElementById('btnSaveHtml').addEventListener('click', openSaveModal);
document.getElementById('btnCopy').addEventListener('click', copyASCII); document.getElementById('btnCopy').addEventListener('click', copyASCII);
document.getElementById('btnClear').addEventListener('click', clearForm); document.getElementById('btnClear').addEventListener('click', clearForm);
// --- File Name Modal Window & HTML Exporter ---
// File Name Modal Window & HTML Exporter
function openSaveModal() { function openSaveModal() {
var now = new Date(); var now = new Date();
var dStr = now.toISOString().slice(0, 10); var dStr = now.toISOString().slice(0, 10);
@@ -405,6 +385,7 @@
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
} }
// Initialize event listeners safely after DOM loads
window.addEventListener('DOMContentLoaded', function() { window.addEventListener('DOMContentLoaded', function() {
var cancelBtn = document.getElementById('saveModalCancelBtn'); var cancelBtn = document.getElementById('saveModalCancelBtn');
var confirmBtn = document.getElementById('saveModalSaveBtn'); var confirmBtn = document.getElementById('saveModalSaveBtn');
@@ -427,29 +408,10 @@
var btnSaveMain = document.getElementById('btnSaveHtml'); var btnSaveMain = document.getElementById('btnSaveHtml');
if (btnSaveMain) { if (btnSaveMain) {
// Clear existing inline handlers just in case
btnSaveMain.onclick = null;
btnSaveMain.addEventListener('click', openSaveModal); btnSaveMain.addEventListener('click', openSaveModal);
} }
}); });
</script> </script>
<!-- Custom File Name Modal Window -->
<div id="saveModalBackdrop" class="save-modal-backdrop">
<div class="save-modal-window">
<h3>Save Form as HTML</h3>
<p>Enter a name for the saved offline HTML file:</p>
<input type="text" id="saveModalFileName" spellcheck="false">
<div class="save-modal-actions">
<button type="button" class="save-modal-btn save-modal-btn-cancel" id="saveModalCancelBtn">Cancel</button>
<button type="button" class="save-modal-btn save-modal-btn-confirm" id="saveModalSaveBtn">Save File</button>
</div>
</div>
</div>
</body> </body>
</html> </html>
+15 -41
View File
@@ -140,6 +140,19 @@
<pre id="outputArea">Form output will appear here...</pre> <pre id="outputArea">Form output will appear here...</pre>
</div> </div>
<!-- Custom File Name Modal Window -->
<div id="saveModalBackdrop" class="save-modal-backdrop">
<div class="save-modal-window">
<h3>Save Form as HTML</h3>
<p>Enter a name for the saved offline HTML file:</p>
<input type="text" id="saveModalFileName" spellcheck="false">
<div class="save-modal-actions">
<button type="button" class="save-modal-btn save-modal-btn-cancel" id="saveModalCancelBtn">Cancel</button>
<button type="button" class="save-modal-btn save-modal-btn-confirm" id="saveModalSaveBtn">Save File</button>
</div>
</div>
</div>
<script> <script>
const gridContainer = document.getElementById('wordGrid'); const gridContainer = document.getElementById('wordGrid');
for (let r = 0; r < 5; r++) { for (let r = 0; r < 5; r++) {
@@ -292,28 +305,7 @@
document.getElementById('btnCopy').addEventListener('click', copyOutput); document.getElementById('btnCopy').addEventListener('click', copyOutput);
document.getElementById('btnClear').addEventListener('click', clearForm); document.getElementById('btnClear').addEventListener('click', clearForm);
}); // --- File Name Modal Window & HTML Exporter ---
});
const pre = document.getElementById('outputPre') || document.getElementById('outputArea');
if (pre) {
pre.textContent = pre.textContent;
}
const doctype = '<!DOCTYPE html>\n';
const htmlContent = doctype + document.documentElement.outerHTML;
const blob = new Blob([htmlContent], { type: 'text/html;charset=utf-8' });
const url = URL.createObjectURL(blob);
const btnSave = document.getElementById('btnSaveHtml');
if (btnSave) {
btnSave.addEventListener('click', openSaveModal);
}
// File Name Modal Window & HTML Exporter
function openSaveModal() { function openSaveModal() {
var now = new Date(); var now = new Date();
var dStr = now.toISOString().slice(0, 10); var dStr = now.toISOString().slice(0, 10);
@@ -373,6 +365,7 @@
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
} }
// Initialize event listeners safely after DOM loads
window.addEventListener('DOMContentLoaded', function() { window.addEventListener('DOMContentLoaded', function() {
var cancelBtn = document.getElementById('saveModalCancelBtn'); var cancelBtn = document.getElementById('saveModalCancelBtn');
var confirmBtn = document.getElementById('saveModalSaveBtn'); var confirmBtn = document.getElementById('saveModalSaveBtn');
@@ -395,29 +388,10 @@
var btnSaveMain = document.getElementById('btnSaveHtml'); var btnSaveMain = document.getElementById('btnSaveHtml');
if (btnSaveMain) { if (btnSaveMain) {
// Clear existing inline handlers just in case
btnSaveMain.onclick = null;
btnSaveMain.addEventListener('click', openSaveModal); btnSaveMain.addEventListener('click', openSaveModal);
} }
}); });
</script> </script>
<!-- Custom File Name Modal Window -->
<div id="saveModalBackdrop" class="save-modal-backdrop">
<div class="save-modal-window">
<h3>Save Form as HTML</h3>
<p>Enter a name for the saved offline HTML file:</p>
<input type="text" id="saveModalFileName" spellcheck="false">
<div class="save-modal-actions">
<button type="button" class="save-modal-btn save-modal-btn-cancel" id="saveModalCancelBtn">Cancel</button>
<button type="button" class="save-modal-btn save-modal-btn-confirm" id="saveModalSaveBtn">Save File</button>
</div>
</div>
</div>
</body> </body>
</html> </html>
+361
View File
@@ -0,0 +1,361 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Winlink Check-in (Official Format - Packet Radio)</title>
<style>
* { box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace; background: #f0f4f8; color: #222; padding: 20px; margin: 0; }
.container { max-width: 920px; margin: 0 auto; background: #fff; padding: 24px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
.header { background: #137333; color: white; padding: 12px; border-radius: 6px; text-align: center; margin-bottom: 18px; }
.header h2 { margin: 0; font-size: 1.3rem; letter-spacing: 1px; }
.header p { margin: 4px 0 0 0; font-size: 0.85rem; opacity: 0.9; }
fieldset { border: 1px solid #a8dab5; border-radius: 6px; padding: 14px; margin-bottom: 14px; background: #f9fdfa; }
legend { font-weight: bold; padding: 0 8px; font-size: 0.85rem; color: #0d5a27; }
.row { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 10px; }
.col { flex: 1; min-width: 140px; display: flex; flex-direction: column; }
.label-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; }
label { font-size: 0.8rem; font-weight: 600; color: #333; }
input[type="text"], textarea, select { padding: 7px 8px; font-size: 0.88rem; border: 1px solid #999; border-radius: 4px; font-family: inherit; width: 100%; }
textarea { resize: vertical; min-height: 70px; }
.btn-now { background: #4a5568; color: #fff; padding: 4px 8px; font-size: 0.72rem; border-radius: 4px; border: none; cursor: pointer; }
.btn-now:hover { background: #2d3748; }
.actions { display: flex; gap: 10px; margin: 18px 0; }
button.main-btn { flex: 1; padding: 12px; font-size: 0.95rem; font-weight: bold; border: none; border-radius: 6px; cursor: pointer; }
.btn-generate { background: #137333; color: #fff; }
.btn-generate:hover { background: #0d5a27; }
.btn-save { background: #5c6bc0; color: #fff; }
.btn-save:hover { background: #3f51b5; }
.btn-copy { background: #2e7d32; color: #fff; }
.btn-copy:hover { background: #1b5e20; }
.btn-clear { background: #c62828; color: #fff; flex: 0.35; }
pre { background: #181c20; color: #39ff14; padding: 14px; border-radius: 6px; overflow-x: auto; font-family: "Courier New", Courier, monospace; font-size: 0.84rem; line-height: 1.35; min-height: 150px; }
.nav-bar { margin-bottom: 12px; font-size: 0.85rem; }
.nav-bar a { color: #137333; text-decoration: none; font-weight: bold; }
.save-modal-backdrop { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.55); z-index: 99999; justify-content: center; align-items: center; }
.save-modal-backdrop.active { display: flex; }
.save-modal-window { background: #ffffff; padding: 24px 28px; border-radius: 10px; width: 92%; max-width: 480px; box-shadow: 0 10px 30px rgba(0,0,0,0.3); border: 1px solid #cbd5e1; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }
.save-modal-window h3 { margin: 0 0 8px 0; font-size: 1.25rem; color: #1e293b; font-weight: bold; }
.save-modal-window p { margin: 0 0 16px 0; font-size: 0.88rem; color: #64748b; line-height: 1.4; }
.save-modal-window input[type="text"] { width: 100%; padding: 10px 12px; font-size: 0.95rem; border: 2px solid #94a3b8; border-radius: 6px; margin-bottom: 20px; font-family: inherit; box-sizing: border-box; text-transform: none; }
.save-modal-window input[type="text"]:focus { border-color: #2563eb; outline: none; }
.save-modal-actions { display: flex; gap: 10px; justify-content: flex-end; }
.save-modal-btn { padding: 9px 18px; font-size: 0.9rem; font-weight: bold; border-radius: 6px; border: none; cursor: pointer; }
.save-modal-btn-confirm { background: #2563eb; color: #ffffff; }
.save-modal-btn-confirm:hover { background: #1d4ed8; }
.save-modal-btn-cancel { background: #e2e8f0; color: #334155; }
.save-modal-btn-cancel:hover { background: #cbd5e1; }
</style>
</head>
<body>
<div class="container">
<div class="nav-bar"><a href="../index.html">&larr; Back to Packet Forms Hub</a></div>
<div class="header">
<h2>WINLINK CHECK-IN</h2>
<p>Official Winlink Check-in Form (Packet / BBS ASCII Format)</p>
</div>
<form id="checkinForm" onsubmit="event.preventDefault(); return false;">
<fieldset>
<legend>1. STATION INFORMATION</legend>
<div class="row">
<div class="col">
<div class="label-row">
<label for="dtZulu">a. Date / Time (UTC)</label>
<button type="button" class="btn-now" id="btnDateNow">Current UTC</button>
</div>
<input type="text" id="dtZulu" placeholder="YYYY-MM-DD HHMMZ">
</div>
<div class="col" style="flex: 2;"><label for="msgTo">b. To (Callsigns / Emails; semicolon separated)</label><input type="text" id="msgTo" placeholder="NCS; WINLINKNET"></div>
</div>
<div class="row">
<div class="col"><label for="msgSender">c. From (Callsign)</label><input type="text" id="msgSender" placeholder="N0CALL"></div>
<div class="col"><label for="contactName">d. Station Contact Name</label><input type="text" id="contactName" placeholder="Operator Name / Authority"></div>
<div class="col"><label for="assigned">e. Initial Operator(s)</label><input type="text" id="assigned" placeholder="Callsign(s) on site"></div>
<div class="col"><label for="exId">Optional Exercise ID</label><input type="text" id="exId" placeholder="e.g. SHAKEOUT"></div>
</div>
</fieldset>
<fieldset>
<legend>2. SESSION CONFIGURATION</legend>
<div class="row">
<div class="col">
<label for="sessType">a. Type</label>
<select id="sessType">
<option value="EXERCISE" selected>EXERCISE</option>
<option value="REAL EVENT">REAL EVENT</option>
</select>
</div>
<div class="col">
<label for="service">b. Service</label>
<select id="service">
<option value="AMATEUR" selected>AMATEUR (FCC Part 97)</option>
<option value="SHARES">SHARES (NTIA)</option>
</select>
</div>
<div class="col">
<label for="band">c. Band</label>
<select id="band">
<option value="NA">NA / Not Applicable</option>
<option value="Telnet" selected>Telnet</option>
<option value="HF">HF (3-30 MHz)</option>
<option value="VHF">VHF (30-300 MHz)</option>
<option value="UHF">UHF (300-3000 MHz)</option>
<option value="SHF">SHF (3 GHz+)</option>
</select>
</div>
<div class="col">
<label for="session">d. Mode / Session</label>
<select id="session">
<option value="Packet (AX.25)">Packet (AX.25)</option>
<option value="VARA FM">VARA FM</option>
<option value="VARA HF">VARA HF</option>
<option value="Telnet" selected>Telnet</option>
<option value="Pactor">Pactor</option>
<option value="Ardop">Ardop</option>
<option value="Robust Packet">Robust Packet</option>
<option value="Mesh (AREDN)">Mesh (AREDN)</option>
<option value="Iridium Go">Iridium Go</option>
</select>
</div>
</div>
</fieldset>
<fieldset>
<legend>3. LOCATION</legend>
<div class="row">
<div class="col" style="flex: 2;"><label for="location">a. Location Descriptor</label><input type="text" id="location" placeholder="e.g. Travis County EOC, Shelter Parking Lot"></div>
</div>
<div class="row">
<div class="col"><label for="lat">b. Latitude (Decimal)</label><input type="text" id="lat" placeholder="e.g. 30.2672"></div>
<div class="col"><label for="lon">c. Longitude (Decimal)</label><input type="text" id="lon" placeholder="e.g. -97.7431"></div>
<div class="col"><label for="mgrs">d. MGRS</label><input type="text" id="mgrs" placeholder="e.g. 14RPU12345678"></div>
<div class="col" style="max-width: 120px;"><label for="grid">e. Grid Square</label><input type="text" id="grid" placeholder="EM10dj"></div>
</div>
</fieldset>
<fieldset>
<legend>4. COMMENTS (Max 500 characters)</legend>
<textarea id="comments" rows="3" maxlength="500" placeholder="Power source (battery/generator), traffic on hand, weather status, relief schedule..."></textarea>
</fieldset>
<div class="actions">
<button type="button" class="main-btn btn-generate" id="btnGen">Generate Packet ASCII</button>
<button type="button" class="main-btn btn-save" id="btnSaveHtml">Save as HTML</button>
<button type="button" class="main-btn btn-copy" id="btnCopy">Copy ASCII</button>
<button type="button" class="main-btn btn-clear" id="btnClear">Reset</button>
</div>
</form>
<label style="font-weight:bold;">Formatted ASCII for Packet / BBS Transmission:</label>
<pre id="outputPre">Fill in the fields above and click "Generate Packet ASCII"...</pre>
</div>
<!-- Custom File Name Modal Window -->
<div id="saveModalBackdrop" class="save-modal-backdrop">
<div class="save-modal-window">
<h3>Save Form as HTML</h3>
<p>Enter a name for the saved offline HTML file:</p>
<input type="text" id="saveModalFileName" spellcheck="false">
<div class="save-modal-actions">
<button type="button" class="save-modal-btn save-modal-btn-cancel" id="saveModalCancelBtn">Cancel</button>
<button type="button" class="save-modal-btn save-modal-btn-confirm" id="saveModalSaveBtn">Save File</button>
</div>
</div>
</div>
<script>
function setUTC() {
const now = new Date();
const y = now.getUTCFullYear();
const m = String(now.getUTCMonth()+1).padStart(2,'0');
const d = String(now.getUTCDate()).padStart(2,'0');
const hh = String(now.getUTCHours()).padStart(2,'0');
const mm = String(now.getUTCMinutes()).padStart(2,'0');
document.getElementById('dtZulu').value = y + '-' + m + '-' + d + ' ' + hh + mm + 'Z';
}
function clean(id) {
const el = document.getElementById(id);
return el ? (el.value || '').trim() : '';
}
function pad(str, len) {
str = (str || '').toString();
return str + ' '.repeat(Math.max(0, len - str.length));
}
function generateASCII() {
try {
const div = '='.repeat(74);
const sub = '-'.repeat(74);
const dt = clean('dtZulu') || 'N/A';
const to = clean('msgTo').toUpperCase() || 'N/A';
const from = clean('msgSender').toUpperCase() || 'N/A';
const contact = clean('contactName').toUpperCase() || 'N/A';
const op = clean('assigned').toUpperCase() || 'N/A';
const ex = clean('exId').toUpperCase() || 'NONE';
const sType = clean('sessType').toUpperCase();
const sServ = clean('service').toUpperCase();
const sBand = clean('band').toUpperCase();
const sMode = clean('session').toUpperCase();
const loc = clean('location').toUpperCase() || 'N/A';
const lat = clean('lat') || 'N/A';
const lon = clean('lon') || 'N/A';
const mgrs = clean('mgrs').toUpperCase() || 'N/A';
const grid = clean('grid').toUpperCase() || 'N/A';
const cmt = clean('comments').toUpperCase() || 'NORMAL OPERATIONS - NO TRAFFIC';
const out = div + '\n' +
'WINLINK CHECK-IN REPORT (PACKET ASCII)\n' +
div + '\n' +
'STATUS / TYPE : ' + pad(sType, 16) + ' SERVICE: ' + sServ + '\n' +
'DATE / TIME : ' + pad(dt, 20) + ' EXERCISE ID: ' + ex + '\n' +
'TO : ' + to + '\n' +
'FROM : ' + pad(from, 16) + ' CONTACT: ' + contact + '\n' +
'INITIAL OPS : ' + op + '\n' +
sub + '\n' +
'SESSION INFO : BAND: ' + pad(sBand, 10) + ' MODE: ' + sMode + '\n' +
sub + '\n' +
'LOCATION : ' + loc + '\n' +
'COORDINATES : LAT: ' + pad(lat, 12) + ' LON: ' + lon + '\n' +
'MGRS / GRID : MGRS: ' + pad(mgrs, 16) + ' GRID: ' + grid + '\n' +
sub + '\n' +
'COMMENTS:\n ' + cmt + '\n' +
div;
document.getElementById('outputPre').textContent = out;
} catch(err) {
alert("Error generating text: " + err.message);
}
}
function copyASCII() {
const txt = document.getElementById('outputPre').textContent;
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(txt).then(function() {
alert("Check-in copied to clipboard!");
}).catch(function() {
fallbackCopy();
});
} else {
fallbackCopy();
}
}
function fallbackCopy() {
const pre = document.getElementById('outputPre');
const range = document.createRange();
range.selectNode(pre);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
document.execCommand('copy');
alert("Copied to clipboard!");
}
function clearForm() {
if(confirm("Reset form?")) {
document.getElementById('checkinForm').reset();
document.getElementById('outputPre').textContent = 'Fill in the fields above and click "Generate Packet ASCII"...';
}
}
document.getElementById('btnDateNow').addEventListener('click', setUTC);
document.getElementById('btnGen').addEventListener('click', generateASCII);
document.getElementById('btnCopy').addEventListener('click', copyASCII);
document.getElementById('btnClear').addEventListener('click', clearForm);
// --- File Name Modal Window & HTML Exporter ---
function openSaveModal() {
var now = new Date();
var dStr = now.toISOString().slice(0, 10);
var defName = 'winlink_checkin_' + dStr + '.html';
var modal = document.getElementById('saveModalBackdrop');
var input = document.getElementById('saveModalFileName');
if (input) input.value = defName;
if (modal) modal.classList.add('active');
setTimeout(function() {
if (input) { input.focus(); input.select(); }
}, 50);
}
function closeSaveModal() {
var modal = document.getElementById('saveModalBackdrop');
if (modal) modal.classList.remove('active');
}
function executeSaveHTML() {
var input = document.getElementById('saveModalFileName');
var chosenName = input ? input.value.trim() : '';
if (!chosenName) return;
if (!chosenName.toLowerCase().endsWith('.html') && !chosenName.toLowerCase().endsWith('.htm')) {
chosenName += '.html';
}
closeSaveModal();
document.querySelectorAll('input[type="text"]').forEach(function(el) { el.setAttribute('value', el.value); });
document.querySelectorAll('input[type="checkbox"], input[type="radio"]').forEach(function(el) {
if (el.checked) el.setAttribute('checked', 'checked');
else el.removeAttribute('checked');
});
document.querySelectorAll('textarea').forEach(function(el) { el.textContent = el.value; });
document.querySelectorAll('select').forEach(function(el) {
Array.from(el.options).forEach(function(opt) {
if (opt.value === el.value) opt.setAttribute('selected', 'selected');
else opt.removeAttribute('selected');
});
});
var pre = document.getElementById('ascii-report') || document.getElementById('outputPre') || document.getElementById('preview') || document.getElementById('outputArea');
if (pre) pre.textContent = pre.textContent;
var clonedDoc = document.documentElement.cloneNode(true);
var modalInClone = clonedDoc.querySelector('#saveModalBackdrop');
if (modalInClone) modalInClone.classList.remove('active');
var htmlContent = '<!DOCTYPE html>\n' + clonedDoc.outerHTML;
var blob = new Blob([htmlContent], { type: 'text/html;charset=utf-8' });
var url = URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = chosenName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// Initialize event listeners safely after DOM loads
window.addEventListener('DOMContentLoaded', function() {
var cancelBtn = document.getElementById('saveModalCancelBtn');
var confirmBtn = document.getElementById('saveModalSaveBtn');
var modalInput = document.getElementById('saveModalFileName');
var modalBackdrop = document.getElementById('saveModalBackdrop');
if (cancelBtn) cancelBtn.addEventListener('click', closeSaveModal);
if (confirmBtn) confirmBtn.addEventListener('click', executeSaveHTML);
if (modalInput) {
modalInput.addEventListener('keydown', function(e) {
if (e.key === 'Enter') { e.preventDefault(); executeSaveHTML(); }
if (e.key === 'Escape') { e.preventDefault(); closeSaveModal(); }
});
}
if (modalBackdrop) {
modalBackdrop.addEventListener('click', function(e) {
if (e.target === modalBackdrop) closeSaveModal();
});
}
var btnSaveMain = document.getElementById('btnSaveHtml');
if (btnSaveMain) {
btnSaveMain.addEventListener('click', openSaveModal);
}
});
</script>
</body>
</html>
+20 -59
View File
@@ -31,23 +31,12 @@
.channel-row input { font-size: 0.82rem; padding: 5px; } .channel-row input { font-size: 0.82rem; padding: 5px; }
.nav-bar { margin-bottom: 12px; font-size: 0.85rem; } .nav-bar { margin-bottom: 12px; font-size: 0.85rem; }
.nav-bar a { color: #4a148c; text-decoration: none; font-weight: bold; } .nav-bar a { color: #4a148c; text-decoration: none; font-weight: bold; }
.save-modal-backdrop { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.55); z-index: 99999; justify-content: center; align-items: center; }
/* Modal Save Dialog Window */
.save-modal-backdrop {
display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.55); z-index: 99999; justify-content: center; align-items: center;
}
.save-modal-backdrop.active { display: flex; } .save-modal-backdrop.active { display: flex; }
.save-modal-window { .save-modal-window { background: #ffffff; padding: 24px 28px; border-radius: 10px; width: 92%; max-width: 480px; box-shadow: 0 10px 30px rgba(0,0,0,0.3); border: 1px solid #cbd5e1; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }
background: #ffffff; padding: 24px 28px; border-radius: 10px; width: 92%; max-width: 480px;
box-shadow: 0 10px 30px rgba(0,0,0,0.3); border: 1px solid #cbd5e1; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.save-modal-window h3 { margin: 0 0 8px 0; font-size: 1.25rem; color: #1e293b; font-weight: bold; } .save-modal-window h3 { margin: 0 0 8px 0; font-size: 1.25rem; color: #1e293b; font-weight: bold; }
.save-modal-window p { margin: 0 0 16px 0; font-size: 0.88rem; color: #64748b; line-height: 1.4; } .save-modal-window p { margin: 0 0 16px 0; font-size: 0.88rem; color: #64748b; line-height: 1.4; }
.save-modal-window input[type="text"] { .save-modal-window input[type="text"] { width: 100%; padding: 10px 12px; font-size: 0.95rem; border: 2px solid #94a3b8; border-radius: 6px; margin-bottom: 20px; font-family: inherit; box-sizing: border-box; text-transform: none; }
width: 100%; padding: 10px 12px; font-size: 0.95rem; border: 2px solid #94a3b8; border-radius: 6px;
margin-bottom: 20px; font-family: inherit; box-sizing: border-box; text-transform: none;
}
.save-modal-window input[type="text"]:focus { border-color: #2563eb; outline: none; } .save-modal-window input[type="text"]:focus { border-color: #2563eb; outline: none; }
.save-modal-actions { display: flex; gap: 10px; justify-content: flex-end; } .save-modal-actions { display: flex; gap: 10px; justify-content: flex-end; }
.save-modal-btn { padding: 9px 18px; font-size: 0.9rem; font-weight: bold; border-radius: 6px; border: none; cursor: pointer; } .save-modal-btn { padding: 9px 18px; font-size: 0.9rem; font-weight: bold; border-radius: 6px; border: none; cursor: pointer; }
@@ -55,7 +44,6 @@
.save-modal-btn-confirm:hover { background: #1d4ed8; } .save-modal-btn-confirm:hover { background: #1d4ed8; }
.save-modal-btn-cancel { background: #e2e8f0; color: #334155; } .save-modal-btn-cancel { background: #e2e8f0; color: #334155; }
.save-modal-btn-cancel:hover { background: #cbd5e1; } .save-modal-btn-cancel:hover { background: #cbd5e1; }
</style> </style>
</head> </head>
<body> <body>
@@ -135,6 +123,19 @@
<pre id="outputPre">Fill in the fields above and click "Generate Packet ASCII"...</pre> <pre id="outputPre">Fill in the fields above and click "Generate Packet ASCII"...</pre>
</div> </div>
<!-- Custom File Name Modal Window -->
<div id="saveModalBackdrop" class="save-modal-backdrop">
<div class="save-modal-window">
<h3>Save Form as HTML</h3>
<p>Enter a name for the saved offline HTML file:</p>
<input type="text" id="saveModalFileName" spellcheck="false">
<div class="save-modal-actions">
<button type="button" class="save-modal-btn save-modal-btn-cancel" id="saveModalCancelBtn">Cancel</button>
<button type="button" class="save-modal-btn save-modal-btn-confirm" id="saveModalSaveBtn">Save File</button>
</div>
</div>
</div>
<script> <script>
function addChannelRow() { function addChannelRow() {
const container = document.getElementById('channelContainer'); const container = document.getElementById('channelContainer');
@@ -196,15 +197,15 @@
const out = div + '\n' + const out = div + '\n' +
'ICS 205 - RADIO COMMUNICATIONS PLAN (PACKET ASCII)\n' + 'ICS 205 - RADIO COMMUNICATIONS PLAN (PACKET ASCII)\n' +
div + '\n' + div + '\n' +
'INCIDENT NAME : ' + inc + '\n' + pad('INCIDENT NAME', 16) + ' : ' + inc + '\n' +
'OP PERIOD : ' + op + '\n' + pad('OP PERIOD', 16) + ' : ' + op + '\n' +
sub + '\n' + sub + '\n' +
'CH # | FUNCTION | NAME | RX FREQ/TONE | TX FREQ/TONE | REMARKS\n' + 'CH # | FUNCTION | NAME | RX FREQ/TONE | TX FREQ/TONE | REMARKS\n' +
sub + '\n' + sub + '\n' +
lines.join('\n') + '\n' + lines.join('\n') + '\n' +
sub + '\n' + sub + '\n' +
'PREPARED BY : ' + prep + '\n' + pad('PREPARED BY', 16) + ' : ' + prep + '\n' +
'DATE/TIME : ' + dt + '\n' + pad('DATE/TIME', 16) + ' : ' + dt + '\n' +
div; div;
document.getElementById('outputPre').textContent = out; document.getElementById('outputPre').textContent = out;
@@ -249,27 +250,6 @@
document.getElementById('btnCopy').addEventListener('click', copyASCII); document.getElementById('btnCopy').addEventListener('click', copyASCII);
document.getElementById('btnClear').addEventListener('click', clearForm); document.getElementById('btnClear').addEventListener('click', clearForm);
});
});
const pre = document.getElementById('outputPre') || document.getElementById('outputArea');
if (pre) {
pre.textContent = pre.textContent;
}
const doctype = '<!DOCTYPE html>\n';
const htmlContent = doctype + document.documentElement.outerHTML;
const blob = new Blob([htmlContent], { type: 'text/html;charset=utf-8' });
const url = URL.createObjectURL(blob);
const btnSave = document.getElementById('btnSaveHtml');
if (btnSave) {
btnSave.addEventListener('click', openSaveModal);
}
// File Name Modal Window & HTML Exporter // File Name Modal Window & HTML Exporter
function openSaveModal() { function openSaveModal() {
var now = new Date(); var now = new Date();
@@ -352,29 +332,10 @@
var btnSaveMain = document.getElementById('btnSaveHtml'); var btnSaveMain = document.getElementById('btnSaveHtml');
if (btnSaveMain) { if (btnSaveMain) {
// Clear existing inline handlers just in case
btnSaveMain.onclick = null;
btnSaveMain.addEventListener('click', openSaveModal); btnSaveMain.addEventListener('click', openSaveModal);
} }
}); });
</script> </script>
<!-- Custom File Name Modal Window -->
<div id="saveModalBackdrop" class="save-modal-backdrop">
<div class="save-modal-window">
<h3>Save Form as HTML</h3>
<p>Enter a name for the saved offline HTML file:</p>
<input type="text" id="saveModalFileName" spellcheck="false">
<div class="save-modal-actions">
<button type="button" class="save-modal-btn save-modal-btn-cancel" id="saveModalCancelBtn">Cancel</button>
<button type="button" class="save-modal-btn save-modal-btn-confirm" id="saveModalSaveBtn">Save File</button>
</div>
</div>
</div>
</body> </body>
</html> </html>
+31 -65
View File
@@ -31,23 +31,12 @@
pre { background: #181c20; color: #39ff14; padding: 14px; border-radius: 6px; overflow-x: auto; font-family: "Courier New", Courier, monospace; font-size: 0.85rem; line-height: 1.35; min-height: 150px; } pre { background: #181c20; color: #39ff14; padding: 14px; border-radius: 6px; overflow-x: auto; font-family: "Courier New", Courier, monospace; font-size: 0.85rem; line-height: 1.35; min-height: 150px; }
.nav-bar { margin-bottom: 12px; font-size: 0.85rem; } .nav-bar { margin-bottom: 12px; font-size: 0.85rem; }
.nav-bar a { color: #2f5496; text-decoration: none; font-weight: bold; } .nav-bar a { color: #2f5496; text-decoration: none; font-weight: bold; }
.save-modal-backdrop { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.55); z-index: 99999; justify-content: center; align-items: center; }
/* Modal Save Dialog Window */
.save-modal-backdrop {
display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.55); z-index: 99999; justify-content: center; align-items: center;
}
.save-modal-backdrop.active { display: flex; } .save-modal-backdrop.active { display: flex; }
.save-modal-window { .save-modal-window { background: #ffffff; padding: 24px 28px; border-radius: 10px; width: 92%; max-width: 480px; box-shadow: 0 10px 30px rgba(0,0,0,0.3); border: 1px solid #cbd5e1; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }
background: #ffffff; padding: 24px 28px; border-radius: 10px; width: 92%; max-width: 480px;
box-shadow: 0 10px 30px rgba(0,0,0,0.3); border: 1px solid #cbd5e1; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.save-modal-window h3 { margin: 0 0 8px 0; font-size: 1.25rem; color: #1e293b; font-weight: bold; } .save-modal-window h3 { margin: 0 0 8px 0; font-size: 1.25rem; color: #1e293b; font-weight: bold; }
.save-modal-window p { margin: 0 0 16px 0; font-size: 0.88rem; color: #64748b; line-height: 1.4; } .save-modal-window p { margin: 0 0 16px 0; font-size: 0.88rem; color: #64748b; line-height: 1.4; }
.save-modal-window input[type="text"] { .save-modal-window input[type="text"] { width: 100%; padding: 10px 12px; font-size: 0.95rem; border: 2px solid #94a3b8; border-radius: 6px; margin-bottom: 20px; font-family: inherit; box-sizing: border-box; text-transform: none; }
width: 100%; padding: 10px 12px; font-size: 0.95rem; border: 2px solid #94a3b8; border-radius: 6px;
margin-bottom: 20px; font-family: inherit; box-sizing: border-box; text-transform: none;
}
.save-modal-window input[type="text"]:focus { border-color: #2563eb; outline: none; } .save-modal-window input[type="text"]:focus { border-color: #2563eb; outline: none; }
.save-modal-actions { display: flex; gap: 10px; justify-content: flex-end; } .save-modal-actions { display: flex; gap: 10px; justify-content: flex-end; }
.save-modal-btn { padding: 9px 18px; font-size: 0.9rem; font-weight: bold; border-radius: 6px; border: none; cursor: pointer; } .save-modal-btn { padding: 9px 18px; font-size: 0.9rem; font-weight: bold; border-radius: 6px; border: none; cursor: pointer; }
@@ -55,7 +44,6 @@
.save-modal-btn-confirm:hover { background: #1d4ed8; } .save-modal-btn-confirm:hover { background: #1d4ed8; }
.save-modal-btn-cancel { background: #e2e8f0; color: #334155; } .save-modal-btn-cancel { background: #e2e8f0; color: #334155; }
.save-modal-btn-cancel:hover { background: #cbd5e1; } .save-modal-btn-cancel:hover { background: #cbd5e1; }
</style> </style>
</head> </head>
<body> <body>
@@ -147,6 +135,19 @@
<pre id="outputPre">Fill in the fields above and click "Generate Packet ASCII"...</pre> <pre id="outputPre">Fill in the fields above and click "Generate Packet ASCII"...</pre>
</div> </div>
<!-- Custom File Name Modal Window -->
<div id="saveModalBackdrop" class="save-modal-backdrop">
<div class="save-modal-window">
<h3>Save Form as HTML</h3>
<p>Enter a name for the saved offline HTML file:</p>
<input type="text" id="saveModalFileName" spellcheck="false">
<div class="save-modal-actions">
<button type="button" class="save-modal-btn save-modal-btn-cancel" id="saveModalCancelBtn">Cancel</button>
<button type="button" class="save-modal-btn save-modal-btn-confirm" id="saveModalSaveBtn">Save File</button>
</div>
</div>
</div>
<script> <script>
function setUTC() { function setUTC() {
const now = new Date(); const now = new Date();
@@ -183,6 +184,11 @@
return lines; return lines;
} }
function pad(str, len) {
str = (str || '').toString();
return str + ' '.repeat(Math.max(0, len - str.length));
}
function generateASCII() { function generateASCII() {
try { try {
const div = '='.repeat(74); const div = '='.repeat(74);
@@ -211,25 +217,25 @@
let out = div + '\n' + let out = div + '\n' +
'ICS 213 - GENERAL MESSAGE (PACKET / WINLINK)\n' + 'ICS 213 - GENERAL MESSAGE (PACKET / WINLINK)\n' +
div + '\n' + div + '\n' +
'INCIDENT NAME : ' + (inc || 'N/A') + '\n' + pad('INCIDENT NAME', 16) + ' : ' + (inc || 'N/A') + '\n' +
'MSG NUMBER : ' + (num || 'N/A') + ' PRECEDENCE : ' + prec + '\n' + pad('MSG NUMBER', 16) + ' : ' + (num || 'N/A') + ' PRECEDENCE : ' + prec + '\n' +
'DATE FILED : ' + (d || 'N/A') + ' TIME FILED : ' + (t || 'N/A') + '\n' + pad('DATE FILED', 16) + ' : ' + (d || 'N/A') + ' TIME FILED : ' + (t || 'N/A') + '\n' +
sub + '\n' + sub + '\n' +
'TO : ' + toName + ' | ' + toAg + '\n' + pad('TO', 16) + ' : ' + toName + ' | ' + toAg + '\n' +
'FROM : ' + fromName + ' | ' + fromAg + '\n' + pad('FROM', 16) + ' : ' + fromName + ' | ' + fromAg + '\n' +
'SUBJECT : ' + subj + '\n' + pad('SUBJECT', 16) + ' : ' + subj + '\n' +
sub + '\n' + sub + '\n' +
'MESSAGE TEXT:\n' + 'MESSAGE TEXT:\n' +
wrappedMsg + '\n' + wrappedMsg + '\n' +
sub + '\n' + sub + '\n' +
'APPROVED BY : ' + apprN + ' (' + apprP + ')\n' + pad('APPROVED BY', 16) + ' : ' + apprN + ' (' + apprP + ')\n' +
'OPERATOR / CALL: ' + (op || 'N/A') + '\n' + pad('OPERATOR/CALL', 16) + ' : ' + (op || 'N/A') + '\n' +
div; div;
if (rep) { if (rep) {
out += '\nREPLY:\n' + wrappedRep + '\n' + out += '\nREPLY:\n' + wrappedRep + '\n' +
'REPLIED BY : ' + repBy + '\n' + pad('REPLIED BY', 16) + ' : ' + repBy + '\n' +
'REPLY DATE/TIME: ' + repDT + '\n' + pad('REPLY DATE/TIME', 16) + ' : ' + repDT + '\n' +
div; div;
} }
@@ -274,27 +280,6 @@
document.getElementById('btnCopy').addEventListener('click', copyASCII); document.getElementById('btnCopy').addEventListener('click', copyASCII);
document.getElementById('btnClear').addEventListener('click', clearForm); document.getElementById('btnClear').addEventListener('click', clearForm);
});
});
const pre = document.getElementById('outputPre') || document.getElementById('outputArea');
if (pre) {
pre.textContent = pre.textContent;
}
const doctype = '<!DOCTYPE html>\n';
const htmlContent = doctype + document.documentElement.outerHTML;
const blob = new Blob([htmlContent], { type: 'text/html;charset=utf-8' });
const url = URL.createObjectURL(blob);
const btnSave = document.getElementById('btnSaveHtml');
if (btnSave) {
btnSave.addEventListener('click', openSaveModal);
}
// File Name Modal Window & HTML Exporter // File Name Modal Window & HTML Exporter
function openSaveModal() { function openSaveModal() {
var now = new Date(); var now = new Date();
@@ -377,29 +362,10 @@
var btnSaveMain = document.getElementById('btnSaveHtml'); var btnSaveMain = document.getElementById('btnSaveHtml');
if (btnSaveMain) { if (btnSaveMain) {
// Clear existing inline handlers just in case
btnSaveMain.onclick = null;
btnSaveMain.addEventListener('click', openSaveModal); btnSaveMain.addEventListener('click', openSaveModal);
} }
}); });
</script> </script>
<!-- Custom File Name Modal Window -->
<div id="saveModalBackdrop" class="save-modal-backdrop">
<div class="save-modal-window">
<h3>Save Form as HTML</h3>
<p>Enter a name for the saved offline HTML file:</p>
<input type="text" id="saveModalFileName" spellcheck="false">
<div class="save-modal-actions">
<button type="button" class="save-modal-btn save-modal-btn-cancel" id="saveModalCancelBtn">Cancel</button>
<button type="button" class="save-modal-btn save-modal-btn-confirm" id="saveModalSaveBtn">Save File</button>
</div>
</div>
</div>
</body> </body>
</html> </html>
+23 -62
View File
@@ -32,23 +32,12 @@
.log-desc { flex: 1; } .log-desc { flex: 1; }
.nav-bar { margin-bottom: 12px; font-size: 0.85rem; } .nav-bar { margin-bottom: 12px; font-size: 0.85rem; }
.nav-bar a { color: #b25900; text-decoration: none; font-weight: bold; } .nav-bar a { color: #b25900; text-decoration: none; font-weight: bold; }
.save-modal-backdrop { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.55); z-index: 99999; justify-content: center; align-items: center; }
/* Modal Save Dialog Window */
.save-modal-backdrop {
display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.55); z-index: 99999; justify-content: center; align-items: center;
}
.save-modal-backdrop.active { display: flex; } .save-modal-backdrop.active { display: flex; }
.save-modal-window { .save-modal-window { background: #ffffff; padding: 24px 28px; border-radius: 10px; width: 92%; max-width: 480px; box-shadow: 0 10px 30px rgba(0,0,0,0.3); border: 1px solid #cbd5e1; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }
background: #ffffff; padding: 24px 28px; border-radius: 10px; width: 92%; max-width: 480px;
box-shadow: 0 10px 30px rgba(0,0,0,0.3); border: 1px solid #cbd5e1; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.save-modal-window h3 { margin: 0 0 8px 0; font-size: 1.25rem; color: #1e293b; font-weight: bold; } .save-modal-window h3 { margin: 0 0 8px 0; font-size: 1.25rem; color: #1e293b; font-weight: bold; }
.save-modal-window p { margin: 0 0 16px 0; font-size: 0.88rem; color: #64748b; line-height: 1.4; } .save-modal-window p { margin: 0 0 16px 0; font-size: 0.88rem; color: #64748b; line-height: 1.4; }
.save-modal-window input[type="text"] { .save-modal-window input[type="text"] { width: 100%; padding: 10px 12px; font-size: 0.95rem; border: 2px solid #94a3b8; border-radius: 6px; margin-bottom: 20px; font-family: inherit; box-sizing: border-box; text-transform: none; }
width: 100%; padding: 10px 12px; font-size: 0.95rem; border: 2px solid #94a3b8; border-radius: 6px;
margin-bottom: 20px; font-family: inherit; box-sizing: border-box; text-transform: none;
}
.save-modal-window input[type="text"]:focus { border-color: #2563eb; outline: none; } .save-modal-window input[type="text"]:focus { border-color: #2563eb; outline: none; }
.save-modal-actions { display: flex; gap: 10px; justify-content: flex-end; } .save-modal-actions { display: flex; gap: 10px; justify-content: flex-end; }
.save-modal-btn { padding: 9px 18px; font-size: 0.9rem; font-weight: bold; border-radius: 6px; border: none; cursor: pointer; } .save-modal-btn { padding: 9px 18px; font-size: 0.9rem; font-weight: bold; border-radius: 6px; border: none; cursor: pointer; }
@@ -56,7 +45,6 @@
.save-modal-btn-confirm:hover { background: #1d4ed8; } .save-modal-btn-confirm:hover { background: #1d4ed8; }
.save-modal-btn-cancel { background: #e2e8f0; color: #334155; } .save-modal-btn-cancel { background: #e2e8f0; color: #334155; }
.save-modal-btn-cancel:hover { background: #cbd5e1; } .save-modal-btn-cancel:hover { background: #cbd5e1; }
</style> </style>
</head> </head>
<body> <body>
@@ -119,6 +107,19 @@
<pre id="outputPre">Fill in the fields above and click "Generate Packet ASCII"...</pre> <pre id="outputPre">Fill in the fields above and click "Generate Packet ASCII"...</pre>
</div> </div>
<!-- Custom File Name Modal Window -->
<div id="saveModalBackdrop" class="save-modal-backdrop">
<div class="save-modal-window">
<h3>Save Form as HTML</h3>
<p>Enter a name for the saved offline HTML file:</p>
<input type="text" id="saveModalFileName" spellcheck="false">
<div class="save-modal-actions">
<button type="button" class="save-modal-btn save-modal-btn-cancel" id="saveModalCancelBtn">Cancel</button>
<button type="button" class="save-modal-btn save-modal-btn-confirm" id="saveModalSaveBtn">Save File</button>
</div>
</div>
</div>
<script> <script>
function addLogRow() { function addLogRow() {
const container = document.getElementById('logEntries'); const container = document.getElementById('logEntries');
@@ -175,18 +176,18 @@
const out = div + '\n' + const out = div + '\n' +
'ICS 214 - ACTIVITY LOG (PACKET ASCII)\n' + 'ICS 214 - ACTIVITY LOG (PACKET ASCII)\n' +
div + '\n' + div + '\n' +
'INCIDENT NAME : ' + inc + '\n' + pad('INCIDENT NAME', 16) + ' : ' + inc + '\n' +
'OP PERIOD : ' + op + '\n' + pad('OP PERIOD', 16) + ' : ' + op + '\n' +
'NAME / CALL : ' + call + '\n' + pad('NAME / CALL', 16) + ' : ' + call + '\n' +
'ICS POSITION : ' + pos + '\n' + pad('ICS POSITION', 16) + ' : ' + pos + '\n' +
'AGENCY / ORG : ' + ag + '\n' + pad('AGENCY / ORG', 16) + ' : ' + ag + '\n' +
sub + '\n' + sub + '\n' +
'TIME (Z) | MAJOR ACTIVITIES & EVENTS\n' + 'TIME (Z) | MAJOR ACTIVITIES & EVENTS\n' +
sub + '\n' + sub + '\n' +
entries.join('\n') + '\n' + entries.join('\n') + '\n' +
sub + '\n' + sub + '\n' +
'PREPARED BY : ' + prep + '\n' + pad('PREPARED BY', 16) + ' : ' + prep + '\n' +
'DATE/TIME : ' + prepDT + '\n' + pad('DATE/TIME', 16) + ' : ' + prepDT + '\n' +
div; div;
document.getElementById('outputPre').textContent = out; document.getElementById('outputPre').textContent = out;
@@ -231,27 +232,6 @@
document.getElementById('btnCopy').addEventListener('click', copyASCII); document.getElementById('btnCopy').addEventListener('click', copyASCII);
document.getElementById('btnClear').addEventListener('click', clearForm); document.getElementById('btnClear').addEventListener('click', clearForm);
});
});
const pre = document.getElementById('outputPre') || document.getElementById('outputArea');
if (pre) {
pre.textContent = pre.textContent;
}
const doctype = '<!DOCTYPE html>\n';
const htmlContent = doctype + document.documentElement.outerHTML;
const blob = new Blob([htmlContent], { type: 'text/html;charset=utf-8' });
const url = URL.createObjectURL(blob);
const btnSave = document.getElementById('btnSaveHtml');
if (btnSave) {
btnSave.addEventListener('click', openSaveModal);
}
// File Name Modal Window & HTML Exporter // File Name Modal Window & HTML Exporter
function openSaveModal() { function openSaveModal() {
var now = new Date(); var now = new Date();
@@ -334,29 +314,10 @@
var btnSaveMain = document.getElementById('btnSaveHtml'); var btnSaveMain = document.getElementById('btnSaveHtml');
if (btnSaveMain) { if (btnSaveMain) {
// Clear existing inline handlers just in case
btnSaveMain.onclick = null;
btnSaveMain.addEventListener('click', openSaveModal); btnSaveMain.addEventListener('click', openSaveModal);
} }
}); });
</script> </script>
<!-- Custom File Name Modal Window -->
<div id="saveModalBackdrop" class="save-modal-backdrop">
<div class="save-modal-window">
<h3>Save Form as HTML</h3>
<p>Enter a name for the saved offline HTML file:</p>
<input type="text" id="saveModalFileName" spellcheck="false">
<div class="save-modal-actions">
<button type="button" class="save-modal-btn save-modal-btn-cancel" id="saveModalCancelBtn">Cancel</button>
<button type="button" class="save-modal-btn save-modal-btn-confirm" id="saveModalSaveBtn">Save File</button>
</div>
</div>
</div>
</body> </body>
</html> </html>
+85 -78
View File
@@ -4,35 +4,34 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Hurricane Report (Winlink / Packet Radio)</title> <title>Hurricane Report (Winlink / Packet Radio)</title>
<style> <style>
* { box-sizing: border-box; } * { box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace; background: #eef2f7; color: #222; padding: 20px; margin: 0; } body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace; background: #eef2f7; color: #222; padding: 20px; margin: 0; }
.container { max-width: 920px; margin: 0 auto; background: #fff; padding: 24px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .container { max-width: 960px; margin: 0 auto; background: #fff; padding: 24px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
.header { background: #01579b; color: white; padding: 12px; border-radius: 6px; text-align: center; margin-bottom: 18px; } .header { padding: 12px; border-radius: 6px; text-align: center; margin-bottom: 18px; color: white; }
.header h2 { margin: 0; font-size: 1.3rem; letter-spacing: 1px; } .header h2 { margin: 0; font-size: 1.3rem; letter-spacing: 1px; }
.header p { margin: 4px 0 0 0; font-size: 0.85rem; opacity: 0.9; } .header p { margin: 4px 0 0 0; font-size: 0.85rem; opacity: 0.9; }
fieldset { border: 1px solid #81d4fa; border-radius: 6px; padding: 14px; margin-bottom: 14px; background: #f9fdff; } fieldset { border: 1px solid #cbd5e1; border-radius: 6px; padding: 14px; margin-bottom: 14px; background: #fafcff; }
legend { font-weight: bold; padding: 0 8px; font-size: 0.85rem; color: #01579b; } legend { font-weight: bold; padding: 0 8px; font-size: 0.85rem; }
.row { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 10px; } .row { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 10px; }
.col { flex: 1; min-width: 140px; display: flex; flex-direction: column; } .col { flex: 1; min-width: 140px; display: flex; flex-direction: column; }
.label-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; } .label-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; }
label { font-size: 0.8rem; font-weight: 600; color: #333; } label { font-size: 0.8rem; font-weight: 600; color: #333; }
input[type="text"], select, textarea { padding: 7px 8px; font-size: 0.88rem; border: 1px solid #999; border-radius: 4px; font-family: inherit; width: 100%; } input[type="text"], select, textarea { padding: 7px 8px; font-size: 0.88rem; border: 1px solid #999; border-radius: 4px; font-family: inherit; width: 100%; }
textarea { resize: vertical; min-height: 70px; } textarea { resize: vertical; min-height: 60px; }
.btn-now { background: #4a5568; color: #fff; padding: 4px 8px; font-size: 0.72rem; border-radius: 4px; border: none; cursor: pointer; } .btn-now { background: #4a5568; color: #fff; padding: 4px 8px; font-size: 0.72rem; border-radius: 4px; border: none; cursor: pointer; }
.btn-now:hover { background: #2d3748; } .btn-now:hover { background: #2d3748; }
.actions { display: flex; gap: 10px; margin: 18px 0; } .actions { display: flex; gap: 10px; margin: 18px 0; }
button.main-btn { flex: 1; padding: 12px; font-size: 0.95rem; font-weight: bold; border: none; border-radius: 6px; cursor: pointer; } button.main-btn { flex: 1; padding: 12px; font-size: 0.95rem; font-weight: bold; border: none; border-radius: 6px; cursor: pointer; color: #fff; }
.btn-generate { background: #01579b; color: #fff; } .btn-save { background: #5c6bc0; }
.btn-generate:hover { background: #002f6c; }
.btn-save { background: #5c6bc0; color: #fff; }
.btn-save:hover { background: #3f51b5; } .btn-save:hover { background: #3f51b5; }
.btn-copy { background: #2e7d32; color: #fff; } .btn-copy { background: #2e7d32; }
.btn-copy:hover { background: #1b5e20; } .btn-copy:hover { background: #1b5e20; }
.btn-clear { background: #c62828; color: #fff; flex: 0.35; } .btn-clear { background: #c62828; flex: 0.35; }
pre { background: #181c20; color: #39ff14; padding: 14px; border-radius: 6px; overflow-x: auto; font-family: "Courier New", Courier, monospace; font-size: 0.82rem; line-height: 1.32; min-height: 150px; } .btn-clear:hover { background: #8e0000; }
pre { background: #181c20; color: #39ff14; padding: 14px; border-radius: 6px; overflow-x: auto; font-family: "Courier New", Courier, monospace; font-size: 0.82rem; line-height: 1.32; min-height: 140px; }
.nav-bar { margin-bottom: 12px; font-size: 0.85rem; } .nav-bar { margin-bottom: 12px; font-size: 0.85rem; }
.nav-bar a { color: #01579b; text-decoration: none; font-weight: bold; } .nav-bar a { text-decoration: none; font-weight: bold; }
.notice { font-size: 0.8rem; color: #01579b; font-weight: bold; margin-bottom: 8px; }
/* Modal Save Dialog Window */ /* Modal Save Dialog Window */
.save-modal-backdrop { .save-modal-backdrop {
@@ -58,6 +57,12 @@
.save-modal-btn-cancel { background: #e2e8f0; color: #334155; } .save-modal-btn-cancel { background: #e2e8f0; color: #334155; }
.save-modal-btn-cancel:hover { background: #cbd5e1; } .save-modal-btn-cancel:hover { background: #cbd5e1; }
.header { background: #01579b; }
legend { color: #01579b; }
.nav-bar a { color: #01579b; }
.btn-generate { background: #01579b; }
.btn-generate:hover { background: #002f6c; }
.notice { font-size: 0.8rem; color: #01579b; font-weight: bold; margin-bottom: 8px; }
</style> </style>
</head> </head>
<body> <body>
@@ -176,19 +181,22 @@
<pre id="outputPre">Fill in the fields above and click "Generate Packet ASCII"...</pre> <pre id="outputPre">Fill in the fields above and click "Generate Packet ASCII"...</pre>
</div> </div>
<!-- Custom File Name Modal Window -->
<script> <script>
function setUTC() { function setUTC() {
const now = new Date(); var now = new Date();
const y = now.getUTCFullYear(); var y = now.getUTCFullYear();
const m = String(now.getUTCMonth()+1).padStart(2,'0'); var m = String(now.getUTCMonth()+1).padStart(2,'0');
const d = String(now.getUTCDate()).padStart(2,'0'); var d = String(now.getUTCDate()).padStart(2,'0');
const hh = String(now.getUTCHours()).padStart(2,'0'); var hh = String(now.getUTCHours()).padStart(2,'0');
const mm = String(now.getUTCMinutes()).padStart(2,'0'); var mm = String(now.getUTCMinutes()).padStart(2,'0');
document.getElementById('dtZulu').value = y + '-' + m + '-' + d + ' ' + hh + mm + 'Z'; document.getElementById('dtZulu').value = y + '-' + m + '-' + d + ' ' + hh + mm + 'Z';
} }
function clean(id) { function clean(id) {
const el = document.getElementById(id); var el = document.getElementById(id);
return el ? (el.value || '').trim() : ''; return el ? (el.value || '').trim() : '';
} }
@@ -199,10 +207,10 @@
function wrapText(str, width) { function wrapText(str, width) {
if (!str) return []; if (!str) return [];
let lines = []; var lines = [];
str.split('\n').forEach(function(paragraph) { str.split('\n').forEach(function(paragraph) {
let words = paragraph.split(' '); var words = paragraph.split(' ');
let cur = ''; var cur = '';
words.forEach(function(w) { words.forEach(function(w) {
if ((cur + (cur ? ' ' : '') + w).length <= width) { if ((cur + (cur ? ' ' : '') + w).length <= width) {
cur += (cur ? ' ' : '') + w; cur += (cur ? ' ' : '') + w;
@@ -218,55 +226,55 @@
function generateASCII() { function generateASCII() {
try { try {
const div = '='.repeat(78); var div = '='.repeat(78);
const sub = '-'.repeat(78); var sub = '-'.repeat(78);
const dt = clean('dtZulu') || 'N/A'; var dt = clean('dtZulu') || 'N/A';
const rType = clean('repType').toUpperCase(); var rType = clean('repType').toUpperCase();
const call = clean('msgSender').toUpperCase() || 'N0CALL'; var call = clean('msgSender').toUpperCase() || 'N0CALL';
const isObs = clean('isObserver').toUpperCase(); var isObs = clean('isObserver').toUpperCase();
const email = clean('obsEmail').toUpperCase() || 'N/A'; var email = clean('obsEmail').toUpperCase() || 'N/A';
const phone = clean('obsPhone') || 'N/A'; var phone = clean('obsPhone') || 'N/A';
const city = clean('city').toUpperCase(); var city = clean('city').toUpperCase();
const county = clean('county').toUpperCase(); var county = clean('county').toUpperCase();
const st = clean('state').toUpperCase(); var st = clean('state').toUpperCase();
const country = clean('country').toUpperCase(); var country = clean('country').toUpperCase();
const lat = clean('lat') || 'N/A'; var lat = clean('lat') || 'N/A';
const lon = clean('lon') || 'N/A'; var lon = clean('lon') || 'N/A';
const mgrs = clean('mgrs').toUpperCase() || 'N/A'; var mgrs = clean('mgrs').toUpperCase() || 'N/A';
const grid = clean('grid').toUpperCase() || 'N/A'; var grid = clean('grid').toUpperCase() || 'N/A';
const mType = clean('measType').toUpperCase(); var mType = clean('measType').toUpperCase();
const inst = clean('instruments').toUpperCase() || 'N/A'; var inst = clean('instruments').toUpperCase() || 'N/A';
const wspd = clean('windSpeed') || 'N/A'; var wspd = clean('windSpeed') || 'N/A';
const wunit = clean('windUnit'); var wunit = clean('windUnit');
const gspd = clean('gustSpeed') || 'N/A'; var gspd = clean('gustSpeed') || 'N/A';
const gunit = clean('gustUnit'); var gunit = clean('gustUnit');
const wdir = clean('windDir'); var wdir = clean('windDir');
const press = clean('pressure') || 'N/A'; var press = clean('pressure') || 'N/A';
const punit = clean('pressUnit'); var punit = clean('pressUnit');
const cmt = clean('comments').toUpperCase() || 'NONE REPORTED'; var cmt = clean('comments').toUpperCase() || 'NONE REPORTED';
let wrappedCmt = wrapText(cmt, 72).map(function(l){ return ' ' + l; }).join('\n'); var wrappedCmt = wrapText(cmt, 72).map(function(l){ return ' ' + l; }).join('\n');
const out = div + '\n' + var out = div + '\n' +
'HURRICANE / TROPICAL EVENT REPORT (PACKET ASCII)\n' + 'HURRICANE / TROPICAL EVENT REPORT (PACKET ASCII)\n' +
div + '\n' + div + '\n' +
'TO: WX4NHC (NATIONAL HURRICANE CENTER) / HWN\n' + pad('TO', 16) + ' : WX4NHC (NATIONAL HURRICANE CENTER) / HWN\n' +
'STATUS / VERSION: ' + pad(rType, 18) + ' DATE/TIME (UTC): ' + dt + '\n' + pad('STATUS / VER', 16) + ' : ' + pad(rType, 18) + ' DATE/TIME (UTC) : ' + dt + '\n' +
'SENDER CALLSIGN : ' + pad(call, 18) + ' OBSERVER STATUS: ' + isObs + '\n' + pad('SENDER CALL', 16) + ' : ' + pad(call, 18) + ' OBSERVER STATUS : ' + isObs + '\n' +
'OBSERVER CONTACT: EMAIL: ' + email + ' | PHONE: ' + phone + '\n' + pad('CONTACT', 16) + ' : EMAIL: ' + email + ' | PHONE: ' + phone + '\n' +
sub + '\n' + sub + '\n' +
'LOCATION & COORDINATES:\n' + 'LOCATION & COORDINATES:\n' +
'AREA : ' + (city ? city + ', ' : '') + (county ? county + ' CO, ' : '') + st + ' (' + country + ')\n' + pad('AREA', 16) + ' : ' + (city ? city + ', ' : '') + (county ? county + ' CO, ' : '') + st + ' (' + country + ')\n' +
'COORDS: LAT: ' + pad(lat, 12) + ' LON: ' + pad(lon, 12) + ' MGRS: ' + pad(mgrs, 16) + ' GRID: ' + grid + '\n' + pad('COORDS', 16) + ' : LAT: ' + pad(lat, 12) + ' LON: ' + pad(lon, 12) + ' MGRS: ' + pad(mgrs, 16) + ' GRID: ' + grid + '\n' +
sub + '\n' + sub + '\n' +
'OBSERVED METEOROLOGICAL DATA:\n' + 'OBSERVED METEOROLOGICAL DATA:\n' +
'MEASUREMENT TYPE: ' + mType + '\n' + pad('MEASUREMENT TYPE', 16) + ' : ' + mType + '\n' +
'INSTRUMENTS : ' + inst + '\n' + pad('INSTRUMENTS', 16) + ' : ' + inst + '\n' +
'SUSTAINED WIND : ' + wspd + ' ' + wunit + ' GUST: ' + gspd + ' ' + gunit + ' DIR: ' + wdir + '\n' + pad('SUSTAINED WIND', 16) + ' : ' + wspd + ' ' + wunit + ' GUST: ' + gspd + ' ' + gunit + ' DIR: ' + wdir + '\n' +
'BAROMETER : ' + press + ' ' + punit + '\n' + pad('BAROMETER', 16) + ' : ' + press + ' ' + punit + '\n' +
sub + '\n' + sub + '\n' +
'SURGE, FLOODING, DAMAGE & EYEWITNESS REPORT:\n' + 'SURGE, FLOODING, DAMAGE & EYEWITNESS REPORT:\n' +
wrappedCmt + '\n' + wrappedCmt + '\n' +
@@ -278,9 +286,8 @@
} }
} }
function copyASCII() { function copyASCII() {
const txt = document.getElementById('outputPre').textContent; var txt = document.getElementById('outputPre').textContent;
if (navigator.clipboard && navigator.clipboard.writeText) { if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(txt).then(function() { navigator.clipboard.writeText(txt).then(function() {
alert("Hurricane Report copied to clipboard!"); alert("Hurricane Report copied to clipboard!");
@@ -293,8 +300,8 @@
} }
function fallbackCopy() { function fallbackCopy() {
const pre = document.getElementById('outputPre'); var pre = document.getElementById('outputPre');
const range = document.createRange(); var range = document.createRange();
range.selectNode(pre); range.selectNode(pre);
window.getSelection().removeAllRanges(); window.getSelection().removeAllRanges();
window.getSelection().addRange(range); window.getSelection().addRange(range);
@@ -309,15 +316,19 @@
} }
} }
document.getElementById('btnDateNow').addEventListener('click', setUTC); window.addEventListener('DOMContentLoaded', function() {
document.getElementById('btnGen').addEventListener('click', generateASCII); document.getElementById('btnDateNow').addEventListener('click', setUTC);
document.getElementById('btnSaveHtml').addEventListener('click', openSaveModal); document.getElementById('btnGen').addEventListener('click', generateASCII);
document.getElementById('btnCopy').addEventListener('click', copyASCII); document.getElementById('btnCopy').addEventListener('click', copyASCII);
document.getElementById('btnClear').addEventListener('click', clearForm); document.getElementById('btnClear').addEventListener('click', clearForm);
// --- File Name Modal Window & HTML Exporter ---
// File Name Modal Window & HTML Exporter });
// --- File Name Modal Window & HTML Exporter ---
function openSaveModal() { function openSaveModal() {
var now = new Date(); var now = new Date();
var dStr = now.toISOString().slice(0, 10); var dStr = now.toISOString().slice(0, 10);
@@ -345,6 +356,7 @@
} }
closeSaveModal(); closeSaveModal();
// Sync input values to DOM attributes so they save offline
document.querySelectorAll('input[type="text"]').forEach(function(el) { el.setAttribute('value', el.value); }); document.querySelectorAll('input[type="text"]').forEach(function(el) { el.setAttribute('value', el.value); });
document.querySelectorAll('input[type="checkbox"], input[type="radio"]').forEach(function(el) { document.querySelectorAll('input[type="checkbox"], input[type="radio"]').forEach(function(el) {
if (el.checked) el.setAttribute('checked', 'checked'); if (el.checked) el.setAttribute('checked', 'checked');
@@ -377,6 +389,7 @@
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
} }
// Initialize event listeners safely after DOM loads
window.addEventListener('DOMContentLoaded', function() { window.addEventListener('DOMContentLoaded', function() {
var cancelBtn = document.getElementById('saveModalCancelBtn'); var cancelBtn = document.getElementById('saveModalCancelBtn');
var confirmBtn = document.getElementById('saveModalSaveBtn'); var confirmBtn = document.getElementById('saveModalSaveBtn');
@@ -399,17 +412,11 @@
var btnSaveMain = document.getElementById('btnSaveHtml'); var btnSaveMain = document.getElementById('btnSaveHtml');
if (btnSaveMain) { if (btnSaveMain) {
// Clear existing inline handlers just in case
btnSaveMain.onclick = null;
btnSaveMain.addEventListener('click', openSaveModal); btnSaveMain.addEventListener('click', openSaveModal);
} }
}); });
</script> </script>
<!-- Custom File Name Modal Window --> <!-- Custom File Name Modal Window -->
<div id="saveModalBackdrop" class="save-modal-backdrop"> <div id="saveModalBackdrop" class="save-modal-backdrop">
<div class="save-modal-window"> <div class="save-modal-window">
+93 -86
View File
@@ -4,14 +4,15 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Current Local Weather Conditions (Packet Radio)</title> <title>Current Local Weather Conditions (Packet Radio)</title>
<style> <style>
* { box-sizing: border-box; } * { box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace; background: #eef2f7; color: #222; padding: 20px; margin: 0; } body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace; background: #eef2f7; color: #222; padding: 20px; margin: 0; }
.container { max-width: 960px; margin: 0 auto; background: #fff; padding: 24px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .container { max-width: 960px; margin: 0 auto; background: #fff; padding: 24px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
.header { background: #0277bd; color: white; padding: 12px; border-radius: 6px; text-align: center; margin-bottom: 18px; } .header { padding: 12px; border-radius: 6px; text-align: center; margin-bottom: 18px; color: white; }
.header h2 { margin: 0; font-size: 1.3rem; letter-spacing: 1px; } .header h2 { margin: 0; font-size: 1.3rem; letter-spacing: 1px; }
.header p { margin: 4px 0 0 0; font-size: 0.85rem; opacity: 0.9; } .header p { margin: 4px 0 0 0; font-size: 0.85rem; opacity: 0.9; }
fieldset { border: 1px solid #81d4fa; border-radius: 6px; padding: 14px; margin-bottom: 14px; background: #f9fdff; } fieldset { border: 1px solid #cbd5e1; border-radius: 6px; padding: 14px; margin-bottom: 14px; background: #fafcff; }
legend { font-weight: bold; padding: 0 8px; font-size: 0.85rem; color: #0277bd; } legend { font-weight: bold; padding: 0 8px; font-size: 0.85rem; }
.row { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 10px; } .row { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 10px; }
.col { flex: 1; min-width: 140px; display: flex; flex-direction: column; } .col { flex: 1; min-width: 140px; display: flex; flex-direction: column; }
.label-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; } .label-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; }
@@ -20,19 +21,17 @@
textarea { resize: vertical; min-height: 60px; } textarea { resize: vertical; min-height: 60px; }
.btn-now { background: #4a5568; color: #fff; padding: 4px 8px; font-size: 0.72rem; border-radius: 4px; border: none; cursor: pointer; } .btn-now { background: #4a5568; color: #fff; padding: 4px 8px; font-size: 0.72rem; border-radius: 4px; border: none; cursor: pointer; }
.btn-now:hover { background: #2d3748; } .btn-now:hover { background: #2d3748; }
.conditions-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 8px; font-size: 0.82rem; margin-top: 6px; }
.actions { display: flex; gap: 10px; margin: 18px 0; } .actions { display: flex; gap: 10px; margin: 18px 0; }
button.main-btn { flex: 1; padding: 12px; font-size: 0.95rem; font-weight: bold; border: none; border-radius: 6px; cursor: pointer; } button.main-btn { flex: 1; padding: 12px; font-size: 0.95rem; font-weight: bold; border: none; border-radius: 6px; cursor: pointer; color: #fff; }
.btn-generate { background: #0277bd; color: #fff; } .btn-save { background: #5c6bc0; }
.btn-generate:hover { background: #014f7c; }
.btn-save { background: #5c6bc0; color: #fff; }
.btn-save:hover { background: #3f51b5; } .btn-save:hover { background: #3f51b5; }
.btn-copy { background: #2e7d32; color: #fff; } .btn-copy { background: #2e7d32; }
.btn-copy:hover { background: #1b5e20; } .btn-copy:hover { background: #1b5e20; }
.btn-clear { background: #c62828; color: #fff; flex: 0.35; } .btn-clear { background: #c62828; flex: 0.35; }
pre { background: #181c20; color: #39ff14; padding: 14px; border-radius: 6px; overflow-x: auto; font-family: "Courier New", Courier, monospace; font-size: 0.82rem; line-height: 1.32; min-height: 150px; } .btn-clear:hover { background: #8e0000; }
pre { background: #181c20; color: #39ff14; padding: 14px; border-radius: 6px; overflow-x: auto; font-family: "Courier New", Courier, monospace; font-size: 0.82rem; line-height: 1.32; min-height: 140px; }
.nav-bar { margin-bottom: 12px; font-size: 0.85rem; } .nav-bar { margin-bottom: 12px; font-size: 0.85rem; }
.nav-bar a { color: #0277bd; text-decoration: none; font-weight: bold; } .nav-bar a { text-decoration: none; font-weight: bold; }
/* Modal Save Dialog Window */ /* Modal Save Dialog Window */
.save-modal-backdrop { .save-modal-backdrop {
@@ -58,6 +57,12 @@
.save-modal-btn-cancel { background: #e2e8f0; color: #334155; } .save-modal-btn-cancel { background: #e2e8f0; color: #334155; }
.save-modal-btn-cancel:hover { background: #cbd5e1; } .save-modal-btn-cancel:hover { background: #cbd5e1; }
.header { background: #0277bd; }
legend { color: #0277bd; }
.nav-bar a { color: #0277bd; }
.btn-generate { background: #0277bd; }
.btn-generate:hover { background: #014f7c; }
.conditions-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 8px; font-size: 0.82rem; margin-top: 6px; }
</style> </style>
</head> </head>
<body> <body>
@@ -211,19 +216,22 @@
<pre id="outputPre">Fill in the fields above and click "Generate Packet ASCII"...</pre> <pre id="outputPre">Fill in the fields above and click "Generate Packet ASCII"...</pre>
</div> </div>
<!-- Custom File Name Modal Window -->
<script> <script>
function setUTC() { function setUTC() {
const now = new Date(); var now = new Date();
const y = now.getUTCFullYear(); var y = now.getUTCFullYear();
const m = String(now.getUTCMonth()+1).padStart(2,'0'); var m = String(now.getUTCMonth()+1).padStart(2,'0');
const d = String(now.getUTCDate()).padStart(2,'0'); var d = String(now.getUTCDate()).padStart(2,'0');
const hh = String(now.getUTCHours()).padStart(2,'0'); var hh = String(now.getUTCHours()).padStart(2,'0');
const mm = String(now.getUTCMinutes()).padStart(2,'0'); var mm = String(now.getUTCMinutes()).padStart(2,'0');
document.getElementById('dtLocal').value = y + '-' + m + '-' + d + ' ' + hh + mm + 'Z'; document.getElementById('dtLocal').value = y + '-' + m + '-' + d + ' ' + hh + mm + 'Z';
} }
function clean(id) { function clean(id) {
const el = document.getElementById(id); var el = document.getElementById(id);
return el ? (el.value || '').trim() : ''; return el ? (el.value || '').trim() : '';
} }
@@ -234,10 +242,10 @@
function wrapText(str, width) { function wrapText(str, width) {
if (!str) return []; if (!str) return [];
let lines = []; var lines = [];
str.split('\n').forEach(function(paragraph) { str.split('\n').forEach(function(paragraph) {
let words = paragraph.split(' '); var words = paragraph.split(' ');
let cur = ''; var cur = '';
words.forEach(function(w) { words.forEach(function(w) {
if ((cur + (cur ? ' ' : '') + w).length <= width) { if ((cur + (cur ? ' ' : '') + w).length <= width) {
cur += (cur ? ' ' : '') + w; cur += (cur ? ' ' : '') + w;
@@ -253,70 +261,70 @@
function generateASCII() { function generateASCII() {
try { try {
const div = '='.repeat(78); var div = '='.repeat(78);
const sub = '-'.repeat(78); var sub = '-'.repeat(78);
const call = clean('call').toUpperCase() || 'N0CALL'; var call = clean('call').toUpperCase() || 'N0CALL';
const name = clean('repName').toUpperCase() || 'N/A'; var name = clean('repName').toUpperCase() || 'N/A';
const dt = clean('dtLocal') || 'N/A'; var dt = clean('dtLocal') || 'N/A';
const loc = clean('locName').toUpperCase() || 'N/A'; var loc = clean('locName').toUpperCase() || 'N/A';
const city = clean('city').toUpperCase(); var city = clean('city').toUpperCase();
const st = clean('state').toUpperCase(); var st = clean('state').toUpperCase();
const county = clean('county').toUpperCase(); var county = clean('county').toUpperCase();
const lat = clean('lat') || 'N/A'; var lat = clean('lat') || 'N/A';
const lon = clean('lon') || 'N/A'; var lon = clean('lon') || 'N/A';
const mgrs = clean('mgrs').toUpperCase() || 'N/A'; var mgrs = clean('mgrs').toUpperCase() || 'N/A';
const grid = clean('grid').toUpperCase() || 'N/A'; var grid = clean('grid').toUpperCase() || 'N/A';
const unit = clean('unitChoice'); var unit = clean('unitChoice');
const sky = clean('skyCover'); var sky = clean('skyCover');
let conds = []; var conds = [];
document.querySelectorAll('.conditions-grid input[type="checkbox"]').forEach(function(cb) { document.querySelectorAll('.conditions-grid input[type="checkbox"]').forEach(function(cb) {
if (cb.checked) conds.push(cb.value); if (cb.checked) conds.push(cb.value);
}); });
const condStr = conds.length ? conds.join(', ') : 'NONE'; var condStr = conds.length ? conds.join(', ') : 'NONE';
const temp = clean('temp') || 'N/A'; var temp = clean('temp') || 'N/A';
const hum = clean('humidity') || 'N/A'; var hum = clean('humidity') || 'N/A';
const dew = clean('dewPoint') || 'N/A'; var dew = clean('dewPoint') || 'N/A';
const bar = clean('barometer') || 'N/A'; var bar = clean('barometer') || 'N/A';
const trend = clean('barTrend'); var trend = clean('barTrend');
const wspd = clean('windSpeed') || '0'; var wspd = clean('windSpeed') || '0';
const wdir = clean('windDir'); var wdir = clean('windDir');
const gust = clean('windGust') || '0'; var gust = clean('windGust') || '0';
const maxg = clean('maxGust') || '0'; var maxg = clean('maxGust') || '0';
const r1 = clean('rain1hr') || '0'; var r1 = clean('rain1hr') || '0';
const rtot = clean('rainTotal') || '0'; var rtot = clean('rainTotal') || '0';
const s1 = clean('snow1hr') || '0'; var s1 = clean('snow1hr') || '0';
const stot = clean('snowTotal') || '0'; var stot = clean('snowTotal') || '0';
const nws = clean('nwsLevel'); var nws = clean('nwsLevel');
const nwsDesc = clean('nwsAlertDesc').toUpperCase(); var nwsDesc = clean('nwsAlertDesc').toUpperCase();
const cmt = clean('comments').toUpperCase() || 'NONE'; var cmt = clean('comments').toUpperCase() || 'NONE';
let wrappedCmt = wrapText(cmt, 72).map(function(l){ return ' ' + l; }).join('\n'); var wrappedCmt = wrapText(cmt, 72).map(function(l){ return ' ' + l; }).join('\n');
const out = div + '\n' + var out = div + '\n' +
'CURRENT LOCAL SURFACE WEATHER OBSERVATION (PACKET ASCII)\n' + 'CURRENT LOCAL SURFACE WEATHER OBSERVATION (PACKET ASCII)\n' +
div + '\n' + div + '\n' +
'CALLSIGN : ' + pad(call, 14) + ' OBSERVER: ' + pad(name, 22) + ' DATE/TIME: ' + dt + '\n' + pad('CALLSIGN', 16) + ' : ' + pad(call, 14) + ' OBSERVER: ' + pad(name, 22) + ' DATE/TIME: ' + dt + '\n' +
'LOCATION : ' + loc + '\n' + pad('LOCATION', 16) + ' : ' + loc + '\n' +
'AREA : ' + (city ? city + ', ' : '') + st + (county ? ' (' + county + ' CO)' : '') + '\n' + pad('AREA', 16) + ' : ' + (city ? city + ', ' : '') + st + (county ? ' (' + county + ' CO)' : '') + '\n' +
'COORDS : LAT: ' + pad(lat, 10) + ' LON: ' + pad(lon, 10) + ' MGRS: ' + pad(mgrs, 14) + ' GRID: ' + grid + '\n' + pad('COORDS', 16) + ' : LAT: ' + pad(lat, 10) + ' LON: ' + pad(lon, 10) + ' MGRS: ' + pad(mgrs, 14) + ' GRID: ' + grid + '\n' +
sub + '\n' + sub + '\n' +
'UNITS: ' + unit + ' | SKY COVER: ' + sky + '\n' + pad('UNITS', 16) + ' : ' + unit + ' | SKY COVER: ' + sky + '\n' +
'CURRENT PHENOMENA: ' + condStr + '\n' + pad('PHENOMENA', 16) + ' : ' + condStr + '\n' +
sub + '\n' + sub + '\n' +
'TEMP : ' + pad(temp, 8) + ' HUMIDITY: ' + pad(hum + '%', 8) + ' DEW POINT: ' + dew + '\n' + pad('TEMP', 16) + ' : ' + pad(temp, 8) + ' HUMIDITY: ' + pad(hum + '%', 8) + ' DEW POINT: ' + dew + '\n' +
'PRESSURE: ' + pad(bar, 8) + ' 3-HR TREND: ' + trend + '\n' + pad('PRESSURE', 16) + ' : ' + pad(bar, 8) + ' 3-HR TREND: ' + trend + '\n' +
'WIND : ' + wspd + ' from ' + wdir + ' (GUST: ' + gust + ' | PEAK: ' + maxg + ')\n' + pad('WIND', 16) + ' : ' + wspd + ' from ' + wdir + ' (GUST: ' + gust + ' | PEAK: ' + maxg + ')\n' +
'RAIN : 1-HR: ' + pad(r1, 8) + ' TOTAL: ' + rtot + '\n' + pad('RAIN', 16) + ' : 1-HR: ' + pad(r1, 8) + ' TOTAL: ' + rtot + '\n' +
'SNOW : 1-HR: ' + pad(s1, 8) + ' TOTAL: ' + stot + '\n' + pad('SNOW', 16) + ' : 1-HR: ' + pad(s1, 8) + ' TOTAL: ' + stot + '\n' +
sub + '\n' + sub + '\n' +
'NWS ALERT: ' + nws + (nwsDesc ? ' (' + nwsDesc + ')' : '') + '\n' + pad('NWS ALERT', 16) + ' : ' + nws + (nwsDesc ? ' (' + nwsDesc + ')' : '') + '\n' +
'NOTES / OBSERVATIONS:\n' + wrappedCmt + '\n' + pad('NOTES', 16) + ' :\n' + wrappedCmt + '\n' +
div; div;
document.getElementById('outputPre').textContent = out; document.getElementById('outputPre').textContent = out;
@@ -325,9 +333,8 @@
} }
} }
function copyASCII() { function copyASCII() {
const txt = document.getElementById('outputPre').textContent; var txt = document.getElementById('outputPre').textContent;
if (navigator.clipboard && navigator.clipboard.writeText) { if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(txt).then(function() { navigator.clipboard.writeText(txt).then(function() {
alert("Local Weather report copied to clipboard!"); alert("Local Weather report copied to clipboard!");
@@ -340,8 +347,8 @@
} }
function fallbackCopy() { function fallbackCopy() {
const pre = document.getElementById('outputPre'); var pre = document.getElementById('outputPre');
const range = document.createRange(); var range = document.createRange();
range.selectNode(pre); range.selectNode(pre);
window.getSelection().removeAllRanges(); window.getSelection().removeAllRanges();
window.getSelection().addRange(range); window.getSelection().addRange(range);
@@ -356,15 +363,19 @@
} }
} }
document.getElementById('btnDateNow').addEventListener('click', setUTC); window.addEventListener('DOMContentLoaded', function() {
document.getElementById('btnGen').addEventListener('click', generateASCII); document.getElementById('btnDateNow').addEventListener('click', setUTC);
document.getElementById('btnSaveHtml').addEventListener('click', openSaveModal); document.getElementById('btnGen').addEventListener('click', generateASCII);
document.getElementById('btnCopy').addEventListener('click', copyASCII); document.getElementById('btnCopy').addEventListener('click', copyASCII);
document.getElementById('btnClear').addEventListener('click', clearForm); document.getElementById('btnClear').addEventListener('click', clearForm);
// --- File Name Modal Window & HTML Exporter ---
// File Name Modal Window & HTML Exporter });
// --- File Name Modal Window & HTML Exporter ---
function openSaveModal() { function openSaveModal() {
var now = new Date(); var now = new Date();
var dStr = now.toISOString().slice(0, 10); var dStr = now.toISOString().slice(0, 10);
@@ -392,6 +403,7 @@
} }
closeSaveModal(); closeSaveModal();
// Sync input values to DOM attributes so they save offline
document.querySelectorAll('input[type="text"]').forEach(function(el) { el.setAttribute('value', el.value); }); document.querySelectorAll('input[type="text"]').forEach(function(el) { el.setAttribute('value', el.value); });
document.querySelectorAll('input[type="checkbox"], input[type="radio"]').forEach(function(el) { document.querySelectorAll('input[type="checkbox"], input[type="radio"]').forEach(function(el) {
if (el.checked) el.setAttribute('checked', 'checked'); if (el.checked) el.setAttribute('checked', 'checked');
@@ -424,6 +436,7 @@
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
} }
// Initialize event listeners safely after DOM loads
window.addEventListener('DOMContentLoaded', function() { window.addEventListener('DOMContentLoaded', function() {
var cancelBtn = document.getElementById('saveModalCancelBtn'); var cancelBtn = document.getElementById('saveModalCancelBtn');
var confirmBtn = document.getElementById('saveModalSaveBtn'); var confirmBtn = document.getElementById('saveModalSaveBtn');
@@ -446,17 +459,11 @@
var btnSaveMain = document.getElementById('btnSaveHtml'); var btnSaveMain = document.getElementById('btnSaveHtml');
if (btnSaveMain) { if (btnSaveMain) {
// Clear existing inline handlers just in case
btnSaveMain.onclick = null;
btnSaveMain.addEventListener('click', openSaveModal); btnSaveMain.addEventListener('click', openSaveModal);
} }
}); });
</script> </script>
<!-- Custom File Name Modal Window --> <!-- Custom File Name Modal Window -->
<div id="saveModalBackdrop" class="save-modal-backdrop"> <div id="saveModalBackdrop" class="save-modal-backdrop">
<div class="save-modal-window"> <div class="save-modal-window">
+88 -81
View File
@@ -4,34 +4,34 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Severe Weather Report (Packet Radio)</title> <title>Severe Weather Report (Packet Radio)</title>
<style> <style>
* { box-sizing: border-box; } * { box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace; background: #fdfaf6; color: #222; padding: 20px; margin: 0; } body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace; background: #eef2f7; color: #222; padding: 20px; margin: 0; }
.container { max-width: 940px; margin: 0 auto; background: #fff; padding: 24px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .container { max-width: 960px; margin: 0 auto; background: #fff; padding: 24px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
.header { background: #d84315; color: white; padding: 12px; border-radius: 6px; text-align: center; margin-bottom: 18px; } .header { padding: 12px; border-radius: 6px; text-align: center; margin-bottom: 18px; color: white; }
.header h2 { margin: 0; font-size: 1.3rem; letter-spacing: 1px; } .header h2 { margin: 0; font-size: 1.3rem; letter-spacing: 1px; }
.header p { margin: 4px 0 0 0; font-size: 0.85rem; opacity: 0.9; } .header p { margin: 4px 0 0 0; font-size: 0.85rem; opacity: 0.9; }
fieldset { border: 1px solid #ffccbc; border-radius: 6px; padding: 14px; margin-bottom: 14px; background: #fffbf9; } fieldset { border: 1px solid #cbd5e1; border-radius: 6px; padding: 14px; margin-bottom: 14px; background: #fafcff; }
legend { font-weight: bold; padding: 0 8px; font-size: 0.85rem; color: #bf360c; } legend { font-weight: bold; padding: 0 8px; font-size: 0.85rem; }
.row { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 10px; } .row { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 10px; }
.col { flex: 1; min-width: 140px; display: flex; flex-direction: column; } .col { flex: 1; min-width: 140px; display: flex; flex-direction: column; }
.label-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; } .label-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; }
label { font-size: 0.8rem; font-weight: 600; color: #333; } label { font-size: 0.8rem; font-weight: 600; color: #333; }
input[type="text"], select, textarea { padding: 7px 8px; font-size: 0.88rem; border: 1px solid #999; border-radius: 4px; font-family: inherit; width: 100%; } input[type="text"], select, textarea { padding: 7px 8px; font-size: 0.88rem; border: 1px solid #999; border-radius: 4px; font-family: inherit; width: 100%; }
textarea { resize: vertical; min-height: 70px; } textarea { resize: vertical; min-height: 60px; }
.btn-now { background: #4a5568; color: #fff; padding: 4px 8px; font-size: 0.72rem; border-radius: 4px; border: none; cursor: pointer; } .btn-now { background: #4a5568; color: #fff; padding: 4px 8px; font-size: 0.72rem; border-radius: 4px; border: none; cursor: pointer; }
.btn-now:hover { background: #2d3748; } .btn-now:hover { background: #2d3748; }
.actions { display: flex; gap: 10px; margin: 18px 0; } .actions { display: flex; gap: 10px; margin: 18px 0; }
button.main-btn { flex: 1; padding: 12px; font-size: 0.95rem; font-weight: bold; border: none; border-radius: 6px; cursor: pointer; } button.main-btn { flex: 1; padding: 12px; font-size: 0.95rem; font-weight: bold; border: none; border-radius: 6px; cursor: pointer; color: #fff; }
.btn-generate { background: #d84315; color: #fff; } .btn-save { background: #5c6bc0; }
.btn-generate:hover { background: #bf360c; }
.btn-save { background: #5c6bc0; color: #fff; }
.btn-save:hover { background: #3f51b5; } .btn-save:hover { background: #3f51b5; }
.btn-copy { background: #2e7d32; color: #fff; } .btn-copy { background: #2e7d32; }
.btn-copy:hover { background: #1b5e20; } .btn-copy:hover { background: #1b5e20; }
.btn-clear { background: #c62828; color: #fff; flex: 0.35; } .btn-clear { background: #c62828; flex: 0.35; }
pre { background: #181c20; color: #39ff14; padding: 14px; border-radius: 6px; overflow-x: auto; font-family: "Courier New", Courier, monospace; font-size: 0.82rem; line-height: 1.32; min-height: 150px; } .btn-clear:hover { background: #8e0000; }
pre { background: #181c20; color: #39ff14; padding: 14px; border-radius: 6px; overflow-x: auto; font-family: "Courier New", Courier, monospace; font-size: 0.82rem; line-height: 1.32; min-height: 140px; }
.nav-bar { margin-bottom: 12px; font-size: 0.85rem; } .nav-bar { margin-bottom: 12px; font-size: 0.85rem; }
.nav-bar a { color: #d84315; text-decoration: none; font-weight: bold; } .nav-bar a { text-decoration: none; font-weight: bold; }
/* Modal Save Dialog Window */ /* Modal Save Dialog Window */
.save-modal-backdrop { .save-modal-backdrop {
@@ -57,6 +57,11 @@
.save-modal-btn-cancel { background: #e2e8f0; color: #334155; } .save-modal-btn-cancel { background: #e2e8f0; color: #334155; }
.save-modal-btn-cancel:hover { background: #cbd5e1; } .save-modal-btn-cancel:hover { background: #cbd5e1; }
.header { background: #d84315; }
legend { color: #bf360c; }
.nav-bar a { color: #d84315; }
.btn-generate { background: #d84315; }
.btn-generate:hover { background: #bf360c; }
</style> </style>
</head> </head>
<body> <body>
@@ -207,19 +212,22 @@
<pre id="outputPre">Fill in the fields above and click "Generate Packet ASCII"...</pre> <pre id="outputPre">Fill in the fields above and click "Generate Packet ASCII"...</pre>
</div> </div>
<!-- Custom File Name Modal Window -->
<script> <script>
function setUTC() { function setUTC() {
const now = new Date(); var now = new Date();
const y = now.getUTCFullYear(); var y = now.getUTCFullYear();
const m = String(now.getUTCMonth()+1).padStart(2,'0'); var m = String(now.getUTCMonth()+1).padStart(2,'0');
const d = String(now.getUTCDate()).padStart(2,'0'); var d = String(now.getUTCDate()).padStart(2,'0');
const hh = String(now.getUTCHours()).padStart(2,'0'); var hh = String(now.getUTCHours()).padStart(2,'0');
const mm = String(now.getUTCMinutes()).padStart(2,'0'); var mm = String(now.getUTCMinutes()).padStart(2,'0');
document.getElementById('dtEvent').value = y + '-' + m + '-' + d + ' ' + hh + mm + 'Z'; document.getElementById('dtEvent').value = y + '-' + m + '-' + d + ' ' + hh + mm + 'Z';
} }
function clean(id) { function clean(id) {
const el = document.getElementById(id); var el = document.getElementById(id);
return el ? (el.value || '').trim() : ''; return el ? (el.value || '').trim() : '';
} }
@@ -230,10 +238,10 @@
function wrapText(str, width) { function wrapText(str, width) {
if (!str) return []; if (!str) return [];
let lines = []; var lines = [];
str.split('\n').forEach(function(paragraph) { str.split('\n').forEach(function(paragraph) {
let words = paragraph.split(' '); var words = paragraph.split(' ');
let cur = ''; var cur = '';
words.forEach(function(w) { words.forEach(function(w) {
if ((cur + (cur ? ' ' : '') + w).length <= width) { if ((cur + (cur ? ' ' : '') + w).length <= width) {
cur += (cur ? ' ' : '') + w; cur += (cur ? ' ' : '') + w;
@@ -249,58 +257,58 @@
function generateASCII() { function generateASCII() {
try { try {
const div = '='.repeat(78); var div = '='.repeat(78);
const sub = '-'.repeat(78); var sub = '-'.repeat(78);
const call = clean('call').toUpperCase() || 'N0CALL'; var call = clean('call').toUpperCase() || 'N0CALL';
const dt = clean('dtEvent') || 'N/A'; var dt = clean('dtEvent') || 'N/A';
const ver = clean('repVersion').toUpperCase(); var ver = clean('repVersion').toUpperCase();
const name = clean('repName').toUpperCase() || 'N/A'; var name = clean('repName').toUpperCase() || 'N/A';
const phone = clean('phone') || 'N/A'; var phone = clean('phone') || 'N/A';
const email = clean('email').toUpperCase() || 'N/A'; var email = clean('email').toUpperCase() || 'N/A';
const city = clean('city').toUpperCase(); var city = clean('city').toUpperCase();
const reg = clean('region').toUpperCase(); var reg = clean('region').toUpperCase();
const county = clean('county').toUpperCase(); var county = clean('county').toUpperCase();
const other = clean('otherGeo').toUpperCase(); var other = clean('otherGeo').toUpperCase();
const lat = clean('lat') || 'N/A'; var lat = clean('lat') || 'N/A';
const lon = clean('lon') || 'N/A'; var lon = clean('lon') || 'N/A';
const mgrs = clean('mgrs').toUpperCase() || 'N/A'; var mgrs = clean('mgrs').toUpperCase() || 'N/A';
const grid = clean('grid').toUpperCase() || 'N/A'; var grid = clean('grid').toUpperCase() || 'N/A';
const units = clean('unitChoice'); var units = clean('unitChoice');
const flood = clean('flood'); var flood = clean('flood');
const hail = clean('hail'); var hail = clean('hail');
const wspd = clean('windSpeed') || 'NONE'; var wspd = clean('windSpeed') || 'NONE';
const tor = clean('tornado'); var tor = clean('tornado');
const wdmg = clean('windDmg'); var wdmg = clean('windDmg');
const ptype = clean('precipType'); var ptype = clean('precipType');
const snow = clean('snowDepth') || 'NONE'; var snow = clean('snowDepth') || 'NONE';
const ice = clean('iceAcc') || 'NONE'; var ice = clean('iceAcc') || 'NONE';
const rain = clean('heavyRain') || 'NONE'; var rain = clean('heavyRain') || 'NONE';
const rperiod = clean('rainPeriod') || 'N/A'; var rperiod = clean('rainPeriod') || 'N/A';
const cmt = clean('comments').toUpperCase() || 'NONE REPORTED'; var cmt = clean('comments').toUpperCase() || 'NONE REPORTED';
let wrappedCmt = wrapText(cmt, 72).map(function(l){ return ' ' + l; }).join('\n'); var wrappedCmt = wrapText(cmt, 72).map(function(l){ return ' ' + l; }).join('\n');
const out = div + '\n' + var out = div + '\n' +
'SEVERE WEATHER SPOTTER REPORT (PACKET ASCII)\n' + 'SEVERE WEATHER SPOTTER REPORT (PACKET ASCII)\n' +
div + '\n' + div + '\n' +
'1. SENDER: ' + pad(call, 14) + ' 2. DATE/TIME: ' + pad(dt, 20) + ' 3. VERSION: ' + ver + '\n' + pad('SENDER', 16) + ' : ' + pad(call, 14) + ' DATE/TIME: ' + pad(dt, 20) + ' VERSION: ' + ver + '\n' +
'4. OBSERVER: ' + pad(name, 22) + ' PHONE: ' + pad(phone, 16) + ' EMAIL: ' + email + '\n' + pad('OBSERVER', 16) + ' : ' + pad(name, 22) + ' PHONE: ' + pad(phone, 16) + ' EMAIL: ' + email + '\n' +
sub + '\n' + sub + '\n' +
'EVENT LOCATION:\n' + 'EVENT LOCATION:\n' +
'AREA : ' + (city ? city + ', ' : '') + reg + (county ? ' (' + county + ' CO)' : '') + (other ? ' [' + other + ']' : '') + '\n' + pad('AREA', 16) + ' : ' + (city ? city + ', ' : '') + reg + (county ? ' (' + county + ' CO)' : '') + (other ? ' [' + other + ']' : '') + '\n' +
'COORDS: LAT: ' + pad(lat, 12) + ' LON: ' + pad(lon, 12) + ' MGRS: ' + pad(mgrs, 16) + ' GRID: ' + grid + '\n' + pad('COORDS', 16) + ' : LAT: ' + pad(lat, 12) + ' LON: ' + pad(lon, 12) + ' MGRS: ' + pad(mgrs, 16) + ' GRID: ' + grid + '\n' +
sub + '\n' + sub + '\n' +
'SEVERE PHENOMENA (' + units + '):\n' + 'SEVERE PHENOMENA (' + units + '):\n' +
'15. FLOODING : ' + flood + '\n' + pad('15. FLOODING', 21) + ' : ' + flood + '\n' +
'16. HAIL SIZE : ' + hail + '\n' + pad('16. HAIL SIZE', 21) + ' : ' + hail + '\n' +
'17. HIGH WIND SPEED : ' + wspd + '\n' + pad('17. WIND SPEED', 21) + ' : ' + wspd + '\n' +
'18. TORNADO / FUNNEL : ' + tor + '\n' + pad('18. TORNADO/FUNNEL', 21) + ' : ' + tor + '\n' +
'19. WIND DAMAGE : ' + wdmg + '\n' + pad('19. WIND DAMAGE', 21) + ' : ' + wdmg + '\n' +
'20. WINTER PRECIP : ' + ptype + ' (SNOW: ' + snow + ' | ICE ACC: ' + ice + ')\n' + pad('20. WINTER PRECIP', 21) + ' : ' + ptype + ' (SNOW: ' + snow + ' | ICE ACC: ' + ice + ')\n' +
'23. HEAVY RAIN : ' + rain + ' (' + rperiod + ')\n' + pad('23. HEAVY RAIN', 21) + ' : ' + rain + ' (' + rperiod + ')\n' +
sub + '\n' + sub + '\n' +
'25. ADDITIONAL COMMENTS & DAMAGE DESCRIPTIONS:\n' + '25. ADDITIONAL COMMENTS & DAMAGE DESCRIPTIONS:\n' +
wrappedCmt + '\n' + wrappedCmt + '\n' +
@@ -312,9 +320,8 @@
} }
} }
function copyASCII() { function copyASCII() {
const txt = document.getElementById('outputPre').textContent; var txt = document.getElementById('outputPre').textContent;
if (navigator.clipboard && navigator.clipboard.writeText) { if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(txt).then(function() { navigator.clipboard.writeText(txt).then(function() {
alert("Severe Weather report copied to clipboard!"); alert("Severe Weather report copied to clipboard!");
@@ -327,8 +334,8 @@
} }
function fallbackCopy() { function fallbackCopy() {
const pre = document.getElementById('outputPre'); var pre = document.getElementById('outputPre');
const range = document.createRange(); var range = document.createRange();
range.selectNode(pre); range.selectNode(pre);
window.getSelection().removeAllRanges(); window.getSelection().removeAllRanges();
window.getSelection().addRange(range); window.getSelection().addRange(range);
@@ -343,15 +350,19 @@
} }
} }
document.getElementById('btnDateNow').addEventListener('click', setUTC); window.addEventListener('DOMContentLoaded', function() {
document.getElementById('btnGen').addEventListener('click', generateASCII); document.getElementById('btnDateNow').addEventListener('click', setUTC);
document.getElementById('btnSaveHtml').addEventListener('click', openSaveModal); document.getElementById('btnGen').addEventListener('click', generateASCII);
document.getElementById('btnCopy').addEventListener('click', copyASCII); document.getElementById('btnCopy').addEventListener('click', copyASCII);
document.getElementById('btnClear').addEventListener('click', clearForm); document.getElementById('btnClear').addEventListener('click', clearForm);
// --- File Name Modal Window & HTML Exporter ---
// File Name Modal Window & HTML Exporter });
// --- File Name Modal Window & HTML Exporter ---
function openSaveModal() { function openSaveModal() {
var now = new Date(); var now = new Date();
var dStr = now.toISOString().slice(0, 10); var dStr = now.toISOString().slice(0, 10);
@@ -379,6 +390,7 @@
} }
closeSaveModal(); closeSaveModal();
// Sync input values to DOM attributes so they save offline
document.querySelectorAll('input[type="text"]').forEach(function(el) { el.setAttribute('value', el.value); }); document.querySelectorAll('input[type="text"]').forEach(function(el) { el.setAttribute('value', el.value); });
document.querySelectorAll('input[type="checkbox"], input[type="radio"]').forEach(function(el) { document.querySelectorAll('input[type="checkbox"], input[type="radio"]').forEach(function(el) {
if (el.checked) el.setAttribute('checked', 'checked'); if (el.checked) el.setAttribute('checked', 'checked');
@@ -411,6 +423,7 @@
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
} }
// Initialize event listeners safely after DOM loads
window.addEventListener('DOMContentLoaded', function() { window.addEventListener('DOMContentLoaded', function() {
var cancelBtn = document.getElementById('saveModalCancelBtn'); var cancelBtn = document.getElementById('saveModalCancelBtn');
var confirmBtn = document.getElementById('saveModalSaveBtn'); var confirmBtn = document.getElementById('saveModalSaveBtn');
@@ -433,17 +446,11 @@
var btnSaveMain = document.getElementById('btnSaveHtml'); var btnSaveMain = document.getElementById('btnSaveHtml');
if (btnSaveMain) { if (btnSaveMain) {
// Clear existing inline handlers just in case
btnSaveMain.onclick = null;
btnSaveMain.addEventListener('click', openSaveModal); btnSaveMain.addEventListener('click', openSaveModal);
} }
}); });
</script> </script>
<!-- Custom File Name Modal Window --> <!-- Custom File Name Modal Window -->
<div id="saveModalBackdrop" class="save-modal-backdrop"> <div id="saveModalBackdrop" class="save-modal-backdrop">
<div class="save-modal-window"> <div class="save-modal-window">
+73 -66
View File
@@ -6,38 +6,32 @@
<style> <style>
* { box-sizing: border-box; } * { box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace; background: #eef1f5; color: #222; padding: 20px; margin: 0; } body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace; background: #eef2f7; color: #222; padding: 20px; margin: 0; }
.container { max-width: 960px; margin: 0 auto; background: #fff; padding: 24px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .container { max-width: 960px; margin: 0 auto; background: #fff; padding: 24px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
.header { background: #b71c1c; color: white; padding: 12px; border-radius: 6px; text-align: center; margin-bottom: 18px; } .header { padding: 12px; border-radius: 6px; text-align: center; margin-bottom: 18px; color: white; }
.header h2 { margin: 0; font-size: 1.3rem; letter-spacing: 1px; } .header h2 { margin: 0; font-size: 1.3rem; letter-spacing: 1px; }
.header p { margin: 4px 0 0 0; font-size: 0.85rem; opacity: 0.9; } .header p { margin: 4px 0 0 0; font-size: 0.85rem; opacity: 0.9; }
fieldset { border: 1px solid #ef9a9a; border-radius: 6px; padding: 14px; margin-bottom: 14px; background: #fffbfb; } fieldset { border: 1px solid #cbd5e1; border-radius: 6px; padding: 14px; margin-bottom: 14px; background: #fafcff; }
legend { font-weight: bold; padding: 0 8px; font-size: 0.85rem; color: #b71c1c; } legend { font-weight: bold; padding: 0 8px; font-size: 0.85rem; }
.row { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 10px; } .row { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 10px; }
.col { flex: 1; min-width: 140px; display: flex; flex-direction: column; } .col { flex: 1; min-width: 140px; display: flex; flex-direction: column; }
.label-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; } .label-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; }
label { font-size: 0.8rem; font-weight: 600; color: #333; } label { font-size: 0.8rem; font-weight: 600; color: #333; }
input[type="text"], textarea, select { padding: 7px 8px; font-size: 0.88rem; border: 1px solid #999; border-radius: 4px; font-family: inherit; width: 100%; } input[type="text"], select, textarea { padding: 7px 8px; font-size: 0.88rem; border: 1px solid #999; border-radius: 4px; font-family: inherit; width: 100%; }
textarea { resize: vertical; min-height: 50px; } textarea { resize: vertical; min-height: 60px; }
.btn-now { background: #4a5568; color: #fff; padding: 4px 8px; font-size: 0.72rem; border-radius: 4px; border: none; cursor: pointer; } .btn-now { background: #4a5568; color: #fff; padding: 4px 8px; font-size: 0.72rem; border-radius: 4px; border: none; cursor: pointer; }
.btn-now:hover { background: #2d3748; } .btn-now:hover { background: #2d3748; }
.lifeline-block { background: #fafafa; border: 1px solid #e0e0e0; border-radius: 5px; padding: 10px; margin-bottom: 8px; }
.lifeline-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; }
.lifeline-header span { font-size: 0.85rem; font-weight: bold; color: #333; }
.lifeline-header select { width: 170px; font-weight: bold; }
.actions { display: flex; gap: 10px; margin: 18px 0; } .actions { display: flex; gap: 10px; margin: 18px 0; }
button.main-btn { flex: 1; padding: 12px; font-size: 0.95rem; font-weight: bold; border: none; border-radius: 6px; cursor: pointer; } button.main-btn { flex: 1; padding: 12px; font-size: 0.95rem; font-weight: bold; border: none; border-radius: 6px; cursor: pointer; color: #fff; }
.btn-generate { background: #b71c1c; color: #fff; } .btn-save { background: #5c6bc0; }
.btn-generate:hover { background: #7f0000; }
.btn-save { background: #5c6bc0; color: #fff; }
.btn-save:hover { background: #3f51b5; } .btn-save:hover { background: #3f51b5; }
.btn-copy { background: #2e7d32; color: #fff; } .btn-copy { background: #2e7d32; }
.btn-copy:hover { background: #1b5e20; } .btn-copy:hover { background: #1b5e20; }
.btn-clear { background: #555; color: #fff; flex: 0.35; } .btn-clear { background: #c62828; flex: 0.35; }
.btn-clear:hover { background: #333; } .btn-clear:hover { background: #8e0000; }
pre { background: #181c20; color: #39ff14; padding: 14px; border-radius: 6px; overflow-x: auto; font-family: "Courier New", Courier, monospace; font-size: 0.82rem; line-height: 1.32; min-height: 150px; } pre { background: #181c20; color: #39ff14; padding: 14px; border-radius: 6px; overflow-x: auto; font-family: "Courier New", Courier, monospace; font-size: 0.82rem; line-height: 1.32; min-height: 140px; }
.nav-bar { margin-bottom: 12px; font-size: 0.85rem; } .nav-bar { margin-bottom: 12px; font-size: 0.85rem; }
.nav-bar a { color: #b71c1c; text-decoration: none; font-weight: bold; } .nav-bar a { text-decoration: none; font-weight: bold; }
/* Modal Save Dialog Window */ /* Modal Save Dialog Window */
.save-modal-backdrop { .save-modal-backdrop {
@@ -63,7 +57,6 @@
.save-modal-btn-cancel { background: #e2e8f0; color: #334155; } .save-modal-btn-cancel { background: #e2e8f0; color: #334155; }
.save-modal-btn-cancel:hover { background: #cbd5e1; } .save-modal-btn-cancel:hover { background: #cbd5e1; }
.header { background: #0056b3; } .header { background: #0056b3; }
legend { color: #0056b3; } legend { color: #0056b3; }
.nav-bar a { color: #0056b3; } .nav-bar a { color: #0056b3; }
@@ -193,21 +186,9 @@
<label style="font-weight:bold;">Formatted ASCII for Packet / BBS Transmission:</label> <label style="font-weight:bold;">Formatted ASCII for Packet / BBS Transmission:</label>
<pre id="outputPre">Fill in the fields above and click "Generate Packet ASCII"...</pre> <pre id="outputPre">Fill in the fields above and click "Generate Packet ASCII"...</pre>
</div> </div>
<!-- Custom File Name Modal Window -->
<div id="saveModalBackdrop" class="save-modal-backdrop">
<div class="save-modal-window">
<h3>Save Form as HTML</h3>
<p>Enter a name for the saved offline HTML file:</p>
<input type="text" id="saveModalFileName" spellcheck="false">
<div class="save-modal-actions">
<button type="button" class="save-modal-btn save-modal-btn-cancel" id="saveModalCancelBtn">Cancel</button>
<button type="button" class="save-modal-btn save-modal-btn-confirm" id="saveModalSaveBtn">Save File</button>
</div>
</div>
</div>
<script> <script>
@@ -223,7 +204,8 @@
var day = String(now.getDate()).padStart(2, '0'); var day = String(now.getDate()).padStart(2, '0');
var hours = String(now.getHours()).padStart(2, '0'); var hours = String(now.getHours()).padStart(2, '0');
var minutes = String(now.getMinutes()).padStart(2, '0'); var minutes = String(now.getMinutes()).padStart(2, '0');
document.getElementById(targetId).value = (year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ' L').toUpperCase(); var el = document.getElementById(targetId);
if (el) el.value = (year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ' L').toUpperCase();
} }
function generateReport() { function generateReport() {
@@ -237,7 +219,7 @@
var toAddr = document.getElementById('nws-office').value.toUpperCase() || "[email protected]"; var toAddr = document.getElementById('nws-office').value.toUpperCase() || "[email protected]";
var call = document.getElementById('callsign').value.toUpperCase() || "NOCALL"; var call = document.getElementById('callsign').value.toUpperCase() || "NOCALL";
var spotter = document.getElementById('spotter-id').value.toUpperCase() || "N/A"; var spotter = document.getElementById('spotter-id').value.toUpperCase() || "N/A";
var event = eventSelect.value.toUpperCase(); var eventVal = eventSelect.value.toUpperCase();
var loc = document.getElementById('location').value.toUpperCase() || "N/A"; var loc = document.getElementById('location').value.toUpperCase() || "N/A";
var grid = document.getElementById('grid').value.toUpperCase() || "N/A"; var grid = document.getElementById('grid').value.toUpperCase() || "N/A";
var time = document.getElementById('time').value.toUpperCase() || "N/A"; var time = document.getElementById('time').value.toUpperCase() || "N/A";
@@ -247,8 +229,7 @@
var toType = document.getElementById('to-type').value.toUpperCase() || "N/A"; var toType = document.getElementById('to-type').value.toUpperCase() || "N/A";
var changeTime = document.getElementById('change-time').value.toUpperCase() || "N/A"; var changeTime = document.getElementById('change-time').value.toUpperCase() || "N/A";
var subject = "WX REPORT - " + event + " - " + loc; var subject = "WX REPORT - " + eventVal + " - " + loc;
var div = "--------------------------------------------------------------------------"; var div = "--------------------------------------------------------------------------";
var body = "*** TPRFN SKYWARN SEVERE WEATHER REPORT ***\n" + var body = "*** TPRFN SKYWARN SEVERE WEATHER REPORT ***\n" +
@@ -258,7 +239,7 @@
div + "\n" + div + "\n" +
pad("SENDER CALL", 16) + " : " + call + "\n" + pad("SENDER CALL", 16) + " : " + call + "\n" +
pad("SPOTTER ID", 16) + " : " + spotter + "\n" + pad("SPOTTER ID", 16) + " : " + spotter + "\n" +
pad("EVENT TYPE", 16) + " : " + event + "\n" + pad("EVENT TYPE", 16) + " : " + eventVal + "\n" +
pad("DATE/TIME", 16) + " : " + time + "\n" + pad("DATE/TIME", 16) + " : " + time + "\n" +
pad("GRID SQUARE", 16) + " : " + grid + "\n" + pad("GRID SQUARE", 16) + " : " + grid + "\n" +
pad("LOCATION", 16) + " : " + loc + "\n"; pad("LOCATION", 16) + " : " + loc + "\n";
@@ -308,7 +289,20 @@
} }
} }
// SAVE AS HTML LOGIC window.addEventListener('DOMContentLoaded', function() {
document.getElementById('btnDateNow').addEventListener('click', function() { setCurrentTime('time'); });
document.getElementById('btnChangeNow').addEventListener('click', function() { setCurrentTime('change-time'); });
document.getElementById('btnGen').addEventListener('click', generateReport);
document.getElementById('btnCopy').addEventListener('click', copyASCII);
document.getElementById('btnClear').addEventListener('click', clearForm);
// --- File Name Modal Window & HTML Exporter ---
});
// --- File Name Modal Window & HTML Exporter ---
function openSaveModal() { function openSaveModal() {
var now = new Date(); var now = new Date();
var dStr = now.toISOString().slice(0, 10); var dStr = now.toISOString().slice(0, 10);
@@ -336,6 +330,7 @@
} }
closeSaveModal(); closeSaveModal();
// Sync input values to DOM attributes so they save offline
document.querySelectorAll('input[type="text"]').forEach(function(el) { el.setAttribute('value', el.value); }); document.querySelectorAll('input[type="text"]').forEach(function(el) { el.setAttribute('value', el.value); });
document.querySelectorAll('input[type="checkbox"], input[type="radio"]').forEach(function(el) { document.querySelectorAll('input[type="checkbox"], input[type="radio"]').forEach(function(el) {
if (el.checked) el.setAttribute('checked', 'checked'); if (el.checked) el.setAttribute('checked', 'checked');
@@ -348,7 +343,8 @@
else opt.removeAttribute('selected'); else opt.removeAttribute('selected');
}); });
}); });
var pre = document.getElementById('outputPre');
var pre = document.getElementById('ascii-report') || document.getElementById('outputPre') || document.getElementById('preview') || document.getElementById('outputArea');
if (pre) pre.textContent = pre.textContent; if (pre) pre.textContent = pre.textContent;
var clonedDoc = document.documentElement.cloneNode(true); var clonedDoc = document.documentElement.cloneNode(true);
@@ -367,35 +363,46 @@
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
} }
var cancelBtn = document.getElementById('saveModalCancelBtn'); // Initialize event listeners safely after DOM loads
var confirmBtn = document.getElementById('saveModalSaveBtn'); window.addEventListener('DOMContentLoaded', function() {
var modalInput = document.getElementById('saveModalFileName'); var cancelBtn = document.getElementById('saveModalCancelBtn');
var modalBackdrop = document.getElementById('saveModalBackdrop'); var confirmBtn = document.getElementById('saveModalSaveBtn');
var modalInput = document.getElementById('saveModalFileName');
var modalBackdrop = document.getElementById('saveModalBackdrop');
if (cancelBtn) cancelBtn.addEventListener('click', closeSaveModal); if (cancelBtn) cancelBtn.addEventListener('click', closeSaveModal);
if (confirmBtn) confirmBtn.addEventListener('click', executeSaveHTML); if (confirmBtn) confirmBtn.addEventListener('click', executeSaveHTML);
if (modalInput) { if (modalInput) {
modalInput.addEventListener('keydown', function(e) { modalInput.addEventListener('keydown', function(e) {
if (e.key === 'Enter') { e.preventDefault(); executeSaveHTML(); } if (e.key === 'Enter') { e.preventDefault(); executeSaveHTML(); }
if (e.key === 'Escape') { e.preventDefault(); closeSaveModal(); } if (e.key === 'Escape') { e.preventDefault(); closeSaveModal(); }
}); });
} }
if (modalBackdrop) { if (modalBackdrop) {
modalBackdrop.addEventListener('click', function(e) { modalBackdrop.addEventListener('click', function(e) {
if (e.target === modalBackdrop) closeSaveModal(); if (e.target === modalBackdrop) closeSaveModal();
}); });
} }
document.getElementById('btnSaveHtml').addEventListener('click', openSaveModal);
// Attach main buttons
document.getElementById('btnDateNow').addEventListener('click', function() { setCurrentTime('time'); });
document.getElementById('btnChangeNow').addEventListener('click', function() { setCurrentTime('change-time'); });
document.getElementById('btnGen').addEventListener('click', generateReport);
document.getElementById('btnCopy').addEventListener('click', copyASCII);
document.getElementById('btnClear').addEventListener('click', clearForm);
var btnSaveMain = document.getElementById('btnSaveHtml');
if (btnSaveMain) {
btnSaveMain.addEventListener('click', openSaveModal);
}
});
</script> </script>
<!-- Custom File Name Modal Window -->
<div id="saveModalBackdrop" class="save-modal-backdrop">
<div class="save-modal-window">
<h3>Save Form as HTML</h3>
<p>Enter a name for the saved offline HTML file:</p>
<input type="text" id="saveModalFileName" spellcheck="false">
<div class="save-modal-actions">
<button type="button" class="save-modal-btn save-modal-btn-cancel" id="saveModalCancelBtn">Cancel</button>
<button type="button" class="save-modal-btn save-modal-btn-confirm" id="saveModalSaveBtn">Save File</button>
</div>
</div>
</div>
</body> </body>
</html> </html>