| 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 => `<mark class="hl">${m}</mark>`); | |
| } | |
| 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); | |
| }); | |
| })(); | |