Files
xget-now/popup.js
T
xixu-me 95bb7d56f9 Refactor and document build, dev, and extension scripts
Added detailed docstrings and comments to Python build and dev scripts, improving maintainability and clarity. Enhanced JavaScript files (background.js, content.js, popup.js, platform-detector.js, webext-compat.js) with module-level and function-level documentation. Standardized package naming in build.py and updated the GitHub Actions workflow for simplified, platform-agnostic publishing, removing platform selection input and automating release steps for Chrome, Edge, and Firefox. Improved code quality checks and test coverage in dev.py.
2025-08-10 18:43:32 +08:00

426 lines
12 KiB
JavaScript

/**
* Xget Now - 弹窗脚本
*
* 功能:
* - 管理扩展设置界面
* - 处理用户交互事件
* - 同步设置到后台脚本
* - 提供平台开关控制
*/
document.addEventListener("DOMContentLoaded", async () => {
// 确保兼容层可用
if (typeof webext === "undefined") {
console.error("WebExt compatibility layer not found in popup");
return;
}
// 加载当前设置
await loadSettings();
// 设置事件监听器
setupEventListeners();
});
/**
* 加载并应用当前设置到 UI
*/
async function loadSettings() {
try {
const settings = await webext.runtime.sendMessage({
action: "getSettings",
});
// 使用当前设置更新 UI
const domainValue = settings.xgetDomain || "";
document.getElementById("domainInput").value = domainValue;
// 仅在设置了域名时启用切换
const enabledValue = settings.enabled && domainValue.trim() !== "";
document.getElementById("enabledToggle").checked = enabledValue;
// 更新平台切换状态
const platformToggles = {
// 代码托管平台
gh: document.getElementById("ghToggle"),
gl: document.getElementById("glToggle"),
gitea: document.getElementById("giteaToggle"),
codeberg: document.getElementById("codebergToggle"),
sf: document.getElementById("sfToggle"),
aosp: document.getElementById("aospToggle"),
// AI/ML 平台
hf: document.getElementById("hfToggle"),
// 包管理平台
npm: document.getElementById("npmToggle"),
pypi: document.getElementById("pypiToggle"),
conda: document.getElementById("condaToggle"),
maven: document.getElementById("mavenToggle"),
rubygems: document.getElementById("rubygemsToggle"),
crates: document.getElementById("cratesToggle"),
nuget: document.getElementById("nugetToggle"),
golang: document.getElementById("golangToggle"),
// 其他平台
arxiv: document.getElementById("arxivToggle"),
fdroid: document.getElementById("fdroidToggle"),
};
// 设置平台切换状态
Object.entries(platformToggles).forEach(([platform, toggle]) => {
if (
toggle &&
settings.enabledPlatforms &&
settings.enabledPlatforms[platform] !== undefined
) {
toggle.checked = settings.enabledPlatforms[platform];
}
});
// 如果缺少域名则显示状态
if (!domainValue && settings.enabled) {
showStatus("请配置你的 Xget 域名", "error");
} else if (domainValue && enabledValue) {
showStatus("扩展已激活并准备就绪", "success");
}
} catch (error) {
console.error("加载设置时出错:", error);
showStatus("加载设置时出错", "error");
}
}
function setupEventListeners() {
// 启用/禁用切换
document
.getElementById("enabledToggle")
.addEventListener("change", handleEnableToggle);
// 域名输入
document
.getElementById("domainInput")
.addEventListener("input", debounce(handleDomainChange, 500));
document
.getElementById("domainInput")
.addEventListener("blur", validateDomain);
// 平台切换
const platformToggles = [
"ghToggle",
"glToggle",
"giteaToggle",
"codebergToggle",
"sfToggle",
"aospToggle",
"hfToggle",
"npmToggle",
"pypiToggle",
"condaToggle",
"mavenToggle",
"rubygemsToggle",
"cratesToggle",
"nugetToggle",
"golangToggle",
"arxivToggle",
"fdroidToggle",
];
platformToggles.forEach((toggleId) => {
const element = document.getElementById(toggleId);
if (element) {
element.addEventListener("change", saveSettings);
}
});
// 滚动优化逻辑
setupScrollOptimization();
}
function setupScrollOptimization() {
const contentElement = document.querySelector(".content");
const platformsElement = document.querySelector(".platforms");
// 为主内容区域添加滚动优化
if (contentElement) {
let scrollTimeout;
contentElement.addEventListener("scroll", () => {
// 添加滚动中的视觉反馈
contentElement.style.scrollbarWidth = "auto";
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(() => {
// 滚动结束后隐藏滚动条(仅在不悬停时)
if (!contentElement.matches(":hover")) {
contentElement.style.scrollbarWidth = "thin";
}
}, 1000);
});
// 鼠标悬停时显示滚动条
contentElement.addEventListener("mouseenter", () => {
contentElement.style.scrollbarWidth = "auto";
});
contentElement.addEventListener("mouseleave", () => {
contentElement.style.scrollbarWidth = "thin";
});
}
// 为平台列表添加滚动优化
if (platformsElement) {
let scrollTimeout;
platformsElement.addEventListener("scroll", () => {
platformsElement.style.scrollbarWidth = "auto";
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(() => {
if (!platformsElement.matches(":hover")) {
platformsElement.style.scrollbarWidth = "thin";
}
}, 1000);
});
platformsElement.addEventListener("mouseenter", () => {
platformsElement.style.scrollbarWidth = "auto";
});
platformsElement.addEventListener("mouseleave", () => {
platformsElement.style.scrollbarWidth = "thin";
});
}
// 添加键盘导航支持
document.addEventListener("keydown", (e) => {
const activeElement = document.activeElement;
const isInScrollableArea =
activeElement &&
(contentElement.contains(activeElement) ||
platformsElement.contains(activeElement));
if (isInScrollableArea) {
switch (e.key) {
case "ArrowUp":
if (e.ctrlKey) {
e.preventDefault();
const scrollable = contentElement.contains(activeElement)
? contentElement
: platformsElement;
scrollable.scrollBy({ top: -50, behavior: "smooth" });
}
break;
case "ArrowDown":
if (e.ctrlKey) {
e.preventDefault();
const scrollable = contentElement.contains(activeElement)
? contentElement
: platformsElement;
scrollable.scrollBy({ top: 50, behavior: "smooth" });
}
break;
case "Home":
if (e.ctrlKey) {
e.preventDefault();
const scrollable = contentElement.contains(activeElement)
? contentElement
: platformsElement;
scrollable.scrollTo({ top: 0, behavior: "smooth" });
}
break;
case "End":
if (e.ctrlKey) {
e.preventDefault();
const scrollable = contentElement.contains(activeElement)
? contentElement
: platformsElement;
scrollable.scrollTo({
top: scrollable.scrollHeight,
behavior: "smooth",
});
}
break;
}
}
});
}
async function saveSettings() {
try {
const domainValue = document.getElementById("domainInput").value.trim();
const enabledValue = document.getElementById("enabledToggle").checked;
// 如果没有设置域名则防止启用
if (enabledValue && !domainValue) {
document.getElementById("enabledToggle").checked = false;
showStatus("没有域名不能启用扩展", "error");
return;
}
const settings = {
enabled: document.getElementById("enabledToggle").checked,
xgetDomain: domainValue,
enabledPlatforms: {
// 代码托管平台
gh: document.getElementById("ghToggle")?.checked || false,
gl: document.getElementById("glToggle")?.checked || false,
gitea: document.getElementById("giteaToggle")?.checked || false,
codeberg: document.getElementById("codebergToggle")?.checked || false,
sf: document.getElementById("sfToggle")?.checked || false,
aosp: document.getElementById("aospToggle")?.checked || false,
// AI/ML 平台
hf: document.getElementById("hfToggle")?.checked || false,
// 包管理平台
npm: document.getElementById("npmToggle")?.checked || false,
pypi: document.getElementById("pypiToggle")?.checked || false,
"pypi-files": true, // 默认开启
conda: document.getElementById("condaToggle")?.checked || false,
"conda-community": true, // 默认开启
maven: document.getElementById("mavenToggle")?.checked || false,
apache: true, // 默认开启
gradle: true, // 默认开启
rubygems: document.getElementById("rubygemsToggle")?.checked || false,
cran: true, // 默认开启
cpan: true, // 默认开启
ctan: true, // 默认开启
golang: document.getElementById("golangToggle")?.checked || false,
nuget: document.getElementById("nugetToggle")?.checked || false,
crates: document.getElementById("cratesToggle")?.checked || false,
packagist: true, // 默认开启
// 其他平台
arxiv: document.getElementById("arxivToggle")?.checked || false,
fdroid: document.getElementById("fdroidToggle")?.checked || false,
},
};
// 清理域名 URL
if (settings.xgetDomain) {
settings.xgetDomain = cleanupDomain(settings.xgetDomain);
}
const response = await webext.runtime.sendMessage({
action: "saveSettings",
settings: settings,
});
if (response.success) {
// 显示适当的状态
if (!settings.xgetDomain && settings.enabled) {
showStatus("请配置你的 Xget 域名", "error");
} else if (settings.xgetDomain && settings.enabled) {
showStatus("✅ 设置已保存!查看页面通知中的刷新按钮", "success");
} else {
showStatus("✅ 设置已保存!查看页面通知中的刷新按钮", "success");
}
}
} catch (error) {
console.error("保存设置时出错:", error);
showStatus("保存设置时出错", "error");
}
}
function validateDomain() {
const domain = document.getElementById("domainInput").value.trim();
if (domain && !isValidDomain(domain)) {
showStatus("域名格式无效", "error");
}
}
function cleanupDomain(domain) {
// 如果意外包含协议,则删除它
domain = domain.replace(/^https?:\/\//, "");
// 删除末尾斜杠
domain = domain.replace(/\/$/, "");
return domain;
}
function isValidDomain(domain) {
// 如果存在协议则删除它
domain = domain.replace(/^https?:\/\//, "");
// 基本域名验证模式
const domainPattern =
/^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
return domainPattern.test(domain) && domain.length <= 253;
}
function isValidUrl(string) {
try {
new URL(string);
return true;
} catch (_) {
return false;
}
}
function showStatus(message, type) {
const statusElement = document.getElementById("status");
statusElement.textContent = message;
statusElement.className = `status ${type}`;
statusElement.style.display = "block";
statusElement.classList.remove("hiding");
// 自动隐藏成功消息
if (type === "success") {
setTimeout(hideStatus, 3000);
}
}
function hideStatus() {
const statusElement = document.getElementById("status");
statusElement.classList.add("hiding");
statusElement.addEventListener(
"animationend",
() => {
statusElement.style.display = "none";
statusElement.classList.remove("hiding");
},
{ once: true }
);
}
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
async function handleEnableToggle() {
const enabledToggle = document.getElementById("enabledToggle");
const domainInput = document.getElementById("domainInput");
if (enabledToggle.checked && !domainInput.value.trim()) {
// 如果没有设置域名则防止启用
enabledToggle.checked = false;
showStatus("启用前请配置你的 Xget 域名", "error");
return;
}
await saveSettings();
}
async function handleDomainChange() {
const domainInput = document.getElementById("domainInput");
const enabledToggle = document.getElementById("enabledToggle");
// 如果域名被清空且扩展已启用,则禁用它
if (!domainInput.value.trim() && enabledToggle.checked) {
enabledToggle.checked = false;
showStatus("扩展已禁用:域名已清空", "error");
}
await saveSettings();
}