Files
luxtools-plugin/script.js
luxick 23403bcc4f
Some checks failed
DokuWiki Default Tasks / all (push) Has been cancelled
Thumbnails verison 1
2026-01-06 10:40:19 +01:00

165 lines
4.7 KiB
JavaScript

/* global window, document */
(function () {
'use strict';
function initGalleryThumbs() {
var imgs = document.querySelectorAll('div.filetools-gallery img[data-thumb-src]');
if (!imgs || !imgs.length) return;
function loadThumb(img) {
var src = img.getAttribute('data-thumb-src') || '';
if (!src) return;
if (img.getAttribute('data-thumb-loading') === '1') return;
img.setAttribute('data-thumb-loading', '1');
var pre = new window.Image();
pre.onload = function () {
img.src = src;
img.removeAttribute('data-thumb-src');
img.removeAttribute('data-thumb-loading');
};
pre.onerror = function () {
img.removeAttribute('data-thumb-loading');
};
pre.src = src;
}
if ('IntersectionObserver' in window) {
var io = new window.IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (!entry.isIntersecting) return;
loadThumb(entry.target);
io.unobserve(entry.target);
});
}, { rootMargin: '200px' });
imgs.forEach(function (img) { io.observe(img); });
} else {
// Fallback: load soon after initial render
window.setTimeout(function () {
imgs.forEach(loadThumb);
}, 0);
}
}
function getServiceUrl(el) {
var url = el.getAttribute('data-service-url') || '';
url = (url || '').trim();
if (!url) return '';
// strip trailing slashes
return url.replace(/\/+$/, '');
}
function getServiceToken(el) {
var token = el.getAttribute('data-service-token') || '';
return (token || '').trim();
}
function pingOpenViaImage(el, rawPath) {
var baseUrl = getServiceUrl(el);
if (!baseUrl) return;
var token = getServiceToken(el);
var url = baseUrl + '/open?path=' + encodeURIComponent(rawPath);
if (token) url += '&token=' + encodeURIComponent(token);
// Fire-and-forget without CORS.
try {
var img = new window.Image();
img.src = url;
} catch (e) {
// ignore
}
}
function openViaService(el, rawPath) {
var baseUrl = getServiceUrl(el);
if (!baseUrl) return Promise.reject(new Error('No opener service configured'));
var headers = {
'Content-Type': 'application/json'
};
var token = getServiceToken(el);
if (token) headers['X-Filetools-Token'] = token;
return window.fetch(baseUrl + '/open', {
method: 'POST',
mode: 'cors',
credentials: 'omit',
headers: headers,
body: JSON.stringify({ path: rawPath })
}).then(function (res) {
if (!res.ok) {
return res.json().catch(function () { return null; }).then(function (body) {
var msg = (body && body.message) ? body.message : ('HTTP ' + res.status);
throw new Error(msg);
});
}
return res.json().catch(function () { return { ok: true }; });
});
}
function normalizeToFileUrl(path) {
if (!path) return '';
// already a file URL
if (/^file:\/\//i.test(path)) return path;
// UNC path: \\server\share\path
if (/^\\\\/.test(path)) {
var p = path.replace(/^\\\\/, '');
p = p.replace(/\\/g, '/');
return 'file://///' + p;
}
// Windows drive: C:\path\to\file
if (/^[a-zA-Z]:\\/.test(path)) {
var drive = path[0].toUpperCase();
var rest = path.slice(2).replace(/\\/g, '/');
return 'file:///' + drive + ':' + rest;
}
// POSIX absolute: /home/user/file
if (path[0] === '/') {
return 'file://' + path;
}
// Fall back to using the provided string.
return path;
}
function onClick(event) {
var el = event.target;
if (!el || !el.classList || !el.classList.contains('filetools-open')) return;
var raw = el.getAttribute('data-path') || '';
if (!raw) return;
// Prefer local opener service.
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.
pingOpenViaImage(el, raw);
// Fallback to old behavior (often blocked in modern browsers).
var url = normalizeToFileUrl(raw);
if (!url) return;
console.warn('Local opener service failed, falling back to file:// navigation:', err);
try {
window.open(url, '_blank', 'noopener');
} catch (e) {
try {
window.location.href = url;
} catch (e2) {
console.error('Failed to open file URL:', e2);
}
}
});
}
document.addEventListener('click', onClick, false);
document.addEventListener('DOMContentLoaded', initGalleryThumbs, false);
})();