/* coduriro — main script (vanilla JS, ES6+) */ (function () { 'use strict'; // ── State ────────────────────────────────────────────────────────────────── let map = null; let mapMarker = null; let localitatiCache = {}; // { judet: string[] } let streetDebounce = null; // ── Tabs ─────────────────────────────────────────────────────────────────── function initTabs() { const btns = document.querySelectorAll('.tab-btn'); const panels = document.querySelectorAll('.tab-panel'); btns.forEach(function (btn) { btn.addEventListener('click', function () { btns.forEach(function (b) { b.classList.remove('active'); b.setAttribute('aria-selected', 'false'); }); panels.forEach(function (p) { p.classList.remove('active'); }); btn.classList.add('active'); btn.setAttribute('aria-selected', 'true'); var target = document.getElementById(btn.dataset.tab); if (target) target.classList.add('active'); // Leaflet needs size recalculation when revealed after being hidden if (map) setTimeout(function () { map.invalidateSize(); }, 60); }); }); // Activate the correct tab on page load (set by PHP via window.activeTab) if (typeof window.activeTab === 'string' && window.activeTab) { var activeBtn = document.querySelector('[data-tab="' + window.activeTab + '"]'); if (activeBtn) activeBtn.click(); } } // ── Map ──────────────────────────────────────────────────────────────────── function initMap() { var mapEl = document.getElementById('search-map'); if (!mapEl || typeof L === 'undefined') return; map = L.map('search-map', { scrollWheelZoom: false, zoomControl: true }); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors', maxZoom: 18 }).addTo(map); // Fit Romania map.fitBounds([[43.6, 20.2], [48.3, 29.7]]); // If results already exist on page, geocode the first row geocodeFirstResult(); } function geocodeAndPin(query) { if (!map || !query) return; var url = 'https://nominatim.openstreetmap.org/search?q=' + encodeURIComponent(query) + '&format=json&limit=1&countrycodes=ro'; fetch(url, { headers: { 'Accept-Language': 'ro' } }) .then(function (r) { return r.json(); }) .then(function (data) { if (!data.length) return; var lat = parseFloat(data[0].lat); var lon = parseFloat(data[0].lon); var name = data[0].display_name; if (mapMarker) map.removeLayer(mapMarker); mapMarker = L.marker([lat, lon]) .addTo(map) .bindPopup('' + escHtml(name) + '') .openPopup(); map.setView([lat, lon], 14, { animate: true }); }) .catch(function () { /* silent */ }); } function geocodeFirstResult() { var firstRow = document.querySelector('.results-table tbody tr'); if (!firstRow) return; buildQueryFromRow(firstRow, geocodeAndPin); } function buildQueryFromRow(row, cb) { var judet = (row.dataset.judet || '').trim(); var localitate = (row.dataset.localitate || '').trim(); var strada = (row.dataset.strada || '').trim(); var parts = [strada, localitate, judet, 'Romania'].filter(Boolean); cb(parts.join(', ')); } // ── Locality dropdown (populated via AJAX on county change) ────────────── function initLocalityDropdown() { var judetEl = document.getElementById('judet'); var localEl = document.getElementById('localitate'); var errorEl = document.getElementById('localitate-error'); var stradaEl = document.getElementById('strada'); if (!judetEl || !localEl) return; // PHP pre-populează localitățile la page load (când există județ selectat), // deci nu facem AJAX la inițializare — doar când utilizatorul schimbă județul. judetEl.addEventListener('change', function () { var judet = judetEl.value; // Reset locality and street selects resetSelect(localEl, 'Alege mai întâi județul'); localEl.disabled = true; if (stradaEl) { stradaEl.value = ''; } if (errorEl) errorEl.textContent = ''; localEl.classList.remove('error'); if (!judet) return; loadLocalitati(judet, '', stradaEl); }); localEl.addEventListener('change', function () { if (errorEl) errorEl.textContent = ''; localEl.classList.remove('error'); }); } function loadLocalitati(judet, preselect, stradaEl) { var localEl = document.getElementById('localitate'); if (!localEl) return; // Show loading state resetSelect(localEl, 'Se încarcă...'); localEl.disabled = true; // Use cache if available if (localitatiCache[judet]) { populateLocalitateSelect(localEl, localitatiCache[judet], preselect); return; } fetch('/get_localitati.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: 'judet=' + encodeURIComponent(judet) }) .then(function (r) { return r.json(); }) .then(function (data) { var localitati = data .filter(function (l) { return l.value; }) .map(function (l) { return l.value; }); localitatiCache[judet] = localitati; populateLocalitateSelect(localEl, localitati, preselect); }) .catch(function () { resetSelect(localEl, 'Eroare la încărcare'); localEl.disabled = false; }); } function populateLocalitateSelect(selectEl, localitati, preselect) { selectEl.innerHTML = ''; var defaultOpt = document.createElement('option'); defaultOpt.value = ''; defaultOpt.textContent = 'Alege localitatea'; selectEl.appendChild(defaultOpt); localitati.forEach(function (loc) { var opt = document.createElement('option'); opt.value = loc; opt.textContent = loc; if (preselect && loc === preselect) opt.selected = true; selectEl.appendChild(opt); }); selectEl.disabled = false; } function resetSelect(selectEl, label) { selectEl.innerHTML = ''; var opt = document.createElement('option'); opt.value = ''; opt.textContent = label; selectEl.appendChild(opt); } // ── Street autocomplete ──────────────────────────────────────────────────── function initStreetAutocomplete() { var judetEl = document.getElementById('judet'); var localEl = document.getElementById('localitate'); var stradaEl = document.getElementById('strada'); var listEl = document.getElementById('strada-list'); if (!stradaEl || !judetEl || !localEl || !listEl) return; stradaEl.addEventListener('input', function () { var judet = judetEl.value.trim(); var localitate = localEl.value.trim(); var term = stradaEl.value.trim(); listEl.innerHTML = ''; if (!judet || !localitate || term.length < 2) return; clearTimeout(streetDebounce); streetDebounce = setTimeout(function () { fetch('/get_strazi.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: 'judet=' + encodeURIComponent(judet) + '&localitate=' + encodeURIComponent(localitate) + '&action=search_streets' + '&term=' + encodeURIComponent(term) }) .then(function (r) { return r.json(); }) .then(function (data) { listEl.innerHTML = ''; if (!data.length) return; var vals = data.map(function (s) { return s.label; }); renderList(listEl, vals, function (val) { stradaEl.value = val; listEl.innerHTML = ''; }); }) .catch(function () {}); }, 280); }); stradaEl.addEventListener('blur', function () { setTimeout(function () { listEl.innerHTML = ''; }, 180); }); stradaEl.addEventListener('keydown', function (e) { handleListKeydown(e, listEl); }); } // ── Shared autocomplete helpers ──────────────────────────────────────────── function renderList(listEl, items, onSelect) { items.forEach(function (val) { var li = document.createElement('li'); li.textContent = val; li.setAttribute('role', 'option'); li.addEventListener('mousedown', function (e) { e.preventDefault(); // prevent blur before click onSelect(val); }); listEl.appendChild(li); }); } function handleListKeydown(e, listEl) { var items = listEl.querySelectorAll('li'); if (!items.length) return; var active = listEl.querySelector('li.ac-active'); var idx = active ? Array.prototype.indexOf.call(items, active) : -1; if (e.key === 'ArrowDown') { e.preventDefault(); if (active) active.classList.remove('ac-active'); var next = items[Math.min(idx + 1, items.length - 1)]; next.classList.add('ac-active'); next.scrollIntoView({ block: 'nearest' }); } else if (e.key === 'ArrowUp') { e.preventDefault(); if (active) active.classList.remove('ac-active'); var prev = items[Math.max(idx - 1, 0)]; prev.classList.add('ac-active'); prev.scrollIntoView({ block: 'nearest' }); } else if (e.key === 'Enter' && active) { e.preventDefault(); active.dispatchEvent(new MouseEvent('mousedown')); } else if (e.key === 'Escape') { listEl.innerHTML = ''; } } // ── Row click → geocode ──────────────────────────────────────────────────── function initRowGeocode() { document.querySelectorAll('.results-table tbody tr').forEach(function (row) { row.addEventListener('click', function (e) { // Don't trigger when copy button is clicked if (e.target.classList.contains('copy-btn')) return; buildQueryFromRow(row, geocodeAndPin); var mapEl = document.getElementById('search-map'); if (mapEl) mapEl.scrollIntoView({ behavior: 'smooth', block: 'center' }); }); }); } // ── Copy postal code ─────────────────────────────────────────────────────── function initCopyButtons() { document.querySelectorAll('.copy-btn').forEach(function (btn) { btn.addEventListener('click', function (e) { e.stopPropagation(); var code = btn.dataset.code || ''; if (!navigator.clipboard) return; navigator.clipboard.writeText(code).then(function () { var orig = btn.innerHTML; btn.innerHTML = '✓'; btn.title = 'Copiat!'; setTimeout(function () { btn.innerHTML = orig; btn.title = 'Copiază codul poștal'; }, 1500); }).catch(function () {}); }); }); } // ── Form validation ──────────────────────────────────────────────────────── function initValidation() { var form = document.getElementById('formularCod'); var localEl = document.getElementById('localitate'); var errorEl = document.getElementById('localitate-error'); if (!form || !localEl) return; form.addEventListener('submit', function (e) { if (!localEl.value.trim()) { e.preventDefault(); localEl.classList.add('error'); if (errorEl) errorEl.textContent = "Câmpul 'Localitate' este obligatoriu."; localEl.focus(); } }); localEl.addEventListener('input', function () { if (localEl.value.trim()) { localEl.classList.remove('error'); if (errorEl) errorEl.textContent = ''; } }); } // ── Utility ──────────────────────────────────────────────────────────────── function escHtml(str) { return String(str) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } // ── Bootstrap ────────────────────────────────────────────────────────────── document.addEventListener('DOMContentLoaded', function () { initTabs(); initMap(); initLocalityDropdown(); initStreetAutocomplete(); initRowGeocode(); initCopyButtons(); initValidation(); }); }());