Unify dialog infrastructure

This commit is contained in:
2026-03-20 07:56:41 +01:00
parent 96cc82db9e
commit a3f021e5e1
6 changed files with 2047 additions and 1150 deletions

View File

@@ -1,7 +1,7 @@
/* global window, document */
(function () {
'use strict';
"use strict";
var Luxtools = window.Luxtools || (window.Luxtools = {});
var Lightbox = Luxtools.Lightbox;
@@ -16,7 +16,7 @@
function findOpenElement(target) {
var el = target;
while (el && el !== document) {
if (el.classList && el.classList.contains('luxtools-open')) return el;
if (el.classList && el.classList.contains("luxtools-open")) return el;
el = el.parentNode;
}
return null;
@@ -25,7 +25,8 @@
function findGalleryItem(target) {
var el = target;
while (el && el !== document) {
if (el.classList && el.classList.contains('luxtools-gallery-item')) return el;
if (el.classList && el.classList.contains("luxtools-gallery-item"))
return el;
el = el.parentNode;
}
return null;
@@ -35,7 +36,9 @@
// Image gallery lightbox: intercept clicks so we don't navigate away.
var galleryItem = findGalleryItem(event.target);
if (galleryItem && Lightbox && Lightbox.open) {
var gallery = galleryItem.closest ? galleryItem.closest('div.luxtools-gallery[data-luxtools-gallery="1"]') : null;
var gallery = galleryItem.closest
? galleryItem.closest('div.luxtools-gallery[data-luxtools-gallery="1"]')
: null;
if (gallery) {
event.preventDefault();
Lightbox.open(gallery, galleryItem);
@@ -47,49 +50,56 @@
if (!el) return;
// {{open>...}} renders as a link; avoid jumping to '#'.
if (el.tagName && el.tagName.toLowerCase() === 'a') {
if (el.tagName && el.tagName.toLowerCase() === "a") {
event.preventDefault();
}
var raw = el.getAttribute('data-path') || '';
var raw = el.getAttribute("data-path") || "";
if (!raw) return;
if (!OpenService || !OpenService.openViaService) return;
// Prefer local client service.
OpenService.openViaService(el, raw)
.catch(function (err) {
// If the browser blocks the request before it reaches localhost (mixed-content,
// extensions, stricter CORS handling), fall back to a no-CORS GET ping.
if (OpenService && OpenService.pingOpenViaImage) {
OpenService.pingOpenViaImage(el, raw);
}
OpenService.openViaService(el, raw).catch(function (err) {
// If the browser blocks the request before it reaches localhost (mixed-content,
// extensions, stricter CORS handling), fall back to a no-CORS GET ping.
if (OpenService && OpenService.pingOpenViaImage) {
OpenService.pingOpenViaImage(el, raw);
}
// Fallback to old behavior (often blocked in modern browsers).
var url = OpenService && OpenService.normalizeToFileUrl ? OpenService.normalizeToFileUrl(raw) : '';
if (!url) return;
console.warn('Local client service failed, falling back to file:// navigation:', err);
// Fallback to old behavior (often blocked in modern browsers).
var url =
OpenService && OpenService.normalizeToFileUrl
? OpenService.normalizeToFileUrl(raw)
: "";
if (!url) return;
console.warn(
"Local client service failed, falling back to file:// navigation:",
err,
);
try {
window.open(url, "_blank", "noopener");
} catch (e) {
try {
window.open(url, '_blank', 'noopener');
} catch (e) {
try {
window.location.href = url;
} catch (e2) {
console.error('Failed to open file URL:', e2);
}
window.location.href = url;
} catch (e2) {
console.error("Failed to open file URL:", e2);
}
});
}
});
}
function initChronologicalEventTimes() {
var nodes = document.querySelectorAll('.luxtools-event-time[data-luxtools-start]');
var nodes = document.querySelectorAll(
".luxtools-event-time[data-luxtools-start]",
);
if (!nodes || nodes.length === 0) return;
var formatter;
try {
formatter = new Intl.DateTimeFormat('de-DE', {
hour: '2-digit',
minute: '2-digit',
hour12: false
formatter = new Intl.DateTimeFormat("de-DE", {
hour: "2-digit",
minute: "2-digit",
hour12: false,
});
} catch (e) {
formatter = null;
@@ -97,7 +107,7 @@
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
var raw = node.getAttribute('data-luxtools-start') || '';
var raw = node.getAttribute("data-luxtools-start") || "";
if (!raw) continue;
var date = new Date(raw);
@@ -107,9 +117,9 @@
if (formatter) {
label = formatter.format(date);
} else {
var hh = String(date.getHours()).padStart(2, '0');
var mm = String(date.getMinutes()).padStart(2, '0');
label = hh + ':' + mm;
var hh = String(date.getHours()).padStart(2, "0");
var mm = String(date.getMinutes()).padStart(2, "0");
label = hh + ":" + mm;
}
node.textContent = label;
@@ -120,123 +130,185 @@
// Purge Cache Dialog
// ============================================================
function initPurgeCacheDialog() {
var $link = jQuery('a.luxtools-invalidate-cache');
if ($link.length === 0) return;
document.addEventListener(
"click",
function (e) {
var link = e.target.closest
? e.target.closest("a.luxtools-invalidate-cache")
: null;
if (!link) return;
$link.on('click.luxtools', function (e) {
e.preventDefault();
e.preventDefault();
var href = $link.attr('href') || '';
var lang = (window.LANG && window.LANG.plugins && window.LANG.plugins.luxtools)
? window.LANG.plugins.luxtools
: {};
var href = link.getAttribute("href") || "";
var lang =
window.LANG && window.LANG.plugins && window.LANG.plugins.luxtools
? window.LANG.plugins.luxtools
: {};
var $dialog = jQuery(
'<div>' +
'<p>' + (lang.cache_purge_dialog_intro || 'The DokuWiki cache will always be purged. Optionally also purge the luxtools-specific caches:') + '</p>' +
'<p><label><input type="checkbox" id="luxtools-purge-pagelinks"> <strong>' + (lang.cache_purge_pagelinks_label || 'Pagelinks') + '</strong>' +
' &ndash; ' + (lang.cache_purge_pagelinks_desc || 'Purges the pagelink mapping cache') + '</label></p>' +
'<p><label><input type="checkbox" id="luxtools-purge-thumbs"> <strong>' + (lang.cache_purge_thumbs_label || 'Thumbnails') + '</strong>' +
' &ndash; ' + (lang.cache_purge_thumbs_desc || 'Purges all cached image thumbnails') + '</label></p>' +
'</div>'
);
var html = '<div class="luxtools-dialog-content">';
html +=
'<button type="button" class="luxtools-dialog-close" aria-label="Close">&times;</button>';
html +=
'<h3 class="luxtools-dialog-title">' +
(lang.cache_purge_dialog_title || "Purge Cache") +
"</h3>";
html +=
"<p>" +
(lang.cache_purge_dialog_intro ||
"The DokuWiki cache will always be purged. Optionally also purge the luxtools-specific caches:") +
"</p>";
html +=
'<p><label><input type="checkbox" id="luxtools-purge-pagelinks"> <strong>' +
(lang.cache_purge_pagelinks_label || "Pagelinks") +
"</strong>";
html +=
" &ndash; " +
(lang.cache_purge_pagelinks_desc ||
"Purges the pagelink mapping cache") +
"</label></p>";
html +=
'<p><label><input type="checkbox" id="luxtools-purge-thumbs"> <strong>' +
(lang.cache_purge_thumbs_label || "Thumbnails") +
"</strong>";
html +=
" &ndash; " +
(lang.cache_purge_thumbs_desc ||
"Purges all cached image thumbnails") +
"</label></p>";
html += '<div class="luxtools-dialog-actions">';
html +=
'<button type="button" class="button luxtools-purge-confirm">' +
(lang.cache_purge_confirm || "Purge Cache") +
"</button> ";
html +=
'<button type="button" class="button luxtools-purge-cancel">' +
(lang.cache_purge_cancel || "Cancel") +
"</button>";
html += "</div>";
html += "</div>";
$dialog.dialog({
title: lang.cache_purge_dialog_title || 'Purge Cache',
modal: true,
width: 420,
buttons: [
{
text: lang.cache_purge_cancel || 'Cancel',
click: function () { $dialog.dialog('close'); }
},
{
text: lang.cache_purge_confirm || 'Purge Cache',
click: function () {
var url = href;
if ($dialog.find('#luxtools-purge-pagelinks').prop('checked')) {
url += '&luxtools_purge_pagelinks=1';
}
if ($dialog.find('#luxtools-purge-thumbs').prop('checked')) {
url += '&luxtools_purge_thumbs=1';
}
$dialog.dialog('close');
window.location.href = url;
}
}
],
close: function () {
jQuery(this).dialog('destroy').remove();
Luxtools.Dialog.show(html);
var container = Luxtools.Dialog.getContainer();
var cancelBtn = container.querySelector(".luxtools-purge-cancel");
if (cancelBtn) {
cancelBtn.addEventListener("click", function () {
Luxtools.Dialog.close();
});
}
});
});
var confirmBtn = container.querySelector(".luxtools-purge-confirm");
if (confirmBtn) {
confirmBtn.addEventListener("click", function () {
var url = href;
var plCheck = container.querySelector("#luxtools-purge-pagelinks");
var thCheck = container.querySelector("#luxtools-purge-thumbs");
if (plCheck && plCheck.checked) {
url += "&luxtools_purge_pagelinks=1";
}
if (thCheck && thCheck.checked) {
url += "&luxtools_purge_thumbs=1";
}
Luxtools.Dialog.close();
window.location.href = url;
});
}
},
false,
);
}
// ============================================================
// Calendar Sync Button (syntax widget)
// ============================================================
function initCalendarSyncButtons() {
document.addEventListener('click', function (e) {
var btn = e.target;
if (!btn || !btn.classList || !btn.classList.contains('luxtools-calendar-sync-btn')) return;
document.addEventListener(
"click",
function (e) {
var btn = e.target;
if (
!btn ||
!btn.classList ||
!btn.classList.contains("luxtools-calendar-sync-btn")
)
return;
e.preventDefault();
e.preventDefault();
var ajaxUrl = btn.getAttribute('data-luxtools-ajax-url') || '';
var sectok = btn.getAttribute('data-luxtools-sectok') || '';
if (!ajaxUrl) return;
var ajaxUrl = btn.getAttribute("data-luxtools-ajax-url") || "";
var sectok = btn.getAttribute("data-luxtools-sectok") || "";
if (!ajaxUrl) return;
var status = btn.parentNode ? btn.parentNode.querySelector('.luxtools-calendar-sync-status') : null;
var status = btn.parentNode
? btn.parentNode.querySelector(".luxtools-calendar-sync-status")
: null;
btn.disabled = true;
if (status) {
status.textContent = 'Syncing...';
status.style.color = '';
}
var xhr = new XMLHttpRequest();
xhr.open('POST', ajaxUrl, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = function () {
btn.disabled = false;
try {
var r = JSON.parse(xhr.responseText);
if (status) {
status.textContent = r.message || (r.ok ? 'Done' : 'Failed');
status.style.color = r.ok ? 'green' : 'red';
}
} catch (ex) {
if (status) {
status.textContent = 'Error';
status.style.color = 'red';
}
}
};
xhr.onerror = function () {
btn.disabled = false;
btn.disabled = true;
if (status) {
status.textContent = 'Network error';
status.style.color = 'red';
status.textContent = "Syncing...";
status.style.color = "";
}
};
xhr.send('call=luxtools_calendar_sync&sectok=' + encodeURIComponent(sectok));
}, false);
var xhr = new XMLHttpRequest();
xhr.open("POST", ajaxUrl, true);
xhr.setRequestHeader(
"Content-Type",
"application/x-www-form-urlencoded",
);
xhr.onload = function () {
btn.disabled = false;
try {
var r = JSON.parse(xhr.responseText);
if (status) {
status.textContent = r.message || (r.ok ? "Done" : "Failed");
status.style.color = r.ok ? "green" : "red";
}
} catch (ex) {
if (status) {
status.textContent = "Error";
status.style.color = "red";
}
}
};
xhr.onerror = function () {
btn.disabled = false;
if (status) {
status.textContent = "Network error";
status.style.color = "red";
}
};
xhr.send(
"call=luxtools_calendar_sync&sectok=" + encodeURIComponent(sectok),
);
},
false,
);
}
// ============================================================
// Initialize
// ============================================================
document.addEventListener('click', onClick, false);
document.addEventListener('DOMContentLoaded', function () {
if (GalleryThumbnails && GalleryThumbnails.init) GalleryThumbnails.init();
initChronologicalEventTimes();
if (CalendarWidget && CalendarWidget.init) CalendarWidget.init();
initPurgeCacheDialog();
initCalendarSyncButtons();
}, false);
document.addEventListener('DOMContentLoaded', function () {
if (Scratchpads && Scratchpads.init) Scratchpads.init();
}, false);
document.addEventListener("click", onClick, false);
document.addEventListener(
"DOMContentLoaded",
function () {
if (GalleryThumbnails && GalleryThumbnails.init) GalleryThumbnails.init();
initChronologicalEventTimes();
if (CalendarWidget && CalendarWidget.init) CalendarWidget.init();
initPurgeCacheDialog();
initCalendarSyncButtons();
},
false,
);
document.addEventListener(
"DOMContentLoaded",
function () {
if (Scratchpads && Scratchpads.init) Scratchpads.init();
},
false,
);
Luxtools.initChronologicalEventTimes = initChronologicalEventTimes;
})();