4,656
edits
No edit summary |
No edit summary |
||
| (30 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 | ||
$("#values-" + filterType).toggle(); | $("#values-" + filterType).toggle(); | ||
| Line 584: | Line 582: | ||
processEventCards(cardSelector); | processEventCards(cardSelector); | ||
updateViews(cardSelector); | updateViews(cardSelector); | ||
}); | }); | ||
| Line 631: | Line 627: | ||
updateViews(cardSelector); | updateViews(cardSelector); | ||
updatePrintSelectionUI(); | updatePrintSelectionUI(); | ||
hidePrintSelectionOptions(); | |||
console.log("Filtering process complete, updated views and borders"); | console.log("Filtering process complete, updated views and borders"); | ||
| Line 700: | 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(); | event.stopPropagation(); | ||
$("#filters .values button").removeClass("active"); | $("#filters .values button").removeClass("active"); | ||
$(".open-filter").removeClass("active-filter"); | $(".open-filter").removeClass("active-filter"); | ||
$(".count-filtered-cards").text("").hide(); | $(".count-filtered-cards").text("").hide(); | ||
filterCards | filterCards(); | ||
hidePrintSelectionOptions(); | |||
}); | }); | ||
$("#filters .values button").click(function () { | $("#filters .values button").click(function () { | ||
$(this).toggleClass("active"); | $(this).toggleClass("active"); | ||
filterCards | filterCards(); | ||
}); | }); | ||
| Line 1,321: | Line 1,303: | ||
function updatePrintSelectionUI() { | function updatePrintSelectionUI() { | ||
var | requestAnimationFrame(function () { | ||
var activeCount = jQuery("#filters .values button.active").length; | |||
if (activeCount > 0) { | |||
jQuery("#print-selection-wrapper").show(); | jQuery("#print-selection-wrapper").show(); | ||
} else { | |||
jQuery("#print-selection-wrapper").hide(); | jQuery("#print-selection-wrapper").hide(); | ||
jQuery("#print-selection-options").hide(); | jQuery("#print-selection-options").hide(); | ||
} | } | ||
}); | |||
} | } | ||
function hidePrintSelectionOptions() { | |||
jQuery("#print-selection-options").hide(); | |||
} | |||
} | |||
function swHandleBatchPrint(borderPref) { | |||
function | swPrintPreloadFont(); | ||
var | var $cards = jQuery(".card:visible").not(".event"); | ||
if (!$cards.length) { | |||
alert("No entries to print."); | |||
return; | |||
} | |||
var requests = []; | |||
var | $cards.each(function () { | ||
var $card = jQuery(this); | |||
var title = $card.data("page"); | |||
if (!title) return; | |||
var url = | |||
window.mw && mw.util && mw.util.getUrl | |||
? swPrintCacheBust(mw.util.getUrl(title)) | |||
: swPrintCacheBust("/wiki/" + String(title)); | |||
requests.push( | |||
jQuery.get(url).then(function (html) { | |||
var $tmp = jQuery("<div>").html(html); | |||
var $print = $tmp.find(".print-only").first(); | |||
return $print.length ? $print.prop("outerHTML") : ""; | |||
}) | |||
); | |||
}); | }); | ||
Promise.all(requests) | |||
.then(function (results) { | |||
var combinedHtml = results.join(""); | |||
if (!combinedHtml.trim()) { | |||
alert("Could not generate print content."); | |||
return; | |||
} | |||
swBuildIframeAndPrint( | |||
combinedHtml, | |||
borderPref, | |||
null, | |||
"filtered.softwear.directory" | |||
); | |||
}) | |||
.catch(function () { | |||
alert("There was a problem preparing the print selection."); | |||
}); | |||
} | |||
/* small boot probe */ | |||
(function () { | |||
try { | |||
console.log("[swprint] probe on load", { | |||
printButton: !!document.getElementById("print-button"), | |||
chooserExists: !!document.getElementById("print-chooser"), | |||
localPrintOnlyCount: jQuery(".print-only").length, | |||
showArticleExists: !!document.getElementById("show-article"), | |||
}); | |||
} catch (e) {} | |||
})(); | |||
// apply border preference to <html> | /* core: build iframe and print */ | ||
(function () { | function swBuildIframeAndPrint(printHtml, borderPref, $btn, filenameOverride) { | ||
var htmlEl = doc.documentElement; | // iframe | ||
if (borderPref === "without") { | var iframe = document.createElement("iframe"); | ||
if (htmlEl.classList) htmlEl.classList.add("print-no-border"); | iframe.style.position = "fixed"; | ||
else if ( | iframe.style.right = "0"; | ||
(" " + htmlEl.className + " ").indexOf(" print-no-border ") === -1 | iframe.style.bottom = "0"; | ||
) { | iframe.style.width = "0"; | ||
htmlEl.className += " print-no-border"; | iframe.style.height = "0"; | ||
} | iframe.style.border = "0"; | ||
} else { | document.body.appendChild(iframe); | ||
if (htmlEl.classList) htmlEl.classList.remove("print-no-border"); | |||
else | var doc = iframe.contentDocument || iframe.contentWindow.document; | ||
htmlEl.className = (htmlEl.className || "").replace( | doc.open(); | ||
/\bprint-no-border\b/g, | doc.write( | ||
"" | '<!doctype html><html><head><meta charset="utf-8"><title>Print</title></head><body></body></html>' | ||
); | ); | ||
doc.close(); | |||
// make relative URLs resolve | |||
var base = doc.createElement("base"); | |||
base.href = location.origin + "/"; | |||
doc.head.appendChild(base); | |||
// print.css | |||
var linkCss = doc.createElement("link"); | |||
linkCss.rel = "stylesheet"; | |||
linkCss.href = swPrintCacheBust( | |||
"/index.php?title=MediaWiki:Print.css&action=raw&ctype=text/css" | |||
); | |||
var cssLoaded = new Promise(function (resolve) { | |||
linkCss.onload = resolve; | |||
linkCss.onerror = resolve; | |||
}); | |||
// font preload (inside iframe) | |||
var linkFont = doc.createElement("link"); | |||
linkFont.rel = "preload"; | |||
linkFont.as = "font"; | |||
linkFont.type = "font/woff2"; | |||
linkFont.href = "/fonts/HALColant-TextRegular.woff2?v=20250820"; | |||
linkFont.crossOrigin = "anonymous"; | |||
doc.head.appendChild(linkFont); | |||
doc.head.appendChild(linkCss); | |||
// inject HTML | |||
doc.body.innerHTML = printHtml; | |||
(function () { | |||
var pres = doc.querySelectorAll(".link-pdf pre"); | |||
Array.prototype.forEach.call(pres, function (pre) { | |||
// move its children out | |||
while (pre.firstChild) { | |||
pre.parentNode.insertBefore(pre.firstChild, pre); | |||
} | |||
// 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, | |||
"" | |||
); | |||
} | |||
})(); | |||
// Glue label + body together (extra safety vs. page breaks) | |||
(function () { | |||
var style = doc.createElement("style"); | |||
style.textContent = | |||
"@media print{.sw-keep{break-inside:avoid;page-break-inside:avoid;}}"; | |||
doc.head.appendChild(style); | |||
var pairs = [ | |||
[".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"], | |||
]; | |||
for (var i = 0; i < pairs.length; i++) { | |||
var labelSel = pairs[i][0]; | |||
var bodySel = pairs[i][1]; | |||
var labels = doc.querySelectorAll(labelSel); | |||
for (var j = 0; j < labels.length; j++) { | |||
var label = labels[j]; | |||
var body = label.nextElementSibling; | |||
if (!body || !body.matches(bodySel)) continue; | |||
var wrap = doc.createElement("div"); | |||
wrap.className = "sw-keep"; | |||
label.parentNode.insertBefore(wrap, label); | |||
wrap.appendChild(label); | |||
wrap.appendChild(body); | |||
} | |||
} | } | ||
})(); | })(); | ||
// | // clean empty paragraphs | ||
(function () { | (function () { | ||
var | 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 && | |||
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); | |||
} | |||
} | |||
} | |||
})(); | |||
var | // inline micro-tweaks for print spacing | ||
(function () { | |||
var css = | |||
"@media print{" + | |||
" .article-description p,.article-reflection p,.article-external-reference p,.article-quote p{margin:0 0 1.2mm!important;}" + | |||
" .article-description p:last-child,.article-reflection p:last-child,.article-external-reference p:last-child,.article-quote p:last-child{margin-bottom:0!important;}" + | |||
" .article-entry-number,.link-pdf,.article-type,.article-metadata,.article-images,.article-description,.article-reflection,.article-external-reference,.article-quote,.article-mod-line{padding-bottom:1mm!important;}" + | |||
" .article-label-description + .article-description," + | |||
" .article-label-reflection + .article-reflection," + | |||
" .article-label-external-reference + .article-external-reference," + | |||
" .article-label-quote + .article-quote," + | |||
" .article-label-modification-date + .article-modification-date{margin-top:0!important;}" + | |||
" .article-title-link{margin:0!important;padding:0!important;}" + | |||
" .article-title-link > *{margin:0!important;}" + | |||
" .link-pdf{margin-top:0!important;}" + | |||
" #article-content > :last-child{padding-bottom:0!important;}" + | |||
" #article-content > :last-child::after{content:none!important;}" + | |||
"}"; | |||
var style = doc.createElement("style"); | |||
style.type = "text/css"; | |||
style.appendChild(doc.createTextNode(css)); | |||
doc.head.appendChild(style); | |||
})(); | |||
// link tweaks (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 | var txt = (a.textContent || "").trim(); | ||
Array.prototype.forEach.call( | var href = a.getAttribute("href") || ""; | ||
var txt = ( | var looksLongUrl = /^https?:\/\//i.test(txt) && txt.length > 60; | ||
var | if (looksLongUrl) { | ||
try { | |||
var u = new URL(href, doc.baseURI); | |||
var label = | |||
u.hostname + (u.pathname.replace(/\/$/, "") ? u.pathname : ""); | |||
if (label.length > 40) label = label.slice(0, 37) + "…"; | |||
a.textContent = label; | |||
} catch (e) { | |||
a.textContent = "Link"; | |||
} | |||
} | } | ||
a.style.whiteSpace = "nowrap"; | |||
a.style.wordBreak = "normal"; | |||
a.style.overflowWrap = "normal"; | |||
}); | }); | ||
})(); | })(); | ||
// | // waits | ||
function waitImages() { | |||
var | 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([ | |||
var | 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([ | |||
Promise.all([ | |||
cssLoaded, | cssLoaded, | ||
waitImages(), | waitImages(), | ||
| Line 1,594: | Line 1,647: | ||
.then(function () { | .then(function () { | ||
// filename via document.title | // filename via document.title | ||
var desiredTitle; | |||
if (filenameOverride) { | |||
desiredTitle = filenameOverride; | |||
} else { | |||
var entryNum = ""; | var entryNum = ""; | ||
var numEl = doc.querySelector(".article-entry-number"); | var numEl = doc.querySelector(".article-entry-number"); | ||
if (numEl) { | if (numEl) { | ||
var m = (numEl.textContent || "").match(/\d+/); | |||
entryNum = m ? m[0] : ""; | |||
} | } | ||
desiredTitle = | |||
(entryNum ? entryNum + "." : "") + "softwear.directory"; | |||
} | |||
var oldIframeTitle = doc.title; | var oldIframeTitle = doc.title; | ||
var oldParentTitle = document.title; | var oldParentTitle = document.title; | ||
| Line 1,618: | Line 1,678: | ||
doc.title = desiredTitle; | doc.title = desiredTitle; | ||
document.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.focus(); | ||
| Line 1,774: | Line 1,839: | ||
return; | return; | ||
} | } | ||
// click directly on a choice link (fallback path) | // click directly on a choice link (fallback path) | ||
var $choice = jQuery(e.target).closest( | var $choice = jQuery(e.target).closest( | ||
"a#print-with-border, a#print-no-border" | "a#print-with-border, a#print-no-border" | ||
); | ); | ||
if (!$choice.length) return; | if (!$choice.length) return; | ||
e.preventDefault(); | e.preventDefault(); | ||
swHandlePrintChoice($choice.attr("id"), $choice); | swHandlePrintChoice($choice.attr("id"), $choice); | ||
} | } | ||
); | ); | ||
// map any <button> inside chooser to its host anchor | // map any <button> inside chooser to its host anchor | ||
jQuery(document).on( | jQuery(document).on( | ||
"click.swprintChoiceBtn2", | "click.swprintChoiceBtn2", | ||
"#print-chooser button", | "#print-chooser button", | ||
function (e) { | function (e) { | ||
var host = this.closest( | var host = this.closest( | ||
'[id="print-with-border"], [id="print-no-border"]' | '[id="print-with-border"], [id="print-no-border"]' | ||
); | ); | ||
if (!host) return; | if (!host) return; | ||
e.preventDefault(); | e.preventDefault(); | ||
swHandlePrintChoice(host.id, (window.jQuery && jQuery(host)) || null); | 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 ---------- */ | /* ---------- /Softwear PRINT ---------- */ | ||
| Line 2,023: | Line 2,130: | ||
updatePrintSelectionUI(); | updatePrintSelectionUI(); | ||
hidePrintSelectionOptions(); | |||
}); | }); | ||