1// ============================ 2// NAVEGACIÃN: CÃMO LLEGAR 3// ============================ 4function abrirNavegador(lat, lon) { 5 lat = lat.toString().replace(",", "."); 6 lon = lon.toString().replace(",", "."); 7 8 const destino = `${lat},${lon}`; 9 const ua = navigator.userAgent; 10 11 // iPhone / iPad â Apple Maps app 12 if (/iPhone|iPad/.test(ua)) { 13 window.location.href = `maps://?daddr=${destino}`; 14 return; 15 } 16 17 // macOS â Apple Maps web 18 if (/Macintosh/.test(ua)) { 19 window.open(`https://maps.apple.com/?daddr=${destino}`, "_blank"); 20 return; 21 } 22 23 // Android â Google Maps 24 if (/Android/.test(ua)) { 25 window.location.href = `https://www.google.com/maps/search/?api=1&query=${destino}`; 26 return; 27 } 28 29 // Escritorio 30 window.open(`https://www.google.com/maps/dir/?api=1&destination=${destino}`, "_blank"); 31} 32 33 34 35 36function formatearMiles(num) { 37 return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, "."); 38} 39 40function formatearDecimal(num, decimales = 2) { 41 return Number(num).toFixed(decimales).replace(".", ","); 42} 43 44function capitalizar(texto) { 45 return texto.charAt(0).toUpperCase() + texto.slice(1).toLowerCase(); 46} 47function formatearFecha(fechaIso) { 48 if (!fechaIso) return ''; 49 const partes = fechaIso.split('-'); 50 if (partes.length !== 3) return fechaIso; 51 return `${partes[2]}/${partes[1]}/${partes[0]}`; 52} 53function crearSlug(texto) { 54 return texto 55 .toLowerCase() 56 .normalize("NFD").replace(/[\u0300-\u036f]/g, "") // quitar acentos 57 .replace(/[^a-z0-9\s-]/g, "") // quitar sÃmbolos 58 .trim() 59 .replace(/\s+/g, "-"); // espacios â guiones 60} 61function existeImagen(url, callback) { 62 const img = new Image(); 63 img.onload = () => callback(true); 64 img.onerror = () => callback(false); 65 img.src = url; 66} 67function getYear(fecha) { 68 if (!fecha) return ""; 69 const d = new Date(fecha); 70 return isNaN(d.getTime()) ? "" : d.getFullYear(); 71} 72 73 74function decidirContenidoWebcam(w) { 75 76 // =============================== 77 // PROXY SOLO PARA HTTP INSEGURO 78 // =============================== 79 const necesitaProxy = url => 80 url && url.startsWith("http://"); 81 82 const proxificar = url => 83 "/WebcamProxy/Stream?url=" + encodeURIComponent(url); 84 85 86 // =============================== 87 // 1) PRIORIDAD MÃXIMA: urlStream 88 // =============================== 89 if (w.urlStream) { 90 91 // A) Streams tipo player.php â visor externo 92 if (w.urlStream.includes("player.php")) { 93 return { tipo: "externo", url: w.urlStream }; 94 } 95 96 // B) Streams reales (HLS/MP4) â NO PROXY 97 if (w.urlStream.endsWith(".m3u8") || w.urlStream.endsWith(".mp4")) { 98 return { tipo: "video", url: w.urlStream }; 99 } 100 101 // C) MJPEG en urlStream â ***AQUà ES DONDE CAMBIAMOS SOLO ESTE CASO*** 102 if (w.urlStream.includes("mjpg") || w.urlStream.includes("mjpeg")) { 103 104 // Si hay visor HTML â mejor usarlo (igual que antes) 105 if (w.urlPagina?.includes("viewer") || 106 w.urlPagina?.includes("view.shtml") || 107 w.urlPagina?.includes("player.php")) { 108 109 return { tipo: "externo", url: w.urlPagina }; 110 } 111 112 // ***ÃNICO CAMBIO REAL*** 113 // MJPEG HTTP â PROXY 114 // MJPEG HTTPS â DIRECTO 115 const finalUrl = necesitaProxy(w.urlStream) 116 ? proxificar(w.urlStream) 117 : w.urlStream; 118 119 return { tipo: "imagen", url: finalUrl }; 120 } 121 122 // D) Cualquier otro stream â externo por seguridad 123 return { tipo: "externo", url: w.urlStream }; 124 } 125 126 127 // =============================== 128 // 2) NO hay stream â usar urlImagen 129 // ***ESTO LO DEJO EXACTAMENTE COMO ESTABA*** 130 // =============================== 131 if (w.urlImagen) { 132 133 // A) Imagen real con extensión 134 if (/\.(jpg|jpeg|png)$/i.test(w.urlImagen)) { 135 136 const finalUrl = necesitaProxy(w.urlImagen) 137 ? proxificar(w.urlImagen) 138 : w.urlImagen; 139 140 return { tipo: "imagen", url: finalUrl }; 141 } 142 143 // B) MJPEG en urlImagen (NO TOCAMOS NADA) 144 if (w.urlImagen.includes("mjpg") || w.urlImagen.includes("mjpeg")) { 145 146 if (w.urlPagina?.includes("viewer") || 147 w.urlPagina?.includes("view.shtml") || 148 w.urlPagina?.includes("player.php")) { 149 150 return { tipo: "externo", url: w.urlPagina }; 151 } 152 153 const finalUrl = necesitaProxy(w.urlImagen) 154 ? proxificar(w.urlImagen) 155 : w.urlImagen; 156 157 return { tipo: "imagen", url: finalUrl }; 158 } 159 160 // C) Imagen sin extensión (cam.php, snapshot.cgi, etc.) 161 if ( 162 w.urlImagen.includes(".php") || 163 w.urlImagen.includes(".cgi") || 164 w.urlImagen.includes("snapshot") || 165 w.urlImagen.includes("capture") || 166 w.urlImagen.includes("cam.") 167 ) { 168 169 const finalUrl = necesitaProxy(w.urlImagen) 170 ? proxificar(w.urlImagen) 171 : w.urlImagen; 172 173 return { tipo: "imagen", url: finalUrl }; 174 } 175 } 176 177 178 // =============================== 179 // 3) Visores externos embebibles (iframe) 180 // ***SIN CAMBIOS*** 181 // =============================== 182 if (w.urlPagina) { 183 184 if (w.urlPagina.includes("skaping.com")) { 185 return { tipo: "iframe", url: w.urlPagina }; 186 } 187 188 if (w.urlPagina.includes("/embed") || 189 w.urlPagina.includes("panomax.com") || 190 w.urlPagina.includes("skylinewebcams.com")) { 191 192 return { tipo: "iframe", url: w.urlPagina }; 193 } 194 195 return { tipo: "externo", url: w.urlPagina }; 196 } 197 198 199 // =============================== 200 // 4) Nada válido â placeholder 201 // =============================== 202 return { tipo: "placeholder", url: null }; 203} 204function textoNivelAludes(nivel) { 205 const estilos = { 206 titulo: "text-align:center; font-weight:bold; font-size:1.2em; margin-bottom:0.5em;", 207 fila: "text-align:left; background:#f7f7f7; padding:6px 10px; border-radius:4px; margin:4px 0;" 208 }; 209 210 switch (nivel) { 211 case 1: 212 return ` 213<div> 214 <div style="${estilos.titulo}">Riesgo Débil (1)</div> 215 <div style="${estilos.fila}">La nieve está <strong>bien asentada</strong> en general. Solo son posibles aludes espontáneos pequeños en muy pocas laderas extremadamente inclinadas.</div> 216 <div style="${estilos.fila}">Las condiciones son <strong>favorables</strong> para actividades en montaña, aunque siempre es necesaria una evaluación local del terreno.</div> 217</div> 218 `; 219 case 2: 220 return ` 221<div> 222 <div style="${estilos.titulo}">Riesgo Limitado (2)</div> 223 <div style="${estilos.fila}">La nieve presenta <strong>inestabilidad moderada</strong> en algunas laderas inclinadas.</div> 224 <div style="${estilos.fila}">Son posibles desencadenamientos por <strong>sobrecarga fuerte</strong> y, en casos aislados, por sobrecarga débil.</div> 225 <div style="${estilos.fila}">Los aludes espontáneos suelen ser pequeños, aunque pueden darse aludes medianos en zonas muy concretas.</div> 226</div> 227 `; 228 case 3: 229 return ` 230<div> 231 <div style="${estilos.titulo}">Riesgo Notable (3)</div> 232 <div style="${estilos.fila}">La nieve está <strong>inestable en muchas laderas</strong>. Es posible el desencadenamiento incluso con sobrecargas débiles.</div> 233 <div style="${estilos.fila}">Los aludes espontáneos pueden ser <strong>medianos o grandes</strong>.</div> 234 <div style="${estilos.fila}">Se requiere <strong>experiencia</strong> en evaluación del manto y elección muy cuidadosa del itinerario.</div> 235</div> 236 `; 237 case 4: 238 return ` 239<div> 240 <div style="${estilos.titulo}">Riesgo Fuerte (4)</div> 241 <div style="${estilos.fila}">La nieve está <strong>muy inestable</strong> en la mayorÃa de laderas inclinadas.</div> 242 <div style="${estilos.fila}">Los aludes espontáneos son <strong>probables</strong>, incluso de gran tamaño.</div> 243 <div style="${estilos.fila}">El desencadenamiento es probable incluso con <strong>sobrecargas débiles</strong>. Las condiciones son peligrosas y se recomienda evitar terreno de aludes.</div> 244</div> 245 `; 246 case 5: 247 return ` 248<div> 249 <div style="${estilos.titulo}">Riesgo Muy Fuerte (5)</div> 250 <div style="${estilos.fila}"><strong>Situación excepcional.</strong> La nieve está <strong>extremadamente inestable</strong> en prácticamente todas las laderas.</div> 251 <div style="${estilos.fila}">Los aludes espontáneos <strong>grandes y muy grandes</strong> son muy probables.</div> 252 <div style="${estilos.fila}">Las actividades en terreno de aludes deben <strong>evitarse por completo</strong>.</div> 253</div> 254 `; 255 default: 256 return "<p>
256Nivel de peligro no disponible.</p>"; 257 } 258} 259 260
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.