4,554
edits
| No edit summary | No edit summary | ||
| Line 1,297: | Line 1,297: | ||
|    $(document).off( |    $(document).off( | ||
|      "click.print", |      "click.print", | ||
|      "#print-button, #print-with-border, #print-no-border" |      "#print-button, #print-with-border, #print-no-border, #print-chooser a.print-choice, #print-options a#print-with-border, #print-options a#print-no-border" | ||
|    ); |    ); | ||
|    // One handler for all  |    // One handler for all the print UI | ||
|    $(document).on( |    $(document).on( | ||
|      "click.print", |      "click.print", | ||
|      "#print-button, #print-with-border, #print-no-border", |      "#print-button, #print-with-border, #print-no-border, #print-chooser a.print-choice, #print-options a#print-with-border, #print-options a#print-no-border", | ||
|      function (e) { |      function (e) { | ||
|        e.preventDefault(); |        // Normalize the target to the closest <a> (covers the widget’s <a><button></button></a>) | ||
|       var $anchor = $(e.target).closest("a"); | |||
|       if ($anchor.length) e.preventDefault(); | |||
|        var id =  |       var rawId = this.id; // in case you clicked the element with the id directly | ||
|        var id = ($anchor.attr("id") || rawId || "").trim(); | |||
|        //  |       console.log("[print] click:", { | ||
|         target: e.target.tagName, | |||
|         thisId: rawId, | |||
|         anchorId: id, | |||
|       }); | |||
|        // Toggle chooser if main [print] clicked | |||
|        if (id === "print-button") { |        if (id === "print-button") { | ||
|          var $chooser = ensurePrintChooser(); |          var $chooser = ensurePrintChooser(); | ||
|          $chooser.toggle(); |          $chooser.toggle(); | ||
|          var $article = $("#show-article"); |          var $article = $("#show-article"); | ||
|          if ($chooser.is(":visible")) { |          if ($chooser.is(":visible")) { | ||
| Line 1,321: | Line 1,329: | ||
|            $article.removeClass("print-opts-open"); |            $article.removeClass("print-opts-open"); | ||
|          } |          } | ||
|          return; //  | |||
|         console.log( | |||
|           "[print] toggled chooser; visible=", | |||
|           $chooser.is(":visible") | |||
|         ); | |||
|          return; // don't start printing yet | |||
|       } | |||
|       // From here on it must be one of the two options | |||
|       if (id !== "print-with-border" && id !== "print-no-border") { | |||
|         console.warn("[print] unexpected click (ignoring). id=", id); | |||
|         return; | |||
|        } |        } | ||
|        var $btn = $anchor.length ? $anchor : $(this); | |||
|        var $btn = $(this); |        if ($btn.data("busy")) { | ||
|        if ($btn.data("busy")) return; |         console.log("[print] busy, ignoring click"); | ||
|         return; | |||
|       } | |||
|        $btn.data("busy", true); |        $btn.data("busy", true); | ||
|        var borderPref = id === "print-no-border" ? "without" : "with"; |        var borderPref = id === "print-no-border" ? "without" : "with"; | ||
|        console.log("[print] option chosen:", borderPref); |        console.log("[print] option chosen:", borderPref); | ||
| Line 1,335: | Line 1,355: | ||
|        preloadFontForPrint(); |        preloadFontForPrint(); | ||
|        //  |        // Prefer local .print-only (Entry page) | ||
|        var localPrintOnly = $(".print-only").first(); |        var localPrintOnly = $(".print-only").first(); | ||
|        console.log("[print] local .print-only present:", localPrintOnly.length); |        console.log("[print] local .print-only present:", localPrintOnly.length); | ||
| Line 1,341: | Line 1,361: | ||
|        if (localPrintOnly.length) { |        if (localPrintOnly.length) { | ||
|          console.log("[print] using local .print-only on this page"); |          console.log("[print] using local .print-only on this page"); | ||
|          $("#print-chooser").hide(); |          $("#print-chooser").hide(); | ||
|          $("#show-article").removeClass("print-opts-open"); |          $("#show-article").removeClass("print-opts-open"); | ||
|          buildIframeAndPrint(localPrintOnly.prop("outerHTML")); |          buildIframeAndPrint(localPrintOnly.prop("outerHTML")); | ||
|          return;  |          return; | ||
|        } |        } | ||
|        //  |        // Otherwise fetch by title (modal/home list flow) | ||
|        var title = |        var title = | ||
|          window.currentEntryTitle || |          window.currentEntryTitle || | ||
| Line 1,355: | Line 1,374: | ||
|        if (!title) { |        if (!title) { | ||
|          console.warn( |          console.warn("[print] no title; falling back to window.print()"); | ||
|          window.print(); |          window.print(); | ||
|          $btn.data("busy", false); |          $btn.data("busy", false); | ||
| Line 1,371: | Line 1,388: | ||
|            var $tmp = $("<div>").html(html); |            var $tmp = $("<div>").html(html); | ||
|            var $print = $tmp.find(".print-only").first(); |            var $print = $tmp.find(".print-only").first(); | ||
|            console.log( |            console.log("[print] .print-only in fetched page:", $print.length); | ||
|            if (!$print.length) { |            if (!$print.length) { | ||
|              console.warn("[print] no .print-only  |              console.warn( | ||
|               "[print] no .print-only in fetched page; window.print()" | |||
|             ); | |||
|              window.print(); |              window.print(); | ||
|              $btn.data("busy", false); |              $btn.data("busy", false); | ||
| Line 1,395: | Line 1,411: | ||
|          }); |          }); | ||
|        //  |        // Helper scoped here so it can see borderPref and $btn | ||
|        function buildIframeAndPrint(printHtml) { |        function buildIframeAndPrint(printHtml) { | ||
|         console.log("[print] buildIframeAndPrint: starting"); | |||
|          // Build hidden iframe |          // Build hidden iframe | ||
|          var iframe = document.createElement("iframe"); |          var iframe = document.createElement("iframe"); | ||
| Line 1,430: | Line 1,447: | ||
|          var cssLoaded = new Promise(function (resolve) { |          var cssLoaded = new Promise(function (resolve) { | ||
|            linkCss.onload = function () { |            linkCss.onload = function () { | ||
|             console.log("[print] print CSS loaded"); | |||
|              resolve(); |              resolve(); | ||
|            }; |            }; | ||
| Line 1,438: | Line 1,456: | ||
|          }); |          }); | ||
|          // Preload the font *inside* iframe  |          // Preload the font *inside* iframe | ||
|          var linkFont = doc.createElement("link"); |          var linkFont = doc.createElement("link"); | ||
|          linkFont.rel = "preload"; |          linkFont.rel = "preload"; | ||
| Line 1,451: | Line 1,469: | ||
|          // Inject the printable HTML |          // Inject the printable HTML | ||
|          doc.body.innerHTML = printHtml; |          doc.body.innerHTML = printHtml; | ||
|         console.log("[print] injected print HTML"); | |||
|          // Apply border preference (class on <html>) |          // Apply border preference (class on <html>) | ||
| Line 1,456: | Line 1,475: | ||
|            var rootHtml = doc.documentElement; |            var rootHtml = doc.documentElement; | ||
|            if (borderPref === "without") { |            if (borderPref === "without") { | ||
|              rootHtml.classList | |||
|               ? rootHtml.classList.add("print-no-border") | |||
|               : (rootHtml.className += " print-no-border"); | |||
|            } else { |            } else { | ||
|              rootHtml.classList | |||
|                rootHtml.classList.remove("print-no-border") |                ? rootHtml.classList.remove("print-no-border") | ||
|                : (rootHtml.className = rootHtml.className.replace( | |||
|                rootHtml.className = rootHtml.className.replace( |                   /\bprint-no-border\b/g, | ||
|                   "" | |||
|                 )); | |||
|            } |            } | ||
|           console.log("[print] borderPref applied:", borderPref); | |||
|          })(); |          })(); | ||
|          //  |          // Clean empty paragraphs | ||
|          (function () { |          (function () { | ||
|            var ps = doc.querySelectorAll("#article-content p"); |            var ps = doc.querySelectorAll("#article-content p"); | ||
| Line 1,495: | Line 1,514: | ||
|          })(); |          })(); | ||
|          // Inline  |          // Inline tweaks | ||
|          (function () { |          (function () { | ||
|            var css = |            var css = | ||
| Line 1,513: | Line 1,532: | ||
|              '  .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-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,531: | Line 1,545: | ||
|          // PDF-friendly links |          // PDF-friendly links | ||
|          var linkCssFix  |          var linkCssFix = | ||
|            "@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 { |         var styleFix = doc.createElement("style"); | ||
|         styleFix.textContent = linkCssFix; | |||
|          doc.head.appendChild(styleFix); | |||
|          doc.head.appendChild( | |||
|          (function () { |          (function () { | ||
| Line 1,561: | Line 1,571: | ||
|                } |                } | ||
|              } |              } | ||
|              a.style.whiteSpace = "nowrap"; |              a.style.whiteSpace = "nowrap"; | ||
|              a.style.wordBreak = "normal"; |              a.style.wordBreak = "normal"; | ||
| Line 1,623: | Line 1,628: | ||
|            waitSpecificFont(1200), |            waitSpecificFont(1200), | ||
|            nextFrame(), |            nextFrame(), | ||
|          ]).then(function () { |          ]) | ||
|           .then(function () { | |||
|             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); | |||
|                 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); | |||
|                 console.log("[print] timeout cleanup done"); | |||
|               }, 1000); | |||
|             } catch (err) { | |||
|               console.warn("[print] failed before print:", err); | |||
|                $btn.data("busy", false); |                $btn.data("busy", false); | ||
|              } |              } | ||
|           }) | |||
|              console.warn("[print]  |            .catch(function (err) { | ||
|              console.warn("[print] Promise chain error:", err); | |||
|              $btn.data("busy", false); |              $btn.data("busy", false); | ||
|            }); | |||
|        } |        } | ||
|      } |      } | ||