Refactor js
This commit is contained in:
230
script.js
230
script.js
@@ -262,12 +262,9 @@
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Gallery Thumbnails
|
// Gallery Thumbnails Module
|
||||||
// ============================================================
|
// ============================================================
|
||||||
function initGalleryThumbs() {
|
var GalleryThumbnails = (function () {
|
||||||
var imgs = document.querySelectorAll('div.luxtools-gallery img[data-thumb-src]');
|
|
||||||
if (!imgs || !imgs.length) return;
|
|
||||||
|
|
||||||
function loadThumb(img) {
|
function loadThumb(img) {
|
||||||
var src = img.getAttribute('data-thumb-src') || '';
|
var src = img.getAttribute('data-thumb-src') || '';
|
||||||
if (!src) return;
|
if (!src) return;
|
||||||
@@ -286,107 +283,124 @@
|
|||||||
pre.src = src;
|
pre.src = src;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('IntersectionObserver' in window) {
|
function init() {
|
||||||
var io = new window.IntersectionObserver(function (entries) {
|
var imgs = document.querySelectorAll('div.luxtools-gallery img[data-thumb-src]');
|
||||||
entries.forEach(function (entry) {
|
if (!imgs || !imgs.length) return;
|
||||||
if (!entry.isIntersecting) return;
|
|
||||||
loadThumb(entry.target);
|
|
||||||
io.unobserve(entry.target);
|
|
||||||
});
|
|
||||||
}, { rootMargin: '200px' });
|
|
||||||
|
|
||||||
imgs.forEach(function (img) { io.observe(img); });
|
if ('IntersectionObserver' in window) {
|
||||||
} else {
|
var io = new window.IntersectionObserver(function (entries) {
|
||||||
// Fallback: load soon after initial render
|
entries.forEach(function (entry) {
|
||||||
window.setTimeout(function () {
|
if (!entry.isIntersecting) return;
|
||||||
imgs.forEach(loadThumb);
|
loadThumb(entry.target);
|
||||||
}, 0);
|
io.unobserve(entry.target);
|
||||||
}
|
});
|
||||||
}
|
}, { rootMargin: '200px' });
|
||||||
|
|
||||||
// ============================================================
|
imgs.forEach(function (img) { io.observe(img); });
|
||||||
// Open Service (file:// links)
|
} else {
|
||||||
// ============================================================
|
// Fallback: load soon after initial render
|
||||||
function getServiceUrl(el) {
|
window.setTimeout(function () {
|
||||||
var url = el.getAttribute('data-service-url') || '';
|
imgs.forEach(loadThumb);
|
||||||
url = (url || '').trim();
|
}, 0);
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
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
|
return {
|
||||||
if (/^[a-zA-Z]:\\/.test(path)) {
|
init: init
|
||||||
var drive = path[0].toUpperCase();
|
};
|
||||||
var rest = path.slice(2).replace(/\\/g, '/');
|
})();
|
||||||
return 'file:///' + drive + ':' + rest;
|
|
||||||
|
// ============================================================
|
||||||
|
// 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
|
function pingOpenViaImage(el, rawPath) {
|
||||||
if (path[0] === '/') {
|
var baseUrl = getServiceUrl(el);
|
||||||
return 'file://' + path;
|
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.
|
function openViaService(el, rawPath) {
|
||||||
return path;
|
var baseUrl = getServiceUrl(el);
|
||||||
}
|
if (!baseUrl) return Promise.reject(new Error('No client service configured'));
|
||||||
|
|
||||||
function initScratchpads() {
|
var headers = {
|
||||||
var pads = document.querySelectorAll('div.luxtools-scratchpad[data-luxtools-scratchpad="1"]');
|
'Content-Type': 'application/json'
|
||||||
if (!pads || !pads.length) return;
|
};
|
||||||
|
|
||||||
|
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) {
|
function setEditMode(root, isEditing) {
|
||||||
if (!root || !root.classList) return;
|
if (!root || !root.classList) return;
|
||||||
|
|
||||||
@@ -511,7 +525,7 @@
|
|||||||
setStatus(root, '');
|
setStatus(root, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('click', function (e) {
|
function onClick(e) {
|
||||||
var t = e.target;
|
var t = e.target;
|
||||||
if (!t) return;
|
if (!t) return;
|
||||||
|
|
||||||
@@ -550,8 +564,18 @@
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
closeEditor(rootC);
|
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
|
// Click Handlers
|
||||||
@@ -598,14 +622,14 @@
|
|||||||
if (!raw) return;
|
if (!raw) return;
|
||||||
|
|
||||||
// Prefer local client service.
|
// Prefer local client service.
|
||||||
openViaService(el, raw)
|
OpenService.openViaService(el, raw)
|
||||||
.catch(function (err) {
|
.catch(function (err) {
|
||||||
// If the browser blocks the request before it reaches localhost (mixed-content,
|
// If the browser blocks the request before it reaches localhost (mixed-content,
|
||||||
// extensions, stricter CORS handling), fall back to a no-CORS GET ping.
|
// 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).
|
// Fallback to old behavior (often blocked in modern browsers).
|
||||||
var url = normalizeToFileUrl(raw);
|
var url = OpenService.normalizeToFileUrl(raw);
|
||||||
if (!url) return;
|
if (!url) return;
|
||||||
console.warn('Local client service failed, falling back to file:// navigation:', err);
|
console.warn('Local client service failed, falling back to file:// navigation:', err);
|
||||||
try {
|
try {
|
||||||
@@ -624,6 +648,6 @@
|
|||||||
// Initialize
|
// Initialize
|
||||||
// ============================================================
|
// ============================================================
|
||||||
document.addEventListener('click', onClick, false);
|
document.addEventListener('click', onClick, false);
|
||||||
document.addEventListener('DOMContentLoaded', initGalleryThumbs, false);
|
document.addEventListener('DOMContentLoaded', GalleryThumbnails.init, false);
|
||||||
document.addEventListener('DOMContentLoaded', initScratchpads, false);
|
document.addEventListener('DOMContentLoaded', Scratchpads.init, false);
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user