Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ce6c272aa | ||
|
|
692ee001fd | ||
|
|
b5c84266f5 | ||
|
|
4a7715f0d0 | ||
|
|
14d1370309 | ||
|
|
fab1ec4ad9 | ||
|
|
72a723b4e4 | ||
|
|
f3fbed6f6c | ||
|
|
bbf7262015 | ||
|
|
8898a54389 | ||
|
|
992ec209ee | ||
|
|
c3e33663c2 | ||
|
|
fe5995bc4b | ||
|
|
00a0e3dff3 | ||
|
|
91b7f645e8 | ||
|
|
1136f4938e | ||
|
|
c4dab8c353 | ||
|
|
a7bb741270 | ||
|
|
c0a142b714 | ||
|
|
45558b0467 | ||
|
|
42d2a6c8e0 | ||
|
|
9ae0eb17b2 | ||
|
|
bfa5594a11 | ||
|
|
b4189ae7cd | ||
|
|
494d6eaa8c | ||
|
|
3d8e3753cd |
+19
-57
@@ -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>
|
||||||
@@ -1,433 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>TPRFN / RRI Radiogram Generator</title>
|
|
||||||
<style>
|
|
||||||
:root { --primary: #2e7d32; --secondary: #1976d2; --danger: #c62828; --warning: #f57c00; --dark: #111; --light: #f9f9f9; --info: #546e7a; --success: #388e3c; }
|
|
||||||
html, body { height: auto; margin: 0; padding: 0; background-color: #eceff1; }
|
|
||||||
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; padding: 15px; line-height: 1.6; }
|
|
||||||
.nav-bar { max-width: 1000px; margin: 0 auto 10px auto; font-size: 0.88rem; }
|
|
||||||
.nav-bar a { color: var(--primary); text-decoration: none; font-weight: bold; }
|
|
||||||
.radiogram-container {
|
|
||||||
font-family: 'Courier New', Courier, monospace;
|
|
||||||
width: 98%; max-width: 1000px; margin: 0 auto 20px auto; padding: 35px;
|
|
||||||
border: 2px solid #333; background: var(--light);
|
|
||||||
box-shadow: 0 15px 35px rgba(0,0,0,0.3); border-radius: 12px;
|
|
||||||
box-sizing: border-box; height: auto; overflow: visible;
|
|
||||||
}
|
|
||||||
.header { text-align: center; border-bottom: 3px solid #333; margin-bottom: 25px; padding-bottom: 15px; }
|
|
||||||
.header h2 { margin: 0; color: var(--primary); font-size: clamp(22px, 6vw, 32px); letter-spacing: 1px; }
|
|
||||||
.section-title { font-size: 14px; font-weight: bold; color: var(--primary); margin: 30px 0 15px 0; text-transform: uppercase; border-left: 6px solid var(--primary); padding: 8px 15px; background: #e8f5e9; }
|
|
||||||
.row { display: flex; margin-bottom: 15px; align-items: center; flex-wrap: wrap; gap: 10px; }
|
|
||||||
.row label { flex: 0 0 220px; font-weight: bold; font-size: 15px; color: #333; }
|
|
||||||
.row input, .row select { flex: 1 1 300px; padding: 12px; border: 1px solid #aaa; border-radius: 5px; text-transform: uppercase; font-family: inherit; font-size: 15px; box-sizing: border-box; }
|
|
||||||
.multi-input { display: flex; gap: 10px; flex: 1 1 300px; flex-wrap: wrap; align-items: center; }
|
|
||||||
.btn-inline { padding: 12px 20px; background: #607d8b; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; font-size: 13px; transition: 0.3s; }
|
|
||||||
.btn-inline:hover { background: #455a64; }
|
|
||||||
textarea { width: 100%; height: 200px; margin-top: 15px; text-transform: uppercase; font-family: inherit; padding: 15px; border: 1px solid #aaa; border-radius: 5px; box-sizing: border-box; resize: vertical; font-size: 16px; }
|
|
||||||
.btns-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin: 30px 0; }
|
|
||||||
button { padding: 15px; font-weight: bold; cursor: pointer; color: white; border: none; border-radius: 6px; font-size: 14px; transition: 0.2s; }
|
|
||||||
.btn-gen { background: var(--primary); grid-column: span 2; font-size: 18px; }
|
|
||||||
.btn-gen:hover { background: #1b5e20; }
|
|
||||||
.btn-copy { background: var(--secondary); }
|
|
||||||
.btn-copy:hover { background: #0d47a1; }
|
|
||||||
.btn-save-txt { background: var(--warning); color: #000; }
|
|
||||||
.btn-save-txt:hover { background: #e65100; color: #fff; }
|
|
||||||
.btn-save-html { background: #5c6bc0; color: #fff; }
|
|
||||||
.btn-save-html:hover { background: #3949ab; }
|
|
||||||
.btn-clear { background: var(--danger); }
|
|
||||||
.btn-clear:hover { background: #b71c1c; }
|
|
||||||
.btn-external { background: var(--secondary); font-size: 14px; text-decoration: none; display: flex; align-items: center; justify-content: center; text-align: center; margin-bottom: 20px; width: 100%; cursor: pointer; border-radius: 4px; }
|
|
||||||
.btn-external:hover { background: #1565c0; }
|
|
||||||
#preview {
|
|
||||||
width: 100%; min-height: 380px; height: auto;
|
|
||||||
background: var(--dark); color: #00ff41;
|
|
||||||
border: 3px solid #444; padding: 25px;
|
|
||||||
white-space: pre-wrap; word-wrap: break-word; font-size: 15px;
|
|
||||||
margin-top: 15px; border-radius: 6px; box-sizing: border-box;
|
|
||||||
box-shadow: inset 0 0 20px #000; overflow: visible;
|
|
||||||
}
|
|
||||||
.footer-sig { margin-top: 40px; text-align: center; font-size: 13px; color: #888; border-top: 1px solid #ddd; padding-top: 20px; font-family: sans-serif; font-weight: bold; letter-spacing: 2px; }
|
|
||||||
@media (max-width: 700px) {
|
|
||||||
.row label { flex: 1 1 100%; }
|
|
||||||
.btns-grid { grid-template-columns: 1fr; }
|
|
||||||
.btn-gen { grid-column: span 1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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-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 onload="initForm()">
|
|
||||||
|
|
||||||
<div class="nav-bar"><a href="../index.html">← Back to Packet Forms Hub</a></div>
|
|
||||||
|
|
||||||
<div class="radiogram-container">
|
|
||||||
<div class="header">
|
|
||||||
<h2>TPRFN / RRI RADIOGRAM GENERATOR</h2>
|
|
||||||
<div style="font-weight:bold; margin-top:8px; font-size: 14px; color: #555;">NTS DIGITAL TRAFFIC BENCH</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="button" class="btn-external" onclick="window.open('https://www.tprfn.net/tprfn-lets-get-connected/tprfn-radiogram-instructions', '_blank')">🔗 RADIOGRAM INSTRUCTIONS (OPENS IN NEW WINDOW)</button>
|
|
||||||
|
|
||||||
<form id="tprfnForm" onsubmit="event.preventDefault(); return false;">
|
|
||||||
<div class="section-title">1. Station of Origin</div>
|
|
||||||
<div class="row"><label for="s_name">Op Name:</label><input type="text" id="s_name" placeholder="FULL NAME"></div>
|
|
||||||
<div class="row"><label for="s_call">Station Callsign:</label><input type="text" id="s_call" placeholder="N3MEL"></div>
|
|
||||||
<div class="row"><label for="s_place">Place of Origin:</label><input type="text" id="s_place" placeholder="CITY STATE"></div>
|
|
||||||
|
|
||||||
<div class="section-title">2. Preamble (The Header)</div>
|
|
||||||
<div class="row"><label for="num">Message Number:</label><input type="text" id="num" placeholder="1"></div>
|
|
||||||
<div class="row"><label for="prec">Precedence:</label>
|
|
||||||
<select id="prec">
|
|
||||||
<option value="R">ROUTINE (R)</option>
|
|
||||||
<option value="W">WELFARE (W)</option>
|
|
||||||
<option value="P">PRIORITY (P)</option>
|
|
||||||
<option value="EMERGENCY">EMERGENCY</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="row"><label for="hx">Handling (HX):</label>
|
|
||||||
<select id="hx">
|
|
||||||
<option value="">NONE</option>
|
|
||||||
<option value="HXA">HXA</option>
|
|
||||||
<option value="HXB">HXB</option>
|
|
||||||
<option value="HXC">HXC</option>
|
|
||||||
<option value="HXD">HXD</option>
|
|
||||||
<option value="HXE">HXE</option>
|
|
||||||
<option value="HXF">HXF</option>
|
|
||||||
<option value="HXG">HXG</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="row"><label for="date_val">Date Filed:</label><input type="text" id="date_val" placeholder="MMM DD"></div>
|
|
||||||
<div class="row">
|
|
||||||
<label for="time_val">Time Filed (UTC):</label>
|
|
||||||
<div class="multi-input">
|
|
||||||
<input type="text" id="time_val" placeholder="0000" style="flex: 1;">
|
|
||||||
<button type="button" class="btn-inline" id="btnNowZulu">🕒 NOW</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="section-title">3. The Address (Recipient)</div>
|
|
||||||
<div class="row"><label for="r_name">Recipient Name:</label><input type="text" id="r_name"></div>
|
|
||||||
<div class="row"><label for="r_call">Recipient Callsign:</label><input type="text" id="r_call"></div>
|
|
||||||
<div class="row"><label for="addr1">Street Address:</label><input type="text" id="addr1"></div>
|
|
||||||
<div class="row"><label for="addr2">Apt / Suite / Box:</label><input type="text" id="addr2"></div>
|
|
||||||
<div class="row">
|
|
||||||
<label for="city">City ST Zip:</label>
|
|
||||||
<div class="multi-input">
|
|
||||||
<input type="text" id="city" placeholder="TOWN" style="flex:2">
|
|
||||||
<select id="state" style="flex:1;">
|
|
||||||
<option value="">ST</option>
|
|
||||||
<option value="AL">AL</option><option value="AK">AK</option><option value="AZ">AZ</option><option value="AR">AR</option>
|
|
||||||
<option value="CA">CA</option><option value="CO">CO</option><option value="CT">CT</option><option value="DE">DE</option>
|
|
||||||
<option value="DC">DC</option><option value="FL">FL</option><option value="GA">GA</option><option value="HI">HI</option>
|
|
||||||
<option value="ID">ID</option><option value="IL">IL</option><option value="IN">IN</option><option value="IA">IA</option>
|
|
||||||
<option value="KS">KS</option><option value="KY">KY</option><option value="LA">LA</option><option value="ME">ME</option>
|
|
||||||
<option value="MD">MD</option><option value="MA">MA</option><option value="MI">MI</option><option value="MN">MN</option>
|
|
||||||
<option value="MS">MS</option><option value="MO">MO</option><option value="MT">MT</option><option value="NE">NE</option>
|
|
||||||
<option value="NV">NV</option><option value="NH">NH</option><option value="NJ">NJ</option><option value="NM">NM</option>
|
|
||||||
<option value="NY">NY</option><option value="NC">NC</option><option value="ND">ND</option><option value="OH">OH</option>
|
|
||||||
<option value="OK">OK</option><option value="OR">OR</option><option value="PA">PA</option><option value="RI">RI</option>
|
|
||||||
<option value="SC">SC</option><option value="SD">SD</option><option value="TN">TN</option><option value="TX">TX</option>
|
|
||||||
<option value="UT">UT</option><option value="VT">VT</option><option value="VA">VA</option><option value="WA">WA</option>
|
|
||||||
<option value="WV">WV</option><option value="WI">WI</option><option value="WY">WY</option>
|
|
||||||
</select>
|
|
||||||
<input type="text" id="zip" placeholder="ZIP" style="flex:1.5">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row"><label for="phone">Phone Number:</label><input type="text" id="phone"></div>
|
|
||||||
<div class="row"><label for="email">Email Address:</label><input type="text" id="email"></div>
|
|
||||||
|
|
||||||
<div class="section-title">4. The Text (Message Body)</div>
|
|
||||||
<textarea id="msg" placeholder="DASH BECOMES 'DASH', # BECOMES 'NR', .COM BECOMES 'DOT COM'."></textarea>
|
|
||||||
|
|
||||||
<div class="btns-grid">
|
|
||||||
<button type="button" class="btn-gen" id="btnGen">GENERATE RRI MESSAGE</button>
|
|
||||||
<button type="button" class="btn-copy" id="btnCopy">COPY FOR TERMINAL</button>
|
|
||||||
<button type="button" class="btn-save-txt" id="btnSaveTxt">SAVE AS .TXT</button>
|
|
||||||
<button type="button" class="btn-save-html" id="btnSaveHtml">SAVE AS HTML</button>
|
|
||||||
<button type="button" class="btn-clear" id="btnClear">RESET RECIPIENT/MSG</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div class="section-title">5. RRI / ARRL ASCII Output</div>
|
|
||||||
<pre id="preview">Ready for input...</pre>
|
|
||||||
|
|
||||||
<div class="footer-sig">CODE CREATED BY N3MEL • PACKET RADIO EMCOMM ADAPTATION</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<textarea id="hidden_copy" style="position:absolute; left:-9999px;"></textarea>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function initForm() { setZuluTime(); }
|
|
||||||
|
|
||||||
function setZuluTime() {
|
|
||||||
const now = new Date();
|
|
||||||
const months = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"];
|
|
||||||
document.getElementById('date_val').value = months[now.getUTCMonth()] + " " + String(now.getUTCDate()).padStart(2, '0');
|
|
||||||
document.getElementById('time_val').value = String(now.getUTCHours()).padStart(2, '0') + String(now.getUTCMinutes()).padStart(2, '0');
|
|
||||||
}
|
|
||||||
|
|
||||||
function generate() {
|
|
||||||
try {
|
|
||||||
const toAsc = (t) => (t || "").trim().toUpperCase().replace(/[^\x00-\x7F]/g, "");
|
|
||||||
|
|
||||||
const strip = (t) => {
|
|
||||||
let work = toAsc(t);
|
|
||||||
work = work.replace(/'/g, "").replace(/#/g, " NR ").replace(/-/g, " DASH ");
|
|
||||||
return work.replace(/[.,\/!$%\^&\*;:{}=_`~()]/g, " ");
|
|
||||||
};
|
|
||||||
|
|
||||||
const getVal = (id) => strip(document.getElementById(id).value).replace(/\s+/g, " ");
|
|
||||||
|
|
||||||
let s_name = getVal('s_name'), s_call = getVal('s_call'), s_place = getVal('s_place');
|
|
||||||
let num = document.getElementById('num').value.trim().replace(/^0+/, "") || "1";
|
|
||||||
let prec = document.getElementById('prec').value, hx = getVal('hx');
|
|
||||||
let date_f = getVal('date_val'), time_f = getVal('time_val');
|
|
||||||
let r_name = getVal('r_name'), r_call = getVal('r_call'), addr1 = getVal('addr1'), addr2 = getVal('addr2');
|
|
||||||
let r_city = getVal('city'), r_state = document.getElementById('state').value, zip = getVal('zip');
|
|
||||||
|
|
||||||
let rawPhone = document.getElementById('phone').value;
|
|
||||||
let formattedPhone = rawPhone.replace(/[^\d]/g, " ").replace(/\s+/g, " ").trim();
|
|
||||||
|
|
||||||
let email = document.getElementById('email').value.toUpperCase();
|
|
||||||
let msg = document.getElementById('msg').value.toUpperCase();
|
|
||||||
|
|
||||||
const msgFmt = (t) => {
|
|
||||||
let work = t.replace(/'/g, "");
|
|
||||||
work = work.replace(/\.NET/g, " DOT NET ").replace(/\.COM/g, " DOT COM ").replace(/\.ORG/g, " DOT ORG ").replace(/\.EDU/g, " DOT EDU ").replace(/\.GOV/g, " DOT GOV ");
|
|
||||||
work = work.replace(/-/g, " DASH ").replace(/#/g, " NR ").replace(/\./g, " X ").replace(/@/g, " ATSIGN ").replace(/_/g, " UNDERSCORE ");
|
|
||||||
return work.replace(/[!$%\^&\*;{}=~()]/g, " ");
|
|
||||||
};
|
|
||||||
|
|
||||||
const emailFmt = (t) => t.replace(/@/g, " ATSIGN ").replace(/\./g, " DOT ").replace(/_/g, " UNDERSCORE ").replace(/-/g, " DASH ");
|
|
||||||
|
|
||||||
let processedMsg = msgFmt(msg);
|
|
||||||
let finalWords = processedMsg.split(/\s+/).filter(w => w.length > 0);
|
|
||||||
|
|
||||||
let out = "";
|
|
||||||
if(zip && r_state) out += "ST " + zip + "@NTS" + r_state + "\n";
|
|
||||||
out += (r_city || "DESTINATION") + (r_call ? " " + r_call : "") + "\n\n";
|
|
||||||
|
|
||||||
out += num + " " + prec + " " + (hx ? hx + " " : "") + (s_call || "ORIGIN") + " " + finalWords.length + " " + (s_place || "ORIGIN") + " " + (time_f ? time_f + "Z " : "") + date_f + "\n";
|
|
||||||
out += (r_name || "RECIPIENT") + (r_call ? " " + r_call : "") + "\n";
|
|
||||||
if(addr1) out += addr1 + "\n";
|
|
||||||
if(addr2) out += addr2 + "\n";
|
|
||||||
if(r_city || r_state || zip) out += (r_city ? r_city + " " : "") + (r_state ? r_state + " " : "") + (zip || "") + "\n";
|
|
||||||
if(formattedPhone) out += formattedPhone + "\n";
|
|
||||||
if(email) out += emailFmt(email).replace(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g, "") + "\n";
|
|
||||||
out += "BT\n";
|
|
||||||
for (let i = 0; i < finalWords.length; i += 5) {
|
|
||||||
out += finalWords.slice(i, i + 5).join(' ') + "\n";
|
|
||||||
}
|
|
||||||
out += "BT\n" + (s_name ? s_name + " " : "") + (s_call || "") + "\n/EX";
|
|
||||||
|
|
||||||
document.getElementById('preview').innerText = out;
|
|
||||||
} catch(err) {
|
|
||||||
alert("Error generating radiogram: " + err.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function copyClean() {
|
|
||||||
const t = document.getElementById('preview').innerText;
|
|
||||||
if(t.includes("Ready")) {
|
|
||||||
alert("Please click GENERATE RRI MESSAGE first.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
||||||
navigator.clipboard.writeText(t).then(function() {
|
|
||||||
showCopyNotice();
|
|
||||||
}).catch(function() {
|
|
||||||
fallbackCopy(t);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
fallbackCopy(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function fallbackCopy(t) {
|
|
||||||
const h = document.getElementById('hidden_copy');
|
|
||||||
h.value = t;
|
|
||||||
h.select();
|
|
||||||
document.execCommand('copy');
|
|
||||||
showCopyNotice();
|
|
||||||
}
|
|
||||||
|
|
||||||
function showCopyNotice() {
|
|
||||||
const b = document.getElementById('btnCopy');
|
|
||||||
const orig = b.innerText;
|
|
||||||
b.innerText = "✅ COPIED!";
|
|
||||||
setTimeout(() => { b.innerText = orig; }, 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveToFile() {
|
|
||||||
const t = document.getElementById('preview').innerText;
|
|
||||||
if(t.includes("Ready")) {
|
|
||||||
alert("Please generate the message before saving.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const b = new Blob([t], { type: "text/plain;charset=us-ascii" });
|
|
||||||
const a = document.createElement("a");
|
|
||||||
let defTxt = "Radiogram_" + (document.getElementById('num').value || "1") + ".txt";
|
|
||||||
let chosenTxt = prompt('Enter filename to save as (.txt):', defTxt);
|
|
||||||
if (!chosenTxt) return;
|
|
||||||
if (!chosenTxt.toLowerCase().endsWith('.txt')) chosenTxt += '.txt';
|
|
||||||
a.download = chosenTxt;
|
|
||||||
a.href = window.URL.createObjectURL(b);
|
|
||||||
document.body.appendChild(a);
|
|
||||||
a.click();
|
|
||||||
document.body.removeChild(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function clearForm() {
|
|
||||||
if(confirm("Clear current recipient and message data?")) {
|
|
||||||
['num','r_name','r_call','addr1','addr2','city','zip','phone','email','msg','time_val'].forEach(id => document.getElementById(id).value = "");
|
|
||||||
document.getElementById('state').selectedIndex = 0;
|
|
||||||
setZuluTime();
|
|
||||||
document.getElementById('preview').innerText = "Ready for input...";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('btnNowZulu').addEventListener('click', setZuluTime);
|
|
||||||
document.getElementById('btnGen').addEventListener('click', generate);
|
|
||||||
document.getElementById('btnCopy').addEventListener('click', copyClean);
|
|
||||||
document.getElementById('btnSaveTxt').addEventListener('click', saveToFile);
|
|
||||||
document.getElementById('btnSaveHtml').addEventListener('click', openSaveModal);
|
|
||||||
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 = 'tprfn_radiogram_' + 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
// Clear existing inline handlers just in case
|
|
||||||
btnSaveMain.onclick = null;
|
|
||||||
btnSaveMain.addEventListener('click', openSaveModal);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
</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>
|
|
||||||
</html>
|
|
||||||
@@ -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>
|
||||||
@@ -32,23 +32,12 @@
|
|||||||
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; }
|
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 { margin-bottom: 12px; font-size: 0.85rem; }
|
||||||
.nav-bar a { color: #137333; text-decoration: none; font-weight: bold; }
|
.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; }
|
||||||
/* 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>
|
||||||
@@ -163,6 +151,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();
|
||||||
@@ -232,8 +233,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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) {
|
||||||
@@ -266,13 +265,11 @@
|
|||||||
|
|
||||||
document.getElementById('btnDateNow').addEventListener('click', setUTC);
|
document.getElementById('btnDateNow').addEventListener('click', setUTC);
|
||||||
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);
|
||||||
@@ -332,6 +329,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');
|
||||||
@@ -354,29 +352,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>
|
||||||
+21
-60
@@ -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>
|
||||||
+32
-66
@@ -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>
|
||||||
+24
-63
@@ -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>
|
||||||
@@ -4,36 +4,35 @@
|
|||||||
<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 {
|
||||||
display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
||||||
@@ -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">
|
||||||
@@ -424,4 +431,4 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+95
-88
@@ -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,20 +21,18 @@
|
|||||||
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 {
|
||||||
display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
||||||
@@ -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">
|
||||||
@@ -471,4 +478,4 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+90
-83
@@ -4,35 +4,35 @@
|
|||||||
<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 {
|
||||||
display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
||||||
@@ -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">
|
||||||
@@ -458,4 +465,4 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+75
-68
@@ -6,39 +6,33 @@
|
|||||||
<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 {
|
||||||
display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
||||||
@@ -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>
|
||||||
Reference in New Issue
Block a user