PageSourceSearch

https://hanoi.edu.vn/js/qi-pdf-duphong.js?v=2

js hanoi.edu.vn collected 2026-10-02 11:30:16 UTC 3,880 bytes, 66 lines download raw bytes

1/* =============================================================================
2   DỰ PHÒNG HIỂN THỊ PDF — KIỂM TỪ PHÍA TRÌNH DUYỆT (2026-08-12)
3
4   Vì sao phải kiểm ở trình duyệt chứ không ở máy chủ:
5   Khối xem PDF trỏ tới BẢN ĐỆM mà máy chủ tải về (`/upload/pdf/<ngày>/…pdf`). Trên máy chủ thật của
6   hcm.edu.vn và thptchuvanan.haiphong.edu.vn, bản đệm đó trả **404** trong khi mã phía máy chủ lại
7   thấy tệp tồn tại (đã kiểm: `File.Exists` + kích thước > 1KB đều đạt nên nhánh dự phòng phía máy chủ
8   không kích hoạt). Nghĩa là "máy chủ thấy có" ≠ "trình duyệt tải được" — nên chỉ có phép thử TỪ TRÌNH
9   DUYỆT mới đáng tin.
10
11   Cách làm: mỗi khối xem PDF được gắn `data-qi-pdf-dem` (đường dẫn bản đệm) và `data-qi-pdf-goc`
12   (địa chỉ tệp gốc). Tệp này gọi HEAD tới bản đệm; không tải được thì thay cả khối bằng
13   `<iframe src="<tệp gốc>">` — trình duyệt mở bằng bộ đọc PDF sẵn có, KHÔNG cần CORS (k
13hác pdf.js và
14   flipbook dflip, hai bộ đó phải đọc được nội dung nên vướng CORS với tên miền khác).
15
16   Đã kiểm tệp gốc của cả hai site: `200 · application/pdf · không có X-Frame-Options` ⇒ nhúng được.
17   ========================================================================== */
18(function () {
19    'use strict';
20
21    function nhungGoc(khoi, urlGoc) {
22        if (!urlGoc) return;
23        var khung = document.createElement('iframe');
24        khung.setAttribute('src', urlGoc);
25        khung.setAttribute('title', 'Xem tệp PDF');
26        khung.setAttribute('allowfullscreen', 'allowfullscreen');
27        khung.style.cssText = 'border:0;width:100%;height:920px;min-height:600px;display:block';
28        //  Khối flipbook hay được bọc trong .heightview/.maincontain có chiều cao riêng — thay CHÍNH nó
29        //  để không phải đoán cấu trúc thẻ bọc.
30        if (khoi.parentNode) khoi.parentNode.replaceChild(khung, khoi);
31    }
32
33    function kiem(khoi) {
34        var dem = khoi.getAttribute('data-qi-pdf-dem');
35        var goc = khoi.getAttribute('data-qi-pdf-goc');
36        if (!dem || !goc) return;
37
38        //  `cache: 'no-store'` để không ăn bản 404 đã nhớ đệm; HEAD cho nhẹ (tệp PDF có thể vài MB).
39        //  🔴 405 (máy chủ không cho HEAD) KHÔNG phải là "tệp hỏng" — phải thử lại bằng GET một byte,
40        //  nếu không sẽ đá nhầm cả những bản đệm đang phục vụ tốt (đã gặp đúng lỗi này khi kiểm thử).
41        function xet(kq, thuLai) {
42            if (kq.status === 405 || kq.status === 501) { if (thuLai) thuLai(); return; }
43            var kieu = (kq.headers.get('content-type') || '').toLowerCase();
44            //  Không ok, hoặc máy chủ trả trang lỗi HTML thay vì PDF ⇒ dùng bản gốc.
45            if (!kq.ok || (kieu && kieu.indexOf('pdf') < 0)) nhungGoc(khoi, goc);
46        }
47
48        fetch(dem, { method: 'HEAD', cache: 'no-store' })
49            .then(function (kq) {
50                xet(kq, function () {
51                    fetch(dem, { method: 'GET', cache: 'no-store', headers: { Range: 'bytes=0-0' } })
52                        .then(function (kq2) { xet(kq2, null); })
53                        .catch(function () { nhungGoc(khoi, goc); });
54                });
55            })
56            .catch(function () { nhungGoc(khoi, goc); });
57    }
58
59    function chay() {
60        var ds = document.querySelectorAll('[data-qi-pdf-dem][data-qi-pdf-goc]');
61        for (var i = 0; i < ds.length; i++) kiem(ds[i]);
62    }
63
64    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', chay);
65    else chay();
66})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.