1/* ============================================================ 2 ת×××ת ××× ××××ר ××××× â סקר××¤× ××××¢× ××צ×× × 3 × ×ª×× ××: Open-Meteo (https://open-meteo.com) â ××× ××, ××× ××¤×ª× API 4 RTL / ×¢×ר×ת / צ××××ס / ×××× ×××× / Bootstrap 3.4.x 5 6 ××××¢× ×××£: 7 <div id="wx-widget" data-default-city="×ת×× ×" data-days="6"></div> 8 <script src="/js/weather-forecast.js" defer></script> 9 10 ×ר×ש×ת: 11 ⢠×××£ ××ר ×××¢× Bootstrap 3 (××× ××תר) â ×סקר××¤× ×שת×ש ×-panel/form-control/list-group. 12 ⢠×××£ ×××ש ×-UTF-8 (××× ×©××¢×ר×ת ת××¦× × ×××). 13 ============================================================ */ 14(function () { 15 "use strict"; 16 17 function init() { 18 var el = document.getElementById("wx-widget"); 19 if (!el) { console.warn('[weather] ×× × ××¦× ×××× × ×¢××× <div id="wx-widget">'); return; } 20 if (el.getAttribute("data-wx-ready")) return; // ×× ×עת ×ת××× ×פ×× 21 el.setAttribute("data-wx-ready", "1"); 22 23 injectStyleOnce(); 24 25 el.setAttribute("dir", "rtl"); 26 el.innerHTML = 27 '<div class="panel wx-panel"><div class="panel-body">' + 28 '<div class="wx-search">' + 29 '<input type="text" class="form-control wx-input" dir="auto" autocomplete="off" ' + 30 'placeholder="××פ×ש ×¢×ר ××××× (×¢×ר×ת ×× ×× ×××ת)â¦" aria-label="××פ×ש ×¢×ר ×××××">' + 31 '<ul class="list-group wx-suggest" role="listbox"></ul>' + 32 '</div>' + 33 '<div class="wx-body"><div class="wx-msg">×××¢×â¦</div></div>' + 34 '</div></div>'; 35 36 run(el); 37 } 38 39 function injectStyleOnce() { 40 if (document.getElementById("wx-style")) return; 41 var s = document.createElement("style"); 42 s.id = "wx-style"; 43 s.textContent = [ 44 '#wx-widget{font-family:"Segoe UI","Arial Hebrew",Arial,sans-serif;max-width:660px;direction:rtl}', 45 '#wx-widget .wx-panel{background: linear-gradient(150deg, #0d5eb0, #ffffff);color:#fff;border:none;border-radius:14px;box-shadow:0 12px 30px rgba(14,42,71,.35)}', 46 '#wx-widget .wx-search{position:relative;margin-bottom:16px}', 47 '#wx-widget .wx-suggest{position:absolute;top:100%;right:0;left:0;z-index:1000;margin-top:4px;max-height:230px;overflow-y:auto;display:none;box-shadow:0 10px 24px rgba(0,0,0,.25)}', 48 '#wx-widget .wx-suggest.open{display:block}', 49 '#wx-widget .wx-suggest .list-group-item{cursor:pointer;text-align:right;padding:9px 14px;color:#16385a}', 50 '#wx-widget .wx-suggest .list-group-item.active{background-color:#1b4a73;border-color:#1b4a73;color:#fff}', 51 '#wx-widget .wx-msg{color:rgba(255,255,255,.75);font-size:14px;padding:6px 2px}', 52 '#wx-widget .wx-now{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:16px}', 53 '#wx-widget .wx-place{font-size:17px;font-weight:600}', 54 '#wx-widget .wx-now-desc{font-size:13px;color:rgba(255,255,255,.75);margin-top:2px}', 55 '#wx-widget .wx-now-temp{font-size:44px;font-weight:700;line-height:1}', 56 '#wx-widget .wx-now-temp .u{font-size:20px;color:#ffd166}', 57 '#wx-widget .wx-now-icon{font-size:40px;line-height:1}', 58 '#wx-widget .wx-days{display:flex;gap:6px;border-top:1px solid rgba(255,255,255,.15);padding-top:14px}', 59 '#wx-widget .wx-day{flex:1;text-align:center}', 60 '#wx-widget .wx-day .d{font-size:12px;color:rgba(255,255,255,.75);font-weight:600}', 61 '#wx-widget .wx-day .i{font-size:24px;margin:5px 0}', 62 '#wx-widget .wx-day .t{font-size:13px;font-weight:600}', 63 '#wx-widget .wx-day .t .lo{color:rgba(255,255,255,.7);font-weight:400}', 64 '#wx-widget .wx-day .p{font-size:11px;color:#7fb8e6;margin-top:3px}' 65 ].join(""); 66 document.head.appendChild(s); 67 } 68 69 function run(el) { 70 var input = el.querySelector(".wx-input"); 71 var sugBox = el.querySelector(".wx-suggest"); 72 var body = el.querySelector(".wx-body"); 73 74 var days = Math.min(parseInt(el.getAttribute("data-days"), 10) || 6, 7); 75 var defCity = el.getAttribute("data-default-city") || "×ת×× ×"; 76 77 var codes = { 78 0:["âï¸","×××ר"], 1:["ð¤ï¸","×××ר ×ר×××"], 2:["â ","××¢×× × ×××§×ת"], 79 3:["âï¸","××¢×× ×"], 45:["ð«ï¸","ערפ×"], 48:["ð«ï¸","ערפ×"], 80 51:["ð¦ï¸","×פ×××£ ×§×"], 53:["ð¦ï¸","×פ×××£"], 55:["ð§ï¸","×פ×××£ ×××§"], 81 61:["ð¦ï¸","××©× ×§×"], 63:["ð§ï¸","×ש×"], 65:["ð§ï¸","××©× ×××§"], 82 71:["ð¨ï¸","ש×× ×§×"], 73:["ð¨ï¸","ש××"], 75:["âï¸","ש×× ×××"], 83 80:["ð¦ï¸","×××ר××"], 81:["ð§ï¸","×××ר××"], 82:["âï¸","×××ר×× ×××§××"], 84 95:["âï¸","ס×פת רע×××"], 96:["âï¸","ס×פ×"], 99:["âï¸","ס××¤× ×¢××"] 85 }; 86 87 function dayName(iso) { 88 return new Date(iso + "T00:00").toLocaleDateString("he-IL", { weekday: "short" }); 89 } 90 function msg(t) { body.innerHTML = '<div class="wx-msg">' + t + "</div>"; } 91 92 function render(place, cur, daily) { 93 var c = codes[cur.weather_code] || ["ð¡ï¸", "â"]; 94 var html = 95 '<div class="wx-now">' + 96 '<div><div class="wx-place" dir="auto">' + place + '</div>' + 97 '<div class="wx-now-desc">' + c[1] + '</div></div>' + 98 '<div style="display:flex;align-items:center;gap:10px">' + 99 '<span class="wx-now-icon">' + c[0] + '</span>' + 100 '<span class="wx-now-temp">' + Math.round(cur.temperature_2m) + 101 '<span class="u">°C</span></span>' + 102 '</div>' + 103 '</div><div class="wx-days">'; 104 for (var i = 0; i < days && i < daily.time.length; i++) { 105 var di = (codes[daily.weather_code[i]] || ["ð¡ï¸"])[0]; 106 var label = i === 0 ? "××××" : dayName(daily.time[i]); 107 var pop = daily.precipitation_probability_max[i]; 108 html += 109 '<div class="wx-day">' + 110 '<div class="d">' + label + '</div>' + 111 '<div class="i">' + di + '</div>' + 112 '<div class="t">' + Math.round(daily.temperature_2m_max[i]) + 113 '° <span class="lo">' + Math.round(daily.temperature_2m_min[i]) + '°</span></div>' + 114 '<div class="p">' + (pop != null ? "ð§" + pop + "%" : " ") + '</div>' + 115 '</div>'; 116 } 117 body.innerHTML = html + "</div>"; 118 } 119 120 function loadWeather(lat, lon, place) { 121 msg("×××¢× ×ת " + place + "â¦"); 122 var u = "https://api.open-meteo.com/v1/forecast?latitude=" + lat + "&longitude=" + lon + 123 "¤t=temperature_2m,weather_code" +
124 "&daily=weather_code,temperature_2m_max,temperature_2m_min,precipitation_probability_max" + 125 "&forecast_days=" + days + "&temperature_unit=celsius&timezone=auto"; 126 fetch(u).then(function (r) { return r.json(); }) 127 .then(function (d) { render(place, d.current, d.daily); }) 128 .catch(function () { msg("×× × ××ª× ×××¢×× ×ת ×ת×××ת. × ×¡× ×©××."); }); 129 } 130 131 function langFor(q) { return /[\u0590-\u05FF]/.test(q) ? "he" : "en"; } 132 133 function geocode(name, lang) { 134 return fetch( 135 "https://geocoding-api.open-meteo.com/v1/search?count=10&language=" + 136 (lang || "he") + "&name=" + encodeURIComponent(name) 137 ).then(function (r) { return r.json(); }) 138 .then(function (j) { 139 return (j.results || []).filter(function (c) { return c.country_code === "GR"; }).slice(0, 6); 140 }); 141 } 142 143 function pick(c) { 144 sugBox.classList.remove("open"); 145 sugBox.innerHTML = ""; 146 input.value = c.name; 147 loadWeather(c.latitude, c.longitude, c.name); 148 } 149 150 var timer, active = -1, results = []; 151 152 input.addEventListener("input", function () { 153 clearTimeout(timer); 154 var q = input.value.trim(); 155 if (q.length < 2) { sugBox.classList.remove("open"); return; } 156 timer = setTimeout(function () { 157 geocode(q, langFor(q)).then(function (res) { 158 results = res; active = -1; 159 if (!results.length) { sugBox.classList.remove("open"); return; } 160 sugBox.innerHTML = results.map(function (c, i) { 161 return '<li class="list-group-item" role="option" dir="auto" data-i="' + i + '">' + 162 c.name + '</li>'; 163 }).join(""); 164 sugBox.classList.add("open"); 165 }); 166 }, 250); 167 }); 168 169 sugBox.addEventListener("click", function (e) { 170 var li = e.target.closest("li"); 171 if (li) pick(results[+li.getAttribute("data-i")]); 172 }); 173 174 input.addEventListener("keydown", function (e) { 175 var items = [].slice.call(sugBox.querySelectorAll("li")); 176 if (!sugBox.classList.contains("open") || !items.length) return; 177 if (e.key === "ArrowDown") { active = (active + 1) % items.length; e.preventDefault(); } 178 else if (e.key === "ArrowUp") { active = (active - 1 + items.length) % items.length; e.preventDefault(); } 179 else if (e.key === "Enter") { if (active >= 0) pick(results[active]); e.preventDefault(); return; } 180 else if (e.key === "Escape") { sugBox.classList.remove("open"); return; } 181 items.forEach(function (it, i) { it.classList.toggle("active", i === active); }); 182 }); 183 184 document.addEventListener("click", function (e) { 185 if (!el.contains(e.target)) sugBox.classList.remove("open"); 186 }); 187 188 geocode(defCity, langFor(defCity)).then(function (r) { 189 if (r.length) { input.value = r[0].name; pick(r[0]); } 190 else msg('×¢×ר ×ר×רת ××××× "' + defCity + '" ×× × ×צ××.'); 191 }); 192 } 193 194 if (document.readyState === "loading") { 195 document.addEventListener("DOMContentLoaded", init); 196 } else { 197 init(); 198 } 199})();
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.