This commit is contained in:
2026-05-05 07:13:54 +08:00
Unverified
parent 759e1fad0d
commit 82210681c0
20 changed files with 4145 additions and 0 deletions
+175
View File
@@ -0,0 +1,175 @@
(function () {
var currentScript = document.currentScript;
var source = currentScript && currentScript.getAttribute("data-notice-src");
if (!source && currentScript && currentScript.src) {
source = currentScript.src.replace(/js\/notice-banner\.js(?:\?.*)?$/, "notices.json");
}
if (!source) {
source = "notices.json";
}
var cacheKey = Math.floor(Date.now() / (5 * 60 * 1000));
var joiner = source.indexOf("?") === -1 ? "?" : "&";
fetch(source + joiner + "v=" + cacheKey, { cache: "no-store" })
.then(function (response) {
if (!response.ok) {
throw new Error("Notice source unavailable");
}
return response.json();
})
.then(function (payload) {
var notice = selectNotice(payload);
if (notice) {
renderNotice(notice);
}
})
.catch(function () {
// The page should stay quiet when the optional notice source is missing.
});
function selectNotice(payload) {
var notices = Array.isArray(payload) ? payload : payload && payload.notices;
if (!Array.isArray(notices)) {
return null;
}
var now = Date.now();
var path = normalizePath(window.location.pathname);
return notices.find(function (notice) {
if (!notice || notice.enabled === false) {
return false;
}
if (notice.startsAt && Date.parse(notice.startsAt) > now) {
return false;
}
if (notice.endsAt && Date.parse(notice.endsAt) < now) {
return false;
}
if (notice.pages && notice.pages.length) {
return notice.pages.some(function (page) {
return pathMatches(path, normalizePath(page));
});
}
return true;
});
}
function renderNotice(notice) {
var dismissedId = window.localStorage.getItem("dismissedNoticeId");
var noticeId = String(notice.id || "");
if (notice.dismissible !== false && noticeId && dismissedId === noticeId) {
return;
}
var body = document.body;
var originalPadding = window.getComputedStyle(body).paddingTop || "0px";
body.style.setProperty("--notice-body-pad-top", originalPadding);
var banner = document.createElement("div");
banner.className = "notice-banner";
banner.setAttribute("role", "status");
banner.setAttribute("aria-live", "polite");
banner.dataset.level = notice.level || "info";
var inner = document.createElement("div");
inner.className = "notice-banner__inner";
var badge = document.createElement("span");
badge.className = "notice-banner__badge";
badge.textContent = notice.badge || levelLabel(notice.level);
var content = document.createElement("div");
content.className = "notice-banner__content";
if (notice.title) {
var title = document.createElement("span");
title.className = "notice-banner__title";
title.textContent = notice.title;
content.appendChild(title);
}
var message = document.createElement("span");
message.className = "notice-banner__message";
message.textContent = notice.message || "";
content.appendChild(message);
inner.appendChild(badge);
inner.appendChild(content);
if (notice.url) {
var link = document.createElement("a");
link.className = "notice-banner__link";
link.href = resolveUrl(notice.url, source);
link.textContent = notice.linkText || "查看详情";
inner.appendChild(link);
}
var close = document.createElement("button");
close.className = "notice-banner__close";
close.type = "button";
close.setAttribute("aria-label", "关闭通知");
close.textContent = "×";
close.addEventListener("click", function () {
if (notice.dismissible !== false && noticeId) {
window.localStorage.setItem("dismissedNoticeId", noticeId);
}
banner.remove();
body.classList.remove("has-notice-banner");
body.style.removeProperty("--notice-banner-height");
});
inner.appendChild(close);
banner.appendChild(inner);
body.insertBefore(banner, body.firstChild);
requestAnimationFrame(function () {
banner.classList.add("is-visible");
body.style.setProperty("--notice-banner-height", banner.offsetHeight + "px");
body.classList.add("has-notice-banner");
});
}
function levelLabel(level) {
var labels = {
warning: "提醒",
error: "紧急",
success: "完成"
};
return labels[level] || "通知";
}
function normalizePath(path) {
return String(path || "").replace(/\\/g, "/").replace(/^.*?:\/\/[^/]+/, "");
}
function resolveUrl(url, base) {
try {
return new URL(url, new URL(base, window.location.href)).href;
} catch (error) {
return url;
}
}
function pathMatches(path, pattern) {
if (!pattern || pattern === "*") {
return true;
}
if (pattern.charAt(pattern.length - 1) === "*") {
return path.indexOf(pattern.slice(0, -1)) === 0;
}
return path === pattern || path.endsWith(pattern);
}
})();