4,554
edits
No edit summary Tag: Reverted  | 
				No edit summary Tags: Manual revert Reverted  | 
				||
| Line 1,263: | Line 1,263: | ||
   /* ---------- Softwear PRINT (scoped, ES5-safe) ---------- */  |    /* ---------- Softwear PRINT (scoped, ES5-safe) ---------- */  | ||
   /*   |    /* helpers */  | ||
   function swPrintPreloadFont() {  | |||
     var link = document.createElement("link");  | |||
    link.rel = "preload";  | |||
    link.as = "font";  | |||
    link.type = "font/woff2";  | |||
    link.href = "/fonts/HALColant-TextRegular.woff2?v=20250820";  | |||
    link.crossOrigin = "anonymous";  | |||
    document.head.appendChild(link);  | |||
  }  | |||
  function swPrintCacheBust(url) {  | |||
    return url + (url.indexOf("?") > -1 ? "&" : "?") + "_=" + Date.now();  | |||
  }  | |||
  function swEnsurePrintChooser() {  | |||
    var $chooser = jQuery("#print-chooser");  | |||
    if ($chooser.length) return $chooser;  | |||
    $chooser = jQuery(  | |||
      '<div id="print-chooser" class="print-chooser" style="display:none;">' +  | |||
        '<a href="#" id="print-with-border" class="print-choice">border</a> ' +  | |||
        '<a href="#" id="print-no-border" class="print-choice">no border</a>' +  | |||
        "</div>"  | |||
    );  | |||
    jQuery("#print-button").after($chooser);  | |||
    return $chooser;  | |||
  }  | |||
  function swHidePrintUI() {  | |||
    jQuery("#print-chooser").hide();  | |||
    jQuery("#show-article").removeClass("print-opts-open");  | |||
  }  | |||
  /* core: build iframe and print */  | |||
  function swBuildIframeAndPrint(printHtml, borderPref, $btn) {  | |||
    // iframe  | |||
    var iframe = document.createElement("iframe");  | |||
    iframe.style.position = "fixed";  | |||
    iframe.style.right = "0";  | |||
    iframe.style.bottom = "0";  | |||
    iframe.style.width = "0";  | |||
    iframe.style.height = "0";  | |||
    iframe.style.border = "0";  | |||
     document.body.appendChild(iframe);  | |||
     var doc = iframe.contentDocument || iframe.contentWindow.document;  | |||
     doc.open();  | |||
    doc.write(  | |||
      '<!doctype html><html><head><meta charset="utf-8"><title>Print</title></head><body></body></html>'  | |||
    );  | |||
    doc.close();  | |||
     //   |      // make relative URLs resolve  | ||
     var base = doc.createElement("base");  | |||
    base.href = location.origin + "/";  | |||
    doc.head.appendChild(base);  | |||
    // print.css  | |||
    var linkCss = doc.createElement("link");  | |||
    linkCss.rel = "stylesheet";  | |||
    linkCss.href = swPrintCacheBust(  | |||
      "/index.php?title=MediaWiki:Print.css&action=raw&ctype=text/css"  | |||
     );  |      );  | ||
     var cssLoaded = new Promise(function (resolve) {  | |||
      linkCss.onload = function () {  | |||
        resolve();  | |||
      };  | |||
      linkCss.onerror = function () {  | |||
        resolve();  | |||
      };  | |||
     });  |      });  | ||
     //   |      // font preload (inside iframe)  | ||
    var linkFont = doc.createElement("link");  | |||
    linkFont.rel = "preload";  | |||
    linkFont.as = "font";  | |||
    linkFont.type = "font/woff2";  | |||
    linkFont.href = "/fonts/HALColant-TextRegular.woff2?v=20250820";  | |||
    linkFont.crossOrigin = "anonymous";  | |||
    doc.head.appendChild(linkFont);  | |||
    doc.head.appendChild(linkCss);  | |||
    // inject HTML  | |||
    doc.body.innerHTML = printHtml;  | |||
    // border preference -> <html> class  | |||
       var   |     (function () {  | ||
       var htmlEl = doc.documentElement;  | |||
       if (borderPref === "without") {  |        if (borderPref === "without") {  | ||
         if (  |          if (htmlEl.classList) htmlEl.classList.add("print-no-border");  | ||
         else   |          else if (  | ||
          (" " + htmlEl.className + " ").indexOf(" print-no-border ") === -1  | |||
        ) {  | |||
          htmlEl.className += " print-no-border";  | |||
        }  | |||
       } else {  |        } else {  | ||
         if (  |          if (htmlEl.classList) htmlEl.classList.remove("print-no-border");  | ||
         else  |          else  | ||
           htmlEl.className = htmlEl.className.replace(  | |||
             /\bprint-no-border\b/g,  |              /\bprint-no-border\b/g,  | ||
             ""  |              ""  | ||
           );  |            );  | ||
       }  |        }  | ||
    })();  | |||
    // clean empty paragraphs  | |||
    (function () {  | |||
      var ps = doc.querySelectorAll("#article-content p");  | |||
      Array.prototype.forEach.call(ps, function (p) {  | |||
        var txt = (p.textContent || "").replace(/\u00a0/g, " ").trim();  | |||
        var onlyBr =  | |||
          p.children.length === 1 &&  | |||
          p.firstElementChild &&  | |||
          p.firstElementChild.tagName === "BR";  | |||
        if (  | |||
          (!txt && !p.querySelector("img, a, strong, em, span:not(:empty)")) ||  | |||
          onlyBr  | |||
        ) {  | |||
          if (p.parentNode) p.parentNode.removeChild(p);  | |||
        }  | |||
      });  | |||
      var root = doc.getElementById("article-content");  | |||
      if (root) {  | |||
        Array.prototype.slice.call(root.childNodes).forEach(function (n) {  | |||
          if (n.nodeType === 3 && !n.textContent.replace(/\s+/g, "")) {  | |||
            root.removeChild(n);  | |||
          }  | |||
        });  | |||
      }  | |||
    })();  | |||
    // small inline tweaks  | |||
    (function () {  | |||
       var css =  | |||
         "@media print{" +  |          "@media print{" +  | ||
         "  .article-description p,.article-reflection p,.article-external-reference p,.article-quote p{margin:0 0 1.2mm!important;}" +  |          "  .article-description p,.article-reflection p,.article-external-reference p,.article-quote p{margin:0 0 1.2mm!important;}" +  | ||
         "  .article-description p:last-child,.article-reflection p:last-child,.article-external-reference p:last-child,.article-quote p:last-child{margin-bottom:0!important;}" +  |          "  .article-description p:last-child,.article-reflection p:last-child,.article-external-reference p:last-child,.article-quote p:last-child{margin-bottom:0!important;}" +  | ||
         "  .article-entry-number,.link-pdf,.article-type,.article-metadata,.article-images,.article-description,.article-reflection,.article-external-reference,.article-quote,.article-mod-line{padding-bottom:1mm!important;}" +  |          "  .article-entry-number,.link-pdf,.article-type,.article-metadata,.article-images,.article-description,.article-reflection,.article-external-reference,.article-quote,.article-mod-line{padding-bottom:1mm!important;}" +  | ||
        '  [class^\\"article-label-\\"]{margin-top:0!important;}' +  | |||
        '  .article-entry-number + [class^\\"article-label-\\"],' +  | |||
        '  .link-pdf + [class^\\"article-label-\\"],' +  | |||
        '  .article-type + [class^\\"article-label-\\"],' +  | |||
        '  .article-metadata + [class^\\"article-label-\\"],' +  | |||
        '  .article-images + [class^\\"article-label-\\"],' +  | |||
        '  .article-description + [class^\\"article-label-\\"],' +  | |||
        '  .article-reflection + [class^\\"article-label-\\"],' +  | |||
        '  .article-external-reference + [class^\\"article-label-\\"],' +  | |||
        '  .article-quote + [class^\\"article-label-\\"],' +  | |||
        '  .article-mod-line + [class^\\"article-label-\\"]{margin-top:0.9mm!important;}' +  | |||
        "  .article-label-description + .article-description," +  | |||
        "  .article-label-reflection + .article-reflection," +  | |||
        "  .article-label-external-reference + .article-external-reference," +  | |||
        "  .article-label-quote + .article-quote," +  | |||
        "  .article-label-modification-date + .article-modification-date{margin-top:0!important;}" +  | |||
         "  .article-title-link{margin:0!important;padding:0!important;}" +  |          "  .article-title-link{margin:0!important;padding:0!important;}" +  | ||
         "  .article-title-link > *{margin:0!important;}" +  |          "  .article-title-link > *{margin:0!important;}" +  | ||
| Line 1,464: | Line 1,425: | ||
         "  #article-content > :last-child::after{content:none!important;}" +  |          "  #article-content > :last-child::after{content:none!important;}" +  | ||
         "}";  |          "}";  | ||
       doc.head.appendChild(tweaks);  |       var style = doc.createElement("style");  | ||
      style.type = "text/css";  | |||
      style.appendChild(doc.createTextNode(css));  | |||
       doc.head.appendChild(style);  | |||
    })();  | |||
    // link tweaks  | |||
    (function () {  | |||
      var styleFix = doc.createElement("style");  | |||
      styleFix.textContent =  | |||
        "@media print {.article-external-reference a,.link-pdf a{white-space:nowrap!important;word-break:normal!important;overflow-wrap:normal!important;text-decoration:underline}.article-external-reference{overflow-wrap:anywhere;word-break:break-word}a[href]{position:relative}}";  | |||
      doc.head.appendChild(styleFix);  | |||
      var refs = doc.querySelectorAll(".article-external-reference a[href]");  | |||
      Array.prototype.forEach.call(refs, function (a) {  | |||
        var txt = (a.textContent || "").trim();  | |||
        var href = a.getAttribute("href") || "";  | |||
        var looksLongUrl = /^https?:\/\//i.test(txt) && txt.length > 60;  | |||
        if (looksLongUrl) {  | |||
          try {  | |||
            var u = new URL(href, doc.baseURI);  | |||
            var label =  | |||
              u.hostname + (u.pathname.replace(/\/$/, "") ? u.pathname : "");  | |||
            if (label.length > 40) label = label.slice(0, 37) + "…";  | |||
            a.textContent = label;  | |||
          } catch (e) {  | |||
            a.textContent = "Link";  | |||
          }  | |||
        }  | |||
        a.style.whiteSpace = "nowrap";  | |||
        a.style.wordBreak = "normal";  | |||
        a.style.overflowWrap = "normal";  | |||
      });  | |||
    })();  | |||
    // waits  | |||
    function waitImages() {  | |||
      var imgs = [].slice.call(doc.images || []);  | |||
      if (!imgs.length) return Promise.resolve();  | |||
      return Promise.all(  | |||
        imgs.map(function (img) {  | |||
          if (img.decode) {  | |||
            try {  | |||
              return img.decode().catch(function () {});  | |||
            } catch (e) {}  | |||
          }  | |||
          return new Promise(function (res) {  | |||
            if (img.complete) return res();  | |||
            img.onload = img.onerror = function () {  | |||
              res();  | |||
            };  | |||
          });  | |||
        })  | |||
      );  | |||
    }  | |||
    function waitFonts(timeoutMs) {  | |||
      if (!doc.fonts || !doc.fonts.ready) return Promise.resolve();  | |||
      var ready = doc.fonts.ready;  | |||
      var t = new Promise(function (res) {  | |||
        setTimeout(res, timeoutMs || 1200);  | |||
      });  | |||
      return Promise.race([ready, t]);  | |||
    }  | |||
    function waitSpecificFont(timeoutMs) {  | |||
      if (!doc.fonts || !doc.fonts.load) return Promise.resolve();  | |||
      var p = Promise.all([  | |||
        doc.fonts.load('400 16px "HALColant-TextRegular"'),  | |||
        doc.fonts.load('normal 16px "HALColant-TextRegular"'),  | |||
      ]);  | |||
      var t = new Promise(function (res) {  | |||
        setTimeout(res, timeoutMs || 1200);  | |||
      });  | |||
      return Promise.race([p, t]);  | |||
    }  | |||
    function nextFrame() {  | |||
      return new Promise(function (res) {  | |||
        (iframe.contentWindow.requestAnimationFrame || setTimeout)(res, 0);  | |||
         });  |       });  | ||
    }  | |||
    Promise.all([  | |||
      cssLoaded,  | |||
      waitImages(),  | |||
      waitFonts(1200),  | |||
      waitSpecificFont(1200),  | |||
      nextFrame(),  | |||
    ])  | |||
      .then(function () {  | |||
        // filename via document.title  | |||
        var entryNum = "";  | |||
        var numEl = doc.querySelector(".article-entry-number");  | |||
        if (numEl) {  | |||
          var m = (numEl.textContent || "").match(/\d+/);  | |||
          entryNum = m ? m[0] : "";  | |||
         }  | |||
        var desiredTitle =  | |||
          (entryNum ? entryNum + "." : "") + "softwear.directory";  | |||
        var oldIframeTitle = doc.title;  | |||
        var oldParentTitle = document.title;  | |||
        iframe.contentWindow.onafterprint = function () {  | |||
          try {  | |||
            doc.title = oldIframeTitle;  | |||
            document.title = oldParentTitle;  | |||
          } catch (e) {}  | |||
          setTimeout(function () {  | |||
            if (iframe.parentNode) iframe.parentNode.removeChild(iframe);  | |||
          }, 100);  | |||
          if ($btn && $btn.length) $btn.data("busy", false);  | |||
        };  | |||
        doc.title = desiredTitle;  | |||
        document.title = desiredTitle;  | |||
        iframe.contentWindow.focus();  | |||
        iframe.contentWindow.print();  | |||
         // safety cleanup  | |||
         setTimeout(function () {  | |||
           try {  |            try {  | ||
             doc.title = oldIframeTitle;  | |||
             document.title = oldParentTitle;  | |||
          } catch (e) {}  | |||
          if (iframe.parentNode) iframe.parentNode.removeChild(iframe);  | |||
          if ($btn && $btn.length) $btn.data("busy", false);  | |||
        }, 1000);  | |||
      })  | |||
      .catch(function () {  | |||
        if ($btn && $btn.length) $btn.data("busy", false);  | |||
      });  | |||
  }  | |||
  /* decide source & kick print */  | |||
  function swHandlePrintChoice(id, $btn) {  | |||
    if ($btn && $btn.data("busy")) return;  | |||
    if ($btn && $btn.length) $btn.data("busy", true);  | |||
    var borderPref = id === "print-no-border" ? "without" : "with";  | |||
    swPrintPreloadFont();  | |||
    // prefer local .print-only (Entry page)  | |||
    var localPrintOnly = jQuery(".print-only").first();  | |||
    if (localPrintOnly.length) {  | |||
      swHidePrintUI();  | |||
      swBuildIframeAndPrint(localPrintOnly.prop("outerHTML"), borderPref, $btn);  | |||
      return;  | |||
    }  | |||
    // otherwise fetch by title (modal/home list flow)  | |||
    var title =  | |||
      window.currentEntryTitle ||  | |||
      (window.mw && mw.config && mw.config.get && mw.config.get("wgPageName"));  | |||
    if (!title) {  | |||
      window.print();  | |||
      if ($btn && $btn.length) $btn.data("busy", false);  | |||
      return;  | |||
    }  | |||
    var pageUrl =  | |||
      window.mw && mw.util && mw.util.getUrl  | |||
        ? mw.util.getUrl(title)  | |||
        : "/wiki/" + String(title);  | |||
    jQuery  | |||
      .get(swPrintCacheBust(pageUrl))  | |||
      .done(function (html) {  | |||
        var $tmp = jQuery("<div>").html(html);  | |||
        var $print = $tmp.find(".print-only").first();  | |||
        if (!$print.length) {  | |||
          window.print();  | |||
          if ($btn && $btn.length) $btn.data("busy", false);  | |||
          return;  | |||
        }  | |||
        swHidePrintUI();  | |||
        swBuildIframeAndPrint($print.prop("outerHTML"), borderPref, $btn);  | |||
      })  | |||
      .fail(function () {  | |||
        window.print();  | |||
        jQuery("#print-button").data("busy", false);  | |||
      });  | |||
  }  | |||
  /* wiring (namespaced) */  | |||
  jQuery(document).off("click.swprint");  | |||
  jQuery(document).on(  | |||
    "click.swprint",  | |||
    "#print-button, #print-chooser, #print-options",  | |||
    function (e) {  | |||
      // click on the main [print]  | |||
      if (jQuery(e.target).closest("#print-button").length) {  | |||
        e.preventDefault();  | |||
        var $chooser = swEnsurePrintChooser();  | |||
        $chooser.toggle();  | |||
        jQuery("#show-article").toggleClass(  | |||
          "print-opts-open",  | |||
          $chooser.is(":visible")  | |||
        );  | |||
        return;  | |||
      }  | |||
      // click on a choice link  | |||
      var $choice = jQuery(e.target).closest(  | |||
        "a#print-with-border, a#print-no-border"  | |||
      );  | |||
      if (!$choice.length) return;  | |||
      e.preventDefault();  | |||
      swHandlePrintChoice($choice.attr("id"), $choice);  | |||
    }  | |||
  );  | |||
  // --- extra safety bindings for Entry page / widgets ---  | |||
  // 1) Directly bind the anchors (works even if container delegation misses)  | |||
  jQuery(document).on(  | |||
    "click.swprintChoice",  | |||
    "a#print-with-border, a#print-no-border, #print-with-border, #print-no-border",  | |||
    function (e) {  | |||
      console.log("[print] direct anchor handler fired:", this.id);  | |||
      e.preventDefault();  | |||
      swHandlePrintChoice(this.id, jQuery(this));  | |||
    }  | |||
  );  | |||
  // 2) If using the Button widget (<a id=...><button>...</button></a>),  | |||
  //    capture clicks on the <button> and route via its parent <a>.  | |||
  jQuery(document).on(  | |||
    "click.swprintChoiceBtn",  | |||
    "#print-options button",  | |||
    function (e) {  | |||
      var $a = jQuery(this).closest("a[id]");  | |||
      if (!$a.length) return;  | |||
      console.log(  | |||
        "[print] widget button handler fired; parent anchor id:",  | |||
        $a.attr("id")  | |||
      );  | |||
      e.preventDefault();  | |||
      swHandlePrintChoice($a.attr("id"), $a);  | |||
     }  |      }  | ||
   );  | |||
  // also hide choices on ESC; your close-button handler already hides them  | |||
  jQuery(document).on("keydown.swprint", function (e) {  | |||
    if (e && e.keyCode === 27) swHidePrintUI();  | |||
  });  | |||
   /* ---------- /Softwear PRINT ---------- */  |    /* ---------- /Softwear PRINT ---------- */  | ||