Satellitenhimmel verbessert

This commit is contained in:
2026-05-17 10:03:32 +02:00
parent 9ede4011f0
commit 44a88c347a
+251 -31
View File
@@ -451,7 +451,9 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
<div class="satpass-map-panel">
<div class="satpass-map-controls">
<button type="button" class="btn btn-secondary btn-sm" id="satpassExportBtn">PNG Export</button>
<button type="button" class="btn btn-secondary btn-sm" id="satpassExportSvgBtn">SVG Export</button>
<button type="button" class="btn btn-secondary btn-sm" id="satpassInvertBtn">Invertiert</button>
<button type="button" class="btn btn-secondary btn-sm" id="satpassMirrorBtn">Ost links / West rechts</button>
</div>
<p id="satpassSkyTime" class="satpass-sky-time"></p>
<div class="satpass-canvas-wrap">
@@ -505,7 +507,9 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
const statusEl = document.getElementById('satpassStatus');
const canvas = document.getElementById('satSkyCanvas');
const exportBtn = document.getElementById('satpassExportBtn');
const exportSvgBtn = document.getElementById('satpassExportSvgBtn');
const invertBtn = document.getElementById('satpassInvertBtn');
const mirrorBtn = document.getElementById('satpassMirrorBtn');
const skyTimeEl = document.getElementById('satpassSkyTime');
const satSelectEl = document.getElementById('satSelect');
const passSelectEl = document.getElementById('passSelect');
@@ -513,7 +517,9 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
const ctx = canvas.getContext('2d');
const exportLogoImg = new Image();
exportLogoImg.src = 'images/logoAM_sw.png';
const EXPORT_BACKGROUND_COLOR = '#f3efe6';
let invertSky = false;
let mirrorEastWest = true;
let exportOpaqueSky = false;
let lastReferenceTime = null;
let lastShowStars = false;
@@ -521,6 +527,20 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
let lastSelectedPass = null;
let lastSkyData = [];
function getDisplayedAzimuthDeg(azimuthDeg) {
const normalizedAzimuth = ((Number(azimuthDeg) % 360) + 360) % 360;
if (!mirrorEastWest) return normalizedAzimuth;
return (360 - normalizedAzimuth) % 360;
}
function updateMirrorButton() {
if (!mirrorBtn) return;
mirrorBtn.textContent = mirrorEastWest
? 'Ost links / West rechts'
: 'West links / Ost rechts';
mirrorBtn.classList.toggle('is-active', mirrorEastWest);
}
function deg2rad(deg) { return deg * Math.PI / 180; }
function rad2deg(rad) { return rad * 180 / Math.PI; }
@@ -594,13 +614,15 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
}
function azToCardinal(az) {
const dirs = ['N','NO','O','SO','S','SW','W','NW'];
return dirs[Math.round(az/45) % 8];
const displayAz = getDisplayedAzimuthDeg(az);
const dirs = ['N','NW','W','SW','S','SO','O','NO'];
return dirs[Math.round(displayAz/45) % 8];
}
function azToDirectionName(az) {
const dirs = ['Norden','Nordosten','Osten','Suedosten','Sueden','Suedwesten','Westen','Nordwesten'];
return dirs[Math.round(az/45) % 8];
const displayAz = getDisplayedAzimuthDeg(az);
const dirs = ['Norden','Nordwesten','Westen','Suedwesten','Sueden','Suedosten','Osten','Nordosten'];
return dirs[Math.round(displayAz/45) % 8];
}
function altitudeToPhrase(el) {
@@ -857,10 +879,84 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
function projectPoint(azimuthDeg, elevationDeg, radius, cx, cy) {
const r = ((90 - elevationDeg) / 90) * radius;
const a = deg2rad(azimuthDeg);
const a = deg2rad(getDisplayedAzimuthDeg(azimuthDeg));
return { x: cx + r*Math.sin(a), y: cy - r*Math.cos(a) };
}
function getSkyRenderMetrics() {
const box = canvas.parentElement.getBoundingClientRect();
const size = Math.max(320, Math.floor(Math.min(box.width, 820)));
return { size, cx: size / 2, cy: size / 2, radius: size * 0.43 };
}
function getSkyPalette() {
return invertSky ? {
skyFill: exportOpaqueSky ? 'rgb(252,252,248)' : 'rgba(252,252,248,0.98)',
grid:'rgba(60,64,76,0.26)', gridText:'rgba(40,44,56,0.90)', cardinal:'#171b24',
constellationLine:'rgba(88,96,118,0.76)', constellationLabel:'rgba(44,50,66,0.92)',
star: a=>`rgba(10,12,18,${Math.max(0.72,a).toFixed(2)})`,
starLabel:'rgba(18,22,32,0.94)', shadowLine:'rgba(150,154,166,0.82)',
shadowMarker:'rgba(118,124,138,0.95)', shadowMarkerStroke:'rgba(24,28,36,0.95)',
shadowLabel:'#111722'
} : {
skyFill: exportOpaqueSky ? 'rgb(4,6,20)' : 'rgba(4,6,20,0.55)',
grid:'rgba(201,168,76,0.18)', gridText:'rgba(136,153,187,0.85)', cardinal:'#f0d990',
constellationLine:'rgba(132,152,214,0.58)', constellationLabel:'rgba(120,136,186,0.68)',
star: a=>`rgba(220,230,255,${a.toFixed(2)})`,
starLabel:'rgba(180,200,255,0.70)', shadowLine:'rgba(140,140,150,0.5)',
shadowMarker:'rgba(155,165,190,0.95)', shadowMarkerStroke:'rgba(15,20,32,0.95)',
shadowLabel:'#d7dcec'
};
}
function estimateTextWidth(text, fontSize, weight = 400) {
ctx.save();
ctx.font = `${weight} ${fontSize}px Segoe UI`;
const width = ctx.measureText(String(text)).width;
ctx.restore();
return width;
}
function escapeXml(value) {
return String(value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&apos;');
}
function toSvgNumber(value) {
return Number(value).toFixed(2);
}
function buildSvgEndpointLabel(point, text, color, cx, cy, radius) {
const offsetX = point.x < cx ? 10 : -10;
const offsetY = point.y < cy ? -10 : 16;
const fontSize = 12;
const paddingX = 7;
const paddingY = 4;
const textWidth = estimateTextWidth(text, fontSize, 700);
let labelX = point.x + offsetX;
let labelY = point.y + offsetY;
if (offsetX < 0) labelX -= textWidth;
const minX = cx - radius + 6;
const maxX = cx + radius - textWidth - paddingX * 2 - 6;
const minY = cy - radius + fontSize + paddingY + 6;
const maxY = cy + radius - fontSize - paddingY - 6;
labelX = Math.max(minX, Math.min(maxX, labelX));
labelY = Math.max(minY, Math.min(maxY, labelY));
const boxX = labelX - paddingX;
const boxY = labelY - (fontSize / 2) - paddingY;
const boxW = textWidth + paddingX * 2;
const boxH = fontSize + paddingY * 2;
const fill = invertSky ? '#ffffff' : '#050814';
const stroke = invertSky ? '#1b2230' : 'rgba(255,255,255,0.22)';
return `
<rect x="${toSvgNumber(boxX)}" y="${toSvgNumber(boxY)}" width="${toSvgNumber(boxW)}" height="${toSvgNumber(boxH)}" fill="${fill}" stroke="${stroke}" stroke-width="1.2" />
<text x="${toSvgNumber(labelX)}" y="${toSvgNumber(labelY)}" fill="${color}" font-family="Segoe UI, Arial, sans-serif" font-size="${fontSize}" font-weight="700" dominant-baseline="middle">${escapeXml(text)}</text>`;
}
function drawEndpointLabel(point, text, color, cx, cy, radius) {
const offsetX = point.x < cx ? 10 : -10;
const offsetY = point.y < cy ? -10 : 16;
@@ -927,8 +1023,7 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
function drawSky(passes, referenceTime=null, showStars=true) {
lastReferenceTime = referenceTime;
lastShowStars = showStars;
const box = canvas.parentElement.getBoundingClientRect();
const size = Math.max(320, Math.floor(Math.min(box.width, 820)));
const { size, cx, cy, radius } = getSkyRenderMetrics();
const dpr = Math.min(window.devicePixelRatio || 1, 2);
canvas.width = size * dpr;
canvas.height = size * dpr;
@@ -936,25 +1031,7 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
canvas.style.height = `${size}px`;
ctx.setTransform(dpr,0,0,dpr,0,0);
ctx.clearRect(0,0,size,size);
const cx=size/2, cy=size/2, radius=size*0.43;
const palette = invertSky ? {
skyFill: exportOpaqueSky ? 'rgb(252,252,248)' : 'rgba(252,252,248,0.98)',
grid:'rgba(60,64,76,0.26)', gridText:'rgba(40,44,56,0.90)', cardinal:'#171b24',
constellationLine:'rgba(88,96,118,0.76)', constellationLabel:'rgba(44,50,66,0.92)',
star: a=>`rgba(10,12,18,${Math.max(0.72,a).toFixed(2)})`,
starLabel:'rgba(18,22,32,0.94)', shadowLine:'rgba(150,154,166,0.82)',
shadowMarker:'rgba(118,124,138,0.95)', shadowMarkerStroke:'rgba(24,28,36,0.95)',
shadowLabel:'#111722'
} : {
skyFill: exportOpaqueSky ? 'rgb(4,6,20)' : 'rgba(4,6,20,0.55)',
grid:'rgba(201,168,76,0.18)', gridText:'rgba(136,153,187,0.85)', cardinal:'#f0d990',
constellationLine:'rgba(132,152,214,0.58)', constellationLabel:'rgba(120,136,186,0.68)',
star: a=>`rgba(220,230,255,${a.toFixed(2)})`,
starLabel:'rgba(180,200,255,0.70)', shadowLine:'rgba(140,140,150,0.5)',
shadowMarker:'rgba(155,165,190,0.95)', shadowMarkerStroke:'rgba(15,20,32,0.95)',
shadowLabel:'#d7dcec'
};
const palette = getSkyPalette();
ctx.fillStyle = palette.skyFill;
ctx.beginPath(); ctx.arc(cx,cy,radius,0,Math.PI*2); ctx.fill();
@@ -973,7 +1050,8 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
ctx.fillStyle = palette.cardinal; ctx.font = '700 14px Segoe UI'; ctx.textAlign = 'center';
ctx.fillText('N', cx, cy-radius+18); ctx.fillText('S', cx, cy+radius-10);
ctx.fillText('W', cx-radius+14, cy+5); ctx.fillText('O', cx+radius-14, cy+5);
ctx.fillText(mirrorEastWest ? 'O' : 'W', cx-radius+14, cy+5);
ctx.fillText(mirrorEastWest ? 'W' : 'O', cx+radius-14, cy+5);
ctx.fillText('Zenit', cx, cy-8);
const lat = Number(SITE_LOCATION.latitude), lon = Number(SITE_LOCATION.longitude);
@@ -1178,11 +1256,143 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
});
}
function createSatpassExportFileName() {
function createSatpassExportFileName(extension = 'png') {
const date = lastSelectedPass?.aosTime instanceof Date ? lastSelectedPass.aosTime : new Date();
const parts = getTzParts(date, SITE_LOCATION.timezone);
const name = (lastSelectedSatelliteName || 'satellit').toLowerCase().replace(/[^a-z0-9]+/gi,'_').replace(/^_+|_+$/g,'') || 'satellit';
return `satellitenhimmel_${name}_${parts.year}-${padNumber(parts.month)}-${padNumber(parts.day)}_${padNumber(parts.hour)}-${padNumber(parts.minute)}.png`;
return `satellitenhimmel_${name}_${parts.year}-${padNumber(parts.month)}-${padNumber(parts.day)}_${padNumber(parts.hour)}-${padNumber(parts.minute)}.${extension}`;
}
function generateSatpassSvgMarkup() {
const prevExport = exportOpaqueSky;
exportOpaqueSky = true;
try {
const { size, cx, cy, radius } = getSkyRenderMetrics();
const palette = getSkyPalette();
const lat = Number(SITE_LOCATION.latitude);
const lon = Number(SITE_LOCATION.longitude);
const starTime = lastReferenceTime ?? new Date();
const parts = [];
parts.push(`<?xml version="1.0" encoding="UTF-8"?>`);
parts.push(`<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" role="img" aria-label="Satellitenhimmel">`);
parts.push(`<rect width="${size}" height="${size}" fill="#ffffff" />`);
parts.push(`<circle cx="${toSvgNumber(cx)}" cy="${toSvgNumber(cy)}" r="${toSvgNumber(radius)}" fill="${palette.skyFill}" />`);
[radius, radius*(2/3), radius*(1/3)].forEach((r, i) => {
parts.push(`<circle cx="${toSvgNumber(cx)}" cy="${toSvgNumber(cy)}" r="${toSvgNumber(r)}" fill="none" stroke="${palette.grid}" stroke-width="1" />`);
parts.push(`<text x="${toSvgNumber(cx + 6)}" y="${toSvgNumber(cy - r + 14)}" fill="${palette.gridText}" font-family="Segoe UI, Arial, sans-serif" font-size="12">${i===0?0:i===1?30:60}°</text>`);
});
parts.push(`<line x1="${toSvgNumber(cx)}" y1="${toSvgNumber(cy - radius)}" x2="${toSvgNumber(cx)}" y2="${toSvgNumber(cy + radius)}" stroke="${palette.grid}" stroke-width="1" />`);
parts.push(`<line x1="${toSvgNumber(cx - radius)}" y1="${toSvgNumber(cy)}" x2="${toSvgNumber(cx + radius)}" y2="${toSvgNumber(cy)}" stroke="${palette.grid}" stroke-width="1" />`);
parts.push(`<text x="${toSvgNumber(cx)}" y="${toSvgNumber(cy - radius + 18)}" fill="${palette.cardinal}" font-family="Segoe UI, Arial, sans-serif" font-size="14" font-weight="700" text-anchor="middle">N</text>`);
parts.push(`<text x="${toSvgNumber(cx)}" y="${toSvgNumber(cy + radius - 10)}" fill="${palette.cardinal}" font-family="Segoe UI, Arial, sans-serif" font-size="14" font-weight="700" text-anchor="middle">S</text>`);
parts.push(`<text x="${toSvgNumber(cx - radius + 14)}" y="${toSvgNumber(cy + 5)}" fill="${palette.cardinal}" font-family="Segoe UI, Arial, sans-serif" font-size="14" font-weight="700" text-anchor="middle">${mirrorEastWest ? 'O' : 'W'}</text>`);
parts.push(`<text x="${toSvgNumber(cx + radius - 14)}" y="${toSvgNumber(cy + 5)}" fill="${palette.cardinal}" font-family="Segoe UI, Arial, sans-serif" font-size="14" font-weight="700" text-anchor="middle">${mirrorEastWest ? 'W' : 'O'}</text>`);
parts.push(`<text x="${toSvgNumber(cx)}" y="${toSvgNumber(cy - 8)}" fill="${palette.cardinal}" font-family="Segoe UI, Arial, sans-serif" font-size="14" font-weight="700" text-anchor="middle">Zenit</text>`);
for (const line of CONSTELLATION_LINES) {
if (!Array.isArray(line) || line.length < 2) continue;
let segment = [];
for (const point of line) {
const pos = equatorialToHorizontal(point.ra, point.dec, lat, lon, starTime);
if (pos.altDeg < 0) {
if (segment.length > 1) {
parts.push(`<polyline points="${segment.map(p => `${toSvgNumber(p.x)},${toSvgNumber(p.y)}`).join(' ')}" fill="none" stroke="${palette.constellationLine}" stroke-width="1.15" stroke-linecap="round" stroke-linejoin="round" />`);
}
segment = [];
continue;
}
segment.push(projectPoint(pos.azimuthDeg, pos.altDeg, radius, cx, cy));
}
if (segment.length > 1) {
parts.push(`<polyline points="${segment.map(p => `${toSvgNumber(p.x)},${toSvgNumber(p.y)}`).join(' ')}" fill="none" stroke="${palette.constellationLine}" stroke-width="1.15" stroke-linecap="round" stroke-linejoin="round" />`);
}
}
if (lastShowStars) {
const occupiedLabelRects = [];
for (const star of BRIGHT_STARS) {
const pos = equatorialToHorizontal(star.ra, star.dec, lat, lon, starTime);
if (pos.altDeg < 0) continue;
const pt = projectPoint(pos.azimuthDeg, pos.altDeg, radius, cx, cy);
const starRadius = Math.max(1.0, Math.min(5.0, 4.5 - star.mag*1.0));
const alpha = Math.max(0.5, Math.min(0.95, 0.95 - star.mag*0.12));
parts.push(`<circle cx="${toSvgNumber(pt.x)}" cy="${toSvgNumber(pt.y)}" r="${toSvgNumber(starRadius)}" fill="${palette.star(alpha)}" />`);
if (star.name && star.mag <= 2.0) {
const lx = pt.x + starRadius + 3;
const ly = pt.y + 3;
const lw = estimateTextWidth(star.name, 10);
parts.push(`<text x="${toSvgNumber(lx)}" y="${toSvgNumber(ly)}" fill="${palette.starLabel}" font-family="Segoe UI, Arial, sans-serif" font-size="10">${escapeXml(star.name)}</text>`);
occupiedLabelRects.push(buildTextRect(lx, ly, lw, 10));
}
}
for (const con of CONSTELLATION_LABELS) {
const pos = equatorialToHorizontal(con.ra, con.dec, lat, lon, starTime);
if (pos.altDeg < 0) continue;
const pt = projectPoint(pos.azimuthDeg, pos.altDeg, radius, cx, cy);
const lw = estimateTextWidth(con.label, 11);
const pl = findFreeLabelPlacement(pt.x, pt.y, lw, 11, occupiedLabelRects, cx, cy, radius, 'center');
if (!pl) continue;
parts.push(`<text x="${toSvgNumber(pl.x)}" y="${toSvgNumber(pl.y)}" fill="${palette.constellationLabel}" font-family="Segoe UI, Arial, sans-serif" font-size="11">${escapeXml(con.label)}</text>`);
occupiedLabelRects.push(pl.rect);
}
}
for (const item of lastSkyData.filter(entry => entry?.pass)) {
const pass = item.pass;
const pts = pass.path.filter(p => p.elevationDeg >= 0);
if (pts.length < 2) continue;
const trackColor = invertSky ? 'rgba(8,10,14,0.96)' : (PYTHON_RESULTS[0]?.color ?? '#f0d990');
for (let i = 0; i < pts.length - 1; i++) {
const p0 = projectPoint(pts[i].azimuthDeg, pts[i].elevationDeg, radius, cx, cy);
const p1 = projectPoint(pts[i + 1].azimuthDeg, pts[i + 1].elevationDeg, radius, cx, cy);
if (pts[i].shadow) {
parts.push(`<line x1="${toSvgNumber(p0.x)}" y1="${toSvgNumber(p0.y)}" x2="${toSvgNumber(p1.x)}" y2="${toSvgNumber(p1.y)}" stroke="${palette.shadowLine}" stroke-width="1" stroke-linecap="round" />`);
} else {
parts.push(`<line x1="${toSvgNumber(p0.x)}" y1="${toSvgNumber(p0.y)}" x2="${toSvgNumber(p1.x)}" y2="${toSvgNumber(p1.y)}" stroke="${trackColor}" stroke-width="${invertSky ? '3.2' : '2.5'}" stroke-linecap="round" />`);
}
}
const startPt = projectPoint(pts[0].azimuthDeg, pts[0].elevationDeg, radius, cx, cy);
const endPt = projectPoint(pts[pts.length - 1].azimuthDeg, pts[pts.length - 1].elevationDeg, radius, cx, cy);
parts.push(`<circle cx="${toSvgNumber(startPt.x)}" cy="${toSvgNumber(startPt.y)}" r="3.5" fill="${trackColor}" />`);
parts.push(`<circle cx="${toSvgNumber(endPt.x)}" cy="${toSvgNumber(endPt.y)}" r="3.5" fill="${trackColor}" />`);
parts.push(buildSvgEndpointLabel(startPt, formatTime(pass.aosTime), trackColor, cx, cy, radius));
parts.push(buildSvgEndpointLabel(endPt, formatTime(pass.losTime), trackColor, cx, cy, radius));
if (isPeakVisible(pass)) {
const peakPt = projectPoint(pass.peakAzimuth, pass.peakElevation, radius, cx, cy);
parts.push(`<circle cx="${toSvgNumber(peakPt.x)}" cy="${toSvgNumber(peakPt.y)}" r="4.5" fill="${trackColor}" stroke="${invertSky ? 'rgba(248,248,244,0.95)' : 'rgba(8,12,20,0.92)'}" stroke-width="1.5" />`);
parts.push(buildSvgEndpointLabel(peakPt, formatTime(pass.peakTime), trackColor, cx, cy, radius));
}
if (pass.shadowEntry) {
const sp = projectPoint(pass.shadowEntry.azimuthDeg, pass.shadowEntry.elevationDeg, radius, cx, cy);
parts.push(`<circle cx="${toSvgNumber(sp.x)}" cy="${toSvgNumber(sp.y)}" r="4" fill="${palette.shadowMarker}" stroke="${palette.shadowMarkerStroke}" stroke-width="1.5" />`);
parts.push(buildSvgEndpointLabel(sp, `ES ${formatTime(pass.shadowEntry.time)}`, palette.shadowLabel, cx, cy, radius));
}
}
parts.push(`</svg>`);
return parts.join('');
} finally {
exportOpaqueSky = prevExport;
}
}
async function exportSatpassSvg() {
try {
const markup = generateSatpassSvgMarkup();
const blob = new Blob([markup], { type: 'image/svg+xml;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = createSatpassExportFileName('svg');
document.body.appendChild(a);
a.click();
a.remove();
setTimeout(() => URL.revokeObjectURL(url), 2000);
} catch (err) {
console.error(err);
statusEl.textContent = 'SVG-Export fehlgeschlagen.';
}
}
async function exportSatpassPng() {
@@ -1193,7 +1403,8 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
const ec = document.createElement('canvas');
ec.width = canvas.width; ec.height = canvas.height;
const ectx = ec.getContext('2d');
ectx.fillStyle = '#ffffff'; ectx.fillRect(0,0,ec.width,ec.height);
ectx.fillStyle = EXPORT_BACKGROUND_COLOR;
ectx.fillRect(0,0,ec.width,ec.height);
ectx.drawImage(canvas, 0, 0);
try {
await ensureExportLogoLoaded();
@@ -1204,7 +1415,7 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
const blob = await canvasToBlob(ec, 'image/png');
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url; a.download = createSatpassExportFileName();
a.href = url; a.download = createSatpassExportFileName('png');
document.body.appendChild(a); a.click(); a.remove();
setTimeout(() => URL.revokeObjectURL(url), 2000);
} catch (err) {
@@ -1224,10 +1435,19 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
invertBtn.classList.toggle('is-active', invertSky);
drawSky(lastSkyData, lastReferenceTime, lastShowStars);
});
mirrorBtn?.addEventListener('click', () => {
mirrorEastWest = !mirrorEastWest;
updateMirrorButton();
drawSky(lastSkyData, lastReferenceTime, lastShowStars);
});
exportBtn?.addEventListener('click', () => exportSatpassPng().catch(err => {
console.error(err); statusEl.textContent = 'PNG-Export fehlgeschlagen.';
}));
exportSvgBtn?.addEventListener('click', () => exportSatpassSvg().catch(err => {
console.error(err); statusEl.textContent = 'SVG-Export fehlgeschlagen.';
}));
updateMirrorButton();
buildDropdowns();
drawSky([], null, false);
</script>