Update index.html
Browse files- index.html +42 -8
index.html
CHANGED
|
@@ -424,11 +424,35 @@ document.getElementById('theme-toggle').addEventListener('click', e=>{
|
|
| 424 |
if(cumChart) generate();
|
| 425 |
if(detailChart) loadDetailChart(region);
|
| 426 |
if(monthlyLoadedRegion) loadMonthlyClimate(region);
|
|
|
|
| 427 |
});
|
| 428 |
document.getElementById('lang-toggle').addEventListener('click', e=>{
|
| 429 |
const btn = e.target.closest('.toggle-btn'); if(!btn) return;
|
| 430 |
currentLang = btn.dataset.lang;
|
| 431 |
applyI18n();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 432 |
});
|
| 433 |
|
| 434 |
/* ============================= CHART THEME COLORS ============================= */
|
|
@@ -743,11 +767,22 @@ async function fetchRecent30(region){
|
|
| 743 |
}
|
| 744 |
function dayOfYearOf(d){ const start=new Date(d.getFullYear(),0,1); let doy=Math.floor((d-start)/86400000)+1; return doy>365?365:doy; }
|
| 745 |
function computeNormalForDates(byYear, dates, excludeYear){
|
|
|
|
|
|
|
|
|
|
|
|
|
| 746 |
const years = Object.keys(byYear).map(Number).filter(y=>y!==excludeYear);
|
| 747 |
-
|
| 748 |
-
|
| 749 |
-
const
|
| 750 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 751 |
});
|
| 752 |
}
|
| 753 |
async function buildRain30(region){
|
|
@@ -760,10 +795,9 @@ async function buildRain30(region){
|
|
| 760 |
const times = recent.time.slice(0,-1);
|
| 761 |
const precs = recent.precipitation_sum.slice(0,-1).map(v=>v==null?0:v);
|
| 762 |
const dates = times.map(s=>new Date(s+'T00:00:00'));
|
| 763 |
-
const
|
| 764 |
-
let accA=0
|
| 765 |
const actualCum = precs.map(v=>{ accA+=v; return accA; });
|
| 766 |
-
const normalCum = normalsDaily.map(v=>{ accN+=v; return accN; });
|
| 767 |
const above = actualCum.map((v,i)=>Math.max(v, normalCum[i]));
|
| 768 |
const below = actualCum.map((v,i)=>Math.min(v, normalCum[i]));
|
| 769 |
const months = MONTHS[currentLang];
|
|
@@ -1071,4 +1105,4 @@ applyI18n();
|
|
| 1071 |
})();
|
| 1072 |
</script>
|
| 1073 |
</body>
|
| 1074 |
-
</html>
|
|
|
|
| 424 |
if(cumChart) generate();
|
| 425 |
if(detailChart) loadDetailChart(region);
|
| 426 |
if(monthlyLoadedRegion) loadMonthlyClimate(region);
|
| 427 |
+
if(window.parent !== window) window.parent.postMessage({type:'dashTheme', theme: currentTheme}, '*');
|
| 428 |
});
|
| 429 |
document.getElementById('lang-toggle').addEventListener('click', e=>{
|
| 430 |
const btn = e.target.closest('.toggle-btn'); if(!btn) return;
|
| 431 |
currentLang = btn.dataset.lang;
|
| 432 |
applyI18n();
|
| 433 |
+
if(window.parent !== window) window.parent.postMessage({type:'dashLang', lang: currentLang}, '*');
|
| 434 |
+
});
|
| 435 |
+
|
| 436 |
+
/* ---- respond to lang/theme commands from a hosting portal page (if embedded in an iframe) ---- */
|
| 437 |
+
window.addEventListener('message', ev=>{
|
| 438 |
+
const msg = ev.data || {};
|
| 439 |
+
if(msg.type === 'setLang' && msg.lang && I18N[msg.lang] && msg.lang !== currentLang){
|
| 440 |
+
currentLang = msg.lang;
|
| 441 |
+
applyI18n();
|
| 442 |
+
}
|
| 443 |
+
if(msg.type === 'setTheme' && msg.theme){
|
| 444 |
+
// the portal supports 3 themes (navy/sky/light); this page only has light/dark — anything but "light" maps to dark
|
| 445 |
+
const mapped = msg.theme === 'light' ? 'light' : 'dark';
|
| 446 |
+
if(mapped !== currentTheme){
|
| 447 |
+
currentTheme = mapped;
|
| 448 |
+
document.documentElement.setAttribute('data-theme', currentTheme);
|
| 449 |
+
document.querySelectorAll('#theme-toggle .toggle-btn').forEach(b=>b.classList.toggle('active', b.dataset.themeVal===currentTheme));
|
| 450 |
+
const region = document.getElementById('sel-region').value;
|
| 451 |
+
if(cumChart) generate();
|
| 452 |
+
if(detailChart) loadDetailChart(region);
|
| 453 |
+
if(monthlyLoadedRegion) loadMonthlyClimate(region);
|
| 454 |
+
}
|
| 455 |
+
}
|
| 456 |
});
|
| 457 |
|
| 458 |
/* ============================= CHART THEME COLORS ============================= */
|
|
|
|
| 767 |
}
|
| 768 |
function dayOfYearOf(d){ const start=new Date(d.getFullYear(),0,1); let doy=Math.floor((d-start)/86400000)+1; return doy>365?365:doy; }
|
| 769 |
function computeNormalForDates(byYear, dates, excludeYear){
|
| 770 |
+
// Uses the same method as the cumulative chart: for each reference year, sum precip over the
|
| 771 |
+
// matching calendar-day window, then take the MEDIAN of those yearly totals — not a day-by-day
|
| 772 |
+
// average, which uses a different statistic (mean) and can silently disagree in sign with the
|
| 773 |
+
// cumulative chart on skewed rainfall distributions.
|
| 774 |
const years = Object.keys(byYear).map(Number).filter(y=>y!==excludeYear);
|
| 775 |
+
const doys = dates.map(d=>dayOfYearOf(d));
|
| 776 |
+
return doys.map((_, i)=>{
|
| 777 |
+
const yearlyTotals = years.map(y=>{
|
| 778 |
+
let total = 0;
|
| 779 |
+
for(let k=0;k<=i;k++){
|
| 780 |
+
const v = byYear[y].precip[doys[k]-1];
|
| 781 |
+
total += (v||0);
|
| 782 |
+
}
|
| 783 |
+
return total;
|
| 784 |
+
});
|
| 785 |
+
return percentile(yearlyTotals, 50) || 0;
|
| 786 |
});
|
| 787 |
}
|
| 788 |
async function buildRain30(region){
|
|
|
|
| 795 |
const times = recent.time.slice(0,-1);
|
| 796 |
const precs = recent.precipitation_sum.slice(0,-1).map(v=>v==null?0:v);
|
| 797 |
const dates = times.map(s=>new Date(s+'T00:00:00'));
|
| 798 |
+
const normalCum = computeNormalForDates(byYear, dates, new Date().getFullYear());
|
| 799 |
+
let accA=0;
|
| 800 |
const actualCum = precs.map(v=>{ accA+=v; return accA; });
|
|
|
|
| 801 |
const above = actualCum.map((v,i)=>Math.max(v, normalCum[i]));
|
| 802 |
const below = actualCum.map((v,i)=>Math.min(v, normalCum[i]));
|
| 803 |
const months = MONTHS[currentLang];
|
|
|
|
| 1105 |
})();
|
| 1106 |
</script>
|
| 1107 |
</body>
|
| 1108 |
+
</html>
|