CVNSS commited on
Commit
f2b38ab
·
verified ·
1 Parent(s): fb9216c

Update tdScript.js

Browse files
Files changed (1) hide show
  1. tdScript.js +304 -304
tdScript.js CHANGED
@@ -1,305 +1,305 @@
1
- /**
2
- * HÁN VIỆT TỪ ĐIỂN - MODERN CORE
3
- * Phiên bản tái cấu trúc bởi Chuyên gia (70 năm kinh nghiệm)
4
- */
5
-
6
- $(document).ready(function() {
7
-
8
- // --- BIẾN TOÀN CỤC ---
9
- let dictData = []; // Sẽ lấy từ dictionaryData.js
10
- let radData = []; // Sẽ lấy từ bothudata.js
11
-
12
- // Canvas vars
13
- let canvas = document.getElementById('handCanvas');
14
- let ctx = canvas.getContext('2d');
15
- let isDrawing = false;
16
- let strokeCount = 0;
17
-
18
- // --- KHỞI TẠO DỮ LIỆU ---
19
- initData();
20
- initEvents();
21
-
22
- function initData() {
23
- // Kiểm tra xem file dictionaryData.js đã load chưa
24
- if (typeof localDictionary !== 'undefined') {
25
- dictData = localDictionary;
26
- } else if (typeof dictionaryData !== 'undefined') {
27
- dictData = dictionaryData; // Fallback
28
- } else {
29
- $('#searchCount').text("Lỗi: Không tìm thấy dữ liệu từ điển!");
30
- }
31
-
32
- // Kiểm tra file bothudata.js
33
- if (typeof universalRadical !== 'undefined') {
34
- radData = universalRadical;
35
- initRadicalDropdown();
36
- renderRadicals(0); // Render all initially
37
- }
38
-
39
- // Render trang đầu
40
- renderList(dictData.slice(0, 50)); // Load 50 từ đầu tiên
41
- $('#searchCount').text(`Tổng: ${dictData.length} mục từ`);
42
-
43
- // Init Canvas Style
44
- ctx.lineWidth = 6;
45
- ctx.lineCap = 'round';
46
- ctx.lineJoin = 'round';
47
- ctx.strokeStyle = '#2C2C2C';
48
- }
49
-
50
- // --- XỬ LÝ SỰ KIỆN (EVENTS) ---
51
- function initEvents() {
52
- // 1. Chuyển Tab
53
- $('.nav-btn').click(function() {
54
- const tabId = $(this).data('tab');
55
-
56
- // UI Update
57
- $('.nav-btn').removeClass('active');
58
- $(this).addClass('active');
59
-
60
- $('.view-section').removeClass('active');
61
- $(`#view-${tabId}`).addClass('active');
62
-
63
- // Mobile sidebar logic
64
- if ($(window).width() <= 768) {
65
- openSidebar();
66
- }
67
- });
68
-
69
- // 2. Tìm kiếm (Debounce nhẹ)
70
- let searchTimeout;
71
- $('#searchInput').on('keyup', function() {
72
- clearTimeout(searchTimeout);
73
- const keyword = $(this).val().toLowerCase();
74
- searchTimeout = setTimeout(() => {
75
- handleSearch(keyword);
76
- }, 300);
77
- });
78
-
79
- // 3. Mobile Toggle
80
- $('#btnToggleSidebar').click(toggleSidebar);
81
- $('#overlay').click(closeSidebar);
82
-
83
- // 4. Canvas Events
84
- $('#btnRecognize').click(recognizeHandwriting);
85
- $('#btnClearHand').click(clearCanvas);
86
-
87
- // Mouse/Touch drawing
88
- $(canvas).on('mousedown touchstart', startDraw);
89
- $(canvas).on('mousemove touchmove', draw);
90
- $(canvas).on('mouseup mouseout touchend', stopDraw);
91
-
92
- // Prevent scrolling when drawing on mobile
93
- $(canvas).on('touchstart touchmove', function(e) { e.preventDefault(); });
94
- }
95
-
96
- // --- LOGIC TÌM KIẾM & HIỂN THỊ LIST ---
97
- function handleSearch(keyword) {
98
- if (!keyword) {
99
- renderList(dictData.slice(0, 50));
100
- $('#searchCount').text(`Tổng: ${dictData.length} mục từ`);
101
- return;
102
- }
103
-
104
- const results = dictData.filter(item => {
105
- return item.hanviet.toLowerCase().includes(keyword);
106
- });
107
-
108
- renderList(results.slice(0, 100)); // Limit render
109
- $('#searchCount').text(`Tìm thấy ${results.length} kết quả`);
110
- }
111
-
112
- function renderList(items) {
113
- const list = $('#textResultList');
114
- list.empty();
115
-
116
- items.forEach(item => {
117
- // Tách chữ Hán và phiên âm (Giả định format: "Chữ PhienAm Nghia...")
118
- // Format thường gặp trong file data của bạn: "HánViệt..."
119
- let displayHan = "";
120
- let displayViet = "";
121
-
122
- if (item.hanviet) {
123
- const parts = item.hanviet.split(' ');
124
- displayHan = parts[0];
125
- displayViet = parts.slice(1).join(' ');
126
- }
127
-
128
- const li = $(`
129
- <li class="list-item" data-id="${item.id}">
130
- <span class="item-han">${displayHan}</span>
131
- <span class="item-viet">${displayViet}</span>
132
- </li>
133
- `);
134
-
135
- li.click(() => showDetail(item));
136
- list.append(li);
137
- });
138
- }
139
-
140
- // --- LOGIC HIỂN THỊ CHI TIẾT ---
141
- function showDetail(item) {
142
- // Tự động đóng sidebar trên mobile
143
- if ($(window).width() <= 768) {
144
- closeSidebar();
145
- }
146
-
147
- const parts = item.hanviet.split(' ');
148
- const char = parts[0];
149
- const phonetics = parts.slice(1).join(' ');
150
-
151
- // Format lại nghĩa (thay thế ký tự đặc biệt)
152
- let formattedMean = item.nghia || "";
153
- formattedMean = formattedMean.replace(/◇/g, '<br><span class="meaning-label">◇</span>');
154
- formattedMean = formattedMean.replace(/♦/g, '<br><span class="meaning-label">♦</span>');
155
- formattedMean = formattedMean.replace(/§/g, '<span class="meaning-label">§</span>');
156
-
157
- const html = `
158
- <div class="detail-header">
159
- <div class="big-char">${char}</div>
160
- <div class="detail-meta">
161
- <div class="hanviet-main">${phonetics}</div>
162
- <div class="pinyin">Unicode: U+${char.charCodeAt(0).toString(16).toUpperCase()}</div>
163
- </div>
164
- </div>
165
- <div class="meaning-group">
166
- ${formattedMean}
167
- </div>
168
- `;
169
-
170
- $('#detailCard').html(html);
171
-
172
- // Highlight active item
173
- $('.list-item').removeClass('selected');
174
- $(`.list-item[data-id="${item.id}"]`).addClass('selected');
175
- }
176
-
177
- // --- LOGIC BỘ THỦ ---
178
- function initRadicalDropdown() {
179
- // Tạo set các số nét để render dropdown
180
- // Format radData: "số_nét|ký_tự|..."
181
- // Bỏ qua phần tử 0 rỗng
182
- for(let i=1; i<=17; i++) { // Max nét bộ thủ thường là 17
183
- $('#radicalStrokes').append(`<option value="${i}">${i} nét</option>`);
184
- }
185
-
186
- $('#radicalStrokes').change(function() {
187
- renderRadicals($(this).val());
188
- });
189
- }
190
-
191
- function renderRadicals(strokeCount) {
192
- const grid = $('#radicalList');
193
- grid.empty();
194
-
195
- // Bỏ qua index 0
196
- for (let i = 1; i < radData.length; i++) {
197
- const line = radData[i];
198
- const parts = line.split('|');
199
- if (parts.length < 4) continue;
200
-
201
- const rStroke = parts[0];
202
- const rChar = parts[1];
203
- const rPinyin = parts[2];
204
- const rViet = parts[3];
205
-
206
- if (strokeCount == 0 || rStroke == strokeCount) {
207
- const box = $(`
208
- <div class="radical-box" title="${rViet} (${rPinyin})">
209
- <span class="r-char">${rChar}</span>
210
- <span class="r-stroke">${rStroke}n</span>
211
- </div>
212
- `);
213
-
214
- box.click(() => {
215
- // Chuyển sang tab search và tìm theo bộ thủ này
216
- $('.nav-btn[data-tab="text"]').click();
217
- $('#searchInput').val(rChar);
218
- handleSearch(rChar);
219
- });
220
-
221
- grid.append(box);
222
- }
223
- }
224
- }
225
-
226
- // --- LOGIC VIẾT TAY (CANVAS) ---
227
- function startDraw(e) {
228
- isDrawing = true;
229
- strokeCount++; // Đếm nét sơ bộ
230
- draw(e);
231
- }
232
-
233
- function stopDraw() {
234
- isDrawing = false;
235
- ctx.beginPath(); // Reset path để nét sau không dính nét trước
236
- }
237
-
238
- function draw(e) {
239
- if (!isDrawing) return;
240
-
241
- const rect = canvas.getBoundingClientRect();
242
- let clientX, clientY;
243
-
244
- if (e.type.includes('touch')) {
245
- clientX = e.touches[0].clientX;
246
- clientY = e.touches[0].clientY;
247
- } else {
248
- clientX = e.clientX;
249
- clientY = e.clientY;
250
- }
251
-
252
- const x = clientX - rect.left;
253
- const y = clientY - rect.top;
254
-
255
- ctx.lineTo(x, y);
256
- ctx.stroke();
257
- ctx.beginPath();
258
- ctx.moveTo(x, y);
259
- }
260
-
261
- function clearCanvas() {
262
- ctx.clearRect(0, 0, canvas.width, canvas.height);
263
- strokeCount = 0;
264
- $('#handResults').empty();
265
- }
266
-
267
- function recognizeHandwriting() {
268
- // Giả lập nhận dạng để UI hoạt động
269
- // (Logic thực tế sẽ cần port từ file handict.js cũ vốn rất phức tạp)
270
- const resDiv = $('#handResults');
271
- resDiv.html('<div style="width:100%; text-align:center; color:#888;">Đang phân tích...</div>');
272
-
273
- setTimeout(() => {
274
- resDiv.empty();
275
- // Demo vài chữ
276
- const demo = ["一", "人", "大", "木", "本"];
277
- demo.forEach(c => {
278
- const item = $(`<div class="hand-res-item">${c}</div>`);
279
- item.click(() => {
280
- $('.nav-btn[data-tab="text"]').click();
281
- $('#searchInput').val(c);
282
- handleSearch(c);
283
- });
284
- resDiv.append(item);
285
- });
286
- }, 500);
287
- }
288
-
289
- // --- UI HELPERS ---
290
- function toggleSidebar() {
291
- $('#sidebar').toggleClass('open');
292
- $('#overlay').toggleClass('active');
293
- }
294
-
295
- function openSidebar() {
296
- $('#sidebar').addClass('open');
297
- $('#overlay').addClass('active');
298
- }
299
-
300
- function closeSidebar() {
301
- $('#sidebar').removeClass('open');
302
- $('#overlay').removeClass('active');
303
- }
304
-
305
  });
 
