1
0
weblog/static/js/main.js
JackCarterSmith 64f91ff50b
All checks were successful
Build Jekyll weblog / build (push) Successful in 2s
Initial commit
2023-09-02 18:55:02 +02:00

244 lines
8.5 KiB
JavaScript

/*
* Common scripts used by the whole website.
* - Performs setup of a few dynamic elements.
* - Loads deferred images.
* - DOM processing starts on jQuery.ready().
*
* Dependencies:
* jQuery
* BootstrapJS
*
*/
"use strict";
function myImgOnLoad(jqImg, callback) {
// The callback gets fired once the image loads, even
// after a dynamic reset of the 'src' attribute.
jqImg.one("load", function() {
callback();
});
}
function myImgLoadDeferred(jqImg) {
// Slides and thumbnails set the 'defpath' attribute
// with the image path, so that we can delay-load them.
jqImg.attr("src", jqImg.attr("defpath"));
jqImg.attr("defpath", null);
}
function myImgIsLoaded(jqImg) {
// Initially, the image should have no 'src', to avoid
// sending invalid requests to the server. We also clear
// the custom 'defpath' property to be sure once it is loaded.
return jqImg.attr("src") != null &&
jqImg.attr("defpath") == null;
}
function setUpCategoryDisplay() {
var imgFadeInDelay = 600;
// Show "All" and load the deferred images for the visible thumbnails:
// ('img.defpath' property will have the actual path)
$(".post-list-body>div[post-cate!=All]").hide();
$(".post-list-body>div[post-cate=All] img").each(function(index, img) {
var jqImg = $(img);
if (!myImgIsLoaded(jqImg)) {
myImgOnLoad(jqImg, function() { jqImg.fadeIn(imgFadeInDelay); });
myImgLoadDeferred(jqImg);
}
});
// Show category when clicking the categories list:
$(".categories-list-item").click(function() {
var category = $(this).attr("cate");
$(".post-list-body>div[post-cate!='" + category + "']").hide(250);
$(".post-list-body>div[post-cate='" + category + "'] img").each(
function(index, img) {
var jqImg = $(img);
if (!myImgIsLoaded(jqImg)) {
myImgOnLoad(jqImg, function() { jqImg.fadeIn(imgFadeInDelay); });
myImgLoadDeferred(jqImg);
}
});
$(".post-list-body>div[post-cate='" + category + "']").show(400);
});
}
function setUpTableOfContents() {
// Properly displays the main post list or the post contents.
if (typeof $("#markdown-toc").html() === "undefined") {
$("#content").hide();
$("#my-article").removeClass("col-sm-9").addClass("col-sm-12");
} else {
$("#content .content-text").html("<ul>" + $("#markdown-toc").html() + "</ul>");
}
}
function setUpSlideShowContainers() {
/*
* Only loads the first image/slide. The following images are loaded
* when the user clicks the given slide button, if not yet loaded.
*
* The img tags in the HTML must follow this convention:
*
* <img defpath="path/to/image.ext">
*
* No 'src' is required.
*/
// Constants:
var slideFadeMilliseconds = 300;
var activeButtonClass = "slideshow-active-button";
var buttonGroupClass = "slideshow-buttons";
var slideShowClass = "slideshow-container";
var buttonTargetHref = "javascript:;";
// Find all slide-shows and iterate them:
$("." + slideShowClass).each(function(index, slideShowContainer) {
// Find all images inside the slideShowContainer, then load the first deferred image:
var childImages = $(slideShowContainer).find("img");
var jqImg = $(childImages[0]);
if (jqImg) {
// Show the image only when loading completes.
myImgOnLoad(jqImg, function() {
jqImg.fadeIn(slideFadeMilliseconds);
});
// Start the download.
myImgLoadDeferred(jqImg);
}
// If the container has a single image, don't add the slide buttons.
// Also shrink the base border to undo the "polaroid" look.
if (childImages.length == 1) {
jqImg.css("border-bottom-width", "10px");
return;
}
// Array to contain the buttons and their corresponding images.
var buttons = [];
// Record which button/image is currently active.
var current = 0;
// Build a div to contain the buttons:
var buttonsContainer = $("<div>").addClass(buttonGroupClass);
// Iterate each child image to add the buttons:
childImages.each(function(index, img) {
// Build a new button. If it's the first button it should be active.
var button = $("<a>").prop("href", buttonTargetHref);
if (index == 0) {
button.addClass(activeButtonClass);
}
buttons.push({ "button": button, "image": img });
buttonsContainer.append(button);
button.click(function() {
if (current == index) {
return; // Clicking the currently active button. Do nothing.
}
function slideFade(jqImgIn, waitForLoading) {
// Fade out the old current image:
$(buttons[current].image).fadeOut(slideFadeMilliseconds,
function() {
// then fade in this slide/image:
if (waitForLoading) {
myImgOnLoad(jqImgIn, function() {
jqImgIn.fadeIn(slideFadeMilliseconds);
});
myImgLoadDeferred(jqImgIn);
} else {
jqImgIn.fadeIn(slideFadeMilliseconds);
}
});
$(buttons[current].button).removeClass(activeButtonClass);
$(buttons[index].button).addClass(activeButtonClass);
current = index; // Remember the active button.
}
// Load the image if not yet loaded. If it is loading for the first
// time, then defer the fade-in until loading completes.
var jqImg = $(img);
if (!myImgIsLoaded(jqImg)) {
slideFade(jqImg, /* waitForLoading = */ true); // Show the image only when loading completes.
} else {
slideFade(jqImg, /* waitForLoading = */ false); // Already loaded, fade-in/out now.
}
});
});
// Add the expanded slide-show to the page:
slideShowContainer.appendChild(buttonsContainer[0]);
});
}
function fixMissingImageCaptions() {
//
// Some post images don't have a caption.
// Now that I've added the "polaroid"-like bottom
// caption area, it looks better to make sure all
// images have this description text. Some don't
// have it, but they have a 'title' property, which
// can be repurposed as a caption.
//
$(".post img").each(function(index, img) {
var jqImg = $(img);
// If an <em> follows, it has a caption already.
var immediateSibling = jqImg.next();
if (immediateSibling && !$(immediateSibling).is("em")) {
// We won't add a caption to the profile picture
// and the StackExchange flair thingy. Slide-show
// images also don't get a caption.
var id = jqImg.attr("id");
var parentClass = jqImg.parent().attr("class");
if (!$(immediateSibling).html() &&
(id != "profile-pic") && (id != "stackexchange-flair") &&
(parentClass != "slideshow-img-list")) {
var title = jqImg.attr("title");
if (title) {
var imgCaption = $("<em>");
imgCaption.html(title);
jqImg.after(imgCaption);
} else {
// Looks like no title/caption is available, shrink back the bottom border.
jqImg.css("border-bottom-width", "10px");
jqImg.css("margin-bottom", "15px");
}
}
}
});
}
function addBlankTargetToExternalLinks() {
// This should make external links always open in a new browser tab.
$("a[href^='http']").each(function() {
$(this).attr("target", "_blank");
});
}
//
// Sets up the dynamic site elements
// when the pages finish loading.
//
$(document).ready(function() {
setUpCategoryDisplay();
setUpTableOfContents();
setUpSlideShowContainers();
fixMissingImageCaptions();
addBlankTargetToExternalLinks();
});