MediaWiki:Common.js: Difference between revisions

Jump to navigation Jump to search
no edit summary
No edit summary
Tag: Reverted
No edit summary
Tag: Reverted
Line 1,263: Line 1,263:
   /* ---------- Softwear PRINT (scoped, ES5-safe) ---------- */
   /* ---------- Softwear PRINT (scoped, ES5-safe) ---------- */


   /* helpers */
   /* ========= Softwear Print — ES5, jQuery only ========= */
  function swPrintPreloadFont() {
  (function ($) {
    var link = document.createElement("link");
     // ---- helpers ----
    link.rel = "preload";
    function preloadFontForPrint() {
    link.as = "font";
      var link = document.createElement("link");
    link.type = "font/woff2";
       link.rel = "preload";
    link.href = "/fonts/HALColant-TextRegular.woff2?v=20250820";
       link.as = "font";
    link.crossOrigin = "anonymous";
      link.type = "font/woff2";
    document.head.appendChild(link);
      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
    (function () {
       var htmlEl = doc.documentElement;
      if (borderPref === "without") {
        if (htmlEl.classList) htmlEl.classList.add("print-no-border");
        else if (
          (" " + htmlEl.className + " ").indexOf(" print-no-border ") === -1
        ) {
          htmlEl.className += " print-no-border";
        }
      } else {
        if (htmlEl.classList) htmlEl.classList.remove("print-no-border");
        else
          htmlEl.className = htmlEl.className.replace(
            /\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{" +
        "  .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-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;}" +
        "  .link-pdf{margin-top:0!important;}" +
        "  #article-content > :last-child{padding-bottom:0!important;}" +
        "  #article-content > :last-child::after{content:none!important;}" +
        "}";
      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 {
            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)
     function cacheBust(url) {
    var title =
       return url + (url.indexOf("?") > -1 ? "&" : "?") + "_=" + Date.now();
      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 =
     function ensurePrintChooser() {
      window.mw && mw.util && mw.util.getUrl
      var $chooser = $("#print-chooser");
        ? mw.util.getUrl(title)
      if ($chooser.length) return $chooser;
        : "/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) */
      $chooser = $(
  jQuery(document).off("click.swprint");
        '<div id="print-chooser" class="print-chooser" style="display:none;margin-top:6px;">' +
  jQuery(document).on(
          '<a href="#" id="print-with-border" class="print-choice">[with border]</a> ' +
    "click.swprint",
          '<a href="#" id="print-no-border" class="print-choice">[no border]</a>' +
    "#print-button, #print-chooser, #print-options",
          "</div>"
    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;
       $("#print-button").after($chooser);
      e.preventDefault();
       return $chooser;
       swHandlePrintChoice($choice.attr("id"), $choice);
     }
     }
  );
  // --- extra safety bindings for Entry page / widgets ---


  // 1) Directly bind the anchors (works even if container delegation misses)
     function hideChooser() {
  jQuery(document).on(
       $("#print-chooser").hide();
    "click.swprintChoice",
       $("#show-article").removeClass("print-opts-open");
    "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>),
    // Remove any previous versions
  //    capture clicks on the <button> and route via its parent <a>.
    $(document).off("click.print");
  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
    // One handler for all the print UI
  jQuery(document).on("keydown.swprint", function (e) {
    $(document).on(
    if (e && e.keyCode === 27) swHidePrintUI();
      "click.print",
  });
      "#print-button, #print-chooser a.print-choice, #print-with-border, #print-no-border",
      function (e) {
        var $anchor = $(e.target).closest("a");
        if ($anchor.length) e.preventDefault();


  /* ---------- /Softwear PRINT ---------- */
        var rawId = this.id;
        var id = ($anchor.attr("id") || rawId || "").trim();


  /* === SW PRINT: native fallback for Entry pages (ES5-safe) === */
        // 1) Toggle the chooser when [print] is clicked
  (function () {
        if (id === "print-button") {
    // prevent double-binding
          var $chooser = ensurePrintChooser();
    if (window.__swPrintNativeBound) {
          var show = !$chooser.is(":visible");
      return;
          $chooser.css({ display: show ? "block" : "none" });
    }
           $("#show-article")[show ? "addClass" : "removeClass"](
    window.__swPrintNativeBound = true;
            "print-opts-open"
 
          );
    // ---- tiny helpers ----
           return;
    function ensurePrintChooserNative() {
      var chooser = document.getElementById("print-chooser");
      if (!chooser) {
        chooser = document.createElement("div");
        chooser.id = "print-chooser";
        chooser.className = "print-chooser";
        chooser.style.display = "none";
        chooser.innerHTML =
           '<a href="#" id="print-with-border" class="print-choice">border</a> ' +
          '<a href="#" id="print-no-border" class="print-choice">no border</a>';
        var printBtn = document.getElementById("print-button");
        if (printBtn && printBtn.parentNode) {
           printBtn.parentNode.insertBefore(chooser, printBtn.nextSibling);
         }
         }
      }
      return chooser;
    }


    function toggleChooser(show) {
         // 2) Must be one of the options
      var chooser = ensurePrintChooserNative();
         if (id !== "print-with-border" && id !== "print-no-border") {
      var want =
           return;
         typeof show === "boolean" ? show : chooser.style.display === "none";
      chooser.style.display = want ? "block" : "none";
      var article = document.getElementById("show-article");
      if (article) {
         if (want) {
          article.classList.add("print-opts-open");
        } else {
          article.classList.remove("print-opts-open");
        }
      }
      if (window.console && console.log) {
        console.log("[swprint-native] chooser", want ? "shown" : "hidden");
      }
    }
 
    function safeCacheBust(url) {
      try {
        if (typeof cacheBust === "function") {
          return cacheBust(url);
        }
      } catch (e) {
        /* ignore */
      }
      var sep = url.indexOf("?") > -1 ? "&" : "?";
      return url + sep + "_=" + new Date().getTime();
    }
 
    // ---- main flow: start printing with preference ----
    function swStartPrint(borderPref) {
      if (window.console && console.log) {
        console.log("[swprint-native] start with pref:", borderPref);
      }
 
      try {
        if (typeof preloadFontForPrint === "function") {
           preloadFontForPrint();
         }
         }
      } catch (e) {}


      // 1) Prefer local .print-only (Entry page content already on page)
        var $btn = $anchor.length ? $anchor : $(this);
      var local = document.querySelector(".print-only");
         if ($btn.data("busy")) return;
      if (local) {
         $btn.data("busy", true);
         if (window.console && console.log) {
          console.log("[swprint-native] using local .print-only");
        }
        toggleChooser(false);
         buildIframeAndPrint(local.outerHTML, borderPref);
        return;
      }


      // 2) Otherwise, fetch by title (modal / people page flow)
        var borderPref = id === "print-no-border" ? "without" : "with";
      var title = null;
         preloadFontForPrint();
      if (
        typeof window.currentEntryTitle === "string" &&
        window.currentEntryTitle
      ) {
        title = window.currentEntryTitle;
      } else if (
        window.mw &&
        mw.config &&
        typeof mw.config.get === "function"
      ) {
         title = mw.config.get("wgPageName");
      }


      if (
        // Prefer local .print-only (Entry pages)
         !title ||
         var $localPrintOnly = $(".print-only").first();
        !(window.mw && mw.util && typeof mw.util.getUrl === "function")
         if ($localPrintOnly.length) {
      ) {
           hideChooser();
         if (window.console && console.warn) {
          buildIframeAndPrint(
           console.warn(
             $localPrintOnly.prop("outerHTML"),
             "[swprint-native] no title/mw.util; fallback to window.print()"
            borderPref,
            $btn
           );
           );
          return;
         }
         }
        window.print();
        return;
      }


      var url = mw.util.getUrl(title);
        // Fallback: fetch by title (modal/home/person pages)
      url = safeCacheBust(url);
        var title =
      if (window.console && console.log) {
          window.currentEntryTitle ||
        console.log("[swprint-native] fetching:", url);
          (mw.config && mw.config.get && mw.config.get("wgPageName"));
      }
        if (!title) {
          window.print();
          $btn.data("busy", false);
          return;
        }


      // Use fetch if present; fall back to XHR
        var pageUrlFresh = cacheBust(mw.util.getUrl(title));
      if (window.fetch) {
         $.get(pageUrlFresh)
         fetch(url, { credentials: "same-origin" })
           .done(function (html) {
           .then(function (r) {
             var $tmp = $("<div>").html(html);
             return r.text();
            var $print = $tmp.find(".print-only").first();
          })
            if (!$print.length) {
          .then(function (html) {
              window.print();
             handleFetchedHtml(html, borderPref);
              $btn.data("busy", false);
              return;
            }
            hideChooser();
             buildIframeAndPrint($print.prop("outerHTML"), borderPref, $btn);
           })
           })
           .catch(function (err) {
           .fail(function () {
            if (window.console && console.warn) {
              console.warn(
                "[swprint-native] fetch failed; window.print()",
                err
              );
            }
             window.print();
             window.print();
            $("#print-button").data("busy", false);
           });
           });
      } else {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", url, true);
        xhr.withCredentials = true;
        xhr.onreadystatechange = function () {
          if (xhr.readyState === 4) {
            if (xhr.status >= 200 && xhr.status < 300) {
              handleFetchedHtml(xhr.responseText, borderPref);
            } else {
              if (window.console && console.warn) {
                console.warn(
                  "[swprint-native] XHR failed; window.print()",
                  xhr.status
                );
              }
              window.print();
            }
          }
        };
        xhr.send(null);
       }
       }
     }
     );


     function handleFetchedHtml(html, borderPref) {
     // Also hide the chooser when closing the card
      var tmp = document.createElement("div");
    $(document).on("click.print", "#close-button", function () {
      tmp.innerHTML = html;
       hideChooser();
      var printOnly = tmp.querySelector(".print-only");
     });
      if (!printOnly) {
        if (window.console && console.warn) {
          console.warn(
            "[swprint-native] no .print-only in fetched page; window.print()"
          );
        }
        window.print();
        return;
      }
       toggleChooser(false);
      buildIframeAndPrint(printOnly.outerHTML, borderPref);
     }
 
    // ---- iframe build + print (ES5) ----
    function buildIframeAndPrint(printHtml, borderPref) {
      if (window.console && console.log) {
        console.log("[swprint-native] buildIframeAndPrint()");
      }


    // ---- iframe + print ----
    function buildIframeAndPrint(printHtml, borderPref, $btn) {
      // Hidden iframe
       var iframe = document.createElement("iframe");
       var iframe = document.createElement("iframe");
       iframe.style.position = "fixed";
       iframe.style.position = "fixed";
Line 1,863: Line 1,402:
       doc.close();
       doc.close();


      // Base for relative URLs
       var base = doc.createElement("base");
       var base = doc.createElement("base");
       base.href = location.origin + "/";
       base.href = location.origin + "/";
       doc.head.appendChild(base);
       doc.head.appendChild(base);


       var cssUrl =
       // Print CSS
        "/index.php?title=MediaWiki:Print.css&action=raw&ctype=text/css";
      cssUrl = safeCacheBust(cssUrl);
 
       var linkCss = doc.createElement("link");
       var linkCss = doc.createElement("link");
       linkCss.rel = "stylesheet";
       linkCss.rel = "stylesheet";
       linkCss.href = cssUrl;
       linkCss.href = cacheBust(
        "/index.php?title=MediaWiki:Print.css&action=raw&ctype=text/css"
      );


       var cssLoaded = new Promise(function (resolve) {
       var cssLoaded = new Promise(function (resolve) {
         linkCss.onload = function () {
         linkCss.onload = function () {
          if (window.console && console.log) {
            console.log("[swprint-native] print CSS loaded");
          }
           resolve();
           resolve();
         };
         };
         linkCss.onerror = function () {
         linkCss.onerror = function () {
          if (window.console && console.warn) {
            console.warn("[swprint-native] print CSS failed");
          }
           resolve();
           resolve();
         };
         };
       });
       });


      // Preload font inside iframe
       var linkFont = doc.createElement("link");
       var linkFont = doc.createElement("link");
       linkFont.rel = "preload";
       linkFont.rel = "preload";
Line 1,900: Line 1,434:
       doc.head.appendChild(linkCss);
       doc.head.appendChild(linkCss);


       // content
       // Content
       doc.body.innerHTML = printHtml;
       doc.body.innerHTML = printHtml;


       // border pref
       // Apply border preference on <html>
       var root = doc.documentElement;
       var rootHtml = doc.documentElement;
       if (borderPref === "without") {
       if (borderPref === "without") {
         root.classList.add("print-no-border");
         if (rootHtml.classList) rootHtml.classList.add("print-no-border");
        else rootHtml.className += " print-no-border";
       } else {
       } else {
         root.classList.remove("print-no-border");
         if (rootHtml.classList) rootHtml.classList.remove("print-no-border");
      }
        else
      if (window.console && console.log) {
          rootHtml.className = rootHtml.className.replace(
        console.log("[swprint-native] borderPref applied:", borderPref);
            /\bprint-no-border\b/g,
            ""
          );
       }
       }


       // minimal inline tweaks
       // Minimal inline tweaks
       var style = doc.createElement("style");
       var tweaks = doc.createElement("style");
       style.type = "text/css";
       tweaks.textContent =
      style.appendChild(
         "@media print{" +
         doc.createTextNode(
        "  .article-description p,.article-reflection p,.article-external-reference p,.article-quote p{margin:0 0 1.2mm!important;}" +
          "@media print{.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-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-title-link{margin:0!important;padding:0!important}.article-title-link>*{margin:0!important}.link-pdf{margin-top:0!important}#article-content>:last-child{padding-bottom:0!important}#article-content>:last-child::after{content:none!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-title-link{margin:0!important;padding:0!important;}" +
       doc.head.appendChild(style);
        "  .article-title-link > *{margin:0!important;}" +
        "  .link-pdf{margin-top:0!important;}" +
        "  #article-content > :last-child{padding-bottom:0!important;}" +
        "  #article-content > :last-child::after{content:none!important;}" +
         "}";
       doc.head.appendChild(tweaks);


       var styleFix = doc.createElement("style");
       // Wait for resources, then print
      styleFix.type = "text/css";
      function waitImages() {
      styleFix.appendChild(
        var imgs = [].slice.call(doc.images || []);
        doc.createTextNode(
        if (!imgs.length) return Promise.resolve();
          "@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}}"
        return Promise.all(
         )
          imgs.map(function (img) {
      );
            if (img.decode) {
      doc.head.appendChild(styleFix);
              try {
 
                return img.decode().catch(function () {});
       // wait helpers
              } catch (e) {}
       function nextFrame(resolve) {
            }
         (iframe.contentWindow.requestAnimationFrame || setTimeout)(resolve, 0);
            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);
        });
       }
       }
      var waitNextFrame = new Promise(function (resolve) {
        nextFrame(resolve);
      });
      var waitFonts =
        doc.fonts && doc.fonts.ready
          ? Promise.race([
              doc.fonts.ready,
              new Promise(function (r) {
                setTimeout(r, 1200);
              }),
            ])
          : Promise.resolve();
      Promise.all([cssLoaded, waitFonts, waitNextFrame]).then(function () {
        if (window.console && console.log) {
          console.log("[swprint-native] resources ready; print()");
        }
        var numEl = doc.querySelector(".article-entry-number");
        var entryNum = "";
        if (numEl) {
          var m = (numEl.textContent || "").match(/\d+/);
          entryNum = m ? m[0] : "";
        }
        var desiredTitle =
          (entryNum ? entryNum + "." : "") + "softwear.directory";


         var oldIframeTitle = doc.title;
      Promise.all([
         var oldParentTitle = document.title;
        cssLoaded,
 
         waitImages(),
         iframe.contentWindow.onafterprint = function () {
        waitFonts(1200),
        waitSpecificFont(1200),
         nextFrame(),
      ])
         .then(function () {
           try {
           try {
             doc.title = oldIframeTitle;
             // Filename via entry number
             document.title = oldParentTitle;
            var entryNum = "";
          } catch (e) {}
             var numEl = doc.querySelector(".article-entry-number");
          setTimeout(function () {
             if (numEl) {
             if (iframe.parentNode) {
               var m = (numEl.textContent || "").match(/\d+/);
               iframe.parentNode.removeChild(iframe);
              entryNum = m ? m[0] : "";
             }
             }
          }, 100);
            var desiredTitle =
          if (window.console && console.log) {
              (entryNum ? entryNum + "." : "") + "softwear.directory";
             console.log("[swprint-native] afterprint cleanup");
 
          }
             var oldIframeTitle = doc.title;
        };
            var oldParentTitle = document.title;


        doc.title = desiredTitle;
            iframe.contentWindow.onafterprint = function () {
        document.title = desiredTitle;
              try {
                doc.title = oldIframeTitle;
                document.title = oldParentTitle;
              } catch (e) {}
              setTimeout(function () {
                if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
              }, 100);
              $btn.data("busy", false);
            };


        iframe.contentWindow.focus();
            doc.title = desiredTitle;
        iframe.contentWindow.print();
            document.title = desiredTitle;


        // safety cleanup if onafterprint never fires
             iframe.contentWindow.focus();
        setTimeout(function () {
             iframe.contentWindow.print();
          try {
             doc.title = oldIframeTitle;
            document.title = oldParentTitle;
          } catch (e) {}
          if (iframe.parentNode) {
             iframe.parentNode.removeChild(iframe);
          }
          if (window.console && console.log) {
            console.log("[swprint-native] timeout cleanup");
          }
        }, 1500);
      });
    }


    // ---- bind native clicks ----
            // Safety cleanup
    var printBtn = document.getElementById("print-button");
            setTimeout(function () {
    if (printBtn) {
              try {
      printBtn.addEventListener(
                doc.title = oldIframeTitle;
        "click",
                document.title = oldParentTitle;
        function (e) {
              } catch (e) {}
           if (e && e.preventDefault) {
              if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
             e.preventDefault();
              $btn.data("busy", false);
            }, 1000);
           } catch (err) {
             $btn.data("busy", false);
           }
           }
          if (window.console && console.log) {
        })
            console.log("[swprint-native] print-button clicked");
        .catch(function () {
          }
          $btn.data("busy", false);
          toggleChooser();
         });
         },
        false
      );
     }
     }
  })(jQuery);


    function onChoice(e) {
  /* ---------- /Softwear PRINT ---------- */
      var t = e.target || e.srcElement;
      var a = null;
      // emulate closest("a") in ES5
      while (t && t !== document) {
        if (t.tagName && t.tagName.toLowerCase() === "a") {
          a = t;
          break;
        }
        t = t.parentNode;
      }
      if (!a) {
        return;
      }
      var id = a.id || "";
      if (id !== "print-with-border" && id !== "print-no-border") {
        return;
      }
      if (e && e.preventDefault) {
        e.preventDefault();
      }
      var pref = id === "print-no-border" ? "without" : "with";
      if (window.console && console.log) {
        console.log("[swprint-native] choice clicked:", pref);
      }
      swStartPrint(pref);
    }
 
    // bind existing anchors (if already in DOM)
    var aWith = document.getElementById("print-with-border");
    if (aWith) {
      aWith.addEventListener("click", onChoice, false);
    }
    var aNo = document.getElementById("print-no-border");
    if (aNo) {
      aNo.addEventListener("click", onChoice, false);
    }
 
    // and delegate for dynamically created chooser
    document.addEventListener("click", onChoice, false);
 
    if (window.console && console.log) {
      console.log("[swprint-native] fallback bound");
    }
  })();
 
  //ENDEND


   // Close modal with Close button
   // Close modal with Close button

Navigation menu