MediaWiki:Common.js: Difference between revisions

Jump to navigation Jump to search
no edit summary
No edit summary
No edit summary
 
(74 intermediate revisions by the same user not shown)
Line 568: Line 568:
     var filterType = $(this).closest(".filter").data("filter");
     var filterType = $(this).closest(".filter").data("filter");
     var cardSelector = $(".card").length > 0 ? ".card" : ".community-card"; // Determine which card type is present
     var cardSelector = $(".card").length > 0 ? ".card" : ".community-card"; // Determine which card type is present
    console.log("Filter type:", filterType, "Card Selector:", cardSelector);


     $("#values-" + filterType).toggle();
     $("#values-" + filterType).toggle();
Line 584: Line 582:
     processEventCards(cardSelector);
     processEventCards(cardSelector);
     updateViews(cardSelector);
     updateViews(cardSelector);
    console.log("Updated views and borders after filter toggle");
   });
   });


Line 630: Line 626:
     processEventCards(cardSelector);
     processEventCards(cardSelector);
     updateViews(cardSelector);
     updateViews(cardSelector);
    updatePrintSelectionUI();
    hidePrintSelectionOptions();


     console.log("Filtering process complete, updated views and borders");
     console.log("Filtering process complete, updated views and borders");
Line 699: Line 697:
   // Reset function to remove active filters
   // Reset function to remove active filters
   $(".reset-filter").click(function (event) {
   $(".reset-filter").click(function (event) {
     event.stopPropagation(); // Prevent event bubbling
     event.stopPropagation();


    // Remove 'active' class from all filter buttons
     $("#filters .values button").removeClass("active");
     $("#filters .values button").removeClass("active");
     $(".open-filter").removeClass("active-filter");
     $(".open-filter").removeClass("active-filter");
    // Reset and hide the filter counts
     $(".count-filtered-cards").text("").hide();
     $(".count-filtered-cards").text("").hide();


     filterCards(); // Reapply filtering based on the updated active buttons
     filterCards();
 
     hidePrintSelectionOptions();
    // Update other UI elements as needed, excluding the general toggle button
    updateLastVisibleCard();
    updateWidthBlockView();
    processEventCards();
     updateViews();
   });
   });


   $("#filters .values button").click(function () {
   $("#filters .values button").click(function () {
    console.log("Filter is clicked!!!");
     $(this).toggleClass("active");
     $(this).toggleClass("active");
     filterCards(); // Re-apply the filters based on the updated active buttons
     filterCards();
 
    updateLastVisibleCard();
    updateWidthBlockView();
    processEventCards();
    updateViews();
   });
   });


