Hệ thống pháp luật

BỘ VĂN HÓA, THỂ THAO
VÀ DU LỊCH
-------

CỘNG HÒA XÃ HỘI CHỦ NGHĨA VIỆT NAM
Độc lập - Tự do - Hạnh phúc
---------------

Số: 5251/VBHN-BVHTTDL

Hà Nội, ngày 29 tháng 11 năm 2023

 

THÔNG TƯ

QUY ĐỊNH QUY TRÌNH GIÁM ĐỊNH TƯ PHÁP ĐỐI VỚI DI VẬT, CỔ VẬT

Thông tư số 03/2019/TT-BVHTTDL ngày 05 tháng 7 năm 2019 của Bộ trưởng Bộ Văn hóa, Thể thao và Du lịch quy định quy trình giám định tư pháp đối với di vật, cổ vật có hiệu lực thi hành kể từ ngày 01 tháng 9 năm 2019 được bổ sung bởi:

Thông tư số 03/2021/TT-BVHTTDL ngày 01 tháng 6 năm 2021 của Bộ trưởng Bộ Văn hóa, Thể thao và Du lịch bổ sung một số điều của các Thông tư quy định quy trình giám định tư pháp trong lĩnh vực văn hóa, có hiệu lực từ ngày 01 tháng 8 năm 2021.

Căn cứ Luật Giám định tư pháp ngày 20 tháng 6 năm 2012;

Căn cứ Luật Di sản văn hóa ngày 29 tháng 6 năm 2001 và Luật sửa đổi, bổ sung một số điều của Luật Di sản văn hóa ngày 18 tháng 6 năm 2009;

Căn cứ Nghị định số 85/2013/NĐ-CP ngày 29 tháng 7 năm 2013 của Chính phủ quy định chi tiết và biện pháp thi hành Luật Giám định tư pháp;

Căn cứ Nghị định số 98/2010/NĐ-CP ngày 21 tháng 9 năm 2010 của Chính phủ quy định chi tiết thi hành một số điều của Luật Di sản văn hóa và Luật sửa đổi, bổ sung một số điều của Luật Di sản văn hóa;

Căn cứ Nghị định số 79/2017/NĐ-CP ngày 17 tháng 7 năm 2017 của Chính phủ quy định chức năng, nhiệm vụ quyền hạn và cơ cấu tổ chức của Bộ Văn hóa, Thể thao và Du lịch;

Theo đề nghị của Vụ trưởng Vụ Pháp chế;

Bộ trưởng Bộ Văn hóa, Thể thao và Du lịch ban hành Thông tư quy định quy trình giám định tư pháp đối với di vật, cổ vật.Chương I

NHỮNG QUY ĐỊNH CHUNG

Điều 1. Phạm vi điều chỉnh

Thông tư này quy định quy trình giám định tư pháp để xác định một hiện vật là di vật, cổ vật hoặc không là di vật, cổ vật theo trưng cầu của cơ quan tiến hành tố tụng, người tiến hành tố tụng hoặc theo yêu cầu của người yêu cầu giám định tư pháp.

Điều 2. Đối tượng áp dụng

Thông tư này áp dụng đối với người giám định tư pháp, tổ chức giám định tư pháp và các tổ chức, cá nhân khác có liên quan đến giám định tư pháp đối với di vật, cổ vật.

Điều 3. Giải thích từ ngữ

Trong Thông tư này, những từ ngữ dưới đây được hiểu như sau:

1. Di vật, cổ vật là hiện vật được xác định theo quy định của pháp luật về di sản văn hóa.

2. Người giám định tư pháp đối với di vật, cổ vật bao gồm giám định viên tư pháp, người giám định tư pháp theo vụ việc trong lĩnh vực văn hóa thuộc chuyên ngành phù hợp đã được bổ nhiệm, công bố theo quy định của pháp luật về giám định tư pháp.

3. Tổ chức giám định tư pháp đối với di vật, cổ vật bao gồm Bộ Văn hóa, Thể thao và Du lịch, Sở Văn hóa, Thể thao và Du lịch, Sở Văn hóa và Thể thao các tỉnh, thành phố trực thuộc Trung ương, Văn phòng giám định tư pháp, tổ chức giám định tư pháp theo vụ việc có hoạt động chuyên môn phù hợp đã được công bố theo quy định của pháp luật về giám định tư pháp.

Chương II

QUY TRÌNH GIÁM ĐỊNH TƯ PHÁP ĐỐI VỚI DI VẬT, CỔ VẬT

Điều 4. Tiếp nhận yêu cầu, trưng cầu giám định

1. Người giám định tư pháp đối với di vật, cổ vật (sau đây gọi là người giám định tư pháp), tổ chức giám định tư pháp đối với di vật, cổ vật (sau đây gọi là tổ chức giám định tư pháp) tiếp nhận trưng cầu, yêu cầu giám định kèm theo đối tượng giám định và tài liệu, đồ vật có liên quan (nếu có) để thực hiện giám định; trường hợp không đủ điều kiện giám định thì từ chối theo quy định của pháp luật.

2. Việc giao, nhận hồ sơ, đối tượng trưng cầu, yêu cầu giám định tư pháp đối với di vật, cổ vật thực hiện theo quy định tại Điều 3 Thông tư số 07/2014/TT- BVHTTDL ngày 23 tháng 7 năm 2014 của Bộ trưởng Bộ Văn hóa, Thể thao và Du lịch quy định hồ sơ, các mẫu văn bản thực hiện giám định tư pháp; áp dụng quy chuẩn chuyên môn cho hoạt động giám định tư pháp trong lĩnh vực văn hóa; điều kiện cơ sở vật chất, trang thiết bị, phương tiện giám định của Văn phòng giám định tư pháp về di vật, cổ vật, quyền tác giả, quyền liên quan (sau đây gọi là Thông tư 07/2014/TT-BVHTTDL).

Điều 5. Chuẩn bị thực hiện giám định

1. Người giám định tư pháp, tổ chức giám định tư pháp tiến hành nghiên cứu hồ sơ trưng cầu, yêu cầu và các quy định cụ thể của pháp luật về di sản văn hóa để chuẩn bị thực hiện giám định tư pháp. Trường hợp cần làm rõ thêm về nội dung trưng cầu, yêu cầu giám định, đối tượng giám định tư pháp thì đề nghị người trưng cầu, yêu cầu cung cấp thêm thông tin, tài liệu có liên quan.

2. Trường hợp cần thiết, người giám định tư pháp tổ chức lấy kết quả xét nghiệm hoặc kết luận chuyên môn khác trước khi đưa ra đánh giá.

