4,554
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. | ||
|          (function  |         // --- Collapse empty sections & their labels, and tidy any leftover spacing --- | ||
|            function  |          (function tidyPrint(doc) { | ||
|              //  |           // 1) Utility: detect “visually empty” blocks | ||
|              var txt = ( |            function isVisuallyEmpty(el) { | ||
|             if (!el) return true; | |||
|              // If it contains any real content, bail | |||
|             if ( | |||
|               el.querySelector("img, video, audio, iframe, svg, object, embed") | |||
|             ) | |||
|               return false; | |||
|             if (el.querySelector("a[href]")) return false; | |||
|             // Remove <br> & empty <p> shells before checking text | |||
|             // (work on a shallow clone so we don't touch the live node yet) | |||
|             var clone = el.cloneNode(true); | |||
|             var i, | |||
|               brs = clone.getElementsByTagName("br"); | |||
|             for (i = brs.length - 1; i >= 0; i--) | |||
|               brs[i].parentNode.removeChild(brs[i]); | |||
|             // Remove <p> that are empty/whitespace only | |||
|             var ps = clone.getElementsByTagName("p"); | |||
|             for (i = ps.length - 1; i >= 0; i--) { | |||
|               var t = (ps[i].textContent || "") | |||
|                 .replace(/\u00A0/g, " ") | |||
|                 .replace(/\u200B/g, "") | |||
|                 .replace(/\s+/g, " ") | |||
|                 .trim(); | |||
|               if (!t) ps[i].parentNode.removeChild(ps[i]); | |||
|             } | |||
|             // Now inspect text | |||
|              var txt = (clone.textContent || "") | |||
|                .replace(/\u00A0/g, " ") // NBSP |                .replace(/\u00A0/g, " ") // NBSP | ||
|                .replace(/\u200B/g, "") // zero-width space |                .replace(/\u200B/g, "") // zero-width space | ||
|                .replace(/\s+/g, " ") //  |                .replace(/\s+/g, " ") // collapse spaces | ||
|                .trim(); |                .trim(); | ||
|              return !txt; | |||
|              return !txt  | |||
|            } |            } | ||
|            function  |           // 2) Remove block + its label if empty | ||
|              var  |            function removePair(blockSel, labelSel) { | ||
|              if ( |              var block = doc.querySelector(blockSel); | ||
|                //  |              if (block && isVisuallyEmpty(block)) { | ||
|                if ( |                // Remove preceding label if it matches | ||
|                  var prev =  |                if (labelSel) { | ||
|                  if (prev && prev.matches && prev.matches( |                  var prev = block.previousElementSibling; | ||
|                  if ( | |||
|                   prev && | |||
|                   prev.matches && | |||
|                   prev.matches(labelSel) && | |||
|                    prev.parentNode | |||
|                 ) { | |||
|                   prev.parentNode.removeChild(prev); | |||
|                  } |                  } | ||
|                } |                } | ||
|                if ( |                if (block.parentNode) block.parentNode.removeChild(block); | ||
|              } |              } | ||
|            } |            } | ||
|            //  |            // 3) Images wrapper: only keep if it actually has a usable <img src> | ||
|            (function () { |            (function () { | ||
|              var  |              var wrap = doc.querySelector(".article-images"); | ||
|              if (! |              if (!wrap) return; | ||
|              var imgs =  |              var imgs = wrap.getElementsByTagName("img"); | ||
|              var  |              var hasSrc = false, | ||
|              for ( |               i; | ||
|              for (i = 0; i < imgs.length; i++) { | |||
|                var s = imgs[i].getAttribute("src"); | |||
|               if (s && /\S/.test(s)) { | |||
|                  hasSrc = true; | |||
|                  break; |                  break; | ||
|                } |                } | ||
|              } |              } | ||
|              if (! |              if (!hasSrc && wrap.parentNode) wrap.parentNode.removeChild(wrap); | ||
|            })(); |            })(); | ||
|            //  |            // 4) Optional sections | ||
|            removePair(".article-description", ".article-label-description"); | |||
|            removePair(".article-reflection", ".article-label-reflection"); | |||
|            removePair( | |||
|              ".article-external-reference", |              ".article-external-reference", | ||
|              ".article-label-external-reference" |              ".article-label-external-reference" | ||
|            ); |            ); | ||
|            removePair(".article-quote", ".article-label-quote"); | |||
|            // Metadata: if all values are empty |            // 5) Metadata grid: drop if all values are empty | ||
|            (function () { |            (function () { | ||
|              var meta = doc.querySelector(".article-metadata"); |              var meta = doc.querySelector(".article-metadata"); | ||
|              if (!meta) return; |              if (!meta) return; | ||
|              var  |              var vals = meta.querySelectorAll(".article-metadata-value"); | ||
|              var allEmpty = true; |              var allEmpty = true, | ||
|              for ( |               i; | ||
|                if (! |              for (i = 0; i < vals.length; i++) { | ||
|                if (!isVisuallyEmpty(vals[i])) { | |||
|                  allEmpty = false; |                  allEmpty = false; | ||
|                  break; |                  break; | ||
|                } |                } | ||
|              } |              } | ||
|              if (allEmpty && meta.parentNode) { |              if (allEmpty && meta.parentNode) meta.parentNode.removeChild(meta); | ||
|           })(); | |||
|           // 6) Title link row: remove the link bar if it ended up empty (no anchors) | |||
|           (function () { | |||
|             var bar = doc.querySelector(".link-pdf"); | |||
|             if (!bar) return; | |||
|             if (!bar.querySelector("a[href]") && bar.parentNode) | |||
|               bar.parentNode.removeChild(bar); | |||
|           })(); | |||
|           // 7) Remove any now-empty label that wasn’t paired (defensive) | |||
|           var strayLabels = doc.querySelectorAll( | |||
|             ".article-label-description, .article-label-reflection, " + | |||
|               ".article-label-external-reference, .article-label-quote" | |||
|           ); | |||
|           for (var i = 0; i < strayLabels.length; i++) { | |||
|             if (strayLabels[i].parentNode) | |||
|                strayLabels[i].parentNode.removeChild(strayLabels[i]); | |||
|           } | |||
|           // 8) Kill truly empty paragraphs that may still add height | |||
|           var empties = doc.querySelectorAll("#article-content p"); | |||
|           for (var j = 0; j < empties.length; j++) { | |||
|             var text = (empties[j].textContent || "") | |||
|               .replace(/\u00A0/g, " ") | |||
|               .replace(/\u200B/g, "") | |||
|               .trim(); | |||
|             if ( | |||
|               !text && | |||
|               empties[j].children.length === 0 && | |||
|               empties[j].parentNode | |||
|             ) { | |||
|               empties[j].parentNode.removeChild(empties[j]); | |||
|              } |              } | ||
|            })(); |            } | ||
|           // 9) Hide the hairline on the very last block to avoid a “phantom gap” | |||
|           var css = doc.createElement("style"); | |||
|           css.textContent = | |||
|             "@media print{#article-content>:last-child{padding-bottom:0!important;}" + | |||
|             "#article-content>:last-child::after{content:none!important;}}"; | |||
|           doc.head.appendChild(css); | |||
|          })(doc); |          })(doc); | ||