Nextcloud-Integration: Dynamische Galerie + Auto-Thumbnails

- Galerie laedt Bilder dynamisch per JavaScript statt Jekyll-Loop
- nginx autoindex liefert JSON-Bilderliste
- Nextcloud-Ordner (Websitebilder) wird als Volume gemountet
- ImageMagick generiert automatisch 512px Thumbnails
- Hintergrund-Check alle 60s fuer neue Bilder
- Neuer entrypoint.sh fuer Thumbnail-Generierung + nginx Start

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Kroonk
2026-04-12 14:03:43 +02:00
parent c885d22346
commit ab6b2bb8fe
7 changed files with 217 additions and 141 deletions

View File

@@ -3,14 +3,14 @@
html5up.net | @ajlkn
Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
Added EXIF data and enhanced for Jekyll by Ram Patra
Enhanced for dynamic gallery loading - Mischkomposition by Tom Misch
*/
(function ($) {
var $window = $(window),
$body = $("body"),
$wrapper = $("#wrapper");
// Breakpoints.
breakpoints({
xlarge: ["1281px", "1680px"],
@@ -19,13 +19,13 @@
small: ["481px", "736px"],
xsmall: [null, "480px"],
});
// Hack: Enable IE workarounds.
if (browser.name == "ie") $body.addClass("ie");
// Touch?
if (browser.mobile) $body.addClass("touch");
// Transitions supported?
if (browser.canUse("transition")) {
// Play initial animations on page load.
@@ -34,37 +34,37 @@
$body.removeClass("is-preload");
}, 100);
});
// Prevent transitions/animations on resize.
var resizeTimeout;
$window.on("resize", function () {
window.clearTimeout(resizeTimeout);
$body.addClass("is-resizing");
resizeTimeout = window.setTimeout(function () {
$body.removeClass("is-resizing");
}, 100);
});
}
// Scroll back to top.
$window.scrollTop(0);
// Panels.
var $panels = $(".panel");
$panels.each(function () {
var $this = $(this),
$toggles = $('[href="#' + $this.attr("id") + '"]'),
$closer = $('<div class="closer" />').appendTo($this);
// Closer.
$closer.on("click", function (event) {
$this.trigger("---hide");
});
// Events.
$this
.on("click", function (event) {
@@ -75,25 +75,17 @@
else $this.triggerHandler("---show");
})
.on("---show", function () {
// Hide other content.
if ($body.hasClass("content-active")) $panels.trigger("---hide");
// Activate content, toggles.
$this.addClass("active");
$toggles.addClass("active");
// Activate body.
$body.addClass("content-active");
})
.on("---hide", function () {
// Deactivate content, toggles.
$this.removeClass("active");
$toggles.removeClass("active");
// Deactivate body.
$body.removeClass("content-active");
});
// Toggles.
$toggles
.removeAttr("href")
@@ -101,154 +93,166 @@
.on("click", function (event) {
event.preventDefault();
event.stopPropagation();
$this.trigger("---toggle");
});
});
// Global events.
$body.on("click", function (event) {
if ($body.hasClass("content-active")) {
event.preventDefault();
event.stopPropagation();
$panels.trigger("---hide");
}
});
$window.on("keyup", function (event) {
if (event.keyCode == 27 && $body.hasClass("content-active")) {
event.preventDefault();
event.stopPropagation();
$panels.trigger("---hide");
}
});
// Header.
var $header = $("#header");
// Links.
$header.find("a").each(function () {
var $this = $(this),
href = $this.attr("href");
// Internal link? Skip.
if (!href || href.charAt(0) == "#") return;
// Redirect on click.
$this
.removeAttr("href")
.css("cursor", "pointer")
.on("click", function (event) {
event.preventDefault();
event.stopPropagation();
window.location.href = href;
});
});
// Footer.
var $footer = $("#footer");
// Copyright.
// This basically just moves the copyright line to the end of the *last* sibling of its current parent
// when the "medium" breakpoint activates, and moves it back when it deactivates.
$footer.find(".copyright").each(function () {
var $this = $(this),
$parent = $this.parent(),
$lastParent = $parent.parent().children().last();
breakpoints.on("<=medium", function () {
$this.appendTo($lastParent);
});
breakpoints.on(">medium", function () {
$this.appendTo($parent);
});
});
// Main.
// Main - Dynamische Galerie
var $main = $("#main"),
exifDatas = {};
// Thumbs.
$main.children(".thumb").each(function () {
var $this = $(this),
$image = $this.find(".image"),
$image_img = $image.children("img"),
x;
// No image? Bail.
if ($image.length == 0) return;
// Image.
// This sets the background of the "image" <span> to the image pointed to by its child
// <img> (which is then hidden). Gives us way more flexibility.
// Set background.
$image.css("background-image", "url(" + $image_img.attr("src") + ")");
// Set background position.
if ((x = $image_img.data("position"))) $image.css("background-position", x);
// Hide original img.
$image_img.hide();
// EXIF data
$image_img[0].addEventListener("load", function() {
EXIF.getData($image_img[0], function () {
exifDatas[$image_img.data('name')] = getExifDataMarkup(this);
var imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
function loadGallery() {
$.ajax({
url: '/images/fulls/',
dataType: 'json',
success: function(files) {
var images = files.filter(function(f) {
if (f.type !== 'file') return false;
var ext = f.name.split('.').pop().toLowerCase();
return imageExtensions.indexOf(ext) !== -1;
});
images.sort(function(a, b) {
return a.name.localeCompare(b.name);
});
images.forEach(function(file) {
var article = $('<article class="thumb"></article>');
var link = $('<a class="image" href="/images/fulls/' + encodeURIComponent(file.name) + '"></a>');
var img = $('<img src="/images/thumbs/' + encodeURIComponent(file.name) + '" alt="" data-name="' + file.name.replace(/"/g, '&quot;') + '" />');
link.append(img);
article.append(link);
$main.append(article);
});
initThumbs();
initPoptrox();
},
error: function() {
$main.html('<p style="text-align:center;color:#fff;padding:2em;">Keine Bilder gefunden. Lade Bilder in deinen Nextcloud-Ordner hoch.</p>');
}
});
});
});
// Poptrox.
$main.poptrox({
baseZIndex: 20000,
caption: function ($a) {
var $image_img = $a.children('img');
var data = exifDatas[$image_img.data('name')];
if (data === undefined) {
// EXIF data
EXIF.getData($image_img[0], function () {
data = exifDatas[$image_img.data('name')] = getExifDataMarkup(this);
}
function initThumbs() {
$main.children(".thumb").each(function () {
var $this = $(this),
$image = $this.find(".image"),
$image_img = $image.children("img"),
x;
if ($image.length == 0) return;
$image.css("background-image", "url(" + $image_img.attr("src") + ")");
if ((x = $image_img.data("position"))) $image.css("background-position", x);
$image_img.hide();
$image_img[0].addEventListener("load", function() {
EXIF.getData($image_img[0], function () {
exifDatas[$image_img.data('name')] = getExifDataMarkup(this);
});
});
}
return data !== undefined ? '<p>' + data + '</p>' : ' ';
},
fadeSpeed: 300,
onPopupClose: function () {
$body.removeClass("modal-active");
},
onPopupOpen: function () {
$body.addClass("modal-active");
},
overlayOpacity: 0,
popupCloserText: "",
popupHeight: 150,
popupLoaderText: "",
popupSpeed: 300,
popupWidth: 150,
selector: ".thumb > a.image",
usePopupCaption: true,
usePopupCloser: true,
usePopupDefaultStyling: false,
usePopupForceClose: true,
usePopupLoader: true,
usePopupNav: true,
windowMargin: 50,
});
// Hack: Set margins to 0 when 'xsmall' activates.
breakpoints.on("<=xsmall", function () {
$main[0]._poptrox.windowMargin = 0;
});
breakpoints.on(">xsmall", function () {
$main[0]._poptrox.windowMargin = 50;
});
});
}
function initPoptrox() {
$main.poptrox({
baseZIndex: 20000,
caption: function ($a) {
var $image_img = $a.children('img');
var data = exifDatas[$image_img.data('name')];
if (data === undefined) {
EXIF.getData($image_img[0], function () {
data = exifDatas[$image_img.data('name')] = getExifDataMarkup(this);
});
}
return data !== undefined ? '<p>' + data + '</p>' : ' ';
},
fadeSpeed: 300,
onPopupClose: function () {
$body.removeClass("modal-active");
},
onPopupOpen: function () {
$body.addClass("modal-active");
},
overlayOpacity: 0,
popupCloserText: "",
popupHeight: 150,
popupLoaderText: "",
popupSpeed: 300,
popupWidth: 150,
selector: ".thumb > a.image",
usePopupCaption: true,
usePopupCloser: true,
usePopupDefaultStyling: false,
usePopupForceClose: true,
usePopupLoader: true,
usePopupNav: true,
windowMargin: 50,
});
breakpoints.on("<=xsmall", function () {
$main[0]._poptrox.windowMargin = 0;
});
breakpoints.on(">xsmall", function () {
$main[0]._poptrox.windowMargin = 50;
});
}
function getExifDataMarkup(img) {
var exif = $('#main').data('exif');
var template = '';
@@ -263,4 +267,7 @@
return template;
}
// Galerie laden
loadGallery();
})(jQuery);