File size: 2,503 Bytes
966d483
b25af45
 
966d483
 
 
 
 
613c7f2
966d483
 
 
 
613c7f2
 
966d483
 
 
 
 
 
7fd73e2
 
966d483
 
 
 
 
 
 
7fd73e2
966d483
 
 
 
 
b25af45
 
 
 
 
 
 
 
 
966d483
 
 
 
 
 
 
 
 
 
b25af45
966d483
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b25af45
966d483
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/**
 * 定根:text/plain 整页即正文;其余走 Readability(预打标 → clone → data-il-rid 映回),
 * 再经 extractRootPatches 手动修补。Readability 失败抛错,不回退启发式。
 */
(() => {
  const ATTR = 'data-il-rid';

  /**
   * 浏览器对 text/plain(.txt / .py raw 等)的原生查看器:整页即正文。
   * @param {Document} doc
   * @returns {Element | null}
   */
  function findPlainTextRoot(doc) {
    if (!doc.body) return null;
    if (doc.contentType === 'text/plain') return doc.body;
    return null;
  }

  function mark(doc) {
    if (!doc.body) return;
    let seq = 0;
    // body 本身也要打标:Readability 退到合成根时映回 page/body
    doc.body.setAttribute(ATTR, String(++seq));
    for (const el of doc.body.querySelectorAll('*')) {
      el.setAttribute(ATTR, String(++seq));
    }
  }

  function unmark(doc) {
    if (!doc.body) return;
    doc.body.removeAttribute(ATTR);
    for (const el of doc.body.querySelectorAll(`[${ATTR}]`)) {
      el.removeAttribute(ATTR);
    }
  }

  /** @param {Element} root @param {Document} doc */
  function applyPatches(root, doc) {
    const apply = globalThis.IL_applyExtractRootPatches;
    if (typeof apply !== 'function') {
      throw new Error('IL_applyExtractRootPatches missing — inject extractRootPatches.js first');
    }
    return apply(root, doc);
  }

  /**
   * @param {Document} doc
   * @returns {Element}
   */
  function findArticleRoot(doc) {
    if (!doc?.body) {
      throw new Error('document.body missing');
    }

    const plain = findPlainTextRoot(doc);
    if (plain) return applyPatches(plain, doc);

    if (typeof Readability !== 'function') {
      throw new Error('Readability missing — inject vendor/Readability.js first');
    }

    mark(doc);
    try {
      const clone = doc.cloneNode(true);
      const reader = new Readability(clone);
      const parsed = reader.parse();
      if (!parsed) {
        throw new Error('Readability: parse failed');
      }
      const rid = reader._ilArticleRootRid;
      if (rid == null || rid === '') {
        throw new Error('Readability: no mappable article root');
      }
      const root = doc.querySelector(`[${ATTR}="${CSS.escape(String(rid))}"]`);
      if (!root) {
        throw new Error(`Readability: live root not found (rid=${rid})`);
      }
      return applyPatches(root, doc);
    } finally {
      unmark(doc);
    }
  }

  globalThis.IL_findArticleRoot = findArticleRoot;
})();