3. Tổ chức giám định tư pháp quyết định thực hiện giám định tư pháp đối với di vật, cổ vật bằng hình thức giám định tập thể. Số lượng người giám định tư pháp phải từ 03 người trở lên.

Quyết định thực hiện giám định tư pháp đối với di vật, cổ vật thực hiện theo Mẫu số 01 tại Phụ lục ban hành kèm theo Thông tư này.

4. Tổ chức giám định tư pháp căn cứ vào hồ sơ trưng cầu, yêu cầu giám định để lựa chọn giám định viên tư pháp, người giám định tư pháp theo vụ việc phù hợp, phân công người chịu trách nhiệm điều phối việc thực hiện giám định tư pháp.

Điều 6. Thực hiện giám định

1. Người giám định tư pháp xem xét đối tượng giám định để xác định niên đại (tuyệt đối hoặc tương đối) và các giá trị về lịch sử, văn hóa, khoa học. Việc xem xét đối tượng giám định bao gồm một hoặc các nội dung sau đây:

a) Hình dáng, kích thước, chất liệu, thành phần hóa học, màu sắc và hoa văn trang trí, văn tự trên hiện vật;

b) Các dấu hiệu khác có liên quan.

2. Đối với đối tượng giám định không thể di chuyển hoặc khó di chuyển, người giám định tư pháp phải tổ chức xem xét đối tượng giám định tại nơi lưu giữ của người yêu cầu, trưng cầu. Việc tổ chức xem xét đối tượng giám định tại nơi lưu giữ của người trưng cầu, yêu cầu phải được lập thành biên bản và được lưu trong hồ sơ giám định.

Biên bản xem xét đối tượng giám định thực hiện theo Mẫu số 02 tại Phụ lục ban hành kèm theo Thông tư này.

3. Người giám định tư pháp có trách nhiệm ghi nhận kịp thời, đầy đủ, trung thực toàn bộ quá trình giám định, kết quả thực hiện giám định bằng văn bản và được lưu trong hồ sơ giám định.

Văn bản ghi nhận quá trình thực hiện giám định thực hiện theo Mẫu số 02 ban hành kèm theo Thông tư số 07/2014/TT-BVHTTDL.

Điều 6a. Thời hạn giám định1. Thời hạn giám định đối với trường hợp bắt buộc phải trưng cầu giám định được thực hiện theo quy định tại Điều 206 Bộ luật Tố tụng hình sự.

2. Thời hạn giám định đối với trường hợp không thuộc khoản 1 Điều này tối đa là 02 tháng tính theo quy định tại khoản 1 Điều 26a Luật Giám định tư pháp được bổ sung theo quy định tại khoản 16 Điều 1 Luật sửa đổi, bổ sung một số điều của Luật Giám định tư pháp.

3. Thời hạn giám định có thể được gia hạn theo quyết định của cơ quan trưng cầu giám định nhưng không quá một phần hai thời hạn giám định tối đa quy định tại khoản 2 Điều này.

4. Người trưng cầu giám định có thể thống nhất về thời hạn giám định với tổ chức, cá nhân được trưng cầu giám định trước khi trưng cầu giám định nhưng không quá thời hạn quy định tại khoản 2 và 3 Điều này.

5. Trường hợp có vấn đề phát sinh hoặc có cơ sở để cho rằng vụ việc giám định không thể hoàn thành đúng thời hạn thì cá nhân, tổ chức thực hiện giám định phải kịp thời thông báo bằng văn bản, nêu rõ lý do cho người trưng cầu giám định và thời gian dự kiến hoàn thành, ra kết luận giám định.

Điều 7. Kết luận giám định

Căn cứ kết quả giám định tư pháp, kết quả xét nghiệm hoặc kết luận chuyên môn khác (nếu có) và quy định của pháp luật về di sản văn hóa, người giám định tư pháp kết luận đối tượng giám định là di vật, cổ vật hoặc không phải di vật, cổ vật. Trường hợp có đủ căn cứ, người giám định tư pháp có thể kết luận thêm về giá trị lịch sử, văn hóa, khoa học của di vật, cổ vật.

Kết luận giám định thực hiện theo Mẫu số 04a và 04b ban hành kèm theo Thông tư số 07/2014/TT-BVHTTDL.

Điều 8. Bàn giao kết luận giám định

Khi việc thực hiện giám định tư pháp đối với di vật, cổ vật hoàn thành, người giám định tư pháp, tổ chức giám định tư pháp có trách nhiệm bàn giao kết luận giám định cho người trưng cầu, yêu cầu giám định.

Biên bản bàn giao kết luận giám định thực hiện theo Mẫu số 05 ban hành kèm theo Thông tư số 07/2014/TT-BVHTTDL.

Điều 9. Lập hồ sơ, lưu giữ hồ sơ giám định

Người giám định tư pháp, tổ chức giám định tư pháp có trách nhiệm lập hồ sơ giám định tư pháp đối với di vật, cổ vật theo quy định tại khoản 1 Điều 33 của Luật Giám định tư pháp và quy định tại Thông tư này.

Việc bảo quản, lưu giữ hồ sơ giám định tư pháp thực hiện theo quy định của pháp luật về lưu trữ.

Chương III

ĐIỀU KHOẢN THI HÀNH

Điều 10. Tổ chức thực hiện

1. Vụ Pháp chế chủ trì, phối hợp với các cơ quan, đơn vị có liên quan hướng dẫn, tổ chức kiểm tra việc thực hiện Thông tư này.

2. Sở Văn hóa, Thể thao và Du lịch, Sở Văn hóa và Thể thao các tỉnh, thành phố trực thuộc Trung ương có trách nhiệm tổ chức triển khai thực hiện, kiểm tra, đôn đốc các cơ quan, tổ chức, cá nhân có liên quan thực hiện Thông tư này.

Điều 11. Hiệu lực thi hành

1. Thông tư này có hiệu lực thi hành kể từ ngày 01 tháng 9 năm 2019.

2. Trong quá trình thực hiện, nếu phát sinh vướng mắc, đề nghị các cơ quan, đơn vị, cá nhân kịp thời phản ánh về Bộ Văn hoá, Thể thao và Du lịch (qua Vụ Pháp chế) để nghiên cứu sửa đổi, bổ sung cho phù hợp./.

 


Nơi nhận:
- Văn phòng Chính phủ (để đăng Công báo);
- Bộ trưởng;
- Các Thứ trưởng;
- Cổng thông tin điện tử của Bộ (để đăng tải);
- Cổng thông tin điện tử Chính phủ (để đăng tải);
- Các cơ quan, đơn vị trực thuộc Bộ;
- Sở VHTTDL; Sở VHTT; SVHTTTTDL;
- Lưu: VT, PC (120).