Line 878: Line 862:
   function openModal(cardElement, event) {
   function openModal(cardElement, event) {
     event.stopPropagation();
     event.stopPropagation();
    $("#print-chooser").hide();
    $("#show-article").removeClass("print-opts-open");
     var pageTitle = $(cardElement).data("page") || null; // e.g. "090"
     var pageTitle = $(cardElement).data("page") || null; // e.g. "090"
     window.currentEntryTitle = pageTitle;
     window.currentEntryTitle = pageTitle;
Line 1,219: Line 1,205:
   // closeModal function
   // closeModal function
   function closeModal() {
   function closeModal() {
    $("#print-chooser").hide();
    $("#show-article").removeClass("print-opts-open");
     if ($(".home-chronicle-list").is(":visible")) {
     if ($(".home-chronicle-list").is(":visible")) {
       $(".home-list-view").css("width", "100%");
       $(".home-list-view").css("width", "100%");
Line 1,256: Line 1,244:
   });
   });


   // --- PRINT HELPERS ---
   /* ---------- Softwear PRINT ---------- */
   // Warm the font cache in the *parent* document.
 
   function preloadFontForPrint() {
   /* helpers */
   function swPrintPreloadFont() {
     var link = document.createElement("link");
     var link = document.createElement("link");
     link.rel = "preload";
     link.rel = "preload";
Line 1,266: Line 1,255:
     link.crossOrigin = "anonymous";
     link.crossOrigin = "anonymous";
     document.head.appendChild(link);
     document.head.appendChild(link);
    // keep it; no need to remove
   }
   }


  // Simple cache buster
   function swPrintCacheBust(url) {
   function cacheBust(url) {
     return url + (url.indexOf("?") > -1 ? "&" : "?") + "_=" + Date.now();
     return url + (url.indexOf("?") > -1 ? "&" : "?") + "_=" + Date.now();
   }
   }


   // Kill any previous print handlers
   function swEnsurePrintChooser() {
  $(document).off("click.print", "#print-button");
    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">show border</a> ' +
        '<a href="#" id="print-no-border" class="print-choice">hide border</a>' +
        "</div>"
    );
    jQuery("#print-button").after($chooser);
 
    // Bind once on the chooser to catch nested elements
    if (!$chooser.data("swBound")) {
      function chooserFire(ev, where) {
        ev = ev || window.event;
        var t = ev && (ev.target || ev.srcElement);
        var a = t && t.closest ? t.closest("a[id]") : null;
        if (!a) return;
        var id = a.getAttribute("id");
        if (id !== "print-with-border" && id !== "print-no-border") return;
        if (ev.preventDefault) ev.preventDefault();
        if (ev.stopImmediatePropagation) ev.stopImmediatePropagation();
        if (ev.stopPropagation) ev.stopPropagation();
        swHandlePrintChoice(id, (window.jQuery && jQuery(a)) || null);
        return false;
      }
      $chooser.on("pointerdown", chooserFire);
      $chooser.on("touchstart", chooserFire);
      $chooser.on("mousedown", chooserFire);
      $chooser.on("click", chooserFire);
      $chooser.data("swBound", true);
    }
    return $chooser;
  }
 
  function swHidePrintUI() {
    jQuery("#print-chooser").hide();
    jQuery("#show-article").removeClass("print-opts-open");
  }
 
  function updatePrintSelectionUI() {
    requestAnimationFrame(function () {
        var activeCount = jQuery("#filters .values button.active").length;
 
        if (activeCount > 0) {
        jQuery("#print-selection-wrapper").show();
        } else {
        jQuery("#print-selection-wrapper").hide();
        jQuery("#print-selection-options").hide();
        }
    });
  }
 
  function hidePrintSelectionOptions() {
        jQuery("#print-selection-options").hide();
    }


   $(document).on("click.print", "#print-button", function () {
   function swHandleBatchPrint(borderPref) {
     // debounce to avoid double/racing prints
     swPrintPreloadFont();
    var $btn = $("#print-button");
    if ($btn.data("busy")) return;
    $btn.data("busy", true);


     preloadFontForPrint();
     var $cards = jQuery(".card:visible").not(".event");


    var title = window.currentEntryTitle; // e.g. "090"
     if (!$cards.length) {
     if (!title) {
        alert("No entries to print.");
      console.warn("[print] no currentEntryTitle");
        return;
      window.print();
      $btn.data("busy", false);
      return;
     }
     }


     var pageUrl = mw.util.getUrl(title);
     var requests = [];
    var pageUrlFresh = cacheBust(pageUrl);
    console.log("[print] fetching page HTML:", pageUrlFresh);


     $.get(pageUrlFresh)
     $cards.each(function () {
      .done(function (html) {
         var $card = jQuery(this);
         var $tmp = $("<div>").html(html);
         var title = $card.data("page");
         var $print = $tmp.find(".print-only").first();
        console.log("[print] .print-only found:", $print.length);


         if (!$print.length) {
         if (!title) return;
          console.warn("[print] no .print-only found; fallback print");
          window.print();
          $btn.data("busy", false);
          return;
        }


        // Build hidden iframe
         var url =
         var iframe = document.createElement("iframe");
         window.mw && mw.util && mw.util.getUrl
         iframe.style.position = "fixed";
            ? swPrintCacheBust(mw.util.getUrl(title))
        iframe.style.right = "0";
            : swPrintCacheBust("/wiki/" + String(title));
        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;
         requests.push(
         doc.open();
         jQuery.get(url).then(function (html) {
        doc.write(
            var $tmp = jQuery("<div>").html(html);
          '<!doctype html><html><head><meta charset="utf-8"><title>Print</title></head><body></body></html>'
            var $print = $tmp.find(".print-only").first();
            return $print.length ? $print.prop("outerHTML") : "";
        })
         );
         );
        doc.close();
    });


        // Ensure relative URLs (fonts/images) resolve inside iframe
    Promise.all(requests)
         var base = doc.createElement("base");
         .then(function (results) {
         base.href = location.origin + "/";
         var combinedHtml = results.join("");
        doc.head.appendChild(base);


         // Inject PRINT CSS (cache-busted) and await it
         if (!combinedHtml.trim()) {
        var printCssUrl =
            alert("Could not generate print content.");
          "/index.php?title=MediaWiki:Print.css&action=raw&ctype=text/css";
            return;
         var printCssFresh = cacheBust(printCssUrl);
         }


         var linkCss = doc.createElement("link");
         swBuildIframeAndPrint(
         linkCss.rel = "stylesheet";
            combinedHtml,
         linkCss.href = printCssFresh;
            borderPref,
            null,
            "filtered.softwear.directory"
        );
         })
        .catch(function () {
        alert("There was a problem preparing the print selection.");
         });
  }


        var cssLoaded = new Promise(function (resolve) {
  /* small boot probe */
          linkCss.onload = function () {
  (function () {
            resolve();
    try {
          };
      console.log("[swprint] probe on load", {
          linkCss.onerror = function () {
        printButton: !!document.getElementById("print-button"),
            console.warn("[print] CSS failed to load");
        chooserExists: !!document.getElementById("print-chooser"),
            resolve();
        localPrintOnlyCount: jQuery(".print-only").length,
          }; // don't block
        showArticleExists: !!document.getElementById("show-article"),
        });
      });
    } catch (e) {}
  })();


        // Preload the font *inside* iframe (Chrome becomes much happier)
  /* core: build iframe and print */
        var linkFont = doc.createElement("link");
  function swBuildIframeAndPrint(printHtml, borderPref, $btn, filenameOverride) {
        linkFont.rel = "preload";
    // iframe
        linkFont.as = "font";
    var iframe = document.createElement("iframe");
        linkFont.type = "font/woff2";
    iframe.style.position = "fixed";
        linkFont.href = "/fonts/HALColant-TextRegular.woff2?v=20250820";
    iframe.style.right = "0";
        linkFont.crossOrigin = "anonymous";
    iframe.style.bottom = "0";
    iframe.style.width = "0";
    iframe.style.height = "0";
    iframe.style.border = "0";
    document.body.appendChild(iframe);


        doc.head.appendChild(linkFont);
    var doc = iframe.contentDocument || iframe.contentWindow.document;
        doc.head.appendChild(linkCss);
    doc.open();
    doc.write(
      '<!doctype html><html><head><meta charset="utf-8"><title>Print</title></head><body></body></html>'
    );
    doc.close();


        // Inject the printable HTML
    // make relative URLs resolve
        doc.body.innerHTML = $print.prop("outerHTML");
    var base = doc.createElement("base");
    base.href = location.origin + "/";
    doc.head.appendChild(base);


        // Remove “empty” optional sections so they don’t leave gaps in print.
    // print.css
        // --- CLEAN: remove empty paragraphs/whitespace nodes that create phantom gaps
    var linkCss = doc.createElement("link");
        (function () {
    linkCss.rel = "stylesheet";
          // 1) Drop <p> that are visually empty or only &nbsp;/whitespace
    linkCss.href = swPrintCacheBust(
          var ps = doc.querySelectorAll("#article-content p");
      "/index.php?title=MediaWiki:Print.css&action=raw&ctype=text/css"
          Array.prototype.forEach.call(ps, function (p) {
    );
            var txt = (p.textContent || "").replace(/\u00a0/g, " ").trim();
            // also ignore spans that were left with only <br>
            var onlyBr =
              p.children.length === 1 && p.firstElementChild.tagName === "BR";
            if (
              (!txt &&
                !p.querySelector("img, a, strong, em, span:not(:empty)")) ||
              onlyBr
            ) {
              p.parentNode && p.parentNode.removeChild(p);
            }
          });


          // 2) Remove pure-whitespace text nodes directly under #article-content
    var cssLoaded = new Promise(function (resolve) {
          var root = doc.getElementById("article-content");
      linkCss.onload = resolve;
          if (root) {
      linkCss.onerror = resolve;
            Array.prototype.slice.call(root.childNodes).forEach(function (n) {
    });
              if (n.nodeType === 3 && !n.textContent.replace(/\s+/g, "")) {
                root.removeChild(n);
              }
            });
          }
        })();


        (function () {
    // font preload (inside iframe)
          var css =
    var linkFont = doc.createElement("link");
            "@media print{" +
    linkFont.rel = "preload";
            // Paragraphs inside rich text
    linkFont.as = "font";
            "  .article-description p,.article-reflection p,.article-external-reference p,.article-quote p{margin:0 0 1.2mm!important;}" +
    linkFont.type = "font/woff2";
            " .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;}" +
    linkFont.href = "/fonts/HALColant-TextRegular.woff2?v=20250820";
            // Ruled sections: one consistent bottom padding for the hairline
    linkFont.crossOrigin = "anonymous";
            "  .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;}" +
            // Labels: zero their default margin, then add ONE spacer when following any ruled block
            '  [class^="article-label-"]{margin-top:0!important;}' +
            // <<< NEW: spacer no matter which section precedes (handles skipped sections)
            '  .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;}' +
            // No gap between any label and its own body
            "  .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;}" +
            // Title/link row cleanup
            "  .article-title-link{margin:0!important;padding:0!important;}" +
            "  .article-title-link > *{margin:0!important;}" +
            "  .link-pdf{margin-top:0!important;}" +
            // Final block: no trailing hairline
            "  #article-content > :last-child{padding-bottom:0!important;}" +
            "  #article-content > :last-child::after{content:none!important;}" +
            "}";


          var style = doc.createElement("style");
    doc.head.appendChild(linkFont);
          style.type = "text/css";
    doc.head.appendChild(linkCss);
          style.appendChild(doc.createTextNode(css));
          doc.head.appendChild(style);
        })();


        // --- PDF-friendly links for Chrome on macOS ---
    // inject HTML
        // 1) Add a tiny print-only CSS override to keep anchors as one box.
    doc.body.innerHTML = printHtml;
        var linkCssFix = doc.createElement("style");
        linkCssFix.textContent =
          "@media print {\n" +
          "  /* Keep anchor boxes intact so Chrome preserves the PDF annotation */\n" +
          "  .article-external-reference a,\n" +
          "  .link-pdf a {\n" +
          "    white-space: nowrap !important;\n" +
          "    word-break: normal !important;\n" +
          "    overflow-wrap: normal !important;\n" +
          "    text-decoration: underline;\n" +
          "  }\n" +
          "  /* Allow wrapping outside the anchor instead */\n" +
          "  .article-external-reference {\n" +
          "    overflow-wrap: anywhere;\n" +
          "    word-break: break-word;\n" +
          "  }\n" +
          "  /* Defensive: make sure anchors have a box */\n" +
          "  a[href] { position: relative; }\n" +
          "}\n";
        doc.head.appendChild(linkCssFix);


        // 2) Normalize long link text so it doesn't force wrapping inside anchors.
    (function () {
        (function () {
        var pres = doc.querySelectorAll(".link-pdf pre");
          // Shorten long visible URLs in external references, keep href intact
          var refs = doc.querySelectorAll(
            ".article-external-reference a[href]"
          );
          refs.forEach(function (a) {
            var txt = (a.textContent || "").trim();
            var href = a.getAttribute("href") || "";
            var looksLongUrl = /^https?:\/\//i.test(txt) && txt.length > 60;


            if (looksLongUrl) {
        Array.prototype.forEach.call(pres, function (pre) {
              try {
            // move its children out
                var u = new URL(href, doc.baseURI);
            while (pre.firstChild) {
                var label =
            pre.parentNode.insertBefore(pre.firstChild, pre);
                  u.hostname +
                  (u.pathname.replace(/\/$/, "") ? u.pathname : "");
                if (label.length > 40) label = label.slice(0, 37) + "…";
                a.textContent = label;
              } catch (e) {
                a.textContent = "Link";
              }
             }
             }
            // remove the <pre>
            pre.parentNode.removeChild(pre);
        });
    })();
    // sanitize: remove inner .print-no-border if user chose WITH border
    (function () {
      var stray = doc.querySelectorAll(".print-no-border");
      if (borderPref === "with" && stray.length) {
        Array.prototype.forEach.call(stray, function (el) {
          el.className = (el.className || "")
            .replace(/\bprint-no-border\b/g, "")
            .trim();
        });
      }
    })();
    // apply border preference to <html>
    (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,
            ""
          );
      }
    })();


            // Ensure single-box anchors
    // Glue label + body together (extra safety vs. page breaks)
            a.style.whiteSpace = "nowrap";
    (function () {
            a.style.wordBreak = "normal";
      var style = doc.createElement("style");
            a.style.overflowWrap = "normal";
      style.textContent =
          });
        "@media print{.sw-keep{break-inside:avoid;page-break-inside:avoid;}}";
      doc.head.appendChild(style);


          // Icon links ([PDF⤴] [WEB⤴]) are short; still enforce single-box
      var pairs = [
          doc.querySelectorAll(".link-pdf a[href]").forEach(function (a) {
        [".article-label-description", ".article-description"],
            a.style.whiteSpace = "nowrap";
        [".article-label-reflection", ".article-reflection"],
            a.style.wordBreak = "normal";
        [".article-label-external-reference", ".article-external-reference"],
            a.style.overflowWrap = "normal";
        [".article-label-quote", ".article-quote"],
          });
        [".article-label-modification-date", ".article-modification-date"],
        })();
      ];


        // Wait helpers
      for (var i = 0; i < pairs.length; i++) {
        function waitImages() {
        var labelSel = pairs[i][0];
          var imgs = [].slice.call(doc.images || []);
        var bodySel = pairs[i][1];
          if (!imgs.length) return Promise.resolve();
        var labels = doc.querySelectorAll(labelSel);
          return Promise.all(
        for (var j = 0; j < labels.length; j++) {
            imgs.map(function (img) {
          var label = labels[j];
              if (img.decode) {
          var body = label.nextElementSibling;
                try {
          if (!body || !body.matches(bodySel)) continue;
                  return img.decode().catch(function () {});
          var wrap = doc.createElement("div");
                } catch (e) {}
          wrap.className = "sw-keep";
              }
          label.parentNode.insertBefore(wrap, label);
              return new Promise(function (res) {
          wrap.appendChild(label);
                if (img.complete) return res();
           wrap.appendChild(body);
                img.onload = img.onerror = function () {
                  res();
                };
              });
            })
           );
         }
         }
      }
    })();


        function waitFonts(timeoutMs) {
    // clean empty paragraphs
          if (!doc.fonts || !doc.fonts.ready) return Promise.resolve();
    (function () {
           var ready = doc.fonts.ready;
      var ps = doc.querySelectorAll("#article-content p");
           var t = new Promise(function (res) {
      Array.prototype.forEach.call(ps, function (p) {
            setTimeout(res, timeoutMs || 1200);
        var txt = (p.textContent || "").replace(/\u00a0/g, " ").trim();
           });
        var onlyBr =
           return Promise.race([ready, t]);
           p.children &&
          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) {
        var kids = Array.prototype.slice.call(root.childNodes);
        for (var k = 0; k < kids.length; k++) {
           var n = kids[k];
           if (n.nodeType === 3 && !n.textContent.replace(/\s+/g, "")) {
            root.removeChild(n);
          }
         }
         }
      }
    })();


        // **Load the specific face** so Chrome actually uses it
    // inline micro-tweaks for print spacing
        function waitSpecificFont(timeoutMs) {
    (function () {
          if (!doc.fonts || !doc.fonts.load) return Promise.resolve();
      var css =
          var p = Promise.all([
        "@media print{" +
            doc.fonts.load('400 16px "HALColant-TextRegular"'),
        "  .article-description p,.article-reflection p,.article-external-reference p,.article-quote p{margin:0 0 1.2mm!important;}" +
            doc.fonts.load('normal 16px "HALColant-TextRegular"'),
        "  .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;}" +
           var t = new Promise(function (res) {
        "  .article-label-description + .article-description," +
             setTimeout(res, timeoutMs || 1200);
        "  .article-label-reflection + .article-reflection," +
           });
        "  .article-label-external-reference + .article-external-reference," +
           return Promise.race([p, t]);
        "  .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 (wrapping / underline)
    (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";
      });
    })();


         function nextFrame() {
    // 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) {
           return new Promise(function (res) {
             (iframe.contentWindow.requestAnimationFrame || setTimeout)(res, 0);
             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 desiredTitle;
        if (filenameOverride) {
        desiredTitle = filenameOverride;
        } else {
        var entryNum = "";
        var numEl = doc.querySelector(".article-entry-number");
        if (numEl) {
            var m = (numEl.textContent || "").match(/\d+/);
            entryNum = m ? m[0] : "";
        }
        desiredTitle =
            (entryNum ? entryNum + "." : "") + "softwear.directory";
         }
         }


         Promise.all([
         var oldIframeTitle = doc.title;
           cssLoaded,
        var oldParentTitle = document.title;
           waitImages(),
 
           waitFonts(1200),
        iframe.contentWindow.onafterprint = function () {
           waitSpecificFont(1200),
           try {
          nextFrame(),
            doc.title = oldIframeTitle;
         ]).then(function () {
            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;
 
        //window._printDoc = doc;
        //window._printFrame = iframe;
        //console.log("PRINT DOC READY", doc);
        //console.log("PRINT HTML", doc.body.innerHTML);
 
         iframe.contentWindow.focus();
        iframe.contentWindow.print();
 
        // safety cleanup
        setTimeout(function () {
           try {
           try {
             // build the desired PDF filename via document.title
             doc.title = oldIframeTitle;
             var entryNum = "";
             document.title = oldParentTitle;
            var numEl = doc.querySelector(".article-entry-number");
          } catch (e) {}
            if (numEl) {
          if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
              var m = (numEl.textContent || "").match(/\d+/);
          if ($btn && $btn.length) $btn.data("busy", false);
              entryNum = m ? m[0] : "";
        }, 1000);
            }
      })
            var desiredTitle =
      .catch(function () {
              (entryNum ? "[" + entryNum + "] " : "") + "softwear.directory";
        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);


            // save originals (scoped here)
    var borderPref = id === "print-no-border" ? "without" : "with";
            var oldIframeTitle = doc.title;
    swPrintPreloadFont();
            var oldParentTitle = document.title;


            // define onafterprint AFTER we have originals so it can close over them
    // prefer local .print-only (Entry page)
            iframe.contentWindow.onafterprint = function () {
    var localPrintOnly = jQuery(".print-only").first();
              try {
    if (localPrintOnly.length) {
                doc.title = oldIframeTitle; // restore iframe doc title
      swHidePrintUI();
                document.title = oldParentTitle; // restore parent title
      swBuildIframeAndPrint(localPrintOnly.prop("outerHTML"), borderPref, $btn);
              } catch (e) {}
      return;
              setTimeout(function () {
    }
                if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
              }, 100);
              $btn.data("busy", false);
            };


            // set temporary titles used by Chrome for the default PDF name
    // otherwise fetch by title (modal/home)
            doc.title = desiredTitle; // iframe document
    var title =
            // (next line is optional/redundant; doc === iframe.contentWindow.document)
      window.currentEntryTitle ||
            // iframe.contentWindow.document.title = desiredTitle;
      (window.mw && mw.config && mw.config.get && mw.config.get("wgPageName"));
            document.title = desiredTitle; // parent (helps on some setups)
    if (!title) {
      window.print();
      if ($btn && $btn.length) $btn.data("busy", false);
      return;
    }


            // print
    var pageUrl =
            iframe.contentWindow.focus();
      window.mw && mw.util && mw.util.getUrl
            iframe.contentWindow.print();
        ? mw.util.getUrl(title)
        : "/wiki/" + String(title);


            // fallback cleanup in case onafterprint doesn’t fire
    jQuery
            setTimeout(function () {
      .get(swPrintCacheBust(pageUrl))
              try {
      .done(function (html) {
                doc.title = oldIframeTitle;
        var $tmp = jQuery("<div>").html(html);
                document.title = oldParentTitle;
        var $print = $tmp.find(".print-only").first();
              } catch (e) {}
        if (!$print.length) {
              if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
          window.print();
              $btn.data("busy", false);
          if ($btn && $btn.length) $btn.data("busy", false);
            }, 1000);
           return;
          } catch (err) {
        }
            console.warn("[print] failed before print:", err);
         swHidePrintUI();
            $btn.data("busy", false);
        swBuildIframeAndPrint($print.prop("outerHTML"), borderPref, $btn);
           }
         });
       })
       })
       .fail(function (xhr) {
       .fail(function () {
        console.warn(
          "[print] fetch failed:",
          xhr && xhr.status,
          xhr && xhr.statusText
        );
         window.print();
         window.print();
         $("#print-button").data("busy", false);
         jQuery("#print-button").data("busy", false);
       });
       });
  }
  /* bind current choice anchors (defensive, for Entry pages) */
  function swBindChoiceAnchors() {
    var sel = "#print-with-border, #print-no-border";
    var els = document.querySelectorAll(sel);
    for (var i = 0; i < els.length; i++) {
      (function (el) {
        if (el.__swChoiceBound) return;
        el.__swChoiceBound = true;
        // ensure clickable/accessible
        try {
          el.style.pointerEvents = el.style.pointerEvents || "auto";
          if (!el.getAttribute("role")) el.setAttribute("role", "button");
          if (!el.getAttribute("tabindex")) el.setAttribute("tabindex", "0");
        } catch (e) {}
        function fire(ev) {
          if (ev && ev.preventDefault) ev.preventDefault();
          if (ev && ev.stopImmediatePropagation) ev.stopImmediatePropagation();
          if (ev && ev.stopPropagation) ev.stopPropagation();
          var $a = (window.jQuery && jQuery(el)) || null;
          swHandlePrintChoice(el.id, $a);
          return false;
        }
        // early + normal phases
        el.addEventListener("pointerdown", fire, true);
        el.addEventListener("touchstart", fire, true);
        el.addEventListener("mousedown", fire, true);
        el.addEventListener("click", fire, true);
        el.addEventListener("click", fire, false);
        if (!el.onclick) el.onclick = fire;
        // keyboard
        el.addEventListener(
          "keydown",
          function (e) {
            var k = e.key || e.keyCode;
            if (k === "Enter" || k === 13 || k === " " || k === 32) fire(e);
          },
          true
        );
      })(els[i]);
    }
  }
  /* early global catcher (minimal) */
  (function () {
    if (window.__swprintEarlyCatcher) return;
    window.__swprintEarlyCatcher = true;
    function routeEarly(ev) {
      var t = ev.target;
      if (!t || !t.closest) return;
      var a = t.closest("a#print-with-border, a#print-no-border");
      if (!a) return;
      if (ev.preventDefault) ev.preventDefault();
      if (ev.stopImmediatePropagation) ev.stopImmediatePropagation();
      if (ev.stopPropagation) ev.stopPropagation();
      swHandlePrintChoice(a.id, (window.jQuery && jQuery(a)) || null);
      return false;
    }
    window.addEventListener("pointerdown", routeEarly, true);
    window.addEventListener("touchstart", routeEarly, true);
    window.addEventListener("mousedown", routeEarly, true);
  })();
  /* wiring (namespaced) */
  jQuery(document).off("click.swprint");
  jQuery(document).on(
    "click.swprint",
    "#print-button, #print-chooser, #print-options",
    function (e) {
      // main [print] toggler
      if (jQuery(e.target).closest("#print-button").length) {
        e.preventDefault();
        var $chooser = swEnsurePrintChooser();
        $chooser.css({ position: "absolute", zIndex: 99999 });
        $chooser.toggle();
        var visible = $chooser.is(":visible");
        jQuery("#show-article").toggleClass("print-opts-open", visible);
        // ensure anchors are bound (important on Entry pages)
        swBindChoiceAnchors();
        return;
      }
      // click directly on a choice link (fallback path)
      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);
    }
  );
  // map any <button> inside chooser to its host anchor
  jQuery(document).on(
    "click.swprintChoiceBtn2",
    "#print-chooser button",
    function (e) {
      var host = this.closest(
        '[id="print-with-border"], [id="print-no-border"]'
      );
      if (!host) return;
      e.preventDefault();
      swHandlePrintChoice(host.id, (window.jQuery && jQuery(host)) || null);
    }
  );
  // hide choices on ESC
  jQuery(document).on("keydown.swprint", function (e) {
    if (e && e.keyCode === 27) {
        swHidePrintUI();
        hidePrintSelectionOptions();
    }
   });
   });
  // toggle filtered print options
  jQuery(document).on("click", ".print-selection-toggle", function (e) {
        e.preventDefault();
        jQuery("#print-selection-options").toggle();
    });
  // run filtered batch print
  jQuery(document).on(
    "click",
    ".print-selection-border, .print-selection-no-border",
    function (e) {
        e.preventDefault();
        var $btn = jQuery(this);
        var borderPref = $btn.hasClass("print-selection-no-border")
        ? "without"
        : "with";
        // disable all related buttons
        jQuery(".print-selection-border, .print-selection-no-border, .print-selection-toggle")
        .prop("disabled", true)
        .css("opacity", "0.5");
        // change ONLY clicked button text (native feeling)
        var originalText = $btn.text();
        $btn.text("working…");
        swHandleBatchPrint(borderPref);
        // optional reset (if user comes back)
        setTimeout(function () {
        $btn.text(originalText);
        jQuery(".print-selection-border, .print-selection-no-border, .print-selection-toggle")
            .prop("disabled", false)
            .css("opacity", "");
        }, 2000);
    }
  );
  /* ---------- /Softwear PRINT ---------- */


   // Close modal with Close button
   // Close modal with Close button
   $("#close-button").on("click", function () {
   $("#close-button").on("click", function () {
    $("#print-chooser").hide();
    $("#show-article").removeClass("print-opts-open");
     $(".list-container").removeClass("fade-out");
     $(".list-container").removeClass("fade-out");
     closeModal();
     closeModal();
Line 1,677: Line 1,976:
   }
   }


   if ($("#show-article-wrapper-entry").length) {
  // paragraph-formatting block
    // Your existing formatParagraphs function
   if (jQuery("#show-article-wrapper-entry").length) {
     function formatParagraphs(text) {
     function formatParagraphs(text) {
       var paragraphs = text.split("\n").filter(function (p) {
      // split on newlines, drop empty lines, wrap each in <p>
         return p.trim() !== "";
       var parts = String(text || "").split("\n");
      });
      var out = [];
      return paragraphs
      for (var i = 0; i < parts.length; i++) {
         .map(function (p) {
         var p = parts[i].replace(/^\s+|\s+$/g, "");
          return "<p>" + p.trim() + "</p>";
         if (p) out.push("<p>" + p + "</p>");
        })
      }
        .join("");
      return out.join("");
     }
     }


     // Check if ".article-description" exists and format its text
     jQuery(
    if ($(".article-description").length) {
      "#show-article .article-description, #show-article .article-reflection"
       var descriptionText = $(".article-description").text();
    ).each(function () {
       var formattedDescription = formatParagraphs(descriptionText);
       var $el = jQuery(this);
      $(".article-description").html(formattedDescription); // Set the formatted text
       if ($el.children("p").length > 0) return; // already formatted by PageForms
    }
       var rawText = $el.text();
 
       $el.html(formatParagraphs(rawText));
    // Check if ".article-reflection" exists and format its text
     });
    if ($(".article-reflection").length) {
       var reflectionText = $(".article-reflection").text();
      var formattedReflection = formatParagraphs(reflectionText);
       $(".article-reflection").html(formattedReflection); // Set the formatted text
     }
   }
   }


Line 1,834: Line 2,128:
     }
     }
   });
   });
  updatePrintSelectionUI();
  hidePrintSelectionOptions();
});
});

Navigation menu