isced / script.js
stat2025's picture
Update script.js
04b296b verified
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);
});
})();