From 8a97197f3eea2a15f34b1249c2684b8c3d644e11 Mon Sep 17 00:00:00 2001 From: luxick Date: Thu, 15 Jan 2026 20:05:41 +0100 Subject: [PATCH] Refactor js --- script.js | 230 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 127 insertions(+), 103 deletions(-) diff --git a/script.js b/script.js index 660120d..67d6a97 100644 --- a/script.js +++ b/script.js @@ -262,12 +262,9 @@ })(); // ============================================================ - // Gallery Thumbnails + // Gallery Thumbnails Module // ============================================================ - function initGalleryThumbs() { - var imgs = document.querySelectorAll('div.luxtools-gallery img[data-thumb-src]'); - if (!imgs || !imgs.length) return; - + var GalleryThumbnails = (function () { function loadThumb(img) { var src = img.getAttribute('data-thumb-src') || ''; if (!src) return; @@ -286,107 +283,124 @@ 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' }); + function init() { + var imgs = document.querySelectorAll('div.luxtools-gallery img[data-thumb-src]'); + if (!imgs || !imgs.length) return; - imgs.forEach(function (img) { io.observe(img); }); - } else { - // Fallback: load soon after initial render - window.setTimeout(function () { - imgs.forEach(loadThumb); - }, 0); - } - } + 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' }); - // ============================================================ - // Open Service (file:// links) - // ============================================================ - function getServiceUrl(el) { - var url = el.getAttribute('data-service-url') || ''; - url = (url || '').trim(); - if (!url) return ''; - // strip trailing slashes - return url.replace(/\/+$/, ''); - } - - function pingOpenViaImage(el, rawPath) { - var baseUrl = getServiceUrl(el); - if (!baseUrl) return; - var url = baseUrl + '/open?path=' + encodeURIComponent(rawPath); - - // 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 client service configured')); - - var headers = { - 'Content-Type': 'application/json' - }; - - 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); - }); + imgs.forEach(function (img) { io.observe(img); }); + } else { + // Fallback: load soon after initial render + window.setTimeout(function () { + imgs.forEach(loadThumb); + }, 0); } - 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; + return { + init: init + }; + })(); + + // ============================================================ + // Open Service Module (file:// links) + // ============================================================ + var OpenService = (function () { + function getServiceUrl(el) { + var url = el.getAttribute('data-service-url') || ''; + url = (url || '').trim(); + if (!url) return ''; + // strip trailing slashes + return url.replace(/\/+$/, ''); } - // POSIX absolute: /home/user/file - if (path[0] === '/') { - return 'file://' + path; + function pingOpenViaImage(el, rawPath) { + var baseUrl = getServiceUrl(el); + if (!baseUrl) return; + var url = baseUrl + '/open?path=' + encodeURIComponent(rawPath); + + // Fire-and-forget without CORS. + try { + var img = new window.Image(); + img.src = url; + } catch (e) { + // ignore + } } - // Fall back to using the provided string. - return path; - } + function openViaService(el, rawPath) { + var baseUrl = getServiceUrl(el); + if (!baseUrl) return Promise.reject(new Error('No client service configured')); - function initScratchpads() { - var pads = document.querySelectorAll('div.luxtools-scratchpad[data-luxtools-scratchpad="1"]'); - if (!pads || !pads.length) return; + var headers = { + 'Content-Type': 'application/json' + }; + 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; + } + + return { + openViaService: openViaService, + pingOpenViaImage: pingOpenViaImage, + normalizeToFileUrl: normalizeToFileUrl + }; + })(); + + // ============================================================ + // Scratchpads Module + // ============================================================ + var Scratchpads = (function () { function setEditMode(root, isEditing) { if (!root || !root.classList) return; @@ -511,7 +525,7 @@ setStatus(root, ''); } - document.addEventListener('click', function (e) { + function onClick(e) { var t = e.target; if (!t) return; @@ -550,8 +564,18 @@ e.preventDefault(); closeEditor(rootC); } - }, true); - } + } + + function init() { + var pads = document.querySelectorAll('div.luxtools-scratchpad[data-luxtools-scratchpad="1"]'); + if (!pads || !pads.length) return; + document.addEventListener('click', onClick, true); + } + + return { + init: init + }; + })(); // ============================================================ // Click Handlers @@ -598,14 +622,14 @@ if (!raw) return; // Prefer local client service. - openViaService(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. - pingOpenViaImage(el, raw); + OpenService.pingOpenViaImage(el, raw); // Fallback to old behavior (often blocked in modern browsers). - var url = normalizeToFileUrl(raw); + var url = OpenService.normalizeToFileUrl(raw); if (!url) return; console.warn('Local client service failed, falling back to file:// navigation:', err); try { @@ -624,6 +648,6 @@ // Initialize // ============================================================ document.addEventListener('click', onClick, false); - document.addEventListener('DOMContentLoaded', initGalleryThumbs, false); - document.addEventListener('DOMContentLoaded', initScratchpads, false); + document.addEventListener('DOMContentLoaded', GalleryThumbnails.init, false); + document.addEventListener('DOMContentLoaded', Scratchpads.init, false); })();