import { GreasyforkApi } from "@/api/GreasyForkApi"; import { $, $$, addStyle, DOMUtils, log, pops, utils } from "@/env"; import { GreasyforkRouter } from "@/router/GreasyforkRouter"; import { GreasyforkElementUtils } from "@/utils/GreasyforkElementUtils"; import { GreasyforkUrlUtils } from "@/utils/GreasyforkUrlUtils"; import { Panel } from "@components/setting/panel"; import { PanelUISize } from "@components/setting/panel-ui-size"; import { CommonUtil } from "@components/utils/CommonUtil"; import i18next from "i18next"; import Qmsg from "qmsg"; import Viewer from "viewerjs"; import ViewerCSS from "viewerjs/dist/viewer.css?raw"; import { GM_getResourceText, GM_getValue, GM_setValue } from "ViteGM"; import { GreasyforkBeautify } from "./beautify/GreasyforkBeautify"; import "./css/OwnCSS.css"; import { GreasyforkAccount } from "./GreasyforkAccount"; import { GreasyforkMenu } from "./GreasyforkMenu"; import { GreasyforkRememberFormTextArea } from "./GreasyforkRememberFormTextArea"; import { GreasyforkShortCut } from "./GreasyforkShortCut"; import { GreasyforkForum as GreasyforkDiscussions } from "./navigator/discussions/GreasyforkDiscussions"; import { GreasyforkScripts } from "./navigator/scripts/GreasyforkScripts"; import { GreasyforkScriptsList } from "./navigator/scripts/GreasyforkScriptsList"; import { GreasyforkScriptsSearch } from "./navigator/search/GreasyforkScriptsSearch"; import { GreasyforkConversations } from "./navigator/users/conversations/GreasyforkConversations"; import { GreasyforkUsers } from "./navigator/users/GreasyforkUsers"; const Greasyfork = { init() { if (GreasyforkRouter.isImageSource()) { log.info(`Router: 资源界面,不执行脚本功能`); return; } Panel.execMenu("checkPage", () => { this.checkPage(); }); GreasyforkBeautify.init(); GreasyforkShortCut.init(); if (GreasyforkRouter.isScript()) { GreasyforkScripts.init(); } if ( GreasyforkRouter.isScriptList() || GreasyforkRouter.isScriptLibraryList() || GreasyforkRouter.isScriptCodeSearch() || GreasyforkRouter.isScriptsBySite() ) { GreasyforkScriptsList.init(); } if (GreasyforkRouter.isDiscuessions()) { log.info(`Router: 讨论页面`); GreasyforkDiscussions.init(); } else if (GreasyforkRouter.isUsers()) { log.info(`Router: 用户页面`); GreasyforkUsers.init(); if (GreasyforkRouter.isUsersConversations()) { log.info(`Router-next: 私聊用户页面`); GreasyforkConversations.init(); } } else if ( GreasyforkRouter.isScriptSearch() || GreasyforkRouter.isScriptsBySite() || GreasyforkRouter.isScriptCodeSearch() || GreasyforkRouter.isScriptLibraryListSearch() ) { log.info(`Router: 脚本搜索页面`); GreasyforkScriptsSearch.init(); } Panel.execMenuOnce("scripts-addOperationPanelBtnWithNavigator", () => { this.addOperationPanelBtnWithNavigator(); }); DOMUtils.onReady(() => { GreasyforkMenu.initEnv(); GreasyforkAccount.init(); GreasyforkRememberFormTextArea.init(); GreasyforkMenu.handleLocalGotoCallBack(); Panel.execMenuOnce("fixImageWidth", () => { return this.fixImageWidth(); }); this.languageSelectorLocale(); Panel.execMenuOnce("optimizeImageBrowsing", () => { this.optimizeImageBrowsing(); }); Panel.execMenuOnce("overlayBedImageClickEvent", () => { this.overlayBedImageClickEvent(); }); /* 不在/code页面添加Markdown复制按钮 */ if (!GreasyforkRouter.isCodeStrict()) { Panel.execMenuOnce("addMarkdownCopyButton", () => { this.addMarkdownCopyButton(); }); } Panel.execMenuOnce("queryUserRegisterTime", () => { return this.queryUserRegisterTime(); }); }); }, /** * 修复图片宽度显示问题 */ fixImageWidth() { if (window.innerWidth < window.innerHeight) { log.info("修复图片显示问题"); return addStyle(/*css*/ ` img.lum-img{ width: 100% !important; height: 100% !important; } `); } }, /** * 优化图片浏览 */ optimizeImageBrowsing() { log.info("优化图片浏览"); if (import.meta.env.DEV) { addStyle(ViewerCSS); } else { addStyle(GM_getResourceText("ViewerCSS")); } addStyle(/*css*/ ` @media (max-width: 460px) { .lum-lightbox-image-wrapper { display:flex; overflow: auto; -webkit-overflow-scrolling: touch } .lum-lightbox-caption { width: 100%; position: absolute; bottom: 0 } .lum-lightbox-position-helper { margin: auto } .lum-lightbox-inner img { max-width:100%; max-height:100%; } } `); /** * 查看图片 * @param imgList * @param viewIndex */ function viewIMG(imgList: string[] = [], viewIndex = 0) { let viewerULNodeHTML = ""; imgList.forEach((item) => { viewerULNodeHTML += `
元素也没有,只有
$code = $parent.querySelector("pre");
}
if (!$code) {
Qmsg.error(i18next.t("未找到{{selector}}元素", { selector: "code" }));
return;
}
utils.copy(DOMUtils.text($code));
clipboardCopyElement.setAttribute("success", "true");
octiconCopyElement.setAttribute("aria-hidden", "true");
octiconCheckCopyElement.removeAttribute("aria-hidden");
let tooltip = pops.tooltip({
$target: clipboardCopyElement,
content: i18next.t("✅ 复制成功!"),
position: "left",
className: "github-tooltip",
alwaysShow: true,
});
tooltip.toolTip.onToolTipAnimationFinishEvent();
setTimeout(() => {
clipboardCopyElement.removeAttribute("success");
octiconCheckCopyElement.setAttribute("aria-hidden", "true");
octiconCopyElement.removeAttribute("aria-hidden");
tooltip.toolTip.close();
}, 2000);
});
return $copy;
}
$$("pre").forEach(($pre) => {
const $zeroclipboard = $pre.querySelector("div.zeroclipboard-container");
if ($zeroclipboard) {
return;
}
const $copy = createCopyElement();
const $snippetClipboardContent = DOMUtils.createElement("div", {
className: "snippet-clipboard-content",
});
DOMUtils.before($pre, $snippetClipboardContent);
$snippetClipboardContent.appendChild($pre);
$snippetClipboardContent.appendChild($copy);
});
},
/**
* 固定当前语言
*/
languageSelectorLocale() {
const localeLanguage = Panel.getValue("language-selector-locale");
const currentLocaleLanguage = window.location.pathname.split("/").filter((item) => Boolean(item))[0];
log.success("选择语言:" + localeLanguage);
log.success("当前语言:" + currentLocaleLanguage);
if (utils.isNull(localeLanguage)) {
return;
}
if (localeLanguage === currentLocaleLanguage) {
return;
} else {
let timer = null as any;
let url = GreasyforkUrlUtils.getSwitchLanguageUrl(localeLanguage);
GreasyforkApi.switchLanguage(url);
log.success("新Url:" + url);
Qmsg.loading(
i18next.t("当前语言:{{currentLocaleLanguage}},,3秒后切换至:{{localeLanguage}}", {
currentLocaleLanguage,
localeLanguage,
}),
{
timeout: 3000,
showClose: true,
onClose() {
clearTimeout(timer);
},
}
);
Qmsg.info(i18next.t("导航至:") + url, {
timeout: 3000,
});
timer = setTimeout(() => {
window.location.href = url;
}, 3000);
}
},
/**
* 检测gf页面是否正确加载,有时候会出现
* We're down for maintenance. Check back again soon.
*/
checkPage() {
log.info("检测gf页面是否正确加载,有时候会出现");
DOMUtils.onReady(() => {
if (
(document.body.firstElementChild as any) &&
(document.body.firstElementChild as any).localName === "p" &&
(document.body.firstElementChild as any).innerText.includes(
"We're down for maintenance. Check back again soon."
)
) {
// 先获取上一次刷新页面的时间
let latestRefreshPageTime = parseInt(GM_getValue("greasyfork-check-page-time", 0) as string);
let checkPageTimeout = Panel.getValue("greasyfork-check-page-timeout", 5);
let checkPageTimeoutStamp = checkPageTimeout * 1000;
if (latestRefreshPageTime && Date.now() - latestRefreshPageTime < checkPageTimeoutStamp) {
/* 上次重载时间在xx秒内的话就拒绝重载 */
Qmsg.warning(
i18next.t("上次重载时间 {{time}},{{timeout}}秒内拒绝反复重载", {
time: utils.formatTime(latestRefreshPageTime, "yyyy-MM-dd HH:mm:ss"),
timeout: checkPageTimeout,
})
);
return;
}
GM_setValue("greasyfork-check-page-time", Date.now());
window.location.reload();
}
});
},
/**
* 在顶部导航栏添加【操作面板】按钮
*/
addOperationPanelBtnWithNavigator() {
log.info("添加【操作面板】按钮");
// 隐藏右侧列表
CommonUtil.addBlockCSS(".sidebarred .sidebar", ".sidebarred-main-content .open-sidebar");
addStyle(/*css*/ `
.sidebarred .sidebarred-main-content{
max-width: 100%;
}
`);
DOMUtils.onReady(() => {
let $nav = $("#site-nav nav");
let $subNav = $("#site-nav .with-submenu nav");
// 右侧的过滤菜单
let $scriptsOptionGroups =
$("#script-list-option-groups")! || $(".list-option-groups")!;
if (!$scriptsOptionGroups) {
log.warn("不存在右侧面板元素#script-list-option-groups");
return;
}
$scriptsOptionGroups = $scriptsOptionGroups.cloneNode(true) as HTMLDivElement;
$scriptsOptionGroups.classList.add("option-panel-groups");
GreasyforkElementUtils.registerTopNavMenu({
name: i18next.t("操作面板"),
className: "filter-scripts",
clickEvent(event) {
let $dialog = pops.alert({
title: {
text: i18next.t("操作面板"),
position: "center",
},
content: {
text: "",
html: true,
},
btn: {
ok: { enable: false },
},
mask: {
enable: true,
clickEvent: {
toClose: true,
},
},
drag: true,
useShadowRoot: true,
width: PanelUISize.setting.width,
height: PanelUISize.setting.height,
zIndex: utils.getMaxZIndex(100),
style: /*css*/ `
.pops-drawer-content div:first-child{
margin: 20px;
}
.option-panel-groups > div{
}
.option-panel-groups ul{
margin: .5em 0 0;
list-style-type: none;
padding: 1em 0;
box-shadow: 0 0 5px #ddd;
border: 1px solid #BBBBBB;
border-radius: 5px;
background-color: #fff;
}
.option-panel-groups ul li{
}
li.list-current{
border-left: 7px solid #800;
box-shadow: inset 0 1px #0000001a, inset 0 -1px #0000001a;
margin: 0 0 0 -4px;
padding: .4em 1em .4em calc(1em - 3px);
background: linear-gradient(#fff, #eee);
}
.list-option-group a {
padding: .35em 1em;
display: block;
}
.list-option-group {
margin-bottom: 1em;
}
form.sidebar-search{
display: flex;
align-items: center;
gap: 10px;
}
form.sidebar-search input[type="search"]{
display: inline-flex;
justify-content: center;
align-items: center;
line-height: normal;
height: 32px;
white-space: nowrap;
cursor: text;
text-align: center;
box-sizing: border-box;
outline: 0;
transition: 0.1s;
font-weight: 500;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
vertical-align: middle;
-webkit-appearance: none;
appearance: none;
background-color: transparent;
border: 0;
padding: 8px 8px;
font-size: 14px;
text-align: start;
/* width: 100%; */
// flex: 1;
display: flex;
align-items: center;
border: 1px solid #dcdfe6;
border-radius: 4px;
background-color: #ffffff;
}
form.sidebar-search input[type="submit"]{
width: 32px;
height: 32px;
}
`,
});
let $content = $dialog.$shadowRoot.querySelector(".pops-alert-content")!;
$content.appendChild($scriptsOptionGroups);
},
});
});
},
/**
* 查询用户注册时间
*/
queryUserRegisterTime() {
const userRegisterTimeMap = new Map<
string,
{
name: string;
formatTime: string;
}
>();
/**
* 更新
*/
const updateUserRegisterTime = (option: { userId: string; formatTime: string; name: string }) => {
$$(`.query-wrapper[data-user-id='${option.userId}']`).forEach(($wrapper) => {
const $userLink = DOMUtils.prev($wrapper);
$wrapper.remove();
DOMUtils.text(
$userLink,
i18next.t("{{name}} {{leftBracket}}{{created_at}}{{rightBracket}}", {
name: option.name,
created_at: option.formatTime,
leftBracket: i18next.t("("),
rightBracket: i18next.t(")"),
})
);
});
};
const lockFn = new utils.LockFunction(() => {
$$("a[href*='/users/']:not(:has(+.query-wrapper))").forEach(($userLink) => {
if ($userLink.closest("#nav-user-info")) return;
const userHomeUrl = $userLink.href;
const isUserLink = $userLink.classList.contains("user-link");
// 在用户主页内的其它链接
if (userHomeUrl.match(/\/users\/.+\/.+/) && !isUserLink) return;
// 私信链接
if (userHomeUrl.match(/\#message-[\d]+$/)) return;
const userId = GreasyforkUrlUtils.getUserId(userHomeUrl);
if (userId == null) return;
if (userRegisterTimeMap.has(userId)) {
// 该用户已查询过注册时间
// 直接更新
const data = userRegisterTimeMap.get(userId)!;
updateUserRegisterTime({ userId, ...data });
return;
}
const $query = DOMUtils.createElement(
"div",
{
className: "query-wrapper",
innerHTML: /*html*/ `
`,
},
{
"data-user-id": userId,
}
);
DOMUtils.after($userLink, $query);
});
});
const observer = utils.mutationObserver(document, {
config: {
subtree: true,
childList: true,
},
immediate: true,
callback: () => {
lockFn.run();
},
});
const listener = DOMUtils.on(document, "click", ".query-badge-control", async (evt, selectorTarget) => {
DOMUtils.preventEvent(evt);
const $wrapper = DOMUtils.parent(selectorTarget);
const userId = $wrapper.getAttribute("data-user-id");
if (!userId) {
Qmsg.error(i18next.t("获取用户ID失败"));
return;
}
const $msg = $wrapper.querySelector(".query-msg")!;
DOMUtils.text($msg, i18next.t("查询中..."));
const userInfo = await GreasyforkApi.getUserInfo(userId);
if (!userInfo) {
DOMUtils.text($msg, i18next.t("查询失败"));
return;
}
DOMUtils.empty($msg);
const { name, created_at } = userInfo;
const formatTime = utils.formatTime(created_at, i18next.t("yyyy年MM月dd日 HH:mm:ss"));
// 将查询结果临时保存在内存中
userRegisterTimeMap.set(userId, {
name,
formatTime,
});
updateUserRegisterTime({ userId, name, formatTime });
});
return [
() => {
observer.disconnect();
listener.off();
DOMUtils.remove(".query-wrapper");
userRegisterTimeMap.clear();
},
addStyle(/*css*/ `
.query-wrapper{
padding: 4px 0px;
}
.query-wrapper{
display: inline-flex;
vertical-align: middle;
flex-wrap: wrap;
align-items: center;
}
/* 查询按钮 */
.query-wrapper .query-badge-control {
display: inline-flex;
justify-content: center;
align-items: center;
width: fit-content;
height: fit-content;
background: #574AB830;
border-radius: 8px;
margin: 0 0 0 6px;
font-family: PingFang SC, HarmonyOS_Regular, Helvetica Neue, Microsoft YaHei, sans-serif;
}
.query-wrapper .query-icon {
color: #7367F0;
padding: 2px 8px;
font-size: 14px;
display: flex;
align-items: center;
height: 20px;
line-height: normal;
}
.query-wrapper .query-icon svg {
vertical-align: middle;
width: 16px;
height: 16px;
}
.query-wrapper .query-msg:not(:empty){
padding-right: 8px;
}
/* ↑查询按钮 */
`),
];
},
};
export { Greasyfork };