+
+
+
+
+
+ | 发送者 |
+ 内容 |
+ 发送时间 |
+ 阅读状态 |
+
+
+
+
`;
+ GetUsernameHTML(ToUser, SearchParams.get("to_user"));
+ let RefreshMessage = (Silent = true) => {
+ if (!Silent) {
+ MessageTable.children[1].innerHTML = "";
+ for (let i = 0; i < 10; i++) {
+ let Row = document.createElement("tr");
+ MessageTable.children[1].appendChild(Row);
+ for (let j = 0; j < 4; j++) {
+ let Cell = document.createElement("td");
+ Row.appendChild(Cell);
+ Cell.innerHTML = `
`;
+ }
+ }
+ }
+ RequestAPI("ReadUserMailMention", {
+ "UserID": String(SearchParams.get("to_user"))
+ });
+ RequestAPI("GetMail", {
+ "OtherUser": String(SearchParams.get("to_user"))
+ }, async (ResponseData) => {
+ if (ResponseData.Success) {
+ ErrorElement.style.display = "none";
+ let Data = ResponseData.Data.Mail;
+ MessageTable.children[1].innerHTML = "";
+ for (let i = 0; i < Data.length; i++) {
+ let Row = document.createElement("tr");
+ MessageTable.children[1].appendChild(Row);
+ if (!Data[i].IsRead && Data[i].FromUser != window.CurrentUsername) {
+ Row.className = "table-info";
+ }
+ let UsernameCell = document.createElement("td");
+ Row.appendChild(UsernameCell);
+ GetUsernameHTML(UsernameCell, Data[i].FromUser);
+ let ContentCell = document.createElement("td");
+ let ContentDiv = document.createElement("div");
+ ContentDiv.style.display = "flex";
+ ContentDiv.style.maxWidth = window.innerWidth - 300 + "px";
+ ContentDiv.style.maxHeight = "500px";
+ ContentDiv.style.overflowX = "auto";
+ ContentDiv.style.overflowY = "auto";
+ ContentDiv.innerHTML = PurifyHTML(marked.parse(Data[i].Content));
+ let mediaElements = ContentDiv.querySelectorAll('img, video');
+ for (let media of mediaElements) {
+ media.style.objectFit = 'contain';
+ media.style.maxWidth = '100%';
+ media.style.maxHeight = '100%';
+ }
+ ContentCell.appendChild(ContentDiv);
+ Row.appendChild(ContentCell);
+ let SendTimeCell = document.createElement("td");
+ Row.appendChild(SendTimeCell);
+ SendTimeCell.innerHTML = GetRelativeTime(Data[i].SendTime);
+ let IsReadCell = document.createElement("td");
+ Row.appendChild(IsReadCell);
+ IsReadCell.innerHTML = (Data[i].IsRead ? "已读" : "未读");
+ }
+ } else {
+ ErrorElement.innerText = ResponseData.Message;
+ ErrorElement.style.display = "";
+ }
+ });
+ };
+ Content.addEventListener("input", () => {
+ Content.classList.remove("is-invalid");
+ });
+ Content.addEventListener("paste", (EventData) => {
+ let Items = EventData.clipboardData.items;
+ if (Items.length !== 0) {
+ for (let i = 0; i < Items.length; i++) {
+ if (Items[i].type.indexOf("image") != -1) {
+ let Reader = new FileReader();
+ Reader.readAsDataURL(Items[i].getAsFile());
+ Reader.onload = () => {
+ let Before = Content.value.substring(0, Content.selectionStart);
+ let After = Content.value.substring(Content.selectionEnd, Content.value.length);
+ const UploadMessage = "![正在上传图片...]()";
+ Content.value = Before + UploadMessage + After;
+ Content.dispatchEvent(new Event("input"));
+ RequestAPI("UploadImage", {
+ "Image": Reader.result
+ }, (ResponseData) => {
+ if (ResponseData.Success) {
+ Content.value = Before + `` + After;
+ Content.dispatchEvent(new Event("input"));
+ } else {
+ Content.value = Before + `![上传失败!` + ResponseData.Message + `]()` + After;
+ Content.dispatchEvent(new Event("input"));
+ }
+ });
+ };
+ }
+ }
+ }
+ });
+ Content.addEventListener("keydown", (Event) => {
+ if (Event.keyCode == 13) {
+ Send.click();
+ }
+ });
+ Send.addEventListener("click", () => {
+ if (Content.value == "") {
+ Content.classList.add("is-invalid");
+ return;
+ }
+ Send.disabled = true;
+ Send.children[0].style.display = "";
+ let ContentData = Content.value;
+ RequestAPI("SendMail", {
+ "ToUser": String(SearchParams.get("to_user")), "Content": String(ContentData)
+ }, (ResponseData) => {
+ Send.disabled = false;
+ Send.children[0].style.display = "none";
+ if (ResponseData.Success) {
+ ErrorElement.style.display = "none";
+ Content.value = "";
+ RefreshMessage();
+ } else {
+ ErrorElement.innerText = ResponseData.Message;
+ ErrorElement.style.display = "";
+ }
+ });
+ });
+ RefreshMessage(false);
+ addEventListener("focus", RefreshMessage);
+ }
+}
diff --git a/src/pages/modifypage.js b/src/pages/modifypage.js
new file mode 100644
index 00000000..df6b6897
--- /dev/null
+++ b/src/pages/modifypage.js
@@ -0,0 +1,271 @@
+import { UtilityEnabled, GetUserInfo, GetRelativeTime, SmartAlert } from '../utils.js';
+import { RequestAPI } from '../api.js';
+import { saveAs } from 'file-saver';
+import JSZip from 'jszip';
+
+export async function handleModifyPage() {
+ const SearchParams = new URLSearchParams(location.search);
+ if (SearchParams.get("ByUserScript") != null) {
+ document.title = "XMOJ-Script 更新日志";
+ document.querySelector("body > div > div.mt-3").innerHTML = "";
+ await fetch(window.ServerURL + "/Update.json", {cache: "no-cache"})
+ .then((Response) => {
+ return Response.json();
+ })
+ .then((Response) => {
+ for (let i = Object.keys(Response.UpdateHistory).length - 1; i >= 0; i--) {
+ let Version = Object.keys(Response.UpdateHistory)[i];
+ let Data = Response.UpdateHistory[Version];
+ let UpdateDataCard = document.createElement("div");
+ document.querySelector("body > div > div.mt-3").appendChild(UpdateDataCard);
+ UpdateDataCard.className = "card mb-3";
+ if (Data.Prerelease) UpdateDataCard.classList.add("text-secondary");
+ let UpdateDataCardBody = document.createElement("div");
+ UpdateDataCard.appendChild(UpdateDataCardBody);
+ UpdateDataCardBody.className = "card-body";
+ let UpdateDataCardTitle = document.createElement("h5");
+ UpdateDataCardBody.appendChild(UpdateDataCardTitle);
+ UpdateDataCardTitle.className = "card-title";
+ UpdateDataCardTitle.innerText = Version;
+ if (Data.Prerelease) {
+ UpdateDataCardTitle.innerHTML += "(预览版)";
+ }
+ let UpdateDataCardSubtitle = document.createElement("h6");
+ UpdateDataCardBody.appendChild(UpdateDataCardSubtitle);
+ UpdateDataCardSubtitle.className = "card-subtitle mb-2 text-muted";
+ UpdateDataCardSubtitle.innerHTML = GetRelativeTime(Data.UpdateDate);
+ let UpdateDataCardText = document.createElement("p");
+ UpdateDataCardBody.appendChild(UpdateDataCardText);
+ UpdateDataCardText.className = "card-text";
+ //release notes
+ if (Data.Notes != undefined) {
+ UpdateDataCardText.innerHTML = Data.Notes;
+ }
+ let UpdateDataCardList = document.createElement("ul");
+ UpdateDataCardText.appendChild(UpdateDataCardList);
+ UpdateDataCardList.className = "list-group list-group-flush";
+ for (let j = 0; j < Data.UpdateContents.length; j++) {
+ let UpdateDataCardListItem = document.createElement("li");
+ UpdateDataCardList.appendChild(UpdateDataCardListItem);
+ UpdateDataCardListItem.className = "list-group-item";
+ UpdateDataCardListItem.innerHTML = "(
" + "#" + Data.UpdateContents[j].PR + ") " + Data.UpdateContents[j].Description;
+ }
+ let UpdateDataCardLink = document.createElement("a");
+ UpdateDataCardBody.appendChild(UpdateDataCardLink);
+ UpdateDataCardLink.className = "card-link";
+ UpdateDataCardLink.href = "https://github.com/XMOJ-Script-dev/XMOJ-Script/releases/tag/" + Version;
+ UpdateDataCardLink.target = "_blank";
+ UpdateDataCardLink.innerText = "查看该版本";
+ }
+ });
+ } else {
+ document.title = "修改账号";
+ let Nickname = document.getElementsByName("nick")[0].value;
+ let School = document.getElementsByName("school")[0].value;
+ let EmailAddress = document.getElementsByName("email")[0].value;
+ let CodeforcesAccount = document.getElementsByName("acc_cf")[0].value;
+ let AtcoderAccount = document.getElementsByName("acc_atc")[0].value;
+ let USACOAccount = document.getElementsByName("acc_usaco")[0].value;
+ let LuoguAccount = document.getElementsByName("acc_luogu")[0].value;
+ document.querySelector("body > div > div").innerHTML = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
修改成功
+
`;
+ document.getElementById("Nickname").value = Nickname;
+ document.getElementById("School").value = School;
+ document.getElementById("EmailAddress").value = EmailAddress;
+ document.getElementById("CodeforcesAccount").value = CodeforcesAccount;
+ document.getElementById("AtcoderAccount").value = AtcoderAccount;
+ document.getElementById("USACOAccount").value = USACOAccount;
+ document.getElementById("LuoguAccount").value = LuoguAccount;
+ RequestAPI("GetBadge", {
+ "UserID": String(window.CurrentUsername)
+ }, (Response) => {
+ if (Response.Success) {
+ BadgeRow.style.display = "";
+ BadgeContent.value = Response.Data.Content;
+ BadgeBackgroundColor.value = Response.Data.BackgroundColor;
+ BadgeColor.value = Response.Data.Color;
+ let Temp = [];
+ for (let i = 0; i < localStorage.length; i++) {
+ if (localStorage.key(i).startsWith("UserScript-User-" + window.CurrentUsername + "-Badge-")) {
+ Temp.push(localStorage.key(i));
+ }
+ }
+ for (let i = 0; i < Temp.length; i++) {
+ localStorage.removeItem(Temp[i]);
+ }
+ }
+ });
+ ModifyInfo.addEventListener("click", async () => {
+ ModifyInfo.disabled = true;
+ ModifyInfo.querySelector("span").style.display = "";
+ ErrorElement.style.display = "none";
+ SuccessElement.style.display = "none";
+ let BadgeContent = document.querySelector("#BadgeContent").value;
+ let BadgeBackgroundColor = document.querySelector("#BadgeBackgroundColor").value;
+ let BadgeColor = document.querySelector("#BadgeColor").value;
+ await new Promise((Resolve) => {
+ RequestAPI("EditBadge", {
+ "UserID": String(window.CurrentUsername),
+ "Content": String(BadgeContent),
+ "BackgroundColor": String(BadgeBackgroundColor),
+ "Color": String(BadgeColor)
+ }, (Response) => {
+ if (Response.Success) {
+ Resolve();
+ } else {
+ ModifyInfo.disabled = false;
+ ModifyInfo.querySelector("span").style.display = "none";
+ ErrorElement.style.display = "block";
+ ErrorElement.innerText = Response.Message;
+ }
+ });
+ });
+ let Nickname = document.querySelector("#Nickname").value;
+ let OldPassword = document.querySelector("#OldPassword").value;
+ let NewPassword = document.querySelector("#NewPassword").value;
+ let NewPasswordAgain = document.querySelector("#NewPasswordAgain").value;
+ let School = document.querySelector("#School").value;
+ let EmailAddress = document.querySelector("#EmailAddress").value;
+ let CodeforcesAccount = document.querySelector("#CodeforcesAccount").value;
+ let AtcoderAccount = document.querySelector("#AtcoderAccount").value;
+ let USACOAccount = document.querySelector("#USACOAccount").value;
+ let LuoguAccount = document.querySelector("#LuoguAccount").value;
+ await fetch("https://www.xmoj.tech/modify.php", {
+ "headers": {
+ "content-type": "application/x-www-form-urlencoded"
+ },
+ "referrer": location.href,
+ "method": "POST",
+ "body": "nick=" + encodeURIComponent(Nickname) + "&" + "opassword=" + encodeURIComponent(OldPassword) + "&" + "npassword=" + encodeURIComponent(NewPassword) + "&" + "rptpassword=" + encodeURIComponent(NewPasswordAgain) + "&" + "school=" + encodeURIComponent(School) + "&" + "email=" + encodeURIComponent(EmailAddress) + "&" + "acc_cf=" + encodeURIComponent(CodeforcesAccount) + "&" + "acc_atc=" + encodeURIComponent(AtcoderAccount) + "&" + "acc_usaco=" + encodeURIComponent(USACOAccount) + "&" + "acc_luogu=" + encodeURIComponent(LuoguAccount)
+ });
+ ModifyInfo.disabled = false;
+ ModifyInfo.querySelector("span").style.display = "none";
+ SuccessElement.style.display = "block";
+ });
+ if (UtilityEnabled("ExportACCode")) {
+ let ExportACCode = document.createElement("button");
+ document.querySelector("body > div.container > div").appendChild(ExportACCode);
+ ExportACCode.innerText = "导出AC代码";
+ ExportACCode.className = "btn btn-outline-secondary";
+ ExportACCode.addEventListener("click", () => {
+ ExportACCode.disabled = true;
+ ExportACCode.innerText = "正在导出...";
+ let Request = new XMLHttpRequest();
+ Request.addEventListener("readystatechange", () => {
+ if (Request.readyState == 4) {
+ if (Request.status == 200) {
+ let Response = Request.responseText;
+ let ACCode = Response.split("------------------------------------------------------\r\n");
+ let ScriptElement = document.createElement("script");
+ ScriptElement.src = "https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js";
+ document.head.appendChild(ScriptElement);
+ ScriptElement.onload = () => {
+ var Zip = new JSZip();
+ for (let i = 0; i < ACCode.length; i++) {
+ let CurrentCode = ACCode[i];
+ if (CurrentCode != "") {
+ let CurrentQuestionID = CurrentCode.substring(7, 11);
+ CurrentCode = CurrentCode.substring(14);
+ CurrentCode = CurrentCode.replaceAll("\r", "");
+ Zip.file(CurrentQuestionID + ".cpp", CurrentCode);
+ }
+ }
+ ExportACCode.innerText = "正在生成压缩包……";
+ Zip.generateAsync({type: "blob"})
+ .then(function (Content) {
+ saveAs(Content, "ACCodes.zip");
+ ExportACCode.innerText = "AC代码导出成功";
+ ExportACCode.disabled = false;
+ setTimeout(() => {
+ ExportACCode.innerText = "导出AC代码";
+ }, 1000);
+ });
+ };
+ } else {
+ ExportACCode.disabled = false;
+ ExportACCode.innerText = "AC代码导出失败";
+ setTimeout(() => {
+ ExportACCode.innerText = "导出AC代码";
+ }, 1000);
+ }
+ }
+ });
+ Request.open("GET", "https://www.xmoj.tech/export_ac_code.php", true);
+ Request.send();
+ });
+ }
+ }
+}
diff --git a/src/pages/open_contest.js b/src/pages/open_contest.js
new file mode 100644
index 00000000..6171fc85
--- /dev/null
+++ b/src/pages/open_contest.js
@@ -0,0 +1,40 @@
+export function handleOpenContestPage() {
+ let Temp = document.querySelector("body > div > div.mt-3 > div > div.col-md-8").children;
+ let NewsData = [];
+ for (let i = 0; i < Temp.length; i += 2) {
+ let Title = Temp[i].children[0].innerText;
+ let Time = 0;
+ if (Temp[i].children[1] != null) {
+ Time = Temp[i].children[1].innerText;
+ }
+ let Body = Temp[i + 1].innerHTML;
+ NewsData.push({"Title": Title, "Time": new Date(Time), "Body": Body});
+ }
+ document.querySelector("body > div > div.mt-3 > div > div.col-md-8").innerHTML = "";
+ for (let i = 0; i < NewsData.length; i++) {
+ let NewsRow = document.createElement("div");
+ NewsRow.className = "cnt-row";
+ let NewsRowHead = document.createElement("div");
+ NewsRowHead.className = "cnt-row-head title";
+ NewsRowHead.innerText = NewsData[i].Title;
+ if (NewsData[i].Time.getTime() != 0) {
+ NewsRowHead.innerHTML += "
" + NewsData[i].Time.toLocaleDateString() + "";
+ }
+ NewsRow.appendChild(NewsRowHead);
+ let NewsRowBody = document.createElement("div");
+ NewsRowBody.className = "cnt-row-body";
+ NewsRowBody.innerHTML = NewsData[i].Body;
+ NewsRow.appendChild(NewsRowBody);
+ document.querySelector("body > div > div.mt-3 > div > div.col-md-8").appendChild(NewsRow);
+ }
+ let MyContestData = document.querySelector("body > div > div.mt-3 > div > div.col-md-4 > div:nth-child(2)").innerHTML;
+ let CountDownData = document.querySelector("#countdown_list").innerHTML;
+ document.querySelector("body > div > div.mt-3 > div > div.col-md-4").innerHTML = `
+
我的月赛
+
${MyContestData}
+
+
`;
+}
diff --git a/src/pages/problem.js b/src/pages/problem.js
new file mode 100644
index 00000000..3e65e098
--- /dev/null
+++ b/src/pages/problem.js
@@ -0,0 +1,203 @@
+import { RenderMathJax, UtilityEnabled, GetUsernameHTML, TidyTable } from '../utils.js';
+import { RequestAPI } from '../api.js';
+import { GM_setClipboard } from '$';
+import $ from 'jquery';
+
+export async function handleProblemPage() {
+ await RenderMathJax();
+ const SearchParams = new URLSearchParams(location.search);
+ if (SearchParams.get("cid") != null) {
+ document.getElementsByTagName("h2")[0].innerHTML += " (" + localStorage.getItem("UserScript-Contest-" + SearchParams.get("cid") + "-Problem-" + SearchParams.get("pid") + "-PID") + ")";
+ }
+ if (document.querySelector("body > div > div.mt-3 > h2") != null) {
+ document.querySelector("body > div > div.mt-3").innerHTML = "没有此题目或题目对你不可见";
+ setTimeout(() => {
+ location.href = "https://www.xmoj.tech/problemset.php";
+ }, 1000);
+ } else {
+ let PID = localStorage.getItem("UserScript-Contest-" + SearchParams.get("cid") + "-Problem-" + SearchParams.get("pid") + "-PID");
+
+ document.querySelector("body > div > div.mt-3 > center").lastChild.style.marginLeft = "10px";
+ //修复提交按钮
+ let SubmitLink = document.querySelector('.mt-3 > center:nth-child(1) > a:nth-child(12)');
+ if (SubmitLink == null) { //a special type of problem
+ SubmitLink = document.querySelector('.mt-3 > center:nth-child(1) > a:nth-child(10)');
+ }
+ if (SubmitLink == null) {
+ SubmitLink = document.querySelector('.mt-3 > center:nth-child(1) > a:nth-child(11)');
+ }
+ if (SubmitLink == null) {
+ SubmitLink = document.querySelector('.mt-3 > center:nth-child(1) > a:nth-child(13)');
+ }
+ if (SubmitLink == null) {
+ SubmitLink = document.querySelector('.mt-3 > center:nth-child(1) > a:nth-child(9)');
+ }
+ let SubmitButton = document.createElement('button');
+ SubmitButton.id = 'SubmitButton';
+ SubmitButton.className = 'btn btn-outline-secondary';
+ SubmitButton.textContent = '提交';
+ SubmitButton.href = SubmitLink.href;
+ SubmitButton.onclick = function () {
+ window.location.href = SubmitLink.href;
+ console.log(SubmitLink.href);
+ };
+
+ // Replace the
element with the button
+ SubmitLink.parentNode.replaceChild(SubmitButton, SubmitLink);
+ // Remove the button's outer []
+ let str = document.querySelector('.mt-3 > center:nth-child(1)').innerHTML;
+ let target = SubmitButton.outerHTML;
+ let result = str.replace(new RegExp(`(.?)${target}(.?)`, 'g'), target);
+ document.querySelector('.mt-3 > center:nth-child(1)').innerHTML = result;
+ document.querySelector('html body.placeholder-glow div.container div.mt-3 center button#SubmitButton.btn.btn-outline-secondary').onclick = function () {
+ window.location.href = SubmitLink.href;
+ console.log(SubmitLink.href);
+ };
+ var Temp = document.querySelectorAll(".sampledata");
+ for (var i = 0; i < Temp.length; i++) {
+ Temp[i].parentElement.className = "card";
+ }
+ if (UtilityEnabled("RemoveUseless")) {
+ document.querySelector("h2.lang_en").remove();
+ document.getElementsByTagName("center")[1].remove();
+ }
+ if (UtilityEnabled("CopySamples")) {
+ $(".copy-btn").click((Event) => {
+ let CurrentButton = $(Event.currentTarget);
+ let span = CurrentButton.parent().last().find(".sampledata");
+ if (!span.length) {
+ CurrentButton.text("未找到代码块").addClass("done");
+ setTimeout(() => {
+ $(".copy-btn").text("复制").removeClass("done");
+ }, 1000);
+ return;
+ }
+ GM_setClipboard(span.text());
+ CurrentButton.text("复制成功").addClass("done");
+ setTimeout(() => {
+ $(".copy-btn").text("复制").removeClass("done");
+ }, 1000);
+ //document.body.removeChild(textarea[0]);
+ });
+ }
+ let IOFileElement = document.querySelector("body > div > div.mt-3 > center > h3");
+ if (IOFileElement != null) {
+ while (IOFileElement.childNodes.length >= 1) {
+ IOFileElement.parentNode.insertBefore(IOFileElement.childNodes[0], IOFileElement);
+ }
+ IOFileElement.parentNode.insertBefore(document.createElement("br"), IOFileElement);
+ IOFileElement.remove();
+ let Temp = document.querySelector("body > div > div.mt-3 > center").childNodes[2].data.trim();
+ let IOFilename = Temp.substring(0, Temp.length - 3);
+ localStorage.setItem("UserScript-Problem-" + PID + "-IOFilename", IOFilename);
+ }
+
+ if (UtilityEnabled("CopyMD")) {
+ await fetch(location.href).then((Response) => {
+ return Response.text();
+ }).then((Response) => {
+ let ParsedDocument = new DOMParser().parseFromString(Response, "text/html");
+ let Temp = ParsedDocument.querySelectorAll(".cnt-row-body");
+ if (UtilityEnabled("DebugMode")) console.log(Temp);
+ for (let i = 0; i < Temp.length; i++) {
+ if (Temp[i].children[0].className === "content lang_cn") {
+ let CopyMDButton = document.createElement("button");
+ CopyMDButton.className = "btn btn-sm btn-outline-secondary copy-btn";
+ CopyMDButton.innerText = "复制";
+ CopyMDButton.style.marginLeft = "10px";
+ CopyMDButton.type = "button";
+ document.querySelectorAll(".cnt-row-head.title")[i].appendChild(CopyMDButton);
+ CopyMDButton.addEventListener("click", () => {
+ GM_setClipboard(Temp[i].children[0].innerText.trim().replaceAll("\n\t", "\n").replaceAll("\n\n", "\n"));
+ CopyMDButton.innerText = "复制成功";
+ setTimeout(() => {
+ CopyMDButton.innerText = "复制";
+ }, 1000);
+ });
+ }
+ }
+ });
+ }
+
+ if (UtilityEnabled("Discussion")) {
+ let DiscussButton = document.createElement("button");
+ DiscussButton.className = "btn btn-outline-secondary position-relative";
+ DiscussButton.innerHTML = `讨论`;
+ DiscussButton.style.marginLeft = "10px";
+ DiscussButton.type = "button";
+ DiscussButton.addEventListener("click", () => {
+ if (SearchParams.get("cid") != null) {
+ open("https://www.xmoj.tech/discuss3/discuss.php?pid=" + PID, "_blank");
+ } else {
+ open("https://www.xmoj.tech/discuss3/discuss.php?pid=" + SearchParams.get("id"), "_blank");
+ }
+ });
+ document.querySelector("body > div > div.mt-3 > center").appendChild(DiscussButton);
+ let UnreadBadge = document.createElement("span");
+ UnreadBadge.className = "position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger";
+ UnreadBadge.style.display = "none";
+ DiscussButton.appendChild(UnreadBadge);
+
+ let RefreshCount = () => {
+ RequestAPI("GetPostCount", {
+ "ProblemID": Number(PID)
+ }, (Response) => {
+ if (Response.Success) {
+ if (Response.Data.DiscussCount != 0) {
+ UnreadBadge.innerText = Response.Data.DiscussCount;
+ UnreadBadge.style.display = "";
+ }
+ }
+ });
+ };
+ RefreshCount();
+ addEventListener("focus", RefreshCount);
+ }
+
+ let Tables = document.getElementsByTagName("table");
+ for (let i = 0; i < Tables.length; i++) {
+ TidyTable(Tables[i]);
+ }
+ }
+ let Style = document.createElement("style");
+ document.body.appendChild(Style);
+ Style.innerHTML += "code, kbd, pre, samp {";
+ Style.innerHTML += " font-family: monospace, Consolas, 'Courier New';";
+ Style.innerHTML += " font-size: 1rem;";
+ Style.innerHTML += "}";
+ Style.innerHTML += "pre {";
+ Style.innerHTML += " padding: 0.3em 0.5em;";
+ Style.innerHTML += " margin: 0.5em 0;";
+ Style.innerHTML += "}";
+ Style.innerHTML += ".in-out {";
+ Style.innerHTML += " overflow: hidden;";
+ Style.innerHTML += " display: flex;";
+ Style.innerHTML += " padding: 0.5em 0;";
+ Style.innerHTML += "}";
+ Style.innerHTML += ".in-out .in-out-item {";
+ Style.innerHTML += " flex: 1;";
+ Style.innerHTML += " overflow: hidden;";
+ Style.innerHTML += "}";
+ Style.innerHTML += ".cnt-row .title {";
+ Style.innerHTML += " font-weight: bolder;";
+ Style.innerHTML += " font-size: 1.1rem;";
+ Style.innerHTML += "}";
+ Style.innerHTML += ".cnt-row .content {";
+ Style.innerHTML += " overflow: hidden;";
+ Style.innerHTML += "}";
+ Style.innerHTML += "a.copy-btn {";
+ Style.innerHTML += " float: right;";
+ Style.innerHTML += " padding: 0 0.4em;";
+ Style.innerHTML += " border: 1px solid var(--bs-primary);";
+ Style.innerHTML += " border-radius: 3px;";
+ Style.innerHTML += " color: var(--bs-primary);";
+ Style.innerHTML += " cursor: pointer;";
+ Style.innerHTML += "}";
+ Style.innerHTML += "a.copy-btn:hover {";
+ Style.innerHTML += " background-color: var(--bs-secondary-bg);";
+ Style.innerHTML += "}";
+ Style.innerHTML += "a.done, a.done:hover {";
+ Style.innerHTML += " background-color: var(--bs-primary);";
+ Style.innerHTML += " color: white;";
+ Style.innerHTML += "}";
+}
diff --git a/src/pages/problem_solution.js b/src/pages/problem_solution.js
new file mode 100644
index 00000000..9f07ee79
--- /dev/null
+++ b/src/pages/problem_solution.js
@@ -0,0 +1,43 @@
+import { UtilityEnabled } from '../utils.js';
+import CodeMirror from 'codemirror';
+import { GM_setClipboard } from '$';
+
+export async function handleProblemSolutionPage() {
+ if (UtilityEnabled("RemoveUseless")) {
+ document.querySelector("h2.lang_en").remove(); //fixes #332
+ }
+ if (UtilityEnabled("CopyMD")) {
+ await fetch(location.href).then((Response) => {
+ return Response.text();
+ }).then((Response) => {
+ let ParsedDocument = new DOMParser().parseFromString(Response, "text/html");
+ let CopyMDButton = document.createElement("button");
+ CopyMDButton.className = "btn btn-sm btn-outline-secondary copy-btn";
+ CopyMDButton.innerText = "复制";
+ CopyMDButton.style.marginLeft = "10px";
+ CopyMDButton.type = "button";
+ document.querySelector("body > div > div.mt-3 > center > h2").appendChild(CopyMDButton);
+ CopyMDButton.addEventListener("click", () => {
+ GM_setClipboard(ParsedDocument.querySelector("body > div > div > div").innerText.trim().replaceAll("\n\t", "\n").replaceAll("\n\n", "\n"));
+ CopyMDButton.innerText = "复制成功";
+ setTimeout(() => {
+ CopyMDButton.innerText = "复制";
+ }, 1000);
+ });
+ });
+ }
+ let Temp = document.getElementsByClassName("prettyprint");
+ for (let i = 0; i < Temp.length; i++) {
+ let Code = Temp[i].innerText;
+ Temp[i].outerHTML = ``;
+ Temp[i].value = Code;
+ }
+ for (let i = 0; i < Temp.length; i++) {
+ CodeMirror.fromTextArea(Temp[i], {
+ lineNumbers: true,
+ mode: "text/x-c++src",
+ readOnly: true,
+ theme: (UtilityEnabled("DarkMode") ? "darcula" : "default")
+ }).setSize("100%", "auto");
+ }
+}
diff --git a/src/pages/problem_std.js b/src/pages/problem_std.js
new file mode 100644
index 00000000..f38493c9
--- /dev/null
+++ b/src/pages/problem_std.js
@@ -0,0 +1,25 @@
+import CodeMirror from 'codemirror';
+import { UtilityEnabled } from '../utils.js';
+
+export async function handleProblemStdPage() {
+ await fetch("https://www.xmoj.tech/problem_std.php?cid=" + SearchParams.get("cid") + "&pid=" + SearchParams.get("pid"))
+ .then((Response) => {
+ return Response.text();
+ }).then((Response) => {
+ let ParsedDocument = new DOMParser().parseFromString(Response, "text/html");
+ let Temp = ParsedDocument.getElementsByTagName("pre");
+ document.querySelector("body > div > div.mt-3").innerHTML = "";
+ for (let i = 0; i < Temp.length; i++) {
+ let CodeElement = document.createElement("div");
+ CodeElement.className = "mb-3";
+ document.querySelector("body > div > div.mt-3").appendChild(CodeElement);
+ CodeMirror(CodeElement, {
+ value: Temp[i].innerText,
+ lineNumbers: true,
+ mode: "text/x-c++src",
+ readOnly: true,
+ theme: (UtilityEnabled("DarkMode") ? "darcula" : "default")
+ }).setSize("100%", "auto");
+ }
+ });
+}
diff --git a/src/pages/problemset.js b/src/pages/problemset.js
new file mode 100644
index 00000000..726f074a
--- /dev/null
+++ b/src/pages/problemset.js
@@ -0,0 +1,42 @@
+import { UtilityEnabled } from '../utils.js';
+
+export function handleProblemsetPage() {
+ if (UtilityEnabled("Translate")) {
+ document.querySelector("body > div > div.mt-3 > center > table:nth-child(2) > tbody > tr > td:nth-child(2) > form > input").placeholder = "题目编号";
+ document.querySelector("body > div > div.mt-3 > center > table:nth-child(2) > tbody > tr > td:nth-child(2) > form > button").innerText = "确认";
+ document.querySelector("body > div > div.mt-3 > center > table:nth-child(2) > tbody > tr > td:nth-child(3) > form > input").placeholder = "标题或内容";
+ document.querySelector("#problemset > thead > tr > th:nth-child(1)").innerText = "状态";
+ }
+ if (UtilityEnabled("ResetType")) {
+ document.querySelector("#problemset > thead > tr > th:nth-child(1)").style.width = "5%";
+ document.querySelector("#problemset > thead > tr > th:nth-child(2)").style.width = "10%";
+ document.querySelector("#problemset > thead > tr > th:nth-child(3)").style.width = "75%";
+ document.querySelector("#problemset > thead > tr > th:nth-child(4)").style.width = "5%";
+ document.querySelector("#problemset > thead > tr > th:nth-child(5)").style.width = "5%";
+ }
+ document.querySelector("body > div > div.mt-3 > center > table:nth-child(2)").outerHTML = `
+`;
+ const SearchParams = new URLSearchParams(location.search);
+ if (SearchParams.get("search") != null) {
+ document.querySelector("body > div > div.mt-3 > center > div > div:nth-child(3) > form > input").value = SearchParams.get("search");
+ }
+
+ let Temp = document.querySelector("#problemset").rows;
+ for (let i = 1; i < Temp.length; i++) {
+ localStorage.setItem("UserScript-Problem-" + Temp[i].children[1].innerText + "-Name", Temp[i].children[2].innerText);
+ }
+}
diff --git a/src/pages/problemstatus.js b/src/pages/problemstatus.js
new file mode 100644
index 00000000..f7cfa3b0
--- /dev/null
+++ b/src/pages/problemstatus.js
@@ -0,0 +1,50 @@
+import { GetUsernameHTML } from '../utils.js';
+
+export function handleProblemStatusPage() {
+ const SearchParams = new URLSearchParams(location.search);
+ document.querySelector("body > div > div.mt-3 > center").insertBefore(document.querySelector("#statics"), document.querySelector("body > div > div.mt-3 > center > table"));
+ document.querySelector("body > div > div.mt-3 > center").insertBefore(document.querySelector("#problemstatus"), document.querySelector("body > div > div.mt-3 > center > table"));
+
+ document.querySelector("body > div > div.mt-3 > center > table:nth-child(3)").remove();
+ let Temp = document.querySelector("#statics").rows;
+ for (let i = 0; i < Temp.length; i++) {
+ Temp[i].removeAttribute("class");
+ }
+
+ document.querySelector("#problemstatus > thead > tr").innerHTML = document.querySelector("#problemstatus > thead > tr").innerHTML.replaceAll("td", "th");
+ document.querySelector("#problemstatus > thead > tr > th:nth-child(2)").innerText = "运行编号";
+ document.querySelector("#problemstatus > thead > tr > th:nth-child(4)").remove();
+ document.querySelector("#problemstatus > thead > tr > th:nth-child(4)").remove();
+ document.querySelector("#problemstatus > thead > tr > th:nth-child(4)").remove();
+ document.querySelector("#problemstatus > thead > tr > th:nth-child(4)").remove();
+ Temp = document.querySelector("#problemstatus > thead > tr").children;
+ for (let i = 0; i < Temp.length; i++) {
+ Temp[i].removeAttribute("class");
+ }
+ Temp = document.querySelector("#problemstatus > tbody").children;
+ for (let i = 0; i < Temp.length; i++) {
+ if (Temp[i].children[5].children[0] != null) {
+ Temp[i].children[1].innerHTML = `${escapeHTML(Temp[i].children[1].innerText.trim())}`;
+ }
+ GetUsernameHTML(Temp[i].children[2], Temp[i].children[2].innerText);
+ Temp[i].children[3].remove();
+ Temp[i].children[3].remove();
+ Temp[i].children[3].remove();
+ Temp[i].children[3].remove();
+ }
+
+
+ let CurrentPage = parseInt(SearchParams.get("page") || 0);
+ let PID = Number(SearchParams.get("id"));
+ document.title = "问题 " + PID + " 状态";
+ let Pagination = `
`;
+ document.querySelector("body > div > div.mt-3 > center").innerHTML += Pagination;
+}
diff --git a/src/pages/reinfo.js b/src/pages/reinfo.js
new file mode 100644
index 00000000..7d157e11
--- /dev/null
+++ b/src/pages/reinfo.js
@@ -0,0 +1,161 @@
+import { UtilityEnabled, TimeToStringTime, SizeToStringSize } from '../utils.js';
+import CodeMirror from 'codemirror';
+import * as CryptoJS from 'crypto-js';
+
+export async function handleReinfoPage() {
+ const SearchParams = new URLSearchParams(location.search);
+ document.title = "测试点信息: " + SearchParams.get("sid");
+ if (document.querySelector("#results > div") == undefined) {
+ document.querySelector("#results").parentElement.innerHTML = "没有测试点信息";
+ } else {
+ for (let i = 0; i < document.querySelector("#results > div").children.length; i++) {
+ let CurrentElement = document.querySelector("#results > div").children[i].children[0].children[0].children[0];
+ let Temp = CurrentElement.innerText.substring(0, CurrentElement.innerText.length - 2).split("/");
+ CurrentElement.innerText = TimeToStringTime(Temp[0]) + "/" + SizeToStringSize(Temp[1]);
+ }
+ if (document.getElementById("apply_data")) {
+ let ApplyDiv = document.getElementById("apply_data").parentElement;
+ console.log("启动!!!");
+ if (UtilityEnabled("ApplyData")) {
+ let GetDataButton = document.createElement("button");
+ GetDataButton.className = "ms-2 btn btn-outline-secondary";
+ GetDataButton.innerText = "获取数据";
+ console.log("按钮创建成功");
+ ApplyDiv.appendChild(GetDataButton);
+ GetDataButton.addEventListener("click", async () => {
+ GetDataButton.disabled = true;
+ GetDataButton.innerText = "正在获取数据...";
+ let PID = localStorage.getItem("UserScript-Solution-" + SearchParams.get("sid") + "-Problem");
+ if (PID == null) {
+ GetDataButton.innerText = "失败! 无法获取PID";
+ GetDataButton.disabled = false;
+ await new Promise((resolve) => {
+ setTimeout(resolve, 800);
+ });
+ GetDataButton.innerText = "获取数据";
+ return;
+ }
+ let Code = "";
+ if (localStorage.getItem(`UserScript-Problem-${PID}-IOFilename`) !== null) {
+ Code = `#define IOFile "${localStorage.getItem(`UserScript-Problem-${PID}-IOFilename`)}"\n`;
+ }
+ Code += `//XMOJ-Script 获取数据代码
+#include
+using namespace std;
+string Base64Encode(string Input)
+{
+const string Base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+string Output;
+for (int i = 0; i < Input.length(); i += 3)
+{
+ Output.push_back(i + 0 > Input.length() ? '=' : Base64Chars[(Input[i + 0] & 0xfc) >> 2]);
+ Output.push_back(i + 1 > Input.length() ? '=' : Base64Chars[((Input[i + 0] & 0x03) << 4) + ((Input[i + 1] & 0xf0) >> 4)]);
+ Output.push_back(i + 2 > Input.length() ? '=' : Base64Chars[((Input[i + 1] & 0x0f) << 2) + ((Input[i + 2] & 0xc0) >> 6)]);
+ Output.push_back(i + 3 > Input.length() ? '=' : Base64Chars[Input[i + 2] & 0x3f]);
+}
+return Output;
+}
+int main()
+{
+#ifdef IOFile
+freopen(IOFile ".in", "r", stdin);
+freopen(IOFile ".out", "w", stdout);
+#endif
+string Input;
+while (1)
+{
+ char Data = getchar();
+ if (Data == EOF)
+ break;
+ Input.push_back(Data);
+}
+throw runtime_error("[" + Base64Encode(Input.c_str()) + "]");
+return 0;
+}`;
+
+ await fetch("https://www.xmoj.tech/submit.php", {
+ "headers": {
+ "content-type": "application/x-www-form-urlencoded"
+ },
+ "referrer": "https://www.xmoj.tech/submitpage.php?id=" + PID,
+ "method": "POST",
+ "body": "id=" + PID + "&" + "language=1&" + "source=" + encodeURIComponent(Code) + "&" + "enable_O2=on"
+ });
+
+ let SID = await fetch("https://www.xmoj.tech/status.php").then((Response) => {
+ return Response.text();
+ }).then((Response) => {
+ let ParsedDocument = new DOMParser().parseFromString(Response, "text/html");
+ return ParsedDocument.querySelector("#result-tab > tbody > tr:nth-child(1) > td:nth-child(2)").innerText;
+ });
+
+ await new Promise((Resolve) => {
+ let Interval = setInterval(async () => {
+ await fetch("status-ajax.php?solution_id=" + SID).then((Response) => {
+ return Response.text();
+ }).then((Response) => {
+ if (Response.split(",")[0] >= 4) {
+ clearInterval(Interval);
+ Resolve();
+ }
+ });
+ }, 500);
+ });
+
+ await fetch(`https://www.xmoj.tech/reinfo.php?sid=${SID}`).then((Response) => {
+ return Response.text();
+ }).then((Response) => {
+ let ParsedDocument = new DOMParser().parseFromString(Response, "text/html");
+ let ErrorData = ParsedDocument.getElementById("errtxt").innerText;
+ let MatchResult = ErrorData.match(/\what\(\): \[([A-Za-z0-9+\/=]+)\]/g);
+ if (MatchResult === null) {
+ GetDataButton.innerText = "获取数据失败";
+ GetDataButton.disabled = false;
+ return;
+ }
+ for (let i = 0; i < MatchResult.length; i++) {
+ let Data = CryptoJS.enc.Base64.parse(MatchResult[i].substring(10, MatchResult[i].length - 1)).toString(CryptoJS.enc.Utf8);
+ ApplyDiv.appendChild(document.createElement("hr"));
+ ApplyDiv.appendChild(document.createTextNode("数据" + (i + 1) + ":"));
+ let CodeElement = document.createElement("div");
+ ApplyDiv.appendChild(CodeElement);
+ CodeMirror(CodeElement, {
+ value: Data,
+ theme: (UtilityEnabled("DarkMode") ? "darcula" : "default"),
+ lineNumbers: true,
+ readOnly: true
+ }).setSize("100%", "auto");
+ }
+ GetDataButton.innerText = "获取数据成功";
+ GetDataButton.disabled = false;
+ });
+ });
+ }
+ document.getElementById("apply_data").addEventListener("click", () => {
+ let ApplyElements = document.getElementsByClassName("data");
+ for (let i = 0; i < ApplyElements.length; i++) {
+ ApplyElements[i].style.display = (ApplyElements[i].style.display == "block" ? "" : "block");
+ }
+ });
+ }
+ let ApplyElements = document.getElementsByClassName("data");
+ for (let i = 0; i < ApplyElements.length; i++) {
+ ApplyElements[i].addEventListener("click", async () => {
+ await fetch("https://www.xmoj.tech/data_distribute_ajax_apply.php", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded"
+ },
+ body: "user_id=" + window.CurrentUsername + "&" + "solution_id=" + SearchParams.get("sid") + "&" + "name=" + ApplyElements[i].getAttribute("name")
+ }).then((Response) => {
+ return Response.json();
+ }).then((Response) => {
+ ApplyElements[i].innerText = Response.msg;
+ setTimeout(() => {
+ ApplyElements[i].innerText = "申请数据";
+ }, 1000);
+ });
+ });
+ }
+ }
+}
diff --git a/src/pages/showsource.js b/src/pages/showsource.js
new file mode 100644
index 00000000..c43dcfa7
--- /dev/null
+++ b/src/pages/showsource.js
@@ -0,0 +1,41 @@
+import { UtilityEnabled } from '../utils.js';
+import CodeMirror from 'codemirror';
+import { RequestAPI } from '../api.js';
+
+export async function handleShowSourcePage() {
+ const SearchParams = new URLSearchParams(location.search);
+ let Code = "";
+ if (SearchParams.get("ByUserScript") == null) {
+ document.title = "查看代码: " + SearchParams.get("id");
+ await fetch("https://www.xmoj.tech/getsource.php?id=" + SearchParams.get("id"))
+ .then((Response) => {
+ return Response.text();
+ }).then((Response) => {
+ Code = Response.replace("\n\n", "");
+ });
+ } else {
+ document.title = "查看标程: " + SearchParams.get("pid");
+ if (localStorage.getItem("UserScript-LastUploadedStdTime") === undefined || new Date().getTime() - localStorage.getItem("UserScript-LastUploadedStdTime") > 1000 * 60 * 60 * 24 * 30) {
+ location.href = "https://www.xmoj.tech/userinfo.php?ByUserScript=1";
+ }
+ await new Promise((Resolve) => {
+ RequestAPI("GetStd", {
+ "ProblemID": Number(SearchParams.get("pid"))
+ }, (Response) => {
+ if (Response.Success) {
+ Code = Response.Data.StdCode;
+ } else {
+ Code = Response.Message;
+ }
+ Resolve();
+ });
+ });
+ }
+ document.querySelector("body > div > div.mt-3").innerHTML = ``;
+ CodeMirror.fromTextArea(document.querySelector("body > div > div.mt-3 > textarea"), {
+ lineNumbers: true,
+ mode: "text/x-c++src",
+ readOnly: true,
+ theme: (UtilityEnabled("DarkMode") ? "darcula" : "default")
+ }).setSize("100%", "auto");
+}
diff --git a/src/pages/status.js b/src/pages/status.js
new file mode 100644
index 00000000..a3865444
--- /dev/null
+++ b/src/pages/status.js
@@ -0,0 +1,233 @@
+import { UtilityEnabled, SizeToStringSize, TimeToStringTime, CodeSizeToStringSize } from '../utils.js';
+import { RequestAPI } from '../api.js';
+
+export async function handleStatusPage() {
+ const SearchParams = new URLSearchParams(location.search);
+ if (SearchParams.get("ByUserScript") == null) {
+ document.title = "提交状态";
+ document.querySelector("body > script:nth-child(5)").remove();
+ if (UtilityEnabled("NewBootstrap")) {
+ document.querySelector("#simform").outerHTML = ``;
+ }
+
+ if (UtilityEnabled("ImproveACRate")) {
+ let ImproveACRateButton = document.createElement("button");
+ document.querySelector("body > div.container > div > div.input-append").appendChild(ImproveACRateButton);
+ ImproveACRateButton.className = "btn btn-outline-secondary";
+ ImproveACRateButton.innerText = "提高正确率";
+ ImproveACRateButton.disabled = true;
+ let ACProblems = [];
+ await fetch("https://www.xmoj.tech/userinfo.php?user=" + CurrentUsername)
+ .then((Response) => {
+ return Response.text();
+ }).then((Response) => {
+ let ParsedDocument = new DOMParser().parseFromString(Response, "text/html");
+ ImproveACRateButton.innerText += "(" + (parseInt(ParsedDocument.querySelector("#statics > tbody > tr:nth-child(4) > td:nth-child(2)").innerText) / parseInt(ParsedDocument.querySelector("#statics > tbody > tr:nth-child(3) > td:nth-child(2)").innerText) * 100).toFixed(2) + "%)";
+ let Temp = ParsedDocument.querySelector("#statics > tbody > tr:nth-child(2) > td:nth-child(3) > script").innerText.split("\n")[5].split(";");
+ for (let i = 0; i < Temp.length; i++) {
+ ACProblems.push(Number(Temp[i].substring(2, Temp[i].indexOf(","))));
+ }
+ ImproveACRateButton.disabled = false;
+ });
+ ImproveACRateButton.addEventListener("click", async () => {
+ ImproveACRateButton.disabled = true;
+ let SubmitTimes = 3;
+ let Count = 0;
+ let SubmitInterval = setInterval(async () => {
+ if (Count >= SubmitTimes) {
+ clearInterval(SubmitInterval);
+ location.reload();
+ return;
+ }
+ ImproveACRateButton.innerText = "正在提交 (" + (Count + 1) + "/" + SubmitTimes + ")";
+ let PID = ACProblems[Math.floor(Math.random() * ACProblems.length)];
+ let SID = 0;
+ await fetch("https://www.xmoj.tech/status.php?problem_id=" + PID + "&jresult=4")
+ .then((Result) => {
+ return Result.text();
+ }).then((Result) => {
+ let ParsedDocument = new DOMParser().parseFromString(Result, "text/html");
+ SID = ParsedDocument.querySelector("#result-tab > tbody > tr:nth-child(1) > td:nth-child(2)").innerText;
+ });
+ let Code = "";
+ await fetch("https://www.xmoj.tech/getsource.php?id=" + SID)
+ .then((Response) => {
+ return Response.text();
+ }).then((Response) => {
+ Code = Response.substring(0, Response.indexOf("/**************************************************************")).trim();
+ });
+ await fetch("https://www.xmoj.tech/submit.php", {
+ "headers": {
+ "content-type": "application/x-www-form-urlencoded"
+ },
+ "referrer": "https://www.xmoj.tech/submitpage.php?id=" + PID,
+ "method": "POST",
+ "body": "id=" + PID + "&" + "language=1&" + "source=" + encodeURIComponent(Code) + "&" + "enable_O2=on"
+ });
+ Count++;
+ }, 1000);
+ });
+ ImproveACRateButton.style.marginBottom = ImproveACRateButton.style.marginRight = "7px";
+ ImproveACRateButton.style.marginRight = "7px";
+ }
+ if (UtilityEnabled("CompareSource")) {
+ let CompareButton = document.createElement("button");
+ document.querySelector("body > div.container > div > div.input-append").appendChild(CompareButton);
+ CompareButton.className = "btn btn-outline-secondary";
+ CompareButton.innerText = "比较提交记录";
+ CompareButton.addEventListener("click", () => {
+ location.href = "https://www.xmoj.tech/comparesource.php";
+ });
+ CompareButton.style.marginBottom = "7px";
+ }
+ if (UtilityEnabled("ResetType")) {
+ document.querySelector("#result-tab > thead > tr > th:nth-child(1)").remove();
+ document.querySelector("#result-tab > thead > tr > th:nth-child(2)").remove();
+ document.querySelector("#result-tab > thead > tr > th:nth-child(10)").innerHTML = "开启O2";
+ }
+ let Temp = document.querySelector("#result-tab > tbody").childNodes;
+ let SolutionIDs = [];
+ for (let i = 1; i < Temp.length; i += 2) {
+ let SID = Number(Temp[i].childNodes[1].innerText);
+ SolutionIDs.push(SID);
+ if (UtilityEnabled("ResetType")) {
+ Temp[i].childNodes[0].remove();
+ Temp[i].childNodes[0].innerHTML = "" + SID + " " + "重交";
+ Temp[i].childNodes[1].remove();
+ Temp[i].childNodes[1].children[0].removeAttribute("class");
+ Temp[i].childNodes[3].childNodes[0].innerText = SizeToStringSize(Temp[i].childNodes[3].childNodes[0].innerText);
+ Temp[i].childNodes[4].childNodes[0].innerText = TimeToStringTime(Temp[i].childNodes[4].childNodes[0].innerText);
+ Temp[i].childNodes[5].innerText = Temp[i].childNodes[5].childNodes[0].innerText;
+ Temp[i].childNodes[6].innerText = CodeSizeToStringSize(Temp[i].childNodes[6].innerText.substring(0, Temp[i].childNodes[6].innerText.length - 1));
+ Temp[i].childNodes[9].innerText = (Temp[i].childNodes[9].innerText == "" ? "否" : "是");
+ }
+ if (SearchParams.get("cid") === null) {
+ localStorage.setItem("UserScript-Solution-" + SID + "-Problem", Temp[i].childNodes[1].innerText);
+ } else {
+ localStorage.setItem("UserScript-Solution-" + SID + "-Contest", SearchParams.get("cid"));
+ localStorage.setItem("UserScript-Solution-" + SID + "-PID-Contest", Temp[i].childNodes[1].innerText.charAt(0));
+ }
+ }
+
+ if (UtilityEnabled("RefreshSolution")) {
+ let StdList;
+ await new Promise((Resolve) => {
+ RequestAPI("GetStdList", {}, async (Result) => {
+ if (Result.Success) {
+ StdList = Result.Data.StdList;
+ Resolve();
+ }
+ })
+ });
+
+ let Rows = document.getElementById("result-tab").rows;
+ let Points = Array();
+ for (let i = 1; i <= SolutionIDs.length; i++) {
+ Rows[i].cells[2].className = "td_result";
+ let SolutionID = SolutionIDs[i - 1];
+ if (Rows[i].cells[2].children.length == 2) {
+ Points[SolutionID] = Rows[i].cells[2].children[1].innerText;
+ Rows[i].cells[2].children[1].remove();
+ }
+ Rows[i].cells[2].innerHTML += "
";
+ setTimeout(() => {
+ RefreshResult(SolutionID);
+ }, 0);
+ }
+
+ let RefreshResult = async (SolutionID) => {
+ let CurrentRow = null;
+ let Rows = document.getElementById("result-tab").rows;
+ for (let i = 0; i < SolutionIDs.length; i++) {
+ if (SolutionIDs[i] == SolutionID) {
+ CurrentRow = Rows[i + 1];
+ break;
+ }
+ }
+ await fetch("status-ajax.php?solution_id=" + SolutionID)
+ .then((Response) => {
+ return Response.text();
+ })
+ .then((Response) => {
+ let PID = 0;
+ if (SearchParams.get("cid") === null) {
+ PID = localStorage.getItem("UserScript-Solution-" + SolutionID + "-Problem");
+ } else {
+ PID = localStorage.getItem("UserScript-Contest-" + SearchParams.get("cid") + "-Problem-" + (CurrentRow.cells[1].innerText.charCodeAt(0) - 65) + "-PID");
+ }
+ let ResponseData = Response.split(",");
+ CurrentRow.cells[3].innerHTML = "" + SizeToStringSize(ResponseData[1]) + "
";
+ CurrentRow.cells[4].innerHTML = "" + TimeToStringTime(ResponseData[2]) + "
";
+ let TempHTML = "";
+ TempHTML += judge_result[ResponseData[0]];
+ TempHTML += "";
+ if (Points[SolutionID] != undefined) {
+ TempHTML += "" + Points[SolutionID] + "";
+ if (Points[SolutionID].substring(0, Points[SolutionID].length - 1) >= 50) {
+ TempHTML += `查看标程`;
+ }
+ }
+ if (ResponseData[0] < 4) {
+ setTimeout(() => {
+ RefreshResult(SolutionID)
+ }, 500);
+ TempHTML += "
";
+ } else if (ResponseData[0] == 4 && UtilityEnabled("UploadStd")) {
+ if (SearchParams.get("cid") == null) CurrentRow.cells[1].innerText;
+ let Std = StdList.find((Element) => {
+ return Element == Number(PID);
+ });
+ if (Std != undefined) {
+ TempHTML += "✅";
+ } else {
+ RequestAPI("UploadStd", {
+ "ProblemID": Number(PID),
+ }, (Result) => {
+ if (Result.Success) {
+ CurrentRow.cells[2].innerHTML += "🆗";
+ } else {
+ CurrentRow.cells[2].innerHTML += "⚠️";
+ }
+ });
+ }
+ }
+ CurrentRow.cells[2].innerHTML = TempHTML;
+ });
+ };
+ }
+ }
+}
diff --git a/src/pages/submitpage.js b/src/pages/submitpage.js
new file mode 100644
index 00000000..1118f5c1
--- /dev/null
+++ b/src/pages/submitpage.js
@@ -0,0 +1,233 @@
+import { UtilityEnabled } from '../utils.js';
+import CodeMirror from 'codemirror';
+import { GM_xmlhttpRequest } from '$';
+
+export async function handleSubmitPage() {
+ const SearchParams = new URLSearchParams(location.search);
+ document.title = "提交代码: " + (SearchParams.get("id") != null ? "题目" + Number(SearchParams.get("id")) : "比赛" + Number(SearchParams.get("cid")));
+ document.querySelector("body > div > div.mt-3").innerHTML = `` + `提交代码
` + (SearchParams.get("id") != null ? `题目${Number(SearchParams.get("id"))}` : `比赛${Number(SearchParams.get("cid")) + ` 题目` + String.fromCharCode(65 + parseInt(SearchParams.get("pid")))}`) + `
+
+
+
+
+
+
+`;
+ if (UtilityEnabled("AutoO2")) {
+ document.querySelector("#enable_O2").checked = true;
+ }
+ let CodeMirrorElement;
+ (() => {
+ CodeMirrorElement = CodeMirror.fromTextArea(document.querySelector("#CodeInput"), {
+ lineNumbers: true,
+ matchBrackets: true,
+ mode: "text/x-c++src",
+ indentUnit: 4,
+ indentWithTabs: true,
+ enterMode: "keep",
+ tabMode: "shift",
+ theme: (UtilityEnabled("DarkMode") ? "darcula" : "default"),
+ extraKeys: {
+ "Ctrl-Space": "autocomplete", "Ctrl-Enter": function (instance) {
+ Submit.click();
+ }
+ }
+ })
+ })();
+ CodeMirrorElement.setSize("100%", "auto");
+ CodeMirrorElement.getWrapperElement().style.border = "1px solid #ddd";
+
+ if (SearchParams.get("sid") !== null) {
+ await fetch("https://www.xmoj.tech/getsource.php?id=" + SearchParams.get("sid"))
+ .then((Response) => {
+ return Response.text()
+ })
+ .then((Response) => {
+ CodeMirrorElement.setValue(Response.substring(0, Response.indexOf("/**************************************************************")).trim());
+ });
+ }
+
+ PassCheck.addEventListener("click", async () => {
+ ErrorElement.style.display = "none";
+ document.querySelector("#Submit").disabled = true;
+ document.querySelector("#Submit").value = "正在提交...";
+ let o2Switch = "&enable_O2=on";
+ if (!document.querySelector("#enable_O2").checked) o2Switch = "";
+ await fetch("https://www.xmoj.tech/submit.php", {
+ "headers": {
+ "content-type": "application/x-www-form-urlencoded"
+ },
+ "referrer": location.href,
+ "method": "POST",
+ "body": (SearchParams.get("id") != null ? "id=" + SearchParams.get("id") : "cid=" + SearchParams.get("cid") + "&pid=" + SearchParams.get("pid")) + "&language=1&" + "source=" + encodeURIComponent(CodeMirrorElement.getValue()) + o2Switch
+ }).then(async (Response) => {
+ if (Response.redirected) {
+ location.href = Response.url;
+ } else {
+ const text = await Response.text();
+ if (text.indexOf("没有这个比赛!") !== -1 && new URL(location.href).searchParams.get("pid") !== null) {
+ // Credit: https://github.com/boomzero/quicksubmit/blob/main/index.ts
+ // Also licensed under GPL-3.0
+ const contestReq = await fetch("https://www.xmoj.tech/contest.php?cid=" + new URL(location.href).searchParams.get("cid"));
+ const res = await contestReq.text();
+ if (
+ contestReq.status !== 200 ||
+ res.indexOf("比赛尚未开始或私有,不能查看题目。") !== -1
+ ) {
+ console.error(`Failed to get contest page!`);
+ return;
+ }
+ const parser = new DOMParser();
+ const dom = parser.parseFromString(res, "text/html");
+ const contestProblems = [];
+ const rows = (dom.querySelector(
+ "#problemset > tbody",
+ )).rows;
+ for (let i = 0; i < rows.length; i++) {
+ contestProblems.push(
+ rows[i].children[1].textContent.substring(2, 6).replaceAll(
+ "\t",
+ "",
+ ),
+ );
+ }
+ rPID = contestProblems[new URL(location.href).searchParams.get("pid")];
+ if (UtilityEnabled("DebugMode")) {
+ console.log("Contest Problems:", contestProblems);
+ console.log("Real PID:", rPID);
+ }
+ ErrorElement.style.display = "block";
+ ErrorMessage.style.color = "red";
+ ErrorMessage.innerText = "比赛已结束, 正在尝试像题目 " + rPID + " 提交";
+ console.log("比赛已结束, 正在尝试像题目 " + rPID + " 提交");
+ let o2Switch = "&enable_O2=on";
+ if (!document.querySelector("#enable_O2").checked) o2Switch = "";
+ await fetch("https://www.xmoj.tech/submit.php", {
+ "headers": {
+ "content-type": "application/x-www-form-urlencoded"
+ },
+ "referrer": location.href,
+ "method": "POST",
+ "body": "id=" + rPID + "&language=1&" + "source=" + encodeURIComponent(CodeMirrorElement.getValue()) + o2Switch
+ }).then(async (Response) => {
+ if (Response.redirected) {
+ location.href = Response.url;
+ }
+ console.log(await Response.text());
+ });
+
+ }
+ if (UtilityEnabled("DebugMode")) {
+ console.log("Submission failed! Response:", text);
+ }
+ ErrorElement.style.display = "block";
+ ErrorMessage.style.color = "red";
+ ErrorMessage.innerText = "提交失败!请关闭脚本后重试!";
+ Submit.disabled = false;
+ Submit.value = "提交";
+ }
+ })
+ });
+
+ Submit.addEventListener("click", async () => {
+ PassCheck.style.display = "none";
+ ErrorElement.style.display = "none";
+ document.querySelector("#Submit").disabled = true;
+ document.querySelector("#Submit").value = "正在检查...";
+ let Source = CodeMirrorElement.getValue();
+ let PID = 0;
+ let IOFilename = "";
+ if (SearchParams.get("cid") != null && SearchParams.get("pid") != null) {
+ PID = localStorage.getItem("UserScript-Contest-" + SearchParams.get("cid") + "-Problem-" + SearchParams.get("pid") + "-PID")
+ } else {
+ PID = SearchParams.get("id");
+ }
+ IOFilename = localStorage.getItem("UserScript-Problem-" + PID + "-IOFilename");
+ if (UtilityEnabled("IOFile") && IOFilename != null) {
+ if (Source.indexOf(IOFilename) == -1) {
+ PassCheck.style.display = "";
+ ErrorElement.style.display = "block";
+ if (UtilityEnabled("DarkMode")) ErrorMessage.style.color = "yellow"; else ErrorMessage.style.color = "red";
+ ErrorMessage.innerText = "此题输入输出文件名为" + IOFilename + ",请检查是否填错";
+
+ let freopenText = document.createElement('small');
+ if (UtilityEnabled("DarkMode")) freopenText.style.color = "white"; else freopenText.style.color = "black";
+ freopenText.textContent = '\n您也可以复制freopen语句。\n';
+ document.getElementById('ErrorMessage').appendChild(freopenText);
+ let copyFreopenButton = document.createElement("button");
+ copyFreopenButton.className = "btn btn-sm btn-outline-secondary copy-btn";
+ copyFreopenButton.innerText = "复制代码";
+ copyFreopenButton.style.marginLeft = "10px";
+ copyFreopenButton.style.marginTop = "10px";
+ copyFreopenButton.style.marginBottom = "10px";
+ copyFreopenButton.type = "button";
+ copyFreopenButton.addEventListener("click", () => {
+ navigator.clipboard.writeText('\n freopen("' + IOFilename + '.in", "r", stdin);\n freopen("' + IOFilename + '.out", "w", stdout);');
+ copyFreopenButton.innerText = "复制成功";
+ setTimeout(() => {
+ copyFreopenButton.innerText = "复制代码";
+ }, 1500);
+ });
+ document.getElementById('ErrorMessage').appendChild(copyFreopenButton);
+ let freopenCodeField = CodeMirror(document.getElementById('ErrorMessage'), {
+ value: 'freopen("' + IOFilename + '.in", "r", stdin);\nfreopen("' + IOFilename + '.out", "w", stdout);',
+ mode: 'text/x-c++src',
+ theme: (UtilityEnabled("DarkMode") ? "darcula" : "default"),
+ readOnly: true,
+ lineNumbers: true
+ });
+ freopenCodeField.setSize("100%", "auto");
+ document.querySelector("#Submit").disabled = false;
+ document.querySelector("#Submit").value = "提交";
+ return false;
+ } else if (RegExp("//.*freopen").test(Source)) {
+ PassCheck.style.display = "";
+ ErrorElement.style.display = "block";
+ if (UtilityEnabled("DarkMode")) ErrorMessage.style.color = "yellow"; else ErrorMessage.style.color = "red";
+ ErrorMessage.innerText = "请不要注释freopen语句";
+ document.querySelector("#Submit").disabled = false;
+ document.querySelector("#Submit").value = "提交";
+ return false;
+ }
+ }
+ if (Source == "") {
+ PassCheck.style.display = "";
+ ErrorElement.style.display = "block";
+ if (UtilityEnabled("DarkMode")) ErrorMessage.style.color = "yellow"; else ErrorMessage.style.color = "red";
+ ErrorMessage.innerText = "源代码为空";
+ document.querySelector("#Submit").disabled = false;
+ document.querySelector("#Submit").value = "提交";
+ return false;
+ }
+ if (UtilityEnabled("CompileError")) {
+ let ResponseData = await new Promise((Resolve) => {
+ GM_xmlhttpRequest({
+ method: "POST", url: "https://cppinsights.io/api/v1/transform", headers: {
+ "content-type": "application/json;charset=UTF-8"
+ }, referrer: "https://cppinsights.io/", data: JSON.stringify({
+ "insightsOptions": ["cpp14"], "code": Source
+ }), onload: (Response) => {
+ Resolve(Response);
+ }
+ });
+ });
+ let Response = JSON.parse(ResponseData.responseText);
+ if (Response.returncode) {
+ PassCheck.style.display = "";
+ ErrorElement.style.display = "block";
+ if (UtilityEnabled("DarkMode")) ErrorMessage.style.color = "yellow"; else ErrorMessage.style.color = "red";
+ ErrorMessage.innerText = "编译错误:\n" + Response.stderr.trim();
+ document.querySelector("#Submit").disabled = false;
+ document.querySelector("#Submit").value = "提交";
+ return false;
+ } else {
+ PassCheck.click();
+ }
+ } else {
+ PassCheck.click();
+ }
+ });
+}
diff --git a/src/pages/userinfo.js b/src/pages/userinfo.js
new file mode 100644
index 00000000..eb6b1da4
--- /dev/null
+++ b/src/pages/userinfo.js
@@ -0,0 +1,229 @@
+import { UtilityEnabled, GetUserInfo, GetUserBadge, SmartAlert } from '../utils.js';
+import { RequestAPI } from '../api.js';
+
+export async function handleUserInfoPage() {
+ const SearchParams = new URLSearchParams(location.search);
+ if (SearchParams.get("ByUserScript") === null) {
+ if (UtilityEnabled("RemoveUseless")) {
+ let Temp = document.getElementById("submission").childNodes;
+ for (let i = 0; i < Temp.length; i++) {
+ Temp[i].remove();
+ }
+ }
+ // This eval is necessary to execute the inline script on the page that sets up
+ // the solved problems chart. It's a legacy part of the original site.
+ eval(document.querySelector("body > script:nth-child(5)").innerHTML);
+ document.querySelector("#statics > tbody > tr:nth-child(1)").remove();
+
+ let Temp = document.querySelector("#statics > tbody").children;
+ for (let i = 0; i < Temp.length; i++) {
+ if (Temp[i].children[0] != undefined) {
+ if (Temp[i].children[0].innerText == "Statistics") {
+ Temp[i].children[0].innerText = "统计";
+ } else if (Temp[i].children[0].innerText == "Email:") {
+ Temp[i].children[0].innerText = "电子邮箱";
+ }
+ Temp[i].children[1].removeAttribute("align");
+ }
+ }
+
+ Temp = document.querySelector("#statics > tbody > tr:nth-child(1) > td:nth-child(3)").childNodes;
+ let ACProblems = [];
+ for (let i = 0; i < Temp.length; i++) {
+ if (Temp[i].tagName == "A" && Temp[i].href.indexOf("problem.php?id=") != -1) {
+ ACProblems.push(Number(Temp[i].innerText.trim()));
+ }
+ }
+ document.querySelector("#statics > tbody > tr:nth-child(1) > td:nth-child(3)").remove();
+
+ let UserID, UserNick;
+ [UserID, UserNick] = document.querySelector("#statics > caption").childNodes[0].data.trim().split("--");
+ document.querySelector("#statics > caption").remove();
+ document.title = "用户 " + UserID + " 的个人中心";
+ let Row = document.createElement("div");
+ Row.className = "row";
+ let LeftDiv = document.createElement("div");
+ LeftDiv.className = "col-md-5";
+ Row.appendChild(LeftDiv);
+
+ let LeftTopDiv = document.createElement("div");
+ LeftTopDiv.className = "row mb-2";
+ LeftDiv.appendChild(LeftTopDiv);
+ let AvatarContainer = document.createElement("div");
+ AvatarContainer.classList.add("col-auto");
+ let AvatarElement = document.createElement("img");
+ let UserEmailHash = (await GetUserInfo(UserID)).EmailHash;
+ if (UserEmailHash == undefined) {
+ AvatarElement.src = `https://cravatar.cn/avatar/00000000000000000000000000000000?d=mp&f=y`;
+ } else {
+ AvatarElement.src = `https://cravatar.cn/avatar/${UserEmailHash}?d=retro`;
+ }
+ AvatarElement.classList.add("rounded", "me-2");
+ AvatarElement.style.height = "120px";
+ AvatarContainer.appendChild(AvatarElement);
+ LeftTopDiv.appendChild(AvatarContainer);
+
+ let UserInfoElement = document.createElement("div");
+ UserInfoElement.classList.add("col-auto");
+ UserInfoElement.style.lineHeight = "40px";
+ UserInfoElement.innerHTML += "用户名:" + UserID + "
";
+ UserInfoElement.innerHTML += "昵称:" + UserNick + "
";
+ if (UtilityEnabled("Rating")) {
+ UserInfoElement.innerHTML += "评分:" + ((await GetUserInfo(UserID)).Rating) + "
";
+ }
+ // Create a placeholder for the last online time
+ let lastOnlineElement = document.createElement('div');
+ lastOnlineElement.innerHTML = "最后在线:加载中...
";
+ UserInfoElement.appendChild(lastOnlineElement);
+ let BadgeInfo = await GetUserBadge(UserID);
+ if (window.IsAdmin) {
+ if (BadgeInfo.Content !== "") {
+ let DeleteBadgeButton = document.createElement("button");
+ DeleteBadgeButton.className = "btn btn-outline-danger btn-sm";
+ DeleteBadgeButton.innerText = "删除标签";
+ DeleteBadgeButton.addEventListener("click", async () => {
+ if (confirm("您确定要删除此标签吗?")) {
+ RequestAPI("DeleteBadge", {
+ "UserID": UserID
+ }, (Response) => {
+ if (UtilityEnabled("DebugMode")) console.log(Response);
+ if (Response.Success) {
+ let Temp = [];
+ for (let i = 0; i < localStorage.length; i++) {
+ if (localStorage.key(i).startsWith("UserScript-User-" + UserID + "-Badge-")) {
+ Temp.push(localStorage.key(i));
+ }
+ }
+ for (let i = 0; i < Temp.length; i++) {
+ localStorage.removeItem(Temp[i]);
+ }
+ window.location.reload();
+ } else {
+ SmartAlert(Response.Message);
+ }
+ });
+ }
+ });
+ UserInfoElement.appendChild(DeleteBadgeButton);
+ } else {
+ let AddBadgeButton = document.createElement("button");
+ AddBadgeButton.className = "btn btn-outline-primary btn-sm";
+ AddBadgeButton.innerText = "添加标签";
+ AddBadgeButton.addEventListener("click", async () => {
+ RequestAPI("NewBadge", {
+ "UserID": UserID
+ }, (Response) => {
+ if (Response.Success) {
+ let Temp = [];
+ for (let i = 0; i < localStorage.length; i++) {
+ if (localStorage.key(i).startsWith("UserScript-User-" + UserID + "-Badge-")) {
+ Temp.push(localStorage.key(i));
+ }
+ }
+ for (let i = 0; i < Temp.length; i++) {
+ localStorage.removeItem(Temp[i]);
+ }
+ window.location.reload();
+ } else {
+ SmartAlert(Response.Message);
+ }
+ });
+ });
+ UserInfoElement.appendChild(AddBadgeButton);
+ }
+ }
+ RequestAPI("LastOnline", {"Username": UserID}, (result) => {
+ if (result.Success) {
+ if (UtilityEnabled("DebugMode")) {
+ console.log('lastOnline:' + result.Data.logintime);
+ }
+ lastOnlineElement.innerHTML = "最后在线:" + GetRelativeTime(result.Data.logintime) + "
";
+ } else {
+ lastOnlineElement.innerHTML = "最后在线:近三个月内从未
";
+ }
+ });
+ LeftTopDiv.appendChild(UserInfoElement);
+ LeftDiv.appendChild(LeftTopDiv);
+
+ let LeftTable = document.querySelector("body > div > div > center > table");
+ LeftDiv.appendChild(LeftTable);
+ let RightDiv = document.createElement("div");
+ RightDiv.className = "col-md-7";
+ Row.appendChild(RightDiv);
+ RightDiv.innerHTML = "已解决题目
";
+ for (let i = 0; i < ACProblems.length; i++) {
+ RightDiv.innerHTML += "" + ACProblems[i] + " ";
+ }
+ document.querySelector("body > div > div").innerHTML = "";
+ document.querySelector("body > div > div").appendChild(Row);
+ } else {
+ document.title = "上传标程";
+ document.querySelector("body > div > div.mt-3").innerHTML = `
+
+
+
+ 您必须要上传标程以后才能使用“查看标程”功能。点击“上传标程”按钮以后,系统会自动上传标程,请您耐心等待。
+ 首次上传标程可能会比较慢,请耐心等待。后续将可以自动上传AC代码。
+ 系统每过30天会自动提醒您上传标程,您必须要上传标程,否则将会被禁止使用“查看标程”功能。
+
`;
+ UploadStd.addEventListener("click", async () => {
+ UploadStd.disabled = true;
+ ErrorElement.style.display = "none";
+ ErrorElement.innerText = "";
+ UploadProgress.classList.remove("bg-success");
+ UploadProgress.classList.remove("bg-warning");
+ UploadProgress.classList.remove("bg-danger");
+ UploadProgress.classList.add("progress-bar-animated");
+ UploadProgress.style.width = "0%";
+ UploadProgress.innerText = "0%";
+ let ACList = [];
+ await fetch("https://www.xmoj.tech/userinfo.php?user=" + window.CurrentUsername)
+ .then((Response) => {
+ return Response.text();
+ }).then((Response) => {
+ let ParsedDocument = new DOMParser().parseFromString(Response, "text/html");
+ let ScriptData = ParsedDocument.querySelector("#statics > tbody > tr:nth-child(2) > td:nth-child(3) > script").innerText;
+ ScriptData = ScriptData.substr(ScriptData.indexOf("}") + 1).trim();
+ ScriptData = ScriptData.split(";");
+ for (let i = 0; i < ScriptData.length; i++) {
+ ACList.push(Number(ScriptData[i].substring(2, ScriptData[i].indexOf(","))));
+ }
+ });
+ RequestAPI("GetStdList", {}, async (Result) => {
+ if (Result.Success) {
+ let StdList = Result.Data.StdList;
+ for (let i = 0; i < ACList.length; i++) {
+ if (StdList.indexOf(ACList[i]) === -1 && ACList[i] !== 0) {
+ await new Promise((Resolve) => {
+ RequestAPI("UploadStd", {
+ "ProblemID": Number(ACList[i])
+ }, (Result) => {
+ if (!Result.Success) {
+ ErrorElement.style.display = "block";
+ ErrorElement.innerText += Result.Message + "\n";
+ UploadProgress.classList.add("bg-warning");
+ }
+ UploadProgress.innerText = (i / ACList.length * 100).toFixed(1) + "% (" + ACList[i] + ")";
+ UploadProgress.style.width = (i / ACList.length * 100) + "%";
+ Resolve();
+ });
+ });
+ }
+ }
+ UploadProgress.classList.add("bg-success");
+ UploadProgress.classList.remove("progress-bar-animated");
+ UploadProgress.innerText = "100%";
+ UploadProgress.style.width = "100%";
+ UploadStd.disabled = false;
+ localStorage.setItem("UserScript-LastUploadedStdTime", new Date().getTime());
+ } else {
+ ErrorElement.style.display = "block";
+ ErrorElement.innerText = Result.Message;
+ UploadStd.disabled = false;
+ }
+ });
+ });
+ }
+}
diff --git a/src/styles.js b/src/styles.js
new file mode 100644
index 00000000..4bdc6458
--- /dev/null
+++ b/src/styles.js
@@ -0,0 +1,131 @@
+import { UtilityEnabled, SmartAlert } from './utils';
+
+export class NavbarStyler {
+ constructor() {
+ try {
+ this.navbar = document.querySelector('.navbar.navbar-expand-lg.bg-body-tertiary');
+ if (this.navbar && UtilityEnabled("NewTopBar")) {
+ this.init();
+ }
+ } catch (e) {
+ console.error(e);
+ if (UtilityEnabled("DebugMode")) {
+ SmartAlert("XMOJ-Script internal error!\n\n" + e + "\n\n" + "If you see this message, please report it to the developer.\nDon't forget to include console logs and a way to reproduce the error!\n\nDon't want to see this message? Disable DebugMode.");
+ }
+ }
+ }
+
+ init() {
+ try {
+ this.applyStyles();
+ this.createOverlay();
+ this.createSpacer();
+ window.addEventListener('resize', () => this.updateBlurOverlay());
+ this.updateBlurOverlay();
+ } catch (e) {
+ console.error(e);
+ if (UtilityEnabled("DebugMode")) {
+ SmartAlert("XMOJ-Script internal error!\n\n" + e + "\n\n" + "If you see this message, please report it to the developer.\nDon't forget to include console logs and a way to reproduce the error!\n\nDon't want to see this message? Disable DebugMode.");
+ }
+ }
+ }
+
+ applyStyles() {
+ try {
+ let n = this.navbar;
+ n.classList.add('fixed-top', 'container', 'ml-auto');
+ Object.assign(n.style, {
+ position: 'fixed',
+ borderRadius: '28px',
+ boxShadow: '0 4px 8px rgba(0, 0, 0, 0.5)',
+ margin: '16px auto',
+ backgroundColor: 'rgba(255, 255, 255, 0)',
+ opacity: '0.75',
+ zIndex: '1000'
+ });
+ } catch (e) {
+ console.error(e);
+ if (UtilityEnabled("DebugMode")) {
+ SmartAlert("XMOJ-Script internal error!\n\n" + e + "\n\n" + "If you see this message, please report it to the developer.\nDon't forget to include console logs and a way to reproduce the error!\n\nDon't want to see this message? Disable DebugMode.");
+ }
+ }
+ }
+
+ createOverlay() {
+ try {
+ if (!document.getElementById('blur-overlay')) {
+ let overlay = document.createElement('div');
+ overlay.id = 'blur-overlay';
+ document.body.appendChild(overlay);
+
+ let style = document.createElement('style');
+ style.textContent = `
+ #blur-overlay {
+ position: fixed;
+ backdrop-filter: blur(4px);
+ z-index: 999;
+ pointer-events: none;
+ border-radius: 28px;
+ }
+ `;
+ document.head.appendChild(style);
+ }
+ } catch (e) {
+ console.error(e);
+ if (UtilityEnabled("DebugMode")) {
+ SmartAlert("XMOJ-Script internal error!\n\n" + e + "\n\n" + "If you see this message, please report it to the developer.\nDon't forget to include console logs and a way to reproduce the error!\n\nDon't want to see this message? Disable DebugMode.");
+ }
+ }
+ }
+
+ updateBlurOverlay() {
+ try {
+ let overlay = document.getElementById('blur-overlay');
+ let n = this.navbar;
+ Object.assign(overlay.style, {
+ top: `${n.offsetTop}px`,
+ left: `${n.offsetLeft}px`,
+ width: `${n.offsetWidth}px`,
+ height: `${n.offsetHeight}px`
+ });
+ } catch (e) {
+ console.error(e);
+ if (UtilityEnabled("DebugMode")) {
+ SmartAlert("XMOJ-Script internal error!\n\n" + e + "\n\n" + "If you see this message, please report it to the developer.\nDon't forget to include console logs and a way to reproduce the error!\n\nDon't want to see this message? Disable DebugMode.");
+ }
+ }
+ }
+
+ createSpacer() {
+ try {
+ let spacer = document.getElementById('navbar-spacer');
+ let newHeight = this.navbar.offsetHeight + 24;
+ if (!spacer) {
+ spacer = document.createElement('div');
+ spacer.id = 'navbar-spacer';
+ spacer.style.height = `${newHeight}px`;
+ spacer.style.width = '100%';
+ document.body.insertBefore(spacer, document.body.firstChild);
+ } else {
+ let currentHeight = parseInt(spacer.style.height, 10);
+ if (currentHeight !== newHeight) {
+ document.body.removeChild(spacer);
+ spacer = document.createElement('div');
+ spacer.id = 'navbar-spacer';
+ spacer.style.height = `${newHeight}px`;
+ spacer.style.width = '100%';
+ document.body.insertBefore(spacer, document.body.firstChild);
+ }
+ }
+ } catch (e) {
+ console.error(e);
+ if (UtilityEnabled("DebugMode")) {
+ SmartAlert("XMOJ-Script internal error!\n\n" + e + "\n\n" + "If you see this message, please report it to the developer.\nDon't forget to include console logs and a way to reproduce the error!\n\nDon't want to see this message? Disable DebugMode.");
+ }
+ }
+ }
+}
+
+export function replaceMarkdownImages(text, string) {
+ return text.replace(/!\[.*?\]\(.*?\)/g, string);
+}
diff --git a/src/theme.js b/src/theme.js
new file mode 100644
index 00000000..a1953d4d
--- /dev/null
+++ b/src/theme.js
@@ -0,0 +1,19 @@
+const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
+
+const applyTheme = (theme) => {
+ document.querySelector("html").setAttribute("data-bs-theme", theme);
+ localStorage.setItem("UserScript-Setting-DarkMode", theme === "dark" ? "true" : "false");
+};
+
+const applySystemTheme = (e) => applyTheme(e.matches ? "dark" : "light");
+
+export const initTheme = () => {
+ const saved = localStorage.getItem("UserScript-Setting-Theme") || "auto";
+ if (saved === "auto") {
+ applyTheme(prefersDark.matches ? "dark" : "light");
+ prefersDark.addEventListener("change", applySystemTheme);
+ } else {
+ applyTheme(saved);
+ prefersDark.removeEventListener("change", applySystemTheme);
+ }
+};
diff --git a/src/utils.js b/src/utils.js
new file mode 100644
index 00000000..7b81f476
--- /dev/null
+++ b/src/utils.js
@@ -0,0 +1,397 @@
+export const escapeHTML = (str) => {
+ return str.replace(/[&<>"']/g, function (match) {
+ const escape = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": '''
+ };
+ return escape[match];
+ });
+};
+
+export const PurifyHTML = (Input) => {
+ try {
+ return DOMPurify.sanitize(Input, {
+ "ALLOWED_TAGS": ["a", "b", "big", "blockquote", "br", "code", "dd", "del", "div", "dl", "dt", "em", "h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8", "hr", "i", "img", "ins", "kbd", "li", "ol", "p", "pre", "q", "rp", "rt", "ruby", "s", "samp", "strike", "strong", "sub", "sup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "tt", "ul", "var"],
+ "ALLOWED_ATTR": ["abbr", "accept", "accept-charset", "accesskey", "action", "align", "alt", "axis", "border", "cellpadding", "cellspacing", "char", "charoff", "charset", "checked", "cite", "clear", "color", "cols", "colspan", "compact", "coords", "datetime", "dir", "disabled", "enctype", "for", "frame", "headers", "height", "href", "hreflang", "hspace", "ismap", "itemprop", "label", "lang", "longdesc", "maxlength", "media", "method", "multiple", "name", "nohref", "noshade", "nowrap", "prompt", "readonly", "rel", "rev", "rows", "rowspan", "rules", "scope", "selected", "shape", "size", "span", "src", "start", "summary", "tabindex", "target", "title", "type", "usemap", "valign", "value", "vspace", "width"]
+ });
+ } catch (e) {
+ console.error(e);
+ if (UtilityEnabled("DebugMode")) {
+ SmartAlert("XMOJ-Script internal error!\n\n" + e + "\n\n" + "If you see this message, please report it to the developer.\nDon't forget to include console logs and a way to reproduce the error!\n\nDon't want to see this message? Disable DebugMode.");
+ }
+ }
+}
+
+export const SmartAlert = (Message) => {
+ if (localStorage.getItem("UserScript-Alert") !== Message) {
+ alert(Message);
+ }
+ localStorage.setItem("UserScript-Alert", Message);
+}
+
+export const GetRelativeTime = (Input) => {
+ try {
+ Input = new Date(Input);
+ let Now = new Date().getTime();
+ let Delta = Now - Input.getTime();
+ let RelativeName = "";
+ if (Delta < 0) {
+ RelativeName = "未来";
+ } else if (Delta <= 1000 * 60) {
+ RelativeName = "刚刚";
+ } else if (Delta <= 1000 * 60 * 60) {
+ RelativeName = Math.floor((Now - Input) / 1000 / 60) + "分钟前";
+ } else if (Delta <= 1000 * 60 * 60 * 24) {
+ RelativeName = Math.floor((Now - Input) / 1000 / 60 / 60) + "小时前";
+ } else if (Delta <= 1000 * 60 * 60 * 24 * 31) {
+ RelativeName = Math.floor((Now - Input) / 1000 / 60 / 60 / 24) + "天前";
+ } else if (Delta <= 1000 * 60 * 60 * 24 * 365) {
+ RelativeName = Math.floor((Now - Input) / 1000 / 60 / 60 / 24 / 31) + "个月前";
+ } else {
+ RelativeName = Math.floor((Now - Input) / 1000 / 60 / 60 / 24 / 365) + "年前";
+ }
+ return "" + RelativeName + "";
+ } catch (e) {
+ console.error(e);
+ if (UtilityEnabled("DebugMode")) {
+ SmartAlert("XMOJ-Script internal error!\n\n" + e + "\n\n" + "If you see this message, please report it to the developer.\nDon't forget to include console logs and a way to reproduce the error!\n\nDon't want to see this message? Disable DebugMode.");
+ }
+ }
+};
+
+export function compareVersions(currVer, remoteVer) {
+ const currParts = currVer.split('.').map(Number);
+ const remoteParts = remoteVer.split('.').map(Number);
+
+ const maxLen = Math.max(currParts.length, remoteParts.length);
+ for (let i = 0; i < maxLen; i++) {
+ const curr = currParts[i] !== undefined ? currParts[i] : 0;
+ const remote = remoteParts[i] !== undefined ? remoteParts[i] : 0;
+ if (remote > curr) {
+ return true; // update needed
+ } else if (remote < curr) {
+ return false; // no update needed
+ }
+ }
+ return false; // versions are equal
+}
+
+export const RenderMathJax = async () => {
+ try {
+ if (document.getElementById("MathJax-script") === null) {
+ var ScriptElement = document.createElement("script");
+ ScriptElement.id = "MathJax-script";
+ ScriptElement.type = "text/javascript";
+ ScriptElement.src = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.0.5/es5/tex-chtml.js";
+ document.body.appendChild(ScriptElement);
+ await new Promise((Resolve) => {
+ ScriptElement.onload = () => {
+ Resolve();
+ };
+ });
+ }
+ if (MathJax !== undefined) { //If there is a Math expression
+ MathJax.startup.input[0].findTeX.options.inlineMath.push(["$", "$"]);
+ MathJax.startup.input[0].findTeX.getPatterns();
+ MathJax.typeset();
+ }
+ } catch (e) {
+ console.error(e);
+ }
+};
+
+export const GetUserInfo = async (Username) => {
+ try {
+ if (localStorage.getItem("UserScript-User-" + Username + "-UserRating") != null && new Date().getTime() - parseInt(localStorage.getItem("UserScript-User-" + Username + "-LastUpdateTime")) < 1000 * 60 * 60 * 24) {
+ return {
+ "Rating": localStorage.getItem("UserScript-User-" + Username + "-UserRating"),
+ "EmailHash": localStorage.getItem("UserScript-User-" + Username + "-EmailHash")
+ }
+ }
+ return await fetch("https://www.xmoj.tech/userinfo.php?user=" + Username).then((Response) => {
+ return Response.text();
+ }).then((Response) => {
+ if (Response.indexOf("No such User!") !== -1) {
+ return null;
+ }
+ const ParsedDocument = new DOMParser().parseFromString(Response, "text/html");
+ let Rating = (parseInt(ParsedDocument.querySelector("#statics > tbody > tr:nth-child(4) > td:nth-child(2)").innerText.trim()) / parseInt(ParsedDocument.querySelector("#statics > tbody > tr:nth-child(3) > td:nth-child(2)").innerText.trim())).toFixed(3) * 1000;
+ let Temp = ParsedDocument.querySelector("#statics > tbody").children;
+ let Email = Temp[Temp.length - 1].children[1].innerText.trim();
+ let EmailHash = CryptoJS.MD5(Email).toString();
+ localStorage.setItem("UserScript-User-" + Username + "-UserRating", Rating);
+ if (Email == "") {
+ EmailHash = undefined;
+ } else {
+ localStorage.setItem("UserScript-User-" + Username + "-EmailHash", EmailHash);
+ }
+ localStorage.setItem("UserScript-User-" + Username + "-LastUpdateTime", new Date().getTime());
+ return {
+ "Rating": Rating, "EmailHash": EmailHash
+ }
+ });
+ } catch (e) {
+ console.error(e);
+ if (UtilityEnabled("DebugMode")) {
+ SmartAlert("XMOJ-Script internal error!\n\n" + e + "\n\n" + "If you see this message, please report it to the developer.\nDon't forget to include console logs and a way to reproduce the error!\n\nDon't want to see this message? Disable DebugMode.");
+ }
+ }
+};
+
+export const GetUserBadge = async (Username) => {
+ try {
+ if (localStorage.getItem("UserScript-User-" + Username + "-Badge-LastUpdateTime") != null && new Date().getTime() - parseInt(localStorage.getItem("UserScript-User-" + Username + "-Badge-LastUpdateTime")) < 1000 * 60 * 60 * 24) {
+ return {
+ "BackgroundColor": localStorage.getItem("UserScript-User-" + Username + "-Badge-BackgroundColor"),
+ "Color": localStorage.getItem("UserScript-User-" + Username + "-Badge-Color"),
+ "Content": localStorage.getItem("UserScript-User-" + Username + "-Badge-Content")
+ }
+ } else {
+ let BackgroundColor = "";
+ let Color = "";
+ let Content = "";
+ await new Promise((Resolve) => {
+ RequestAPI("GetBadge", {
+ "UserID": String(Username)
+ }, (Response) => {
+ if (Response.Success) {
+ BackgroundColor = Response.Data.BackgroundColor;
+ Color = Response.Data.Color;
+ Content = Response.Data.Content;
+ }
+ Resolve();
+ });
+ });
+ localStorage.setItem("UserScript-User-" + Username + "-Badge-BackgroundColor", BackgroundColor);
+ localStorage.setItem("UserScript-User-" + Username + "-Badge-Color", Color);
+ localStorage.setItem("UserScript-User-" + Username + "-Badge-Content", Content);
+ localStorage.setItem("UserScript-User-" + Username + "-Badge-LastUpdateTime", String(new Date().getTime()));
+ return {
+ "BackgroundColor": BackgroundColor, "Color": Color, "Content": Content
+ }
+ }
+ } catch (e) {
+ console.error(e);
+ if (UtilityEnabled("DebugMode")) {
+ SmartAlert("XMOJ-Script internal error!\n\n" + e + "\n\n" + "If you see this message, please report it to the developer.\nDon't forget to include console logs and a way to reproduce the error!\n\nDon't want to see this message? Disable DebugMode.");
+ }
+ }
+};
+
+export const GetUsernameHTML = async (Element, Username, Simple = false, Href = "https://www.xmoj.tech/userinfo.php?user=") => {
+ try {
+ //Username = Username.replaceAll(/[^a-zA-Z0-9]/g, "");
+ let ID = "Username-" + Username + "-" + Math.random();
+ Element.id = ID;
+ Element.innerHTML = ``;
+ Element.appendChild(document.createTextNode(Username));
+ let UserInfo = await GetUserInfo(Username);
+ if (UserInfo === null) {
+ document.getElementById(ID).innerHTML = "";
+ document.getElementById(ID).appendChild(document.createTextNode(Username));
+ return;
+ }
+ let HTMLData = "";
+ if (!Simple) {
+ HTMLData += `
`;
+ }
+ HTMLData += ` 500) {
+ HTMLData += "link-danger";
+ } else if (Rating >= 400) {
+ HTMLData += "link-warning";
+ } else if (Rating >= 300) {
+ HTMLData += "link-success";
+ } else {
+ HTMLData += "link-info";
+ }
+ } else {
+ HTMLData += "link-info";
+ }
+ HTMLData += `\";">`;
+ if (!Simple) {
+ if (AdminUserList.includes(Username)) {
+ HTMLData += `脚本管理员`;
+ }
+ let BadgeInfo = await GetUserBadge(Username);
+ if (BadgeInfo.Content != "") {
+ HTMLData += `${BadgeInfo.Content}`;
+ }
+ }
+ if (document.getElementById(ID) !== null) {
+ document.getElementById(ID).innerHTML = HTMLData;
+ document.getElementById(ID).getElementsByTagName("a")[0].appendChild(document.createTextNode(Username));
+ }
+ } catch (e) {
+ console.error(e);
+ if (UtilityEnabled("DebugMode")) {
+ SmartAlert("XMOJ-Script internal error!\n\n" + e + "\n\n" + "If you see this message, please report it to the developer.\nDon't forget to include console logs and a way to reproduce the error!\n\nDon't want to see this message? Disable DebugMode.");
+ }
+ }
+};
+
+export const SecondsToString = (InputSeconds) => {
+ try {
+ let Hours = Math.floor(InputSeconds / 3600);
+ let Minutes = Math.floor((InputSeconds % 3600) / 60);
+ let Seconds = InputSeconds % 60;
+ return (Hours < 10 ? "0" : "") + Hours + ":" + (Minutes < 10 ? "0" : "") + Minutes + ":" + (Seconds < 10 ? "0" : "") + Seconds;
+ } catch (e) {
+ console.error(e);
+ if (UtilityEnabled("DebugMode")) {
+ SmartAlert("XMOJ-Script internal error!\n\n" + e + "\n\n" + "If you see this message, please report it to the developer.\nDon't forget to include console logs and a way to reproduce the error!\n\nDon't want to see this message? Disable DebugMode.");
+ }
+ }
+}
+
+export const StringToSeconds = (InputString) => {
+ try {
+ let SplittedString = InputString.split(":");
+ return parseInt(SplittedString[0]) * 60 * 60 + parseInt(SplittedString[1]) * 60 + parseInt(SplittedString[2]);
+ } catch (e) {
+ console.error(e);
+ if (UtilityEnabled("DebugMode")) {
+ SmartAlert("XMOJ-Script internal error!\n\n" + e + "\n\n" + "If you see this message, please report it to the developer.\nDon't forget to include console logs and a way to reproduce the error!\n\nDon't want to see this message? Disable DebugMode.");
+ }
+ }
+}
+
+export const SizeToStringSize = (Memory) => {
+ try {
+ if (UtilityEnabled("AddUnits")) {
+ if (Memory < 1024) {
+ return Memory + "KB";
+ } else if (Memory < 1024 * 1024) {
+ return (Memory / 1024).toFixed(2) + "MB";
+ } else if (Memory < 1024 * 1024 * 1024) {
+ return (Memory / 1024 / 1024).toFixed(2) + "GB";
+ } else {
+ return (Memory / 1024 / 1024 / 1024).toFixed(2) + "TB";
+ }
+ } else {
+ return Memory;
+ }
+ } catch (e) {
+ console.error(e);
+ if (UtilityEnabled("DebugMode")) {
+ SmartAlert("XMOJ-Script internal error!\n\n" + e + "\n\n" + "If you see this message, please report it to the developer.\nDon't forget to include console logs and a way to reproduce the error!\n\nDon't want to see this message? Disable DebugMode.");
+ }
+ }
+};
+
+export const CodeSizeToStringSize = (Memory) => {
+ try {
+ if (UtilityEnabled("AddUnits")) {
+ if (Memory < 1024) {
+ return Memory + "B";
+ } else if (Memory < 1024 * 1024) {
+ return (Memory / 1024).toFixed(2) + "KB";
+ } else if (Memory < 1024 * 1024 * 1024) {
+ return (Memory / 1024 / 1024).toFixed(2) + "MB";
+ } else {
+ return (Memory / 1024 / 1024 / 1024).toFixed(2) + "GB";
+ }
+ } else {
+ return Memory;
+ }
+ } catch (e) {
+ console.error(e);
+ if (UtilityEnabled("DebugMode")) {
+ SmartAlert("XMOJ-Script internal error!\n\n" + e + "\n\n" + "If you see this message, please report it to the developer.\nDon't forget to include console logs and a way to reproduce the error!\n\nDon't want to see this message? Disable DebugMode.");
+ }
+ }
+};
+
+export const TimeToStringTime = (Time) => {
+ try {
+ if (UtilityEnabled("AddUnits")) {
+ if (Time < 1000) {
+ return Time + "ms";
+ } else if (Time < 1000 * 60) {
+ return (Time / 1000).toFixed(2) + "s";
+ }
+ } else {
+ return Time;
+ }
+ } catch (e) {
+ console.error(e);
+ if (UtilityEnabled("DebugMode")) {
+ SmartAlert("XMOJ-Script internal error!\n\n" + e + "\n\n" + "If you see this message, please report it to the developer.\nDon't forget to include console logs and a way to reproduce the error!\n\nDon't want to see this message? Disable DebugMode.");
+ }
+ }
+};
+
+export const TidyTable = (Table) => {
+ try {
+ if (UtilityEnabled("NewBootstrap") && Table != null) {
+ Table.className = "table table-hover";
+ }
+ } catch (e) {
+ console.error(e);
+ if (UtilityEnabled("DebugMode")) {
+ SmartAlert("XMOJ-Script internal error!\n\n" + e + "\n\n" + "If you see this message, please report it to the developer.\nDon't forget to include console logs and a way to reproduce the error!\n\nDon't want to see this message? Disable DebugMode.");
+ }
+ }
+};
+
+export const UtilityEnabled = (Name) => {
+ try {
+ if (localStorage.getItem("UserScript-Setting-" + Name) == null) {
+ const defaultOffItems = ["DebugMode", "SuperDebug", "ReplaceXM"];
+ localStorage.setItem("UserScript-Setting-" + Name, defaultOffItems.includes(Name) ? "false" : "true");
+ }
+ return localStorage.getItem("UserScript-Setting-" + Name) == "true";
+ } catch (e) {
+ console.error(e);
+ if (UtilityEnabled("DebugMode")) {
+ SmartAlert("XMOJ-Script internal error!\n\n" + e + "\n\n" + "If you see this message, please report it to the developer.\nDon't forget to include console logs and a way to reproduce the error!\n\nDon't want to see this message? Disable DebugMode.");
+ }
+ }
+};
+
+export const storeCredential = async (username, password) => {
+ if ('credentials' in navigator && window.PasswordCredential) {
+ try {
+ const credential = new PasswordCredential({ id: username, password: password });
+ await navigator.credentials.store(credential);
+ } catch (e) {
+ console.error(e);
+ }
+ }
+};
+
+export const getCredential = async () => {
+ if ('credentials' in navigator && window.PasswordCredential) {
+ try {
+ return await navigator.credentials.get({ password: true, mediation: 'optional' });
+ } catch (e) {
+ console.error(e);
+ }
+ }
+ return null;
+};
+
+export const clearCredential = async () => {
+ if ('credentials' in navigator && window.PasswordCredential) {
+ try {
+ await navigator.credentials.preventSilentAccess();
+ } catch (e) {
+ console.error(e);
+ }
+ }
+};
diff --git a/vite.config.js b/vite.config.js
new file mode 100644
index 00000000..72f9bd13
--- /dev/null
+++ b/vite.config.js
@@ -0,0 +1,55 @@
+import { defineConfig } from 'vite';
+import monkey from 'vite-plugin-monkey';
+
+export default defineConfig({
+ plugins: [
+ monkey({
+ entry: 'src/main.js',
+ userscript: {
+ name: 'XMOJ',
+ namespace: 'https://github.com/XMOJ-Script-dev/XMOJ-Script',
+ version: '2.2.1',
+ description: 'XMOJ增强脚本',
+ author: 'XMOJ-Script-dev',
+ match: ['*://*.xmoj.tech/*'],
+ icon: 'https://www.google.com/s2/favicons?sz=64&domain=xmoj.tech',
+ grant: ['GM_xmlhttpRequest'],
+ },
+ build: {
+ fileName: 'XMOJ.user.js',
+ },
+ }),
+ ],
+ build: {
+ outDir: 'dist',
+ rollupOptions: {
+ input: 'src/main.js',
+ output: {
+ entryFileNames: 'XMOJ.user.js',
+ format: 'iife',
+ globals: {
+ $: '$',
+ marked: 'marked',
+ jquery: 'jQuery',
+ codemirror: 'CodeMirror',
+ 'file-saver': 'saveAs',
+ jszip: 'JSZip',
+ 'crypto-js': 'CryptoJS',
+ 'diff-match-patch': 'diff_match_patch',
+ dompurify: 'DOMPurify',
+ },
+ },
+ external: [
+ '$',
+ 'codemirror',
+ 'crypto-js',
+ 'diff-match-patch',
+ 'dompurify',
+ 'file-saver',
+ 'jquery',
+ 'marked',
+ 'jszip',
+ ],
+ },
+ },
+});