XÁC THỰC VĂN BẢN HỢP NHẤT

KT. BỘ TRƯỞNG
THỨ TRƯỞNG




Trịnh Thị Thủy

 

PHỤ LỤC

(Kèm theo Thông tư số 02/2019/TT-BVHTTDL ngày 05 tháng 7 năm 2019 của Bộ trưởng Bộ Văn hóa, Thể thao và Du lịch)

Mẫu số 01: Biên bản cung cấp mẫu giám định

Mẫu số 02: Quyết định thực hiện giám định tư pháp về quyền tác giả, quyền liên quan

Mẫu số 01. Biên bản cung cấp mẫu giám định

 

(1)........................
-------

CỘNG HÒA XÃ HỘI CHỦ NGHĨA VIỆT NAM
Độc lập - Tự do - Hạnh phúc
---------------

BIÊN BẢN

CUNG CẤP MẪU GIÁM ĐỊNH

Hôm nay, hồi........ giờ...... ngày...... tháng..... năm..... tại:.......................................................................................................................... (2)

Chúng tôi gồm:

1. Đại diện người trưng cầu/yêu cầu giám định:

+ Ông (bà)..............................................................chức vụ...............................

2. Đại diện.....................................................................................................(3):

+ Ông (bà)..............................................................chức vụ...............................

3. Người chứng kiến (nếu có):

+ Ông (bà)......................................................................................................(4)

Tiến hành cung cấp mẫu giám định đối với vụ việc theo Quyết định trưng cầu/yêu cầu giám định số.... (5) như sau:

.....................................................................................................................................

.....................................................................................................................................

(Chú ý: Ghi rõ tên, loại, số, ký hiệu, ngày, tháng, năm, trích yếu nội dung thông tin và tình trạng của mẫu giám định; cách thức bảo quản mẫu giám định khi được cung cấp).

Biên bản cung cấp mẫu giám định đã được đọc lại cho những người có tên nêu trên nghe và đại diện ký xác nhận; biên bản được lập thành 02 bản, mỗi bên giữ 01 bản.

Việc giao nhận hoàn thành hồi....... giờ............ ngày....../....../......

 

ĐẠI DIỆN CƠ QUAN TRƯNG CẦU/YÊU CẦU
(Ký, ghi rõ họ tên)

NGƯỜI CHỨNG KIẾN
(Ký, ghi rõ họ tên)

ĐẠI DIỆN
........................(3)
(Ký, ghi rõ họ tên)

____________________

(1) Tên cơ quan/Giám định viên tiếp nhận trưng cầu.

(2) Địa điểm cung cấp mẫu giám định.

(3) Tên cơ quan, đơn vị hoặc giám định viên tiếp nhận trưng cầu.

(4) Ghi rõ họ tên, chức vụ, địa chỉ người chứng kiến.

(5) Số văn bản trưng cầu (hoặc yêu cầu) giám định.

 

Mẫu số 02. Quyết định thực hiện giám định tư pháp về quyền tác giả, quyền liên quan

.............(1)
-------

CỘNG HÒA XÃ HỘI CHỦ NGHĨA VIỆT NAM
Độc lập - Tự do - Hạnh phúc
---------------

Số:    /QĐ-......(2)

.....(3), ngày    tháng    năm

 

QUYẾT ĐỊNH

Về việc thực hiện giám định tư pháp theo Quyết định trưng cầu/yêu cầu giám định số..... ngày..... tháng..... năm.... của.......(4)

........................(5)

Căn cứ Luật Giám định tư pháp ngày 20 tháng 6 năm 2012;

Căn cứ Nghị định số 85/2013/NĐ-CP ngày 29 tháng 7 năm 2013 của Chính phủ quy định chi tiết và biện pháp thi hành Luật Giám định tư pháp;

Căn cứ Thông tư số 04/2013/TT-BVHTTDL ngày 03 tháng 5 năm 2013 của Bộ trưởng Bộ Văn hóa, Thể thao và Du lịch quy định tiêu chuẩn, hồ sơ, thủ tục bổ nhiệm, miễn nhiệm giám định viên tư pháp; lập và công bố danh sách người giám định tư pháp, tổ chức giám định tư pháp theo vụ việc; thủ tục cử người tham gia giám định tư pháp và thành lập hội đồng giám định trong lĩnh vực văn hóa;

Căn cứ......................(6);

Căn cứ Quyết định trưng cầu/yêu cầu giám định số.... ngày... tháng... năm.... của.......(4);

Xét đề nghị của.... (nếu có),

QUYẾT ĐỊNH:

Điều 1. Thực hiện giám định tư pháp đối với vụ án/vụ việc theo Quyết định trưng cầu/yêu cầu giám định số.... ngày... tháng.... năm..... của.....(4) bằng hình thức..............(7), thành viên tham gia giám định như sau:

1....;

2.....;

3.....; (8)

Điều 2. Giao....(9) chủ trì tổ chức thực hiện giám định tư pháp theo quy định của pháp luật.....(9) cử...(10) đồng chí là người giúp việc cho người giám định tư pháp.

Điều 3..........(11) chịu trách nhiệm thi hành Quyết định này./.

 


Nơi nhận:

..........(5)
 (Ký, đóng dấu)
Họ và tên

____________________

(1) Tên tổ chức ra quyết định thực hiện giám định.

(2) Viết tắt in hoa tên tổ chức ra quyết định thực hiện giám định.

(3) Địa điểm quyết định thực hiện giám định tư pháp.

(4) Ghi rõ số, ngày, tháng, năm, cơ quan, tổ chức trưng cầu/yêu cầu giám định.

(5) Người có thẩm quyền của tổ chức giám định tư pháp ra quyết định thực hiện giám định.

(6) Căn cứ xác định thẩm quyền của người ra quyết định thực hiện giám định tư pháp.

(7) Ghi rõ hình thức giám định là tập thể hoặc cá nhân

(8) Tên, chức danh, đơn vị công tác của từng người thực hiện giám định, phải ít nhất từ 03 người trở lên, trong đó có 01 người thuộc chuyên ngành đào tạo về luật.

(9) Đơn vị làm đầu mối trong công tác giám định tư pháp của tổ chức giám định tư pháp.

(10) Số người giúp việc cho người giám định tư pháp.

(11) Các tổ chức, cá nhân có liên quan chịu trách nhiệm thực hiện quyết định.

 



