| |
| |
| |
| |
| (() => { |
| const ATTR = 'data-il-rid'; |
|
|
| |
| |
| |
| |
| |
| 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; |
| |
| 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); |
| } |
| } |
|
|
| |
| 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); |
| } |
|
|
| |
| |
| |
| |
| 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; |
| })(); |
|
|