MediaWiki:Common.js: Difference between revisions
Jump to navigation
Jump to search
No edit summary |
No edit summary |
||
(9 intermediate revisions by the same user not shown) | |||
Line 176: | Line 176: | ||
if ($("#home").length > 0) { | if ($("#home").length > 0) { | ||
// This code will only run only on the homepage. | // This code will only run only on the homepage. | ||
$(".home-block-view").show(); | $(".home-block-view").show(); | ||
$(".home-chronicle-block-button, .home-block-view-button").addClass( | $(".home-chronicle-block-button, .home-block-view-button").addClass( | ||
Line 184: | Line 182: | ||
); | ); | ||
// Initially hide list view sorting buttons and set the default sorted view for block | // Initially hide list view sorting buttons and set the default sorted view for block | ||
$( | $( | ||
Line 197: | Line 194: | ||
$(".home-list-view-button").click(function () { | $(".home-list-view-button").click(function () { | ||
$(".home-list-sorting-buttons").css("display", "flex"); | $(".home-list-sorting-buttons").css("display", "flex"); | ||
// Switching view classes | // Switching view classes | ||
Line 1,370: | Line 1,366: | ||
// Remove “empty” optional sections so they don’t leave gaps in print. | // Remove “empty” optional sections so they don’t leave gaps in print. | ||
// --- | // --- CLEAN: remove empty paragraphs/whitespace nodes that create phantom gaps | ||
(function | (function () { | ||
// | // 1) Drop <p> that are visually empty or only /whitespace | ||
var ps = doc.querySelectorAll("#article-content p"); | |||
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"; | |||
var txt = ( | |||
if ( | 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 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); | |||
} | |||
}); | |||
} | } | ||
})(); | |||
(function () { | |||
var css = | |||
"@media print{" + | |||
// Paragraphs inside rich text | |||
" .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;}" + | |||
// Ruled sections: one consistent bottom padding for the hairline | |||
} | " .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"); | |||
style.type = "text/css"; | |||
style.appendChild(doc.createTextNode(css)); | |||
doc.head.appendChild(style); | |||
})(); | |||
// --- PDF-friendly links for Chrome on macOS --- | |||
// 1) Add a tiny print-only CSS override to keep anchors as one box. | |||
var linkCssFix = doc.createElement("style"); | |||
// --- PDF-friendly links for Chrome on macOS --- | |||
// 1) Add a tiny print-only CSS override to keep anchors as one box. | |||
var linkCssFix = doc.createElement("style"); | |||
linkCssFix.textContent = | linkCssFix.textContent = | ||
"@media print {\n" + | "@media print {\n" + | ||
Line 1,663: | Line 1,546: | ||
}); | }); | ||
} | } | ||
Promise.all([ | Promise.all([ | ||
Line 1,677: | Line 1,552: | ||
waitFonts(1200), | waitFonts(1200), | ||
waitSpecificFont(1200), | waitSpecificFont(1200), | ||
nextFrame(), | nextFrame(), | ||
]).then(function () { | ]).then(function () { | ||
try { | try { | ||
// build the desired PDF 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"; | |||
// save originals (scoped here) | |||
var oldIframeTitle = doc.title; | |||
var oldParentTitle = document.title; | |||
// define onafterprint AFTER we have originals so it can close over them | |||
iframe.contentWindow.onafterprint = function () { | |||
try { | |||
doc.title = oldIframeTitle; // restore iframe doc title | |||
document.title = oldParentTitle; // restore parent title | |||
} catch (e) {} | |||
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 | |||
doc.title = desiredTitle; // iframe document | |||
// (next line is optional/redundant; doc === iframe.contentWindow.document) | |||
// iframe.contentWindow.document.title = desiredTitle; | |||
document.title = desiredTitle; // parent (helps on some setups) | |||
// print | |||
iframe.contentWindow.focus(); | iframe.contentWindow.focus(); | ||
iframe.contentWindow.print(); | iframe.contentWindow.print(); | ||
// fallback cleanup in case onafterprint | // fallback cleanup in case onafterprint doesn’t fire | ||
setTimeout(function () { | setTimeout(function () { | ||
try { | |||
doc.title = oldIframeTitle; | |||
document.title = oldParentTitle; | |||
} catch (e) {} | |||
if (iframe.parentNode) iframe.parentNode.removeChild(iframe); | if (iframe.parentNode) iframe.parentNode.removeChild(iframe); | ||
$btn.data("busy", false); | $btn.data("busy", false); | ||
}, 1000); | }, 1000); | ||
} catch (err) { | |||
console.warn("[print] failed before print:", err); | |||
$btn.data("busy", false); | |||
} | } | ||
}); | }); | ||
Line 1,761: | Line 1,676: | ||
}); | }); | ||
} | } | ||
if ($("#show-article-wrapper-entry").length) { | if ($("#show-article-wrapper-entry").length) { | ||
// Your existing formatParagraphs function | // Your existing formatParagraphs function | ||
function formatParagraphs(text) { | function formatParagraphs(text) { | ||
Line 1,855: | Line 1,765: | ||
// Check if #submit button exists and add event listener if it does | // Check if #submit button exists and add event listener if it does | ||
var submitButton = document.querySelector("#submit"); | var submitButton = document.querySelector("#submit"); | ||
if (submitButton) { | if (submitButton) { | ||
// Add click event listener | // Add click event listener | ||
submitButton.addEventListener("click", function (event) { | submitButton.addEventListener("click", function (event) { | ||
event.preventDefault(); // Prevent the default link behavior | event.preventDefault(); // Prevent the default link behavior | ||
Latest revision as of 14:35, 28 August 2025
$(document).ready(function () { // Global variables var cards = $(".card"); var showArticleWrapper = $("#show-article-wrapper"); var areFiltersActive = false; // Make header-box in Home clickable $(".head-box").click(function () { window.location.href = "/Main_Page"; // Redirects to the home page }); // Loop through each card to format related articles cards.each(function () { // Check if the card has related articles var relatedArticles = $(this).find(".related-articles"); if (relatedArticles.length > 0) { // Get all the related article elements var relatedArticleElements = relatedArticles.find(".related-article"); // Create an array to store unique related articles var uniqueArticles = []; // Loop through each related article element relatedArticleElements.each(function () { // Remove <p> tags from the article $(this).find("p").remove(); // Convert the article HTML to a string var articleHTML = $(this)[0].outerHTML; // Check if the article HTML already exists in the uniqueArticles array if ($.inArray(articleHTML, uniqueArticles) === -1) { // If it doesn't exist, add it to the uniqueArticles array uniqueArticles.push(articleHTML); } }); // Clear the content of the related articles container relatedArticles.empty(); // Append the unique related articles back to the container relatedArticles.append(uniqueArticles.join("")); } }); // Utility Functions function sortChronologically() { var cards = $(".list-container .card").get(); cards.sort(function (a, b) { var numberA = parseInt( $(a).find(".entry-number").text().replace(/\[|\]/g, ""), 10 ); var numberB = parseInt( $(b).find(".entry-number").text().replace(/\[|\]/g, ""), 10 ); return numberB - numberA; // Descending order }); $.each(cards, function (index, item) { $(".list-container").append(item); }); } function randomizeCards(selector) { var cards = $(selector).get(); var i = cards.length, j, temp; while (--i > 0) { j = Math.floor(Math.random() * (i + 1)); temp = cards[i]; cards[i] = cards[j]; cards[j] = temp; } $.each(cards, function (index, item) { $(selector).parent().append(item); }); } function sortAlphabetically(selector) { var cards = $(selector).get(); cards.sort(function (a, b) { var titleA = $(a).find(".title").text().toUpperCase(); var titleB = $(b).find(".title").text().toUpperCase(); return titleA < titleB ? -1 : titleA > titleB ? 1 : 0; }); $.each(cards, function (index, item) { $(selector).parent().append(item); }); } function updateViews() { // Handle cards in the list view $(".home-chronicle-list div.list-container div.card:not(.event)").each( function () { if (!$(this).closest(".home-chronicle-block").length) { var title = $(this).find(".title").detach(); var images = $(this).find(".images").detach(); // Remove existing .title-images if it exists $(this).find(".title-images").remove(); var titleImagesContainer = $( '<div class="title-images"></div>' ).append(images, title); $(this).find(".people").after(titleImagesContainer); } } ); // Handle cards in the block view $(".home-chronicle-block div.list-container div.card:not(.event)").each( function () { // Remove .title-images container if it exists, re-attach .title and .images to their original places var titleImagesContainer = $(this).find(".title-images"); if (titleImagesContainer.length) { var title = titleImagesContainer.find(".title").detach(); var images = titleImagesContainer.find(".images").detach(); titleImagesContainer.remove(); $(this).find(".people").after(title); $(this).find(".type").after(images); } else { // If .title-images doesn't exist, ensure .images is placed correctly var images = $(this).find(".images").detach(); $(this).find(".type").after(images); } } ); } function processEventCards() { $(".card.event").each(function () { var $card = $(this); var existingContainer = $card.find(".container-people-date"); // Create container if missing if (existingContainer.length === 0) { existingContainer = $('<div class="container-people-date"></div>'); $card.append(existingContainer); // temp placement } // Detach people and date var people = $card.find(".people").detach(); var date = $card.find(".date").detach(); // BLOCK VIEW (gallery) if ($card.closest(".home-chronicle-block").length) { existingContainer.append(people).append(date); // Place container after title if (!existingContainer.is($card.find(".title").next())) { $card.find(".title").after(existingContainer); } // LIST VIEW } else if ($card.closest(".home-chronicle-list").length) { // Only append .people into container existingContainer.empty().append(people); // Place container before title $card.find(".title").before(existingContainer); // Place date after title $card.find(".title").after(date); } }); } if ($("#home").length > 0) { // This code will only run only on the homepage. $(".home-block-view").show(); $(".home-chronicle-block-button, .home-block-view-button").addClass( "active-view-button" ); // Initially hide list view sorting buttons and set the default sorted view for block $( ".home-chronicle-list-button, .home-random-list-button, .home-alphabetical-list-button" ).hide(); sortChronologically(); // Sort the block view chronologically by default updateLastVisibleCard(); updateWidthBlockView(); processEventCards(); updateViews(); $(".home-list-view-button").click(function () { $(".home-list-sorting-buttons").css("display", "flex"); // Switching view classes $(".home-block-view") .removeClass("home-block-view") .addClass("home-list-view"); // Additional class switch $(".home-chronicle-block") .removeClass("home-chronicle-block") .addClass("home-chronicle-list"); // Toggling visibility of buttons $( ".home-chronicle-block-button, .home-random-block-button, .home-alphabetical-block-button" ).hide(); $( ".home-chronicle-list-button, .home-random-list-button, .home-alphabetical-list-button" ).show(); updateLastVisibleCard(); updateWidthBlockView(); processEventCards(); updateViews(); // Remove active class from block view button and add to list view button $(".home-block-view-button").removeClass("active-view-button"); $(".home-list-view-button").addClass("active-view-button"); // Conditional checks for transferring the active class from block to list buttons if ($(".home-chronicle-block-button").hasClass("active-view-button")) { $(".home-chronicle-block-button").removeClass("active-view-button"); $(".home-chronicle-list-button").addClass("active-view-button"); } else if ( $(".home-random-block-button").hasClass("active-view-button") ) { $(".home-random-block-button").removeClass("active-view-button"); $(".home-random-list-button").addClass("active-view-button"); } else if ( $(".home-alphabetical-block-button").hasClass("active-view-button") ) { $(".home-alphabetical-block-button").removeClass("active-view-button"); $(".home-alphabetical-list-button").addClass("active-view-button"); } }); $(".home-block-view-button").click(function () { console.log("Block view button clicked."); $(".home-list-sorting-buttons").hide(); // Hide list sorting buttons $(".home-list-view") .removeClass("home-list-view") .addClass("home-block-view"); $(".home-chronicle-list") .removeClass("home-chronicle-list") .addClass("home-chronicle-block"); $( ".home-chronicle-block-button, .home-random-block-button, .home-alphabetical-block-button" ).show(); $( ".home-chronicle-list-button, .home-random-list-button, .home-alphabetical-list-button" ).hide(); updateLastVisibleCard(); updateWidthBlockView(); processEventCards(); updateViews(); $(".home-list-view-button").removeClass("active-view-button"); $(".home-block-view-button").addClass("active-view-button"); // Conditional checks for transferring the active class from block to list buttons if ($(".home-chronicle-list-button").hasClass("active-view-button")) { $(".home-chronicle-list-button").removeClass("active-view-button"); $(".home-chronicle-block-button").addClass("active-view-button"); } else if ($(".home-random-list-button").hasClass("active-view-button")) { $(".home-random-list-button").removeClass("active-view-button"); $(".home-random-block-button").addClass("active-view-button"); } else if ( $(".home-alphabetical-list-button").hasClass("active-view-button") ) { $(".home-alphabetical-list-button").removeClass("active-view-button"); $(".home-alphabetical-block-button").addClass("active-view-button"); } }); // BLOCK VIEW SORTING BUTTONS $(".home-chronicle-block-button").click(function () { sortChronologically(); updateLastVisibleCard(); updateWidthBlockView(); processEventCards(); updateViews(); $(".home-chronicle-block-button").addClass("active-view-button"); $(".home-random-block-button").removeClass("active-view-button"); $(".home-alphabetical-block-button").removeClass("active-view-button"); }); $(".home-random-block-button").click(function () { randomizeCards(".list-container .card"); updateLastVisibleCard(); updateWidthBlockView(); processEventCards(); updateViews(); $(".home-random-block-button").addClass("active-view-button"); $(".home-chronicle-block-button").removeClass("active-view-button"); $(".home-alphabetical-block-button").removeClass("active-view-button"); }); $(".home-alphabetical-block-button").click(function () { sortAlphabetically(".list-container .card"); updateLastVisibleCard(); updateWidthBlockView(); processEventCards(); updateViews(); $(".home-alphabetical-block-button").addClass("active-view-button"); $(".home-chronicle-block-button").removeClass("active-view-button"); $(".home-random-block-button").removeClass("active-view-button"); }); // LIST VIEW SORTING BUTTONS $(".home-chronicle-list-button").click(function () { sortChronologically(); updateLastVisibleCard(); updateWidthBlockView(); processEventCards(); updateViews(); $(".home-chronicle-list-button").addClass("active-view-button"); $(".home-random-list-button").removeClass("active-view-button"); $(".home-alphabetical-list-button").removeClass("active-view-button"); }); $(".home-random-list-button").click(function () { randomizeCards(".list-container .card"); updateLastVisibleCard(); updateWidthBlockView(); processEventCards(); updateViews(); $(".home-random-list-button").addClass("active-view-button"); $(".home-chronicle-list-button").removeClass("active-view-button"); $(".home-alphabetical-list-button").removeClass("active-view-button"); }); $(".home-alphabetical-list-button").click(function () { sortAlphabetically(".list-container .card"); updateLastVisibleCard(); updateWidthBlockView(); processEventCards(); updateViews(); $(".home-alphabetical-list-button").addClass("active-view-button"); $(".home-chronicle-list-button").removeClass("active-view-button"); $(".home-random-list-button").removeClass("active-view-button"); }); } else { console.log("NOT HOMEPAGE"); $(".home-list-view").show(); $(".chronicle-list-button, .list-view-button").addClass( "active-view-button" ); // Initialization and Default Settings // Initially hide block view sorting buttons and set the default sorted view for list $( ".chronicle-block-button, .random-block-button, .alphabetical-block-button" ).hide(); sortChronologically(); // Sort the block view chronologically by default updateLastVisibleCard(); updateWidthBlockView(); processEventCards(); updateViews(); $(".list-view-button").click(function () { console.log("List view button clicked."); $(".list-sorting-buttons").css("display", "flex"); $(".block-sorting-buttons").hide(); // Switching view classes $(".home-block-view") .removeClass("home-block-view") .addClass("home-list-view"); // Additional class switch $(".home-chronicle-block") .removeClass("home-chronicle-block") .addClass("home-chronicle-list"); // Toggling visibility of buttons $( ".chronicle-block-button, .random-block-button, .alphabetical-block-button" ).hide(); $( ".chronicle-list-button, .random-list-button, .alphabetical-list-button" ).show(); updateLastVisibleCard(); updateWidthBlockView(); processEventCards(); updateViews(); // Remove active class from block view button and add to list view button $(".block-view-button").removeClass("active-view-button"); $(".list-view-button").addClass("active-view-button"); // Conditional checks for transferring the active class from block to list buttons if ($(".chronicle-block-button").hasClass("active-view-button")) { $(".chronicle-block-button").removeClass("active-view-button"); $(".chronicle-list-button").addClass("active-view-button"); } else if ($(".random-block-button").hasClass("active-view-button")) { $(".random-block-button").removeClass("active-view-button"); $(".random-list-button").addClass("active-view-button"); } else if ( $(".alphabetical-block-button").hasClass("active-view-button") ) { $(".alphabetical-block-button").removeClass("active-view-button"); $(".alphabetical-list-button").addClass("active-view-button"); } }); $(".block-view-button").click(function () { console.log("Block view button clicked."); $(".list-sorting-buttons").hide(); // Hide list sorting buttons $(".block-sorting-buttons").css("display", "flex"); $(".home-list-view") .removeClass("home-list-view") .addClass("home-block-view"); $(".home-chronicle-list") .removeClass("home-chronicle-list") .addClass("home-chronicle-block"); $( ".chronicle-block-button, .random-block-button, .alphabetical-block-button" ).show(); $( ".chronicle-list-button, .random-list-button, .alphabetical-list-button" ).hide(); updateLastVisibleCard(); updateWidthBlockView(); processEventCards(); updateViews(); $(".list-view-button").removeClass("active-view-button"); $(".block-view-button").addClass("active-view-button"); // Conditional checks for transferring the active class from block to list buttons if ($(".chronicle-list-button").hasClass("active-view-button")) { $(".chronicle-list-button").removeClass("active-view-button"); $(".chronicle-block-button").addClass("active-view-button"); } else if ($(".random-list-button").hasClass("active-view-button")) { $(".random-list-button").removeClass("active-view-button"); $(".random-block-button").addClass("active-view-button"); } else if ( $(".alphabetical-list-button").hasClass("active-view-button") ) { $(".alphabetical-list-button").removeClass("active-view-button"); $(".alphabetical-block-button").addClass("active-view-button"); } }); // BLOCK VIEW SORTING BUTTONS $(".chronicle-block-button").click(function () { sortChronologically(); updateLastVisibleCard(); updateWidthBlockView(); processEventCards(); updateViews(); $(".chronicle-block-button").addClass("active-view-button"); $(".random-block-button").removeClass("active-view-button"); $(".alphabetical-block-button").removeClass("active-view-button"); }); $(".random-block-button").click(function () { randomizeCards(".list-container .card"); updateLastVisibleCard(); updateWidthBlockView(); processEventCards(); updateViews(); $(".random-block-button").addClass("active-view-button"); $(".chronicle-block-button").removeClass("active-view-button"); $(".alphabetical-block-button").removeClass("active-view-button"); }); $(".alphabetical-block-button").click(function () { sortAlphabetically(".list-container .card"); updateLastVisibleCard(); updateWidthBlockView(); processEventCards(); updateViews(); $(".alphabetical-block-button").addClass("active-view-button"); $(".chronicle-block-button").removeClass("active-view-button"); $(".random-block-button").removeClass("active-view-button"); }); // LIST VIEW SORTING BUTTONS $(".chronicle-list-button").click(function () { sortChronologically(); updateLastVisibleCard(); updateWidthBlockView(); processEventCards(); updateViews(); $(".chronicle-list-button").addClass("active-view-button"); $(".random-list-button").removeClass("active-view-button"); $(".alphabetical-list-button").removeClass("active-view-button"); }); $(".random-list-button").click(function () { randomizeCards(".list-container .card"); updateLastVisibleCard(); updateWidthBlockView(); processEventCards(); updateViews(); $(".random-list-button").addClass("active-view-button"); $(".chronicle-list-button").removeClass("active-view-button"); $(".alphabetical-list-button").removeClass("active-view-button"); }); $(".alphabetical-list-button").click(function () { sortAlphabetically(".list-container .card"); updateLastVisibleCard(); updateWidthBlockView(); processEventCards(); updateViews(); $(".alphabetical-list-button").addClass("active-view-button"); $(".chronicle-list-button").removeClass("active-view-button"); $(".random-list-button").removeClass("active-view-button"); }); } // FILTERS --------------------- SECTION // // General Filters Toggle Button $(".general-toggle").click(function () { var filtersDiv = $("#filters"); var resetButton = $(".reset-button"); filtersDiv.toggleClass("is-visible"); if (filtersDiv.hasClass("is-visible")) { filtersDiv.css("display", "grid").hide().slideDown(100); $(this).text("[FILTER]"); // Attach click handler to document $(document).on("mousedown.hideFilters", function (event) { var isOutsideFilters = !filtersDiv.is(event.target) && filtersDiv.has(event.target).length === 0; var isOnToggle = $(event.target).closest(".general-toggle").length > 0; if (isOutsideFilters && !isOnToggle) { filtersDiv.removeClass("is-visible").slideUp(100, function () { $(this).css("display", "none"); }); $(".general-toggle").text("[FILTER]"); // Remove the document click handler $(document).off("mousedown.hideFilters"); } }); } else { filtersDiv.slideUp(100, function () { $(this).css("display", "none"); }); $(this).text("[FILTER]"); // Remove the document click handler $(document).off("mousedown.hideFilters"); } updateLastVisibleCard(); updateWidthBlockView(); processEventCards(); updateViews(); }); // Specific Toggle for Filter Sections like TYPE, ENTITY, etc. $(".open-filter").click(function (event) { event.stopPropagation(); var filterType = $(this).closest(".filter").data("filter"); 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(); if ($("#values-" + filterType).is(":visible")) { $(this).addClass("active-filter"); } else { $(this).removeClass("active-filter"); } // Pass the determined card selector to the function updateLastVisibleCard(cardSelector); updateWidthBlockView(cardSelector); processEventCards(cardSelector); updateViews(cardSelector); console.log("Updated views and borders after filter toggle"); }); function filterCards() { var displayCountsHtml = ""; var cardSelector = $(".card").length > 0 ? ".card" : ".community-card"; // Determine which card type is present $(".filter .values a[title]").each(function () { var anchor = $(this); var filterValue = anchor.attr("title").toLowerCase(); var count = 0; if (anchor.find("button").hasClass("active")) { $(cardSelector).each(function () { var card = $(this); $(".filter").each(function () { var filterType = $(this).data("filter"); var cardValue = card .find("." + filterType) .text() .toLowerCase(); if (cardValue.indexOf(filterValue) !== -1) { count++; } }); }); displayCountsHtml += "<span>[" + count + "] " + filterValue + "</span> "; } }); if (displayCountsHtml) { $(".count-filtered-cards").html(displayCountsHtml).show(); } else { $(".count-filtered-cards").hide(); } // Apply filtering and pass the determined card selector to the function applyFiltering(cardSelector); updateLastVisibleCard(cardSelector); updateWidthBlockView(cardSelector); processEventCards(cardSelector); updateViews(cardSelector); console.log("Filtering process complete, updated views and borders"); } function applyFiltering() { // Determine which card selector to use based on the elements present in the DOM var cardSelector = $(".card").length > 0 ? ".card" : ".community-card"; // Apply the logic to the determined card type $(cardSelector) .show() .each(function () { var card = $(this); var hideCard = false; $(".filter").each(function () { if (hideCard) return; var filterType = $(this).data("filter"); var activeFilters = $(this) .find(".values a[title] button.active") .map(function () { return $(this).parent("a").attr("title").toLowerCase(); }) .get(); if (activeFilters.length > 0) { var cardValue = card .find("." + filterType) .text() .toLowerCase(); var matchesFilter = activeFilters.some(function (filterValue) { return cardValue.indexOf(filterValue) !== -1; }); if (!matchesFilter) hideCard = true; } }); if (hideCard) card.hide(); }); } function updateLastVisibleCard() { // Target only the list view container for updating the last visible card $(".home-chronicle-list div.list-container div.card").removeClass( "last-visible" ); // Find the last visible card within the list view and add the class var lastVisibleCard = $( ".home-chronicle-list div.list-container div.card:visible:last" ); lastVisibleCard.addClass("last-visible"); } function updateWidthBlockView() { // Target only the block view container for updating the with of card $(".home-chronicle-block div.list-container").css("width", "100%"); $(".home-chronicle-block div.list-container div.card").css( "width", "calc(20% - 0px)" ); $( ".home-chronicle-block div.list-container div.card:nth-child(5n + 1)" ).css("width", "calc(20% + 4px)"); } // Reset function to remove active filters $(".reset-filter").click(function (event) { event.stopPropagation(); // Prevent event bubbling // Remove 'active' class from all filter buttons $("#filters .values button").removeClass("active"); $(".open-filter").removeClass("active-filter"); // Reset and hide the filter counts $(".count-filtered-cards").text("").hide(); filterCards(); // Reapply filtering based on the updated active buttons // Update other UI elements as needed, excluding the general toggle button updateLastVisibleCard(); updateWidthBlockView(); processEventCards(); updateViews(); }); $("#filters .values button").click(function () { console.log("Filter is clicked!!!"); $(this).toggleClass("active"); filterCards(); // Re-apply the filters based on the updated active buttons updateLastVisibleCard(); updateWidthBlockView(); processEventCards(); updateViews(); }); // Hide filters when window is scrolled $(window).on("scroll", function () { var filtersDiv = $("#filters"); if (filtersDiv.hasClass("is-visible")) { filtersDiv.removeClass("is-visible").slideUp(100, function () { $(this).css("display", "none"); // The filter reset code has been removed to keep the filters active }); $(".general-toggle").text("[FILTER]"); // Update the toggle button text } }); // MODAL ARTICLE --------------------- SECTION // // Format paragraphs function formatParagraphs(text) { var paragraphs = text.split("\n").filter(function (p) { return p.trim() !== ""; }); return paragraphs .map(function (p) { return "<p>" + p.trim() + "</p>"; }) .join(""); } var images = []; // Initialize an empty array to store the images // Find all image containers within the article content and extract the necessary information $(".article-images .image-container").each(function () { var img = $(this).find("img"); var captionDiv = $(this).find('div[class^="caption-image"]'); var image = { src: img.attr("src"), alt: img.attr("alt"), caption: captionDiv.text(), captionClass: captionDiv.attr("class"), }; images.push(image); // Add the image object to the images array }); if (images.length > 0) { setupImageToggle(images); // Call the setupImageToggle function with the images array updateImageLabel(1, images.length); // Set the label for the first image immediately } function setupImageToggle(images) { var currentIndex = 0; var enableNavigation = images.length > 1; // Enable navigation only if there is more than one image function showImage(index) { currentIndex = index; var image = images[currentIndex]; updateImageLabel(currentIndex + 1, images.length); $("#article-content") .find(".article-images") .html( getImageHtml(image, currentIndex, images.length, enableNavigation) ); } // Attach click handlers only if navigation is enabled if (enableNavigation) { $("#article-content").on("click", ".next-arrow", function () { showImage((currentIndex + 1) % images.length); }); $("#article-content").on("click", ".prev-arrow", function () { showImage((currentIndex - 1 + images.length) % images.length); }); } // Display the first image showImage(currentIndex); } function getImageHtml(image, currentIndex, totalImages, enableNavigation) { var imageLabel = currentIndex + 1 + "/" + totalImages + " IMAGES"; // Render navigation arrows based on the enableNavigation flag var navigationHtml = enableNavigation ? '<div class="prev-arrow"><</div><div class="next-arrow">></div>' : ""; return ( '<div class="image-navigation">' + '<p class="article-label-image">' + imageLabel + "</p>" + '<div class="image-container">' + '<div class="arrows-and-image">' + navigationHtml + '<img src="' + image.src + '" alt="' + image.alt + '">' + "</div>" + '<div class="' + image.captionClass + '">' + image.caption + "</div>" + "</div>" + "</div>" ); } function updateImageLabel(currentIndex, totalImages) { var imageLabel = currentIndex + "/" + totalImages + " IMAGES"; $("#article-content .article-label-image").text(imageLabel); } $(".caption-image1").each(function () { // Split the caption at each <br> tag and wrap each line in a span var htmlContent = $(this).html(); var lines = htmlContent.split("<br>"); var wrappedLines = lines.map(function (line) { return '<span class="caption-line">' + line + "</span>"; }); var newHtml = wrappedLines.join("<br>"); $(this).html(newHtml); }); function setShowArticleRotationEffect() { const offset = 20; const showArticle = document.querySelector("#show-article"); const h = showArticle.clientHeight; const theta = -Math.atan(offset / h); const a = Math.cos(theta); const b = Math.sin(theta); const c = -Math.sin(theta); const d = Math.cos(theta); const showArticleBefore = document.querySelector("#show-article-before"); const transformValue = "matrix(" + a + "," + b + "," + c + "," + d + ",0,0)"; showArticleBefore.style.transform = transformValue; } function openEvent(element, event) { event.stopPropagation(); event.preventDefault(); var url = $(element).find(".link a").attr("href"); if (url) { window.open(url, "_blank").focus(); } } function openModal(cardElement, event) { event.stopPropagation(); var pageTitle = $(cardElement).data("page") || null; // e.g. "090" window.currentEntryTitle = pageTitle; var isRelatedArticle = $(cardElement).hasClass("related-article"); showArticleWrapper.css("display", "block"); // Clear existing content in modal $("#article-title").empty(); $("#article-content").empty(); if (isRelatedArticle) { // Handle card elements (existing logic) var cardImages = []; for (var i = 1; i <= 5; i++) { var imageClass = ".related-article-image" + i; var captionClass = ".related-article-caption-image" + i; var imageElem = $(cardElement).find(imageClass + " img"); if (imageElem.length) { var captionText = $(cardElement) .find(imageClass + " " + captionClass) .text(); cardImages.push({ link: $(cardElement) .find(imageClass + " a") .attr("href"), src: imageElem.attr("src"), alt: imageElem.attr("alt"), caption: captionText, captionClass: "related-article-caption-image" + i, }); } } if (cardImages.length > 1) { setupImageToggle(cardImages); } // Handle related-article elements var entryNumber = $(cardElement) .find(".related-article-entry-number") .text(); var peopleHtml = $(cardElement).find(".related-article-people").html(); var title = $(cardElement).find(".related-article-title").text(); var typeHtml = $(cardElement).find(".related-article-type").html(); var externalPdfURL = $(cardElement) .find(".related-article-pdf a") .attr("href"); var externalLinkURL = $(cardElement) .find(".related-article-link a") .attr("href"); var entity = $(cardElement).find(".related-article-entity").text(); var discipline = $(cardElement) .find(".related-article-discipline") .text(); var subjectHtml = $(cardElement).find(".related-article-subject").html(); var description = $(cardElement) .find(".related-article-description") .html(); var reflection = $(cardElement) .find(".related-article-reflection") .html(); var quote = $(cardElement).find(".related-article-quote").text(); var modificationDate = $(cardElement) .find(".related-article-modification-date") .text(); // Update modal content for related-article $("#article-title").html( '<p class="article-entry-number">' + entryNumber + '</p><p class="article-people">' + peopleHtml + "</p>" ); var articleContentHtml = '<div class="article-title-link">'; articleContentHtml += '<p class="article-title">' + title + "</p>"; // Create a div that will wrap the links articleContentHtml += '<div class="link-pdf">'; if (externalPdfURL) { articleContentHtml += '<a href="' + externalPdfURL + '" target="_blank" class="pdf-link-icon">[PDF<span class="text-symbol">⤴</span>]</a>'; } if (externalLinkURL) { articleContentHtml += '<a href="' + externalLinkURL + '" target="_blank" class="external-link-icon">[WEB<span class="text-symbol">⤴</span>]</a>'; } // Close the .link-pdf div articleContentHtml += "</div>"; articleContentHtml += "</div>"; // Close the container div // Append type, entity, discipline, and subject details articleContentHtml += '<p class="article-type">' + typeHtml + "</p>" + '<div class="article-metadata">' + '<div class="article-metadata-column">' + '<p class="article-metadata-label">Entity</p>' + '<p class="article-metadata-value">' + entity + "</p>" + "</div>" + '<div class="article-metadata-column">' + '<p class="article-metadata-label">Discipline</p>' + '<p class="article-metadata-value">' + discipline + "</p>" + "</div>" + '<div class="article-metadata-column">' + '<p class="article-metadata-label">Subject(s)</p>' + '<p class="article-metadata-value">' + subjectHtml + "</p>" + "</div>" + "</div>"; // Add images if any if (cardImages.length > 0) { var initialImage = cardImages[0]; // Use the first image initially var enableNavigation = cardImages.length > 1; // Enable navigation only if more than one image articleContentHtml += '<div class="article-images">' + getImageHtml(initialImage, 0, cardImages.length, enableNavigation) + "</div>"; } // Add non-image content (description, reflection, etc.) articleContentHtml += (description ? '<p class="article-label-description">Description</p>' + '<div class="article-description">' + formatParagraphs(description) + "</div>" : "") + (reflection ? '<p class="article-label-reflection">Reflection</p>' + '<div class="article-reflection">' + formatParagraphs(reflection) + "</div>" : "") + (quote ? '<p class="article-label-quote">Quote</p>' + '<p class="article-quote">' + quote + "</p>" : "") + '<p class="article-label-modification-date">Added on</p>' + '<div class="article-modification-date">' + modificationDate + "</div>"; $("#article-content").html(articleContentHtml); } else { // Handle card elements (existing logic) var cardImages = []; for (var i = 1; i <= 5; i++) { var imageClass = ".image" + i; var captionClass = ".caption-image" + i; var imageElem = $(cardElement).find(imageClass + " img"); if (imageElem.length) { var captionText = $(cardElement) .find(imageClass + " " + captionClass) .text(); cardImages.push({ link: $(cardElement) .find(imageClass + " a") .attr("href"), src: imageElem.attr("src"), alt: imageElem.attr("alt"), caption: captionText, captionClass: "caption-image" + i, }); } } if (cardImages.length > 1) { setupImageToggle(cardImages); } var entryNumber = $(cardElement).find(".entry-number").text(); var title = $(cardElement).find(".title").text(); var peopleHtml = $(cardElement).find(".people").html(); var typeHtml = $(cardElement).find(".type").html(); var externalPdfURL = $(cardElement).find(".pdf a").attr("href"); var externalLinkURL = $(cardElement).find(".link a").attr("href"); var entity = $(cardElement).find(".entity").text(); var discipline = $(cardElement).find(".discipline").text(); var subjectHtml = $(cardElement).find(".subject").html(); var description = $(cardElement).find(".description").html(); var reflection = $(cardElement).find(".reflection").html(); var quote = $(cardElement).find(".quote").text(); var externalReferenceHtml = $(cardElement) .find(".external-reference") .html(); var modificationDate = $(cardElement).find(".modification-date").text(); var relatedArticlesHtml = $(cardElement).find(".related-articles").html(); $("#article-title").html( '<p class="article-entry-number">' + entryNumber + '</p><p class="article-people">' + peopleHtml + "</p>" ); var articleContentHtml = '<div class="article-title-link">'; articleContentHtml += '<p class="article-title">' + title + "</p>"; // Create a div that will wrap the links articleContentHtml += '<div class="link-pdf">'; if (externalPdfURL) { articleContentHtml += '<a href="' + externalPdfURL + '" target="_blank" class="pdf-link-icon">[PDF<span class="text-symbol">⤴</span>]</a>'; } if (externalLinkURL) { articleContentHtml += '<a href="' + externalLinkURL + '" target="_blank" class="external-link-icon">[WEB<span class="text-symbol">⤴</span>]</a>'; } // Close the .link-pdf div articleContentHtml += "</div>"; articleContentHtml += "</div>"; // Close the new div // Append type, entity, discipline, and subject details articleContentHtml += '<p class="article-type">' + typeHtml + "</p>" + '<div class="article-metadata">' + '<div class="article-metadata-column">' + '<p class="article-metadata-label">Entity</p>' + '<p class="article-metadata-value">' + entity + "</p>" + "</div>" + '<div class="article-metadata-column">' + '<p class="article-metadata-label">Discipline</p>' + '<p class="article-metadata-value">' + discipline + "</p>" + "</div>" + '<div class="article-metadata-column">' + '<p class="article-metadata-label">Subject(s)</p>' + '<p class="article-metadata-value">' + subjectHtml + "</p>" + "</div>" + "</div>"; // Add images if any if (cardImages.length > 0) { var initialImage = cardImages[0]; // Use the first image initially var enableNavigation = cardImages.length > 1; // Enable navigation only if more than one image articleContentHtml += '<div class="article-images">' + getImageHtml(initialImage, 0, cardImages.length, enableNavigation) + "</div>"; } // Add non-image content (description, reflection, etc.) articleContentHtml += (description ? '<p class="article-label-description">Description</p>' + '<div class="article-description">' + formatParagraphs(description) + "</div>" : "") + (reflection ? '<p class="article-label-reflection">Reflection</p>' + '<div class="article-reflection">' + formatParagraphs(reflection) + "</div>" : "") + (externalReferenceHtml ? '<p class="article-label-external-reference">References</p>' + '<p class="article-external-reference">' + externalReferenceHtml + "</p>" : "") + (quote ? '<p class="article-label-quote">Quote</p>' + '<p class="article-quote">' + quote + "</p>" : "") + '<p class="article-label-modification-date">Added on</p>' + '<div class="article-modification-date">' + modificationDate + "</div>"; $("#article-content").html(articleContentHtml); $("#related-articles").html(relatedArticlesHtml); if (relatedArticlesHtml && relatedArticlesHtml.trim().length > 0) { $("#related-articles") .html( '<div class="related-articles-label">Related Articles</div><div class="related-articles-container">' + relatedArticlesHtml + "</div>" ) .show(); } } // Check which view is active and set the width accordingly if ($(".home-chronicle-list").is(":visible")) { $(".home-list-view").each(function () { var currentWidth = $(this).width(); // Get the current width $(this).data("originalWidth", currentWidth); // Store the original width $(this).css("width", "calc(60% - 2px)"); }); // Modify the .type elements within .home-chronicle-list $(".home-chronicle-list .type").each(function () { var currentLeft = $(this).css("left"); // Get the current left value $(this).data("originalLeft", currentLeft); // Store the original left value $(this).css("left", "85%"); }); } else if ($(".home-chronicle-block").is(":visible")) { $(".home-chronicle-block div.list-container").each(function () { var currentWidth = $(this).width(); // Get the current width $(this).css("width", "calc(60% - 0px)"); // Set the new width as 30% of the current width }); $(".home-chronicle-block div.list-container div.card").each(function () { var currentWidth = $(this).width(); // Get the current width $(this).css("width", "calc(33.333% - 0px)"); // Set the new width as 30% of the current width }); } // Apply the fade-out effect to both #list and #list-list elements $(".list-container").addClass("fade-out"); } // closeModal function function closeModal() { if ($(".home-chronicle-list").is(":visible")) { $(".home-list-view").css("width", "100%"); $(".home-chronicle-list div.list-container div.card div.type").css( "left", "90%" ); } else if ($(".home-chronicle-block").is(":visible")) { updateWidthBlockView(); } showArticleWrapper.hide(); } $(".card").on("click", function (event) { // Check if the click event is originating from a link within .people or .type, or any other specific area if ($(event.target).closest(".people a, .type a").length) { // The click is inside a link; let the default behavior proceed without opening the modal return; } // Prevent further event handling if the card has the 'event' class if ($(this).hasClass("event")) { event.stopImmediatePropagation(); openEvent(this, event); $(".list-container").removeClass("fade-out"); closeModal(); } else { // Handle cards without the 'event' class openModal(this, event); setShowArticleRotationEffect(); } }); $("#show-article-wrapper").on("click", ".related-article", function (event) { openModal(this, event); // Call openModal when a related-article is clicked setShowArticleRotationEffect(); }); // --- PRINT HELPERS --- // Warm the font cache in the *parent* document. function preloadFontForPrint() { var link = document.createElement("link"); link.rel = "preload"; link.as = "font"; link.type = "font/woff2"; link.href = "/fonts/HALColant-TextRegular.woff2?v=20250820"; link.crossOrigin = "anonymous"; document.head.appendChild(link); // keep it; no need to remove } // Simple cache buster function cacheBust(url) { return url + (url.indexOf("?") > -1 ? "&" : "?") + "_=" + Date.now(); } // Kill any previous print handlers $(document).off("click.print", "#print-button"); $(document).on("click.print", "#print-button", function () { // debounce to avoid double/racing prints var $btn = $("#print-button"); if ($btn.data("busy")) return; $btn.data("busy", true); preloadFontForPrint(); var title = window.currentEntryTitle; // e.g. "090" if (!title) { console.warn("[print] no currentEntryTitle"); window.print(); $btn.data("busy", false); return; } var pageUrl = mw.util.getUrl(title); var pageUrlFresh = cacheBust(pageUrl); console.log("[print] fetching page HTML:", pageUrlFresh); $.get(pageUrlFresh) .done(function (html) { var $tmp = $("<div>").html(html); var $print = $tmp.find(".print-only").first(); console.log("[print] .print-only found:", $print.length); if (!$print.length) { console.warn("[print] no .print-only found; fallback print"); window.print(); $btn.data("busy", false); return; } // Build hidden 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); 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(); // Ensure relative URLs (fonts/images) resolve inside iframe var base = doc.createElement("base"); base.href = location.origin + "/"; doc.head.appendChild(base); // Inject PRINT CSS (cache-busted) and await it var printCssUrl = "/index.php?title=MediaWiki:Print.css&action=raw&ctype=text/css"; var printCssFresh = cacheBust(printCssUrl); var linkCss = doc.createElement("link"); linkCss.rel = "stylesheet"; linkCss.href = printCssFresh; var cssLoaded = new Promise(function (resolve) { linkCss.onload = function () { resolve(); }; linkCss.onerror = function () { console.warn("[print] CSS failed to load"); resolve(); }; // don't block }); // Preload the font *inside* iframe (Chrome becomes much happier) 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 the printable HTML doc.body.innerHTML = $print.prop("outerHTML"); // Remove “empty” optional sections so they don’t leave gaps in print. // --- CLEAN: remove empty paragraphs/whitespace nodes that create phantom gaps (function () { // 1) Drop <p> that are visually empty or only /whitespace var ps = doc.querySelectorAll("#article-content p"); 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 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); } }); } })(); (function () { var css = "@media print{" + // Paragraphs inside rich text " .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;}" + // Ruled sections: one consistent bottom padding for the hairline " .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"); style.type = "text/css"; style.appendChild(doc.createTextNode(css)); doc.head.appendChild(style); })(); // --- PDF-friendly links for Chrome on macOS --- // 1) Add a tiny print-only CSS override to keep anchors as one box. 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 () { // 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) { 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"; } } // Ensure single-box anchors a.style.whiteSpace = "nowrap"; a.style.wordBreak = "normal"; a.style.overflowWrap = "normal"; }); // Icon links ([PDF⤴] [WEB⤴]) are short; still enforce single-box doc.querySelectorAll(".link-pdf a[href]").forEach(function (a) { a.style.whiteSpace = "nowrap"; a.style.wordBreak = "normal"; a.style.overflowWrap = "normal"; }); })(); // 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]); } // **Load the specific face** so Chrome actually uses it 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 () { try { // build the desired PDF 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"; // save originals (scoped here) var oldIframeTitle = doc.title; var oldParentTitle = document.title; // define onafterprint AFTER we have originals so it can close over them iframe.contentWindow.onafterprint = function () { try { doc.title = oldIframeTitle; // restore iframe doc title document.title = oldParentTitle; // restore parent title } catch (e) {} 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 doc.title = desiredTitle; // iframe document // (next line is optional/redundant; doc === iframe.contentWindow.document) // iframe.contentWindow.document.title = desiredTitle; document.title = desiredTitle; // parent (helps on some setups) // print iframe.contentWindow.focus(); iframe.contentWindow.print(); // fallback cleanup in case onafterprint doesn’t fire setTimeout(function () { try { doc.title = oldIframeTitle; document.title = oldParentTitle; } catch (e) {} if (iframe.parentNode) iframe.parentNode.removeChild(iframe); $btn.data("busy", false); }, 1000); } catch (err) { console.warn("[print] failed before print:", err); $btn.data("busy", false); } }); }) .fail(function (xhr) { console.warn( "[print] fetch failed:", xhr && xhr.status, xhr && xhr.statusText ); window.print(); $("#print-button").data("busy", false); }); }); // Close modal with Close button $("#close-button").on("click", function () { $(".list-container").removeClass("fade-out"); closeModal(); }); // Close modal and remove fade out also when clicking outside of card $(document).on("mousedown", function (event) { var isOutsideWrapper = !showArticleWrapper.is(event.target) && showArticleWrapper.has(event.target).length === 0; var isOnCard = $(event.target).closest(".card, .list-card").length > 0; if (!areFiltersActive) { if (isOutsideWrapper && !isOnCard) { $(".list-container").removeClass("fade-out"); showArticleWrapper.css("display", "none"); closeModal(); // Use closeModal() for cleanup } } }); // Hover effect for scrolling $("#show-article-wrapper").hover( function () { // On hover, enable scrolling on #show-article-wrapper $(this).css("overflow-y", "auto"); $(this).css("overflow-x", "hidden"); }, function () { // On hover out, disable scrolling on #show-article-wrapper $(this).css("overflow-y", "hidden"); $(this).css("overflow-x", "hidden"); } ); // Format community card, when in the Community Entries page if ($(".community-card").length) { formatCommunityCardDescriptions(); } function formatCommunityCardDescriptions() { $(".community-card").each(function () { // Format paragraphs in community-description var descriptionContainer = $(this).find(".community-description"); var rawDescription = descriptionContainer.text(); var formattedDescription = formatParagraphs(rawDescription); descriptionContainer.html(formattedDescription); // Remove empty elements in the entire card $(this) .find("*") .each(function () { if ($(this).is(":empty") || $(this).html().trim() === "<br>") { $(this).remove(); } }); }); } if ($("#show-article-wrapper-entry").length) { // Your existing formatParagraphs function function formatParagraphs(text) { var paragraphs = text.split("\n").filter(function (p) { return p.trim() !== ""; }); return paragraphs .map(function (p) { return "<p>" + p.trim() + "</p>"; }) .join(""); } // Check if ".article-description" exists and format its text if ($(".article-description").length) { var descriptionText = $(".article-description").text(); var formattedDescription = formatParagraphs(descriptionText); $(".article-description").html(formattedDescription); // Set the formatted text } // 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 } } // SEARCH --------------------- SECTION // // Check if div with class "mw-search-results-info" exists if ($(".mw-search-results-info").length) { // Select the child <p> element and check its content var $paragraph = $(".mw-search-results-info > p"); var currentText = $paragraph.text().trim(); // Check if the current text is not "There were no results matching the query." if (currentText !== "There were no results matching the query.") { // Overwrite the content with "Search results" $paragraph.text("Pages related to your Search"); } } // Object to store encountered titles var encounteredTitles = {}; // Iterate over each search result $(".mw-search-result-heading").each(function () { // Get the title of the current search result var title = $(this).find("a").attr("title"); // Check if the title has already been encountered if (encounteredTitles[title]) { // Hide the duplicate search result $(this).hide(); } else { // Mark the title as encountered encounteredTitles[title] = true; } }); // Remove unwanted white spaces between lines $(".mw-search-results-container") .contents() .filter(function () { return this.nodeType === 3; // Filter text nodes }) .remove(); // Edits regarding Search Results // Define the new form HTML as a string var newFormHtml = '<form action="/index.php" id="searchform">' + '<div id="simpleSearchSpecial" class="right-inner-addon">' + "<span>[ Search ]</span>" + '<input class="form-control" name="search" placeholder="" title="Search [alt-shift-f]" accesskey="f" id="searchInput" tabindex="1" autocomplete="off" type="search">' + '<span class="closing-bracket">]</span>' + '<input value="Special:Search" name="title" type="hidden">' + "</div>" + "</form>"; // Replace the div with id="searchText" with the new form $("#searchText").replaceWith(newFormHtml); // Target the button based on its complex class structure $(".oo-ui-actionFieldLayout-button .oo-ui-buttonInputWidget").remove(); // Check if #submit button exists and add event listener if it does var submitButton = document.querySelector("#submit"); if (submitButton) { // Add click event listener submitButton.addEventListener("click", function (event) { event.preventDefault(); // Prevent the default link behavior var email = "submit@softwear.directory"; var subject = "new entry to the softwear directory"; var body = "☺ the following content could be interesting for the directory:\n\n" + "[ author / creator ]\n\n" + "---\n\n" + "[ title ]\n\n" + "---\n\n" + "[ why should it be included? ]\n\n" + "---\n\n" + "[ link or pdf ]\n\n" + "---\n\n" + "[ your name / contact / social ]\n\n" + "---"; var mailtoLink = "mailto:" + encodeURIComponent(email) + "?subject=" + encodeURIComponent(subject) + "&body=" + encodeURIComponent(body).replace(/%20/g, " "); window.location.href = mailtoLink; }); } // Tooltip for "wander elsewhere..." on .card.event var tooltip = $( '<div class="tooltip-popup">wander elsewhere...</div>' ).appendTo("body"); $(".card.event").on("mouseenter", function () { tooltip.css("opacity", 1); }); $(".card.event").on("mousemove", function (e) { var offsetX = 10; // right of cursor var offsetY = -30; // above cursor tooltip.css({ left: e.clientX + offsetX + "px", top: e.clientY + offsetY + "px", }); }); $(".card.event").on("mouseleave", function () { tooltip.css("opacity", 0); }); mw.loader.using("mediawiki.api", function () { // Only run on form edit page if (mw.config.get("wgCanonicalSpecialPageName") === "FormEdit") { new mw.Api() .post({ action: "purge", titles: "Main", }) .fail(function (err) { // Optional: leave a minimal fallback error log console.warn("Main page purge failed", err); }); } }); });