lồng nhau (bên trong) hay không const memberID = 0; const vbID = '259dcd55abbe0f0f4a363373e67d708d'; // State management cho phân tích let isAnalyzing = false; // Có đang phân tích không let currentAnalyzingAddress = null; // Address đang được phân tích let currentAnalyzingElement = null; // Element đang được phân tích let currentAnalyzingBadge = null; // Badge của element đang phân tích let isPanelOpen = false; // Panel phân tích có đang mở không console.log('Tiện ích loaded - memberID:', memberID, 'vbID:', vbID); function isInViewportAndTabNoiDung(element) { const rect = element.getBoundingClientRect(); const buffer = 1500; // Buffer to preload content below the viewport (approx. 50+ lines) const viewHeight = window.innerHeight || document.documentElement.clientHeight; const isInViewport = rect.top < viewHeight + buffer && rect.bottom >= 0; const isInTabNoiDung = $(element).closest('#tab_noi_dung_vb').length > 0; return isInViewport && isInTabNoiDung; } function getAddress(element) { const validTags = ['trichyeu', 'cancu', 'phan', 'chuong', 'muc', 'tieumuc', 'dieu', 'khoan', 'diem']; const $parent = $(element).closest(validTags.join(',')); if (!$parent.length) { return null; } let addr = $parent.attr('address'); if (!addr && $parent.prop('tagName').toLowerCase() === 'trichyeu') { addr = 'trichyeu'; $parent.attr('address', addr); } return addr || null; } function processTnplClasses($element) { const tnplKeysInLine = new Set(); // key = slug hoặc text (thường là slug) $element.find('tnpl').each(function () { const $tnpl = $(this); const tnplSlug = ($tnpl.attr('slug') || '').trim().toLowerCase(); const tnplKey = tnplSlug || $tnpl.text().trim().toLowerCase(); // Đã xử lý trong cùng dòng => bỏ if (tnplKeysInLine.has(tnplKey)) { return; } tnplKeysInLine.add(tnplKey); let tnplExists = false; // Chỉ duyệt các tnpl đã được tô màu (class on) $('tnpl.on').each(function () { const $existingTnpl = $(this); const existingSlug = ($existingTnpl.attr('slug') || '').trim().toLowerCase(); const existingKey = existingSlug || $existingTnpl.text().trim().toLowerCase(); if ( existingKey === tnplKey && isInViewportAndTabNoiDung($existingTnpl[0]) ) { tnplExists = true; return false; // break each } }); if (!tnplExists) { $tnpl.addClass('on'); } }); } function processQueue() { while (pendingRequests < maxConcurrentRequests && requestQueue.length > 0) { const task = requestQueue.shift(); pendingRequests++; task() .always(() => { pendingRequests--; processQueue(); }); } } function processVisibleParagraphs() { try { $('#tab_noi_dung_vb p:not([is-posted="1"])').each(function () { let $element = $(this); if (isInViewportAndTabNoiDung(this)) { $element.attr('is-posted', '1'); $element.addClass('loading-content'); let p_innerHTML = $element.html(); let address = null; if (cac_cau_hinh.loai_noi_dung.includes('docs')) { address = getAddress($element); } const isSubP = $element.parents('p').length > 0; if (isSubP && !allow_sub_p) { $element.removeClass('loading-content'); return; // Không gửi nếu không cho phép } const postData = { p_content: p_innerHTML, cac_cau_hinh, address }; if (isSubP && allow_sub_p) { postData.sub_p = 1; } requestQueue.push(() => $.ajax({ url: '//tnpl' + (Math.floor(Math.random() * 10) + 1) + '.hethongphapluat.com/tien-ich/tim.tien.ich.php', type: 'POST', data: postData, success: function(response) { $element.html(response); processTnplClasses($element); // Đợi CTTD và các tiện ích load xong rồi mới attach badge if (memberID === 4 && typeof attachPhanTichBadge === 'function') { setTimeout(function() { // $element chính là thẻ p, kiểm tra và attach badge trực tiếp const $parent = $element.closest('phan, chuong, muc, tieumuc, dieu, khoan, diem'); if ($parent.length > 0 && $parent.find('.badge-phan-tich[data-for="' + $parent.attr('address') + '"]').length === 0) { const address = $parent.attr('address'); $element.attr('data-address', address); // Lấy tên loại thẻ cho tooltip const parentType = getParentTypeName($parent.prop('tagName').toLowerCase()); // Append badge VÀO PARENT (dieu, khoan,...) thay vì vào

để tránh xung đột CTTD const $badge = $('Phân tích'); $parent.append($badge); // Thêm class để CSS set position: relative CHỈ cho element có badge $parent.addClass('has-phan-tich-badge'); } // Xử lý các p con (nếu có sub-p) attachPhanTichBadge($element); }, 300); // Đợi 300ms để CTTD render xong } }, complete: function() { $element.removeClass('loading-content'); } }) ); processQueue(); } }); } catch(e) { console.error('Lỗi processVisibleParagraphs:', e); } } $(window).on('scroll resize', function () { processVisibleParagraphs(); }); console.log('Bắt đầu processVisibleParagraphs lần đầu...'); processVisibleParagraphs(); console.log('processVisibleParagraphs lần đầu hoàn thành'); // Chức năng phân tích điều luật (chỉ cho member_id = 4) if (memberID === 4 || memberID === 3) { // Modal cảnh báo function showWarningModal(message) { // Tạo modal nếu chưa có if ($('#warningModal').length === 0) { const modalHTML = `

