HUFLIS: tìm kiếm bảng, dropdown Tiết học, tô đỏ dòng đã ĐK đủ slot
// ==UserScript==
// @name Tiến cụt - HUFLIS: NNKC
// @namespace http://tampermonkey.net/
// @version 5.2
// @description HUFLIS: tìm kiếm bảng, dropdown Tiết học, tô đỏ dòng đã ĐK đủ slot
// @author tiencut (viet script by Tiencut)
// @license MIT
// @icon https://flts.huflis.edu.vn/favicon.ico
// @match https://flts.huflis.edu.vn/Student/ChooseCourse*
// ==/UserScript==
(function() {
'use strict';
const MAX_PAGES = 5; // Số trang muốn crawl
setTimeout(async function() {
const table = document.querySelector('table.table_data');
if (!table) return;
const thead = table.tHead;
const tbody = table.tBodies[0];
if (!thead || !tbody) return;
// Tạo filter row nếu chưa có (đủ 9 cell)
if (thead.rows.length < 3) {
const filterRow = thead.insertRow(-1);
for (let i = 0; i < 9; i++) {
let cell = document.createElement('td');
if (i === 1) {
let input = document.createElement('input');
input.type = "text";
input.placeholder = "Tìm Giảng viên";
input.style.width = "90%";
input.style.marginTop = "4px";
cell.appendChild(input);
}
if (i === 2) {
let input = document.createElement('input');
input.type = "text";
input.placeholder = "Tìm Thứ";
input.style.width = "90%";
input.style.marginTop = "4px";
cell.appendChild(input);
}
if (i === 3) {
let input = document.createElement('input');
input.type = "text";
input.placeholder = "Tìm Phòng";
input.style.width = "90%";
input.style.marginTop = "4px";
cell.appendChild(input);
}
if (i === 4) {
let input = document.createElement('input');
input.type = "text";
input.placeholder = "Tìm Tiết học";
input.style.width = "90%";
input.style.marginTop = "4px";
input.setAttribute("autocomplete","off");
input.id = "autocomplete-tiet-hoc";
cell.appendChild(input);
let suggestDiv = document.createElement('div');
suggestDiv.style.position = "relative";
suggestDiv.id = "suggest-container-tiet-hoc";
cell.appendChild(suggestDiv);
}
filterRow.appendChild(cell);
}
}
const filterRow = thead.rows[2];
const inpGv = filterRow.cells[1].querySelector('input');
const inpThu = filterRow.cells[2].querySelector('input');
const inpPhong = filterRow.cells[3].querySelector('input');
const inpTiet = filterRow.cells[4].querySelector('input');
const suggestDiv = filterRow.cells[4].querySelector('div');
const inputs = [inpGv, inpThu, inpPhong, inpTiet];
const colIdx = [1, 2, 3, 4];
// Crawl dữ liệu nhiều page
let allRows = [];
async function fetchPages() {
let links = [];
let base = location.origin + location.pathname + '?page=';
for(let i = 1; i <= MAX_PAGES; i++)
links.push(base + i);
let reqs = links.map(link =>
fetch(link, {credentials: "include"})
.then(r=>r.text())
.then(html=>{
let parser = new DOMParser();
let doc = parser.parseFromString(html,"text/html");
let tb = doc.querySelector('table.table_data');
if(!tb) return [];
let body = tb.tBodies[0];
if (!body) return [];
return Array.from(body.rows).map(tr=>{
let d = Array.from(tr.cells).map(t=>t.innerText.trim());
d._html = tr.outerHTML;
return d;
});
})
);
let results = await Promise.all(reqs);
allRows = [].concat(...results);
}
await fetchPages();
// Tìm các giá trị unique của Tiết học (cột 4)
let tietHocArr = Array.from(new Set(allRows.map(row => row[4]).filter(s=>s)));
tietHocArr.sort((a,b)=>a.localeCompare(b,'vi'));
// Hàm render với dòng full slot sẽ tô đỏ
function renderFiltered() {
let filters = inputs.map(inp=>inp && inp.value.trim().toLowerCase() || "");
let filteredRows = allRows.filter(rowArr=>{
for (let k=0; k<colIdx.length; k++) {
let kw = filters[k];
if (kw && !rowArr[colIdx[k]].toLowerCase().includes(kw)) return false;
}
return true;
});
tbody.innerHTML = filteredRows.map(row => {
// Tối đa ở cột 6, Đã ĐK ở cột 7 (index tính từ 0)
let daDK = parseInt(row[7]);
let toiDa = parseInt(row[6]);
let isFull = !isNaN(daDK) && !isNaN(toiDa) && daDK >= toiDa;
let html = row._html;
if(isFull) {
html = html.replace(/^<tr/, '<tr style="background-color: #ffeaea;color:#b80000;font-weight:bold;" title="Lớp đã đầy slot!"');
}
return html;
}).join('');
}
// Suggestion handler cho input Tiết học (dropdown)
function showSuggestionsTietHoc(val) {
suggestDiv.innerHTML = "";
if (!val && tietHocArr.length === 0) return;
let filtered = tietHocArr.filter(item => item.toLowerCase().includes(val.toLowerCase()));
if(filtered.length === 0) return;
let list = document.createElement('div');
list.style.position = "absolute";
list.style.background = "#fff";
list.style.border = "1px solid #aaa";
list.style.zIndex = 1000;
list.style.width = inpTiet.offsetWidth + "px";
filtered.forEach(item => {
let o = document.createElement('div');
o.textContent = item;
o.style.padding = "4px 8px";
o.style.cursor = "pointer";
o.onmousedown = function(e){
e.preventDefault();
inpTiet.value = item;
renderFiltered();
suggestDiv.innerHTML = "";
};
list.appendChild(o);
});
suggestDiv.appendChild(list);
}
// Sự kiện cho input Tiết học
inpTiet.addEventListener('focus', function(){
showSuggestionsTietHoc(this.value);
});
inpTiet.addEventListener('input', function(){
showSuggestionsTietHoc(this.value);
renderFiltered();
});
inpTiet.addEventListener('blur',function(){
setTimeout(() => {suggestDiv.innerHTML = "";},150);
});
// 3 input còn lại
[inpGv, inpThu, inpPhong].forEach(inp => inp.addEventListener('input', renderFiltered));
// Lọc lần đầu
renderFiltered();
}, 1);
})();