记得(/important-dates) - 修复 500:th:classappend 里 BEM 类名 wi-id__car 的两个 __ 触发 Thymeleaf __...__ 预处理,提取片段含引号导致 Could not parse as expression; 皮肤修饰类改为 wi-id-skin-cool/cute/neutral(表达式内不再出现 __) - 牵挂的人卡片:倒计时改到右上角与日期卡片一致,生日一行显示不再折行; 网格改为桌面三列 / 平板两列 / 手机一列 - 页头与订阅/记得统一(标题-副标题-强调线间距) 友链(/links) - 再次消除两处 __ 预处理隐患:回执修饰类改 wi-links-check-ok/error, 验证码 onerror 由 th:onerror="|...|" 改为普通静态属性(不参与表达式解析) - 新增反链/可达性检测徽章:读取 link.status.verification,头像右下角 绿点=访问正常(反链有效),红点=访问异常或未检测到反链,未检测不显示 留言板 - 移除 -124px 负 margin 补偿(头部卡片顶部被导航盖住的根因), 装饰引号 top 改为正值不再被 overflow 裁掉 瞬间 - 修复九宫格失效根因:lightgallery 处理时把每张图包进 <a>,导致 :has(:only-child) 恒真、全部图片退化为单列大图;改为直接把 img 作为图库项(data-src),瀑布流样式同样补上 4 图 2x2 规则 全局 - 暗色模式配色调为偏中性暖灰(bg #1e1c1a / raised #292623),降低黄褐闷感, 同步 theme-color 与依赖旧色值的变量 - 正文排版:p 字号 1.05~1.15rem、行高 1.85,H1~H6 逐级放大、层级更清晰 新增「文章组件」插件适配 - 新增 src/styles/_widgets.scss,将插件 --xhhao-com-* 变量映射到主题变量, 明暗模式自动跟随
270 lines
8.7 KiB
Plaintext
270 lines
8.7 KiB
Plaintext
---
|
|
---
|
|
|
|
<script>
|
|
import 'lightgallery/css/lightgallery.css';
|
|
import 'lightgallery/css/lg-zoom.css';
|
|
import 'lightgallery/css/lg-thumbnail.css';
|
|
import 'lightgallery/css/lg-fullscreen.css';
|
|
import lightGallery from 'lightgallery';
|
|
import lgZoom from 'lightgallery/plugins/zoom';
|
|
import lgThumbnail from 'lightgallery/plugins/thumbnail';
|
|
import lgFullscreen from 'lightgallery/plugins/fullscreen';
|
|
|
|
var _initialized = new WeakSet();
|
|
|
|
function isInitialized(el) {
|
|
if (_initialized.has(el)) return true;
|
|
return false;
|
|
}
|
|
|
|
function markInitialized(el) {
|
|
_initialized.add(el);
|
|
}
|
|
|
|
function buildExifHtml(exifStr, caption) {
|
|
if (!exifStr) return caption || '';
|
|
var parts = exifStr.split('|');
|
|
if (parts.length < 8) return caption || '';
|
|
var make = parts[0], model = parts[1], lensModel = parts[2];
|
|
var fNumber = parts[3], exposureTime = parts[4], iso = parts[5];
|
|
var focalLength = parts[6], focalLengthIn35mm = parts[7];
|
|
var hasAnyExif = make || model || lensModel || fNumber || exposureTime || iso || focalLength;
|
|
if (!hasAnyExif) return caption || '';
|
|
var html = '<div class="wi-lg-caption">' + (caption || '') + '</div>';
|
|
html += '<div class="wi-lg-exif">';
|
|
if (make || model) {
|
|
html += '<span class="wi-lg-exif__item wi-lg-exif__camera">' + (make ? make + ' ' : '') + model + '</span>';
|
|
}
|
|
if (lensModel) {
|
|
html += '<span class="wi-lg-exif__item wi-lg-exif__lens">' + lensModel + '</span>';
|
|
}
|
|
html += '<div class="wi-lg-exif__params">';
|
|
if (fNumber) html += '<span class="wi-lg-exif__item wi-lg-exif__aperture">f/' + fNumber + '</span>';
|
|
if (exposureTime) html += '<span class="wi-lg-exif__item wi-lg-exif__shutter">' + exposureTime + 's</span>';
|
|
if (iso) html += '<span class="wi-lg-exif__item wi-lg-exif__iso">ISO ' + iso + '</span>';
|
|
if (focalLength) {
|
|
var focalText = focalLength + 'mm';
|
|
if (focalLengthIn35mm && focalLengthIn35mm !== focalLength) {
|
|
focalText += ' (eq. ' + focalLengthIn35mm + 'mm)';
|
|
}
|
|
html += '<span class="wi-lg-exif__item wi-lg-exif__focal">' + focalText + '</span>';
|
|
}
|
|
html += '</div></div>';
|
|
return html;
|
|
}
|
|
|
|
function preprocessExif(el) {
|
|
var items = el.querySelectorAll('.wi-lightgallery-item[data-exif]');
|
|
items.forEach(function(item) {
|
|
var exifStr = item.getAttribute('data-exif');
|
|
if (!exifStr) return;
|
|
var caption = item.getAttribute('data-sub-html') || '';
|
|
var html = buildExifHtml(exifStr, caption);
|
|
if (html && html !== caption) {
|
|
item.setAttribute('data-sub-html', html);
|
|
}
|
|
});
|
|
}
|
|
|
|
function createLgConfig() {
|
|
return {
|
|
selector: '.wi-lightgallery-item',
|
|
plugins: [lgZoom, lgThumbnail, lgFullscreen],
|
|
speed: 280,
|
|
mode: 'lg-fade',
|
|
licenseKey: '0000-0000-0000-0000',
|
|
download: false,
|
|
counter: true,
|
|
preload: 2,
|
|
thumbWidth: 80,
|
|
thumbHeight: '60px',
|
|
thumbMargin: 4,
|
|
zoomFromOrigin: true,
|
|
actualSize: false,
|
|
showZoomInOutIcons: true,
|
|
toggleThumb: true,
|
|
allowMediaOverlap: true,
|
|
mobileSettings: {
|
|
controls: true,
|
|
showCloseIcon: true,
|
|
download: false,
|
|
},
|
|
};
|
|
}
|
|
|
|
function initLg(el) {
|
|
if (isInitialized(el)) return;
|
|
var items = el.querySelectorAll('.wi-lightgallery-item');
|
|
if (items.length === 0) return;
|
|
preprocessExif(el);
|
|
lightGallery(el, createLgConfig());
|
|
markInitialized(el);
|
|
}
|
|
|
|
function isMobile() {
|
|
return window.innerWidth < 768;
|
|
}
|
|
|
|
function initLightGallery() {
|
|
initContentPages();
|
|
initPhotosPage();
|
|
initMomentsPage();
|
|
|
|
document.addEventListener('click', function(e) {
|
|
var toggleBtn = e.target.closest('.lg-toggle-thumb');
|
|
if (toggleBtn) {
|
|
e.stopPropagation();
|
|
var lgOuter = toggleBtn.closest('.lg-outer');
|
|
if (lgOuter) {
|
|
lgOuter.classList.toggle('wi-thumb-shown');
|
|
}
|
|
}
|
|
}, true);
|
|
|
|
document.addEventListener('click', function(e) {
|
|
var link = e.target.closest('.wi-lightgallery-item');
|
|
if (link) {
|
|
setTimeout(function() {
|
|
var lgOuter = document.querySelector('.lg-outer');
|
|
if (lgOuter && !lgOuter.classList.contains('wi-thumb-shown') && !isMobile()) {
|
|
lgOuter.classList.add('wi-thumb-shown');
|
|
}
|
|
}, 350);
|
|
}
|
|
});
|
|
}
|
|
|
|
function initContentPages() {
|
|
var selectors = ['.wi-post__body', '.wi-page__body'];
|
|
selectors.forEach(function(sel) {
|
|
var els = document.querySelectorAll(sel);
|
|
els.forEach(function(el) {
|
|
processContentImages(el);
|
|
initLg(el);
|
|
});
|
|
});
|
|
}
|
|
|
|
function initPhotosPage() {
|
|
var allGroups = document.querySelector('.wi-photos-page__all-groups');
|
|
if (allGroups) {
|
|
var links = allGroups.querySelectorAll('.wi-photos-page__link');
|
|
links.forEach(function(link) {
|
|
link.classList.add('wi-lightgallery-item');
|
|
});
|
|
initLg(allGroups);
|
|
return;
|
|
}
|
|
|
|
var singleGrid = document.querySelector('.wi-photos-page__grid:not(.wi-photos-page__all-groups .wi-photos-page__grid)');
|
|
if (singleGrid) {
|
|
var links2 = singleGrid.querySelectorAll('.wi-photos-page__link');
|
|
links2.forEach(function(link) {
|
|
link.classList.add('wi-lightgallery-item');
|
|
});
|
|
initLg(singleGrid);
|
|
return;
|
|
}
|
|
|
|
var grids = document.querySelectorAll('.wi-photos-page__grid');
|
|
grids.forEach(function(el) {
|
|
processPhotosGrid(el);
|
|
initLg(el);
|
|
});
|
|
}
|
|
|
|
function initMomentsPage() {
|
|
var selectors = ['.wi-moments-page__media', '.wi-moments-page__card-media', '.wi-moments-page__masonry-media'];
|
|
selectors.forEach(function(sel) {
|
|
var els = document.querySelectorAll(sel);
|
|
els.forEach(function(el) {
|
|
processMomentsMedia(el);
|
|
initLg(el);
|
|
});
|
|
});
|
|
}
|
|
|
|
function processContentImages(container) {
|
|
var images = container.querySelectorAll('img');
|
|
images.forEach(function(img) {
|
|
var src = img.getAttribute('src');
|
|
if (!src) return;
|
|
|
|
var w = img.getAttribute('width');
|
|
var h = img.getAttribute('height');
|
|
if ((w && parseInt(w) < 50) || (h && parseInt(h) < 50)) return;
|
|
|
|
var className = (img.getAttribute('class') || '').toLowerCase();
|
|
if (/emoji|icon|avatar|logo|badge|smiley/.test(className)) return;
|
|
|
|
if (img.getAttribute('role') === 'presentation') return;
|
|
if (img.getAttribute('aria-hidden') === 'true') return;
|
|
|
|
var parentA = img.closest('a');
|
|
if (parentA) {
|
|
if (parentA.getAttribute('target') === '_blank') return;
|
|
parentA.classList.add('wi-lightgallery-item');
|
|
if (!parentA.getAttribute('data-src')) {
|
|
var href = parentA.getAttribute('href') || src;
|
|
parentA.setAttribute('data-src', href);
|
|
}
|
|
var alt = img.getAttribute('alt') || '';
|
|
if (alt && !parentA.getAttribute('data-sub-html')) {
|
|
parentA.setAttribute('data-sub-html', alt);
|
|
}
|
|
} else {
|
|
var a = document.createElement('a');
|
|
a.setAttribute('href', src);
|
|
a.setAttribute('data-src', src);
|
|
a.classList.add('wi-lightgallery-item');
|
|
var alt2 = img.getAttribute('alt') || '';
|
|
if (alt2) {
|
|
a.setAttribute('data-sub-html', alt2);
|
|
}
|
|
img.parentNode.insertBefore(a, img);
|
|
a.appendChild(img);
|
|
}
|
|
});
|
|
}
|
|
|
|
function processPhotosGrid(container) {
|
|
var links = container.querySelectorAll('.wi-photos-page__link');
|
|
links.forEach(function(link) {
|
|
link.classList.add('wi-lightgallery-item');
|
|
});
|
|
}
|
|
|
|
function processMomentsMedia(container) {
|
|
// 注意:不要用 <a> 包裹瞬间图片。九宫格布局依赖
|
|
// .wi-moments-page__card-media 的直接子元素数量(:has/:nth-child/:only-child),
|
|
// 每张图各自包一层 <a> 后,img 永远是「唯一子元素」,全部图片都会退化成单列大图。
|
|
// 直接把 img 本身作为 lightgallery 项即可(data-src 提供原图)。
|
|
var images = container.querySelectorAll('img');
|
|
images.forEach(function(img) {
|
|
var src = img.getAttribute('src');
|
|
if (!src) return;
|
|
img.classList.add('wi-lightgallery-item');
|
|
if (!img.getAttribute('data-src')) {
|
|
img.setAttribute('data-src', src);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
if ('requestIdleCallback' in window) {
|
|
requestIdleCallback(initLightGallery, { timeout: 2000 });
|
|
} else {
|
|
setTimeout(initLightGallery, 200);
|
|
}
|
|
});
|
|
} else {
|
|
if ('requestIdleCallback' in window) {
|
|
requestIdleCallback(initLightGallery, { timeout: 2000 });
|
|
} else {
|
|
setTimeout(initLightGallery, 200);
|
|
}
|
|
}
|
|
</script>
|