`; $('body').append(modalHTML); } $('#warningModalBody').html('

' + message + '

'); $('#warningModal').modal('show'); } // Hàm lấy tên tiếng Việt của thẻ function getParentTypeName(tagName) { const typeNames = { 'phan': 'Phần', 'chuong': 'Chương', 'muc': 'Mục', 'tieumuc': 'Tiểu mục', 'dieu': 'Điều', 'khoan': 'Khoản', 'diem': 'Điểm' }; return typeNames[tagName] || 'Nội dung'; } function attachPhanTichBadge($container) { const validTags = 'phan, chuong, muc, tieumuc, dieu, khoan, diem'; $container.find('p').each(function() { const $p = $(this); const $parent = $p.closest(validTags); if ($parent.length > 0) { const address = $parent.attr('address'); // Kiểm tra đã có badge cho parent này chưa if ($parent.find('.badge-phan-tich[data-for="' + address + '"]').length === 0) { // Lưu address vào data attribute $p.attr('data-address', address); // Lấy tên loại thẻ cho tooltip const parentType = getParentTypeName($parent.prop('tagName').toLowerCase()); // Append badge vào PARENT, không vào

const $badge = $('Phân tích'); $parent.append($badge); // Thêm class để CSS set position: relative CHỈ cho element có badge $parent.addClass('has-phan-tich-badge'); } } }); } // Helper: Escape HTML entities function escapeHtml(text) { const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return String(text).replace(/[&<>"']/g, function(m) { return map[m]; }); } // Helper: Convert Markdown to HTML (đơn giản) function markdownToHtml(markdown) { if (!markdown) return ''; let html = markdown; // Headers html = html.replace(/^### (.*$)/gim, '

$1
'); html = html.replace(/^## (.*$)/gim, '

$1

'); html = html.replace(/^# (.*$)/gim, '

$1

'); // Bold html = html.replace(/\*\*(.*?)\*\*/g, '$1'); // Italic html = html.replace(/\*(.*?)\*/g, '$1'); // Blockquote html = html.replace(/^> (.*$)/gim, '
$1
'); html = html.replace(/^> (.*$)/gim, '
$1
'); // Lists (unordered) html = html.replace(/^\- (.*$)/gim, '
  • $1
  • '); html = html.replace(/(
  • .*<\/li>)/s, '
      $1
    '); // Lists (ordered) html = html.replace(/^\d+\. (.*$)/gim, '
  • $1
  • '); // Line breaks và paragraphs html = html.split('\n\n').map(para => { para = para.trim(); if (para.startsWith('')) { return para; } if (para) { return '

    ' + para.replace(/\n/g, '
    ') + '

    '; } return ''; }).join('\n'); // Clean up multiple line breaks html = html.replace(/\n{3,}/g, '\n\n'); return html; } // Panel fixed position function closePhanTichPanel() { const $panel = $('#phanTichPanel'); if ($panel.length) { $panel.removeClass('show'); setTimeout(() => { $panel.remove(); }, 300); } // Reset highlight và badge khi đóng panel if (currentAnalyzingElement) { currentAnalyzingElement.removeClass('highlight-border-persistent'); } if (currentAnalyzingBadge) { currentAnalyzingBadge.text('Phân tích').removeClass('analyzing'); currentAnalyzingBadge.data('analyzing', false); currentAnalyzingBadge.data('hovering', false); currentAnalyzingBadge.css({display: 'none'}); // Ẩn badge khi đóng } // Reset tất cả các element khác (trong trường hợp có nhiều) $('#tab_noi_dung_vb .highlight-border-persistent').removeClass('highlight-border-persistent'); $('#tab_noi_dung_vb .badge-phan-tich-container.analyzing').each(function() { $(this).text('Phân tích').removeClass('analyzing').data('analyzing', false); }); // Check: có CTTD pointer đang mở không? const $visiblePointers = $('.pointer:visible'); const hadCTTDOpen = $visiblePointers.length > 0; if (hadCTTDOpen) { // CÓ CTTD đang mở → giữ rightdocinfo ẩn console.log('ℹ️ CTTD pointer is visible, keeping rightdocinfo hidden'); } else { // KHÔNG có CTTD → SHOW lại rightdocinfo const $rightdocinfo = $('#rightdocinfo'); if ($rightdocinfo.length > 0) { $rightdocinfo.show(); console.log('✅ Showing rightdocinfo back (no CTTD pointer)'); } } // Reset state isAnalyzing = false; currentAnalyzingAddress = null; currentAnalyzingElement = null; currentAnalyzingBadge = null; isPanelOpen = false; // Đánh dấu panel đã đóng console.log('✅ Panel closed, state reset, isPanelOpen = false'); } // Panel đã song song với rightdocinfo → không cần MutationObserver nữa console.log('✅ Panel running in standalone mode (parallel to rightdocinfo)'); // Resize event để update panel dimensions khi browser resize let resizeTimer; $(window).on('resize', function() { clearTimeout(resizeTimer); resizeTimer = setTimeout(function() { if (isPanelOpen && $('#phanTichPanel').length > 0) { updatePanelDimensions(); console.log('✅ Panel dimensions updated on window resize'); } }, 250); // Debounce 250ms }); // Function để detect và áp dụng dimensions từ rightdocinfo function updatePanelDimensions() { const $panel = $('#phanTichPanel'); const $rightdocinfo = $('#rightdocinfo'); const $docRightCol = $('#doc-right-col'); if ($panel.length === 0) return; // Ưu tiên: doc-right-col > rightdocinfo let $reference = $docRightCol.length > 0 ? $docRightCol : $rightdocinfo; // Nếu reference bị ẩn (display:none), tạm show để get dimensions let wasHidden = false; if ($reference.length > 0 && !$reference.is(':visible')) { wasHidden = true; $reference.css('visibility', 'hidden').show(); } if ($reference.length > 0) { const refWidth = $reference.outerWidth(); const refOffset = $reference.offset(); if (refWidth && refOffset) { // Tính vị trí right từ edge màn hình const windowWidth = $(window).width(); const rightPosition = windowWidth - (refOffset.left + refWidth); $panel.css({ 'width': refWidth + 'px', 'right': rightPosition + 'px' }); console.log('✅ Panel dimensions updated:', { width: refWidth + 'px', right: rightPosition + 'px', reference: $reference.attr('id') }); } else { console.warn('⚠️ Could not get dimensions from reference element'); } // Restore trạng thái hidden nếu cần if (wasHidden) { $reference.hide().css('visibility', ''); } } else { console.warn('⚠️ No reference element found for panel dimensions'); } } function openPhanTichPanel(address, vbID) { console.log('openPhanTichPanel called with address:', address); console.log('Current state - isAnalyzing:', isAnalyzing, 'currentAnalyzingAddress:', currentAnalyzingAddress); // Kiểm tra nếu đang phân tích element khác if (isAnalyzing && currentAnalyzingAddress && currentAnalyzingAddress !== address) { const currentName = getElementDisplayName(currentAnalyzingAddress); console.warn('Already analyzing:', currentAnalyzingAddress, 'Cannot analyze:', address); showWarningModal('Vui lòng chờ phân tích ' + currentName + ' hoàn tất...'); return; } // Nếu đang phân tích cùng element → không làm gì if (isAnalyzing && currentAnalyzingAddress === address) { console.log('Already analyzing this element, ignoring duplicate request'); return; } // Panel sẽ fixed position append vào body const $rightdocinfo = $('#rightdocinfo'); // KHÔNG ẨN CTTD pointer - cho phép CTTD và panel cùng tồn tại console.log('Panel opening, CTTD pointer can stay visible'); // ẨN rightdocinfo để tiết kiệm không gian if ($rightdocinfo.length > 0) { $rightdocinfo.hide(); console.log('Hidden rightdocinfo to save space'); } // XÓA highlight persistent của TẤT CẢ elements cũ trước $('#tab_noi_dung_vb .highlight-border-persistent').removeClass('highlight-border-persistent'); console.log('Removed all previous highlight-border-persistent'); // Tìm element đang được phân tích và badge của nó const $element = $('[address="' + address + '"]'); const $badge = $element.find('.badge-phan-tich-container[data-for="' + address + '"]').first(); // Set state isAnalyzing = true; currentAnalyzingAddress = address; currentAnalyzingElement = $element; currentAnalyzingBadge = $badge; console.log('State set:', { isAnalyzing: isAnalyzing, currentAnalyzingAddress: currentAnalyzingAddress, elementFound: $element.length > 0, badgeFound: $badge.length > 0 }); // Thêm highlight persistent cho element MỚI này $element.addClass('highlight-border-persistent'); // Thay đổi badge thành "Đang phân tích..." và giữ hiển thị if ($badge.length > 0) { $badge.text('Đang phân tích...').addClass('analyzing'); // Giữ badge hiển thị và ở đúng vị trí $badge.data('analyzing', true); $badge.data('hovering', true); // Prevent auto-hide console.log('Badge set to analyzing state'); // Đảm bảo badge hiển thị ở đúng vị trí (vì dùng position: fixed) showPhanTichBadgeForParent($element); } // Tạo panel nếu chưa có - fixed position append vào body if ($('#phanTichPanel').length === 0) { const panelHTML = `
    Phân tích điều luật
    Đang phân tích...

    Đang phân tích...

    `; // Append vào body (fixed position không cần container cụ thể) $('body').append(panelHTML); // Detect width từ rightdocinfo và áp dụng cho panel updatePanelDimensions(); // Trigger show và set flag setTimeout(() => { $('#phanTichPanel').addClass('show'); isPanelOpen = true; console.log('✅ Panel opened (fixed position), isPanelOpen = true'); }, 10); } else { $('#phanTichPanelBody').html(`
    Đang phân tích...

    Đang phân tích...

    `); // Update dimensions khi re-open updatePanelDimensions(); $('#phanTichPanel').addClass('show'); isPanelOpen = true; console.log('✅ Panel re-opened (fixed position), isPanelOpen = true'); } // Bind nút đóng và ESC $(document).off('click.closePhanTich').on('click.closePhanTich', '.close-phan-tich', function() { closePhanTichPanel(); }); $(document).off('keyup.closePhanTich').on('keyup.closePhanTich', function(e) { if (e.key === 'Escape') closePhanTichPanel(); }); // Bind nút refresh - phân tích lại $(document).off('click.refreshPhanTich').on('click.refreshPhanTich', '.btn-refresh-phan-tich', function(e) { e.preventDefault(); e.stopPropagation(); const $btn = $(this); const $icon = $btn.find('i'); // Disable button và thêm animation $btn.prop('disabled', true); $icon.addClass('fa-spin'); console.log('🔄 Refresh: Phân tích lại address:', address); // Show loading trong panel $('#phanTichPanelBody').html(`
    Đang phân tích lại...

    Đang xóa cache và phân tích lại...

    `); // Gọi API xóa cache trước deletePhanTichCache(address, vbID, function(deleteSuccess) { if (deleteSuccess) { console.log('✅ Cache deleted, now re-analyzing...'); // Sau khi xóa cache, gọi lại API phân tích callPhanTichAPI(address, vbID, function() { // Enable lại button $btn.prop('disabled', false); $icon.removeClass('fa-spin'); }); } else { console.error('❌ Failed to delete cache'); $('#phanTichPanelBody').html(` `); $btn.prop('disabled', false); $icon.removeClass('fa-spin'); } }); }); // Gọi API phân tích (dùng function helper) callPhanTichAPI(address, vbID); } // Helper: Gọi API phân tích (tách riêng để dùng lại) function callPhanTichAPI(address, vbID, callback) { const randomServer = Math.floor(Math.random() * 10) + 1; $.ajax({ url: '//tnpl' + randomServer + '.hethongphapluat.com/tien-ich/phan.tich.dieu.luat.php', type: 'POST', contentType: 'application/json', timeout: 300000, // 5 phút data: JSON.stringify({ address: address, vb_id: vbID }), success: function(response) { console.log('Analysis complete for:', address, response); // Reset badge về trạng thái bình thường (nhưng vẫn hiển thị) if (currentAnalyzingBadge) { currentAnalyzingBadge.text('Phân tích').removeClass('analyzing'); currentAnalyzingBadge.data('analyzing', false); console.log('Badge reset to normal state'); } // Reset state analyzing để có thể phân tích element khác isAnalyzing = false; console.log('State reset: isAnalyzing = false, can analyze other elements now'); if (response.ok) { // Render kết quả phân tích let html = ''; html += '
    '; html += '
    ' + escapeHtml(response.ten_van_ban) + '
    '; if (response.so_hieu) { html += 'Số hiệu: ' + escapeHtml(response.so_hieu) + '
    '; } html += 'Điều khoản: ' + escapeHtml(response.address) + ''; if (response.from_cache) { html += ' Cache'; } html += '
    '; html += '
    ' + markdownToHtml(response.phan_tich) + '
    '; if (response.usage) { html += '
    '; html += 'Thống kê: '; html += 'Input tokens: ' + (response.usage.promptTokenCount || 0) + ', '; html += 'Output tokens: ' + (response.usage.candidatesTokenCount || 0); html += '
    '; } $('#phanTichPanelBody').html(html); } else { $('#phanTichPanelBody').html(` `); } if (callback) callback(); }, error: function(xhr, status, error) { console.error('Analysis error:', error); // Reset badge về trạng thái bình thường if (currentAnalyzingBadge) { currentAnalyzingBadge.text('Phân tích').removeClass('analyzing'); currentAnalyzingBadge.data('analyzing', false); } // Reset state analyzing isAnalyzing = false; let errorMsg = error; if (xhr.responseJSON && xhr.responseJSON.error) { errorMsg = xhr.responseJSON.error; } $('#phanTichPanelBody').html(` `); if (callback) callback(); } }); } // Helper: Xóa cache phân tích function deletePhanTichCache(address, vbID, callback) { const randomServer = Math.floor(Math.random() * 10) + 1; $.ajax({ url: '//tnpl' + randomServer + '.hethongphapluat.com/tien-ich/delete.phan.tich.cache.php', type: 'POST', contentType: 'application/json', timeout: 10000, data: JSON.stringify({ address: address, vb_id: vbID }), success: function(response) { console.log('Delete cache response:', response); if (callback) callback(response.ok || false); }, error: function(xhr, status, error) { console.error('Delete cache error:', error); if (callback) callback(false); } }); } // Helper: Lấy tên hiển thị của element từ address function getElementDisplayName(address) { if (!address) return 'nội dung'; const $element = $('[address="' + address + '"]'); if ($element.length === 0) return address; // Parse address: vd "dieu_3_khoan_29" -> "Khoản 29 Điều 3" // Address format: lớn đến nhỏ (phan > chuong > muc > dieu > khoan > diem) const parts = address.split('_'); const displayParts = []; for (let i = 0; i < parts.length; i += 2) { if (i + 1 < parts.length) { const type = getParentTypeName(parts[i]); const num = parts[i + 1]; displayParts.push(type + ' ' + num); } } // Reverse để hiển thị từ nhỏ đến lớn: "Khoản 29 Điều 3" (thay vì "Điều 3 Khoản 29") return displayParts.reverse().join(' '); } function openPhanTichModal(address, vbID) { // Tạo modal nếu chưa có if ($('#modalPhanTich').length === 0) { const modalHTML = ` `; $('body').append(modalHTML); } // Reset và hiển thị modal với loading $('#modalPhanTichBody').html(`
    Đang phân tích...

    Đang phân tích...

    `); $('#modalPhanTich').modal('show'); // AJAX request const randomServer = Math.floor(Math.random() * 10) + 1; $.ajax({ url: '//tnpl' + randomServer + '.hethongphapluat.com/tien-ich/phan.tich.dieu.luat.php', type: 'POST', contentType: 'application/json', data: JSON.stringify({ address: address, vb_id: vbID }), success: function(response) { if (response.ok) { // Render kết quả phân tích let html = ''; // Header thông tin văn bản html += '
    '; html += '
    ' + escapeHtml(response.ten_van_ban) + '
    '; if (response.so_hieu) { html += 'Số hiệu: ' + escapeHtml(response.so_hieu) + '
    '; } html += 'Điều khoản: ' + escapeHtml(response.address) + ''; html += '
    '; // Nội dung phân tích (Markdown -> HTML) html += '
    '; html += markdownToHtml(response.phan_tich); html += '
    '; // Thông tin usage (nếu có) if (response.usage) { html += '
    '; html += 'Thống kê: '; html += 'Input tokens: ' + (response.usage.promptTokenCount || 0) + ', '; html += 'Output tokens: ' + (response.usage.candidatesTokenCount || 0); html += '
    '; } $('#modalPhanTichBody').html(html); } else { $('#modalPhanTichBody').html(` `); } }, error: function(xhr, status, error) { let errorMsg = error; if (xhr.responseJSON && xhr.responseJSON.error) { errorMsg = xhr.responseJSON.error; } $('#modalPhanTichBody').html(` `); } }); } // Helpers: show/hide badge cho parent element (dieu, khoan,...) với position: fixed function showPhanTichBadgeForParent($parent) { // Lấy badge CỦA CHÍNH parent này (match data-for với address của parent) const parentAddress = $parent.attr('address'); const $badge = $parent.find('.badge-phan-tich-container[data-for="' + parentAddress + '"]').first(); if ($badge.length === 0) { console.warn('No badge found for parent:', parentAddress); return; } // Ẩn TẤT CẢ các badge khác để tránh overlap $('.badge-phan-tich-container').not($badge).each(function() { const $otherBadge = $(this); // Chỉ ẩn badge KHÔNG đang analyzing if (!$otherBadge.data('analyzing')) { $otherBadge.css({display: 'none'}); } }); // Show badge tạm để tính width $badge.css({display: 'inline-block', opacity: 0, visibility: 'hidden'}); const badgeWidth = $badge.outerWidth(); // Tính toán vị trí fixed dựa trên offset của parent const offset = $parent.offset(); const scrollTop = $(window).scrollTop(); const scrollLeft = $(window).scrollLeft(); // Position badge top-right của parent và show $badge.css({ display: 'inline-block', visibility: 'visible', opacity: 1, top: (offset.top - scrollTop) + 'px', left: (offset.left + $parent.outerWidth() - badgeWidth - scrollLeft - 5) + 'px' // -5px padding }); console.log('Showing badge for:', parentAddress, 'at position:', $badge.css('top'), $badge.css('left')); $parent.addClass('highlight-border'); } function hidePhanTichBadgeForParent($parent) { const $badge = $parent.find('.badge-phan-tich-container').first(); if ($badge.length === 0) return; $badge.css({display: 'none', opacity: 0}); $parent.removeClass('highlight-border'); } // Biến lưu element đang hover let currentHoveredElement = null; let hoverDebounceTimer = null; // Dùng mousemove để track chính xác element nào đang được hover $(document).on('mousemove', '#tab_noi_dung_vb', function(e) { // Tìm element gần nhất (phan, chuong, muc, dieu, khoan, diem) tại vị trí chuột const $target = $(e.target).closest('phan, chuong, muc, tieumuc, dieu, khoan, diem'); if ($target.length === 0) { // Không hover vào element nào return; } const address = $target.attr('address'); // Nếu đang hover vào cùng element → skip if (currentHoveredElement && currentHoveredElement[0] === $target[0]) { return; } // Clear debounce timer cũ if (hoverDebounceTimer) { clearTimeout(hoverDebounceTimer); } // Debounce để tránh trigger quá nhiều hoverDebounceTimer = setTimeout(function() { // Element thay đổi console.log('Hover changed to:', address); // Set flag hovering cho element mới $target.data('hovering', true); // Cancel timeout nếu có const timeoutId = $target.data('hideTimeout'); if (timeoutId) { clearTimeout(timeoutId); } // Ẩn badge của TẤT CẢ elements khác $('#tab_noi_dung_vb phan, #tab_noi_dung_vb chuong, #tab_noi_dung_vb muc, #tab_noi_dung_vb tieumuc, #tab_noi_dung_vb dieu, #tab_noi_dung_vb khoan, #tab_noi_dung_vb diem') .not($target) .each(function() { const $el = $(this); // Chỉ xóa highlight-border, KHÔNG xóa highlight-border-persistent $el.removeClass('highlight-border'); // Ẩn badge nếu KHÔNG đang analyzing const $badge = $el.find('.badge-phan-tich-container'); if ($badge.length && !$badge.data('analyzing')) { $badge.css({display: 'none'}); } }); // Attach badge nếu chưa có if (address && $target.find('.badge-phan-tich-container[data-for="' + address + '"]').length === 0) { const parentType = getParentTypeName($target.prop('tagName').toLowerCase()); const $badge = $('Phân tích'); console.log('Creating badge for address:', address, 'parentType:', parentType); $target.append($badge); $target.addClass('has-phan-tich-badge'); } // Show badge cho element này if ($target.find('.badge-phan-tich-container').length > 0) { showPhanTichBadgeForParent($target); } // Update current hovered element currentHoveredElement = $target; }, 50); // Debounce 50ms }); // Event delegation cho hover ra khỏi #tab_noi_dung_vb $(document).on('mouseleave', '#tab_noi_dung_vb', function(e) { // Clear current hovered element currentHoveredElement = null; // Ẩn tất cả badge không đang analyzing sau một khoảng thời gian setTimeout(function() { if (currentHoveredElement === null) { // Chỉ ẩn nếu thực sự không hover vào element nào $('#tab_noi_dung_vb phan, #tab_noi_dung_vb chuong, #tab_noi_dung_vb muc, #tab_noi_dung_vb tieumuc, #tab_noi_dung_vb dieu, #tab_noi_dung_vb khoan, #tab_noi_dung_vb diem') .each(function() { const $el = $(this); const $badge = $el.find('.badge-phan-tich-container'); if ($badge.length && !$badge.data('analyzing')) { $badge.css({display: 'none'}); } }); console.log('Mouse left tab_noi_dung_vb, hiding all badges'); } }, 300); }); // Event delegation cho hover ra khỏi parent (giữ lại cho badge behavior) $(document).on('mouseleave', '#tab_noi_dung_vb phan, #tab_noi_dung_vb chuong, #tab_noi_dung_vb muc, #tab_noi_dung_vb tieumuc, #tab_noi_dung_vb dieu, #tab_noi_dung_vb khoan, #tab_noi_dung_vb diem', function(e) { const $parent = $(this); const parentAddress = $parent.attr('address'); const $badge = $parent.find('.badge-phan-tich-container[data-for="' + parentAddress + '"]').first(); // Set flag parent not hovering $parent.data('hovering', false); // Nếu badge đang analyzing thì KHÔNG ẩn, GIỮ hiển thị if ($badge.length > 0 && $badge.data('analyzing')) { console.log('Badge is analyzing, keep visible'); return; } // Delay để có thời gian di chuột vào badge const timeoutId = setTimeout(() => { // Chỉ ẩn nếu cả parent và badge đều không hover và không analyzing if ($badge.length > 0 && !$parent.data('hovering') && !$badge.data('hovering') && !$badge.data('analyzing')) { hidePhanTichBadgeForParent($parent); } }, 300); // Tăng lên 300ms $parent.data('hideTimeout', timeoutId); }); // Hover vào badge → giữ hiển thị $(document).on('mouseenter', '.badge-phan-tich-container', function(e) { e.stopPropagation(); const $badge = $(this); const $parent = $badge.parent(); $badge.data('hovering', true); // Cancel timeout của parent const timeoutId = $parent.data('hideTimeout'); if (timeoutId) { clearTimeout(timeoutId); } }); // Hover ra khỏi badge → ẩn nếu không hover parent $(document).on('mouseleave', '.badge-phan-tich-container', function(e) { const $badge = $(this); $badge.data('hovering', false); const $parent = $badge.parent(); // Nếu badge đang analyzing thì KHÔNG ẩn, GIỮ hiển thị if ($badge.data('analyzing') || $badge.hasClass('analyzing')) { console.log('Badge is analyzing on mouseleave, keep visible'); return; } setTimeout(() => { // Chỉ ẩn nếu cả parent và badge đều không hover và không analyzing if (!$parent.data('hovering') && !$badge.data('hovering') && !$badge.data('analyzing') && !$badge.hasClass('analyzing')) { hidePhanTichBadgeForParent($parent); } }, 300); }); // Event delegation cho hover vào badge → hiện tooltip $(document).on('mouseenter', '.badge-phan-tich, .badge-phan-tich-container, .badge-phan-tich-fixed', function() { const $badge = $(this); const parentType = $badge.attr('data-parent-type') || 'Nội dung'; if ($badge.find('.badge-tooltip').length === 0) { const $tooltip = $('Phân tích chi tiết nội dung ' + parentType + ' này'); $badge.append($tooltip); setTimeout(() => $tooltip.addClass('show'), 10); } }); // Event delegation cho hover ra khỏi badge → ẩn tooltip $(document).on('mouseleave', '.badge-phan-tich, .badge-phan-tich-container, .badge-phan-tich-fixed', function() { const $tooltip = $(this).find('.badge-tooltip'); if ($tooltip.length > 0) { $tooltip.removeClass('show'); setTimeout(() => $tooltip.remove(), 300); } }); // Event delegation cho click badge → mở panel $(document).on('click', '.badge-phan-tich, .badge-phan-tich-container, .badge-phan-tich-fixed', function(e) { e.preventDefault(); e.stopPropagation(); const $badge = $(this); console.log('Badge clicked! Element:', $badge[0]); console.log('Badge parent:', $badge.parent().prop('tagName'), $badge.parent().attr('address')); console.log('Badge data-for:', $badge.attr('data-for')); console.log('Badge data-parent-type:', $badge.attr('data-parent-type')); // Nếu badge đang analyzing thì không cho click if ($badge.hasClass('analyzing') || $badge.data('analyzing')) { console.log('Badge is analyzing, click ignored'); return; } // Lấy address từ data-for attribute const address = $badge.attr('data-for'); console.log('Will analyze address:', address, 'vbID:', vbID); if (address && vbID) { openPhanTichPanel(address, vbID); } else { console.error('Missing address or vbID', {address, vbID}); showWarningModal('Không tìm thấy địa chỉ điều luật hoặc ID văn bản!'); } }); // Ẩn badge khi click vào CTTD $(document).on('click', 'cttd.chuthichtudong span, dctk span, dctd span', function(e) { console.log('CTTD clicked'); // Ẩn TẤT CẢ badge KHÔNG đang analyzing $('.badge-phan-tich-container').each(function() { const $badge = $(this); if (!$badge.data('analyzing') && !$badge.hasClass('analyzing')) { $badge.css({display: 'none'}); console.log('Hiding badge:', $badge.attr('data-for')); } }); }); // Update badge position khi scroll hoặc resize (vì dùng position: fixed) function updateBadgePositions() { $('.badge-phan-tich-container:visible').each(function() { const $badge = $(this); const $parent = $badge.parent(); // Cập nhật position nếu parent đang hover HOẶC badge đang analyzing if ($parent.length && ($parent.is(':hover') || $badge.data('analyzing'))) { // Re-calculate position const offset = $parent.offset(); const scrollTop = $(window).scrollTop(); const scrollLeft = $(window).scrollLeft(); const badgeWidth = $badge.outerWidth(); $badge.css({ top: (offset.top - scrollTop) + 'px', left: (offset.left + $parent.outerWidth() - badgeWidth - scrollLeft - 5) + 'px' }); } }); } $(window).on('scroll', updateBadgePositions); $(window).on('resize', updateBadgePositions); } });