mirror of
https://github.com/greflm13/StaticGalleryBuilder.git
synced 2026-02-05 02:59:27 +00:00
384 lines
13 KiB
Django/Jinja
384 lines
13 KiB
Django/Jinja
{%- macro render_tags(tag_tree, parent) -%}
|
|
<ol>
|
|
{%- for key, value in tag_tree.items() %}
|
|
<li class="tagentry">
|
|
<label onclick="filter()" title="{{ key }}" id="{{ parent }}|{{ key }}">
|
|
<input class="tag" type="checkbox" />{{ key }}
|
|
</label>
|
|
{%- if value %}
|
|
{{ render_tags(value, parent + '|' + key) }}
|
|
{%- endif %}
|
|
</li>
|
|
{%- endfor %}
|
|
</ol>
|
|
{%- endmacro -%}
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>{{ title }}</title>
|
|
{%- if webmanifest %}
|
|
<link rel="manifest" href="/.static/manifest.json">
|
|
{%- endif %}
|
|
<link rel="preload" href="{{ stylesheet }}" as="style">
|
|
{%- if theme %}
|
|
<link rel="preload" href="{{ theme }}" as="style">
|
|
{%- endif %}
|
|
<link rel="icon" type="image/x-icon" href="{{ favicon }}">
|
|
<link rel="stylesheet" href="{{ stylesheet }}">
|
|
{%- if theme %}
|
|
<link rel="stylesheet" href="{{ theme }}">
|
|
{%- endif %}
|
|
<link rel="preload" href="{{ root }}.static/pswp/photoswipe.css" as="style">
|
|
<link rel="preload" href="{{ root }}.static/pswp/default-skin/default-skin.css" as="style">
|
|
<link rel="modulepreload" href="{{ root }}.static/pswp/photoswipe.min.js">
|
|
<link rel="modulepreload" href="{{ root }}.static/pswp/photoswipe-ui-default.min.js">
|
|
<link rel="stylesheet" href="{{ root }}.static/pswp/photoswipe.css">
|
|
<link rel="stylesheet" href="{{ root }}.static/pswp/default-skin/default-skin.css">
|
|
<script src="{{ root }}.static/pswp/photoswipe.min.js"></script>
|
|
<script src="{{ root }}.static/pswp/photoswipe-ui-default.min.js"></script>
|
|
</head>
|
|
|
|
<body>
|
|
<div class="header">
|
|
<ol class="navbar">
|
|
<div class="navleft">
|
|
<li><a href="{{ root }}">Home</a></li>
|
|
{%- if parent %}
|
|
<li><a href="{{ parent }}">Parent Directory</a></li>
|
|
{%- endif %}
|
|
{%- if info %}
|
|
<li class="tooltip"><a>Info</a><span class="tooltiptext infotext">
|
|
{%- for infoline in info -%}
|
|
{{ infoline }}<br />
|
|
{%- endfor -%}
|
|
</span></li>
|
|
{%- endif %}
|
|
</div>
|
|
<div class="navcenter">
|
|
<li class="title"><span class="header">{{ header }}</span></li>
|
|
</div>
|
|
<div class="navright">
|
|
{% if tags %}
|
|
<li class="tooltip">
|
|
<a>Filter by Tags</a>
|
|
<ol class="tooltiptext tagdropdown" id="tagdropdown">
|
|
<span class="tagentry">
|
|
<label onclick="recursive()">
|
|
<input type="checkbox" id="recursive" />recursive filter
|
|
</label>
|
|
</span>
|
|
{{ render_tags(tags, '') }}
|
|
</ol>
|
|
</li>
|
|
{% endif %}
|
|
{%- if licensefile %}
|
|
<li class="license"><a href="{{ licensefile }}">License</a></li>
|
|
{%- endif %}
|
|
</div>
|
|
</ol>
|
|
{% if subdirectories %}
|
|
{%- for subdirectory in subdirectories %}
|
|
<link rel="preload" href="{{ subdirectory.url }}/index.html" type="text/html">
|
|
{%- endfor %}
|
|
<div class="folders">
|
|
{%- for subdirectory in subdirectories %}
|
|
<a href="{{ subdirectory.url }}">
|
|
<figure>
|
|
<img class="foldericon" />
|
|
{%- if subdirectory.thumb %}
|
|
<img class="folderthumb" src="{{ subdirectory.thumb }}" />
|
|
{%- endif %}
|
|
<figcaption>{{ subdirectory.name }}</figcaption>
|
|
</figure>
|
|
</a>
|
|
{%- endfor %}
|
|
</div>
|
|
{%- endif %}
|
|
</div>
|
|
<div class="row" id="imagelist">
|
|
</div>
|
|
{% if license %}
|
|
{%- if 'CC' in license.type %}
|
|
<div class="footer" xmlns:cc="http://creativecommons.org/ns#" xmlns:dct="http://purl.org/dc/terms/">
|
|
{%- if license.type == 'CC0 1.0' %}
|
|
<a property="dct:title" rel="cc:attributionURL" href="{{ root }}">{{ license.project }}</a> by <span property="cc:attributionName">{{ license.author }}</span> is marked with
|
|
<a href="{{ license.url }}" target="_blank" rel="license noopener noreferrer" style="display: inline-block">CC0 1.0
|
|
{%- for pic in license.pics %}
|
|
<img style="height: 22px !important; margin-left: 3px; vertical-align: text-bottom" src="{{ pic }}" alt="" />
|
|
{%- endfor %}
|
|
</a>
|
|
{%- else %}
|
|
<a property="dct:title" rel="cc:attributionURL" href="{{ root }}">{{ license.project }}</a> by <span property="cc:attributionName">{{ license.author }}</span> is licensed under
|
|
<a href="{{ license.url }}" target="_blank" rel="license noopener noreferrer">{{ license.type }}
|
|
{%- for pic in license.pics %}
|
|
<img src="{{ pic }}" alt="" />
|
|
{%- endfor %}
|
|
</a>
|
|
{%- endif %}
|
|
<span class="attribution">Made with <a href="https://github.com/greflm13/StaticGalleryBuilder" target="_blank" rel="noopener noreferrer">StaticGalleryBuilder {{ version }}</a> by <a
|
|
href="https://github.com/greflm13" target="_blank" rel="noopener noreferrer">{{ logo }}</a>.</span>
|
|
<button type="button" onclick="topFunction()" id="totop" title="Back to Top">Back to Top</button>
|
|
</div>
|
|
{%- endif %}
|
|
{%- else %}
|
|
<div class="footer">
|
|
<span class="attribution">Made with <a href="https://github.com/greflm13/StaticGalleryBuilder" target="_blank" rel="noopener noreferrer">StaticGalleryBuilder {{ version }}</a> by <a
|
|
href="https://github.com/greflm13" target="_blank" rel="noopener noreferrer">{{ logo }}</a>.</span>
|
|
<button type="button" onclick="topFunction()" id="totop" title="Back to Top">Back to Top</button>
|
|
</div>
|
|
{%- endif %}
|
|
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
|
|
<div class="pswp__bg"></div>
|
|
<div class="pswp__scroll-wrap">
|
|
<div class="pswp__container">
|
|
<div class="pswp__item"></div>
|
|
<div class="pswp__item"></div>
|
|
<div class="pswp__item"></div>
|
|
</div>
|
|
<div class="pswp__ui pswp__ui--hidden">
|
|
<div class="pswp__top-bar">
|
|
<div class="pswp__counter"></div>
|
|
<button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
|
|
<button class="pswp__button pswp__button--share" title="Share"></button>
|
|
<button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
|
|
<button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
|
|
<div class="pswp__preloader">
|
|
<div class="pswp__preloader__icn">
|
|
<div class="pswp__preloader__cut">
|
|
<div class="pswp__preloader__donut"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
|
|
<div class="pswp__share-tooltip"></div>
|
|
</div>
|
|
<button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
|
|
</button>
|
|
<button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
|
|
</button>
|
|
<div class="pswp__caption">
|
|
<div class="pswp__caption__center"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
const pswpElement = document.querySelectorAll('.pswp')[0];
|
|
const re = /pid=(\d+)/;
|
|
const filterre = /#(.*)/;
|
|
const recursere = /\?recursive/;
|
|
let items = [];
|
|
let shown = [];
|
|
let subfolders = [];
|
|
let controllers = {};
|
|
|
|
function requestMetadata() {
|
|
fetch(".metadata.json").then(response => {
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! Status: ${response.status}`);
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
items = Object.values(data.images);
|
|
subfolders = data.subfolders;
|
|
if (filterre.test(window.location.href)) {
|
|
const selected = window.location.href.match(filterre)[1].split(",");
|
|
setFilter(selected);
|
|
}
|
|
if (recursere.test(window.location.href)) {
|
|
document.getElementById("recursive").checked = true;
|
|
recursive();
|
|
}
|
|
filter();
|
|
|
|
if (re.test(window.location.href)) {
|
|
const pid = window.location.href.match(re)[1];
|
|
openSwipe(parseInt(pid));
|
|
}
|
|
})
|
|
.catch(error => console.error('Failed to fetch data:', error));
|
|
}
|
|
|
|
function openSwipe(img) {
|
|
const options = {
|
|
index: img
|
|
};
|
|
const gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, shown, options);
|
|
gallery.init();
|
|
}
|
|
|
|
async function recursive(sub = undefined) {
|
|
const curr = window.location.href.split("#");
|
|
const content = document.getRootNode().innerHTML;
|
|
const title = document.title;
|
|
const ischecked = document.getElementById("recursive").checked;
|
|
const folders = document.getElementsByClassName("folders")[0];
|
|
|
|
if (sub == undefined) {
|
|
sub = subfolders;
|
|
}
|
|
|
|
if (ischecked) {
|
|
window.history.replaceState({ "html": content, "pageTitle": title }, "", curr[0].split("?")[0] + "?recursive#" + curr[1]);
|
|
if (folders != undefined) {
|
|
folders.style.display = "none";
|
|
}
|
|
|
|
for (const folder of sub) {
|
|
try {
|
|
const response = await fetch(folder.metadata);
|
|
const data = await response.json();
|
|
|
|
const existingItems = new Set(items.map(item => item.src));
|
|
|
|
for (const image of Object.values(data.images)) {
|
|
if (!existingItems.has(image.src)) {
|
|
items.push(image);
|
|
}
|
|
}
|
|
|
|
if (data.subfolders.length > 0) {
|
|
await recursive(data.subfolders);
|
|
}
|
|
|
|
filter();
|
|
} catch (error) {
|
|
console.error('Failed to fetch folder metadata:', error);
|
|
}
|
|
}
|
|
} else {
|
|
window.history.replaceState({ "html": content, "pageTitle": title }, "", curr[0].split("?")[0] + "#" + curr[1]);
|
|
if (folders != undefined) {
|
|
folders.style.display = "";
|
|
}
|
|
requestMetadata();
|
|
}
|
|
}
|
|
|
|
const totopbutton = document.getElementById("totop");
|
|
|
|
window.onscroll = function () { scrollFunction() };
|
|
|
|
function scrollFunction() {
|
|
if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
|
|
totopbutton.style.display = "block";
|
|
} else {
|
|
totopbutton.style.display = "none";
|
|
}
|
|
}
|
|
|
|
function topFunction() {
|
|
window.scrollTo({ top: 0, behavior: 'smooth' })
|
|
}
|
|
|
|
function updateImageList() {
|
|
let str = ""
|
|
let imagelist = document.getElementById("imagelist");
|
|
shown.forEach((item, index) => {
|
|
str += '<div class="column"><figure><img src="' + item.msrc + '" onclick="openSwipe(' + index + ')" onmouseover="prefetch(' + index + ')" onmouseleave="cancel(' + index + ')" /><figcaption class="caption">' + item.name;
|
|
if (item.tiff != "" & item.tiff != undefined) {
|
|
str += ' <a href="' + item.tiff + '">TIFF</a>';
|
|
}
|
|
if (item.raw != "" & item.raw != undefined) {
|
|
str += ' <a href="' + item.raw + '">RAW</a>';
|
|
}
|
|
str += '</figcaption></figure></div>';
|
|
});
|
|
|
|
imagelist.innerHTML = str;
|
|
}
|
|
|
|
function prefetch(img) {
|
|
const controller = new AbortController()
|
|
const signal = controller.signal
|
|
controllers[img] = controller;
|
|
let urlToFetch = items[img].src;
|
|
|
|
fetch(urlToFetch, {
|
|
method: 'get',
|
|
signal: signal,
|
|
}).catch(function (err) { });
|
|
}
|
|
|
|
function cancel(img) {
|
|
controllers[img].abort();
|
|
delete controllers[img];
|
|
}
|
|
|
|
function filter() {
|
|
shown = [];
|
|
|
|
const curr = window.location.href.split("#")[0] + "#";
|
|
const path = window.location.href.split("#")[0].replace("index.html", "").replace("(", "%28").replace(")", "%29");
|
|
|
|
const selected_tags = [];
|
|
const tagcheckboxes = document.querySelectorAll("#tagdropdown input[class='tag']:checked");
|
|
|
|
tagcheckboxes.forEach((checkbox) => {
|
|
let tag = checkbox.parentElement.id.trim().substring(1);
|
|
if (checkbox.parentElement.parentElement.children.length > 1) {
|
|
tag += "|"
|
|
}
|
|
selected_tags.push(tag);
|
|
});
|
|
|
|
console.log(selected_tags);
|
|
|
|
const urltags = selected_tags.join(",");
|
|
|
|
const isRecursiveChecked = document.getElementById("recursive").checked;
|
|
|
|
for (const item of items) {
|
|
const tags = item.tags || [];
|
|
const include = selected_tags.every(selected => {
|
|
const isParent = selected.endsWith('|');
|
|
if (isParent) {
|
|
return tags.some(t => t.startsWith(selected));
|
|
} else {
|
|
return tags.includes(selected);
|
|
}
|
|
});
|
|
|
|
if (include || selected_tags.length === 0) {
|
|
if (!isRecursiveChecked) {
|
|
if (item.src.replace(item.name, "") == path) {
|
|
shown.push(item);
|
|
}
|
|
} else {
|
|
shown.push(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
updateImageList();
|
|
window.location.href = curr + urltags;
|
|
}
|
|
|
|
function setFilter(selected) {
|
|
const tagcheckboxes = document.querySelectorAll("#tagdropdown input[class='tag']");
|
|
selected.forEach((tag) => {
|
|
tagcheckboxes.forEach((checkbox) => {
|
|
if (checkbox.parentElement.id.trim().substring(1).replace(" ", "%20") == tag) {
|
|
checkbox.checked = true;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function onLoad() {
|
|
requestMetadata();
|
|
}
|
|
|
|
window.addEventListener ?
|
|
window.addEventListener("load", onLoad, false) :
|
|
window.attachEvent && window.attachEvent("onload", onLoad);
|
|
|
|
</script>
|
|
</body>
|
|
|
|
</html> |