// ==UserScript==
// @name 0x3f-problem-solution
// @namespace https://greasyfork.org/zh-CN/users/1326420-wuxin0012
// @version 0.0.1
// @author wuxin0011
// @description 配合灵神题单解题
// @license MIT
// @icon https://assets.leetcode.cn/aliyun-lc-upload/users/endlesscheng/avatar_1690721039.png
// @source https://greasyfork.org/zh-CN/users/1326420-wuxin0012
// @match https://leetcode.cn/circle/discuss/*
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.global.prod.js
// @require data:application/javascript,%3Bwindow.Vue%3DVue%3B
// @require https://unpkg.com/[email protected]/dist/index.full.js
// @resource elementPlusCss https://unpkg.com/[email protected]/dist/index.css
// @grant GM_addStyle
// @grant GM_getResourceText
// @grant GM_getValue
// @grant GM_registerMenuCommand
// @grant GM_setValue
// ==/UserScript==
(t=>{if(typeof GM_addStyle=="function"){GM_addStyle(t);return}const e=document.createElement("style");e.textContent=t,document.head.append(e)})(" .m-setting-button[data-v-6d3b190f]{position:fixed;top:200px;right:0;z-index:100000} ");
(function (vue, ElementPlus) {
'use strict';
var _GM_getValue = /* @__PURE__ */ (() => typeof GM_getValue != "undefined" ? GM_getValue : void 0)();
var _GM_registerMenuCommand = /* @__PURE__ */ (() => typeof GM_registerMenuCommand != "undefined" ? GM_registerMenuCommand : void 0)();
var _GM_setValue = /* @__PURE__ */ (() => typeof GM_setValue != "undefined" ? GM_setValue : void 0)();
const inf = 4e3;
const mi = 1e3;
const __0x3f_problmes_solution__ = "__0x3f_problmes_solution__";
const __0x3f_problmes_urls__ = "__0x3f_problmes_urls__";
const __0x3f_problmes_update__ = "__0x3f_problmes_update__";
const __add_cur__ = "__add_cur__";
const Problems = () => Array.from(document.querySelectorAll(".css-1ayia3m-MarkdownContent li>a"));
const defaultObj = {
min: mi,
max: inf,
visiableMember: true,
onlyUrls: false,
useDefaultSetting: true
};
function isShow(text, min, max) {
if (!text) {
return true;
}
let res = text.match(/\d+/ig);
if (!res) {
return true;
}
if (Array.isArray(res) && res.length < 2) {
return true;
}
let s = 0;
for (let i = res.length - 1; i >= 0; i--) {
s = res[i];
if (s >= mi && s <= inf) {
return s >= min && s <= max;
}
}
return true;
}
let A = Problems();
function handlerProblem(data) {
var _a;
if (!Array.isArray(A) || Array.isArray(A) && A.length == 0) {
A = Problems();
}
let { min, max, visiableMember, useDefaultSetting, onlyUrls } = data;
if (isNaN(min) || isNaN(max)) {
min = mi;
max = inf;
}
if (min < mi) {
min = mi;
}
if (max < min) {
max = inf;
}
min = Number(min);
max = Number(max);
data.min = min;
data.max = max;
_GM_setValue(__0x3f_problmes_solution__, data);
for (let i = 0; i < A.length; i++) {
let d = (_a = A[i]) == null ? void 0 : _a.parentElement;
if (!d) {
continue;
}
let flag = isShow(d.textContent, min, max);
d.style.display = flag ? "" : "none";
let c = d.textContent && d.textContent.indexOf("会员") != -1;
if (!c) {
continue;
}
d.style.display = visiableMember ? "" : "none";
}
}
const initUrls = () => _GM_getValue(__0x3f_problmes_update__) ? Array.isArray(_GM_getValue(__0x3f_problmes_urls__)) ? _GM_getValue(__0x3f_problmes_urls__) : [] : defaultUrls;
const initObj = () => _GM_getValue(__0x3f_problmes_solution__) ? Object.assign(defaultObj, _GM_getValue(__0x3f_problmes_solution__)) : defaultObj;
const support_plugins = () => {
const u = initObj();
if (!u || !u.onlyUrls) return true;
const url = window.location.href;
const urls = initUrls();
for (let info of urls) {
if (!info || !(info == null ? void 0 : info.link)) {
continue;
}
console.log("url find", info.link.indexOf(url) != -1, info.link, url);
if (info.link.indexOf(url) != -1) {
return true;
}
}
return false;
};
const defaultUrls = [
{
title: "滑动窗口(定长/不定长/多指针)",
link: "https://leetcode.cn/circle/discuss/0viNMK/"
},
{
title: "二分算法(二分答案/最小化最大值/最大化最小值/第K小)",
link: "https://leetcode.cn/circle/discuss/SqopEo/"
},
{
title: "单调栈(矩形面积/贡献法/最小字典序)",
link: "https://leetcode.cn/circle/discuss/9oZFK9/"
},
{
title: "网格图(DFS/BFS/综合应用)",
link: "https://leetcode.cn/circle/discuss/YiXPXW/"
},
{
title: "位运算(基础/性质/拆位/试填/恒等式/贪心/脑筋急转弯)",
link: "https://leetcode.cn/circle/discuss/dHn9Vk/"
},
{
title: "图论算法(DFS/BFS/拓扑排序/最短路/最小生成树/二分图/基环树/欧拉路径)",
link: "https://leetcode.cn/circle/discuss/01LUak/"
},
{
title: "动态规划(入门/背包/状态机/划分/区间/状压/数位/树形/数据结构优化)",
link: "https://leetcode.cn/circle/discuss/tXLS3i/"
},
{
title: "常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)",
link: "https://leetcode.cn/circle/discuss/mOr1u6/"
},
{
title: "数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)",
link: "https://leetcode.cn/circle/discuss/IYT3ss/"
}
];
const _export_sfc = (sfc, props) => {
const target = sfc.__vccOpts || sfc;
for (const [key, val] of props) {
target[key] = val;
}
return target;
};
const _hoisted_1 = { class: "dialog-footer" };
const formLabelWidth = "44px";
const _sfc_main = {
__name: "App",
setup(__props) {
const drawer = vue.ref(false);
const fromData = vue.reactive(initObj());
vue.watch(fromData, () => {
handlerProblem(vue.toRaw(Object.assign({}, fromData)));
});
let tableData = vue.reactive(initUrls());
const keywords = vue.ref("");
const dialogTableVisible = vue.ref(false);
const urlsData = vue.computed(() => tableData.filter((v) => v && v.title && v.title.indexOf(keywords.value) != -1));
const dialogFormVisible = vue.ref(false);
const info = vue.reactive({
title: "",
link: "",
status: "add"
});
const addlocal = () => {
let ok = false;
for (let a of tableData) {
if (a && a.link.indexOf(window.location.href) != -1) {
ok = true;
}
}
if (ok) {
return;
}
tableData.unshift({ title: document.title, link: window.location.href });
};
const updateIndex = vue.ref(-1);
const showProblems = () => {
dialogTableVisible.value = true;
if (_GM_getValue(__add_cur__)) {
addlocal();
}
};
const handlerProblems = (status, updateInfo = { title: "", link: "" }, index = -1) => {
dialogFormVisible.value = true;
info.status = status;
updateIndex.value = index;
Object.assign(info, updateInfo);
};
const handlerMessage = (u, title, link) => {
const a = u ? "添加" : "修改";
const error = !title || !/https?:\/\/.*/.test(link);
if (error) {
ElementPlus.ElMessage.error(`${a} 失败 请保证标题或者链接有效 `);
} else {
ElementPlus.ElMessage.success(`${a} 成功 `);
}
return !error;
};
const addOrUpdate = () => {
if (!handlerMessage(info.status == "add", info.title, info.link)) {
return;
}
if (info.status == "add") {
tableData.unshift({ title: info.title, link: info.link });
} else {
let index = updateIndex.value;
if (index != -1 && index < tableData.length) {
tableData[index].link = info.link;
tableData[index].title = info.title;
}
}
dialogFormVisible.value = false;
};
const deleteProblems = (index) => {
tableData.splice(index, 1);
_GM_setValue(__0x3f_problmes_urls__, vue.toRaw(tableData));
};
const handlerDefault = () => {
ElementPlus.ElMessageBox.confirm(
"确认使用默认题单,将会重置题单?",
"警告",
{
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "warning"
}
).then(() => {
for (let i = 0; i < tableData.length; i++) {
delete tableData[i];
}
for (let item of defaultUrls) {
tableData.unshift(item);
}
ElementPlus.ElMessage({
type: "success",
message: "重置成功"
});
}).catch(() => {
ElementPlus.ElMessage({
type: "info",
message: "取消重置"
});
});
};
window.addEventListener("beforeunload", () => {
console.log("save ....");
_GM_setValue(__0x3f_problmes_urls__, vue.toRaw(tableData));
_GM_setValue(__0x3f_problmes_update__, true);
_GM_setValue(__add_cur__, false);
});
vue.onMounted(() => {
if (support_plugins()) {
setTimeout(() => {
handlerProblem(vue.toRaw(Object.assign({}, fromData)));
}, 3e3);
}
});
return (_ctx, _cache) => {
const _component_el_button = vue.resolveComponent("el-button");
const _component_el_input = vue.resolveComponent("el-input");
const _component_el_col = vue.resolveComponent("el-col");
const _component_el_form_item = vue.resolveComponent("el-form-item");
const _component_el_switch = vue.resolveComponent("el-switch");
const _component_el_tooltip = vue.resolveComponent("el-tooltip");
const _component_el_form = vue.resolveComponent("el-form");
const _component_el_divider = vue.resolveComponent("el-divider");
const _component_el_row = vue.resolveComponent("el-row");
const _component_el_link = vue.resolveComponent("el-link");
const _component_el_table_column = vue.resolveComponent("el-table-column");
const _component_el_table = vue.resolveComponent("el-table");
const _component_el_dialog = vue.resolveComponent("el-dialog");
const _component_el_drawer = vue.resolveComponent("el-drawer");
return vue.openBlock(), vue.createElementBlock(vue.Fragment, null, [
vue.createElementVNode("div", null, [
vue.createVNode(_component_el_button, {
type: "primary",
style: { "margin-left": "16px" },
onClick: _cache[0] || (_cache[0] = ($event) => drawer.value = !drawer.value),
class: "m-setting-button"
}, {
default: vue.withCtx(() => [
vue.createTextVNode(" 设置 ")
]),
_: 1
})
]),
vue.createVNode(_component_el_drawer, {
modelValue: drawer.value,
"onUpdate:modelValue": _cache[13] || (_cache[13] = ($event) => drawer.value = $event),
"with-header": false,
size: "30%"
}, {
default: vue.withCtx(() => [
vue.createVNode(_component_el_form, {
"label-position": "left",
"label-width": "auto",
model: fromData,
style: { "max-width": "600px" }
}, {
default: vue.withCtx(() => [
vue.createVNode(_component_el_form_item, { label: "分数区间" }, {
default: vue.withCtx(() => [
vue.createVNode(_component_el_col, { span: 10 }, {
default: vue.withCtx(() => [
vue.createVNode(_component_el_input, {
modelValue: fromData.min,
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => fromData.min = $event),
"aria-placeholder": "",
placeholder: " min "
}, null, 8, ["modelValue"])
]),
_: 1
}),
vue.createVNode(_component_el_col, {
class: "text-center",
span: 1,
style: { "margin": "0 0.5rem" }
}, {
default: vue.withCtx(() => [
vue.createTextVNode("-")
]),
_: 1
}),
vue.createVNode(_component_el_col, { span: 10 }, {
default: vue.withCtx(() => [
vue.createVNode(_component_el_input, {
modelValue: fromData.max,
"onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => fromData.max = $event),
"aria-placeholder": "",
placeholder: " max"
}, null, 8, ["modelValue"])
]),
_: 1
})
]),
_: 1
}),
vue.createVNode(_component_el_form_item, { label: "显示会员题" }, {
default: vue.withCtx(() => [
vue.createVNode(_component_el_switch, {
modelValue: fromData.visiableMember,
"onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => fromData.visiableMember = $event)
}, null, 8, ["modelValue"])
]),
_: 1
}),
vue.createVNode(_component_el_form_item, { label: "只在收藏题单中生效" }, {
default: vue.withCtx(() => [
vue.createVNode(_component_el_tooltip, {
content: "插件只在收藏题单中生效,刷新生效 ",
placement: "bottom-end"
}, {
default: vue.withCtx(() => [
vue.createVNode(_component_el_switch, {
modelValue: fromData.onlyUrls,
"onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => fromData.onlyUrls = $event)
}, null, 8, ["modelValue"])
]),
_: 1
})
]),
_: 1
}),
vue.createVNode(_component_el_form_item, { label: "使用题单" }, {
default: vue.withCtx(() => [
vue.createVNode(_component_el_switch, {
modelValue: fromData.useDefaultSetting,
"onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => fromData.useDefaultSetting = $event)
}, null, 8, ["modelValue"])
]),
_: 1
})
]),
_: 1
}, 8, ["model"]),
fromData.useDefaultSetting ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 0 }, [
vue.createVNode(_component_el_divider),
vue.createVNode(_component_el_button, {
plain: "",
onClick: showProblems
}, {
default: vue.withCtx(() => [
vue.createTextVNode(" 查看收藏的题单 ")
]),
_: 1
}),
vue.createVNode(_component_el_divider)
], 64)) : vue.createCommentVNode("", true),
vue.createVNode(_component_el_dialog, {
modelValue: dialogTableVisible.value,
"onUpdate:modelValue": _cache[8] || (_cache[8] = ($event) => dialogTableVisible.value = $event),
title: "题单"
}, {
default: vue.withCtx(() => [
vue.createVNode(_component_el_row, { gutter: 10 }, {
default: vue.withCtx(() => [
vue.createVNode(_component_el_col, { span: 8 }, {
default: vue.withCtx(() => [
vue.createVNode(_component_el_input, {
modelValue: keywords.value,
"onUpdate:modelValue": _cache[6] || (_cache[6] = ($event) => keywords.value = $event),
placeholder: "请输入关键词过滤",
clearable: ""
}, null, 8, ["modelValue"])
]),
_: 1
}),
vue.createVNode(_component_el_col, { span: 16 }, {
default: vue.withCtx(() => [
vue.createVNode(_component_el_button, {
plain: "",
onClick: addlocal
}, {
default: vue.withCtx(() => [
vue.createTextVNode(" 添加本页 ")
]),
_: 1
}),
vue.createVNode(_component_el_button, {
plain: "",
onClick: _cache[7] || (_cache[7] = ($event) => handlerProblems("add"))
}, {
default: vue.withCtx(() => [
vue.createTextVNode(" 自定义 ")
]),
_: 1
}),
vue.createVNode(_component_el_button, {
plain: "",
onClick: handlerDefault
}, {
default: vue.withCtx(() => [
vue.createTextVNode(" 默认 ")
]),
_: 1
})
]),
_: 1
})
]),
_: 1
}),
vue.createVNode(_component_el_table, {
data: urlsData.value,
height: "300",
style: { "width": "100%", "margin-top": "10px" }
}, {
default: vue.withCtx(() => [
vue.createVNode(_component_el_table_column, {
label: "标题",
width: "auto",
align: "center"
}, {
default: vue.withCtx((scope) => [
vue.createVNode(_component_el_link, {
href: scope.row.link,
target: "_blank",
type: "default"
}, {
default: vue.withCtx(() => [
vue.createTextVNode(vue.toDisplayString(scope.row.title), 1)
]),
_: 2
}, 1032, ["href"])
]),
_: 1
}),
vue.createVNode(_component_el_table_column, {
label: "操作",
width: "auto",
align: "center"
}, {
default: vue.withCtx((scope) => [
vue.createVNode(_component_el_button, {
type: "primary",
size: "small",
onClick: ($event) => handlerProblems("update", scope.row, scope.$index)
}, {
default: vue.withCtx(() => [
vue.createTextVNode("编辑")
]),
_: 2
}, 1032, ["onClick"]),
vue.createVNode(_component_el_button, {
type: "danger",
size: "small",
onClick: ($event) => deleteProblems(scope.$index)
}, {
default: vue.withCtx(() => [
vue.createTextVNode("删除")
]),
_: 2
}, 1032, ["onClick"])
]),
_: 1
})
]),
_: 1
}, 8, ["data"])
]),
_: 1
}, 8, ["modelValue"]),
vue.createVNode(_component_el_dialog, {
modelValue: dialogFormVisible.value,
"onUpdate:modelValue": _cache[12] || (_cache[12] = ($event) => dialogFormVisible.value = $event),
title: `${info.status == "add" ? "添加" : "编辑"}`,
width: "400"
}, {
footer: vue.withCtx(() => [
vue.createElementVNode("div", _hoisted_1, [
vue.createVNode(_component_el_button, {
onClick: _cache[11] || (_cache[11] = ($event) => dialogFormVisible.value = false)
}, {
default: vue.withCtx(() => [
vue.createTextVNode("取消")
]),
_: 1
}),
vue.createVNode(_component_el_button, {
type: "primary",
onClick: addOrUpdate
}, {
default: vue.withCtx(() => [
vue.createTextVNode(" 确认 ")
]),
_: 1
})
])
]),
default: vue.withCtx(() => [
vue.createVNode(_component_el_form, null, {
default: vue.withCtx(() => [
vue.createVNode(_component_el_form_item, {
label: "标题",
"label-width": formLabelWidth
}, {
default: vue.withCtx(() => [
vue.createVNode(_component_el_input, {
modelValue: info.title,
"onUpdate:modelValue": _cache[9] || (_cache[9] = ($event) => info.title = $event),
autocomplete: "off"
}, null, 8, ["modelValue"])
]),
_: 1
}),
vue.createVNode(_component_el_form_item, {
label: "链接",
"label-width": formLabelWidth
}, {
default: vue.withCtx(() => [
vue.createVNode(_component_el_input, {
modelValue: info.link,
"onUpdate:modelValue": _cache[10] || (_cache[10] = ($event) => info.link = $event),
autocomplete: "off"
}, null, 8, ["modelValue"])
]),
_: 1
})
]),
_: 1
})
]),
_: 1
}, 8, ["modelValue", "title"])
]),
_: 1
}, 8, ["modelValue"])
], 64);
};
}
};
const App = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-6d3b190f"]]);
const cssLoader = (e) => {
const t = GM_getResourceText(e);
return GM_addStyle(t), t;
};
cssLoader("elementPlusCss");
let Container = null;
const VueApp = vue.createApp(App);
const start = () => {
Container = document.createElement("div");
const body = document.querySelector("body");
body.append(Container);
Container.style.display = support_plugins() ? "block" : "none";
return Container;
};
VueApp.use(ElementPlus).mount(start());
_GM_registerMenuCommand(`${initObj().onlyUrls ? "仅在收藏题单页面生效" : "所有题单生效"}`, () => {
const u = initObj();
u.onlyUrls = !u.onlyUrls;
Container.style.display = support_plugins() ? "block" : "none";
_GM_setValue(__0x3f_problmes_solution__, u);
});
_GM_registerMenuCommand(`添加本页`, () => {
const urls = initUrls();
let ok = false;
const url = window.location.href;
for (let info of urls) {
if (!info || !(info == null ? void 0 : info.link)) {
continue;
}
if (info.link.indexOf(url) != -1) {
ok = true;
break;
}
}
if (ok) {
ElementPlus.ElMessage({
message: "收藏失败,链接已经存在!",
type: "error"
});
} else {
urls.unshift({
title: document.title,
link: url
});
Container.style.display = "block";
_GM_setValue(__0x3f_problmes_urls__, urls);
_GM_setValue(__add_cur__, true);
ElementPlus.ElMessage({
message: "收藏成功!刷新生效",
type: "success"
});
}
});
})(Vue, ElementPlus);