172 lines
5.7 KiB
JavaScript
172 lines
5.7 KiB
JavaScript
const DEFAULT_BRAND_LANGUAGE = "zh";
|
|
const EARTH_BRAND_API = "/api/v1/earth/brand";
|
|
|
|
const BRANDS = {
|
|
zh: {
|
|
ariaLabel: "智能星球计划品牌标识",
|
|
titleAlt: "智能星球计划",
|
|
logoSrc: "/earth/assets/brand/earth-logo.png",
|
|
titleSrc: "/earth/assets/brand/title-zh.png",
|
|
titleText: "智能星球计划",
|
|
subtitle: "现实层宇宙全息感知系统",
|
|
description: "卫星 · 海底光缆 · 算力基础设施",
|
|
},
|
|
en: {
|
|
ariaLabel: "Intelligent Planet Program brand banner",
|
|
titleAlt: "Intelligent Planet Program",
|
|
logoSrc: "/earth/assets/brand/earth-logo.png",
|
|
titleSrc: "/earth/assets/brand/title-en.png",
|
|
titleText: "Intelligent Planet Program",
|
|
subtitle: "Reality Layer Situational Awareness System",
|
|
description: "Satellites · Subsea Cables · Compute Infrastructure",
|
|
},
|
|
};
|
|
|
|
const DEFAULT_BRAND_TITLE_BY_VARIANT = {
|
|
zh: BRANDS.zh.titleSrc,
|
|
en: BRANDS.en.titleSrc,
|
|
};
|
|
|
|
const LOCALIZED_FIELD_NAMES = {
|
|
ariaLabel: ["aria_label", "ariaLabel"],
|
|
titleAlt: ["title_alt", "titleAlt"],
|
|
titleSrc: ["title_src", "titleSrc"],
|
|
titleText: ["title_text", "titleText"],
|
|
subtitle: ["subtitle"],
|
|
description: ["description"],
|
|
};
|
|
|
|
export function getDefaultBrandConfig(variant = DEFAULT_BRAND_LANGUAGE) {
|
|
return BRANDS[variant] ?? BRANDS[DEFAULT_BRAND_LANGUAGE];
|
|
}
|
|
|
|
function escapeHtml(value = "") {
|
|
return String(value)
|
|
.replace(/&/g, "&")
|
|
.replace(/</g, "<")
|
|
.replace(/>/g, ">")
|
|
.replace(/"/g, """)
|
|
.replace(/'/g, "'");
|
|
}
|
|
|
|
function hasCjkText(value = "") {
|
|
return /[\u3400-\u9fff]/.test(String(value ?? ""));
|
|
}
|
|
|
|
function readConfigValue(config, keys = []) {
|
|
for (const key of keys) {
|
|
if (config[key] !== undefined && config[key] !== null && config[key] !== "") {
|
|
return config[key];
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function readLocalizedConfigValue(config, fieldName, variant, fallback) {
|
|
const keys = LOCALIZED_FIELD_NAMES[fieldName] || [fieldName];
|
|
const localeSuffix = variant === "en" ? "en" : "zh";
|
|
const localeKeys = keys.flatMap((key) => [
|
|
`${key}_${localeSuffix}`,
|
|
`${key}${localeSuffix.charAt(0).toUpperCase()}${localeSuffix.slice(1)}`,
|
|
]);
|
|
const localized = readConfigValue(config, localeKeys);
|
|
if (localized !== undefined) return localized;
|
|
const generic = readConfigValue(config, keys);
|
|
return generic ?? fallback;
|
|
}
|
|
|
|
function normalizeBrandConfig(config = {}, variant = DEFAULT_BRAND_LANGUAGE) {
|
|
const defaults = getDefaultBrandConfig(variant);
|
|
const sourceTitleSrc = readLocalizedConfigValue(config, "titleSrc", variant, undefined);
|
|
const normalized = {
|
|
...defaults,
|
|
...config,
|
|
ariaLabel: readLocalizedConfigValue(config, "ariaLabel", variant, defaults.ariaLabel),
|
|
titleAlt: readLocalizedConfigValue(config, "titleAlt", variant, defaults.titleAlt),
|
|
logoSrc: config.logo_src ?? config.logoSrc ?? defaults.logoSrc,
|
|
titleSrc: sourceTitleSrc ?? defaults.titleSrc,
|
|
titleText: readLocalizedConfigValue(config, "titleText", variant, defaults.titleText),
|
|
subtitle: readLocalizedConfigValue(config, "subtitle", variant, defaults.subtitle),
|
|
description: readLocalizedConfigValue(config, "description", variant, defaults.description),
|
|
};
|
|
|
|
if (
|
|
variant === "en" &&
|
|
(
|
|
!sourceTitleSrc ||
|
|
sourceTitleSrc === DEFAULT_BRAND_TITLE_BY_VARIANT.zh ||
|
|
hasCjkText(normalized.titleText) ||
|
|
hasCjkText(normalized.titleAlt) ||
|
|
hasCjkText(normalized.ariaLabel)
|
|
)
|
|
) {
|
|
normalized.titleSrc = DEFAULT_BRAND_TITLE_BY_VARIANT.en;
|
|
normalized.titleAlt = defaults.titleAlt;
|
|
normalized.titleText = defaults.titleText;
|
|
normalized.ariaLabel = defaults.ariaLabel;
|
|
normalized.subtitle = defaults.subtitle;
|
|
normalized.description = defaults.description;
|
|
}
|
|
|
|
if (!normalized.titleText) normalized.titleText = defaults.titleText;
|
|
if (!normalized.ariaLabel) normalized.ariaLabel = normalized.titleText;
|
|
if (!normalized.titleAlt) normalized.titleAlt = normalized.titleText;
|
|
return normalized;
|
|
}
|
|
|
|
export function renderBrand(configOrVariant = DEFAULT_BRAND_LANGUAGE) {
|
|
const variant =
|
|
typeof configOrVariant === "string"
|
|
? configOrVariant
|
|
: configOrVariant.variant ?? DEFAULT_BRAND_LANGUAGE;
|
|
const config =
|
|
typeof configOrVariant === "string"
|
|
? normalizeBrandConfig({}, variant)
|
|
: normalizeBrandConfig(configOrVariant, variant);
|
|
const titleMarkup = config.titleSrc
|
|
? `
|
|
<img
|
|
class="earth-brand__title"
|
|
src="${escapeHtml(config.titleSrc)}"
|
|
alt="${escapeHtml(config.titleAlt)}"
|
|
fetchpriority="high"
|
|
loading="eager"
|
|
>
|
|
`
|
|
: `<div class="earth-brand__title-text">${escapeHtml(config.titleText)}</div>`;
|
|
|
|
return `
|
|
<div class="earth-brand earth-brand--${escapeHtml(variant)}" aria-label="${escapeHtml(config.ariaLabel)}">
|
|
<img
|
|
class="earth-brand__logo"
|
|
src="${escapeHtml(config.logoSrc)}"
|
|
alt=""
|
|
aria-hidden="true"
|
|
fetchpriority="high"
|
|
loading="eager"
|
|
>
|
|
<div class="earth-brand__copy">
|
|
${titleMarkup}
|
|
<div class="earth-brand__meta">
|
|
<span class="earth-brand__subtitle">${escapeHtml(config.subtitle)}</span>
|
|
<span class="earth-brand__description">${escapeHtml(config.description)}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`.trim();
|
|
}
|
|
|
|
export function mountBrand(target, configOrVariant = DEFAULT_BRAND_LANGUAGE) {
|
|
if (!target) return;
|
|
target.innerHTML = renderBrand(configOrVariant);
|
|
}
|
|
|
|
export async function fetchEarthBrandConfig() {
|
|
const response = await fetch(EARTH_BRAND_API, { cache: "no-store" });
|
|
if (!response.ok) {
|
|
throw new Error(`Earth brand config request failed: ${response.status}`);
|
|
}
|
|
const payload = await response.json();
|
|
return payload?.brand ?? getDefaultBrandConfig();
|
|
}
|