const searchBox = document.getElementById("searchBox"); const clearSearch = document.getElementById("clearSearch"); const pageSizeEl = document.getElementById("pageSize"); const themeBtn = document.getElementById("themeToggle"); const tableBody = document.getElementById("tableBody"); const pagination = document.getElementById("pagination"); const counter = document.getElementById("counter"); const loading = document.getElementById("loading"); let data = []; let filtered = []; let currentPage = 1; const DEFAULT_PAGE_SIZE = 10; let rowsPerPage = DEFAULT_PAGE_SIZE; if (pageSizeEl) pageSizeEl.value = String(DEFAULT_PAGE_SIZE); const colors = ["#00bcd4","#4caf50","#f44336","#ff9800","#9c27b0","#e91e63","#009688","#3f51b5","#607d8b","#795548"]; function normalizeArabic(str=""){ return str .replace(/[\u064B-\u0652]/g, "") .replace(/[أإآا]/g,"ا").replace(/ى/g,"ي") .replace(/ؤ/g,"و").replace(/ئ/g,"ي").replace(/ة/g,"ه") .toLowerCase(); } function matches(text, q){ if(!q) return true; return normalizeArabic(String(text ?? "")).includes(normalizeArabic(q)); } function highlight(text, q){ if(!q) return String(text ?? ""); const esc = q.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); return String(text ?? "").replace(new RegExp(esc, 'gi'), m => `${m}`); } function renderPagination(){ const pages = Math.ceil(filtered.length / rowsPerPage); pagination.innerHTML = ""; if (pages <= 1) return; const makeBtn = (label, page, opts={})=>{ const b = document.createElement("button"); b.textContent = label; b.className = "page-btn" + (opts.ghost ? " ghost" : ""); b.disabled = !!opts.disabled; b.addEventListener("click", ()=>{ currentPage = page; updateView(); window.scrollTo({top:0, behavior:"smooth"}); }); return b; }; pagination.appendChild(makeBtn("‹", Math.max(1, currentPage-1), {ghost:true, disabled: currentPage===1})); const windowSize = 5; let total = pages; let start = Math.max(1, currentPage - Math.floor(windowSize/2)); let end = Math.min(total, start + windowSize - 1); if (end - start + 1 < windowSize) start = Math.max(1, end - windowSize + 1); if (start > 1) pagination.appendChild(makeBtn("1", 1, {ghost: currentPage!==1})); if (start > 2) pagination.appendChild(Object.assign(document.createElement("span"), {className:"dots", textContent:"..."})); for (let p=start; p<=end; p++){ const btn = makeBtn(String(p), p); if (p === currentPage) btn.classList.add("active"); pagination.appendChild(btn); } if (end < total - 1) pagination.appendChild(Object.assign(document.createElement("span"), {className:"dots", textContent:"..."})); if (end < total) pagination.appendChild(makeBtn(String(total), total, {ghost: currentPage!==total})); pagination.appendChild(makeBtn("›", Math.min(total, currentPage+1), {ghost:true, disabled: currentPage===total})); } function renderTable(){ const q = searchBox.value.trim(); tableBody.innerHTML = ""; const start = (currentPage - 1) * rowsPerPage; const pageItems = filtered.slice(start, start + rowsPerPage); if (pageItems.length === 0) { const tr = document.createElement("tr"); const td = document.createElement("td"); td.colSpan = 3; td.className = "no-results"; td.textContent = "🤔 لا توجد نتائج مطابقة"; tr.appendChild(td); tableBody.appendChild(tr); return; } pageItems.forEach((it, i)=>{ const tr = document.createElement("tr"); const code = it.code ?? ""; const act = it.activity ?? it.name ?? ""; const codeTd = document.createElement("td"); codeTd.innerHTML = highlight(code, q); const actTd = document.createElement("td"); actTd.innerHTML = highlight(act, q); const colorTd = document.createElement("td"); const colorBar = document.createElement("div"); colorBar.className = "color-bar"; colorBar.style.backgroundColor = colors[i % colors.length]; colorTd.appendChild(colorBar); tr.appendChild(codeTd); tr.appendChild(actTd); tr.appendChild(colorTd); tableBody.appendChild(tr); }); } function updateView(){ const q = searchBox.value.trim(); filtered = data.filter(it => matches(it.code, q) || matches(it.activity ?? it.name, q)); if (!q) { counter.textContent = `📊 إجمالي التخصصات: ${data.length.toLocaleString()}`; } else { counter.textContent = filtered.length ? `✅ عدد النتائج: ${filtered.length.toLocaleString()}` : "🤔 لا توجد نتائج مطابقة"; } if (loading) loading.style.display = "none"; renderTable(); renderPagination(); } function debounce(fn, delay=200){ let t; return (...a)=>{ clearTimeout(t); t=setTimeout(()=>fn(...a), delay); }; } searchBox.addEventListener("input", debounce(()=>{ currentPage=1; updateView(); }, 180)); clearSearch.addEventListener("click", ()=>{ searchBox.value=""; searchBox.focus(); currentPage=1; updateView(); }); pageSizeEl.addEventListener("change", ()=>{ rowsPerPage = parseInt(pageSizeEl.value, 10); currentPage=1; updateView(); }); document.addEventListener("keydown", (e)=>{ if(e.key==="/"){ e.preventDefault(); searchBox.focus(); } }); (function initTheme(){ const saved = localStorage.getItem("theme") || "light"; document.documentElement.classList.toggle("dark", saved === "dark"); themeBtn.textContent = saved === "dark" ? "☀️ فاتح" : "🌙 ليلي"; themeBtn.addEventListener("click", ()=>{ const isDark = document.documentElement.classList.toggle("dark"); localStorage.setItem("theme", isDark ? "dark" : "light"); themeBtn.textContent = isDark ? "☀️ فاتح" : "🌙 ليلي"; }); })(); fetch("data.json", { cache: "no-cache" }) .then(r=> r.json()) .then(json=>{ data = Array.isArray(json) ? json.map(row=>({ code: row.code ?? row.Code ?? row["رمز"] ?? "", activity: row.activity ?? row.Activity ?? row.name ?? row["النشاط"] ?? "" })) : []; updateView(); }) .catch(err=>{ console.error(err); if (loading) loading.textContent = "تعذر تحميل البيانات ❌"; }); (function(){ const tw = document.querySelector('.table-wrap'); if (!tw) return; let t; tw.addEventListener('scroll', ()=>{ tw.classList.add('scrolling'); clearTimeout(t); t = setTimeout(()=> tw.classList.remove('scrolling'), 180); }); })();