1
+ /**
2
+ * HÁN VIỆT TỪ ĐIỂN - MODERN CORE
3
+ * Phiên bản viết bởi Long Ngo, tác giả dùng AI trợ giúp
4
+ * Dự án này được hỗ trợ bởi CVNSS4.0 xem chi tiết https://chuvnsongsong.com/
5
+
6
+ $(document).ready(function() {
7
+
8
+ // --- BIẾN TOÀN CỤC ---
9
+ let dictData = []; // Sẽ lấy từ dictionaryData.js
10
+ let radData = []; // Sẽ lấy từ bothudata.js
11
+
12
+ // Canvas vars
13
+ let canvas = document.getElementById('handCanvas');
14
+ let ctx = canvas.getContext('2d');
15
+ let isDrawing = false;
16
+ let strokeCount = 0;
17
+
18
+ // --- KHỞI TẠO DỮ LIỆU ---
19
+ initData();
20
+ initEvents();
21
+
22
+ function initData() {
23
+ // Kiểm tra xem file dictionaryData.js đã load chưa
24
+ if (typeof localDictionary !== 'undefined') {
25
+ dictData = localDictionary;
26
+ } else if (typeof dictionaryData !== 'undefined') {
27
+ dictData = dictionaryData; // Fallback
28
+ } else {
29
+ $('#searchCount').text("Lỗi: Không tìm thấy dữ liệu từ điển!");
30
+ }
31
+
32
+ // Kiểm tra file bothudata.js
33
+ if (typeof universalRadical !== 'undefined') {
34
+ radData = universalRadical;
35
+ initRadicalDropdown();
36
+ renderRadicals(0); // Render all initially
37
+ }
38
+
39
+ // Render trang đầu
40
+ renderList(dictData.slice(0, 50)); // Load 50 từ đầu tiên
41
+ $('#searchCount').text(`Tổng: ${dictData.length} mục từ`);
42
+
43
+ // Init Canvas Style
44
+ ctx.lineWidth = 6;
45
+ ctx.lineCap = 'round';
46
+ ctx.lineJoin = 'round';
47
+ ctx.strokeStyle = '#2C2C2C';
48
+ }
49
+
50
+ // --- XỬ LÝ SỰ KIỆN (EVENTS) ---
51
+ function initEvents() {
52
+ // 1. Chuyển Tab
53
+ $('.nav-btn').click(function() {
54
+ const tabId = $(this).data('tab');
55
+
56
+ // UI Update
57
+ $('.nav-btn').removeClass('active');
58
+ $(this).addClass('active');
59
+
60
+ $('.view-section').removeClass('active');
61
+ $(`#view-${tabId}`).addClass('active');
62
+
63
+ // Mobile sidebar logic
64
+ if ($(window).width() <= 768) {
65
+ openSidebar();
66
+ }
67
+ });
68
+
69
+ // 2. Tìm kiếm (Debounce nhẹ)
70
+ let searchTimeout;
71
+ $('#searchInput').on('keyup', function() {
72
+ clearTimeout(searchTimeout);
73
+ const keyword = $(this).val().toLowerCase();
74
+ searchTimeout = setTimeout(() => {
75
+ handleSearch(keyword);
76
+ }, 300);
77
+ });
78
+
79
+ // 3. Mobile Toggle
80
+ $('#btnToggleSidebar').click(toggleSidebar);
81
+ $('#overlay').click(closeSidebar);
82
+
83
+ // 4. Canvas Events
84
+ $('#btnRecognize').click(recognizeHandwriting);
85
+ $('#btnClearHand').click(clearCanvas);
86
+
87
+ // Mouse/Touch drawing
88
+ $(canvas).on('mousedown touchstart', startDraw);
89
+ $(canvas).on('mousemove touchmove', draw);
90
+ $(canvas).on('mouseup mouseout touchend', stopDraw);
91
+
92
+ // Prevent scrolling when drawing on mobile
93
+ $(canvas).on('touchstart touchmove', function(e) { e.preventDefault(); });
94
+ }
95
+
96
+ // --- LOGIC TÌM KIẾM & HIỂN THỊ LIST ---
97
+ function handleSearch(keyword) {
98
+ if (!keyword) {
99
+ renderList(dictData.slice(0, 50));
100
+ $('#searchCount').text(`Tổng: ${dictData.length} mục từ`);
101
+ return;
102
+ }
103
+
104
+ const results = dictData.filter(item => {
105
+ return item.hanviet.toLowerCase().includes(keyword);
106
+ });
107
+
108
+ renderList(results.slice(0, 100)); // Limit render
109
+ $('#searchCount').text(`Tìm thấy ${results.length} kết quả`);
110
+ }
111
+
112
+ function renderList(items) {
113
+ const list = $('#textResultList');
114
+ list.empty();
115
+
116
+ items.forEach(item => {
117
+ // Tách chữ Hán và phiên âm (Giả định format: "Chữ PhienAm Nghia...")
118
+ // Format thường gặp trong file data của bạn: "HánViệt..."
119
+ let displayHan = "";
120
+ let displayViet = "";
121
+
122
+ if (item.hanviet) {
123
+ const parts = item.hanviet.split(' ');
124
+ displayHan = parts[0];
125
+ displayViet = parts.slice(1).join(' ');
126
+ }
127
+
128
+ const li = $(`
129
+ <li class="list-item" data-id="${item.id}">
130
+ <span class="item-han">${displayHan}</span>
131
+ <span class="item-viet">${displayViet}</span>
132
+ </li>
133
+ `);
134
+
135
+ li.click(() => showDetail(item));
136
+ list.append(li);
137
+ });
138
+ }
139
+
140
+ // --- LOGIC HIỂN THỊ CHI TIẾT ---
141
+ function showDetail(item) {
142
+ // Tự động đóng sidebar trên mobile
143
+ if ($(window).width() <= 768) {
144
+ closeSidebar();
145
+ }
146
+
147
+ const parts = item.hanviet.split(' ');
148
+ const char = parts[0];
149
+ const phonetics = parts.slice(1).join(' ');
150
+
151
+ // Format lại nghĩa (thay thế ký tự đặc biệt)
152
+ let formattedMean = item.nghia || "";
153
+ formattedMean = formattedMean.replace(/◇/g, '<br><span class="meaning-label">◇</span>');
154
+ formattedMean = formattedMean.replace(/♦/g, '<br><span class="meaning-label">♦</span>');
155
+ formattedMean = formattedMean.replace(/§/g, '<span class="meaning-label">§</span>');
156
+
157
+ const html = `
158
+ <div class="detail-header">
159
+ <div class="big-char">${char}</div>
160
+ <div class="detail-meta">
161
+ <div class="hanviet-main">${phonetics}</div>
162
+ <div class="pinyin">Unicode: U+${char.charCodeAt(0).toString(16).toUpperCase()}</div>
163
+ </div>
164
+ </div>
165
+ <div class="meaning-group">
166
+ ${formattedMean}
167
+ </div>
168
+ `;
169
+
170
+ $('#detailCard').html(html);
171
+
172
+ // Highlight active item
173
+ $('.list-item').removeClass('selected');
174
+ $(`.list-item[data-id="${item.id}"]`).addClass('selected');
175
+ }
176
+
177
+ // --- LOGIC BỘ THỦ ---
178
+ function initRadicalDropdown() {
179
+ // Tạo set các số nét để render dropdown
180
+ // Format radData: "số_nét|ký_tự|..."
181
+ // Bỏ qua phần tử 0 rỗng
182
+ for(let i=1; i<=17; i++) { // Max nét bộ thủ thường là 17
183
+ $('#radicalStrokes').append(`<option value="${i}">${i} nét</option>`);
184
+ }
185
+
186
+ $('#radicalStrokes').change(function() {
187
+ renderRadicals($(this).val());
188
+ });
189
+ }
190
+
191
+ function renderRadicals(strokeCount) {
192
+ const grid = $('#radicalList');
193
+ grid.empty();
194
+
195
+ // Bỏ qua index 0
196
+ for (let i = 1; i < radData.length; i++) {
197
+ const line = radData[i];
198
+ const parts = line.split('|');
199
+ if (parts.length < 4) continue;
200
+
201
+ const rStroke = parts[0];
202
+ const rChar = parts[1];
203
+ const rPinyin = parts[2];
204
+ const rViet = parts[3];
205
+
206
+ if (strokeCount == 0 || rStroke == strokeCount) {
207
+ const box = $(`
208
+ <div class="radical-box" title="${rViet} (${rPinyin})">
209
+ <span class="r-char">${rChar}</span>
210
+ <span class="r-stroke">${rStroke}n</span>
211
+ </div>
212
+ `);
213
+
214
+ box.click(() => {
215
+ // Chuyển sang tab search và tìm theo bộ thủ này
216
+ $('.nav-btn[data-tab="text"]').click();
217
+ $('#searchInput').val(rChar);
218
+ handleSearch(rChar);
219
+ });
220
+
221
+ grid.append(box);
222
+ }
223
+ }
224
+ }
225
+
226
+ // --- LOGIC VIẾT TAY (CANVAS) ---
227
+ function startDraw(e) {
228
+ isDrawing = true;
229
+ strokeCount++; // Đếm nét sơ bộ
230
+ draw(e);
231
+ }
232
+
233
+ function stopDraw() {
234
+ isDrawing = false;
235
+ ctx.beginPath(); // Reset path để nét sau không dính nét trước
236
+ }
237
+
238
+ function draw(e) {
239
+ if (!isDrawing) return;
240
+
241
+ const rect = canvas.getBoundingClientRect();
242
+ let clientX, clientY;
243
+
244
+ if (e.type.includes('touch')) {
245
+ clientX = e.touches[0].clientX;
246
+ clientY = e.touches[0].clientY;
247
+ } else {
248
+ clientX = e.clientX;
249
+ clientY = e.clientY;
250
+ }
251
+
252
+ const x = clientX - rect.left;
253
+ const y = clientY - rect.top;
254
+
255
+ ctx.lineTo(x, y);
256
+ ctx.stroke();
257
+ ctx.beginPath();
258
+ ctx.moveTo(x, y);
259
+ }
260
+
261
+ function clearCanvas() {
262
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
263
+ strokeCount = 0;
264
+ $('#handResults').empty();
265
+ }
266
+
267
+ function recognizeHandwriting() {
268
+ // Giả lập nhận dạng để UI hoạt động
269
+ // (Logic thực tế sẽ cần port từ file handict.js cũ vốn rất phức tạp)
270
+ const resDiv = $('#handResults');
271
+ resDiv.html('<div style="width:100%; text-align:center; color:#888;">Đang phân tích...</div>');
272
+
273
+ setTimeout(() => {
274
+ resDiv.empty();
275
+ // Demo vài chữ
276
+ const demo = ["一", "人", "大", "木", "本"];
277
+ demo.forEach(c => {
278
+ const item = $(`<div class="hand-res-item">${c}</div>`);
279
+ item.click(() => {
280
+ $('.nav-btn[data-tab="text"]').click();
281
+ $('#searchInput').val(c);
282
+ handleSearch(c);
283
+ });
284
+ resDiv.append(item);
285
+ });
286
+ }, 500);
287
+ }
288
+
289
+ // --- UI HELPERS ---
290
+ function toggleSidebar() {
291
+ $('#sidebar').toggleClass('open');
292
+ $('#overlay').toggleClass('active');
293
+ }
294
+
295
+ function openSidebar() {
296
+ $('#sidebar').addClass('open');
297
+ $('#overlay').addClass('active');
298
+ }
299
+
300
+ function closeSidebar() {
301
+ $('#sidebar').removeClass('open');
302
+ $('#overlay').removeClass('active');
303
+ }
304
+
305
  });