4,456
edits
No edit summary |
No edit summary |
||
Line 1,370: | Line 1,370: | ||
// Remove “empty” optional sections so they don’t leave gaps in print. | // Remove “empty” optional sections so they don’t leave gaps in print. | ||
// --- | // --- DEBUG + robust removal of empty sections in the print iframe --- | ||
(function | (function debugAndTidyPrint(doc) { | ||
// 1) | // Toggle verbose output / colored outlines | ||
function | var DEBUG_PRINT = true; | ||
// 0) Add a tag so we can scope debug styles safely | |||
doc.documentElement.setAttribute("data-print-ifr", "1"); | |||
// 1) Debug CSS (visualize what the iframe thinks exists) | |||
if (DEBUG_PRINT) { | |||
var dbg = doc.createElement("style"); | |||
dbg.textContent = | |||
"[data-print-ifr] .article-label-description{outline:1px solid #1e90ff!important}" + | |||
"[data-print-ifr] .article-description{outline:1px dashed #1e90ff!important}" + | |||
"[data-print-ifr] .article-label-reflection{outline:1px solid #aa00ff!important}" + | |||
"[data-print-ifr] .article-reflection{outline:1px dashed #aa00ff!important}" + | |||
"[data-print-ifr] .article-label-external-reference{outline:1px solid #00aa88!important}" + | |||
"[data-print-ifr] .article-external-reference{outline:1px dashed #00aa88!important}" + | |||
"[data-print-ifr] .article-label-quote{outline:1px solid #ff8800!important}" + | |||
"[data-print-ifr] .article-quote{outline:1px dashed #ff8800!important}" + | |||
"[data-print-ifr] .article-images{outline:1px dashed #e91e63!important}" + | |||
// show the last-child hairline situation | |||
"@media print{" + | |||
"[data-print-ifr] #article-content>:last-child{outline:2px solid red!important}" + | |||
"}"; | |||
doc.head.appendChild(dbg); | |||
} | |||
// 2) Helpers | |||
function cleanText(node) { | |||
var txt = (node.textContent || "") | |||
.replace(/\u00A0/g, " ") // nbsp | |||
.replace(/\u200B/g, "") // zero-width | |||
.replace(/\s+/g, " ") // collapse | |||
.trim(); | |||
return txt; | |||
} | |||
function looksEmptyBlock(el) { | |||
if (!el) return true; | if (!el) return true; | ||
// If it | // If it has meaningful children, it's not empty | ||
if ( | if ( | ||
el.querySelector("img, video, audio, iframe, svg, object, embed") | el.querySelector("img,video,audio,iframe,svg,object,embed,canvas") | ||
) | ) | ||
return false; | return false; | ||
if (el.querySelector("a[href]")) return false; | if (el.querySelector("a[href]")) return false; | ||
// | // Clone to strip <br> and empty <p> safely | ||
var c = el.cloneNode(true), | |||
var | i; | ||
var | |||
// remove <br> | |||
var brs = c.getElementsByTagName("br"); | |||
for (i = brs.length - 1; i >= 0; i--) | for (i = brs.length - 1; i >= 0; i--) | ||
brs[i].parentNode.removeChild(brs[i]); | brs[i].parentNode.removeChild(brs[i]); | ||
// | // remove empty/whitespace-only <p> | ||
var ps = | var ps = c.getElementsByTagName("p"); | ||
for (i = ps.length - 1; i >= 0; i--) { | for (i = ps.length - 1; i >= 0; i--) { | ||
var t = (ps[i] | var t = cleanText(ps[i]); | ||
if (!t) ps[i].parentNode.removeChild(ps[i]); | if (!t) ps[i].parentNode.removeChild(ps[i]); | ||
} | } | ||
return cleanText(c) === ""; | |||
} | } | ||
function collapsePair(labelSel, blockSel) { | |||
function | |||
var block = doc.querySelector(blockSel); | var block = doc.querySelector(blockSel); | ||
if (block && | var label = doc.querySelector(labelSel); | ||
if (!block && !label) { | |||
if (DEBUG_PRINT) | |||
console.log("[print] pair missing:", labelSel, blockSel); | |||
return; | |||
} | |||
var empty = block ? looksEmptyBlock(block) : true; | |||
if (DEBUG_PRINT) { | |||
console.log("[print] check", blockSel, { | |||
exists: !!block, | |||
empty: empty, | |||
if (block.parentNode) block.parentNode.removeChild(block); | textLen: block ? cleanText(block).length : 0, | ||
}); | |||
} | |||
if (empty) { | |||
if (label && label.parentNode) | |||
label.parentNode.removeChild(label); | |||
if (block && block.parentNode) | |||
block.parentNode.removeChild(block); | |||
} | } | ||
} | } | ||
// 3) Images wrapper | // 3) Images: drop wrapper if no usable <img src> | ||
(function () { | (function () { | ||
var wrap = doc.querySelector(".article-images"); | var wrap = doc.querySelector(".article-images"); | ||
Line 1,446: | Line 1,478: | ||
} | } | ||
} | } | ||
if (DEBUG_PRINT) | |||
console.log( | |||
"[print] images wrapper, hasImg:", | |||
hasSrc, | |||
"count:", | |||
imgs.length | |||
); | |||
if (!hasSrc && wrap.parentNode) wrap.parentNode.removeChild(wrap); | if (!hasSrc && wrap.parentNode) wrap.parentNode.removeChild(wrap); | ||
})(); | })(); | ||
// 4) | // 4) Collapse optional sections | ||
collapsePair(".article-label-description", ".article-description"); | |||
collapsePair(".article-label-reflection", ".article-reflection"); | |||
collapsePair( | |||
".article-external-reference", | ".article-label-external-reference", | ||
".article | ".article-external-reference" | ||
); | ); | ||
collapsePair(".article-label-quote", ".article-quote"); | |||
// 5) Metadata grid: | // 5) Metadata grid: remove if all values empty | ||
(function () { | (function () { | ||
var meta = doc.querySelector(".article-metadata"); | var meta = doc.querySelector(".article-metadata"); | ||
Line 1,466: | Line 1,505: | ||
i; | i; | ||
for (i = 0; i < vals.length; i++) { | for (i = 0; i < vals.length; i++) { | ||
if (! | if (!looksEmptyBlock(vals[i])) { | ||
allEmpty = false; | allEmpty = false; | ||
break; | break; | ||
} | } | ||
} | } | ||
if (DEBUG_PRINT) | |||
console.log("[print] metadata allEmpty:", allEmpty); | |||
if (allEmpty && meta.parentNode) meta.parentNode.removeChild(meta); | if (allEmpty && meta.parentNode) meta.parentNode.removeChild(meta); | ||
})(); | })(); | ||
// 6) | // 6) Link bar: remove if it has no anchors | ||
(function () { | (function () { | ||
var bar = doc.querySelector(".link-pdf"); | var bar = doc.querySelector(".link-pdf"); | ||
var hasLink = | |||
if ( | bar && bar.querySelector && bar.querySelector("a[href]"); | ||
if (DEBUG_PRINT) | |||
console.log("[print] link-pdf hasLink:", !!hasLink); | |||
if (bar && !hasLink && bar.parentNode) | |||
bar.parentNode.removeChild(bar); | bar.parentNode.removeChild(bar); | ||
})(); | })(); | ||
// 7) | // 7) Kill truly empty <p> shells that still occupy height | ||
(function () { | |||
var ps = doc.querySelectorAll("#article-content p"); | |||
for (var i = 0; i < ps.length; i++) { | |||
var p = ps[i]; | |||
if (!p.children.length && cleanText(p) === "" && p.parentNode) { | |||
if (DEBUG_PRINT) | |||
console.log( | |||
"[print] remove empty <p>", | |||
p.className || "(no class)" | |||
); | |||
p.parentNode.removeChild(p); | |||
} | |||
} | } | ||
} | })(); | ||
// | // 8) Remove trailing hairline padding on last visible block | ||
var | (function () { | ||
var fix = doc.createElement("style"); | |||
fix.textContent = | |||
"@media print{" + | |||
"#article-content>:last-child{padding-bottom:0!important}" + | |||
"#article-content>:last-child::after{content:none!important}" + | |||
"}"; | |||
doc.head.appendChild(fix); | |||
})(); | |||
})(doc); | })(doc); | ||