MediaWiki:Common.js: Difference between revisions

Jump to navigation Jump to search
no edit summary
No edit summary
No edit summary
Line 1,260: Line 1,260:
   });
   });


   (function ($) {
   // HERE SHOULD BE THE NEW CODE!!!!
    var DEBUG = true; // set false to silence console logs
  /* ---------- Softwear PRINT (scoped, ES5-safe) ---------- */


    // 1) Warm the font cache in the *parent* document
  /* helpers */
    function preloadFontForPrint() {
  function swPrintPreloadFont() {
      var link = document.createElement("link");
    var link = document.createElement("link");
      link.rel = "preload";
    link.rel = "preload";
      link.as = "font";
    link.as = "font";
      link.type = "font/woff2";
    link.type = "font/woff2";
      link.href = "/fonts/HALColant-TextRegular.woff2?v=20250820";
    link.href = "/fonts/HALColant-TextRegular.woff2?v=20250820";
      link.crossOrigin = "anonymous";
    link.crossOrigin = "anonymous";
      document.head.appendChild(link);
    document.head.appendChild(link);
    }
  }


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


    // 3) Create the little chooser under [print] if needed
  function swEnsurePrintChooser() {
    function ensurePrintChooser() {
    var $chooser = jQuery("#print-chooser");
      var $chooser = $("#print-chooser");
    if ($chooser.length) return $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;
  }


      // MediaWiki-safe: <div> + <a>, no <button>
  function swHidePrintUI() {
      $chooser = $(
    jQuery("#print-chooser").hide();
        '<div id="print-chooser" class="print-chooser" style="display:none;">' +
    jQuery("#show-article").removeClass("print-opts-open");
          '<a href="#" id="print-with-border" class="print-choice">border</a> ' +
  }
          '<a href="#" id="print-no-border" class="print-choice">no border</a>' +
          "</div>"
      );
      $("#print-button").after($chooser);
      return $chooser;
    }


    // 4) Hide chooser utility (used by close handlers)
  /* core: build iframe and print */
    function hidePrintUI() {
  function swBuildIframeAndPrint(printHtml, borderPref, $btn) {
      $("#print-chooser").hide();
    // iframe
      $("#show-article").removeClass("print-opts-open");
    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);


     // 5) Build a hidden iframe, inject CSS+HTML, and print
     var doc = iframe.contentDocument || iframe.contentWindow.document;
     function buildIframeAndPrint(printHtml, borderPref, $btn) {
     doc.open();
       if (DEBUG) console.log("[print] buildIframeAndPrint: starting");
    doc.write(
       '<!doctype html><html><head><meta charset="utf-8"><title>Print</title></head><body></body></html>'
    );
    doc.close();


      // Hidden iframe
    // make relative URLs resolve
      var iframe = document.createElement("iframe");
    var base = doc.createElement("base");
      iframe.style.position = "fixed";
    base.href = location.origin + "/";
      iframe.style.right = "0";
    doc.head.appendChild(base);
      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;
    // print.css
      doc.open();
    var linkCss = doc.createElement("link");
      doc.write(
    linkCss.rel = "stylesheet";
        '<!doctype html><html><head><meta charset="utf-8"><title>Print</title></head><body></body></html>'
    linkCss.href = swPrintCacheBust(
      );
      "/index.php?title=MediaWiki:Print.css&action=raw&ctype=text/css"
      doc.close();
    );


      // Ensure relative URLs resolve
    var cssLoaded = new Promise(function (resolve) {
      var base = doc.createElement("base");
       linkCss.onload = function () {
       base.href = location.origin + "/";
        resolve();
      doc.head.appendChild(base);
       };
 
       linkCss.onerror = function () {
       // Print CSS
         resolve();
       var printCssUrl =
       };
         "/index.php?title=MediaWiki:Print.css&action=raw&ctype=text/css";
    });
      var linkCss = doc.createElement("link");
       linkCss.rel = "stylesheet";
      linkCss.href = cacheBust(printCssUrl);


      var cssLoaded = new Promise(function (resolve) {
    // font preload (inside iframe)
        linkCss.onload = function () {
    var linkFont = doc.createElement("link");
          if (DEBUG) console.log("[print] print CSS loaded");
    linkFont.rel = "preload";
          resolve();
    linkFont.as = "font";
        };
    linkFont.type = "font/woff2";
        linkCss.onerror = function () {
    linkFont.href = "/fonts/HALColant-TextRegular.woff2?v=20250820";
          console.warn("[print] CSS failed to load");
    linkFont.crossOrigin = "anonymous";
          resolve();
        };
      });


      // Preload font inside iframe
    doc.head.appendChild(linkFont);
      var linkFont = doc.createElement("link");
    doc.head.appendChild(linkCss);
      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);
    // inject HTML
      doc.head.appendChild(linkCss);
    doc.body.innerHTML = printHtml;


      // Inject printable HTML
    // border preference -> <html> class
       doc.body.innerHTML = printHtml;
    (function () {
       if (DEBUG) console.log("[print] injected print HTML");
       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,
            ""
          );
      }
    })();


      // Apply border preference to <html>
    // clean empty paragraphs
      (function () {
    (function () {
        var htmlEl = doc.documentElement;
      var ps = doc.querySelectorAll("#article-content p");
        if (borderPref === "without") {
      Array.prototype.forEach.call(ps, function (p) {
          if (htmlEl.classList) {
        var txt = (p.textContent || "").replace(/\u00a0/g, " ").trim();
            htmlEl.classList.add("print-no-border");
        var onlyBr =
          } else if (
          p.children.length === 1 &&
            (" " + htmlEl.className + " ").indexOf(" print-no-border ") === -1
           p.firstElementChild &&
           ) {
          p.firstElementChild.tagName === "BR";
            htmlEl.className += " print-no-border";
         if (
          }
           (!txt && !p.querySelector("img, a, strong, em, span:not(:empty)")) ||
         } else {
           onlyBr
           if (htmlEl.classList) {
        ) {
            htmlEl.classList.remove("print-no-border");
          if (p.parentNode) p.parentNode.removeChild(p);
           } else {
            htmlEl.className = htmlEl.className.replace(
              /\bprint-no-border\b/g,
              ""
            );
          }
         }
         }
        if (DEBUG) console.log("[print] borderPref applied:", borderPref);
       });
       })();
       var root = doc.getElementById("article-content");
 
      if (root) {
      // Clean empty paragraphs
         Array.prototype.slice.call(root.childNodes).forEach(function (n) {
       (function () {
           if (n.nodeType === 3 && !n.textContent.replace(/\s+/g, "")) {
        var ps = doc.querySelectorAll("#article-content p");
             root.removeChild(n);
         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 for print
    // small inline tweaks
      (function () {
    (function () {
        var css =
      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;}' +
        '  [class^\\"article-label-\\"]{margin-top:0!important;}' +
          '  .article-entry-number + [class^="article-label-"],' +
        '  .article-entry-number + [class^\\"article-label-\\"],' +
          '  .link-pdf + [class^="article-label-"],' +
        '  .link-pdf + [class^\\"article-label-\\"],' +
          '  .article-type + [class^="article-label-"],' +
        '  .article-type + [class^\\"article-label-\\"],' +
          '  .article-metadata + [class^="article-label-"],' +
        '  .article-metadata + [class^\\"article-label-\\"],' +
          '  .article-images + [class^="article-label-"],' +
        '  .article-images + [class^\\"article-label-\\"],' +
          '  .article-description + [class^="article-label-"],' +
        '  .article-description + [class^\\"article-label-\\"],' +
          '  .article-reflection + [class^="article-label-"],' +
        '  .article-reflection + [class^\\"article-label-\\"],' +
          '  .article-external-reference + [class^="article-label-"],' +
        '  .article-external-reference + [class^\\"article-label-\\"],' +
          '  .article-quote + [class^="article-label-"],' +
        '  .article-quote + [class^\\"article-label-\\"],' +
          '  .article-mod-line + [class^="article-label-"]{margin-top:0.9mm!important;}' +
        '  .article-mod-line + [class^\\"article-label-\\"]{margin-top:0.9mm!important;}' +
          "  .article-label-description + .article-description," +
        "  .article-label-description + .article-description," +
          "  .article-label-reflection + .article-reflection," +
        "  .article-label-reflection + .article-reflection," +
          "  .article-label-external-reference + .article-external-reference," +
        "  .article-label-external-reference + .article-external-reference," +
          "  .article-label-quote + .article-quote," +
        "  .article-label-quote + .article-quote," +
          "  .article-label-modification-date + .article-modification-date{margin-top:0!important;}" +
        "  .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;}" +
          "  .link-pdf{margin-top:0!important;}" +
        "  .link-pdf{margin-top:0!important;}" +
          "  #article-content > :last-child{padding-bottom:0!important;}" +
        "  #article-content > :last-child{padding-bottom:0!important;}" +
          "  #article-content > :last-child::after{content:none!important;}" +
        "  #article-content > :last-child::after{content:none!important;}" +
          "}";
        "}";
        var style = doc.createElement("style");
      var style = doc.createElement("style");
        style.type = "text/css";
      style.type = "text/css";
        style.appendChild(doc.createTextNode(css));
      style.appendChild(doc.createTextNode(css));
        doc.head.appendChild(style);
      doc.head.appendChild(style);
      })();
    })();


      // PDF-friendly links
    // link tweaks
      (function () {
    (function () {
        var styleFix = doc.createElement("style");
      var styleFix = doc.createElement("style");
        styleFix.textContent =
      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}}";
        "@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);
      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";
      });
    })();


        var refs = doc.querySelectorAll(".article-external-reference a[href]");
    // waits
        Array.prototype.forEach.call(refs, function (a) {
    function waitImages() {
          var txt = (a.textContent || "").trim();
      var imgs = [].slice.call(doc.images || []);
          var href = a.getAttribute("href") || "";
      if (!imgs.length) return Promise.resolve();
          var looksLongUrl = /^https?:\/\//i.test(txt) && txt.length > 60;
      return Promise.all(
           if (looksLongUrl) {
        imgs.map(function (img) {
           if (img.decode) {
             try {
             try {
               var u = new URL(href, doc.baseURI);
               return img.decode().catch(function () {});
              var label =
             } catch (e) {}
                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";
           return new Promise(function (res) {
          a.style.wordBreak = "normal";
            if (img.complete) return res();
          a.style.overflowWrap = "normal";
            img.onload = img.onerror = function () {
        });
               res();
      })();
 
      // Wait helpers
      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 () {
          if (DEBUG) console.log("[print] resources ready; calling print()");
          try {
            // 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);
              $btn.data("busy", false);
              if (DEBUG) console.log("[print] afterprint cleanup done");
             };
             };
 
           });
            doc.title = desiredTitle;
            document.title = desiredTitle;
 
            iframe.contentWindow.focus();
            iframe.contentWindow.print();
 
            // Safety cleanup if onafterprint never fires
            setTimeout(function () {
              try {
                doc.title = oldIframeTitle;
                document.title = oldParentTitle;
              } catch (e) {}
              if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
              $btn.data("busy", false);
              if (DEBUG) console.log("[print] timeout cleanup done");
            }, 1000);
           } catch (err) {
            console.warn("[print] failed before print:", err);
            $btn.data("busy", false);
          }
         })
         })
         .catch(function (err) {
      );
          console.warn("[print] Promise chain error:", err);
    }
          $btn.data("busy", false);
    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);
      });
     }
     }


     // 6b) Choice click handler (decides source and calls buildIframeAndPrint)
     Promise.all([
     function handlePrintChoice(id, $btn) {
      cssLoaded,
      if ($btn.data("busy")) {
      waitImages(),
        if (DEBUG) console.log("[print] busy, ignoring click");
      waitFonts(1200),
         return;
      waitSpecificFont(1200),
      }
      nextFrame(),
      $btn.data("busy", true);
     ])
      .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;


      var borderPref = id === "print-no-border" ? "without" : "with";
        iframe.contentWindow.onafterprint = function () {
      if (DEBUG) console.log("[print] option chosen:", borderPref);
          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);
        };


      preloadFontForPrint();
        doc.title = desiredTitle;
        document.title = desiredTitle;


      // Prefer local .print-only (Entry page)
        iframe.contentWindow.focus();
      var localPrintOnly = $(".print-only").first();
         iframe.contentWindow.print();
      if (DEBUG)
         console.log(
          "[print] local .print-only present:",
          localPrintOnly.length
        );


      if (localPrintOnly.length) {
        // safety cleanup
        if (DEBUG) console.log("[print] using local .print-only on this page");
        setTimeout(function () {
        hidePrintUI();
          try {
        buildIframeAndPrint(localPrintOnly.prop("outerHTML"), borderPref, $btn);
            doc.title = oldIframeTitle;
         return;
            document.title = oldParentTitle;
       }
          } catch (e) {}
 
          if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
       // Otherwise fetch by title (modal/home list flow)
          if ($btn && $btn.length) $btn.data("busy", false);
      var title =
         }, 1000);
        window.currentEntryTitle ||
       })
         (window.mw &&
       .catch(function () {
          mw.config &&
         if ($btn && $btn.length) $btn.data("busy", false);
          mw.config.get &&
       });
          mw.config.get("wgPageName"));
  }
       if (DEBUG) console.log("[print] currentEntryTitle / wgPageName:", title);


      if (!title) {
  /* decide source & kick print */
        console.warn("[print] no title; falling back to window.print()");
  function swHandlePrintChoice(id, $btn) {
        window.print();
    if ($btn && $btn.data("busy")) return;
        $btn.data("busy", false);
    if ($btn && $btn.length) $btn.data("busy", true);
        return;
      }


      var pageUrl =
    var borderPref = id === "print-no-border" ? "without" : "with";
        window.mw && mw.util && mw.util.getUrl
    swPrintPreloadFont();
          ? mw.util.getUrl(title)
          : "/wiki/" + String(title);
      var pageUrlFresh = cacheBust(pageUrl);
      if (DEBUG) console.log("[print] fetching page HTML:", pageUrlFresh);


      $.get(pageUrlFresh)
    // prefer local .print-only (Entry page)
        .done(function (html) {
    var localPrintOnly = jQuery(".print-only").first();
          var $tmp = $("<div>").html(html);
    if (localPrintOnly.length) {
          var $print = $tmp.find(".print-only").first();
      swHidePrintUI();
          if (DEBUG)
      swBuildIframeAndPrint(localPrintOnly.prop("outerHTML"), borderPref, $btn);
            console.log("[print] .print-only in fetched page:", $print.length);
      return;
          if (!$print.length) {
            console.warn(
              "[print] no .print-only in fetched page; window.print()"
            );
            window.print();
            $btn.data("busy", false);
            return;
          }
          hidePrintUI();
          buildIframeAndPrint($print.prop("outerHTML"), borderPref, $btn);
        })
        .fail(function (xhr) {
          console.warn(
            "[print] fetch failed:",
            xhr && xhr.status,
            xhr && xhr.statusText
          );
          window.print();
          $("#print-button").data("busy", false);
        });
     }
     }


     // 7) Event wiring — one tolerant handler
     // otherwise fetch by title (modal/home list flow)
     $(document).off("click.print");
     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;
    }


     // Container handler:
     var pageUrl =
    // - #print-button toggles chooser
      window.mw && mw.util && mw.util.getUrl
    // - #print-chooser / #print-options capture clicks on the two anchors
        ? mw.util.getUrl(title)
    $(document).on(
         : "/wiki/" + String(title);
      "click.print",
    jQuery
      "#print-button, #print-chooser, #print-options",
      .get(swPrintCacheBust(pageUrl))
      function (e) {
      .done(function (html) {
         // If the main [print] (or a child of it) was clicked, toggle chooser
        var $tmp = jQuery("<div>").html(html);
        if ($(e.target).closest("#print-button").length) {
        var $print = $tmp.find(".print-only").first();
          e.preventDefault();
        if (!$print.length) {
          var $chooser = ensurePrintChooser();
           window.print();
          $chooser.toggle();
           if ($btn && $btn.length) $btn.data("busy", false);
          $("#show-article").toggleClass(
            "print-opts-open",
            $chooser.is(":visible")
           );
           if (DEBUG)
            console.log(
              "[print] toggled chooser; visible=",
              $chooser.is(":visible")
            );
           return;
           return;
         }
         }
        swHidePrintUI();
        swBuildIframeAndPrint($print.prop("outerHTML"), borderPref, $btn);
      })
      .fail(function () {
        window.print();
        jQuery("#print-button").data("busy", false);
      });
  }


        // Otherwise, check if one of the two choice anchors was clicked
  /* wiring (namespaced) */
         var $choice = $(e.target).closest(
  jQuery(document).off("click.swprint");
           "a#print-with-border, a#print-no-border"
  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")
         );
         );
         if (!$choice.length) return; // click inside container but not on a choice
         return;
 
        e.preventDefault();
        var id = $choice.attr("id");
        if (DEBUG) console.log("[print] option anchor click:", id);
        handlePrintChoice(id, $choice);
       }
       }
     );
      // 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);
     }
  );


    // 8) Also hide the chooser when the close button is clicked or ESC is pressed
  // also hide choices on ESC; your close-button handler already hides them
    $(document).on("click.print", "#close-button", hidePrintUI);
  jQuery(document).on("keydown.swprint", function (e) {
    $(document).on("keydown.print", function (e) {
    if (e && e.keyCode === 27) swHidePrintUI();
      // No ES6 keys here; use keyCode for ES5 compatibility
      if (e.keyCode === 27) hidePrintUI(); // ESC
    });
   });
   });
  /* ---------- /Softwear PRINT ---------- */


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

Navigation menu