MediaWiki:Common.js: Difference between revisions

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


   /* ========= Softwear Print — ES5, jQuery only ========= */
   /* helpers */
   (function ($) {
   function swPrintPreloadFont() {
     // ---- helpers ----
     var link = document.createElement("link");
    function preloadFontForPrint() {
    link.rel = "preload";
      var link = document.createElement("link");
    link.as = "font";
      link.rel = "preload";
    link.type = "font/woff2";
      link.as = "font";
    link.href = "/fonts/HALColant-TextRegular.woff2?v=20250820";
      link.type = "font/woff2";
    link.crossOrigin = "anonymous";
      link.href = "/fonts/HALColant-TextRegular.woff2?v=20250820";
    document.head.appendChild(link);
      link.crossOrigin = "anonymous";
  }
      document.head.appendChild(link);
    }


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


    function ensurePrintChooser() {
  function swEnsurePrintChooser() {
      var $chooser = $("#print-chooser");
    var $chooser = jQuery("#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;
  }


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


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


     // Remove any previous versions
     var doc = iframe.contentDocument || iframe.contentWindow.document;
     $(document).off("click.print");
     doc.open();
    doc.write(
      '<!doctype html><html><head><meta charset="utf-8"><title>Print</title></head><body></body></html>'
    );
    doc.close();


     // One handler for all the print UI
     // make relative URLs resolve
     $(document).on(
     var base = doc.createElement("base");
      "click.print",
    base.href = location.origin + "/";
      "#print-button, #print-chooser a.print-choice, #print-with-border, #print-no-border",
    doc.head.appendChild(base);
      function (e) {
        var $anchor = $(e.target).closest("a");
        if ($anchor.length) e.preventDefault();


        var rawId = this.id;
    // print.css
        var id = ($anchor.attr("id") || rawId || "").trim();
    var linkCss = doc.createElement("link");
 
    linkCss.rel = "stylesheet";
        // 1) Toggle the chooser when [print] is clicked
    linkCss.href = swPrintCacheBust(
        if (id === "print-button") {
      "/index.php?title=MediaWiki:Print.css&action=raw&ctype=text/css"
          var $chooser = ensurePrintChooser();
          var show = !$chooser.is(":visible");
          $chooser.css({ display: show ? "block" : "none" });
          $("#show-article")[show ? "addClass" : "removeClass"](
            "print-opts-open"
          );
          return;
        }
 
        // 2) Must be one of the options
        if (id !== "print-with-border" && id !== "print-no-border") {
          return;
        }
 
        var $btn = $anchor.length ? $anchor : $(this);
        if ($btn.data("busy")) return;
        $btn.data("busy", true);
 
        var borderPref = id === "print-no-border" ? "without" : "with";
        preloadFontForPrint();
 
        // Prefer local .print-only (Entry pages)
        var $localPrintOnly = $(".print-only").first();
        if ($localPrintOnly.length) {
          hideChooser();
          buildIframeAndPrint(
            $localPrintOnly.prop("outerHTML"),
            borderPref,
            $btn
          );
          return;
        }
 
        // Fallback: fetch by title (modal/home/person pages)
        var title =
          window.currentEntryTitle ||
          (mw.config && mw.config.get && mw.config.get("wgPageName"));
        if (!title) {
          window.print();
          $btn.data("busy", false);
          return;
        }
 
        var pageUrlFresh = cacheBust(mw.util.getUrl(title));
        $.get(pageUrlFresh)
          .done(function (html) {
            var $tmp = $("<div>").html(html);
            var $print = $tmp.find(".print-only").first();
            if (!$print.length) {
              window.print();
              $btn.data("busy", false);
              return;
            }
            hideChooser();
            buildIframeAndPrint($print.prop("outerHTML"), borderPref, $btn);
          })
          .fail(function () {
            window.print();
            $("#print-button").data("busy", false);
          });
      }
     );
     );


     // Also hide the chooser when closing the card
     var cssLoaded = new Promise(function (resolve) {
    $(document).on("click.print", "#close-button", function () {
      linkCss.onload = function () {
      hideChooser();
        resolve();
      };
      linkCss.onerror = function () {
        resolve();
      };
     });
     });


     // ---- iframe + print ----
     // font preload (inside iframe)
    function buildIframeAndPrint(printHtml, borderPref, $btn) {
    var linkFont = doc.createElement("link");
      // Hidden iframe
    linkFont.rel = "preload";
      var iframe = document.createElement("iframe");
    linkFont.as = "font";
      iframe.style.position = "fixed";
    linkFont.type = "font/woff2";
      iframe.style.right = "0";
    linkFont.href = "/fonts/HALColant-TextRegular.woff2?v=20250820";
      iframe.style.bottom = "0";
    linkFont.crossOrigin = "anonymous";
      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();
 
      // Base for relative URLs
      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 = cacheBust(
        "/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();
        };
      });
 
      // Preload font 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(linkFont);
      doc.head.appendChild(linkCss);
    doc.head.appendChild(linkCss);


      // Content
    // inject HTML
      doc.body.innerHTML = printHtml;
    doc.body.innerHTML = printHtml;


      // Apply border preference on <html>
    // border preference -> <html> class
       var rootHtml = doc.documentElement;
    (function () {
       var htmlEl = doc.documentElement;
       if (borderPref === "without") {
       if (borderPref === "without") {
         if (rootHtml.classList) rootHtml.classList.add("print-no-border");
         if (htmlEl.classList) htmlEl.classList.add("print-no-border");
         else rootHtml.className += " print-no-border";
         else if (
          (" " + htmlEl.className + " ").indexOf(" print-no-border ") === -1
        ) {
          htmlEl.className += " print-no-border";
        }
       } else {
       } else {
         if (rootHtml.classList) rootHtml.classList.remove("print-no-border");
         if (htmlEl.classList) htmlEl.classList.remove("print-no-border");
         else
         else
           rootHtml.className = rootHtml.className.replace(
           htmlEl.className = htmlEl.className.replace(
             /\bprint-no-border\b/g,
             /\bprint-no-border\b/g,
             ""
             ""
           );
           );
       }
       }
    })();
    // clean empty paragraphs
    (function () {
      var ps = doc.querySelectorAll("#article-content p");
      Array.prototype.forEach.call(ps, function (p) {
        var txt = (p.textContent || "").replace(/\u00a0/g, " ").trim();
        var onlyBr =
          p.children.length === 1 &&
          p.firstElementChild &&
          p.firstElementChild.tagName === "BR";
        if (
          (!txt && !p.querySelector("img, a, strong, em, span:not(:empty)")) ||
          onlyBr
        ) {
          if (p.parentNode) p.parentNode.removeChild(p);
        }
      });
      var root = doc.getElementById("article-content");
      if (root) {
        Array.prototype.slice.call(root.childNodes).forEach(function (n) {
          if (n.nodeType === 3 && !n.textContent.replace(/\s+/g, "")) {
            root.removeChild(n);
          }
        });
      }
    })();


      // Minimal inline tweaks
    // small inline tweaks
      var tweaks = doc.createElement("style");
    (function () {
       tweaks.textContent =
       var css =
         "@media print{" +
         "@media print{" +
         "  .article-description p,.article-reflection p,.article-external-reference p,.article-quote p{margin:0 0 1.2mm!important;}" +
         "  .article-description p,.article-reflection p,.article-external-reference p,.article-quote p{margin:0 0 1.2mm!important;}" +
         "  .article-description p:last-child,.article-reflection p:last-child,.article-external-reference p:last-child,.article-quote p:last-child{margin-bottom:0!important;}" +
         "  .article-description p:last-child,.article-reflection p:last-child,.article-external-reference p:last-child,.article-quote p:last-child{margin-bottom:0!important;}" +
         "  .article-entry-number,.link-pdf,.article-type,.article-metadata,.article-images,.article-description,.article-reflection,.article-external-reference,.article-quote,.article-mod-line{padding-bottom:1mm!important;}" +
         "  .article-entry-number,.link-pdf,.article-type,.article-metadata,.article-images,.article-description,.article-reflection,.article-external-reference,.article-quote,.article-mod-line{padding-bottom:1mm!important;}" +
        '  [class^\\"article-label-\\"]{margin-top:0!important;}' +
        '  .article-entry-number + [class^\\"article-label-\\"],' +
        '  .link-pdf + [class^\\"article-label-\\"],' +
        '  .article-type + [class^\\"article-label-\\"],' +
        '  .article-metadata + [class^\\"article-label-\\"],' +
        '  .article-images + [class^\\"article-label-\\"],' +
        '  .article-description + [class^\\"article-label-\\"],' +
        '  .article-reflection + [class^\\"article-label-\\"],' +
        '  .article-external-reference + [class^\\"article-label-\\"],' +
        '  .article-quote + [class^\\"article-label-\\"],' +
        '  .article-mod-line + [class^\\"article-label-\\"]{margin-top:0.9mm!important;}' +
        "  .article-label-description + .article-description," +
        "  .article-label-reflection + .article-reflection," +
        "  .article-label-external-reference + .article-external-reference," +
        "  .article-label-quote + .article-quote," +
        "  .article-label-modification-date + .article-modification-date{margin-top:0!important;}" +
         "  .article-title-link{margin:0!important;padding:0!important;}" +
         "  .article-title-link{margin:0!important;padding:0!important;}" +
         "  .article-title-link > *{margin:0!important;}" +
         "  .article-title-link > *{margin:0!important;}" +
Line 1,464: Line 1,425:
         "  #article-content > :last-child::after{content:none!important;}" +
         "  #article-content > :last-child::after{content:none!important;}" +
         "}";
         "}";
       doc.head.appendChild(tweaks);
      var style = doc.createElement("style");
      style.type = "text/css";
      style.appendChild(doc.createTextNode(css));
       doc.head.appendChild(style);
    })();
 
    // link tweaks
    (function () {
      var styleFix = doc.createElement("style");
      styleFix.textContent =
        "@media print {.article-external-reference a,.link-pdf a{white-space:nowrap!important;word-break:normal!important;overflow-wrap:normal!important;text-decoration:underline}.article-external-reference{overflow-wrap:anywhere;word-break:break-word}a[href]{position:relative}}";
      doc.head.appendChild(styleFix);
      var refs = doc.querySelectorAll(".article-external-reference a[href]");
      Array.prototype.forEach.call(refs, function (a) {
        var txt = (a.textContent || "").trim();
        var href = a.getAttribute("href") || "";
        var looksLongUrl = /^https?:\/\//i.test(txt) && txt.length > 60;
        if (looksLongUrl) {
          try {
            var u = new URL(href, doc.baseURI);
            var label =
              u.hostname + (u.pathname.replace(/\/$/, "") ? u.pathname : "");
            if (label.length > 40) label = label.slice(0, 37) + "…";
            a.textContent = label;
          } catch (e) {
            a.textContent = "Link";
          }
        }
        a.style.whiteSpace = "nowrap";
        a.style.wordBreak = "normal";
        a.style.overflowWrap = "normal";
      });
    })();


      // Wait for resources, then print
    // waits
      function waitImages() {
    function waitImages() {
        var imgs = [].slice.call(doc.images || []);
      var imgs = [].slice.call(doc.images || []);
        if (!imgs.length) return Promise.resolve();
      if (!imgs.length) return Promise.resolve();
        return Promise.all(
      return Promise.all(
          imgs.map(function (img) {
        imgs.map(function (img) {
            if (img.decode) {
          if (img.decode) {
              try {
            try {
                return img.decode().catch(function () {});
              return img.decode().catch(function () {});
              } catch (e) {}
            } catch (e) {}
            }
          }
            return new Promise(function (res) {
          return new Promise(function (res) {
              if (img.complete) return res();
            if (img.complete) return res();
              img.onload = img.onerror = function () {
            img.onload = img.onerror = function () {
                res();
              res();
              };
            };
            });
          });
          })
        })
        );
      );
      }
    }
      function waitFonts(timeoutMs) {
    function waitFonts(timeoutMs) {
        if (!doc.fonts || !doc.fonts.ready) return Promise.resolve();
      if (!doc.fonts || !doc.fonts.ready) return Promise.resolve();
        var ready = doc.fonts.ready;
      var ready = doc.fonts.ready;
        var t = new Promise(function (res) {
      var t = new Promise(function (res) {
          setTimeout(res, timeoutMs || 1200);
        setTimeout(res, timeoutMs || 1200);
        });
      });
        return Promise.race([ready, t]);
      return Promise.race([ready, t]);
      }
    }
      function waitSpecificFont(timeoutMs) {
    function waitSpecificFont(timeoutMs) {
        if (!doc.fonts || !doc.fonts.load) return Promise.resolve();
      if (!doc.fonts || !doc.fonts.load) return Promise.resolve();
        var p = Promise.all([
      var p = Promise.all([
          doc.fonts.load('400 16px "HALColant-TextRegular"'),
        doc.fonts.load('400 16px "HALColant-TextRegular"'),
          doc.fonts.load('normal 16px "HALColant-TextRegular"'),
        doc.fonts.load('normal 16px "HALColant-TextRegular"'),
        ]);
      ]);
        var t = new Promise(function (res) {
      var t = new Promise(function (res) {
          setTimeout(res, timeoutMs || 1200);
        setTimeout(res, timeoutMs || 1200);
        });
      });
        return Promise.race([p, t]);
      return Promise.race([p, t]);
      }
    }
      function nextFrame() {
    function nextFrame() {
        return new Promise(function (res) {
      return new Promise(function (res) {
          (iframe.contentWindow.requestAnimationFrame || setTimeout)(res, 0);
        (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();


      Promise.all([
         // safety cleanup
        cssLoaded,
         setTimeout(function () {
        waitImages(),
        waitFonts(1200),
         waitSpecificFont(1200),
        nextFrame(),
      ])
         .then(function () {
           try {
           try {
             // Filename via entry number
             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);
 
    var borderPref = id === "print-no-border" ? "without" : "with";
    swPrintPreloadFont();
 
    // prefer local .print-only (Entry page)
    var localPrintOnly = jQuery(".print-only").first();
    if (localPrintOnly.length) {
      swHidePrintUI();
      swBuildIframeAndPrint(localPrintOnly.prop("outerHTML"), borderPref, $btn);
      return;
    }
 
    // otherwise fetch by title (modal/home list flow)
    var title =
      window.currentEntryTitle ||
      (window.mw && mw.config && mw.config.get && mw.config.get("wgPageName"));
    if (!title) {
      window.print();
      if ($btn && $btn.length) $btn.data("busy", false);
      return;
    }


            var oldIframeTitle = doc.title;
    var pageUrl =
            var oldParentTitle = document.title;
      window.mw && mw.util && mw.util.getUrl
        ? mw.util.getUrl(title)
        : "/wiki/" + String(title);
    jQuery
      .get(swPrintCacheBust(pageUrl))
      .done(function (html) {
        var $tmp = jQuery("<div>").html(html);
        var $print = $tmp.find(".print-only").first();
        if (!$print.length) {
          window.print();
          if ($btn && $btn.length) $btn.data("busy", false);
          return;
        }
        swHidePrintUI();
        swBuildIframeAndPrint($print.prop("outerHTML"), borderPref, $btn);
      })
      .fail(function () {
        window.print();
        jQuery("#print-button").data("busy", false);
      });
  }


            iframe.contentWindow.onafterprint = function () {
  /* wiring (namespaced) */
              try {
  jQuery(document).off("click.swprint");
                doc.title = oldIframeTitle;
  jQuery(document).on(
                document.title = oldParentTitle;
    "click.swprint",
              } catch (e) {}
    "#print-button, #print-chooser, #print-options",
              setTimeout(function () {
    function (e) {
                if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
      // click on the main [print]
              }, 100);
      if (jQuery(e.target).closest("#print-button").length) {
              $btn.data("busy", false);
        e.preventDefault();
            };
        var $chooser = swEnsurePrintChooser();
        $chooser.toggle();
        jQuery("#show-article").toggleClass(
          "print-opts-open",
          $chooser.is(":visible")
        );
        return;
      }
      // click on a choice link
      var $choice = jQuery(e.target).closest(
        "a#print-with-border, a#print-no-border"
      );
      if (!$choice.length) return;
      e.preventDefault();
      swHandlePrintChoice($choice.attr("id"), $choice);
    }
  );


            doc.title = desiredTitle;
  // --- extra safety bindings for Entry page / widgets ---
            document.title = desiredTitle;


            iframe.contentWindow.focus();
  // 1) Directly bind the anchors (works even if container delegation misses)
            iframe.contentWindow.print();
  jQuery(document).on(
    "click.swprintChoice",
    "a#print-with-border, a#print-no-border, #print-with-border, #print-no-border",
    function (e) {
      console.log("[print] direct anchor handler fired:", this.id);
      e.preventDefault();
      swHandlePrintChoice(this.id, jQuery(this));
    }
  );


            // Safety cleanup
  // 2) If using the Button widget (<a id=...><button>...</button></a>),
            setTimeout(function () {
  //    capture clicks on the <button> and route via its parent <a>.
              try {
  jQuery(document).on(
                doc.title = oldIframeTitle;
    "click.swprintChoiceBtn",
                document.title = oldParentTitle;
    "#print-options button",
              } catch (e) {}
    function (e) {
              if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
      var $a = jQuery(this).closest("a[id]");
              $btn.data("busy", false);
      if (!$a.length) return;
            }, 1000);
      console.log(
          } catch (err) {
        "[print] widget button handler fired; parent anchor id:",
            $btn.data("busy", false);
        $a.attr("id")
          }
      );
        })
      e.preventDefault();
        .catch(function () {
      swHandlePrintChoice($a.attr("id"), $a);
          $btn.data("busy", false);
        });
     }
     }
   })(jQuery);
   );
 
  // also hide choices on ESC; your close-button handler already hides them
  jQuery(document).on("keydown.swprint", function (e) {
    if (e && e.keyCode === 27) swHidePrintUI();
  });


   /* ---------- /Softwear PRINT ---------- */
   /* ---------- /Softwear PRINT ---------- */

Navigation menu