Umrechungen eingebaut

This commit is contained in:
2026-04-06 11:04:11 +02:00
parent f3e613e98e
commit c820759d4e
6 changed files with 904 additions and 1 deletions
+752
View File
@@ -0,0 +1,752 @@
<?php
declare(strict_types=1);
ini_set('display_errors', '1');
error_reporting(E_ALL);
session_start();
$loggedIn = isset($_SESSION['user_id']);
$pageTitle = 'Astronomische Umrechnungen';
$headerIntroPre = 'Astronomie-Werkzeuge';
$headerIntroTitle = 'Astronomische Umrechnungen';
$headerIntroSub = 'Entfernungen zwischen haeufigen astronomischen Einheiten umrechnen.';
function h(?string $value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
?>
<?php require __DIR__ . '/header.php'; ?>
<style>
.astro-conv-layout {
display: grid;
gap: 1.25rem;
}
.astro-conv-tabs {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
}
.astro-conv-tab {
padding: 0.75rem 1rem;
border-radius: 999px;
border: 1px solid rgba(130, 170, 255, 0.18);
background: rgba(255,255,255,0.04);
color: #dce8ff;
cursor: pointer;
}
.astro-conv-tab.is-active {
background: linear-gradient(180deg, rgba(82, 153, 255, 0.28), rgba(49, 112, 255, 0.18));
border-color: rgba(130, 170, 255, 0.4);
color: #ffffff;
}
.astro-conv-panel {
padding: 1.2rem;
border-radius: 1rem;
background:
radial-gradient(circle at top left, rgba(82, 153, 255, 0.12), transparent 36%),
linear-gradient(180deg, rgba(13, 19, 33, 0.96), rgba(8, 11, 20, 0.98));
border: 1px solid rgba(130, 170, 255, 0.16);
}
.astro-conv-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 0.85rem;
}
.astro-conv-field {
display: grid;
gap: 0.45rem;
}
.astro-conv-field label {
color: #c7d6f4;
font-size: 0.94rem;
}
.astro-conv-label-help {
border-bottom: 1px dotted rgba(199, 214, 244, 0.45);
cursor: help;
}
.astro-conv-input {
width: 100%;
padding: 0.72rem 0.8rem;
border-radius: 0.8rem;
border: 1px solid rgba(255,255,255,0.12);
background: rgba(255,255,255,0.05);
color: #f8fbff;
font-size: 0.96rem;
}
.astro-conv-input:focus {
outline: none;
border-color: rgba(130, 170, 255, 0.6);
box-shadow: 0 0 0 3px rgba(82, 153, 255, 0.16);
}
.astro-conv-field {
position: relative;
}
.astro-conv-tooltip {
position: absolute;
left: 0;
top: calc(100% + 0.45rem);
z-index: 20;
min-width: 14rem;
max-width: 22rem;
padding: 0.7rem 0.8rem;
border-radius: 0.8rem;
background: rgba(10, 15, 27, 0.96);
border: 1px solid rgba(130, 170, 255, 0.2);
color: #eef4ff;
font-size: 0.9rem;
line-height: 1.45;
white-space: pre-line;
box-shadow: 0 12px 30px rgba(0,0,0,0.35);
opacity: 0;
pointer-events: none;
transform: translateY(-0.25rem);
transition: opacity 140ms ease, transform 140ms ease;
}
.astro-conv-field.is-tooltip-open .astro-conv-tooltip {
opacity: 1;
transform: translateY(0);
}
.astro-conv-actions {
display: flex;
flex-wrap: wrap;
gap: 0.8rem;
margin-top: 1rem;
}
.astro-conv-tab-panel {
display: none;
}
.astro-conv-tab-panel.is-active {
display: block;
}
@media (max-width: 960px) {
.astro-conv-grid {
grid-template-columns: 1fr;
}
}
</style>
<div class="astro-conv-layout">
<div class="astro-conv-tabs" role="tablist" aria-label="Umrechnungsbereiche">
<button type="button" class="astro-conv-tab is-active" data-tab-target="distanceTab" role="tab" aria-selected="true">Entfernung</button>
<button type="button" class="astro-conv-tab" data-tab-target="redshiftTab" role="tab" aria-selected="false">Rotverschiebung</button>
<button type="button" class="astro-conv-tab" data-tab-target="timeTab" role="tab" aria-selected="false">Zeit</button>
</div>
<section class="card astro-conv-panel astro-conv-tab-panel is-active" id="distanceTab" role="tabpanel">
<h2>Entfernungs-Umrechner</h2>
<div class="astro-conv-grid" id="distanceConverter">
<div class="astro-conv-field">
<label for="distanceKm">Kilometer (km)</label>
<input class="astro-conv-input" id="distanceKm" type="text" inputmode="decimal" value="384400">
</div>
<div class="astro-conv-field">
<label for="distanceAu">Astronomische Einheiten (AU)</label>
<input class="astro-conv-input" id="distanceAu" type="text" inputmode="decimal">
</div>
<div class="astro-conv-field">
<label for="distanceLightMinute">Lichtminuten (lm)</label>
<input class="astro-conv-input" id="distanceLightMinute" type="text" inputmode="decimal">
</div>
<div class="astro-conv-field">
<label for="distanceLightHour">Lichtstunden (lh)</label>
<input class="astro-conv-input" id="distanceLightHour" type="text" inputmode="decimal">
</div>
<div class="astro-conv-field">
<label for="distanceLy">Lichtjahre (ly)</label>
<input class="astro-conv-input" id="distanceLy" type="text" inputmode="decimal">
</div>
<div class="astro-conv-field">
<label for="distancePc">Parsec (pc)</label>
<input class="astro-conv-input" id="distancePc" type="text" inputmode="decimal">
</div>
<div class="astro-conv-field">
<label for="distanceMpc">Megaparsec (Mpc)</label>
<input class="astro-conv-input" id="distanceMpc" type="text" inputmode="decimal">
</div>
</div>
<div class="astro-conv-actions">
<button type="button" class="btn btn-secondary" id="distanceClear">Leeren</button>
<button type="button" class="btn btn-primary" id="distanceMoon">Mondentfernung</button>
<button type="button" class="btn btn-primary" id="distanceSun">1 AU</button>
</div>
</section>
<section class="card astro-conv-panel astro-conv-tab-panel" id="redshiftTab" role="tabpanel">
<h2>Rotverschiebung (z)</h2>
<div class="astro-conv-grid" id="redshiftConverter">
<div class="astro-conv-field">
<label for="redshiftZ">Rotverschiebung z</label>
<input class="astro-conv-input" id="redshiftZ" type="text" inputmode="decimal" value="0,1">
</div>
<div class="astro-conv-field">
<label for="redshiftFactor"><span class="astro-conv-label-help" title="Der Faktor, um den sich Wellenlaengen durch die Expansion des Universums verlaengern.">Faktor (1 + z)</span></label>
<input class="astro-conv-input" id="redshiftFactor" type="text" inputmode="decimal" readonly>
</div>
<div class="astro-conv-field">
<label for="redshiftScaleFactor"><span class="astro-conv-label-help" title="Der kosmologische Skalenfaktor a = 1 / (1 + z). Er beschreibt, wie gross das Universum zum Zeitpunkt der Aussendung relativ zu heute war.">Skalenfaktor a</span></label>
<input class="astro-conv-input" id="redshiftScaleFactor" type="text" inputmode="decimal" readonly>
</div>
<div class="astro-conv-field">
<label for="redshiftVelocityApprox"><span class="astro-conv-label-help" title="Einfache Naeherung fuer kleine Rotverschiebungen: v ungefaehr gleich z mal Lichtgeschwindigkeit.">Geschwindigkeit naeherungsweise (km/s)</span></label>
<input class="astro-conv-input" id="redshiftVelocityApprox" type="text" inputmode="decimal" readonly>
</div>
<div class="astro-conv-field">
<label for="redshiftVelocityRel"><span class="astro-conv-label-help" title="Relativistisch korrigierte Geschwindigkeit aus der Doppler-Formel. Sinnvoller als die lineare Naeherung bei groesserem z.">Geschwindigkeit relativistisch (km/s)</span></label>
<input class="astro-conv-input" id="redshiftVelocityRel" type="text" inputmode="decimal" readonly>
</div>
<div class="astro-conv-field">
<label for="redshiftVelocityC"><span class="astro-conv-label-help" title="Dieselbe relativistische Geschwindigkeit, aber als Anteil der Lichtgeschwindigkeit in Prozent.">Geschwindigkeit relativistisch (% c)</span></label>
<input class="astro-conv-input" id="redshiftVelocityC" type="text" inputmode="decimal" readonly>
</div>
<div class="astro-conv-field">
<label for="redshiftRestNm">Ruhewellenlaenge (nm)</label>
<input class="astro-conv-input" id="redshiftRestNm" type="text" inputmode="decimal" value="656,3">
</div>
<div class="astro-conv-field">
<label for="redshiftObservedNm">Beobachtete Wellenlaenge (nm)</label>
<input class="astro-conv-input" id="redshiftObservedNm" type="text" inputmode="decimal" readonly>
</div>
<div class="astro-conv-field">
<label for="redshiftFrequencyFactor"><span class="astro-conv-label-help" title="Die beobachtete Frequenz ist um diesen Faktor kleiner als die urspruengliche Frequenz.">Frequenzfaktor 1 / (1 + z)</span></label>
<input class="astro-conv-input" id="redshiftFrequencyFactor" type="text" inputmode="decimal" readonly>
</div>
<div class="astro-conv-field">
<label for="redshiftLookbackGyr"><span class="astro-conv-label-help" title="Zeit, die das Licht seit seiner Aussendung bis heute unterwegs war. Gyr steht fuer Milliarden Jahre.">Rueckblickzeit (Gyr)</span></label>
<input class="astro-conv-input" id="redshiftLookbackGyr" type="text" inputmode="decimal" readonly>
</div>
<div class="astro-conv-field">
<label for="redshiftComovingMpc"><span class="astro-conv-label-help" title="Kosmologische Entfernung im mitbewegten Koordinatensystem. Sie bleibt fuer mit der Expansion mitbewegte Objekte zeitlich konstant.">Mitbewegte Entfernung (Mpc)</span></label>
<input class="astro-conv-input" id="redshiftComovingMpc" type="text" inputmode="decimal" readonly>
</div>
<div class="astro-conv-field">
<label for="redshiftLuminosityMpc"><span class="astro-conv-label-help" title="Entfernung, die man bei der beobachteten Helligkeit eines Objekts in einem expandierenden Universum verwendet. Wichtig fuer Standardkerzen.">Leuchtkraftentfernung (Mpc)</span></label>
<input class="astro-conv-input" id="redshiftLuminosityMpc" type="text" inputmode="decimal" readonly>
</div>
<div class="astro-conv-field">
<label for="redshiftAngularDiameterMpc"><span class="astro-conv-label-help" title="Entfernung, die den beobachteten Winkel mit der echten Groesse eines Objekts verknuepft. Wichtig fuer scheinbare Groessen am Himmel.">Winkeldurchmesser-Entfernung (Mpc)</span></label>
<input class="astro-conv-input" id="redshiftAngularDiameterMpc" type="text" inputmode="decimal" readonly>
</div>
</div>
<div class="astro-conv-actions">
<button type="button" class="btn btn-secondary" id="redshiftClear">Leeren</button>
<button type="button" class="btn btn-primary" id="redshiftGalaxy">z = 0,1</button>
<button type="button" class="btn btn-primary" id="redshiftQuasar">z = 6</button>
</div>
</section>
<section class="card astro-conv-panel astro-conv-tab-panel" id="timeTab" role="tabpanel">
<h2>Zeit-Umrechner</h2>
<div class="astro-conv-grid" id="timeConverter">
<div class="astro-conv-field">
<label for="timeUtc">UTC</label>
<input class="astro-conv-input" id="timeUtc" type="text" value="2026-04-06 12:00:00">
</div>
<div class="astro-conv-field">
<label for="timeJd">Julianisches Datum (JD)</label>
<input class="astro-conv-input" id="timeJd" type="text" inputmode="decimal">
</div>
<div class="astro-conv-field">
<label for="timeMjd">Modifiziertes Julianisches Datum (MJD)</label>
<input class="astro-conv-input" id="timeMjd" type="text" inputmode="decimal">
</div>
<div class="astro-conv-field">
<label for="timeUnix">Unix-Zeit</label>
<input class="astro-conv-input" id="timeUnix" type="text" inputmode="decimal">
</div>
</div>
<div class="astro-conv-actions">
<button type="button" class="btn btn-secondary" id="timeClear">Leeren</button>
<button type="button" class="btn btn-primary" id="timeNow">Jetzt</button>
<button type="button" class="btn btn-primary" id="timeJ2000">J2000</button>
</div>
</section>
</div>
<script src="js/number_to_german_words.js"></script>
<script>
(function () {
const SPEED_OF_LIGHT_KM_S = 299792.458;
const H0 = 70;
const OMEGA_M = 0.3;
const OMEGA_LAMBDA = 0.7;
const MPC_IN_KM = 3.0856775814913673e19;
const SECONDS_PER_GYR = 365.25 * 24 * 3600 * 1e9;
const KM_PER_AU = 149597870.7;
const KM_PER_LIGHT_MINUTE = 17987547.48;
const KM_PER_LIGHT_HOUR = 1079252848.8;
const KM_PER_LY = 9460730472580.8;
const KM_PER_PC = 30856775814913.67;
const KM_PER_MPC = KM_PER_PC * 1000000;
const distanceFields = {
km: document.getElementById('distanceKm'),
au: document.getElementById('distanceAu'),
lm: document.getElementById('distanceLightMinute'),
lh: document.getElementById('distanceLightHour'),
ly: document.getElementById('distanceLy'),
pc: document.getElementById('distancePc'),
mpc: document.getElementById('distanceMpc')
};
const redshiftFields = {
z: document.getElementById('redshiftZ'),
factor: document.getElementById('redshiftFactor'),
scaleFactor: document.getElementById('redshiftScaleFactor'),
velocityApprox: document.getElementById('redshiftVelocityApprox'),
velocityRel: document.getElementById('redshiftVelocityRel'),
velocityC: document.getElementById('redshiftVelocityC'),
restNm: document.getElementById('redshiftRestNm'),
observedNm: document.getElementById('redshiftObservedNm'),
frequencyFactor: document.getElementById('redshiftFrequencyFactor'),
lookbackGyr: document.getElementById('redshiftLookbackGyr'),
comovingMpc: document.getElementById('redshiftComovingMpc'),
luminosityMpc: document.getElementById('redshiftLuminosityMpc'),
angularDiameterMpc: document.getElementById('redshiftAngularDiameterMpc')
};
const timeFields = {
utc: document.getElementById('timeUtc'),
jd: document.getElementById('timeJd'),
mjd: document.getElementById('timeMjd'),
unix: document.getElementById('timeUnix')
};
const unitToKm = {
km: 1,
au: KM_PER_AU,
lm: KM_PER_LIGHT_MINUTE,
lh: KM_PER_LIGHT_HOUR,
ly: KM_PER_LY,
pc: KM_PER_PC,
mpc: KM_PER_MPC
};
const germanNumberFormatter = new Intl.NumberFormat('de-DE', {
maximumFractionDigits: 10
});
const parseGermanNumber = function (value) {
if (typeof value !== 'string') {
return Number.NaN;
}
const normalized = value
.trim()
.replace(/\s+/g, '')
.replace(/\./g, '')
.replace(',', '.');
if (normalized === '') {
return Number.NaN;
}
return Number(normalized);
};
const formatValue = function (value) {
if (!Number.isFinite(value)) {
return '';
}
if (value === 0) {
return '0';
}
return germanNumberFormatter.format(value);
};
const cosmologyE = function (z) {
return Math.sqrt(OMEGA_M * Math.pow(1 + z, 3) + OMEGA_LAMBDA);
};
const integrateSimpson = function (fn, start, end, steps) {
if (steps % 2 !== 0) {
steps += 1;
}
const h = (end - start) / steps;
let sum = fn(start) + fn(end);
for (let i = 1; i < steps; i += 1) {
const x = start + i * h;
sum += fn(x) * (i % 2 === 0 ? 2 : 4);
}
return (h / 3) * sum;
};
const updateTooltip = function (field) {
const wrapper = field.closest('.astro-conv-field');
const tooltipId = field.getAttribute('data-tooltip-id');
const tooltip = tooltipId ? document.getElementById(tooltipId) : null;
const numericValue = parseGermanNumber(field.value);
if (!wrapper || !tooltip || !Number.isFinite(numericValue)) {
if (wrapper) {
wrapper.classList.remove('is-tooltip-open');
}
return;
}
if (typeof window.numberToGermanText !== 'function') {
return;
}
tooltip.textContent = window.numberToGermanText(numericValue);
};
const attachTooltip = function (field, key) {
const wrapper = field.closest('.astro-conv-field');
if (!wrapper) {
return;
}
const tooltip = document.createElement('div');
tooltip.className = 'astro-conv-tooltip';
tooltip.id = 'tooltip-' + key;
wrapper.appendChild(tooltip);
field.setAttribute('data-tooltip-id', tooltip.id);
};
const wireTooltipHover = function (field) {
field.addEventListener('mouseenter', function () {
updateTooltip(field);
if (field.value.trim() === '') {
return;
}
const wrapper = field.closest('.astro-conv-field');
if (wrapper) {
wrapper.classList.add('is-tooltip-open');
}
});
field.addEventListener('mouseleave', function () {
const wrapper = field.closest('.astro-conv-field');
if (wrapper) {
wrapper.classList.remove('is-tooltip-open');
}
});
};
const distanceFieldEntries = Object.entries(distanceFields);
distanceFieldEntries.forEach(function ([unit, field]) {
attachTooltip(field, 'distance-' + unit);
wireTooltipHover(field);
});
const redshiftFieldEntries = Object.entries(redshiftFields);
redshiftFieldEntries.forEach(function ([unit, field]) {
attachTooltip(field, 'redshift-' + unit);
wireTooltipHover(field);
});
const timeFieldEntries = Object.entries(timeFields);
timeFieldEntries.forEach(function ([unit, field]) {
attachTooltip(field, 'time-' + unit);
wireTooltipHover(field);
});
const updateDistanceFrom = function (sourceUnit) {
const rawValue = distanceFields[sourceUnit].value.trim();
if (rawValue === '') {
Object.values(distanceFields).forEach(function (field, index) {
if (Object.keys(distanceFields)[index] !== sourceUnit) {
field.value = '';
}
});
return;
}
const numericValue = parseGermanNumber(rawValue);
if (!Number.isFinite(numericValue)) {
return;
}
const valueInKm = numericValue * unitToKm[sourceUnit];
distanceFields[sourceUnit].value = formatValue(numericValue);
updateTooltip(distanceFields[sourceUnit]);
Object.entries(distanceFields).forEach(function ([unit, field]) {
if (unit === sourceUnit) {
return;
}
field.value = formatValue(valueInKm / unitToKm[unit]);
updateTooltip(field);
});
};
distanceFieldEntries.forEach(function ([unit, field]) {
field.addEventListener('input', function () {
updateDistanceFrom(unit);
});
});
document.getElementById('distanceClear').addEventListener('click', function () {
Object.values(distanceFields).forEach(function (field) {
field.value = '';
});
distanceFields.km.focus();
});
document.getElementById('distanceMoon').addEventListener('click', function () {
distanceFields.km.value = '384400';
updateDistanceFrom('km');
});
document.getElementById('distanceSun').addEventListener('click', function () {
distanceFields.au.value = '1';
updateDistanceFrom('au');
});
const updateRedshift = function () {
const z = parseGermanNumber(redshiftFields.z.value.trim());
const restNm = parseGermanNumber(redshiftFields.restNm.value.trim());
if (!Number.isFinite(z) || !Number.isFinite(restNm) || z <= -1) {
return;
}
const factor = 1 + z;
const scaleFactor = 1 / factor;
const observedNm = restNm * factor;
const frequencyFactor = 1 / factor;
const velocityApprox = z * SPEED_OF_LIGHT_KM_S;
const betaRel = ((factor * factor) - 1) / ((factor * factor) + 1);
const velocityRel = betaRel * SPEED_OF_LIGHT_KM_S;
const velocityC = betaRel * 100;
const integrationSteps = Math.max(200, Math.ceil(z * 200));
const comovingIntegral = integrateSimpson(function (currentZ) {
return 1 / cosmologyE(currentZ);
}, 0, z, integrationSteps);
const lookbackIntegral = integrateSimpson(function (currentZ) {
return 1 / ((1 + currentZ) * cosmologyE(currentZ));
}, 0, z, integrationSteps);
const hubbleTimeSeconds = MPC_IN_KM / H0;
const comovingMpc = (SPEED_OF_LIGHT_KM_S / H0) * comovingIntegral;
const luminosityMpc = comovingMpc * factor;
const angularDiameterMpc = comovingMpc / factor;
const lookbackGyr = (hubbleTimeSeconds * lookbackIntegral) / SECONDS_PER_GYR;
redshiftFields.z.value = formatValue(z);
redshiftFields.restNm.value = formatValue(restNm);
redshiftFields.factor.value = formatValue(factor);
redshiftFields.scaleFactor.value = formatValue(scaleFactor);
redshiftFields.observedNm.value = formatValue(observedNm);
redshiftFields.frequencyFactor.value = formatValue(frequencyFactor);
redshiftFields.velocityApprox.value = formatValue(velocityApprox);
redshiftFields.velocityRel.value = formatValue(velocityRel);
redshiftFields.velocityC.value = formatValue(velocityC);
redshiftFields.lookbackGyr.value = formatValue(lookbackGyr);
redshiftFields.comovingMpc.value = formatValue(comovingMpc);
redshiftFields.luminosityMpc.value = formatValue(luminosityMpc);
redshiftFields.angularDiameterMpc.value = formatValue(angularDiameterMpc);
Object.values(redshiftFields).forEach(function (field) {
updateTooltip(field);
});
};
['z', 'restNm'].forEach(function (key) {
redshiftFields[key].addEventListener('input', updateRedshift);
});
document.getElementById('redshiftClear').addEventListener('click', function () {
Object.values(redshiftFields).forEach(function (field) {
field.value = '';
});
redshiftFields.z.focus();
});
document.getElementById('redshiftGalaxy').addEventListener('click', function () {
redshiftFields.z.value = '0,1';
redshiftFields.restNm.value = '656,3';
updateRedshift();
});
document.getElementById('redshiftQuasar').addEventListener('click', function () {
redshiftFields.z.value = '6';
redshiftFields.restNm.value = '121,567';
updateRedshift();
});
const parseUtcDateTime = function (value) {
const match = String(value).trim().match(/^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2}))?)?$/);
if (!match) {
return null;
}
const year = Number(match[1]);
const month = Number(match[2]);
const day = Number(match[3]);
const hour = Number(match[4] || '0');
const minute = Number(match[5] || '0');
const second = Number(match[6] || '0');
const date = new Date(Date.UTC(year, month - 1, day, hour, minute, second));
return Number.isNaN(date.getTime()) ? null : date;
};
const formatUtcDateTime = function (date) {
if (!(date instanceof Date) || Number.isNaN(date.getTime())) {
return '';
}
const parts = [
date.getUTCFullYear().toString().padStart(4, '0'),
(date.getUTCMonth() + 1).toString().padStart(2, '0'),
date.getUTCDate().toString().padStart(2, '0')
];
const timeParts = [
date.getUTCHours().toString().padStart(2, '0'),
date.getUTCMinutes().toString().padStart(2, '0'),
date.getUTCSeconds().toString().padStart(2, '0')
];
return parts.join('-') + ' ' + timeParts.join(':');
};
const dateToJulianDate = function (date) {
return (date.getTime() / 86400000) + 2440587.5;
};
const julianDateToDate = function (jd) {
return new Date((jd - 2440587.5) * 86400000);
};
const updateTimeFrom = function (sourceUnit) {
let date = null;
if (sourceUnit === 'utc') {
date = parseUtcDateTime(timeFields.utc.value);
} else if (sourceUnit === 'jd') {
const jd = parseGermanNumber(timeFields.jd.value.trim());
date = Number.isFinite(jd) ? julianDateToDate(jd) : null;
} else if (sourceUnit === 'mjd') {
const mjd = parseGermanNumber(timeFields.mjd.value.trim());
date = Number.isFinite(mjd) ? julianDateToDate(mjd + 2400000.5) : null;
} else if (sourceUnit === 'unix') {
const unixSeconds = parseGermanNumber(timeFields.unix.value.trim());
date = Number.isFinite(unixSeconds) ? new Date(unixSeconds * 1000) : null;
}
if (!(date instanceof Date) || Number.isNaN(date.getTime())) {
return;
}
const jd = dateToJulianDate(date);
const mjd = jd - 2400000.5;
const unixSeconds = Math.floor(date.getTime() / 1000);
timeFields.utc.value = formatUtcDateTime(date);
timeFields.jd.value = formatValue(jd);
timeFields.mjd.value = formatValue(mjd);
timeFields.unix.value = formatValue(unixSeconds);
Object.values(timeFields).forEach(function (field) {
updateTooltip(field);
});
};
timeFields.utc.addEventListener('input', function () {
updateTimeFrom('utc');
});
['jd', 'mjd', 'unix'].forEach(function (key) {
timeFields[key].addEventListener('input', function () {
updateTimeFrom(key);
});
});
document.getElementById('timeClear').addEventListener('click', function () {
Object.values(timeFields).forEach(function (field) {
field.value = '';
});
timeFields.utc.focus();
});
document.getElementById('timeNow').addEventListener('click', function () {
timeFields.utc.value = formatUtcDateTime(new Date());
updateTimeFrom('utc');
});
document.getElementById('timeJ2000').addEventListener('click', function () {
timeFields.utc.value = '2000-01-01 12:00:00';
updateTimeFrom('utc');
});
const tabs = Array.from(document.querySelectorAll('.astro-conv-tab'));
const tabPanels = Array.from(document.querySelectorAll('.astro-conv-tab-panel'));
const activateTab = function (targetId) {
tabs.forEach(function (tab) {
const isActive = tab.getAttribute('data-tab-target') === targetId;
tab.classList.toggle('is-active', isActive);
tab.setAttribute('aria-selected', isActive ? 'true' : 'false');
});
tabPanels.forEach(function (panel) {
panel.classList.toggle('is-active', panel.id === targetId);
});
};
tabs.forEach(function (tab) {
tab.addEventListener('click', function () {
activateTab(tab.getAttribute('data-tab-target'));
});
});
updateDistanceFrom('km');
updateRedshift();
updateTimeFrom('utc');
})();
</script>
<?php require __DIR__ . '/footer.php'; ?>