/** * HÁN VIỆT TỪ ĐIỂN - 2025 * Phiên bản viết bởi Long Ngo, dự án này được hỗ trợ bởi CVNSS4.0 xem tại https://chuvnsongsong.com/ * Xin chân thành cảm ơn 2 Nhà nghiên cứu: Anh Kiều Trường Lâm và Thầy Trần Tư Bình đã ủng hộ cho Dự án. */ $(document).ready(function() { // --- BIẾN TOÀN CỤC --- let dictData = []; // Sẽ lấy từ dictionaryData.js let radData = []; // Sẽ lấy từ bothudata.js // Canvas vars let canvas = document.getElementById('handCanvas'); let ctx = canvas.getContext('2d'); let isDrawing = false; let strokeCount = 0; // --- KHỞI TẠO DỮ LIỆU --- initData(); initEvents(); function initData() { // Kiểm tra xem file dictionaryData.js đã load chưa if (typeof localDictionary !== 'undefined') { dictData = localDictionary; } else if (typeof dictionaryData !== 'undefined') { dictData = dictionaryData; // Fallback } else { $('#searchCount').text("Lỗi: Không tìm thấy dữ liệu từ điển!"); } // Kiểm tra file bothudata.js if (typeof universalRadical !== 'undefined') { radData = universalRadical; initRadicalDropdown(); renderRadicals(0); // Render all initially } // Render trang đầu renderList(dictData.slice(0, 50)); // Load 50 từ đầu tiên $('#searchCount').text(`Tổng: ${dictData.length} mục từ`); // Init Canvas Style ctx.lineWidth = 6; ctx.lineCap = 'round'; ctx.lineJoin = 'round'; ctx.strokeStyle = '#2C2C2C'; } // --- XỬ LÝ SỰ KIỆN (EVENTS) --- function initEvents() { // 1. Chuyển Tab $('.nav-btn').click(function() { const tabId = $(this).data('tab'); // UI Update $('.nav-btn').removeClass('active'); $(this).addClass('active'); $('.view-section').removeClass('active'); $(`#view-${tabId}`).addClass('active'); // Mobile sidebar logic if ($(window).width() <= 768) { openSidebar(); } }); // 2. Tìm kiếm (Debounce nhẹ) let searchTimeout; $('#searchInput').on('keyup', function() { clearTimeout(searchTimeout); const keyword = $(this).val().toLowerCase(); searchTimeout = setTimeout(() => { handleSearch(keyword); }, 300); }); // 3. Mobile Toggle $('#btnToggleSidebar').click(toggleSidebar); $('#overlay').click(closeSidebar); // 4. Canvas Events $('#btnRecognize').click(recognizeHandwriting); $('#btnClearHand').click(clearCanvas); // Mouse/Touch drawing $(canvas).on('mousedown touchstart', startDraw); $(canvas).on('mousemove touchmove', draw); $(canvas).on('mouseup mouseout touchend', stopDraw); // Prevent scrolling when drawing on mobile $(canvas).on('touchstart touchmove', function(e) { e.preventDefault(); }); } // --- LOGIC TÌM KIẾM & HIỂN THỊ LIST --- function handleSearch(keyword) { if (!keyword) { renderList(dictData.slice(0, 50)); $('#searchCount').text(`Tổng: ${dictData.length} mục từ`); return; } const results = dictData.filter(item => { return item.hanviet.toLowerCase().includes(keyword); }); renderList(results.slice(0, 100)); // Limit render $('#searchCount').text(`Tìm thấy ${results.length} kết quả`); } function renderList(items) { const list = $('#textResultList'); list.empty(); items.forEach(item => { // Tách chữ Hán và phiên âm (Giả định format: "Chữ PhienAm Nghia...") // Format thường gặp trong file data của bạn: "HánViệt..." let displayHan = ""; let displayViet = ""; if (item.hanviet) { const parts = item.hanviet.split(' '); displayHan = parts[0]; displayViet = parts.slice(1).join(' '); } const li = $(`
  • ${displayHan} ${displayViet}
  • `); li.click(() => showDetail(item)); list.append(li); }); } // --- LOGIC HIỂN THỊ CHI TIẾT --- function showDetail(item) { // Tự động đóng sidebar trên mobile if ($(window).width() <= 768) { closeSidebar(); } const parts = item.hanviet.split(' '); const char = parts[0]; const phonetics = parts.slice(1).join(' '); // Format lại nghĩa (thay thế ký tự đặc biệt) let formattedMean = item.nghia || ""; formattedMean = formattedMean.replace(/◇/g, '
    '); formattedMean = formattedMean.replace(/♦/g, '
    '); formattedMean = formattedMean.replace(/§/g, '§'); const html = `
    ${char}
    ${phonetics}
    Unicode: U+${char.charCodeAt(0).toString(16).toUpperCase()}
    ${formattedMean}
    `; $('#detailCard').html(html); // Highlight active item $('.list-item').removeClass('selected'); $(`.list-item[data-id="${item.id}"]`).addClass('selected'); } // --- LOGIC BỘ THỦ --- function initRadicalDropdown() { // Tạo set các số nét để render dropdown // Format radData: "số_nét|ký_tự|..." // Bỏ qua phần tử 0 rỗng for(let i=1; i<=17; i++) { // Max nét bộ thủ thường là 17 $('#radicalStrokes').append(``); } $('#radicalStrokes').change(function() { renderRadicals($(this).val()); }); } function renderRadicals(strokeCount) { const grid = $('#radicalList'); grid.empty(); // Bỏ qua index 0 for (let i = 1; i < radData.length; i++) { const line = radData[i]; const parts = line.split('|'); if (parts.length < 4) continue; const rStroke = parts[0]; const rChar = parts[1]; const rPinyin = parts[2]; const rViet = parts[3]; if (strokeCount == 0 || rStroke == strokeCount) { const box = $(`
    ${rChar} ${rStroke}n
    `); box.click(() => { // Chuyển sang tab search và tìm theo bộ thủ này $('.nav-btn[data-tab="text"]').click(); $('#searchInput').val(rChar); handleSearch(rChar); }); grid.append(box); } } } // --- LOGIC VIẾT TAY (CANVAS) --- function startDraw(e) { isDrawing = true; strokeCount++; // Đếm nét sơ bộ draw(e); } function stopDraw() { isDrawing = false; ctx.beginPath(); // Reset path để nét sau không dính nét trước } function draw(e) { if (!isDrawing) return; const rect = canvas.getBoundingClientRect(); let clientX, clientY; if (e.type.includes('touch')) { clientX = e.touches[0].clientX; clientY = e.touches[0].clientY; } else { clientX = e.clientX; clientY = e.clientY; } const x = clientX - rect.left; const y = clientY - rect.top; ctx.lineTo(x, y); ctx.stroke(); ctx.beginPath(); ctx.moveTo(x, y); } function clearCanvas() { ctx.clearRect(0, 0, canvas.width, canvas.height); strokeCount = 0; $('#handResults').empty(); } function recognizeHandwriting() { // Giả lập nhận dạng để UI hoạt động // (Logic thực tế sẽ cần port từ file handict.js cũ vốn rất phức tạp) const resDiv = $('#handResults'); resDiv.html('
    Đang phân tích...
    '); setTimeout(() => { resDiv.empty(); // Demo vài chữ const demo = ["一", "人", "大", "木", "本"]; demo.forEach(c => { const item = $(`
    ${c}
    `); item.click(() => { $('.nav-btn[data-tab="text"]').click(); $('#searchInput').val(c); handleSearch(c); }); resDiv.append(item); }); }, 500); } // --- UI HELPERS --- function toggleSidebar() { $('#sidebar').toggleClass('open'); $('#overlay').toggleClass('active'); } function openSidebar() { $('#sidebar').addClass('open'); $('#overlay').addClass('active'); } function closeSidebar() { $('#sidebar').removeClass('open'); $('#overlay').removeClass('active'); } });