/* 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();
});
}());