signalk-mareas-ihm 2.1.3 → 2.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-info.json +3 -3
- package/dist/index.js +29 -3
- package/dist/mobile.html +212 -74
- package/package.json +1 -1
- package/public/assets/{index-Db9bTqdz.js → index-CoLpTGJ6.js} +3 -3
- package/public/index.html +1 -1
- package/public/mobile.html +212 -74
package/dist/build-info.json
CHANGED
package/dist/index.js
CHANGED
|
@@ -49,7 +49,7 @@ function isPositionValue(v) {
|
|
|
49
49
|
// timestamp + git hash so we can verify exactly which build is running on the Pi
|
|
50
50
|
// without ambiguity. ("¿Qué versión tengo deployada?" → /api/paths or landing.)
|
|
51
51
|
const PLUGIN_VERSION = esmRequire("../package.json").version;
|
|
52
|
-
const PLUGIN_REVISION = "
|
|
52
|
+
const PLUGIN_REVISION = "Rev317";
|
|
53
53
|
let _buildInfo = null;
|
|
54
54
|
try {
|
|
55
55
|
_buildInfo = esmRequire("./build-info.json");
|
|
@@ -3507,6 +3507,24 @@ export default function (app) {
|
|
|
3507
3507
|
}
|
|
3508
3508
|
});
|
|
3509
3509
|
expressApp.get("/signalk-mareas-ihm/navtiles/:z/:x/:y.png", async (req, res) => {
|
|
3510
|
+
/* Rev317: guard res.headersSent en todos los error paths. Si la conexión
|
|
3511
|
+
upstream falla MID-STREAM (después de pipe()), no se puede llamar a
|
|
3512
|
+
res.status/send porque ya se enviaron headers → "Cannot set headers
|
|
3513
|
+
after they are sent". Solución: solo enviar 502 si headersSent=false;
|
|
3514
|
+
en otro caso destruir el socket para cortar el stream colgado. */
|
|
3515
|
+
const sendFallback = () => {
|
|
3516
|
+
if (res.headersSent) {
|
|
3517
|
+
try {
|
|
3518
|
+
res.end();
|
|
3519
|
+
}
|
|
3520
|
+
catch { /* socket already closed */ }
|
|
3521
|
+
return;
|
|
3522
|
+
}
|
|
3523
|
+
try {
|
|
3524
|
+
res.status(502).set("Content-Type", "image/png").send(TRANSPARENT_PNG);
|
|
3525
|
+
}
|
|
3526
|
+
catch { /* ignore */ }
|
|
3527
|
+
};
|
|
3510
3528
|
try {
|
|
3511
3529
|
const { token, bearer } = await fetchChartTokens();
|
|
3512
3530
|
const z = req.params.z, x = req.params.x, y = req.params.y;
|
|
@@ -3519,16 +3537,24 @@ export default function (app) {
|
|
|
3519
3537
|
"Origin": "https://maps.garmin.com"
|
|
3520
3538
|
}
|
|
3521
3539
|
}, (tileResp) => {
|
|
3540
|
+
if (res.headersSent) {
|
|
3541
|
+
try {
|
|
3542
|
+
tileResp.destroy();
|
|
3543
|
+
}
|
|
3544
|
+
catch { }
|
|
3545
|
+
return;
|
|
3546
|
+
}
|
|
3522
3547
|
const ct = tileResp.headers["content-type"] || "image/png";
|
|
3523
3548
|
res.set("Content-Type", ct);
|
|
3524
3549
|
res.set("Cache-Control", "public, max-age=15552000, immutable");
|
|
3550
|
+
tileResp.on("error", sendFallback);
|
|
3525
3551
|
tileResp.pipe(res);
|
|
3526
3552
|
});
|
|
3527
|
-
tileReq.on("error",
|
|
3553
|
+
tileReq.on("error", sendFallback);
|
|
3528
3554
|
tileReq.end();
|
|
3529
3555
|
}
|
|
3530
3556
|
catch {
|
|
3531
|
-
|
|
3557
|
+
sendFallback();
|
|
3532
3558
|
}
|
|
3533
3559
|
});
|
|
3534
3560
|
// ═══════════════ SSE (Server-Sent Events) for multi-device sync ═══════════════
|
package/dist/mobile.html
CHANGED
|
@@ -755,6 +755,33 @@ body.mobile-ui #m-bottom-bar .m-bb-grade .ic{font-size:10px}
|
|
|
755
755
|
body.mobile-ui #m-bottom-bar .m-bb-grade #m-bb-grade{width:28px!important;height:28px!important;font-size:18px!important}
|
|
756
756
|
body.mobile-ui #m-bottom-bar #m-bb-pres-svg{width:50px!important;height:18px!important}
|
|
757
757
|
|
|
758
|
+
/* Rev315: estilos UNIFICADOS de back-button y título en TODAS las ventanas.
|
|
759
|
+
Estas clases utility se pueden aplicar a cualquier botón/título de un modal
|
|
760
|
+
para forzar la misma apariencia visual en todo el plugin. Color principal
|
|
761
|
+
var(--org) naranja, peso 900, tamaño 22px back y 28px título. */
|
|
762
|
+
.m-back-btn{
|
|
763
|
+
background:rgba(255,178,63,.15)!important;
|
|
764
|
+
border:1px solid var(--org)!important;
|
|
765
|
+
color:var(--org)!important;
|
|
766
|
+
font-size:18px!important;
|
|
767
|
+
font-weight:800!important;
|
|
768
|
+
padding:12px 18px!important;
|
|
769
|
+
border-radius:12px!important;
|
|
770
|
+
cursor:pointer!important;
|
|
771
|
+
min-height:48px!important;
|
|
772
|
+
display:inline-flex!important;align-items:center!important;gap:4px!important;
|
|
773
|
+
letter-spacing:-.2px!important;
|
|
774
|
+
}
|
|
775
|
+
.m-back-btn:active{opacity:.6!important;transform:scale(.95)!important}
|
|
776
|
+
.m-modal-title{
|
|
777
|
+
font-size:28px!important;
|
|
778
|
+
font-weight:900!important;
|
|
779
|
+
color:#fff!important;
|
|
780
|
+
letter-spacing:-.3px!important;
|
|
781
|
+
text-align:center!important;
|
|
782
|
+
margin:0!important;
|
|
783
|
+
}
|
|
784
|
+
|
|
758
785
|
/* === MODALES fullscreen Rev173: contenido MUCHO más grande, sin pinch, sin scroll horizontal === */
|
|
759
786
|
body.mobile-ui .popup-overlay{
|
|
760
787
|
align-items:stretch!important;justify-content:stretch!important;
|
|
@@ -790,19 +817,24 @@ body.mobile-ui .m-modal-hdr{
|
|
|
790
817
|
display:flex;align-items:center;justify-content:space-between;
|
|
791
818
|
padding:0 12px;
|
|
792
819
|
}
|
|
820
|
+
/* Rev315: m-back UNIFICADO (alineado con clase utility .m-back-btn). Ya no
|
|
821
|
+
necesita min-width:110px ni height fija — es más compacto y consistente
|
|
822
|
+
con todos los demás botones Back del proyecto (Anchor Calculation, etc). */
|
|
793
823
|
body.mobile-ui .m-modal-hdr .m-back{
|
|
794
824
|
background:rgba(255,178,63,.15);border:1px solid var(--org);color:var(--org);
|
|
795
|
-
font-size:
|
|
796
|
-
display:flex;align-items:center;gap:
|
|
797
|
-
border-radius:12px;
|
|
825
|
+
font-size:18px;font-weight:800;cursor:pointer;padding:12px 18px;
|
|
826
|
+
display:flex;align-items:center;gap:4px;min-height:48px;
|
|
827
|
+
border-radius:12px;letter-spacing:-.2px;
|
|
798
828
|
}
|
|
799
829
|
body.mobile-ui .m-modal-hdr .m-back:active{opacity:.6;transform:scale(.95)}
|
|
830
|
+
/* Rev315: título UNIFICADO 28px (antes 38px). Coherente con la clase
|
|
831
|
+
utility .m-modal-title y con todos los modales del proyecto. */
|
|
800
832
|
body.mobile-ui .m-modal-hdr .m-title{
|
|
801
|
-
flex:1;text-align:center;font-size:
|
|
833
|
+
flex:1;text-align:center;font-size:28px;font-weight:900;color:#fff;
|
|
802
834
|
overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
|
|
803
|
-
padding:0 8px;letter-spacing:-.
|
|
835
|
+
padding:0 8px;letter-spacing:-.3px;
|
|
804
836
|
}
|
|
805
|
-
body.mobile-ui .m-modal-hdr .m-close-spacer{min-width:
|
|
837
|
+
body.mobile-ui .m-modal-hdr .m-close-spacer{min-width:80px}
|
|
806
838
|
|
|
807
839
|
/* === Tipografía GIGANTE dentro de modales (legibilidad marítima de sol) ===
|
|
808
840
|
Rev183: subimos todo +2-4 px más. El usuario insiste en que todavía
|
|
@@ -1271,26 +1303,24 @@ body.mobile-ui #shelter-hours .sh-hr{flex:0 0 60px!important}
|
|
|
1271
1303
|
<!-- Rev296: botones izquierdos dentro de #m-leftbar (scroll vertical, mismo
|
|
1272
1304
|
estilo que #m-sidebar derecha). -->
|
|
1273
1305
|
<div id="m-leftbar">
|
|
1274
|
-
<button id="m-ham" title="Menú" onclick="document.getElementById('m-menu').classList.add('open')"><span class="ico">☰</span></button>
|
|
1275
|
-
<button id="m-cartas" title="Activar / desactivar audio" onclick="toggleAudioEnable&&toggleAudioEnable()">
|
|
1306
|
+
<button id="m-ham" data-i18n-title="lb_ham_tip" title="Menú" onclick="document.getElementById('m-menu').classList.add('open')"><span class="ico">☰</span></button>
|
|
1307
|
+
<button id="m-cartas" data-i18n-title="lb_audio_tip" title="Activar / desactivar audio" onclick="toggleAudioEnable&&toggleAudioEnable()">
|
|
1276
1308
|
<span class="ico" id="m-mute-icon">🔊</span>
|
|
1277
1309
|
<span class="lbl" data-i18n="m_silencio">Silencio</span>
|
|
1278
1310
|
</button>
|
|
1279
|
-
<button id="m-snooze" title="Snooze 5 min" onclick="m_toggleSnooze()">
|
|
1311
|
+
<button id="m-snooze" data-i18n-title="lb_snooze_tip" title="Snooze 5 min" onclick="m_toggleSnooze()">
|
|
1280
1312
|
<span class="ico" id="m-snooze-icon">💤</span>
|
|
1281
1313
|
<span class="lbl" id="m-snooze-lbl" data-i18n="m_snooze">Snooze</span>
|
|
1282
1314
|
</button>
|
|
1283
|
-
<button id="m-fav" title="Fondeo favorito" onclick="m_favClick()">
|
|
1315
|
+
<button id="m-fav" data-i18n-title="lb_fav_tip" title="Fondeo favorito" onclick="m_favClick()">
|
|
1284
1316
|
<span class="ico">❤</span>
|
|
1285
1317
|
<span class="lbl" data-i18n="m_favorito">Favorito</span>
|
|
1286
1318
|
</button>
|
|
1287
|
-
|
|
1288
|
-
(entre Favorito y KIP). Mismos handlers que los del sidebar derecho. -->
|
|
1289
|
-
<button id="m-lb-curvas" title="Curvas" onclick="openPopup('curvas-pop');fetchCurvas&&fetchCurvas()">
|
|
1319
|
+
<button id="m-lb-curvas" data-i18n-title="sb_curvas" title="Curvas" onclick="openPopup('curvas-pop');fetchCurvas&&fetchCurvas()">
|
|
1290
1320
|
<span class="ico">〰️</span>
|
|
1291
1321
|
<span class="lbl" data-i18n="sb_curvas">Curvas</span>
|
|
1292
1322
|
</button>
|
|
1293
|
-
<button id="m-lb-mareas" title="Mareas (TidesView)" onclick="m_openMareas&&m_openMareas()">
|
|
1323
|
+
<button id="m-lb-mareas" data-i18n-title="lb_mareas_tip" title="Mareas (TidesView)" onclick="m_openMareas&&m_openMareas()">
|
|
1294
1324
|
<span class="ico" style="display:inline-flex;align-items:center;justify-content:center">
|
|
1295
1325
|
<svg viewBox="0 0 44 44" width="30" height="30">
|
|
1296
1326
|
<rect x="2" y="2" width="40" height="14" rx="2" fill="#ffd966"/>
|
|
@@ -1302,10 +1332,10 @@ body.mobile-ui #shelter-hours .sh-hr{flex:0 0 60px!important}
|
|
|
1302
1332
|
</span>
|
|
1303
1333
|
<span class="lbl" data-i18n="sb_mareas">Mareas</span>
|
|
1304
1334
|
</button>
|
|
1305
|
-
<button id="m-kip" title="Abrir KIP dashboard" onclick="window.location.href = window.location.origin + '/@mxtommy/kip/'">
|
|
1335
|
+
<button id="m-kip" data-i18n-title="lb_kip_tip" title="Abrir KIP dashboard" onclick="window.location.href = window.location.origin + '/@mxtommy/kip/'">
|
|
1306
1336
|
<span class="ico" style="font-weight:900;letter-spacing:1px">KIP</span>
|
|
1307
1337
|
</button>
|
|
1308
|
-
<button id="m-fb" title="Abrir Freeboard-SK" onclick="window.location.href = window.location.origin + '/@signalk/freeboard-sk/'">
|
|
1338
|
+
<button id="m-fb" data-i18n-title="lb_fb_tip" title="Abrir Freeboard-SK" onclick="window.location.href = window.location.origin + '/@signalk/freeboard-sk/'">
|
|
1309
1339
|
<img src="assets/freeboard.png" alt="Freeboard"
|
|
1310
1340
|
style="width:42px;height:42px;object-fit:contain;background:#fff;border-radius:6px;padding:2px;display:block;box-sizing:border-box"/>
|
|
1311
1341
|
</button>
|
|
@@ -1357,7 +1387,7 @@ body.mobile-ui #shelter-hours .sh-hr{flex:0 0 60px!important}
|
|
|
1357
1387
|
<span class="vl" id="m-bb-wind-kt-txt">—<span style="font-size:11px;font-weight:700;opacity:.75;margin-left:2px">kts</span></span>
|
|
1358
1388
|
</div>
|
|
1359
1389
|
</div>
|
|
1360
|
-
<div class="it" data-i18n-title="bb_sonda_tip" title="Profundidad bajo quilla · Sonda" onclick="openPopup&&openPopup('sonda-pop')"><span class="lb" data-i18n="bb_sonda">Sonda</span><span class="vl" id="m-bb-sonda">—</span><span id="m-bb-sonda-status" style="display:none;font-size:9px;font-weight:800;margin-top:2px;text-align:center;line-height:1.1"></span></div>
|
|
1390
|
+
<div class="it" data-i18n-title="bb_sonda_tip" title="Profundidad bajo quilla · Sonda" onclick="openPopup&&openPopup('sonda-pop');typeof fetchSondaData==='function'&&fetchSondaData()"><span class="lb" data-i18n="bb_sonda">Sonda</span><span class="vl" id="m-bb-sonda">—</span><span id="m-bb-sonda-status" style="display:none;font-size:9px;font-weight:800;margin-top:2px;text-align:center;line-height:1.1"></span></div>
|
|
1361
1391
|
<div class="it" data-i18n-title="bb_cadena_tip" title="Cadena recomendada" onclick="m_openInfoModal&&m_openInfoModal()"><span class="lb" data-i18n="bb_cadena_rec">Cadena rec.</span><span class="vl" id="m-bb-cad">—</span></div>
|
|
1362
1392
|
<div class="it" data-i18n-title="bb_dist_tip" title="Distancia al ancla" onclick="m_openInfoModal&&m_openInfoModal()"><span class="lb" data-i18n="bb_dist_ancla">Dist. ancla</span><span class="vl" id="m-bb-dist">—</span></div>
|
|
1363
1393
|
<div class="it m-bb-graph" data-i18n-title="bb_presion_tip" title="Presión barométrica · línea naranja = AHORA · click → Meteo" onclick="toggleMeteo&&toggleMeteo()">
|
|
@@ -1561,18 +1591,20 @@ body.mobile-ui #shelter-hours .sh-hr{flex:0 0 60px!important}
|
|
|
1561
1591
|
</div>
|
|
1562
1592
|
<!-- Popup info histórico ola: leyenda colores + tabla detallada. -->
|
|
1563
1593
|
<div class="popup-overlay" id="wave-hist-info-pop" onclick="closePopup('wave-hist-info-pop')">
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
<
|
|
1568
|
-
<
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
<div style="text-align:center"><div style="width:100%;height:
|
|
1572
|
-
<div style="text-align:center"><div style="width:100%;height:
|
|
1573
|
-
<div style="text-align:center"><div style="width:100%;height:
|
|
1594
|
+
<!-- Rev316: ventana wave-hist-info ampliada (antes 620px / 88vh) y todo
|
|
1595
|
+
bilingüe. Estilo título alineado con resto de modales (.m-modal-title). -->
|
|
1596
|
+
<div class="popup-box" onclick="event.stopPropagation()" style="width:900px;max-width:96vw;max-height:92vh;overflow-y:auto;padding:28px 32px">
|
|
1597
|
+
<button onclick="closePopup('wave-hist-info-pop')" style="position:absolute;top:10px;right:14px;background:none;border:none;color:#aaa;font-size:28px;cursor:pointer;line-height:1">✕</button>
|
|
1598
|
+
<h4 style="margin:0 0 12px 0;font-size:24px;font-weight:900;color:#fff" data-i18n="sh_wh_title">Historial de olas — leyenda y detalle</h4>
|
|
1599
|
+
<div style="font-size:16px;color:#9ad;margin-bottom:18px" data-i18n="sh_wh_subtitle">Cada barra resume 15 minutos. Color = intensidad media medida por el IMU del barco. Altura = oscilación del bin.</div>
|
|
1600
|
+
<div style="display:grid;grid-template-columns:repeat(5,1fr);gap:10px;margin-bottom:22px">
|
|
1601
|
+
<div style="text-align:center"><div style="width:100%;height:22px;background:#3aa856;border-radius:3px"></div><div style="font-size:14px;color:#fff;margin-top:4px;font-weight:700" data-i18n="m_calma">Calma</div><div style="font-size:12px;color:#888"><0.5°</div></div>
|
|
1602
|
+
<div style="text-align:center"><div style="width:100%;height:22px;background:#a3c813;border-radius:3px"></div><div style="font-size:14px;color:#fff;margin-top:4px;font-weight:700" data-i18n="sh_wh_rizada">Rizada</div><div style="font-size:12px;color:#888">0.5–1.5°</div></div>
|
|
1603
|
+
<div style="text-align:center"><div style="width:100%;height:22px;background:#f3c812;border-radius:3px"></div><div style="font-size:14px;color:#fff;margin-top:4px;font-weight:700" data-i18n="sh_wh_moderada">Moderada</div><div style="font-size:12px;color:#888">1.5–3°</div></div>
|
|
1604
|
+
<div style="text-align:center"><div style="width:100%;height:22px;background:#f37812;border-radius:3px"></div><div style="font-size:14px;color:#fff;margin-top:4px;font-weight:700" data-i18n="sh_wh_agitada">Agitada</div><div style="font-size:12px;color:#888">3–6°</div></div>
|
|
1605
|
+
<div style="text-align:center"><div style="width:100%;height:22px;background:#e74c3c;border-radius:3px"></div><div style="font-size:14px;color:#fff;margin-top:4px;font-weight:700" data-i18n="sh_wh_fuerte">Fuerte</div><div style="font-size:12px;color:#888">>6°</div></div>
|
|
1574
1606
|
</div>
|
|
1575
|
-
<div id="wave-hist-info-table" style="font-size:
|
|
1607
|
+
<div id="wave-hist-info-table" style="font-size:15px;color:#cfd8dc"></div>
|
|
1576
1608
|
</div>
|
|
1577
1609
|
</div>
|
|
1578
1610
|
<!-- Rev79: popup explicacion del % de proteccion -->
|
|
@@ -1744,6 +1776,12 @@ body.mobile-ui #shelter-hours .sh-hr{flex:0 0 60px!important}
|
|
|
1744
1776
|
<div class="alarm-info"><b style="color:var(--red)" data-i18n="alarma_sonda">🌊 Alarma Sonda</b><br><small style="color:#aaa" data-i18n="alarma_sonda_desc">Riesgo de varada por profundidad</small><div class="alarm-st" id="alarm-sonda-st"></div></div>
|
|
1745
1777
|
<div class="alarm-sw"><input type="checkbox" id="chk-alarm-sonda" onclick="event.stopPropagation()" onchange="setAlarm('sonda',this.checked)"><label for="chk-alarm-sonda" onclick="event.stopPropagation()"></label></div>
|
|
1746
1778
|
</div>
|
|
1779
|
+
<!-- Rev316: nuevo selector "Mala condición climática" — alerta cuando
|
|
1780
|
+
la previsión meteo en las próximas 6h tiene viento > 25 kt o ola > 1.5 m. -->
|
|
1781
|
+
<div class="alarm-row" id="ar-weather" onclick="var c=document.getElementById('chk-alarm-weather');c.checked=!c.checked;setAlarm('weather',c.checked)">
|
|
1782
|
+
<div class="alarm-info"><b style="color:#ffb23f" data-i18n="alarma_meteo">⛈️ Mala condición climática</b><br><small style="color:#aaa" data-i18n="alarma_meteo_desc">Aviso si previsión 6h supera viento 25 kt u ola 1.5 m</small><div class="alarm-st" id="alarm-weather-st"></div></div>
|
|
1783
|
+
<div class="alarm-sw"><input type="checkbox" id="chk-alarm-weather" onclick="event.stopPropagation()" onchange="setAlarm('weather',this.checked)"><label for="chk-alarm-weather" onclick="event.stopPropagation()"></label></div>
|
|
1784
|
+
</div>
|
|
1747
1785
|
</div>
|
|
1748
1786
|
<div id="alarm-active-list" style="margin-top:6px"></div>
|
|
1749
1787
|
<!-- Rev184: orden correcto — debajo de alarmas va el VOLUMEN, luego
|
|
@@ -2023,6 +2061,54 @@ var _i18n={
|
|
|
2023
2061
|
sb_abrigo_tip:{es:'Previsión abrigo',en:'Shelter forecast'},
|
|
2024
2062
|
sb_alarmas_tip:{es:'Panel de alarmas',en:'Alarms panel'},
|
|
2025
2063
|
sb_fondeo_tip:{es:'Cálculo fondeo',en:'Anchorage calculator'},
|
|
2064
|
+
/* Rev315: meteo panel AHORA + row labels */
|
|
2065
|
+
met_viento:{es:'VIENTO',en:'WIND'},
|
|
2066
|
+
met_aire:{es:'AIRE',en:'AIR'},
|
|
2067
|
+
met_mar:{es:'MAR',en:'SEA'},
|
|
2068
|
+
met_resumen:{es:'Resumen',en:'Summary'},
|
|
2069
|
+
met_row_aire:{es:'Aire',en:'Air'},
|
|
2070
|
+
met_row_presion:{es:'Presión',en:'Pressure'},
|
|
2071
|
+
met_row_lluvia:{es:'Lluvia',en:'Rain'},
|
|
2072
|
+
met_row_tmar:{es:'T. Mar',en:'Sea T.'},
|
|
2073
|
+
met_row_ola:{es:'Ola',en:'Wave'},
|
|
2074
|
+
met_row_periodo:{es:'Período',en:'Period'},
|
|
2075
|
+
met_row_dirola:{es:'Dir ola',en:'Wave dir'},
|
|
2076
|
+
/* Rev315: shelter NOW box */
|
|
2077
|
+
sh_veleta:{es:'Veleta',en:'Vane'},
|
|
2078
|
+
sh_sensor:{es:'Sensor',en:'Sensor'},
|
|
2079
|
+
sh_racha:{es:'racha',en:'gust'},
|
|
2080
|
+
sh_aire:{es:'aire',en:'air'},
|
|
2081
|
+
sh_agua:{es:'agua',en:'water'},
|
|
2082
|
+
m_ahora_low:{es:'ahora',en:'now'},
|
|
2083
|
+
/* Rev315: labels flotantes visor (mapa) */
|
|
2084
|
+
m_viento:{es:'Viento',en:'Wind'},
|
|
2085
|
+
m_olas:{es:'Olas',en:'Waves'},
|
|
2086
|
+
m_mar_calma:{es:'Mar en calma',en:'Calm sea'},
|
|
2087
|
+
/* Rev316: wave-hist-info popup */
|
|
2088
|
+
sh_wh_title:{es:'Historial de olas — leyenda y detalle',en:'Wave history — legend & details'},
|
|
2089
|
+
sh_wh_subtitle:{es:'Cada barra resume 15 minutos. Color = intensidad media medida por el IMU del barco. Altura = oscilación del bin.',en:'Each bar summarises 15 minutes. Color = average intensity measured by boat IMU. Bar height = bin oscillation.'},
|
|
2090
|
+
sh_wh_no_data:{es:'No hay datos todavía. Echa el ancla y espera al menos 15 min para que aparezcan barras.',en:'No data yet. Drop anchor and wait at least 15 min for bars to appear.'},
|
|
2091
|
+
sh_wh_inicio:{es:'Inicio',en:'Start'},
|
|
2092
|
+
sh_wh_intensidad:{es:'Intensidad',en:'Intensity'},
|
|
2093
|
+
sh_wh_periodo:{es:'Período',en:'Period'},
|
|
2094
|
+
sh_wh_altura:{es:'Altura',en:'Height'},
|
|
2095
|
+
sh_wh_rms:{es:'RMS',en:'RMS'},
|
|
2096
|
+
sh_wh_rizada:{es:'Rizada',en:'Rippled'},
|
|
2097
|
+
sh_wh_moderada:{es:'Moderada',en:'Moderate'},
|
|
2098
|
+
sh_wh_agitada:{es:'Agitada',en:'Rough'},
|
|
2099
|
+
sh_wh_fuerte:{es:'Fuerte',en:'Strong'},
|
|
2100
|
+
sh_wh_actual:{es:'ACTUAL',en:'CURRENT'},
|
|
2101
|
+
/* Rev316: tooltips left bar */
|
|
2102
|
+
lb_ham_tip:{es:'Menú',en:'Menu'},
|
|
2103
|
+
lb_audio_tip:{es:'Activar / desactivar audio',en:'Enable / disable audio'},
|
|
2104
|
+
lb_snooze_tip:{es:'Snooze 5 min',en:'Snooze 5 min'},
|
|
2105
|
+
lb_fav_tip:{es:'Fondeo favorito',en:'Favourite anchorage'},
|
|
2106
|
+
lb_mareas_tip:{es:'Mareas (TidesView)',en:'Tides (TidesView)'},
|
|
2107
|
+
lb_kip_tip:{es:'Abrir KIP dashboard',en:'Open KIP dashboard'},
|
|
2108
|
+
lb_fb_tip:{es:'Abrir Freeboard-SK',en:'Open Freeboard-SK'},
|
|
2109
|
+
/* Rev316: alarma meteo */
|
|
2110
|
+
alarma_meteo:{es:'⛈️ Mala condición climática',en:'⛈️ Bad weather conditions'},
|
|
2111
|
+
alarma_meteo_desc:{es:'Aviso si previsión 6h supera viento 25 kt u ola 1.5 m',en:'Warning if 6h forecast exceeds wind 25 kt or wave 1.5 m'},
|
|
2026
2112
|
/* Rev313: modal guardar favorito */
|
|
2027
2113
|
fav_guardar_title:{es:'❤ Guardar fondeo favorito',en:'❤ Save favourite anchorage'},
|
|
2028
2114
|
fav_guardar_hint:{es:'Nombre del favorito (vacío = nombre automático por geolocalización):',en:'Favourite name (empty = auto name by geolocation):'},
|
|
@@ -4797,25 +4883,32 @@ function _renderWaveHistInfo(){
|
|
|
4797
4883
|
var bins=Array.isArray(j.bins)?j.bins.slice():[];
|
|
4798
4884
|
if(j.currentBin)bins.push(j.currentBin);
|
|
4799
4885
|
if(bins.length===0){
|
|
4800
|
-
el.
|
|
4886
|
+
var _noDataMsg=(typeof T==='function')?T('sh_wh_no_data','No hay datos todavía. Echa el ancla y espera al menos 15 min para que aparezcan barras.'):'No hay datos todavía. Echa el ancla y espera al menos 15 min para que aparezcan barras.';
|
|
4887
|
+
el.innerHTML='<div style="color:#888;text-align:center;padding:12px">'+_noDataMsg+'</div>';
|
|
4801
4888
|
return;
|
|
4802
4889
|
}
|
|
4803
|
-
/*
|
|
4804
|
-
var
|
|
4805
|
-
|
|
4890
|
+
/* Rev316: headers i18n. */
|
|
4891
|
+
var _tw=function(k,fb){ return (typeof T==='function')?T(k,fb):fb; };
|
|
4892
|
+
var _hInicio=_tw('sh_wh_inicio','Inicio'), _hInten=_tw('sh_wh_intensidad','Intensidad'),
|
|
4893
|
+
_hPer=_tw('sh_wh_periodo','Período'), _hAlt=_tw('sh_wh_altura','Altura'), _hRms=_tw('sh_wh_rms','RMS');
|
|
4894
|
+
var _capCalma=_tw('m_calma','Calma'), _capRiz=_tw('sh_wh_rizada','Rizada'),
|
|
4895
|
+
_capMod=_tw('sh_wh_moderada','Moderada'), _capAgi=_tw('sh_wh_agitada','Agitada'), _capFue=_tw('sh_wh_fuerte','Fuerte');
|
|
4896
|
+
var _capMap={calma:_capCalma,rizada:_capRiz,moderada:_capMod,agitada:_capAgi,fuerte:_capFue};
|
|
4897
|
+
var _badgeActual=_tw('sh_wh_actual','ACTUAL');
|
|
4898
|
+
var rows=['<div style="display:grid;grid-template-columns:80px 1fr 60px 70px 90px;gap:4px;padding:6px 0;border-bottom:1px solid rgba(255,255,255,.18);font-weight:800;color:#9ad;font-size:13px;letter-spacing:.8px;text-transform:uppercase">',
|
|
4899
|
+
'<span>'+_hInicio+'</span>','<span>'+_hInten+'</span>','<span style="text-align:right">'+_hPer+'</span>','<span style="text-align:right">'+_hAlt+'</span>','<span style="text-align:right">'+_hRms+'</span>',
|
|
4806
4900
|
'</div>'];
|
|
4807
|
-
/* Más recientes arriba */
|
|
4808
4901
|
var sorted=bins.slice().reverse();
|
|
4809
4902
|
for(var i=0;i<sorted.length;i++){
|
|
4810
4903
|
var b=sorted[i];
|
|
4811
4904
|
var d=new Date(b.binStartMs);
|
|
4812
4905
|
var hhmm=('0'+d.getHours()).slice(-2)+':'+('0'+d.getMinutes()).slice(-2);
|
|
4813
4906
|
var color=_WAVE_INT_COLORS[b.intensityMode]||'#888';
|
|
4814
|
-
var intCap=(b.intensityMode||'').charAt(0).toUpperCase()+(b.intensityMode||'').slice(1);
|
|
4907
|
+
var intCap=_capMap[b.intensityMode||'']||((b.intensityMode||'').charAt(0).toUpperCase()+(b.intensityMode||'').slice(1));
|
|
4815
4908
|
var per=(typeof b.periodAvgSec==='number'&&b.periodAvgSec>0)?b.periodAvgSec.toFixed(1)+'s':'—';
|
|
4816
4909
|
var alt=(typeof b.heightSigMaxM==='number'&&b.heightSigMaxM>0)?b.heightSigMaxM.toFixed(2).replace('.',',')+' m':'<5cm';
|
|
4817
4910
|
var rms=(typeof b.rmsMaxDeg==='number'&&b.rmsMaxDeg>0)?b.rmsMaxDeg.toFixed(2)+'°':'—';
|
|
4818
|
-
var curBadge=b.isCurrent?' <span style="font-size:10px;color:#ffeb3b;font-weight:800;margin-left:4px">
|
|
4911
|
+
var curBadge=b.isCurrent?' <span style="font-size:10px;color:#ffeb3b;font-weight:800;margin-left:4px">'+_badgeActual+'</span>':'';
|
|
4819
4912
|
rows.push('<div style="display:grid;grid-template-columns:80px 1fr 60px 70px 90px;gap:4px;padding:5px 0;border-bottom:1px solid rgba(255,255,255,.06);align-items:center">'+
|
|
4820
4913
|
'<span style="color:#fff;font-weight:700">'+hhmm+'</span>'+
|
|
4821
4914
|
'<span><span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:'+color+';margin-right:6px;vertical-align:middle"></span>'+intCap+curBadge+'</span>'+
|
|
@@ -5324,8 +5417,11 @@ function _shelterRenderPopup(){
|
|
|
5324
5417
|
temperaturas. Sin sensor → forecast Open-Meteo (sin badge).
|
|
5325
5418
|
_BADGE_VELETA y _BADGE_SENSOR son verdes (#3aa856), mismo tono que
|
|
5326
5419
|
el badge "IMU" pre-existente para coherencia visual. */
|
|
5327
|
-
|
|
5328
|
-
var
|
|
5420
|
+
/* Rev315: badges Veleta/Sensor i18n. */
|
|
5421
|
+
var _badgeVeletaTxt=(typeof T==='function')?T('sh_veleta','Veleta'):'Veleta';
|
|
5422
|
+
var _badgeSensorTxt=(typeof T==='function')?T('sh_sensor','Sensor'):'Sensor';
|
|
5423
|
+
var _BADGE_VELETA=' <span style="color:#3aa856;font-size:11px;font-weight:800;margin-left:4px">'+_badgeVeletaTxt+'</span>';
|
|
5424
|
+
var _BADGE_SENSOR=' <span style="color:#3aa856;font-size:10px;font-weight:800;margin-left:3px">'+_badgeSensorTxt+'</span>';
|
|
5329
5425
|
var _useSensorWind=isNow&&_boatWind;
|
|
5330
5426
|
var _useSensorEnv=isNow&&_envSensors;
|
|
5331
5427
|
var stWindKt,stDirDeg,windBadge;
|
|
@@ -5481,7 +5577,7 @@ function _shelterRenderPopup(){
|
|
|
5481
5577
|
var axis=
|
|
5482
5578
|
'<text x="'+x_24h.toFixed(1)+'" y="'+(H-6)+'" text-anchor="start" fill="rgba(255,255,255,.78)" font-size="13" font-weight="700">-24 h</text>'+
|
|
5483
5579
|
'<text x="'+x_12h.toFixed(1)+'" y="'+(H-6)+'" text-anchor="middle" fill="rgba(255,255,255,.65)" font-size="13" font-weight="700">-12 h</text>'+
|
|
5484
|
-
'<text x="'+nowX.toFixed(1)+'" y="'+(H-6)+'" text-anchor="middle" fill="#ffeb3b" font-size="13" font-weight="900">ahora</text>'+
|
|
5580
|
+
'<text x="'+nowX.toFixed(1)+'" y="'+(H-6)+'" text-anchor="middle" fill="#ffeb3b" font-size="13" font-weight="900">'+((typeof T==='function')?T('m_ahora_low','ahora'):'ahora')+'</text>'+
|
|
5485
5581
|
'<text x="'+x_max.toFixed(1)+'" y="'+(H-6)+'" text-anchor="end" fill="rgba(255,235,59,.85)" font-size="13" font-weight="800">+'+maxFutureH+' h</text>'+
|
|
5486
5582
|
'<text x="2" y="'+(padTop+10)+'" text-anchor="start" fill="rgba(255,255,255,.6)" font-size="11" font-weight="700">'+Math.round(pMax)+'</text>'+
|
|
5487
5583
|
'<text x="2" y="'+(H-padBot-2)+'" text-anchor="start" fill="rgba(255,255,255,.6)" font-size="11" font-weight="700">'+Math.round(pMin)+'</text>';
|
|
@@ -5525,11 +5621,13 @@ function _shelterRenderPopup(){
|
|
|
5525
5621
|
/* Rev128: reorden infobox — wind, gust, wave, TEMPS, pressure-num, sparkline.
|
|
5526
5622
|
La sparkline queda al final para visual cleanliness (es el elemento más
|
|
5527
5623
|
alto y rompía si quedaba en medio). */
|
|
5624
|
+
/* Rev315: labels i18n (racha/aire/agua). */
|
|
5625
|
+
var _ts=function(k,fb){ return (typeof T==='function')?T(k,fb):fb; };
|
|
5528
5626
|
statusBody.innerHTML=
|
|
5529
5627
|
'<div>🌬 <b style="font-size:24px">'+Math.round(stWindKt)+' kt</b> <span style="color:#9ad">'+stDir+'</span>'+windBadge+'</div>'+
|
|
5530
|
-
'<div>💨 racha <b style="font-size:21px">'+Math.round(stGustKt)+' kt</b>'+gustBadge+'</div>'+
|
|
5628
|
+
'<div>💨 '+_ts('sh_racha','racha')+' <b style="font-size:21px">'+Math.round(stGustKt)+' kt</b>'+gustBadge+'</div>'+
|
|
5531
5629
|
'<div>'+waveLine+'</div>'+
|
|
5532
|
-
'<div>🌡 aire <b style="font-size:20px">'+tempA+'</b>'+tempABadge+' 🌊 agua <b style="font-size:20px">'+tempW+'</b>'+tempWBadge+'</div>'+
|
|
5630
|
+
'<div>🌡 '+_ts('sh_aire','aire')+' <b style="font-size:20px">'+tempA+'</b>'+tempABadge+' 🌊 '+_ts('sh_agua','agua')+' <b style="font-size:20px">'+tempW+'</b>'+tempWBadge+'</div>'+
|
|
5533
5631
|
pressureNumLine+
|
|
5534
5632
|
sparkRow;
|
|
5535
5633
|
/* Botón AHORA: resaltado si NO hay hora seleccionada */
|
|
@@ -5896,7 +5994,7 @@ function _wxMapMarkerUpdate(){
|
|
|
5896
5994
|
if(_wDeg!==null){
|
|
5897
5995
|
var wEdge=geoEdge(pos.lat,pos.lng,alarmR,_wDeg);
|
|
5898
5996
|
var wDirTxt=_wxC16?_wxC16(_wDeg):'';
|
|
5899
|
-
var wLabel='Viento '+(wDirTxt?wDirTxt+' ':'')+Math.round(_wKt)+' kt';
|
|
5997
|
+
var wLabel=((typeof T==='function')?T('m_viento','Viento'):'Viento')+' '+(wDirTxt?wDirTxt+' ':'')+Math.round(_wKt)+' kt';
|
|
5900
5998
|
var html=_wxMarkerHtml('#ffeb3b',wLabel,_wDeg,'wind',_labOffW);
|
|
5901
5999
|
var icon=L.divIcon({html:html,iconSize:[1,1],iconAnchor:[0,0],className:'wx-map-marker'});
|
|
5902
6000
|
if(_wxMapMarkers.wind){_wxMapMarkers.wind.setIcon(icon);_wxMapMarkers.wind.setLatLng(wEdge);_wxMapMarkers.wind.setZIndexOffset(300);if(!map.hasLayer(_wxMapMarkers.wind))_wxMapMarkers.wind.addTo(map);}
|
|
@@ -5910,13 +6008,12 @@ function _wxMapMarkerUpdate(){
|
|
|
5910
6008
|
var oEdge=geoEdge(pos.lat,pos.lng,alarmR,_vDirEff);
|
|
5911
6009
|
var oDirTxt=_wxC16?_wxC16(_vDirEff):'';
|
|
5912
6010
|
var oLabel;
|
|
6011
|
+
/* Rev315: labels "Olas" y "Mar en calma" i18n. */
|
|
6012
|
+
var _tw=function(k,fb){ return (typeof T==='function')?T(k,fb):fb; };
|
|
5913
6013
|
if(_boatWave){
|
|
5914
|
-
/* Rev128: si la ola es despreciable (intensidad calma), no marear con
|
|
5915
|
-
direcciones y períodos cortos sin sentido — decir simplemente
|
|
5916
|
-
"Mar en calma". Si hay movimiento medible, mostrar altura+dir+período. */
|
|
5917
6014
|
var _vbCalm=(_boatWave.intensity==='calma')&&(typeof _boatWave.heightSigM!=='number'||_boatWave.heightSigM<0.05);
|
|
5918
6015
|
if(_vbCalm){
|
|
5919
|
-
oLabel='Mar en calma';
|
|
6016
|
+
oLabel=_tw('m_mar_calma','Mar en calma');
|
|
5920
6017
|
}else{
|
|
5921
6018
|
var _vbPer=(typeof _boatWave.periodSec==='number'&&_boatWave.periodSec>0)?Math.round(_boatWave.periodSec)+'s':'';
|
|
5922
6019
|
var _vbMain;
|
|
@@ -5925,10 +6022,10 @@ function _wxMapMarkerUpdate(){
|
|
|
5925
6022
|
}else{
|
|
5926
6023
|
_vbMain=(_boatWave.intensity||'').charAt(0).toUpperCase()+(_boatWave.intensity||'').slice(1);
|
|
5927
6024
|
}
|
|
5928
|
-
oLabel='Olas '+(oDirTxt?oDirTxt+' ':'')+_vbMain+(_vbPer?' · '+_vbPer:'');
|
|
6025
|
+
oLabel=_tw('m_olas','Olas')+' '+(oDirTxt?oDirTxt+' ':'')+_vbMain+(_vbPer?' · '+_vbPer:'');
|
|
5929
6026
|
}
|
|
5930
6027
|
}else{
|
|
5931
|
-
oLabel='Olas '+(oDirTxt?oDirTxt+' ':'')+h.waveHeightM.toFixed(1).replace('.',',')+' m'+(h.wavePeriodSec>0?' · '+Math.round(h.wavePeriodSec)+' s':'');
|
|
6028
|
+
oLabel=_tw('m_olas','Olas')+' '+(oDirTxt?oDirTxt+' ':'')+h.waveHeightM.toFixed(1).replace('.',',')+' m'+(h.wavePeriodSec>0?' · '+Math.round(h.wavePeriodSec)+' s':'');
|
|
5932
6029
|
}
|
|
5933
6030
|
var oHtml=_wxMarkerHtml('#4dd0ff',oLabel,_vDirEff,'wave',_labOffV);
|
|
5934
6031
|
var oIcon=L.divIcon({html:oHtml,iconSize:[1,1],iconAnchor:[0,0],className:'wx-map-marker'});
|
|
@@ -6642,8 +6739,44 @@ function setAlarm(type,on){
|
|
|
6642
6739
|
document.getElementById('alarm-sonda-st').textContent=T('activa');document.getElementById('alarm-sonda-st').style.color='var(--grn)';
|
|
6643
6740
|
}
|
|
6644
6741
|
});
|
|
6742
|
+
}else if(type==='weather'){
|
|
6743
|
+
/* Rev316: alarma meteo — frontend-only por ahora. Estado en localStorage
|
|
6744
|
+
y check periódico contra _shelterCache.assessment.hours (próximas 6 h).
|
|
6745
|
+
Si viento > 25 kt u ola > 1.5 m → setAlarmActive('weather', true). */
|
|
6746
|
+
_alarmWeatherEnabled = on;
|
|
6747
|
+
localStorage.setItem('ihm-weather-alarm', on?'1':'0');
|
|
6748
|
+
var st = document.getElementById('alarm-weather-st');
|
|
6749
|
+
if (st) {
|
|
6750
|
+
st.textContent = on ? T('activa','activa') : T('desactivada','desactivada');
|
|
6751
|
+
st.style.color = on ? 'var(--grn)' : '#666';
|
|
6752
|
+
}
|
|
6753
|
+
if (!on) {
|
|
6754
|
+
try { setAlarmActive('weather', false); } catch(_){}
|
|
6755
|
+
} else {
|
|
6756
|
+
try { _checkWeatherAlarm(); } catch(_){}
|
|
6757
|
+
}
|
|
6645
6758
|
}
|
|
6646
6759
|
}
|
|
6760
|
+
/* Rev316: estado y comprobación de alarma meteo (frontend-only).
|
|
6761
|
+
Mira las próximas 6h de _shelterCache.assessment.hours buscando viento>25kt
|
|
6762
|
+
u ola>1.5m. Se invoca tras tocar el switch y cada 5 min mientras esté on. */
|
|
6763
|
+
var _alarmWeatherEnabled = (localStorage.getItem('ihm-weather-alarm')==='1');
|
|
6764
|
+
function _checkWeatherAlarm(){
|
|
6765
|
+
if (!_alarmWeatherEnabled) return;
|
|
6766
|
+
try {
|
|
6767
|
+
var asm = (typeof _shelterCache!=='undefined' && _shelterCache) ? _shelterCache.assessment : null;
|
|
6768
|
+
if (!asm || !asm.hours || !asm.hours.length) return;
|
|
6769
|
+
var bad = false;
|
|
6770
|
+
for (var i=0; i<Math.min(6, asm.hours.length); i++){
|
|
6771
|
+
var hr = asm.hours[i];
|
|
6772
|
+
var w = hr.windKt || hr.exposureKt || 0;
|
|
6773
|
+
var wave = hr.waveM || 0;
|
|
6774
|
+
if (w > 25 || wave > 1.5) { bad = true; break; }
|
|
6775
|
+
}
|
|
6776
|
+
setAlarmActive('weather', bad);
|
|
6777
|
+
} catch(_){}
|
|
6778
|
+
}
|
|
6779
|
+
setInterval(_checkWeatherAlarm, 5*60*1000);
|
|
6647
6780
|
/* ══════════════ CALC SONDA ══════════════ */
|
|
6648
6781
|
/* ═══ SHARED INPUT STYLE (touch-friendly, wide for fingers) ═══ */
|
|
6649
6782
|
/* ═══ SHARED INPUT STYLE (touch-friendly) ═══ */
|
|
@@ -8051,13 +8184,17 @@ function m_syncBottomBarGraphs(){
|
|
|
8051
8184
|
var v = srcVal.textContent.trim();
|
|
8052
8185
|
if (v && v !== '—') dstVal.textContent = v;
|
|
8053
8186
|
}
|
|
8054
|
-
/* Grade del abrigo: A=verde, B=verde claro, C=naranja, D=naranja oscuro, F=rojo
|
|
8187
|
+
/* Grade del abrigo: A=verde, B=verde claro, C=naranja, D=naranja oscuro, F=rojo.
|
|
8188
|
+
Rev316: el % del donut leía un mapa hardcoded por letra (A=100, B=80, ...)
|
|
8189
|
+
que NO coincidía con el % real del shelter modal. Ahora lee el valor REAL
|
|
8190
|
+
(asm.scorePercent) del _shelterCache. Si no hay cache aún caemos al mapa
|
|
8191
|
+
como antes. */
|
|
8055
8192
|
var srcGrade = document.getElementById('wx-shelter-grade');
|
|
8056
8193
|
var dstGrade = document.getElementById('m-bb-grade');
|
|
8057
8194
|
var dstDonut = document.getElementById('m-bb-donut');
|
|
8058
8195
|
var dstDonutText = document.getElementById('m-bb-donut-text');
|
|
8059
8196
|
var gradeColors = { 'A':'#3aa856','B':'#9ed02e','C':'#ffb23f','D':'#ff7043','F':'#f44336' };
|
|
8060
|
-
var
|
|
8197
|
+
var donutPctFallback = { 'A':100, 'B':80, 'C':60, 'D':40, 'F':20 };
|
|
8061
8198
|
if (srcGrade && dstGrade){
|
|
8062
8199
|
var g = (srcGrade.textContent.trim() || '—').toUpperCase();
|
|
8063
8200
|
if (g && g !== '—' && g !== '?') {
|
|
@@ -8065,15 +8202,19 @@ function m_syncBottomBarGraphs(){
|
|
|
8065
8202
|
var bgColor = gradeColors[g] || 'rgba(120,120,120,.35)';
|
|
8066
8203
|
dstGrade.style.background = bgColor;
|
|
8067
8204
|
dstGrade.style.color = '#fff';
|
|
8205
|
+
/* Pct real del cache shelter; fallback al mapa por letra si no hay. */
|
|
8206
|
+
var realPct = null;
|
|
8207
|
+
try {
|
|
8208
|
+
var _asm = (typeof _shelterCache !== 'undefined' && _shelterCache) ? _shelterCache.assessment : null;
|
|
8209
|
+
if (_asm && typeof _asm.scorePercent === 'number') realPct = Math.round(_asm.scorePercent);
|
|
8210
|
+
} catch(_){}
|
|
8211
|
+
if (realPct === null) realPct = donutPctFallback[g] || 0;
|
|
8068
8212
|
if (dstDonut) {
|
|
8069
|
-
|
|
8070
|
-
dstDonut.setAttribute('stroke-dasharray', pct + ',100');
|
|
8213
|
+
dstDonut.setAttribute('stroke-dasharray', realPct + ',100');
|
|
8071
8214
|
dstDonut.setAttribute('stroke', bgColor);
|
|
8072
8215
|
}
|
|
8073
|
-
/* Rev286: porcentaje (con %) dentro del donut, no letra. */
|
|
8074
8216
|
if (dstDonutText) {
|
|
8075
|
-
|
|
8076
|
-
dstDonutText.textContent = p + '%';
|
|
8217
|
+
dstDonutText.textContent = realPct + '%';
|
|
8077
8218
|
}
|
|
8078
8219
|
}
|
|
8079
8220
|
}
|
|
@@ -9033,12 +9174,14 @@ function m_renderMeteoWindy(d, seaD){
|
|
|
9033
9174
|
var nowColW = (nowWindKt!=null) ? wColor(nowWindKt) : '#fff';
|
|
9034
9175
|
var nowColG = (nowGustKt!=null) ? wColor(nowGustKt) : '#fff';
|
|
9035
9176
|
/* Rev238: panel AHORA — sin iconos en labels (limpio). */
|
|
9177
|
+
/* Rev315: AHORA panel — labels i18n. */
|
|
9178
|
+
var _tn = function(k,fb){ return (typeof T==='function')?T(k,fb):fb; };
|
|
9036
9179
|
var nowPanelHtml =
|
|
9037
9180
|
'<div style="display:flex;flex-direction:column;height:100%;background:var(--bg);border:3px solid var(--org);box-sizing:border-box">' +
|
|
9038
|
-
'<div style="background:var(--org);color:#000;font-size:22px;font-weight:900;text-align:center;padding:4px 4px;letter-spacing:2px;line-height:1">AHORA</div>' +
|
|
9181
|
+
'<div style="background:var(--org);color:#000;font-size:22px;font-weight:900;text-align:center;padding:4px 4px;letter-spacing:2px;line-height:1">'+_tn('m_ahora','AHORA')+'</div>' +
|
|
9039
9182
|
'<div style="display:grid;grid-template-columns:1fr 1fr;grid-template-rows:1fr 1fr;flex:1;gap:0;color:#fff;text-align:center">' +
|
|
9040
9183
|
'<div style="border-right:1px solid rgba(255,255,255,.4);border-bottom:1px solid rgba(255,255,255,.4);padding:4px 4px;display:flex;flex-direction:column;justify-content:center">' +
|
|
9041
|
-
'<div style="font-size:16px;color:var(--org);font-weight:700;letter-spacing:1px;line-height:1">VIENTO'+srcBadge(srcWind)+'</div>' +
|
|
9184
|
+
'<div style="font-size:16px;color:var(--org);font-weight:700;letter-spacing:1px;line-height:1">'+_tn('met_viento','VIENTO').toUpperCase()+srcBadge(srcWind)+'</div>' +
|
|
9042
9185
|
'<div style="font-size:32px;font-weight:900;color:#fff;line-height:1.05;display:flex;align-items:center;justify-content:center;gap:4px">'+
|
|
9043
9186
|
'<span>'+fmtKt(nowWindKt)+'</span>'+
|
|
9044
9187
|
'<span style="font-size:14px;color:#9aa;font-weight:700">kt</span>'+
|
|
@@ -9046,15 +9189,15 @@ function m_renderMeteoWindy(d, seaD){
|
|
|
9046
9189
|
'</div>' +
|
|
9047
9190
|
'</div>' +
|
|
9048
9191
|
'<div style="border-bottom:1px solid rgba(255,255,255,.4);padding:4px 4px;display:flex;flex-direction:column;justify-content:center">' +
|
|
9049
|
-
'<div style="font-size:16px;color:var(--org);font-weight:700;letter-spacing:1px;line-height:1">RACHA'+srcBadge(srcGust)+'</div>' +
|
|
9192
|
+
'<div style="font-size:16px;color:var(--org);font-weight:700;letter-spacing:1px;line-height:1">'+_tn('met_racha','RACHA').toUpperCase()+srcBadge(srcGust)+'</div>' +
|
|
9050
9193
|
'<div style="font-size:32px;font-weight:900;color:#fff;line-height:1.05">'+fmtKt(nowGustKt)+'<span style="font-size:14px;color:#9aa;font-weight:700"> kt</span></div>' +
|
|
9051
9194
|
'</div>' +
|
|
9052
9195
|
'<div style="border-right:1px solid rgba(255,255,255,.4);padding:4px 4px;display:flex;flex-direction:column;justify-content:center">' +
|
|
9053
|
-
'<div style="font-size:16px;color:var(--org);font-weight:700;letter-spacing:1px;line-height:1">AIRE'+srcBadge(srcAir)+'</div>' +
|
|
9196
|
+
'<div style="font-size:16px;color:var(--org);font-weight:700;letter-spacing:1px;line-height:1">'+_tn('met_aire','AIRE').toUpperCase()+srcBadge(srcAir)+'</div>' +
|
|
9054
9197
|
'<div style="font-size:32px;font-weight:900;color:#fff;line-height:1.05">'+fmtT(nowTAir)+'</div>' +
|
|
9055
9198
|
'</div>' +
|
|
9056
9199
|
'<div style="padding:4px 4px;display:flex;flex-direction:column;justify-content:center">' +
|
|
9057
|
-
'<div style="font-size:16px;color:var(--org);font-weight:700;letter-spacing:1px;line-height:1">MAR'+srcBadge(srcSea)+'</div>' +
|
|
9200
|
+
'<div style="font-size:16px;color:var(--org);font-weight:700;letter-spacing:1px;line-height:1">'+_tn('met_mar','MAR').toUpperCase()+srcBadge(srcSea)+'</div>' +
|
|
9058
9201
|
'<div style="font-size:32px;font-weight:900;color:#fff;line-height:1.05">'+fmtT(nowTSea)+'</div>' +
|
|
9059
9202
|
'</div>' +
|
|
9060
9203
|
'</div>' +
|
|
@@ -9203,32 +9346,27 @@ function m_renderMeteoWindy(d, seaD){
|
|
|
9203
9346
|
if (kn == null) return '–';
|
|
9204
9347
|
return '<span style="color:'+wColor(kn)+';font-size:32px">'+kn.toFixed(0)+'</span>';
|
|
9205
9348
|
});
|
|
9206
|
-
/*
|
|
9207
|
-
|
|
9349
|
+
/* Rev315: row labels i18n. */
|
|
9350
|
+
var _tr=function(k,fb){ return (typeof T==='function')?T(k,fb):fb; };
|
|
9351
|
+
if (h.temperature_2m) rows += rowMetric(_tr('met_row_aire','Aire'),'🌡️','#ffb23f','°C',
|
|
9208
9352
|
function(col){ return getVal(h.temperature_2m, col); },
|
|
9209
9353
|
function(v){ return v==null?'–':'<span style="font-size:30px">'+v.toFixed(0)+'°</span>'; });
|
|
9210
|
-
|
|
9211
|
-
if (h.pressure_msl || h.surface_pressure) rows += rowMetric('Presión','🔵','#9ad','mbar',
|
|
9354
|
+
if (h.pressure_msl || h.surface_pressure) rows += rowMetric(_tr('met_row_presion','Presión'),'🔵','#9ad','mbar',
|
|
9212
9355
|
function(col){ return getVal(h.pressure_msl || h.surface_pressure, col); },
|
|
9213
9356
|
function(v){ return v==null?'–':'<span style="font-size:26px">'+Math.round(v)+'</span>'; });
|
|
9214
|
-
|
|
9215
|
-
if (h.precipitation || h.rain) rows += rowMetric('Lluvia','💧','#4dd0ff','mm',
|
|
9357
|
+
if (h.precipitation || h.rain) rows += rowMetric(_tr('met_row_lluvia','Lluvia'),'💧','#4dd0ff','mm',
|
|
9216
9358
|
function(col){ return getVal(h.precipitation || h.rain, col); },
|
|
9217
9359
|
function(v){ return (v==null||v===0)?'–':'<span style="font-size:28px">'+v.toFixed(1)+'</span>'; });
|
|
9218
|
-
|
|
9219
|
-
if (seaH.sea_surface_temperature) rows += rowMetric('T. Mar','🐟','#4dd0ff','°C',
|
|
9360
|
+
if (seaH.sea_surface_temperature) rows += rowMetric(_tr('met_row_tmar','T. Mar'),'🐟','#4dd0ff','°C',
|
|
9220
9361
|
function(col){ return getSeaVal(seaH.sea_surface_temperature, col); },
|
|
9221
9362
|
function(v){ return v==null?'–':'<span style="font-size:30px">'+v.toFixed(0)+'°</span>'; });
|
|
9222
|
-
|
|
9223
|
-
if (seaH.wave_height) rows += rowMetric('Ola','🌊','#4dd0ff','m',
|
|
9363
|
+
if (seaH.wave_height) rows += rowMetric(_tr('met_row_ola','Ola'),'🌊','#4dd0ff','m',
|
|
9224
9364
|
function(col){ return getSeaVal(seaH.wave_height, col); },
|
|
9225
9365
|
function(v){ return v==null?'–':'<span style="font-size:30px">'+v.toFixed(1)+'</span>'; });
|
|
9226
|
-
|
|
9227
|
-
if (seaH.wave_period) rows += rowMetric('Período','⏱️','#9ad','s',
|
|
9366
|
+
if (seaH.wave_period) rows += rowMetric(_tr('met_row_periodo','Período'),'⏱️','#9ad','s',
|
|
9228
9367
|
function(col){ return getSeaVal(seaH.wave_period, col); },
|
|
9229
9368
|
function(v){ return v==null?'–':'<span style="font-size:28px">'+Math.round(v)+'</span>'; });
|
|
9230
|
-
|
|
9231
|
-
if (seaH.wave_direction) rows += rowMetric('Dir ola','🧭','#4dd0ff','°',
|
|
9369
|
+
if (seaH.wave_direction) rows += rowMetric(_tr('met_row_dirola','Dir ola'),'🧭','#4dd0ff','°',
|
|
9232
9370
|
function(col){ return getSeaVal(seaH.wave_direction, col); },
|
|
9233
9371
|
function(v){
|
|
9234
9372
|
if (v == null) return '–';
|
|
@@ -9338,7 +9476,7 @@ function m_renderMeteoWindy(d, seaD){
|
|
|
9338
9476
|
try { collapsed = localStorage.getItem('m-met-summary-collapsed') === '1'; } catch(_){}
|
|
9339
9477
|
return '<div id="m-met-summary" class="' + (collapsed ? 'collapsed' : '') + '" style="background:rgba(20,40,60,.7);border-top:2px solid var(--org);color:#dde;flex-shrink:0">' +
|
|
9340
9478
|
'<button id="m-met-summary-toggle" onclick="m_toggleMeteoSummary()" style="display:flex;align-items:center;justify-content:space-between;width:100%;background:none;border:none;color:var(--org);font-size:28px;font-weight:900;padding:18px 24px;cursor:pointer;text-align:left">' +
|
|
9341
|
-
'<span>📰 Resumen</span>' +
|
|
9479
|
+
'<span>📰 '+((typeof T==='function')?T('met_resumen','Resumen'):'Resumen')+'</span>' +
|
|
9342
9480
|
'<span id="m-met-summary-chev" style="font-size:24px">' + (collapsed ? '▼' : '▲') + '</span>' +
|
|
9343
9481
|
'</button>' +
|
|
9344
9482
|
'<div id="m-met-summary-body" style="padding:0 24px 18px;font-size:24px;line-height:1.55;' + (collapsed ? 'display:none' : '') + '">' +
|