tudienHanViet / tdScript.js
CVNSS's picture
Update tdScript.js
47f6034 verified
/**
* 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 = $(`
<li class="list-item" data-id="${item.id}">
<span class="item-han">${displayHan}</span>
<span class="item-viet">${displayViet}</span>
</li>
`);
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, '<br><span class="meaning-label">◇</span>');
formattedMean = formattedMean.replace(/♦/g, '<br><span class="meaning-label">♦</span>');
formattedMean = formattedMean.replace(/§/g, '<span class="meaning-label">§</span>');
const html = `
<div class="detail-header">
<div class="big-char">${char}</div>
<div class="detail-meta">
<div class="hanviet-main">${phonetics}</div>
<div class="pinyin">Unicode: U+${char.charCodeAt(0).toString(16).toUpperCase()}</div>
</div>
</div>
<div class="meaning-group">
${formattedMean}
</div>
`;
$('#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(`<option value="${i}">${i} nét</option>`);
}
$('#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 = $(`
<div class="radical-box" title="${rViet} (${rPinyin})">
<span class="r-char">${rChar}</span>
<span class="r-stroke">${rStroke}n</span>
</div>
`);
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('<div style="width:100%; text-align:center; color:#888;">Đang phân tích...</div>');
setTimeout(() => {
resDiv.empty();
// Demo vài chữ
const demo = ["一", "人", "大", "木", "本"];
demo.forEach(c => {
const item = $(`<div class="hand-res-item">${c}</div>`);
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');
}
});