OIer 的脚本

ggc的个人博客 / 2024-12-19 / 原文

Luogu 脚本

1. extend-luogu

// ==UserScript==
// @name           extend-luogu
// @namespace      http://tampermonkey.net/
// @description    Make luogu more powerful.
// @description:zh 使洛谷拥有更多功能
// @icon           https://raw.fastgit.org/extend-luogu/extend-luogu/main/favicon.ico
// @icon64         https://exlg.cc/img/logo.png
// @homepage       https://exlg.cc
// @version        6.7.8
//
// @match          https://*.luogu.com.cn/*
// @match          https://*.luogu.org/*
// @match          https://www.bilibili.com/robots.txt?*
// @match          https://service-ig5px5gh-1305163805.sh.apigw.tencentcs.com/release/APIGWHtmlDemo-1615602121
// @match          https://service-nd5kxeo3-1305163805.sh.apigw.tencentcs.com/release/exlg-nextgen
// @match          https://extend-luogu.github.io/exlg-setting-new/*
// @match          https://dash.exlg.cc/*
// @include        http://localhost:1634/*
//
// @connect        tencentcs.com
// @connect        xn--fx-ex2c330n.ml
// @connect        bens.rotriw.com
// @connect        codeforces.com
// @connect        codeforces.ml
// @connect        codeforc.es
// @connect        kenkoooo.com
// @connect        api.github.com
// @connect        piterator.com
// @connect        exlgcs.jin-dan.site
// @connect        localhost
//
// @require        https://cdn.luogu.com.cn/js/jquery-2.1.1.min.js
//
// @resource       colorpicker https://extend-luogu.github.io/exlg-color-picker/dist/xncolorpicker.min.js
// @resource       colorpicker_old https://www.jq22.com/demo/xncolorpicker-main202101270059/dist/xncolorpicker.min.js
// @resource       pickr_resource https://unpkg.com/@simonwep/pickr@1.8.2/dist/pickr.min.js
// @resource       pickr_resource_css https://unpkg.com/@simonwep/pickr@1.8.2/dist/themes/nano.min.css
//
// @grant          GM_addStyle
// @grant          GM_getValue
// @grant          GM_setValue
// @grant          GM_deleteValue
// @grant          GM_listValues
// @grant          GM_setClipboard
// @grant          GM_xmlhttpRequest
// @grant          GM_getResourceText
// @grant          unsafeWindow
// ==/UserScript==

;
(()=>{var Yr=Object.create;var ut=Object.defineProperty;var Kr=Object.getOwnPropertyDescriptor;var en=Object.getOwnPropertyNames;var tn=Object.getPrototypeOf,rn=Object.prototype.hasOwnProperty;var le=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var nn=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of en(t))!rn.call(e,o)&&o!==r&&ut(e,o,{get:()=>t[o],enumerable:!(n=Kr(t,o))||n.enumerable});return e};var on=(e,t,r)=>(r=e!=null?Yr(tn(e)):{},nn(t||!e||!e.__esModule?ut(r,"default",{value:e,enumerable:!0}):r,e));var Ye=le(be=>{function ft(){var e={};return e["align-content"]=!1,e["align-items"]=!1,e["align-self"]=!1,e["alignment-adjust"]=!1,e["alignment-baseline"]=!1,e.all=!1,e["anchor-point"]=!1,e.animation=!1,e["animation-delay"]=!1,e["animation-direction"]=!1,e["animation-duration"]=!1,e["animation-fill-mode"]=!1,e["animation-iteration-count"]=!1,e["animation-name"]=!1,e["animation-play-state"]=!1,e["animation-timing-function"]=!1,e.azimuth=!1,e["backface-visibility"]=!1,e.background=!0,e["background-attachment"]=!0,e["background-clip"]=!0,e["background-color"]=!0,e["background-image"]=!0,e["background-origin"]=!0,e["background-position"]=!0,e["background-repeat"]=!0,e["background-size"]=!0,e["baseline-shift"]=!1,e.binding=!1,e.bleed=!1,e["bookmark-label"]=!1,e["bookmark-level"]=!1,e["bookmark-state"]=!1,e.border=!0,e["border-bottom"]=!0,e["border-bottom-color"]=!0,e["border-bottom-left-radius"]=!0,e["border-bottom-right-radius"]=!0,e["border-bottom-style"]=!0,e["border-bottom-width"]=!0,e["border-collapse"]=!0,e["border-color"]=!0,e["border-image"]=!0,e["border-image-outset"]=!0,e["border-image-repeat"]=!0,e["border-image-slice"]=!0,e["border-image-source"]=!0,e["border-image-width"]=!0,e["border-left"]=!0,e["border-left-color"]=!0,e["border-left-style"]=!0,e["border-left-width"]=!0,e["border-radius"]=!0,e["border-right"]=!0,e["border-right-color"]=!0,e["border-right-style"]=!0,e["border-right-width"]=!0,e["border-spacing"]=!0,e["border-style"]=!0,e["border-top"]=!0,e["border-top-color"]=!0,e["border-top-left-radius"]=!0,e["border-top-right-radius"]=!0,e["border-top-style"]=!0,e["border-top-width"]=!0,e["border-width"]=!0,e.bottom=!1,e["box-decoration-break"]=!0,e["box-shadow"]=!0,e["box-sizing"]=!0,e["box-snap"]=!0,e["box-suppress"]=!0,e["break-after"]=!0,e["break-before"]=!0,e["break-inside"]=!0,e["caption-side"]=!1,e.chains=!1,e.clear=!0,e.clip=!1,e["clip-path"]=!1,e["clip-rule"]=!1,e.color=!0,e["color-interpolation-filters"]=!0,e["column-count"]=!1,e["column-fill"]=!1,e["column-gap"]=!1,e["column-rule"]=!1,e["column-rule-color"]=!1,e["column-rule-style"]=!1,e["column-rule-width"]=!1,e["column-span"]=!1,e["column-width"]=!1,e.columns=!1,e.contain=!1,e.content=!1,e["counter-increment"]=!1,e["counter-reset"]=!1,e["counter-set"]=!1,e.crop=!1,e.cue=!1,e["cue-after"]=!1,e["cue-before"]=!1,e.cursor=!1,e.direction=!1,e.display=!0,e["display-inside"]=!0,e["display-list"]=!0,e["display-outside"]=!0,e["dominant-baseline"]=!1,e.elevation=!1,e["empty-cells"]=!1,e.filter=!1,e.flex=!1,e["flex-basis"]=!1,e["flex-direction"]=!1,e["flex-flow"]=!1,e["flex-grow"]=!1,e["flex-shrink"]=!1,e["flex-wrap"]=!1,e.float=!1,e["float-offset"]=!1,e["flood-color"]=!1,e["flood-opacity"]=!1,e["flow-from"]=!1,e["flow-into"]=!1,e.font=!0,e["font-family"]=!0,e["font-feature-settings"]=!0,e["font-kerning"]=!0,e["font-language-override"]=!0,e["font-size"]=!0,e["font-size-adjust"]=!0,e["font-stretch"]=!0,e["font-style"]=!0,e["font-synthesis"]=!0,e["font-variant"]=!0,e["font-variant-alternates"]=!0,e["font-variant-caps"]=!0,e["font-variant-east-asian"]=!0,e["font-variant-ligatures"]=!0,e["font-variant-numeric"]=!0,e["font-variant-position"]=!0,e["font-weight"]=!0,e.grid=!1,e["grid-area"]=!1,e["grid-auto-columns"]=!1,e["grid-auto-flow"]=!1,e["grid-auto-rows"]=!1,e["grid-column"]=!1,e["grid-column-end"]=!1,e["grid-column-start"]=!1,e["grid-row"]=!1,e["grid-row-end"]=!1,e["grid-row-start"]=!1,e["grid-template"]=!1,e["grid-template-areas"]=!1,e["grid-template-columns"]=!1,e["grid-template-rows"]=!1,e["hanging-punctuation"]=!1,e.height=!0,e.hyphens=!1,e.icon=!1,e["image-orientation"]=!1,e["image-resolution"]=!1,e["ime-mode"]=!1,e["initial-letters"]=!1,e["inline-box-align"]=!1,e["justify-content"]=!1,e["justify-items"]=!1,e["justify-self"]=!1,e.left=!1,e["letter-spacing"]=!0,e["lighting-color"]=!0,e["line-box-contain"]=!1,e["line-break"]=!1,e["line-grid"]=!1,e["line-height"]=!1,e["line-snap"]=!1,e["line-stacking"]=!1,e["line-stacking-ruby"]=!1,e["line-stacking-shift"]=!1,e["line-stacking-strategy"]=!1,e["list-style"]=!0,e["list-style-image"]=!0,e["list-style-position"]=!0,e["list-style-type"]=!0,e.margin=!0,e["margin-bottom"]=!0,e["margin-left"]=!0,e["margin-right"]=!0,e["margin-top"]=!0,e["marker-offset"]=!1,e["marker-side"]=!1,e.marks=!1,e.mask=!1,e["mask-box"]=!1,e["mask-box-outset"]=!1,e["mask-box-repeat"]=!1,e["mask-box-slice"]=!1,e["mask-box-source"]=!1,e["mask-box-width"]=!1,e["mask-clip"]=!1,e["mask-image"]=!1,e["mask-origin"]=!1,e["mask-position"]=!1,e["mask-repeat"]=!1,e["mask-size"]=!1,e["mask-source-type"]=!1,e["mask-type"]=!1,e["max-height"]=!0,e["max-lines"]=!1,e["max-width"]=!0,e["min-height"]=!0,e["min-width"]=!0,e["move-to"]=!1,e["nav-down"]=!1,e["nav-index"]=!1,e["nav-left"]=!1,e["nav-right"]=!1,e["nav-up"]=!1,e["object-fit"]=!1,e["object-position"]=!1,e.opacity=!1,e.order=!1,e.orphans=!1,e.outline=!1,e["outline-color"]=!1,e["outline-offset"]=!1,e["outline-style"]=!1,e["outline-width"]=!1,e.overflow=!1,e["overflow-wrap"]=!1,e["overflow-x"]=!1,e["overflow-y"]=!1,e.padding=!0,e["padding-bottom"]=!0,e["padding-left"]=!0,e["padding-right"]=!0,e["padding-top"]=!0,e.page=!1,e["page-break-after"]=!1,e["page-break-before"]=!1,e["page-break-inside"]=!1,e["page-policy"]=!1,e.pause=!1,e["pause-after"]=!1,e["pause-before"]=!1,e.perspective=!1,e["perspective-origin"]=!1,e.pitch=!1,e["pitch-range"]=!1,e["play-during"]=!1,e.position=!1,e["presentation-level"]=!1,e.quotes=!1,e["region-fragment"]=!1,e.resize=!1,e.rest=!1,e["rest-after"]=!1,e["rest-before"]=!1,e.richness=!1,e.right=!1,e.rotation=!1,e["rotation-point"]=!1,e["ruby-align"]=!1,e["ruby-merge"]=!1,e["ruby-position"]=!1,e["shape-image-threshold"]=!1,e["shape-outside"]=!1,e["shape-margin"]=!1,e.size=!1,e.speak=!1,e["speak-as"]=!1,e["speak-header"]=!1,e["speak-numeral"]=!1,e["speak-punctuation"]=!1,e["speech-rate"]=!1,e.stress=!1,e["string-set"]=!1,e["tab-size"]=!1,e["table-layout"]=!1,e["text-align"]=!0,e["text-align-last"]=!0,e["text-combine-upright"]=!0,e["text-decoration"]=!0,e["text-decoration-color"]=!0,e["text-decoration-line"]=!0,e["text-decoration-skip"]=!0,e["text-decoration-style"]=!0,e["text-emphasis"]=!0,e["text-emphasis-color"]=!0,e["text-emphasis-position"]=!0,e["text-emphasis-style"]=!0,e["text-height"]=!0,e["text-indent"]=!0,e["text-justify"]=!0,e["text-orientation"]=!0,e["text-overflow"]=!0,e["text-shadow"]=!0,e["text-space-collapse"]=!0,e["text-transform"]=!0,e["text-underline-position"]=!0,e["text-wrap"]=!0,e.top=!1,e.transform=!1,e["transform-origin"]=!1,e["transform-style"]=!1,e.transition=!1,e["transition-delay"]=!1,e["transition-duration"]=!1,e["transition-property"]=!1,e["transition-timing-function"]=!1,e["unicode-bidi"]=!1,e["vertical-align"]=!1,e.visibility=!1,e["voice-balance"]=!1,e["voice-duration"]=!1,e["voice-family"]=!1,e["voice-pitch"]=!1,e["voice-range"]=!1,e["voice-rate"]=!1,e["voice-stress"]=!1,e["voice-volume"]=!1,e.volume=!1,e["white-space"]=!1,e.widows=!1,e.width=!0,e["will-change"]=!1,e["word-break"]=!0,e["word-spacing"]=!0,e["word-wrap"]=!0,e["wrap-flow"]=!1,e["wrap-through"]=!1,e["writing-mode"]=!1,e["z-index"]=!1,e}function sn(e,t,r){}function an(e,t,r){}var ln=/javascript\s*\:/img;function cn(e,t){return ln.test(t)?"":t}be.whiteList=ft();be.getDefaultWhiteList=ft;be.onAttr=sn;be.onIgnoreAttr=an;be.safeAttrValue=cn});var Ke=le((li,mt)=>{mt.exports={indexOf:function(e,t){var r,n;if(Array.prototype.indexOf)return e.indexOf(t);for(r=0,n=e.length;r<n;r++)if(e[r]===t)return r;return-1},forEach:function(e,t,r){var n,o;if(Array.prototype.forEach)return e.forEach(t,r);for(n=0,o=e.length;n<o;n++)t.call(r,e[n],n,e)},trim:function(e){return String.prototype.trim?e.trim():e.replace(/(^\s*)|(\s*$)/g,"")},trimRight:function(e){return String.prototype.trimRight?e.trimRight():e.replace(/(\s*$)/g,"")}}});var bt=le((ci,ht)=>{var ke=Ke();function dn(e,t){e=ke.trimRight(e),e[e.length-1]!==";"&&(e+=";");var r=e.length,n=!1,o=0,i=0,s="";function l(){if(!n){var c=ke.trim(e.slice(o,i)),a=c.indexOf(":");if(a!==-1){var p=ke.trim(c.slice(0,a)),f=ke.trim(c.slice(a+1));if(p){var x=t(o,s.length,p,f,c);x&&(s+=x+"; ")}}}o=i+1}for(;i<r;i++){var d=e[i];if(d==="/"&&e[i+1]==="*"){var u=e.indexOf("*/",i+2);if(u===-1)break;i=u+1,o=i+1,n=!1}else d==="("?n=!0:d===")"?n=!1:d===";"?n||l():d===`
`&&l()}return ke.trim(s)}ht.exports=dn});var vt=le((gi,yt)=>{var Me=Ye(),gn=bt(),di=Ke();function xt(e){return e==null}function pn(e){var t={};for(var r in e)t[r]=e[r];return t}function wt(e){e=pn(e||{}),e.whiteList=e.whiteList||Me.whiteList,e.onAttr=e.onAttr||Me.onAttr,e.onIgnoreAttr=e.onIgnoreAttr||Me.onIgnoreAttr,e.safeAttrValue=e.safeAttrValue||Me.safeAttrValue,this.options=e}wt.prototype.process=function(e){if(e=e||"",e=e.toString(),!e)return"";var t=this,r=t.options,n=r.whiteList,o=r.onAttr,i=r.onIgnoreAttr,s=r.safeAttrValue,l=gn(e,function(d,u,c,a,p){var f=n[c],x=!1;if(f===!0?x=f:typeof f=="function"?x=f(a):f instanceof RegExp&&(x=f.test(a)),x!==!0&&(x=!1),a=s(c,a),!!a){var h={position:u,sourcePosition:d,source:p,isWhite:x};if(x){var m=o(c,a,h);return xt(m)?c+":"+a:m}else{var m=i(c,a,h);if(!xt(m))return m}}});return l};yt.exports=wt});var Ee=le((Ae,tt)=>{var kt=Ye(),_t=vt();function un(e,t){var r=new _t(t);return r.process(e)}Ae=tt.exports=un;Ae.FilterCSS=_t;for(et in kt)Ae[et]=kt[et];var et;typeof window<"u"&&(window.filterCSS=tt.exports)});var Ne=le((pi,$t)=>{$t.exports={indexOf:function(e,t){var r,n;if(Array.prototype.indexOf)return e.indexOf(t);for(r=0,n=e.length;r<n;r++)if(e[r]===t)return r;return-1},forEach:function(e,t,r){var n,o;if(Array.prototype.forEach)return e.forEach(t,r);for(n=0,o=e.length;n<o;n++)t.call(r,e[n],n,e)},trim:function(e){return String.prototype.trim?e.trim():e.replace(/(^\s*)|(\s*$)/g,"")},spaceIndex:function(e){var t=/\s|\n|\t/,r=t.exec(e);return r?r.index:-1}}});var rt=le(P=>{var fn=Ee().FilterCSS,mn=Ee().getDefaultWhiteList,Ie=Ne();function Ct(){return{a:["target","href","title"],abbr:["title"],address:[],area:["shape","coords","href","alt"],article:[],aside:[],audio:["autoplay","controls","crossorigin","loop","muted","preload","src"],b:[],bdi:["dir"],bdo:["dir"],big:[],blockquote:["cite"],br:[],caption:[],center:[],cite:[],code:[],col:["align","valign","span","width"],colgroup:["align","valign","span","width"],dd:[],del:["datetime"],details:["open"],div:[],dl:[],dt:[],em:[],figcaption:[],figure:[],font:["color","size","face"],footer:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],header:[],hr:[],i:[],img:["src","alt","title","width","height"],ins:["datetime"],li:[],mark:[],nav:[],ol:[],p:[],pre:[],s:[],section:[],small:[],span:[],sub:[],summary:[],sup:[],strong:[],strike:[],table:["width","border","align","valign"],tbody:["align","valign"],td:["width","rowspan","colspan","align","valign"],tfoot:["align","valign"],th:["width","rowspan","colspan","align","valign"],thead:["align","valign"],tr:["rowspan","align","valign"],tt:[],u:[],ul:[],video:["autoplay","controls","crossorigin","loop","muted","playsinline","poster","preload","src","height","width"]}}var St=new fn;function hn(e,t,r){}function bn(e,t,r){}function xn(e,t,r){}function wn(e,t,r){}function Mt(e){return e.replace(vn,"&lt;").replace(kn,"&gt;")}function yn(e,t,r,n){if(r=jt(r),t==="href"||t==="src"){if(r=Ie.trim(r),r==="#")return"#";if(!(r.substr(0,7)==="http://"||r.substr(0,8)==="https://"||r.substr(0,7)==="mailto:"||r.substr(0,4)==="tel:"||r.substr(0,11)==="data:image/"||r.substr(0,6)==="ftp://"||r.substr(0,2)==="./"||r.substr(0,3)==="../"||r[0]==="#"||r[0]==="/"))return""}else if(t==="background"){if(ze.lastIndex=0,ze.test(r))return""}else if(t==="style"){if(Lt.lastIndex=0,Lt.test(r)||(Tt.lastIndex=0,Tt.test(r)&&(ze.lastIndex=0,ze.test(r))))return"";n!==!1&&(n=n||St,r=n.process(r))}return r=Ot(r),r}var vn=/</g,kn=/>/g,_n=/"/g,$n=/&quot;/g,Ln=/&#([a-zA-Z0-9]*);?/gim,Tn=/&colon;?/gim,Cn=/&newline;?/gim,ze=/((j\s*a\s*v\s*a|v\s*b|l\s*i\s*v\s*e)\s*s\s*c\s*r\s*i\s*p\s*t\s*|m\s*o\s*c\s*h\s*a):/gi,Lt=/e\s*x\s*p\s*r\s*e\s*s\s*s\s*i\s*o\s*n\s*\(.*/gi,Tt=/u\s*r\s*l\s*\(.*/gi;function At(e){return e.replace(_n,"&quot;")}function Et(e){return e.replace($n,'"')}function Nt(e){return e.replace(Ln,function(r,n){return n[0]==="x"||n[0]==="X"?String.fromCharCode(parseInt(n.substr(1),16)):String.fromCharCode(parseInt(n,10))})}function zt(e){return e.replace(Tn,":").replace(Cn," ")}function It(e){for(var t="",r=0,n=e.length;r<n;r++)t+=e.charCodeAt(r)<32?" ":e.charAt(r);return Ie.trim(t)}function jt(e){return e=Et(e),e=Nt(e),e=zt(e),e=It(e),e}function Ot(e){return e=At(e),e=Mt(e),e}function Sn(){return""}function Mn(e,t){typeof t!="function"&&(t=function(){});var r=!Array.isArray(e);function n(s){return r?!0:Ie.indexOf(e,s)!==-1}var o=[],i=!1;return{onIgnoreTag:function(s,l,d){if(n(s))if(d.isClosing){var u="[/removed]",c=d.position+u.length;return o.push([i!==!1?i:d.position,c]),i=!1,u}else return i||(i=d.position),"[removed]";else return t(s,l,d)},remove:function(s){var l="",d=0;return Ie.forEach(o,function(u){l+=s.slice(d,u[0]),d=u[1]}),l+=s.slice(d),l}}}function An(e){for(var t="",r=0;r<e.length;){var n=e.indexOf("<!--",r);if(n===-1){t+=e.slice(r);break}t+=e.slice(r,n);var o=e.indexOf("-->",n);if(o===-1)break;r=o+3}return t}function En(e){var t=e.split("");return t=t.filter(function(r){var n=r.charCodeAt(0);return n===127?!1:n<=31?n===10||n===13:!0}),t.join("")}P.whiteList=Ct();P.getDefaultWhiteList=Ct;P.onTag=hn;P.onIgnoreTag=bn;P.onTagAttr=xn;P.onIgnoreTagAttr=wn;P.safeAttrValue=yn;P.escapeHtml=Mt;P.escapeQuote=At;P.unescapeQuote=Et;P.escapeHtmlEntities=Nt;P.escapeDangerHtml5Entities=zt;P.clearNonPrintableCharacter=It;P.friendlyAttrValue=jt;P.escapeAttrValue=Ot;P.onIgnoreTagStripAll=Sn;P.StripTagBody=Mn;P.stripCommentTag=An;P.stripBlankChar=En;P.cssFilter=St;P.getDefaultCSSWhiteList=mn});var ot=le(nt=>{var ge=Ne();function Nn(e){var t=ge.spaceIndex(e),r;return t===-1?r=e.slice(1,-1):r=e.slice(1,t+1),r=ge.trim(r).toLowerCase(),r.slice(0,1)==="/"&&(r=r.slice(1)),r.slice(-1)==="/"&&(r=r.slice(0,-1)),r}function zn(e){return e.slice(0,2)==="</"}function In(e,t,r){"use strict";var n="",o=0,i=!1,s=!1,l=0,d=e.length,u="",c="";e:for(l=0;l<d;l++){var a=e.charAt(l);if(i===!1){if(a==="<"){i=l;continue}}else if(s===!1){if(a==="<"){n+=r(e.slice(o,l)),i=l,o=l;continue}if(a===">"){n+=r(e.slice(o,i)),c=e.slice(i,l+1),u=Nn(c),n+=t(i,n.length,u,c,zn(c)),o=l+1,i=!1;continue}if(a==='"'||a==="'")for(var p=1,f=e.charAt(l-p);f.trim()===""||f==="=";){if(f==="="){s=a;continue e}f=e.charAt(l-++p)}}else if(a===s){s=!1;continue}}return o<e.length&&(n+=r(e.substr(o))),n}var jn=/[^a-zA-Z0-9\\_:.-]/gim;function On(e,t){"use strict";var r=0,n=0,o=[],i=!1,s=e.length;function l(p,f){if(p=ge.trim(p),p=p.replace(jn,"").toLowerCase(),!(p.length<1)){var x=t(p,f||"");x&&o.push(x)}}for(var d=0;d<s;d++){var u=e.charAt(d),c,a;if(i===!1&&u==="="){i=e.slice(r,d),r=d+1,n=e.charAt(r)==='"'||e.charAt(r)==="'"?r:Rn(e,d+1);continue}if(i!==!1&&d===n){if(a=e.indexOf(u,d+1),a===-1)break;c=ge.trim(e.slice(n+1,a)),l(i,c),i=!1,d=a,r=d+1;continue}if(/\s|\n|\t/.test(u))if(e=e.replace(/\s|\n|\t/g," "),i===!1)if(a=Dn(e,d),a===-1){c=ge.trim(e.slice(r,d)),l(c),i=!1,r=d+1;continue}else{d=a-1;continue}else if(a=Pn(e,d-1),a===-1){c=ge.trim(e.slice(r,d)),c=Dt(c),l(i,c),i=!1,r=d+1;continue}else continue}return r<e.length&&(i===!1?l(e.slice(r)):l(i,Dt(ge.trim(e.slice(r))))),ge.trim(o.join(" "))}function Dn(e,t){for(;t<e.length;t++){var r=e[t];if(r!==" ")return r==="="?t:-1}}function Rn(e,t){for(;t<e.length;t++){var r=e[t];if(r!==" ")return r==="'"||r==='"'?t:-1}}function Pn(e,t){for(;t>0;t--){var r=e[t];if(r!==" ")return r==="="?t:-1}}function qn(e){return e[0]==='"'&&e[e.length-1]==='"'||e[0]==="'"&&e[e.length-1]==="'"}function Dt(e){return qn(e)?e.substr(1,e.length-2):e}nt.parseTag=In;nt.parseAttr=On});var Bt=le((mi,qt)=>{var Bn=Ee().FilterCSS,te=rt(),Rt=ot(),Un=Rt.parseTag,Zn=Rt.parseAttr,Oe=Ne();function je(e){return e==null}function Gn(e){var t=Oe.spaceIndex(e);if(t===-1)return{html:"",closing:e[e.length-2]==="/"};e=Oe.trim(e.slice(t+1,-1));var r=e[e.length-1]==="/";return r&&(e=Oe.trim(e.slice(0,-1))),{html:e,closing:r}}function Vn(e){var t={};for(var r in e)t[r]=e[r];return t}function Fn(e){var t={};for(var r in e)Array.isArray(e[r])?t[r.toLowerCase()]=e[r].map(function(n){return n.toLowerCase()}):t[r.toLowerCase()]=e[r];return t}function Pt(e){e=Vn(e||{}),e.stripIgnoreTag&&(e.onIgnoreTag&&console.error('Notes: cannot use these two options "stripIgnoreTag" and "onIgnoreTag" at the same time'),e.onIgnoreTag=te.onIgnoreTagStripAll),e.whiteList||e.allowList?e.whiteList=Fn(e.whiteList||e.allowList):e.whiteList=te.whiteList,e.onTag=e.onTag||te.onTag,e.onTagAttr=e.onTagAttr||te.onTagAttr,e.onIgnoreTag=e.onIgnoreTag||te.onIgnoreTag,e.onIgnoreTagAttr=e.onIgnoreTagAttr||te.onIgnoreTagAttr,e.safeAttrValue=e.safeAttrValue||te.safeAttrValue,e.escapeHtml=e.escapeHtml||te.escapeHtml,this.options=e,e.css===!1?this.cssFilter=!1:(e.css=e.css||{},this.cssFilter=new Bn(e.css))}Pt.prototype.process=function(e){if(e=e||"",e=e.toString(),!e)return"";var t=this,r=t.options,n=r.whiteList,o=r.onTag,i=r.onIgnoreTag,s=r.onTagAttr,l=r.onIgnoreTagAttr,d=r.safeAttrValue,u=r.escapeHtml,c=t.cssFilter;r.stripBlankChar&&(e=te.stripBlankChar(e)),r.allowCommentTag||(e=te.stripCommentTag(e));var a=!1;r.stripIgnoreTagBody&&(a=te.StripTagBody(r.stripIgnoreTagBody,i),i=a.onIgnoreTag);var p=Un(e,function(f,x,h,m,b){var w={sourcePosition:f,position:x,isClosing:b,isWhite:Object.prototype.hasOwnProperty.call(n,h)},k=o(h,m,w);if(!je(k))return k;if(w.isWhite){if(w.isClosing)return"</"+h+">";var L=Gn(m),S=n[h],R=Zn(L.html,function(j,O){var H=Oe.indexOf(S,j)!==-1,ee=s(h,j,O,H);return je(ee)?H?(O=d(h,j,O,c),O?j+'="'+O+'"':j):(ee=l(h,j,O,H),je(ee)?void 0:ee):ee});return m="<"+h,R&&(m+=" "+R),L.closing&&(m+=" /"),m+=">",m}else return k=i(h,m,w),je(k)?u(m):k},u);return a&&(p=a.remove(p)),p};qt.exports=Pt});var Ft=le((xe,De)=>{var Ut=rt(),Zt=ot(),Gt=Bt();function Vt(e,t){var r=new Gt(t);return r.process(e)}xe=De.exports=Vt;xe.filterXSS=Vt;xe.FilterXSS=Gt;(function(){for(var e in Ut)xe[e]=Ut[e];for(var t in Zt)xe[t]=Zt[t]})();typeof window<"u"&&(window.filterXSS=De.exports);function Hn(){return typeof self<"u"&&typeof DedicatedWorkerGlobalScope<"u"&&self instanceof DedicatedWorkerGlobalScope}Hn()&&(self.filterXSS=De.exports)});var Qt=on(Ft(),1),re=unsafeWindow,A=re;function Re(e){return new Proxy(e,{get(t,r){return r==="then"?n=>(t.then(n),Re(t)):Re(t.then(n=>n[r]))}})}var M=(e,...t)=>re.console.log(`%c[exlg] ${e}`,"color: #0e90d2;",...t),Q=(e,...t)=>re.console.warn(`%c[exlg] ${e}`,"color: #0e90d2;",...t),U=(e,...t)=>{throw re.console.error(`%c[exlg] ${e}`,"color: #0e90d2;",...t),Error(t.join(" "))},E,C;location.host==="www.luogu.com.cn"&&!/blog/g.test(location.href)&&(/(\?|&)_contentOnly($|=)/g.test(location.search)&&U("Content-Only pages."),re._feInjection.code!==200&&U("Luogu failed to load. Exlg stops loading."),E=re._feInjection.currentData,C=re._feInjection.currentUser);var Ht=e=>[e.ctrlKey?"Ctrl":"",e.shiftKey?"Shift":"",e.altKey?"Alt":"",e.key.toInitialCase()].join(""),g=jQuery.extend({double:(e,t,r)=>[e(t),e(r)]});jQuery.fn.extend({whenKey(e,t){typeof e=="object"?this.on("keydown",r=>{let n=e[Ht(r)];n&&n(r)}):this.on("keydown",r=>{Ht(r)===e&&t(r)})}});var me=new Qt.filterXSS.FilterXSS({onTagAttr:(e,t,r)=>{if(t==="style")return`${t}="${r}"`}});Date.prototype.format=function(e,t){t=t?"UTC":"";let r={"y+":this[`get${t}FullYear`](),"m+":this[`get${t}Month`]()+1,"d+":this[`get${t}Date`](),"H+":this[`get${t}Hours`](),"M+":this[`get${t}Minutes`](),"S+":this[`get${t}Seconds`](),"s+":this[`get${t}Milliseconds`]()};for(let n in r)RegExp(`(${n})`).test(e)&&(e=e.replace(RegExp.$1,`000${r[n]}`.substr(r[n].toString().length+3-RegExp.$1.length)));return e};String.prototype.toInitialCase=function(){return this[0].toUpperCase()+this.slice(1)};var Jt=e=>new Promise(t=>setTimeout(t,e)),pe=(e,t)=>{if(!e)return"<<";let r=(c,a)=>c===a?"==":c<a?"<<":">>",n=["pre","alpha","beta"],[[o,i],[s,l]]=[e,t].map(c=>c.split("-")),[d,u]=o===s?[i,l].map(c=>[c?n.findIndex(a=>c.startsWith(a)):1/0,c?.match(/[0-9]+$/g)?.[0]??1/0]):[o,s].map(c=>c.split("."));for(let[c,a]of d.entries())if(a!==u[c])return r(+a||0,+u[c]||0);return"=="},Pe=(e,t)=>{let r=new URLSearchParams;for(let o in e)r.set(o,e[o]);let n=g(`
        <iframe id="exlg-${e.type}" src=" https://www.bilibili.com/robots.txt?${r}" style="${t}" exlg="exlg"></iframe>
    `);return M("Building springboard: %o",n[0]),n},ne=({url:e,onload:t,onerror:r=n=>U(n)})=>GM_xmlhttpRequest({url:e,method:"GET",onload:t,onerror:r}),st=(e,t={})=>{let r=new Promise((n,o)=>{GM_xmlhttpRequest({url:e,method:"GET",headers:t,onload:i=>{try{i.data=JSON.parse(i.responseText)}catch{}n(i)},onerror:o})});return Re(r)},K=(e,t,r={},n="application/json")=>{let o=new Promise((i,s)=>{GM_xmlhttpRequest({url:e,method:"POST",data:typeof t!="string"?JSON.stringify(t):t,headers:{"Content-Type":typeof t=="string"?n:"application/json",...r},onload:l=>{try{l.data=JSON.parse(l.responseText)}catch{}i(l)},onerror:s})});return Re(o)},W=(e=1e3)=>Math.floor(Date.now()/e),X=e=>new Promise((t,r)=>{g.get(`${e+(e.includes("?")?"&":"?")}_contentOnly=1`,n=>{n.code!==200&&r(new Error(`Requesting failure code: ${t.code}.`)),t(n)})}),Wt=(e,t="exlg 提醒您")=>re.show_alert?re.show_alert(t,e):re.alert(`${t}
${e}`),it=null,we=(e,t)=>g.ajax({url:e,data:t,headers:{"x-csrf-token":it===null?it=g("meta[name=csrf-token]").attr("content"):it,"content-type":"application/json"},method:"post"}),ye=e=>[/^AT[1-9][0-9]{0,}$/i,/^CF[1-9][0-9]{0,}[A-Z][0-9]?$/i,/^SP[1-9][0-9]{0,}$/i,/^P[1-9][0-9]{3,}$/i,/^UVA[1-9][0-9]{2,}$/i,/^U[1-9][0-9]{0,}$/i,/^T[1-9][0-9]{0,}$/i,/^B[2-9][0-9]{3,}$/i].some(t=>t.test(e)),at=(e,t)=>e.map(r=>({dft:r,...t})),_e=e=>t=>{let r=t.target.querySelectorAll(e);return{result:Boolean(r?.length),args:r}},Xt=(e,t,r)=>{let n=new Date().valueOf(),o=new BroadcastChannel(`${e}-ctrl`),i=new BroadcastChannel(`${e}-data`),s=!1,l,d,u=()=>{l=0,o.postMessage(`Alive ${n}`),setTimeout(()=>{l<n&&(s=!0,o.postMessage("Victory"),g(re).on("unload",()=>{o.postMessage("Election")}),t(i))},1e3)};o.onmessage=c=>{let a=c.data;a==="Ping"&&s?o.postMessage("Pong"):a==="Pong"?clearTimeout(d):a==="Election"?u():a.split(" ")[0]==="Alive"?(console.log(Number(a.split(" ")[1])),l=Math.max(l,Number(a.split(" ")[1]))):a==="Victory"&&(s=!1)},i.onmessage=c=>{r(c.data)},o.postMessage("Ping"),d=setTimeout(()=>{o.postMessage("Election"),u()},1e3)};var de={_:new Map,reg:(e,t,r,n,o)=>{de._.set(e,{description:t,alias:r,icon:n,unclosable:o})},alias:e=>{let t=de._.get(e);return t?t.alias:""},register:()=>{M("Registering categories"),de.reg("core","核心","@","bug_report",!0),de.reg("module","模块","","tunes",!1),de.reg("admin","管理模块","!","build",!1),de.reg("chore","定时任务","^","alarm",!0),de.reg("component","组件","#","widgets",!0)}};de.register();var Y=de;var J={};var lt=class{constructor(){this.fq=[]}push(...t){this.fq=this.fq.concat(t)}apply(){this.fq.forEach(t=>t())}},Qn=Object.fromEntries(["onload","preload"].map(e=>[e,new lt])),ce=Qn;var $e={_:new Map,sto:null,reg:(e,t,r,n,o,i)=>{let s=Y.alias("component")+e;return t=t.replaceAll(" ","_"),J[s]={ty:"object",lvs:{on:{ty:"boolean",dft:!0},...r}},$e._.set(e,{info:t,pre:n,styl:i}),(...l)=>o(...(r?[{msto:$e.sto[s]}]:[]).concat(l))}};ce.onload.push(()=>{for(let[e,t]of $e._.entries())t.styl&&GM_addStyle(t.styl),M(`Preparing component: ${e}`),t.pre&&t.pre({msto:$e.sto[Y.alias("component")+e]})});var oe=$e;var qe='<svg xmlns="http://www.w3.org/2000/svg" height="30" viewBox="0 0 136.14 30.56"><g transform="translate(1.755, 0)" fill="#00a0d8"><g><path d="M5.02-33.80L34.56-33.80L34.07-28.62L16.96-28.62L15.93-21.92L31.97-21.92L31.48-16.74L14.85-16.74L13.82-8.42L31.97-8.42L31.48-3.24L2.43-3.24L6.59-31.75L5.02-33.80Z" transform="translate(-4.14, 33.9)"></path><path d="M7.34-32.29L5.78-33.80L16.63-33.80L21.33-25.00L27.54-32.78L26.51-33.80L38.93-33.80L25.49-18.79L34.78-3.24L24.41-3.24L19.76-12.58L11.99-3.24L1.62-3.24L15.12-18.79L7.34-32.29Z" transform="translate(27.23, 33.9)"></path><path d="M4.00-33.80L16.42-33.80L12.80-8.42L32.99-8.42L32.51-3.24L5.56-3.24Q4.00-3.24 3.21-4.27Q2.43-5.29 2.43-6.86L2.43-6.86L5.56-31.75L4.00-33.80Z" transform="translate(63.8, 33.9)"></path><path d="M38.83-33.80L37.80-25.00L27.43-25.00L27.92-28.62L15.50-28.62L12.91-8.42L25.33-8.42L25.87-14.63L22.73-19.82L36.72-19.82L34.67-3.24L5.62-3.24Q4.86-3.24 4.21-3.51Q3.56-3.78 3.10-4.27Q2.65-4.75 2.48-5.43Q2.32-6.10 2.54-6.86L2.54-6.86L6.16-33.80L38.83-33.80Z" transform="translate(95.6, 33.9)"></path></g></g></svg>';var I=null,z={_:new Map,fake_sto:I,data:{},path_alias:[["",".*\\.luogu\\.(com\\.cn|org)"],["dash","dash.exlg.cc"],["cdn","cdn.luogu.com.cn"],["bili","www.bilibili.com"],["tcs1","service-ig5px5gh-1305163805.sh.apigw.tencentcs.com"],["tcs2","service-nd5kxeo3-1305163805.sh.apigw.tencentcs.com"],["tcs3","service-otgstbe5-1305163805.sh.apigw.tencentcs.com"],["debug","localhost:1634"],["ghpage","extend-luogu.github.io"]].map(([e,t])=>[new RegExp(`^@${e}/`),t]),pth_modify:e=>(Array.isArray(e)||(e=[e]),e.map(t=>(z.path_alias.some(([r,n])=>t.match(r)?(t=t.replace(r,`${n}/`),!0):!1),t.endsWith("$")||(t+="$"),t))),path_dash_board:["@dash/((index|bundle)(.html)?)?","@ghpage/exlg-setting-new/((index|bundle)(.html)?)?","@debug/((index|bundle).html)?"],reg:(e,t,r,n,o,i,s)=>{r=z.pth_modify(r);let l=Y.alias(s)+e;z.data[l]={ty:"object",lvs:n||{}},"on"in z.data[l].lvs||(z.data[l].lvs.on={ty:"boolean",dft:!0}),J[l]=z.data[l],t=t.replaceAll(" ","_"),z._.set(e,{info:t,path:r,func:o,styl:i,cate:s})},_regv2_invoker:(e,t)=>{let r=(o,i)=>{if(typeof i=="object"&&o in i)return i[o]};e=z.pth_modify(e);let n=(o,i,s,l)=>{i=i?z.pth_modify(i):[];let d=r(o,t.private);r("on",d)!==!1&&i.concat(e).some(c=>RegExp(c).test(location.href))&&ce[s].push((...c)=>l({msto:d,gsto:t.public},...c))};return{onload:({name:o,path:i},s,l)=>n(o,i,"onload",l),preload:({name:o,path:i},s,l)=>n(o,i,"preload",l),chore:({name:o,period:i},s,l)=>{if(typeof i=="string"){let d=+i.slice(0,-1),u={s:1e3,m:1e3*60,h:1e3*60*60,D:1e3*60*60*24}[i.slice(-1)];!isNaN(d)&&u?i=d*u:U(`Parsing period failed: "${i}"`)}n(o,"@/.*","preload",async d=>{let u=d.msto.last_chore,c=W(1);!u||c-u>i?await l(d)?Q(`Chore failed: "${o}"`):d.msto.last_chore=W(1):M(`Pending chore: "${o}"`)})},hook:({name:o,path:i},s,l,d)=>n(o,i,"preload",u=>{document.querySelector("body").addEventListener("DOMNodeInserted",c=>{if(!c.target.tagName)return!1;let a=d(c);return a.result&&l({...u,...a})})})}},reg_v2:({name:e,info:t,path:r,cate:n},o,i,s)=>{t=t.replaceAll(" ","_");let l={},d=[],u=(x,...h)=>l=Object.fromEntries(Object.entries(l).concat(Object.entries(x).map(([m,b])=>{let w=b.migration===!0?m:b.migration;return d.push([w,h.concat(m)]),[w,{...b,priv:!0}]}))),c=Y.alias(n)+e,a=Object.entries(o??{}).filter(([x])=>x!=="on");u(a,"public"),J[c]={ty:"object",lvs:{...a&&{public:{ty:"object",lvs:Object.fromEntries(a)}},private:{ty:"object",lvs:{}},on:{ty:"boolean",dft:o?.on?.dft??!0}}};let p=[];i((x=>{let h=(m,b)=>{b&&(x.private.lvs[m]={ty:"object",lvs:b},u(b,"private",m))};return new Proxy({onload:(m,b)=>h(m.name,b),preload:(m,b)=>h(m.name,b),chore:(m,b)=>h(m.name,{...b,last_chore:{ty:"number",dft:-1,priv:!0}}),hook:(m,b)=>h(m.name,b)},{get:(m,b)=>(...w)=>{p.push([b,...w]),m[b](...w)}})})(J[c].lvs)),J[c].lvs={...J[c].lvs,...l},z._.set(e,{info:t,path:z.pth_modify(r),data:o,func:i,subfuncs:p,migrlist:d,styl:s,cate:n})},reg_main:(e,t,r,n,o,i)=>z.reg(e,t,r,n,s=>(o(s),!1),i,"core"),reg_user_tab:(e,t,r,n,o,i,s,l)=>z.reg(e,t,"@/user/.*",o,d=>{let u=g(".items"),c=()=>{(location.hash||"#main")===`#${r}`&&(M(`Working user tab#${r} mod: "${e}"`),i({...d,vars:n}))};u.on("click",c),c()},s,l),reg_chore:(e,t,r,n,o,i,s)=>{if(typeof r=="string"){let l=+r.slice(0,-1),d={s:1e3,m:1e3*60,h:1e3*60*60,D:1e3*60*60*24}[r.slice(-1)];!isNaN(l)&&d?r=l*d:U(`Parsing period failed: "${r}"`)}o={...o,last_chore:{ty:"number",dft:-1,priv:!0}},z.reg(e,t,n,o,async l=>{let d=I[`^${e}`].last_chore,u=W(1),c=!0;l.named||!d||u-d>r?(c&&(GM_addStyle(s),c=!1),await i(l)?Q(`Chore failed: "${e}"`):I[`^${e}`].last_chore=W(1)):M(`Pending chore: "${e}"`)},null,"chore")},reg_board:(e,t,r,n,o,i)=>z.reg(e,t,"@/",r,s=>{let l=g("#exlg-board");l.length||(l=g(`
                    <div class="lg-article" id="exlg-board" exlg="exlg"><h2>${qe} &nbsp;&nbsp;${GM_info.script.version}</h2></div>
                `).prependTo(".lg-right.am-u-md-4"),l[0].firstChild.style["font-size"]="1em"),n({...s,$board:g("<div></div>").appendTo(l)})},o,i),reg_hook_new:(e,t,r,n,o,i,s,l,d)=>z.reg(e,t,r,n,u=>{o({...u,result:!1,args:s()}),g("body").bind("DOMNodeInserted",c=>{if(!c.target.tagName)return!1;let a=i(c);return a.result&&o({...u,...a})})},l,d),reg_lfe:(e,t,r,n,o,i,s)=>{z.reg(e,t,r,n,o,i,s),z._.set(e,{lfe:!0,...z._.get(e)})},find:e=>z._.get(e),has:e=>z._.has(e),disable:e=>{let t=z.find(e);t.on=!1,z._.set(e,t)},enable:e=>{let t=z.find(e);t.on=!0,z._.set(e,t)},execute_v2:()=>{for(let[e,t]of z._.entries()){let r=Y.alias(t.cate)+e;if(I[r].on&&(t.styl&&GM_addStyle(t.styl),t.subfuncs)){if(Array.isArray(t.migrlist)){for(let[o,i]of t.migrlist)if(I[r][o]!==J[r].lvs[o].dft){let s=I[r],l=i.slice(0,-1);for(let d of l)s=s[d];s[i.at(-1)]=I[r][o],I[r][o]=J[r].lvs[o].dft}}let n=z._regv2_invoker(t.path,I[r]);for(let o of t.subfuncs)M(`Executing "${o[1].name}" of "${e}"`),n[o[0]](...o.slice(1))}}},preload:()=>{I===null&&(I=z.fake_sto);let e=location.href;for(let[r,n]of z._.entries())I[Y.alias(n.cate)+r].on&&n.path.some(o=>new RegExp(o,"g").test(e))&&(n.willrun=!0,z._.set(r,n));let t=console.info.bind({});A.console.info=(...r)=>{let n=t(...r);if(M(`info hooked: ${r.join(" ")}`),r[0]==="[@lfe/loader]")for(let[o,i]of z._.entries())I[Y.alias(i.cate)+o].on&&i.path.some(s=>new RegExp(s,"g").test(e))&&"lfe"in i&&(z.execute(o),M(`loading lfe module: ${o}`));return n}},execute:e=>{let t=(r,n)=>{r||U(`Executing named mod but not found: "${e}"`),M(`Executing ${n?"named ":""}mod: "${r.name}"`);try{return r.func({msto:I[Y.alias(r.cate)+r.name],named:n})}catch(o){Q(o)}};if(e){let r=z.find(e);return t({name:e,...r},!0)}for(let[r,n]of z._.entries())if(n.on=I[Y.alias(n.cate)+r].on,n.willrun&&!n.subfuncs&&t({name:r,...n})===!1)break}};ce.onload.push(z.execute);var v=z;v.reg_v2({name:"auto-o2",info:"自动开启 O2",path:"@/problem/[A-Z0-9]+(#.*)?",cate:"module"},{on:{ty:"boolean",dft:!1},mode:{ty:"enum",dft:"lastSubmitted",vals:["lastSubmitted","lastChecked","always"],info:["Mode","模式"]},last_enabled:{ty:"boolean",dft:!1,priv:!0}},e=>{e.hook({name:"submit-detector",info:"提交检测"},null,({gsto:t,args:r})=>{t.mode==="lastSubmitted"&&g(r).on("click",()=>{t.last_enabled=g("input[id^=check]").is(":checked")})},_e("button[data-v-01cd4e24]")),e.hook({name:"o2-checker",info:"钩取开启 o2"},null,({gsto:t,args:r})=>{t.mode==="lastChecked"&&g(r).off("click"),(t.mode==="always"||t.mode==="lastChecked"&&t.last_enabled)&&g(r).trigger("click"),t.mode==="lastChecked"&&g(r).on("click",n=>{t.last_enabled=n.currentTarget.checked})},_e("input[id^=check]"))});function or(){return{baseUrl:null,breaks:!1,extensions:null,gfm:!0,headerIds:!0,headerPrefix:"",highlight:null,langPrefix:"language-",mangle:!0,pedantic:!1,renderer:null,sanitize:!1,sanitizer:null,silent:!1,smartLists:!1,smartypants:!1,tokenizer:null,walkTokens:null,xhtml:!1}}var ve=or();function Wn(e){ve=e}var Xn=/[&<>"']/,Yn=/[&<>"']/g,Kn=/[<>"']|&(?!#?\w+;)/,eo=/[<>"']|&(?!#?\w+;)/g,to={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},Yt=e=>to[e];function B(e,t){if(t){if(Xn.test(e))return e.replace(Yn,Yt)}else if(Kn.test(e))return e.replace(eo,Yt);return e}var ro=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;function ir(e){return e.replace(ro,(t,r)=>(r=r.toLowerCase(),r==="colon"?":":r.charAt(0)==="#"?r.charAt(1)==="x"?String.fromCharCode(parseInt(r.substring(2),16)):String.fromCharCode(+r.substring(1)):""))}var no=/(^|[^\[])\^/g;function D(e,t){e=typeof e=="string"?e:e.source,t=t||"";let r={replace:(n,o)=>(o=o.source||o,o=o.replace(no,"$1"),e=e.replace(n,o),r),getRegex:()=>new RegExp(e,t)};return r}var oo=/[^\w:]/g,io=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;function Kt(e,t,r){if(e){let n;try{n=decodeURIComponent(ir(r)).replace(oo,"").toLowerCase()}catch{return null}if(n.indexOf("javascript:")===0||n.indexOf("vbscript:")===0||n.indexOf("data:")===0)return null}t&&!io.test(r)&&(r=co(t,r));try{r=encodeURI(r).replace(/%25/g,"%")}catch{return null}return r}var Be={},so=/^[^:]+:\/*[^/]*$/,ao=/^([^:]+:)[\s\S]*$/,lo=/^([^:]+:\/*[^/]*)[\s\S]*$/;function co(e,t){Be[" "+e]||(so.test(e)?Be[" "+e]=e+"/":Be[" "+e]=Ue(e,"/",!0)),e=Be[" "+e];let r=e.indexOf(":")===-1;return t.substring(0,2)==="//"?r?t:e.replace(ao,"$1")+t:t.charAt(0)==="/"?r?t:e.replace(lo,"$1")+t:e+t}var Ze={exec:function(){}};function ae(e){let t=1,r,n;for(;t<arguments.length;t++){r=arguments[t];for(n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e}function er(e,t){let r=e.replace(/\|/g,(i,s,l)=>{let d=!1,u=s;for(;--u>=0&&l[u]==="\\";)d=!d;return d?"|":" |"}),n=r.split(/ \|/),o=0;if(n[0].trim()||n.shift(),n.length>0&&!n[n.length-1].trim()&&n.pop(),n.length>t)n.splice(t);else for(;n.length<t;)n.push("");for(;o<n.length;o++)n[o]=n[o].trim().replace(/\\\|/g,"|");return n}function Ue(e,t,r){let n=e.length;if(n===0)return"";let o=0;for(;o<n;){let i=e.charAt(n-o-1);if(i===t&&!r)o++;else if(i!==t&&r)o++;else break}return e.slice(0,n-o)}function go(e,t){if(e.indexOf(t[1])===-1)return-1;let r=e.length,n=0,o=0;for(;o<r;o++)if(e[o]==="\\")o++;else if(e[o]===t[0])n++;else if(e[o]===t[1]&&(n--,n<0))return o;return-1}function sr(e){e&&e.sanitize&&!e.silent&&console.warn("marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options")}function tr(e,t){if(t<1)return"";let r="";for(;t>1;)t&1&&(r+=e),t>>=1,e+=e;return r+e}function rr(e,t,r,n){let o=t.href,i=t.title?B(t.title):null,s=e[1].replace(/\\([\[\]])/g,"$1");if(e[0].charAt(0)!=="!"){n.state.inLink=!0;let l={type:"link",raw:r,href:o,title:i,text:s,tokens:n.inlineTokens(s,[])};return n.state.inLink=!1,l}return{type:"image",raw:r,href:o,title:i,text:B(s)}}function po(e,t){let r=e.match(/^(\s+)(?:```)/);if(r===null)return t;let n=r[1];return t.split(`
`).map(o=>{let i=o.match(/^\s+/);if(i===null)return o;let[s]=i;return s.length>=n.length?o.slice(n.length):o}).join(`
`)}var Le=class{constructor(t){this.options=t||ve}space(t){let r=this.rules.block.newline.exec(t);if(r&&r[0].length>0)return{type:"space",raw:r[0]}}code(t){let r=this.rules.block.code.exec(t);if(r){let n=r[0].replace(/^ {1,4}/gm,"");return{type:"code",raw:r[0],codeBlockStyle:"indented",text:this.options.pedantic?n:Ue(n,`
`)}}}fences(t){let r=this.rules.block.fences.exec(t);if(r){let n=r[0],o=po(n,r[3]||"");return{type:"code",raw:n,lang:r[2]?r[2].trim():r[2],text:o}}}heading(t){let r=this.rules.block.heading.exec(t);if(r){let n=r[2].trim();if(/#$/.test(n)){let i=Ue(n,"#");(this.options.pedantic||!i||/ $/.test(i))&&(n=i.trim())}let o={type:"heading",raw:r[0],depth:r[1].length,text:n,tokens:[]};return this.lexer.inline(o.text,o.tokens),o}}hr(t){let r=this.rules.block.hr.exec(t);if(r)return{type:"hr",raw:r[0]}}blockquote(t){let r=this.rules.block.blockquote.exec(t);if(r){let n=r[0].replace(/^ *>[ \t]?/gm,"");return{type:"blockquote",raw:r[0],tokens:this.lexer.blockTokens(n,[]),text:n}}}list(t){let r=this.rules.block.list.exec(t);if(r){let n,o,i,s,l,d,u,c,a,p,f,x,h=r[1].trim(),m=h.length>1,b={type:"list",raw:"",ordered:m,start:m?+h.slice(0,-1):"",loose:!1,items:[]};h=m?`\\d{1,9}\\${h.slice(-1)}`:`\\${h}`,this.options.pedantic&&(h=m?h:"[*+-]");let w=new RegExp(`^( {0,3}${h})((?:[	 ][^\\n]*)?(?:\\n|$))`);for(;t&&(x=!1,!(!(r=w.exec(t))||this.rules.block.hr.test(t)));){if(n=r[0],t=t.substring(n.length),c=r[2].split(`
`,1)[0],a=t.split(`
`,1)[0],this.options.pedantic?(s=2,f=c.trimLeft()):(s=r[2].search(/[^ ]/),s=s>4?1:s,f=c.slice(s),s+=r[1].length),d=!1,!c&&/^ *$/.test(a)&&(n+=a+`
`,t=t.substring(a.length+1),x=!0),!x){let L=new RegExp(`^ {0,${Math.min(3,s-1)}}(?:[*+-]|\\d{1,9}[.)])((?: [^\\n]*)?(?:\\n|$))`),S=new RegExp(`^ {0,${Math.min(3,s-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),R=new RegExp(`^( {0,${Math.min(3,s-1)}})(\`\`\`|~~~)`);for(;t&&(p=t.split(`
`,1)[0],c=p,this.options.pedantic&&(c=c.replace(/^ {1,4}(?=( {4})*[^ ])/g,"  ")),!(R.test(c)||this.rules.block.heading.test(c)||L.test(c)||S.test(t)));){if(c.search(/[^ ]/)>=s||!c.trim())f+=`
`+c.slice(s);else if(!d)f+=`
`+c;else break;!d&&!c.trim()&&(d=!0),n+=p+`
`,t=t.substring(p.length+1)}}b.loose||(u?b.loose=!0:/\n *\n *$/.test(n)&&(u=!0)),this.options.gfm&&(o=/^\[[ xX]\] /.exec(f),o&&(i=o[0]!=="[ ] ",f=f.replace(/^\[[ xX]\] +/,""))),b.items.push({type:"list_item",raw:n,task:!!o,checked:i,loose:!1,text:f}),b.raw+=n}b.items[b.items.length-1].raw=n.trimRight(),b.items[b.items.length-1].text=f.trimRight(),b.raw=b.raw.trimRight();let k=b.items.length;for(l=0;l<k;l++){this.lexer.state.top=!1,b.items[l].tokens=this.lexer.blockTokens(b.items[l].text,[]);let L=b.items[l].tokens.filter(R=>R.type==="space"),S=L.every(R=>{let j=R.raw.split(""),O=0;for(let H of j)if(H===`
`&&(O+=1),O>1)return!0;return!1});!b.loose&&L.length&&S&&(b.loose=!0,b.items[l].loose=!0)}return b}}html(t){let r=this.rules.block.html.exec(t);if(r){let n={type:"html",raw:r[0],pre:!this.options.sanitizer&&(r[1]==="pre"||r[1]==="script"||r[1]==="style"),text:r[0]};return this.options.sanitize&&(n.type="paragraph",n.text=this.options.sanitizer?this.options.sanitizer(r[0]):B(r[0]),n.tokens=[],this.lexer.inline(n.text,n.tokens)),n}}def(t){let r=this.rules.block.def.exec(t);if(r){r[3]&&(r[3]=r[3].substring(1,r[3].length-1));let n=r[1].toLowerCase().replace(/\s+/g," ");return{type:"def",tag:n,raw:r[0],href:r[2],title:r[3]}}}table(t){let r=this.rules.block.table.exec(t);if(r){let n={type:"table",header:er(r[1]).map(o=>({text:o})),align:r[2].replace(/^ *|\| *$/g,"").split(/ *\| */),rows:r[3]&&r[3].trim()?r[3].replace(/\n[ \t]*$/,"").split(`
`):[]};if(n.header.length===n.align.length){n.raw=r[0];let o=n.align.length,i,s,l,d;for(i=0;i<o;i++)/^ *-+: *$/.test(n.align[i])?n.align[i]="right":/^ *:-+: *$/.test(n.align[i])?n.align[i]="center":/^ *:-+ *$/.test(n.align[i])?n.align[i]="left":n.align[i]=null;for(o=n.rows.length,i=0;i<o;i++)n.rows[i]=er(n.rows[i],n.header.length).map(u=>({text:u}));for(o=n.header.length,s=0;s<o;s++)n.header[s].tokens=[],this.lexer.inline(n.header[s].text,n.header[s].tokens);for(o=n.rows.length,s=0;s<o;s++)for(d=n.rows[s],l=0;l<d.length;l++)d[l].tokens=[],this.lexer.inline(d[l].text,d[l].tokens);return n}}}lheading(t){let r=this.rules.block.lheading.exec(t);if(r){let n={type:"heading",raw:r[0],depth:r[2].charAt(0)==="="?1:2,text:r[1],tokens:[]};return this.lexer.inline(n.text,n.tokens),n}}paragraph(t){let r=this.rules.block.paragraph.exec(t);if(r){let n={type:"paragraph",raw:r[0],text:r[1].charAt(r[1].length-1)===`
`?r[1].slice(0,-1):r[1],tokens:[]};return this.lexer.inline(n.text,n.tokens),n}}text(t){let r=this.rules.block.text.exec(t);if(r){let n={type:"text",raw:r[0],text:r[0],tokens:[]};return this.lexer.inline(n.text,n.tokens),n}}escape(t){let r=this.rules.inline.escape.exec(t);if(r)return{type:"escape",raw:r[0],text:B(r[1])}}tag(t){let r=this.rules.inline.tag.exec(t);if(r)return!this.lexer.state.inLink&&/^<a /i.test(r[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&/^<\/a>/i.test(r[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&/^<(pre|code|kbd|script)(\s|>)/i.test(r[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(r[0])&&(this.lexer.state.inRawBlock=!1),{type:this.options.sanitize?"text":"html",raw:r[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,text:this.options.sanitize?this.options.sanitizer?this.options.sanitizer(r[0]):B(r[0]):r[0]}}link(t){let r=this.rules.inline.link.exec(t);if(r){let n=r[2].trim();if(!this.options.pedantic&&/^</.test(n)){if(!/>$/.test(n))return;let s=Ue(n.slice(0,-1),"\\");if((n.length-s.length)%2===0)return}else{let s=go(r[2],"()");if(s>-1){let d=(r[0].indexOf("!")===0?5:4)+r[1].length+s;r[2]=r[2].substring(0,s),r[0]=r[0].substring(0,d).trim(),r[3]=""}}let o=r[2],i="";if(this.options.pedantic){let s=/^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(o);s&&(o=s[1],i=s[3])}else i=r[3]?r[3].slice(1,-1):"";return o=o.trim(),/^</.test(o)&&(this.options.pedantic&&!/>$/.test(n)?o=o.slice(1):o=o.slice(1,-1)),rr(r,{href:o&&o.replace(this.rules.inline._escapes,"$1"),title:i&&i.replace(this.rules.inline._escapes,"$1")},r[0],this.lexer)}}reflink(t,r){let n;if((n=this.rules.inline.reflink.exec(t))||(n=this.rules.inline.nolink.exec(t))){let o=(n[2]||n[1]).replace(/\s+/g," ");if(o=r[o.toLowerCase()],!o||!o.href){let i=n[0].charAt(0);return{type:"text",raw:i,text:i}}return rr(n,o,n[0],this.lexer)}}emStrong(t,r,n=""){let o=this.rules.inline.emStrong.lDelim.exec(t);if(!o||o[3]&&n.match(/[\p{L}\p{N}]/u))return;let i=o[1]||o[2]||"";if(!i||i&&(n===""||this.rules.inline.punctuation.exec(n))){let s=o[0].length-1,l,d,u=s,c=0,a=o[0][0]==="*"?this.rules.inline.emStrong.rDelimAst:this.rules.inline.emStrong.rDelimUnd;for(a.lastIndex=0,r=r.slice(-1*t.length+s);(o=a.exec(r))!=null;){if(l=o[1]||o[2]||o[3]||o[4]||o[5]||o[6],!l)continue;if(d=l.length,o[3]||o[4]){u+=d;continue}else if((o[5]||o[6])&&s%3&&!((s+d)%3)){c+=d;continue}if(u-=d,u>0)continue;if(d=Math.min(d,d+u+c),Math.min(s,d)%2){let f=t.slice(1,s+o.index+d);return{type:"em",raw:t.slice(0,s+o.index+d+1),text:f,tokens:this.lexer.inlineTokens(f,[])}}let p=t.slice(2,s+o.index+d-1);return{type:"strong",raw:t.slice(0,s+o.index+d+1),text:p,tokens:this.lexer.inlineTokens(p,[])}}}}codespan(t){let r=this.rules.inline.code.exec(t);if(r){let n=r[2].replace(/\n/g," "),o=/[^ ]/.test(n),i=/^ /.test(n)&&/ $/.test(n);return o&&i&&(n=n.substring(1,n.length-1)),n=B(n,!0),{type:"codespan",raw:r[0],text:n}}}br(t){let r=this.rules.inline.br.exec(t);if(r)return{type:"br",raw:r[0]}}del(t){let r=this.rules.inline.del.exec(t);if(r)return{type:"del",raw:r[0],text:r[2],tokens:this.lexer.inlineTokens(r[2],[])}}autolink(t,r){let n=this.rules.inline.autolink.exec(t);if(n){let o,i;return n[2]==="@"?(o=B(this.options.mangle?r(n[1]):n[1]),i="mailto:"+o):(o=B(n[1]),i=o),{type:"link",raw:n[0],text:o,href:i,tokens:[{type:"text",raw:o,text:o}]}}}url(t,r){let n;if(n=this.rules.inline.url.exec(t)){let o,i;if(n[2]==="@")o=B(this.options.mangle?r(n[0]):n[0]),i="mailto:"+o;else{let s;do s=n[0],n[0]=this.rules.inline._backpedal.exec(n[0])[0];while(s!==n[0]);o=B(n[0]),n[1]==="www."?i="http://"+o:i=o}return{type:"link",raw:n[0],text:o,href:i,tokens:[{type:"text",raw:o,text:o}]}}}inlineText(t,r){let n=this.rules.inline.text.exec(t);if(n){let o;return this.lexer.state.inRawBlock?o=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(n[0]):B(n[0]):n[0]:o=B(this.options.smartypants?r(n[0]):n[0]),{type:"text",raw:n[0],text:o}}}},$={newline:/^(?: *(?:\n|$))+/,code:/^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/,fences:/^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?=\n|$)|$)/,hr:/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,heading:/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,blockquote:/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/,html:"^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|<![A-Z][\\s\\S]*?(?:>\\n*|$)|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>\\n*|$)|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n *)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)|</(?!script|pre|style|textarea)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$))",def:/^ {0,3}\[(label)\]: *(?:\n *)?<?([^\s>]+)>?(?:(?: +(?:\n *)?| *\n *)(title))? *(?:\n+|$)/,table:Ze,lheading:/^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,_paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,text:/^[^\n]+/};$._label=/(?!\s*\])(?:\\.|[^\[\]\\])+/;$._title=/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;$.def=D($.def).replace("label",$._label).replace("title",$._title).getRegex();$.bullet=/(?:[*+-]|\d{1,9}[.)])/;$.listItemStart=D(/^( *)(bull) */).replace("bull",$.bullet).getRegex();$.list=D($.list).replace(/bull/g,$.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+$.def.source+")").getRegex();$._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul";$._comment=/<!--(?!-?>)[\s\S]*?(?:-->|$)/;$.html=D($.html,"i").replace("comment",$._comment).replace("tag",$._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex();$.paragraph=D($._paragraph).replace("hr",$.hr).replace("heading"," {0,3}#{1,6} ").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",$._tag).getRegex();$.blockquote=D($.blockquote).replace("paragraph",$.paragraph).getRegex();$.normal=ae({},$);$.gfm=ae({},$.normal,{table:"^ *([^\\n ].*\\|.*)\\n {0,3}(?:\\| *)?(:?-+:? *(?:\\| *:?-+:? *)*)(?:\\| *)?(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)"});$.gfm.table=D($.gfm.table).replace("hr",$.hr).replace("heading"," {0,3}#{1,6} ").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",$._tag).getRegex();$.gfm.paragraph=D($._paragraph).replace("hr",$.hr).replace("heading"," {0,3}#{1,6} ").replace("|lheading","").replace("table",$.gfm.table).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",$._tag).getRegex();$.pedantic=ae({},$.normal,{html:D(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)|<tag(?:"[^"]*"|'[^']*'|\\s[^'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment",$._comment).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:Ze,paragraph:D($.normal._paragraph).replace("hr",$.hr).replace("heading",` *#{1,6} *[^
]`).replace("lheading",$.lheading).replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").getRegex()});var _={escape:/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,autolink:/^<(scheme:[^\s\x00-\x1f<>]*|email)>/,url:Ze,tag:"^comment|^</[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^<![a-zA-Z]+\\s[\\s\\S]*?>|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>",link:/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,reflink:/^!?\[(label)\]\[(ref)\]/,nolink:/^!?\[(ref)\](?:\[\])?/,reflinkSearch:"reflink|nolink(?!\\()",emStrong:{lDelim:/^(?:\*+(?:([punct_])|[^\s*]))|^_+(?:([punct*])|([^\s_]))/,rDelimAst:/^[^_*]*?\_\_[^_*]*?\*[^_*]*?(?=\_\_)|[^*]+(?=[^*])|[punct_](\*+)(?=[\s]|$)|[^punct*_\s](\*+)(?=[punct_\s]|$)|[punct_\s](\*+)(?=[^punct*_\s])|[\s](\*+)(?=[punct_])|[punct_](\*+)(?=[punct_])|[^punct*_\s](\*+)(?=[^punct*_\s])/,rDelimUnd:/^[^_*]*?\*\*[^_*]*?\_[^_*]*?(?=\*\*)|[^_]+(?=[^_])|[punct*](\_+)(?=[\s]|$)|[^punct*_\s](\_+)(?=[punct*\s]|$)|[punct*\s](\_+)(?=[^punct*_\s])|[\s](\_+)(?=[punct*])|[punct*](\_+)(?=[punct*])/},code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,br:/^( {2,}|\\)\n(?!\s*$)/,del:Ze,text:/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*_]|\b_|$)|[^ ](?= {2,}\n)))/,punctuation:/^([\spunctuation])/};_._punctuation="!\"#$%&'()+\\-.,/:;<=>?@\\[\\]`^{|}~";_.punctuation=D(_.punctuation).replace(/punctuation/g,_._punctuation).getRegex();_.blockSkip=/\[[^\]]*?\]\([^\)]*?\)|`[^`]*?`|<[^>]*?>/g;_.escapedEmSt=/\\\*|\\_/g;_._comment=D($._comment).replace("(?:-->|$)","-->").getRegex();_.emStrong.lDelim=D(_.emStrong.lDelim).replace(/punct/g,_._punctuation).getRegex();_.emStrong.rDelimAst=D(_.emStrong.rDelimAst,"g").replace(/punct/g,_._punctuation).getRegex();_.emStrong.rDelimUnd=D(_.emStrong.rDelimUnd,"g").replace(/punct/g,_._punctuation).getRegex();_._escapes=/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;_._scheme=/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;_._email=/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/;_.autolink=D(_.autolink).replace("scheme",_._scheme).replace("email",_._email).getRegex();_._attribute=/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;_.tag=D(_.tag).replace("comment",_._comment).replace("attribute",_._attribute).getRegex();_._label=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;_._href=/<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/;_._title=/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;_.link=D(_.link).replace("label",_._label).replace("href",_._href).replace("title",_._title).getRegex();_.reflink=D(_.reflink).replace("label",_._label).replace("ref",$._label).getRegex();_.nolink=D(_.nolink).replace("ref",$._label).getRegex();_.reflinkSearch=D(_.reflinkSearch,"g").replace("reflink",_.reflink).replace("nolink",_.nolink).getRegex();_.normal=ae({},_);_.pedantic=ae({},_.normal,{strong:{start:/^__|\*\*/,middle:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,endAst:/\*\*(?!\*)/g,endUnd:/__(?!_)/g},em:{start:/^_|\*/,middle:/^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/,endAst:/\*(?!\*)/g,endUnd:/_(?!_)/g},link:D(/^!?\[(label)\]\((.*?)\)/).replace("label",_._label).getRegex(),reflink:D(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",_._label).getRegex()});_.gfm=ae({},_.normal,{escape:D(_.escape).replace("])","~|])").getRegex(),_extended_email:/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,url:/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,_backpedal:/(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,text:/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/});_.gfm.url=D(_.gfm.url,"i").replace("email",_.gfm._extended_email).getRegex();_.breaks=ae({},_.gfm,{br:D(_.br).replace("{2,}","*").getRegex(),text:D(_.gfm.text).replace("\\b_","\\b_| {2,}\\n").replace(/\{2,\}/g,"*").getRegex()});function uo(e){return e.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…")}function nr(e){let t="",r,n,o=e.length;for(r=0;r<o;r++)n=e.charCodeAt(r),Math.random()>.5&&(n="x"+n.toString(16)),t+="&#"+n+";";return t}var ie=class{constructor(t){this.tokens=[],this.tokens.links=Object.create(null),this.options=t||ve,this.options.tokenizer=this.options.tokenizer||new Le,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};let r={block:$.normal,inline:_.normal};this.options.pedantic?(r.block=$.pedantic,r.inline=_.pedantic):this.options.gfm&&(r.block=$.gfm,this.options.breaks?r.inline=_.breaks:r.inline=_.gfm),this.tokenizer.rules=r}static get rules(){return{block:$,inline:_}}static lex(t,r){return new ie(r).lex(t)}static lexInline(t,r){return new ie(r).inlineTokens(t)}lex(t){t=t.replace(/\r\n|\r/g,`
`),this.blockTokens(t,this.tokens);let r;for(;r=this.inlineQueue.shift();)this.inlineTokens(r.src,r.tokens);return this.tokens}blockTokens(t,r=[]){this.options.pedantic?t=t.replace(/\t/g,"    ").replace(/^ +$/gm,""):t=t.replace(/^( *)(\t+)/gm,(l,d,u)=>d+"    ".repeat(u.length));let n,o,i,s;for(;t;)if(!(this.options.extensions&&this.options.extensions.block&&this.options.extensions.block.some(l=>(n=l.call({lexer:this},t,r))?(t=t.substring(n.raw.length),r.push(n),!0):!1))){if(n=this.tokenizer.space(t)){t=t.substring(n.raw.length),n.raw.length===1&&r.length>0?r[r.length-1].raw+=`
`:r.push(n);continue}if(n=this.tokenizer.code(t)){t=t.substring(n.raw.length),o=r[r.length-1],o&&(o.type==="paragraph"||o.type==="text")?(o.raw+=`
`+n.raw,o.text+=`
`+n.text,this.inlineQueue[this.inlineQueue.length-1].src=o.text):r.push(n);continue}if(n=this.tokenizer.fences(t)){t=t.substring(n.raw.length),r.push(n);continue}if(n=this.tokenizer.heading(t)){t=t.substring(n.raw.length),r.push(n);continue}if(n=this.tokenizer.hr(t)){t=t.substring(n.raw.length),r.push(n);continue}if(n=this.tokenizer.blockquote(t)){t=t.substring(n.raw.length),r.push(n);continue}if(n=this.tokenizer.list(t)){t=t.substring(n.raw.length),r.push(n);continue}if(n=this.tokenizer.html(t)){t=t.substring(n.raw.length),r.push(n);continue}if(n=this.tokenizer.def(t)){t=t.substring(n.raw.length),o=r[r.length-1],o&&(o.type==="paragraph"||o.type==="text")?(o.raw+=`
`+n.raw,o.text+=`
`+n.raw,this.inlineQueue[this.inlineQueue.length-1].src=o.text):this.tokens.links[n.tag]||(this.tokens.links[n.tag]={href:n.href,title:n.title});continue}if(n=this.tokenizer.table(t)){t=t.substring(n.raw.length),r.push(n);continue}if(n=this.tokenizer.lheading(t)){t=t.substring(n.raw.length),r.push(n);continue}if(i=t,this.options.extensions&&this.options.extensions.startBlock){let l=1/0,d=t.slice(1),u;this.options.extensions.startBlock.forEach(function(c){u=c.call({lexer:this},d),typeof u=="number"&&u>=0&&(l=Math.min(l,u))}),l<1/0&&l>=0&&(i=t.substring(0,l+1))}if(this.state.top&&(n=this.tokenizer.paragraph(i))){o=r[r.length-1],s&&o.type==="paragraph"?(o.raw+=`
`+n.raw,o.text+=`
`+n.text,this.inlineQueue.pop(),this.inlineQueue[this.inlineQueue.length-1].src=o.text):r.push(n),s=i.length!==t.length,t=t.substring(n.raw.length);continue}if(n=this.tokenizer.text(t)){t=t.substring(n.raw.length),o=r[r.length-1],o&&o.type==="text"?(o.raw+=`
`+n.raw,o.text+=`
`+n.text,this.inlineQueue.pop(),this.inlineQueue[this.inlineQueue.length-1].src=o.text):r.push(n);continue}if(t){let l="Infinite loop on byte: "+t.charCodeAt(0);if(this.options.silent){console.error(l);break}else throw new Error(l)}}return this.state.top=!0,r}inline(t,r){this.inlineQueue.push({src:t,tokens:r})}inlineTokens(t,r=[]){let n,o,i,s=t,l,d,u;if(this.tokens.links){let c=Object.keys(this.tokens.links);if(c.length>0)for(;(l=this.tokenizer.rules.inline.reflinkSearch.exec(s))!=null;)c.includes(l[0].slice(l[0].lastIndexOf("[")+1,-1))&&(s=s.slice(0,l.index)+"["+tr("a",l[0].length-2)+"]"+s.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;(l=this.tokenizer.rules.inline.blockSkip.exec(s))!=null;)s=s.slice(0,l.index)+"["+tr("a",l[0].length-2)+"]"+s.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);for(;(l=this.tokenizer.rules.inline.escapedEmSt.exec(s))!=null;)s=s.slice(0,l.index)+"++"+s.slice(this.tokenizer.rules.inline.escapedEmSt.lastIndex);for(;t;)if(d||(u=""),d=!1,!(this.options.extensions&&this.options.extensions.inline&&this.options.extensions.inline.some(c=>(n=c.call({lexer:this},t,r))?(t=t.substring(n.raw.length),r.push(n),!0):!1))){if(n=this.tokenizer.escape(t)){t=t.substring(n.raw.length),r.push(n);continue}if(n=this.tokenizer.tag(t)){t=t.substring(n.raw.length),o=r[r.length-1],o&&n.type==="text"&&o.type==="text"?(o.raw+=n.raw,o.text+=n.text):r.push(n);continue}if(n=this.tokenizer.link(t)){t=t.substring(n.raw.length),r.push(n);continue}if(n=this.tokenizer.reflink(t,this.tokens.links)){t=t.substring(n.raw.length),o=r[r.length-1],o&&n.type==="text"&&o.type==="text"?(o.raw+=n.raw,o.text+=n.text):r.push(n);continue}if(n=this.tokenizer.emStrong(t,s,u)){t=t.substring(n.raw.length),r.push(n);continue}if(n=this.tokenizer.codespan(t)){t=t.substring(n.raw.length),r.push(n);continue}if(n=this.tokenizer.br(t)){t=t.substring(n.raw.length),r.push(n);continue}if(n=this.tokenizer.del(t)){t=t.substring(n.raw.length),r.push(n);continue}if(n=this.tokenizer.autolink(t,nr)){t=t.substring(n.raw.length),r.push(n);continue}if(!this.state.inLink&&(n=this.tokenizer.url(t,nr))){t=t.substring(n.raw.length),r.push(n);continue}if(i=t,this.options.extensions&&this.options.extensions.startInline){let c=1/0,a=t.slice(1),p;this.options.extensions.startInline.forEach(function(f){p=f.call({lexer:this},a),typeof p=="number"&&p>=0&&(c=Math.min(c,p))}),c<1/0&&c>=0&&(i=t.substring(0,c+1))}if(n=this.tokenizer.inlineText(i,uo)){t=t.substring(n.raw.length),n.raw.slice(-1)!=="_"&&(u=n.raw.slice(-1)),d=!0,o=r[r.length-1],o&&o.type==="text"?(o.raw+=n.raw,o.text+=n.text):r.push(n);continue}if(t){let c="Infinite loop on byte: "+t.charCodeAt(0);if(this.options.silent){console.error(c);break}else throw new Error(c)}}return r}},Te=class{constructor(t){this.options=t||ve}code(t,r,n){let o=(r||"").match(/\S*/)[0];if(this.options.highlight){let i=this.options.highlight(t,o);i!=null&&i!==t&&(n=!0,t=i)}return t=t.replace(/\n$/,"")+`
`,o?'<pre><code class="'+this.options.langPrefix+B(o,!0)+'">'+(n?t:B(t,!0))+`</code></pre>
`:"<pre><code>"+(n?t:B(t,!0))+`</code></pre>
`}blockquote(t){return`<blockquote>
${t}</blockquote>
`}html(t){return t}heading(t,r,n,o){if(this.options.headerIds){let i=this.options.headerPrefix+o.slug(n);return`<h${r} id="${i}">${t}</h${r}>
`}return`<h${r}>${t}</h${r}>
`}hr(){return this.options.xhtml?`<hr/>
`:`<hr>
`}list(t,r,n){let o=r?"ol":"ul",i=r&&n!==1?' start="'+n+'"':"";return"<"+o+i+`>
`+t+"</"+o+`>
`}listitem(t){return`<li>${t}</li>
`}checkbox(t){return"<input "+(t?'checked="" ':"")+'disabled="" type="checkbox"'+(this.options.xhtml?" /":"")+"> "}paragraph(t){return`<p>${t}</p>
`}table(t,r){return r&&(r=`<tbody>${r}</tbody>`),`<table>
<thead>
`+t+`</thead>
`+r+`</table>
`}tablerow(t){return`<tr>
${t}</tr>
`}tablecell(t,r){let n=r.header?"th":"td";return(r.align?`<${n} align="${r.align}">`:`<${n}>`)+t+`</${n}>
`}strong(t){return`<strong>${t}</strong>`}em(t){return`<em>${t}</em>`}codespan(t){return`<code>${t}</code>`}br(){return this.options.xhtml?"<br/>":"<br>"}del(t){return`<del>${t}</del>`}link(t,r,n){if(t=Kt(this.options.sanitize,this.options.baseUrl,t),t===null)return n;let o='<a href="'+B(t)+'"';return r&&(o+=' title="'+r+'"'),o+=">"+n+"</a>",o}image(t,r,n){if(t=Kt(this.options.sanitize,this.options.baseUrl,t),t===null)return n;let o=`<img src="${t}" alt="${n}"`;return r&&(o+=` title="${r}"`),o+=this.options.xhtml?"/>":">",o}text(t){return t}},Ge=class{strong(t){return t}em(t){return t}codespan(t){return t}del(t){return t}html(t){return t}text(t){return t}link(t,r,n){return""+n}image(t,r,n){return""+n}br(){return""}},Ve=class{constructor(){this.seen={}}serialize(t){return t.toLowerCase().trim().replace(/<[!\/a-z].*?>/ig,"").replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g,"").replace(/\s/g,"-")}getNextSafeSlug(t,r){let n=t,o=0;if(this.seen.hasOwnProperty(n)){o=this.seen[t];do o++,n=t+"-"+o;while(this.seen.hasOwnProperty(n))}return r||(this.seen[t]=o,this.seen[n]=0),n}slug(t,r={}){let n=this.serialize(t);return this.getNextSafeSlug(n,r.dryrun)}},se=class{constructor(t){this.options=t||ve,this.options.renderer=this.options.renderer||new Te,this.renderer=this.options.renderer,this.renderer.options=this.options,this.textRenderer=new Ge,this.slugger=new Ve}static parse(t,r){return new se(r).parse(t)}static parseInline(t,r){return new se(r).parseInline(t)}parse(t,r=!0){let n="",o,i,s,l,d,u,c,a,p,f,x,h,m,b,w,k,L,S,R,j=t.length;for(o=0;o<j;o++){if(f=t[o],this.options.extensions&&this.options.extensions.renderers&&this.options.extensions.renderers[f.type]&&(R=this.options.extensions.renderers[f.type].call({parser:this},f),R!==!1||!["space","hr","heading","code","table","blockquote","list","html","paragraph","text"].includes(f.type))){n+=R||"";continue}switch(f.type){case"space":continue;case"hr":{n+=this.renderer.hr();continue}case"heading":{n+=this.renderer.heading(this.parseInline(f.tokens),f.depth,ir(this.parseInline(f.tokens,this.textRenderer)),this.slugger);continue}case"code":{n+=this.renderer.code(f.text,f.lang,f.escaped);continue}case"table":{for(a="",c="",l=f.header.length,i=0;i<l;i++)c+=this.renderer.tablecell(this.parseInline(f.header[i].tokens),{header:!0,align:f.align[i]});for(a+=this.renderer.tablerow(c),p="",l=f.rows.length,i=0;i<l;i++){for(u=f.rows[i],c="",d=u.length,s=0;s<d;s++)c+=this.renderer.tablecell(this.parseInline(u[s].tokens),{header:!1,align:f.align[s]});p+=this.renderer.tablerow(c)}n+=this.renderer.table(a,p);continue}case"blockquote":{p=this.parse(f.tokens),n+=this.renderer.blockquote(p);continue}case"list":{for(x=f.ordered,h=f.start,m=f.loose,l=f.items.length,p="",i=0;i<l;i++)w=f.items[i],k=w.checked,L=w.task,b="",w.task&&(S=this.renderer.checkbox(k),m?w.tokens.length>0&&w.tokens[0].type==="paragraph"?(w.tokens[0].text=S+" "+w.tokens[0].text,w.tokens[0].tokens&&w.tokens[0].tokens.length>0&&w.tokens[0].tokens[0].type==="text"&&(w.tokens[0].tokens[0].text=S+" "+w.tokens[0].tokens[0].text)):w.tokens.unshift({type:"text",text:S}):b+=S),b+=this.parse(w.tokens,m),p+=this.renderer.listitem(b,L,k);n+=this.renderer.list(p,x,h);continue}case"html":{n+=this.renderer.html(f.text);continue}case"paragraph":{n+=this.renderer.paragraph(this.parseInline(f.tokens));continue}case"text":{for(p=f.tokens?this.parseInline(f.tokens):f.text;o+1<j&&t[o+1].type==="text";)f=t[++o],p+=`
`+(f.tokens?this.parseInline(f.tokens):f.text);n+=r?this.renderer.paragraph(p):p;continue}default:{let O='Token with "'+f.type+'" type was not found.';if(this.options.silent){console.error(O);return}else throw new Error(O)}}}return n}parseInline(t,r){r=r||this.renderer;let n="",o,i,s,l=t.length;for(o=0;o<l;o++){if(i=t[o],this.options.extensions&&this.options.extensions.renderers&&this.options.extensions.renderers[i.type]&&(s=this.options.extensions.renderers[i.type].call({parser:this},i),s!==!1||!["escape","html","link","image","strong","em","codespan","br","del","text"].includes(i.type))){n+=s||"";continue}switch(i.type){case"escape":{n+=r.text(i.text);break}case"html":{n+=r.html(i.text);break}case"link":{n+=r.link(i.href,i.title,this.parseInline(i.tokens,r));break}case"image":{n+=r.image(i.href,i.title,i.text);break}case"strong":{n+=r.strong(this.parseInline(i.tokens,r));break}case"em":{n+=r.em(this.parseInline(i.tokens,r));break}case"codespan":{n+=r.codespan(i.text);break}case"br":{n+=r.br();break}case"del":{n+=r.del(this.parseInline(i.tokens,r));break}case"text":{n+=r.text(i.text);break}default:{let d='Token with "'+i.type+'" type was not found.';if(this.options.silent){console.error(d);return}else throw new Error(d)}}}return n}};function T(e,t,r){if(typeof e>"u"||e===null)throw new Error("marked(): input parameter is undefined or null");if(typeof e!="string")throw new Error("marked(): input parameter is of type "+Object.prototype.toString.call(e)+", string expected");if(typeof t=="function"&&(r=t,t=null),t=ae({},T.defaults,t||{}),sr(t),r){let n=t.highlight,o;try{o=ie.lex(e,t)}catch(l){return r(l)}let i=function(l){let d;if(!l)try{t.walkTokens&&T.walkTokens(o,t.walkTokens),d=se.parse(o,t)}catch(u){l=u}return t.highlight=n,l?r(l):r(null,d)};if(!n||n.length<3||(delete t.highlight,!o.length))return i();let s=0;T.walkTokens(o,function(l){l.type==="code"&&(s++,setTimeout(()=>{n(l.text,l.lang,function(d,u){if(d)return i(d);u!=null&&u!==l.text&&(l.text=u,l.escaped=!0),s--,s===0&&i()})},0))}),s===0&&i();return}try{let n=ie.lex(e,t);return t.walkTokens&&T.walkTokens(n,t.walkTokens),se.parse(n,t)}catch(n){if(n.message+=`
Please report this to https://github.com/markedjs/marked.`,t.silent)return"<p>An error occurred:</p><pre>"+B(n.message+"",!0)+"</pre>";throw n}}T.options=T.setOptions=function(e){return ae(T.defaults,e),Wn(T.defaults),T};T.getDefaults=or;T.defaults=ve;T.use=function(...e){let t=ae({},...e),r=T.defaults.extensions||{renderers:{},childTokens:{}},n;e.forEach(o=>{if(o.extensions&&(n=!0,o.extensions.forEach(i=>{if(!i.name)throw new Error("extension name required");if(i.renderer){let s=r.renderers?r.renderers[i.name]:null;s?r.renderers[i.name]=function(...l){let d=i.renderer.apply(this,l);return d===!1&&(d=s.apply(this,l)),d}:r.renderers[i.name]=i.renderer}if(i.tokenizer){if(!i.level||i.level!=="block"&&i.level!=="inline")throw new Error("extension level must be 'block' or 'inline'");r[i.level]?r[i.level].unshift(i.tokenizer):r[i.level]=[i.tokenizer],i.start&&(i.level==="block"?r.startBlock?r.startBlock.push(i.start):r.startBlock=[i.start]:i.level==="inline"&&(r.startInline?r.startInline.push(i.start):r.startInline=[i.start]))}i.childTokens&&(r.childTokens[i.name]=i.childTokens)})),o.renderer){let i=T.defaults.renderer||new Te;for(let s in o.renderer){let l=i[s];i[s]=(...d)=>{let u=o.renderer[s].apply(i,d);return u===!1&&(u=l.apply(i,d)),u}}t.renderer=i}if(o.tokenizer){let i=T.defaults.tokenizer||new Le;for(let s in o.tokenizer){let l=i[s];i[s]=(...d)=>{let u=o.tokenizer[s].apply(i,d);return u===!1&&(u=l.apply(i,d)),u}}t.tokenizer=i}if(o.walkTokens){let i=T.defaults.walkTokens;t.walkTokens=function(s){o.walkTokens.call(this,s),i&&i.call(this,s)}}n&&(t.extensions=r),T.setOptions(t)})};T.walkTokens=function(e,t){for(let r of e)switch(t.call(T,r),r.type){case"table":{for(let n of r.header)T.walkTokens(n.tokens,t);for(let n of r.rows)for(let o of n)T.walkTokens(o.tokens,t);break}case"list":{T.walkTokens(r.items,t);break}default:T.defaults.extensions&&T.defaults.extensions.childTokens&&T.defaults.extensions.childTokens[r.type]?T.defaults.extensions.childTokens[r.type].forEach(function(n){T.walkTokens(r[n],t)}):r.tokens&&T.walkTokens(r.tokens,t)}};T.parseInline=function(e,t){if(typeof e>"u"||e===null)throw new Error("marked.parseInline(): input parameter is undefined or null");if(typeof e!="string")throw new Error("marked.parseInline(): input parameter is of type "+Object.prototype.toString.call(e)+", string expected");t=ae({},T.defaults,t||{}),sr(t);try{let r=ie.lexInline(e,t);return t.walkTokens&&T.walkTokens(r,t.walkTokens),se.parseInline(r,t)}catch(r){if(r.message+=`
Please report this to https://github.com/markedjs/marked.`,t.silent)return"<p>An error occurred:</p><pre>"+B(r.message+"",!0)+"</pre>";throw r}};T.Parser=se;T.parser=se.parse;T.Renderer=Te;T.TextRenderer=Ge;T.Lexer=ie;T.lexer=ie.lex;T.Tokenizer=Le;T.Slugger=Ve;T.parse=T;var ji=T.options,Oi=T.setOptions,Di=T.use,Ri=T.walkTokens,Pi=T.parseInline;var qi=se.parse,Bi=ie.lex;var ar='<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="%" style="margin-bottom: -3px;" exlg="exlg"><path d="M16 8C16 6.84375 15.25 5.84375 14.1875 5.4375C14.6562 4.4375 14.4688 3.1875 13.6562 2.34375C12.8125 1.53125 11.5625 1.34375 10.5625 1.8125C10.1562 0.75 9.15625 0 8 0C6.8125 0 5.8125 0.75 5.40625 1.8125C4.40625 1.34375 3.15625 1.53125 2.34375 2.34375C1.5 3.1875 1.3125 4.4375 1.78125 5.4375C0.71875 5.84375 0 6.84375 0 8C0 9.1875 0.71875 10.1875 1.78125 10.5938C1.3125 11.5938 1.5 12.8438 2.34375 13.6562C3.15625 14.5 4.40625 14.6875 5.40625 14.2188C5.8125 15.2812 6.8125 16 8 16C9.15625 16 10.1562 15.2812 10.5625 14.2188C11.5938 14.6875 12.8125 14.5 13.6562 13.6562C14.4688 12.8438 14.6562 11.5938 14.1875 10.5938C15.25 10.1875 16 9.1875 16 8ZM11.4688 6.625L7.375 10.6875C7.21875 10.8438 7 10.8125 6.875 10.6875L4.5 8.3125C4.375 8.1875 4.375 7.96875 4.5 7.8125L5.3125 7C5.46875 6.875 5.6875 6.875 5.8125 7.03125L7.125 8.34375L10.1562 5.34375C10.3125 5.1875 10.5312 5.1875 10.6562 5.34375L11.4688 6.15625C11.5938 6.28125 11.5938 6.5 11.4688 6.625Z"></path></svg>';v.reg("benben","全网犇犇","@/",{source:{ty:"enum",dft:"o2",vals:["o2","shy"],info:["Source","切换全网犇犇获取方式"]},reply_with_md:{ty:"boolean",dft:!1,info:["Reply with markdown","回复时附上原 markdown"]}},({msto:e})=>{let t={Gray:"gray",Blue:"bluelight",Green:"green",Orange:"orange lg-bold",Red:"red lg-bold",Purple:"purple lg-bold",Cheater:"brown lg-bold"},r='&nbsp;<a class="sb_amazeui" target="_blank" href="/discuss/show/142324">$</a>',n=s=>s<=3?"":r.replace("$",ar.replace("%",s<=5?"#5eb95e":s<=7?"#3498db":"#f1c40f")),o=A.loadFeed;A.loadFeed=function(){A.feedMode==="all-exlg"?(ne({url:e.source==="o2"?"https://service-ig5px5gh-1305163805.sh.apigw.tencentcs.com/release/APIGWHtmlDemo-1615602121":`https://bens.rotriw.com/api/list/proxy?page=${A.feedPage}`,onload:s=>{JSON.parse(s.response).forEach(d=>{let u=T(me.process(d.content));g(`
                <li class="am-comment am-comment-primary feed-li" exlg="exlg">
                    <div class="lg-left">
                        <a href="/user/${d.user.uid}" class="center">
                            <img src="https://cdn.luogu.com.cn/upload/usericon/${d.user.uid}.png" class="am-comment-avatar">
                        </a>
                    </div>
                    <div class="am-comment-main">
                        <header class="am-comment-hd">
                            <div class="am-comment-meta">
                                <span class="feed-username">
                                    <a class="lg-fg-${t[d.user.color]}" href="/user/${d.user.uid}" target="_blank">${d.user.name}</a>${n(d.user.ccfLevel)}${d.user.badge?`&nbsp;<span class="am-badge am-radius lg-bg-${t[d.user.color]}">${d.user.badge}</span>`:""}
                                </span>
                                ${new Date(d.time*1e3).format("yyyy-mm-dd HH:MM")}
                                <a name="feed-reply">回复</a>
                            </div>
                        </header>
                        <div class="am-comment-bd">
                            <span class="feed-comment">
                                ${u}
                            </span>
                        </div>
                    </div>
                </li>
            `).appendTo(g("ul#feed")).find("a[name=feed-reply]").on("click",()=>{scrollToId("feed-content"),setTimeout(()=>g("textarea").trigger("focus").val(` || @${d.user.name} : ${e.reply_with_md?d.content:g(u).text()}`).trigger("input"),50)})})}}),e.source==="shy"&&(A.feedPage++,g("#feed-more").children("a").text("点击查看更多..."))):o()};let i=g(".feed-selector");g('<li class="feed-selector" id="exlg-benben-selector" data-mode="all-exlg" exlg="exlg"><a style="cursor: pointer">全网动态</a></li>').appendTo(i.parent()).on("click",s=>{let l=g(s.currentTarget);i.removeClass("am-active"),l.addClass("am-active"),e.source==="o2"&&g("#feed-more").hide(),A.feedPage=1,A.feedMode="all-exlg",g("li.am-comment").remove(),A.loadFeed()})},null,"module");v.reg("benben-quickpost","CtrlEnter 发送犇犇","@/",null,()=>g("textarea").whenKey("CtrlEnter",()=>g("#feed-submit").trigger("click")),null,"module");v.reg_v2({name:"blog",info:"博客 ex",path:["@/blogAdmin/article/edit/.*","@/blogAdmin/article/new"],cate:"module"},null,e=>{e.onload({name:"format",info:"按照洛谷题解标准格式化"},{on:{ty:"boolean",dft:!0,info:["Enable format","显示格式化按钮"],migration:"format"},cloud:{ty:"boolean",dft:!0,info:["Enable cloud format","启用云格式化"],migration:!0}},({msto:t})=>{let r=g(".mp-editor-menu");r.append('<li data-v-6d5597b1="" class="mp-divider"><span data-v-6d5597b1="">|</span></li>'),r.append(g('<li data-v-6d5597b1=""></li>').append(g('<a data-v-6d5597b1="" title="自动排版" unslectable="on" class="exlg-format-btn"></a>').append('<i data-v-6d5597b1="" unslectable="on" class="fa fa-check"></i>'))),g(".exlg-format-btn").on("click",async()=>{let n=A.articleEditor.content;t.cloud?n=await K("https://exlgcs.jin-dan.site/autocorrect",{content:n}).data.data:(n=n.replaceAll(/([\u4e00-\u9fa5])([a-z])/igu,"$1 $2"),n=n.replaceAll(/([a-z])([\u4e00-\u9fa5])/igu,"$1 $2"),n=n.replaceAll(/([\u4e00-\u9fa5])(\$)/igu,"$1 $2"),n=n.replaceAll(/(\$)([\u4e00-\u9fa5])/igu,"$1 $2")),A.articleEditor.content=n})}),e.onload({name:"hotkeys",info:"编辑快捷键"},{on:{ty:"boolean",dft:!0,info:["Enable hotkeys","快捷键"],migration:"hotkeys"}},()=>{g(A).whenKey({CtrlB:()=>g("a[title='粗体']")[0].click(),CtrlI:()=>g("a[title='斜体']")[0].click(),CtrlShiftX:()=>g("a[title='删除线']")[0].click()})})});var lr=".bb-rnkitm:nth-child(1)::marker{color:var(--lg-red);font-weight:900}.bb-rnkitm:nth-child(2)::marker{color:var(--lg-orange);font-weight:900}.bb-rnkitm:nth-child(3)::marker{color:var(--lg-yellow);font-weight:900}.bb-rnkitm>span{display:flex;flex-wrap:wrap}.bb-rnkitm>span>span{margin-left:auto}.bb-rnklst-span{font-size:1em;font-weight:400}.btn-disable{pointer-events:none;color:#97979a}";var cr='<h3>查找用户</h3><div class="am-input-group am-input-group-primary am-input-group-sm">	<input type="text" class="am-form-field" placeholder="例:kkksc03,可跳转站长主页" name="username" id="search-user-input"></div><p>	<button class="am-btn am-btn-danger am-btn-sm" id="search-user">跳转</button></p>';var dr="input[exlg-badge-register],select[exlg-badge-register]{outline:none;display:inline-block;width:auto;padding:.5em;line-height:1.2;color:#555;vertical-align:middle;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:0;-webkit-appearance:none;-webkit-transition:border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}input[exlg-badge-register][disabled]{cursor:not-allowed;background-color:#eee}input[exlg-badge-register]:focus{border:1px solid #3bb4f2}body{margin:0}.exlg-dialog-footer{bottom:0;position:absolute;right:0;padding:10px 6px}.exlg-dialog-container.container-show:hover>.exlg-dialog-btn{background:rgba(255,255,255,.8)}.exlg-dialog-btn{margin:0 4px;float:right;color:#666;min-width:75px;cursor:pointer;background:rgba(255,255,255,.2);padding:7px 10px;border:1px solid #ddd;border-radius:3px}.exlg-dialog-container.container-hide{opacity:0}.exlg-dialog-container.container-show:hover{background:rgba(250,250,250,.8);box-shadow:0 2px 8px #0006;opacity:1}.exlg-dialog-container{filter:blur(0);position:relative;opacity:.75;background:rgba(204,204,204,.2);width:500px;min-height:300px;border-radius:5px;margin:0 auto;box-shadow:0 2px 8px #00000040;font-size:16px;line-height:1.5;backdrop-filter:blur(20px)}.exlg-dialog-wrapper{position:fixed;left:0;top:0;background:rgba(0,0,0,0);width:100%;height:100%;align-items:center;display:table-cell;z-index:150}.exlg-dialog-header{height:auto;border-bottom:1px solid #eee;padding:11px 20px}.exlg-dialog-body{text-align:center;margin-bottom:50px;padding:20px 30px 10px;line-height:2}#header-right{position:absolute;width:30px;height:30px;border-radius:5px;background:rgba(0,0,0,0);color:red;right:10px;top:10px;text-align:center}";var gr='<div class="exlg-dialog-wrapper" id="exlg-wrapper" style="display: none;"><div class="exlg-dialog-container container-hide" id="exlg-container"><div class="exlg-dialog-header"><strong id="exlg-dialog-title"></strong><div id="header-right" style="opacity: 0.5;"><svg class="icon" style="vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5446"><path d="M512 128c-211.7 0-384 172.3-384 384s172.3 384 384 384 384-172.3 384-384-172.3-384-384-384z m0 717.4c-183.8 0-333.4-149.6-333.4-333.4S328.2 178.6 512 178.6 845.4 328.2 845.4 512 695.8 845.4 512 845.4zM651.2 372.8c-9.9-9.9-25.9-9.9-35.8 0L512 476.2 408.6 372.8c-9.9-9.9-25.9-9.9-35.8 0-9.9 9.9-9.9 25.9 0 35.8L476.2 512 372.8 615.4c-9.9 9.9-9.9 25.9 0 35.8 4.9 4.9 11.4 7.4 17.9 7.4s13-2.5 17.9-7.4L512 547.8l103.4 103.4c4.9 4.9 11.4 7.4 17.9 7.4s13-2.5 17.9-7.4c9.9-9.9 9.9-25.9 0-35.8L547.8 512l103.4-103.4c9.9-9.9 9.9-25.9 0-35.8z" p-id="5447"></path></svg></div></div><div class="exlg-dialog-body"><div id="exlg-dialog-content"></div><div class="exlg-dialog-footer"><button class="exlg-dialog-btn" btn-rnk="0"></button><button class="exlg-dialog-btn" btn-rnk="1"></button></div></div></div></div>';var pr=e=>new Promise(t=>{setTimeout(t,e)}),q={},wo=oe.reg("exlg-dialog-board","exlg 公告板",{animation_speed:{ty:"enum",dft:".4s",vals:["0s",".1s",".25s",".4s"],info:["Speed of Board Animation","启动消失动画速度"]},confirm_position:{ty:"enum",dft:"left",vals:["left","right"],info:["Position of Confirm Button","确定按钮相对位置"]}},({msto:e})=>{let t=!1,r=g(gr).appendTo(g(document.body)).on("mouseup",()=>{t=!1}),[n,o,i,s]=["#exlg-container","#exlg-dialog-title","#exlg-dialog-content","#header-right"].map(a=>r.find(a)),[l,d]=(e.confirm_position==="right"?[0,1]:[1,0]).map(a=>r.find(`button[btn-rnk="${a}"]`));l.text("确定"),d.text("取消"),l.on("click",async()=>{let a=q.action.onconfirm?.(q);((a instanceof Promise?await a:a)??!0)&&q.hide_dialog(),q.resolve_result("confirmed")}),d.on("click",async()=>{let a=q.action.oncancel?.(q);((a instanceof Promise?await a:a)??!0)&&q.hide_dialog(),q.resolve_result("canceled")}),s.on("click",async()=>{let a=q.action.onclose?.(q);((a instanceof Promise?await a:a)??!0)&&q.hide_dialog(),q.resolve_result("closed")}),n.on("click",a=>a.stopPropagation()),n.on("mousedown",a=>(t=!0)&&a.stopPropagation()),e.animation_speed!=="0s"&&n.css("transition",e.animation_speed);let u={"0s":0,".1s":100,".25s":250,".4s":400},c={0:"0s",100:".1s",250:".25s",400:".4s"};q=new Proxy({dom:{$wrap:r,$cont:n,$head:o,$main:i,$close:s},jsdom:{wrapper:r[0],container:n[0],header:o[0].parentNode,title:o[0],content:i[0],button:{confirm:l[0],cancel:d[0],close:s[0]}},jqdom:{wrapper:r[0],container:n[0],header:o[0].parentNode,title:o[0],content:i[0],button:{confirm:l[0],cancel:d[0],close:s[0]}},wait_time:u[e.animation_speed],async show_dialog(){this.jsdom.wrapper.style.display="flex",await pr(50),this.jsdom.container.classList.remove("container-hide"),this.jsdom.container.classList.add("container-show")},async hide_dialog(){this.jsdom.container.classList.add("container-hide"),this.jsdom.container.classList.remove("container-show"),await pr(this.wait_time),this.jsdom.wrapper.style.display="none"},resolve_result(a){this._resolve?.(a)},then(){return new Promise(a=>{this._resolve=a})}},{get(a,p,f){return p==="width"?a.jsdom.container.style.width:/^min(-h|_h|H)eight$/.test(p)?a.jsdom.container.style.minHeight:p==="title"?a.jsdom.title.innerHTML:p==="content"?a.jsdom.content.innerHTML:Reflect.get(a,p)},set(a,p,f,x){return p==="width"?Reflect.set(a.jsdom.container.style,"width",f):/^min(-h|_h|H)eight$/.test(p)?Reflect.set(a.jsdom.container.style,"minHeight",f):p==="wait_time"?typeof f=="number"&&Object.keys(c).includes(f)?(e.animation_speed=c[f],Reflect.set(a,"wait_time",f)):!1:p==="title"?Reflect.set(a.jsdom.title,"innerHTML",f):p==="content"?Reflect.set(a.jsdom.content,"innerHTML",f):Reflect.set(a,p,f)}})},(e,t="",r="exlg 提醒您",n={},{width:o,min_height:i}={})=>(q.action=typeof n=="function"?{onconfirm:n}:n,q.jsdom.container.style.width=o??"500px",q.jsdom.container.style.minHeight=i??"300px",q.jsdom.title.innerHTML=r,q.jsdom.content.innerHTML=t,q.show_dialog(),q.action.onopen?.(q),q),dr),N=wo;v.reg_board("search-user","用户查找",null,({$board:e})=>{e.html(cr);let t=g("#search-user"),r=()=>{t.prop("disabled",!0),g.get(`/api/user/search?keyword=${g("[name=username]").val().trim()}`,n=>{n.users[0]?location.href=`/user/${n.users[0].uid}`:(t.prop("disabled",!1),N("无法找到指定用户"))})};t.on("click",r),g("#search-user-input").whenKey("Enter",r)},null,"module");v.reg_board("benben-ranklist","犇犇龙王排行榜",{show:{ty:"boolean",dft:!0,info:["Show in default","是否默认展开"]}},({msto:e,$board:t})=>{t.html(`<h3 id="bb-rnklst-h2">犇犇排行榜 <span id="bb-rnklst-btn" class="bb-rnklst-span"> [<a>${e.show?"收起":"展开"}</a>]</span><span style="float: right;" class="bb-rnklst-span"> [<a id="refresh-bbrnk">刷新</a>]</span></h3><ol style="display: ${e.show?"block":"none"}" id="bb-rnklst"></ol>`);let r=t.find("#bb-rnklst"),n=t.find("#bb-rnklst-btn > a").on("click",()=>{e.show=!e.show,n.text(e.show?"收起":"展开"),r.toggle()}),o=t.find("#refresh-bbrnk"),i=!1;function s(d){i||(i=!0,r.html(d),i=!1)}function l(){o.addClass("btn-disable").text("刷新中"),ne({url:"https://bens.rotriw.com/ranklist?_contentOnly=1",onload(d){o.removeClass("btn-disable").text("刷新"),s(JSON.parse(d.response).map(([u,c,a])=>`<li class="bb-rnkitm">
                    <span>
                        <a href="https://bens.rotriw.com/user/${a}">${c}</a>
                        <span>${u} 条</span>
                    </span>
                </li>`).join(""))}})}t.find("#refresh-bbrnk").on("click",l),l()},lr,"module");v.reg("captcha","验证码自动填充",["@/discuss/.+","@/image"],null,()=>{let e=g("img[data-v-3e1b4641],#verify_img"),t=async()=>{let r=document.createElement("canvas");r.width=e[0].width,r.height=e[0].height,r.getContext("2d").drawImage(e[0],0,0);let n=g("input[placeholder$='验证码']")[0];n.value=await K("https://luogu-captcha-bypass.piterator.com/predict/",r.toDataURL("image/jpeg")).responseText,n.dispatchEvent(new Event("input"))};e.length?(e.click(),e[0].onload=t):g(document).on("focus","input[placeholder$='验证码']",()=>{e=g("#--swal-image-hosting-upload-captcha"),e[0].onload=t})},null,"module");var ur=".exlg-copy{position:relative;display:inline-block;border:1px solid #3498db;border-radius:3px;background-color:#3498db00;color:#3498db;font-family:-apple-system,BlinkMacSystemFont,San Francisco,Helvetica Neue,Noto Sans,Noto Sans CJK SC,Noto Sans CJK,Source Han Sans,PingFang SC,Segoe UI,Microsoft YaHei,sans-serif;flex:none;outline:0;cursor:pointer;font-weight:400;line-height:1.5;text-align:center;vertical-align:middle;background:0 0;font-size:12px;padding:0 5px;margin-left:1px}.exlg-copy.exlg-copy-right{float:right}.exlg-copy:hover{background-color:#3498db1a}div.exlg-copied{background-color:#3498dbe6!important;color:#fff!important}div.exlg-copied.exlg-copied-fail{background-color:#db3434e6!important;color:#fff!important}.copy-btn{font-size:.8em;padding:0 5px}.lfe-form-sz-middle{font-size:.875em;padding:.313em 1em}.exlg-code-title{margin:0;font-family:inherit;font-size:1.125em;color:inherit}";var fr=`h3.exlg-code-title.exlg-beautified-cbex{margin-bottom:0!important;padding:.4rem .6rem .4rem 2rem;background-color:#eee;font-weight:700;border-top-left-radius:5px;border-top-right-radius:5px;padding-top:8px;margin-top:10px;box-shadow:0 2px 2px #00000024,0 1px 5px #0000001f,0 3px 1px -2px #0003}h3.exlg-code-title.exlg-beautified-cbex+pre[exlg-copy-code-block]{margin-top:0;border-bottom-left-radius:5px;border-bottom-right-radius:5px;border:1px solid #eee;box-shadow:0 2px 2px #00000024,0 1px 5px #0000001f,0 3px 1px -2px #0003;background-color:#fafafa}h3.exlg-code-title.exlg-beautified-cbex+pre>code.hljs[class]{font-family:Fira Code,Consolas,monospace;background:white;padding:.5em}h3.exlg-code-title.exlg-beautified-cbex>div.exlg-copy{color:#6c757d;border:0px solid #3498db;margin-left:1px;font-size:0;transition:background-color .3s;background-image:url("data:image/svg+xml;utf8,<svg class='icon' style='width: 1em;height: 1em;vertical-align: middle;fill: rgb(108, 117, 125);overflow: hidden;' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='2669'><path d='M661.333333 234.666667A64 64 0 0 1 725.333333 298.666667v597.333333a64 64 0 0 1-64 64h-469.333333A64 64 0 0 1 128 896V298.666667a64 64 0 0 1 64-64z m-21.333333 85.333333H213.333333v554.666667h426.666667v-554.666667z m191.829333-256a64 64 0 0 1 63.744 57.856l0.256 6.144v575.701333a42.666667 42.666667 0 0 1-85.034666 4.992l-0.298667-4.992V149.333333H384a42.666667 42.666667 0 0 1-42.368-37.674666L341.333333 106.666667a42.666667 42.666667 0 0 1 37.674667-42.368L384 64h447.829333z' p-id='2670'></path></svg>");height:20px;width:20px;padding:0}h3.exlg-code-title.exlg-beautified-cbex>div.exlg-copy:hover{background-color:#6c757d1a}h3.exlg-code-title.exlg-beautified-cbex>div.exlg-copied[class]{background-color:#6c757d00!important;background-image:url("data:image/svg+xml;utf8,<svg class='icon' style='width: 1em;height: 1em;vertical-align: middle;fill: rgb(70, 160, 240);overflow: hidden;' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='2669'><path d='M661.333333 234.666667A64 64 0 0 1 725.333333 298.666667v597.333333a64 64 0 0 1-64 64h-469.333333A64 64 0 0 1 128 896V298.666667a64 64 0 0 1 64-64z m-21.333333 85.333333H213.333333v554.666667h426.666667v-554.666667z m191.829333-256a64 64 0 0 1 63.744 57.856l0.256 6.144v575.701333a42.666667 42.666667 0 0 1-85.034666 4.992l-0.298667-4.992V149.333333H384a42.666667 42.666667 0 0 1-42.368-37.674666L341.333333 106.666667a42.666667 42.666667 0 0 1 37.674667-42.368L384 64h447.829333z' p-id='2670'></path></svg>")}h3.exlg-code-title.exlg-beautified-cbex>div.exlg-copied[class]:hover{background-color:#6c757d4d!important}h3.exlg-code-title.exlg-beautified-cbex>div.exlg-copied.exlg-copied-fail[class]{background-color:#6c757d00!important;background-image:url("data:image/svg+xml;utf8,<svg class='icon' style='width: 1em;height: 1em;vertical-align: middle;fill: rgb(219, 52, 52);overflow: hidden;' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='2669'><path d='M661.333333 234.666667A64 64 0 0 1 725.333333 298.666667v597.333333a64 64 0 0 1-64 64h-469.333333A64 64 0 0 1 128 896V298.666667a64 64 0 0 1 64-64z m-21.333333 85.333333H213.333333v554.666667h426.666667v-554.666667z m191.829333-256a64 64 0 0 1 63.744 57.856l0.256 6.144v575.701333a42.666667 42.666667 0 0 1-85.034666 4.992l-0.298667-4.992V149.333333H384a42.666667 42.666667 0 0 1-42.368-37.674666L341.333333 106.666667a42.666667 42.666667 0 0 1 37.674667-42.368L384 64h447.829333z' p-id='2670'></path></svg>")}h3.exlg-code-title.exlg-beautified-cbex>div.exlg-copied.exlg-copied-fail[class]:hover{background-color:#6c757d4d!important}`;v.reg_hook_new("code-block-ex","代码块优化","@/.*",{copy_code_position:{ty:"enum",vals:["left","right"],dft:"left",info:["Copy Button Position","复制按钮对齐方式"]},beautify_code_block:{ty:"boolean",dft:!0,info:["Beautify Code Block","代码块美化"]},code_block_title:{ty:"string",dft:"源代码 - ${lang}",info:["Custom Code Title(with Language)","自定义代码块标题 - 限定语言"]},code_block_title_nolang:{ty:"string",dft:"源代码",info:["Custom Code Title(without Language)","自定义代码块标题 - 默认"]},copy_code_font:{ty:"string",dft:"'Fira Code', 'Fira Mono', Consolas",info:["Code Block Font","代码块字体"],strict:!0},cb_background_color:{ty:"string",dft:"white",info:["Code Block Background Color","代码块背景色 (配合其他美化插件)"],strict:!0},max_show_lines:{ty:"number",dft:-1,min:-1,max:100,info:["Max Lines On Show","代码块最大显示行数"],strict:!0}},({msto:e,args:t})=>{let r=/\/record\/.*/.test(location.href),n={c:"C",cpp:"C++",pascal:"Pascal",python:"Python",java:"Java",javascript:"JavaScript",php:"PHP",latex:"LaTeX"},o=i=>{let s="undefined";return r?g(g(".value.lfe-caption")[0]).text():(i.attr("data-rendered-lang")?s=i.attr("data-rendered-lang"):i.attr("class")&&i.attr("class").split(" ").forEach(l=>{l.startsWith("language-")&&(s=l.slice(9))}),n[s])};t.attr("exlg-copy-code-block",""),t.each((i,s,l=g(s))=>{if(s.parentNode.className==="mp-preview-content"||s.parentNode.parentNode.className==="mp-preview-area")return;let d=r?l.children(".copy-btn"):g('<div class="exlg-copy">复制</div>').on("click",()=>{if(d.text()==="复制"){try{GM_setClipboard(l.text(),"text/plain")}catch(f){d.text("复制失败").toggleClass("exlg-copied").toggleClass("exlg-copied-fail"),setTimeout(()=>d.text("复制").toggleClass("exlg-copied").toggleClass("exlg-copied-fail"),800),M("复制到剪贴板失败,错误信息: ",f);return}d.text("复制成功").toggleClass("exlg-copied"),setTimeout(()=>d.text("复制").toggleClass("exlg-copied"),800)}}),u=l.children("code");e.copy_code_font&&u.css("font-family",e.copy_code_font||""),u.hasClass("hljs")||u.addClass("hljs").css("background",e.cb_background_color),d.addClass(`exlg-copy-${e.copy_code_position}`);let c=o(u),a=c?e.code_block_title.replace("${lang}",c):e.code_block_title_nolang,p=r?g(".lfe-h3").text(a):g(`<h3 class="exlg-code-title" style="/*width: 100%;*/">${a}</h3>`);e.beautify_code_block&&p.addClass("exlg-beautified-cbex"),r||l.before(p.append(d))})},e=>{let t=g(e.target).find("pre:has(> code:not(.cm-s-default)):not([exlg-copy-code-block])");return{result:t.length,args:t}},()=>g("pre:has(> code:not(.cm-s-default)):not([exlg-copy-code-block])"),ur+fr,"module");var mr='<svg data-v-a97ae32a="" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="code" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" class="svg-inline--fa fa-code fa-w-20"><path data-v-a97ae32a="" fill="currentColor" d="M278.9 511.5l-61-17.7c-6.4-1.8-10-8.5-8.2-14.9L346.2 8.7c1.8-6.4 8.5-10 14.9-8.2l61 17.7c6.4 1.8 10 8.5 8.2 14.9L293.8 503.3c-1.9 6.4-8.5 10.1-14.9 8.2zm-114-112.2l43.5-46.4c4.6-4.9 4.3-12.7-.8-17.2L117 256l90.6-79.7c5.1-4.5 5.5-12.3.8-17.2l-43.5-46.4c-4.5-4.8-12.1-5.1-17-.5L3.8 247.2c-5.1 4.7-5.1 12.8 0 17.5l144.1 135.1c4.9 4.6 12.5 4.4 17-.5zm327.2.6l144.1-135.1c5.1-4.7 5.1-12.8 0-17.5L492.1 112.1c-4.8-4.5-12.4-4.3-17 .5L431.6 159c-4.6 4.9-4.3 12.7.8 17.2L523 256l-90.6 79.7c-5.1 4.5-5.5 12.3-.8 17.2l43.5 46.4c4.5 4.9 12.1 5.1 17 .6z" class=""></path></svg>';var hr='<svg aria-hidden="true" height="12" viewBox="0 0 16 16" version="1.1" width="12" data-view-component="true" class="octicon octicon-mark-github"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path></svg>';var br='<svg class="icon" style="vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" width="24" height="24" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5446"><path d="M512 128c-211.7 0-384 172.3-384 384s172.3 384 384 384 384-172.3 384-384-172.3-384-384-384z m0 717.4c-183.8 0-333.4-149.6-333.4-333.4S328.2 178.6 512 178.6 845.4 328.2 845.4 512 695.8 845.4 512 845.4zM651.2 372.8c-9.9-9.9-25.9-9.9-35.8 0L512 476.2 408.6 372.8c-9.9-9.9-25.9-9.9-35.8 0-9.9 9.9-9.9 25.9 0 35.8L476.2 512 372.8 615.4c-9.9 9.9-9.9 25.9 0 35.8 4.9 4.9 11.4 7.4 17.9 7.4s13-2.5 17.9-7.4L512 547.8l103.4 103.4c4.9 4.9 11.4 7.4 17.9 7.4s13-2.5 17.9-7.4c9.9-9.9 9.9-25.9 0-35.8L547.8 512l103.4-103.4c9.9-9.9 9.9-25.9 0-35.8z" p-id="5447"></path></svg>';var xr='<div class="exlg-unselectable exlg-badge-page" style="font-family: Consolas;white-space: nowrap;text-align: left;"><div class="exlg-regbadge-board" mode="main" wd="800px" mh="400px"><!-- 左侧的一列,包含用户信息和按钮 --><div class="exlg-regbadge-box"><span style="margin: 5px;"><span class="exlg-regbadge-fronttitle">uid</span><input key="uid" type="text" style="padding: .1em;" placeholder="填写用户名或uid" value="" disabled title="暂不支持为别人注册 badge" /></span><br /><span style="margin: 5px;"><span class="exlg-regbadge-fronttitle">激活码</span><input key="active" type="text" style="padding: .1em;" /></span><br /><span style="margin: 5px;" style="display: none"><span class="exlg-regbadge-fronttitle">伪tag</span><input key="tagText" type="text" style="padding: .1em;" /></span><br /><span style="margin: 5px;"><span class="exlg-regbadge-fronttitle">内容</span><input key="badgeText" type="text" style="padding: .1em;" /></span><br /><span style="margin: 5px;"><span style="height: 1.5em;float: left;padding: .1em;width: 5em;"> </span><span fid="buttons" style="float: left;margin-left: 5px;"><button act="toPreset" exlg>预设...</button><br /></span></span></div><div class="exlg-regbadge-box" id="regbadge-settings"></div></div><div class="exlg-regbadge-board" mode="showError"><div style="margin-bottom: 1.5em;"><div><strong style="color: red;">错误信息:</strong></div><div class="error-message"></div></div><small>点击确定以返回。</small></div><div class="exlg-regbadge-board" mode="getPreset" wd="600px">想要与别人分享你的预设方案吗?使用下面的输入框吧!<br /><input exlg-regbadge act="preset-json" type="text" style="padding: .1em;width: 100%;height: 2em" /><br /><span style="color: red;font-weight: bold">JSON 不合法!</span><br />另外,这里是些我们早期制作好的!<div class="exlg-regbadge-preset-list"></div></div><div class="exlg-regbadge-board" mode="setbgColor" wd="600px">模式<select id="regbadge-setbgColor-type-select" type="select" style="padding: .1em;padding-right: 20px;width: auto;display: inline;"><option value="text">配置文本</option><option value="lgdefault">跟随名字颜色</option><option value="single">单色</option><option value="xncolorpicker">xncolorpicker</option><option value="image">图片填充</option></select><br /><div class="exlg-regbadge-bg-box" mode="text" style="display: none;"><input /></div><div class="exlg-regbadge-bg-box" mode="lgdefault" style="display: none;"><small>提示:exlg 会把 bg 中的 <code style="font-family: Fira Code, Consolas, monospace">${luogu-default}</code>替换为你当前的名字颜色,<br />并且随着名字颜色变化而变化。<br />试试将 bg 中的颜色换成上述占位符?</small></div><div class="exlg-regbadge-bg-box" mode="single" style="display: none;"><!--这里应该有一个拾色器--><span exlg="bg-single-pickr"></span></div><div class="exlg-regbadge-bg-box" mode="xncolorpicker" style="display: none;"><!--这里应该有一个拾色器--><span exlg="bg-xncolorpicker"></span></div><div class="exlg-regbadge-bg-box" mode="image" style="display: none;">这里先摆了。</div></div></div><div class="exlg-unselectable exlg-badge-page" style="text-align: left;"><span class="exlg-regbadge-fronttitle">预览</span><span class="feed-username"><span class="user-name"><a><span style="font-weight: bold; color: LG_USER_COLOR">LG_USER_NAME</span></a><span data-v-499508d0="" class="ccf-badge" data-v-f9624136=""><a data-v-303bbf52="" href="/discuss/show/142324" target="_blank" colorscheme="none" class="color-none"><svg data-v-303bbf52="" aria-hidden="true" focusable="false" data-prefix="fad" data-icon="badge-check" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="svg-inline--fa fa-badge-check fa-w-16"><g data-v-303bbf52="" class="fa-group"><path data-v-303bbf52="" fill="currentColor" d="M512 256a88 88 0 0 0-57.1-82.4A88 88 0 0 0 338.4 57.1a88 88 0 0 0-164.8 0A88 88 0 0 0 57.1 173.6a88 88 0 0 0 0 164.8 88 88 0 0 0 116.5 116.5 88 88 0 0 0 164.8 0 88 88 0 0 0 116.5-116.5A88 88 0 0 0 512 256zm-144.8-44.25l-131 130a11 11 0 0 1-15.55-.06l-75.72-76.33a11 11 0 0 1 .06-15.56L171 224a11 11 0 0 1 15.56.06l42.15 42.49 97.2-96.42a11 11 0 0 1 15.55.06l25.82 26a11 11 0 0 1-.08 15.56z" class="fa-secondary"></path><path data-v-303bbf52="" fill="currentColor" d="M367.2 211.75l-131 130a11 11 0 0 1-15.55-.06l-75.72-76.33a11 11 0 0 1 .06-15.56L171 224a11 11 0 0 1 15.56.06l42.15 42.49 97.2-96.42a11 11 0 0 1 15.55.06l25.82 26a11 11 0 0 1-.06 15.56z" class="fa-primary"></path></g></svg></a></span></span><span tag-preview></span></span><span badge-preview></span></div><div class="exlg-unselectable exlg-badge-page" style="text-align: left;"></div><!--<select id="regbadge-type-select" type="select" style="padding: .1em;padding-right: 20px;width: auto;display: inline;"><option value="default">default</option></select>-->';var wr='#regbadge-preview .exlg-badge-preview{border:none}#regbadge-preview input{border:none;background:0 0;outline:0}textarea.exlg-regbadge-configinput,.exlg-regbadge-box input:not([disabled]),input[exlg-regbadge]{padding:.5em;line-height:1.2;color:#555;outline:0;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:0;-webkit-appearance:none;-webkit-transition:border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}textarea.exlg-regbadge-configinput:hover{background-color:#fefffe;border-color:#3bb4f2;outline:0;-webkit-box-shadow:inset 0 1px 1px rgb(0 0 0 / 8%),0 0 5px rgb(59 180 242 / 30%);box-shadow:inset 0 1px 1px #00000014,0 0 5px #3bb4f24d}input[exlg-regbadge]:hover{background-color:#fefffe;border-color:#3bb4f2}input[exlg-regbadge]:focus{background-color:#fefffe;border-color:#3bb4f2;outline:0;-webkit-box-shadow:inset 0 1px 1px rgb(0 0 0 / 8%),0 0 5px rgb(59 180 242 / 30%);box-shadow:inset 0 1px 1px #00000014,0 0 5px #3bb4f24d}.exlg-regbadge-box input:not([disabled]):hover{background-color:#fefffe;border-color:#3bb4f2}.exlg-regbadge-box input:not([disabled]):focus{background-color:#fefffe;border-color:#3bb4f2;outline:0;-webkit-box-shadow:inset 0 1px 1px rgb(0 0 0 / 8%),0 0 5px rgb(59 180 242 / 30%);box-shadow:inset 0 1px 1px #00000014,0 0 5px #3bb4f24d}#regbadge-preview-id{font-family:apple-system,BlinkMacSystemFont,San Francisco,Helvetica Neue,Noto Sans CJK SC,Noto Sans CJK,Source Han Sans,PingFang SC,Microsoft YaHei,sans-serif}#preview-lg-badge>.lg3-badge{text-align:center;box-sizing:border-box;display:inline-block;min-width:10px;padding:.25em .625em;font-size:1.2rem;font-weight:700;color:#fff;line-height:1;vertical-align:baseline;white-space:nowrap;border-radius:2px}#preview-lg-badge>.lg4-badge{color:#fff;display:inline-block;padding:0 8px;box-sizing:border-box;font-weight:400;line-height:1.5;border-radius:2px;margin-left:.2em;font-size:.875em}.exlg-badge-page .exlg-regbadge-box{width:50%;float:left;display:inline}.exlg-badge-page .exlg-regbadge-fronttitle{height:1.5em;float:left;padding:.1em;width:5em}.exlg-badge-page button[exlg]{color:#666;min-width:75px;cursor:pointer;background:rgba(255,255,255,.2);padding:3px 10px;border:1px solid #ddd;border-radius:3px}select[exlg-badge-register]{background:url("data:image/svg+xml;charset=UTF-8, %3Csvg%20style=%22fill:%20rgb(108,%20117,%20125);%22%20width=%2210%22%20height=%2210%22%20viewBox=%220%200%209%209%22%20fill=%22rgb(108,%20117,%20125)%22%20xmlns=%22http://www.w3.org/2000/svg%22%20class=%22link-icon--right%22%3E%3Cpath%20fill-rule=%22evenodd%22%20clip-rule=%22evenodd%22%20d=%22M7.50588%203.40623C7.40825%203.3086%207.24996%203.3086%207.15232%203.40623L4.41244%206.14612L1.67255%203.40623C1.57491%203.3086%201.41662%203.3086%201.31899%203.40623C1.22136%203.50386%201.22136%203.66215%201.31899%203.75978L4.11781%206.5586C4.28053%206.72132%204.54434%206.72132%204.70706%206.5586L7.50588%203.75978C7.60351%203.66215%207.60351%203.50386%207.50588%203.40623Z%22%20fill=%22rgb(108,%20117,%20125)%22%3E%3C/path%3E%3Cpath%20d=%22M7.15232%203.40623L7.50588%203.75978L7.50588%203.75978L7.15232%203.40623ZM7.50588%203.40623L7.15232%203.75978L7.15233%203.75978L7.50588%203.40623ZM4.41244%206.14612L4.05888%206.49967C4.15265%206.59344%204.27983%206.64612%204.41244%206.64612C4.54504%206.64612%204.67222%206.59344%204.76599%206.49967L4.41244%206.14612ZM1.67255%203.40623L2.0261%203.05268L2.0261%203.05268L1.67255%203.40623ZM1.31899%203.40623L0.965439%203.05268L0.965439%203.05268L1.31899%203.40623ZM1.31899%203.75978L1.67255%203.40623V3.40623L1.31899%203.75978ZM4.11781%206.5586L3.76425%206.91215L4.11781%206.5586ZM4.70706%206.5586L4.35351%206.20505L4.70706%206.5586ZM7.50588%203.75978L7.15233%203.40623L7.15232%203.40623L7.50588%203.75978ZM7.50588%203.75978C7.40825%203.85742%207.24996%203.85742%207.15232%203.75978L7.85943%203.05268C7.56654%202.75978%207.09166%202.75978%206.79877%203.05268L7.50588%203.75978ZM4.76599%206.49967L7.50588%203.75978L6.79877%203.05268L4.05888%205.79257L4.76599%206.49967ZM1.31899%203.75978L4.05888%206.49967L4.76599%205.79257L2.0261%203.05268L1.31899%203.75978ZM1.67254%203.75979C1.57491%203.85742%201.41662%203.85742%201.31899%203.75979L2.0261%203.05268C1.73321%202.75978%201.25833%202.75978%200.965439%203.05268L1.67254%203.75979ZM1.67255%203.40623C1.77018%203.50386%201.77018%203.66215%201.67255%203.75978L0.965439%203.05268C0.672546%203.34557%200.672546%203.82044%200.965439%204.11334L1.67255%203.40623ZM4.47136%206.20505L1.67255%203.40623L0.965439%204.11334L3.76425%206.91215L4.47136%206.20505ZM4.35351%206.20505C4.38605%206.1725%204.43882%206.1725%204.47136%206.20505L3.76425%206.91215C4.12223%207.27013%204.70264%207.27013%205.06062%206.91215L4.35351%206.20505ZM7.15232%203.40623L4.35351%206.20505L5.06062%206.91215L7.85943%204.11334L7.15232%203.40623ZM7.15233%203.75978C7.05469%203.66215%207.05469%203.50386%207.15233%203.40623L7.85943%204.11334C8.15233%203.82045%208.15233%203.34557%207.85943%203.05268L7.15233%203.75978Z%22%20fill=%22rgb(108,%20117,%20125)%22%3E%3C/path%3E%3C/svg%3E") right no-repeat;background-size:auto}.exlg-badge-page div.exlg-regbadge-preset-list{border:1px solid #ccc;height:200px;overflow:overlay}.exlg-badge-page div.exlg-regbadge-preset-list>span{width:50%;display:inline-block}.exlg-badge-page div.exlg-regbadge-preset-list>span>span{text-align:center;margin:0 10px}.exlg-badge-page div.exlg-regbadge-preset-title{font-weight:700;margin:0 15px;font-size:large}.exlg-badge-page div.pickr{display:inline-block}';var Ce={"exlg-presets":{title:"exlg 预设",presetList:[{name:"exlg 经典",id:"exlg-default",config:"{}"},{name:"洛谷经典",id:"luogu-default",config:'{"bg":"${luogu-default}"}'},{name:"西红柿炒鸡蛋",id:"flandre-scarlet",config:'{"bg":"linear-gradient(90.0deg,rgb(254, 76, 97) 21.5%,rgba(255,213,79,1) 100.0%)"}'},{name:"⑨baka 联名",id:"cirno",config:"{}"},{name:"复古控制台",id:"console",config:"{}"},{name:"迈恩克拉夫特",id:"minecraft",config:"{}"},{name:"妈妈我紫了",id:"be-purple",config:"{}"},{name:"铃酱同款",id:"green-hook",config:`{"bg":"url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0 PSIxNiIgdmlld0JveD0iMCAwIDE2IDE2IiBmaWxsPSIjNWViOTVlIiBzdHlsZT0ibWFyZ2luLWJv dHRvbTogLTNweDsiPjxwYXRoIGQ9Ik0xNiA4QzE2IDYuODQzNzUgMTUuMjUgNS44NDM3NSAxNC4x ODc1IDUuNDM3NUMxNC42NTYyIDQuNDM3NSAxNC40Njg4IDMuMTg3NSAxMy42NTYyIDIuMzQzNzVD MTIuODEyNSAxLjUzMTI1IDExLjU2MjUgMS4zNDM3NSAxMC41NjI1IDEuODEyNUMxMC4xNTYyIDAu NzUgOS4xNTYyNSAwIDggMEM2LjgxMjUgMCA1LjgxMjUgMC43NSA1LjQwNjI1IDEuODEyNUM0LjQw NjI1IDEuMzQzNzUgMy4xNTYyNSAxLjUzMTI1IDIuMzQzNzUgMi4zNDM3NUMxLjUgMy4xODc1IDEu MzEyNSA0LjQzNzUgMS43ODEyNSA1LjQzNzVDMC43MTg3NSA1Ljg0Mzc1IDAgNi44NDM3NSAwIDhD MCA5LjE4NzUgMC43MTg3NSAxMC4xODc1IDEuNzgxMjUgMTAuNTkzOEMxLjMxMjUgMTEuNTkzOCAx LjUgMTIuODQzOCAyLjM0Mzc1IDEzLjY1NjJDMy4xNTYyNSAxNC41IDQuNDA2MjUgMTQuNjg3NSA1 LjQwNjI1IDE0LjIxODhDNS44MTI1IDE1LjI4MTIgNi44MTI1IDE2IDggMTZDOS4xNTYyNSAxNiAx MC4xNTYyIDE1LjI4MTIgMTAuNTYyNSAxNC4yMTg4QzExLjU5MzggMTQuNjg3NSAxMi44MTI1IDE0 LjUgMTMuNjU2MiAxMy42NTYyQzE0LjQ2ODggMTIuODQzOCAxNC42NTYyIDExLjU5MzggMTQuMTg3 NSAxMC41OTM4QzE1LjI1IDEwLjE4NzUgMTYgOS4xODc1IDE2IDhaTTExLjQ2ODggNi42MjVMNy4z NzUgMTAuNjg3NUM3LjIxODc1IDEwLjg0MzggNyAxMC44MTI1IDYuODc1IDEwLjY4NzVMNC41IDgu MzEyNUM0LjM3NSA4LjE4NzUgNC4zNzUgNy45Njg3NSA0LjUgNy44MTI1TDUuMzEyNSA3QzUuNDY4 NzUgNi44NzUgNS42ODc1IDYuODc1IDUuODEyNSA3LjAzMTI1TDcuMTI1IDguMzQzNzVMMTAuMTU2 MiA1LjM0Mzc1QzEwLjMxMjUgNS4xODc1IDEwLjUzMTIgNS4xODc1IDEwLjY1NjIgNS4zNDM3NUwx MS40Njg4IDYuMTU2MjVDMTEuNTkzOCA2LjI4MTI1IDExLjU5MzggNi41IDExLjQ2ODggNi42MjVa Ij48L3BhdGg+PC9zdmc+') 0px center / 16px no-repeat","fg":"rgba(0,0,0,0)"}`},{name:"AKIOI",id:"ak-ioi",config:"{}"},{name:"三 哼 经",id:"tadokoro-koji",config:"{}"}]}};var yr=".exlg-badge{border-radius:50px;padding:2px 10px;transition:all .15s;display:inline;min-width:10px;font-size:1em;font-weight:700;line-height:1;vertical-align:baseline;white-space:nowrap;cursor:pointer;margin-left:2px;margin-right:2px}";var ue={100250:"风神少女",196899:"可爱",89092:"",224978:"o2"},Mo={"lg-fg-purple":"#8e44ad","lg-fg-red":"#e74c3c","lg-fg-orange":"#e67e22","lg-fg-green":"#5eb95e","lg-fg-bluelight":"#0e90d2","lg-fg-gray":"#bbb","lg-fg-brown":"#996600"},Ao=e=>{let t=e.className.slice(e.className.indexOf("lg-fg-"));return t?t.slice(0,t.indexOf(" ")):e.childNodes.length?e.childNodes[0].style.color:null},Fe=(e,t,r,{bg:n,fg:o,text:i,ft:s,fw:l,bd:d,fs:u,pseudo:c},a=!0)=>{typeof e=="number"&&(e=String(e));let p=i?g(`<span class="exlg-badge" badge-uid="${e}" badge-type="${r}">${i}</span>`).css({background:(n||"mediumturquoise").replaceAll("${luogu-default}",t.includes("lg-fg-")?Mo[t]:t),color:o||"#fff","font-family":s||"","font-weight":l||"700","font-size":u||"",border:d||""}):g("");return i&&a&&p.on("click",()=>A.exlg.register_badge()),Object.keys(ue).includes(e)&&!c&&(c??=ue[e]),{pseudoTag:Object.keys(ue).includes(e)&&c?(r!=="luogu4"?g(`<span class="am-badge am-radius lg-bg-${t.slice(6)}">${c}</span>`):g(`<span class="lfe-caption" style="color: rgb(255, 255, 255); background: ${t};">${c}</span>`).css({display:"inline-block",padding:"0 8px","box-sizing":"border-box","font-weight":400,"line-height":1.5,"border-radius":"2px","margin-left":"0.2em"}))[0]:null,exlg:p[0]}},vr=[{pathTest:/^\/problem\/solution.*$/,domSelector:".card>.info-rows a[target='_blank']",type:{displayType:"luogu4",elementType:"solu",anceLevel:3}},{pathTest:/^\/problem\/.*$/,domSelector:".full-container .user a[target='_blank']",type:{displayType:"luogu4",elementType:"prob",anceLevel:0}},{pathTest:({pathname:e,hash:t})=>/^\/user\/[0-9]{0,}.*$/.test(e)&&t==="#activity",domSelector:".feed a[target='_blank']",type:{displayType:"luogu4",elementType:"user-feed",anceLevel:3}},{pathTest:({pathname:e,hash:t})=>/^\/user\/[0-9]{0,}.*$/.test(e)&&/^#following/.test(t),domSelector:".follow-container a[target='_blank']",type:{displayType:"luogu4",elementType:"user-follow",anceLevel:1}},{pathTest:()=>!0,domSelector:"a[target='_blank'][href^='/user/']",type:{displayType:"luogu3",elementType:"luogu3",anceLevel:0}}],kr=e=>!e.querySelectorAll("svg").length;v.reg_hook_new("sponsor-tag","badge 显示",["@/","@/paste","@/discuss/.*","@/problem/.*","@/ranking.*","@/user/\\d*.*"],{cache:{ty:"string",dft:"3600",info:["Cache time","缓存时间(秒)"]},badges:{ty:"string",priv:!0}},((e,t,r)=>async({msto:n,args:{domList:o,type:{displayType:i,elementType:s,anceLevel:l}}})=>{g.isEmptyObject(t)&&n.badges&&Object.assign(t,JSON.parse(n.badges));let d=Array.from(o).map(p=>{let f=p.attributes.href.value.slice(6);return!e.has(f)&&!(f in t&&W()-t[f].ts<=n.cache)?(e.add(f),f):null});d.length&&r.push((async()=>{Object.assign(t,Object.fromEntries(Object.entries(await K("https://exlg.piterator.com/badge/mget",{uid:C.uid,token:I["^token"].token,data:d}).data.data).map(([p,f])=>[p,Object.assign(f,{ts:W()})])))})()),await Promise.all(r),n.badges=JSON.stringify(t);let u=(p,f)=>{if(p==="luogu3"||!Object.keys(f).includes("lg4"))return f;let x=Object.clone(f);return delete x.lg4,Object.assign(Object.clone(f.lg4))},c=(p,f)=>{if(!f)return;let x=p.previousElementSibling;if(x){let h=x.tagName.toLowerCase()==="img"?x:x.childNodes.filter(m=>m.tagName.toLowerCase()==="img")[0];if(h)return h.getAttribute("src").replace(/[^0-9]/ig,"")}return c(p.parentNode,f-1)},a=(p,f)=>p?f?a(p.parentNode,f-1):p:null;o.forEach(p=>{let f=g(p);if(!f||f.hasClass("exlg-badge-username"))return;f.addClass("exlg-badge-username");let x=f.attr("href").slice(6);if(s==="prob"){let{provider:S}=_feInstance.currentData.problem;S.name===p.innerText.trim()&&(x=S.uid)}i==="luogu4"&&x==="ript:void 0"&&(x=c(p,5));let h=t[x];if(!h||!h.text&&!(Object.keys(ue).includes(x)&&h.pseudo))return;let[m,b,w]=[p,p.nextElementSibling,!1];b&&(b.classList?[...b.classList]:b.className.split(" ")).includes("sb_amazeui")&&(m=b),b=m.nextElementSibling,b&&(b.classList?[...b.classList]:b.className.split(" ")).includes("am-badge")&&([m,w]=[b,!0]);let k=Fe(x,i==="luogu4"?p.childNodes[0].style.color:Ao(p),i,u(i,h)),L=a(m,3);if(L&&(L.classList?[...L.classList]:L.className.split(" ")).includes("card"))k.pseudoTag&&!w&&m.parentNode.appendChild(k.pseudoTag),m.parentNode.appendChild(k.exlg);else{let S=document.createElement("span");S.innerHTML="&nbsp;",L=a(m,l),L.after(k.exlg),l===0&&L.after(S),k.pseudoTag&&!w&&(L.after(k.pseudoTag),l===0&&L.after(S))}})})(new Set,{},[]),e=>{let t={args:null,result:!1};return vr.every(({pathTest:r,domSelector:n,type:o})=>(typeof r=="function"?r(location):r.test(location.pathname))?(t=(()=>{let i=Array.from(e.target.querySelectorAll(n)).filter(kr);return{args:{type:o,domList:i},result:Boolean(i?.length)}})(),0):1),t},()=>{let e={args:null,result:!1};return vr.every(({pathTest:t,domSelector:r,type:n})=>(typeof t=="function"?t(location):t.test(location.pathname))?(e=(()=>{let o=Array.from(document.querySelectorAll(r)).filter(kr);return{type:n,domList:o}})(),0):1),e},yr,"module");var Z={},G,y={},ct={Red:"rgb(254, 76, 97)",Orange:"rgb(243, 156, 17)",Green:"rgb(82, 196, 26)",Blue:"rgb(52, 152, 219)",Gray:"rgb(191, 191, 191)",Cheater:"rgb(173, 139, 0)",Purple:"rgb(157, 61, 207)"},Eo=oe.reg("register-badge","badge 注册",{isFirst:{ty:"boolean",dft:!0,priv:!0}},null,async({msto:e},t=null)=>{if(e.isFirst){if(confirm(`看起来您是第一次使用注册器,您是否要注册 badge?
出于各种原因,badge 只能对实名认证的用户注册,且 badge 注册以后不能更改用户。
如果选择“否”,您仍然可以在官网(exlg.cc)注册。`)){let s=prompt("请输入激活码"),l=prompt("请输入你要的 badge 文字(可以后续修改,包括样式)");M("开始注册"),await v.execute("token"),M("成功生成 token"),GM_xmlhttpRequest({url:"https://exlg.piterator.com/badge/set",method:"POST",headers:{"Content-Type":"application/json"},data:JSON.stringify({token:I["^token"].token,uid:C.uid,activation:s,data:{text:l}}),onload(d){let u=JSON.parse(d.responseText);u.status===200?(M("OK. 请手动刷新 badge 以查看效果,后续的修改可使用注册器。"),alert("OK. 请手动刷新 badge 以查看效果,后续的修改可使用注册器。")):(Q(u.error),alert("出现了一点问题。请在控制台中查看"))},onerror(d){Q(d),alert("出现了一点问题。请在控制台中查看")}})}e.isFirst=!1}A.getconf=()=>Z;let r={bg:{css:"background",name:"背景",default:"mediumturquoise"},fg:{css:"color",name:"字色",default:"white"},bd:{css:"border",name:"边框",default:""},ft:{css:"font-family",name:"字体",default:""},fs:{css:"font-size",name:"字号",default:""},fw:{css:"font-weight",name:"字粗",default:"700"}},n=Object.keys(r),o=s=>{try{(0,eval)(s)}catch(l){return Q("Fail to execute the action: ",l),!1}return!0};if(o(GM_getResourceText("colorpicker_temp_new"))||o(GM_getResourceText("colorpicker_old")))M("起码至少有一个能用了");else{M("废了废了"),N("这个狗屎页面不能用取色器,错误信息自己看控制台输出<br/>点击确定回到主页。","exlg 提醒您",()=>location.href=location.origin);return}if(o(GM_getResourceText("pickr_resource")))M("OK Well~");else{M("废了废了"),N("这个狗屎页面不能用取色器,错误信息自己看控制台输出<br/>点击确定回到主页。","exlg 提醒您",()=>location.href=location.origin);return}let i="exlg badge 注册器 ~ 奶奶的,还在测试,bug 巨他妈多";return N(xr.replaceAll("LG_USER_NAME",C.name).replaceAll("LG_USER_COLOR",ct[C.color]),i,{onopen:s=>{y.boards={},["main","showError","getPreset","setbgColor"].forEach(c=>y.boards[c]=s.jsdom.content.querySelector(`div.exlg-regbadge-board[mode="${c}"]`)),y.errorMessage=y.boards.showError.querySelector(".error-message"),s.jsdom.content.querySelector("input[key='uid']").value=C.uid,y.badgePreview=s.jsdom.content.querySelector("[badge-preview]"),y.pseudoPreview=s.jsdom.content.querySelector("[tag-preview]"),y.badgePresetList=s.jsdom.content.querySelector(".exlg-regbadge-preset-list"),y.pseudoInput=s.jsdom.content.querySelector("input[key='tagText']"),y.activeInput=s.jsdom.content.querySelector("input[key='active']"),y.badgeInput=s.jsdom.content.querySelector("input[key='badgeText']"),y.presetInput=s.jsdom.content.querySelector("input[act='preset-json']"),y.ccfBadge=s.jsdom.content.querySelector(".ccf-badge"),y.JSONerror=s.jsdom.content.querySelector(".exlg-regbadge-board > span"),y.JSONerror.style.display="none";let l=JSON.parse(I["sponsor-tag"].badges)[C.uid];typeof l>"u"?y.isactive=!1:(t===null?Z=l:Z=t,y.activeInput.value="已激活",y.activeInput.setAttribute("disabled","")),console.log(Z),y.badgeInput.value=Z.text??="",C.uid in ue?(y.pseudoInput.parentNode.style.display="",y.pseudoInput.value=Z.pseudo??=ue[C.uid]):y.pseudoInput.parentNode.style.display="none";let d=(c=Z)=>{let a={};Object.assign(a,c),a.pseudo=C.badge??a.pseudo??ue[C.uid];let p=Fe(C.uid,ct[C.color],"luogu4",a,!1);y.badgePreview.innerHTML="",y.badgePreview.append(p.exlg??"(内容为空则不显示)"),p.pseudoTag&&(y.pseudoPreview.innerHTML="",y.pseudoPreview.append(p.pseudoTag))};C.ccfLevel<3?y.ccfBadge.style.cssText="display: none;":C.ccfLevel<6?y.ccfBadge.style.cssText="--fa-primary-color:#fff; --fa-secondary-color:#52c41a; --fa-secondary-opacity:1;":C.ccfLevel<8?y.ccfBadge.style.cssText="--fa-primary-color:#fff; --fa-secondary-color:#3498db; --fa-secondary-opacity:1;":y.ccfBadge.style.cssText="--fa-primary-color:#fff; --fa-secondary-color:#ffc116; --fa-secondary-opacity:1;",d(),y.setMode=function(c){this.mode=c,Object.keys(this.boards).forEach(a=>this.boards[a].style.display="none"),this.boards[c].style.display="",s.width=this.boards[c].getAttribute("wd")??"500px",s.minHeight=this.boards[c].getAttribute("mh")??"300px",c==="main"&&d()},y.showError=function(c){this.setMode("showError"),this.errorMessage.innerText=c},y.setMode("main");let u=null;y.presetInput.oninput=()=>{u&&(u.style.border="2px solid rgba(0, 0, 0, 0)");let c=null,a=!0;document.querySelector(".exlg-regbadge-board > span").style.display="none";try{if(c=JSON.parse(y.presetInput.value),typeof c!="object"||Array.isArray(c))throw new TypeError("JSON ConfigData are supposed to be an Object!")}catch(p){Q(p),a=!1,document.querySelector(".exlg-regbadge-board > span").style.display=""}a&&d({text:Z.text,pseudo:Z.pseudo,...c})},s.jsdom.content.querySelector('button[act="toPreset"]').onclick=()=>{y.setMode("getPreset");let c={};n.forEach(a=>{c[a]=Z[a]??""}),y.presetInput.value=JSON.stringify(c),y.presetInput.oninput()},n.forEach(c=>Z[c]??=""),G=new Proxy(Z,{get(c,a,p){if(a==="pseudo")return c.pseudo;if(a==="text")return c.text;if(n.includes(a))return c[a]},set(c,a,p,f){try{if(a==="pseudo"||a==="text")return(a==="pseudo"?y.pseudoInput:y.badgeInput).value=p,Reflect.set(c,a,p);if(n.includes(a))return r[a].inputdom.value=p,Reflect.set(c,a,p);if(a==="configString")return y.assignData(JSON.parse(p)),!0}catch(x){Q(x)}finally{d()}}}),y.assignData=c=>{if(console.log("assign: ",c),typeof c!="object"||Array.isArray(c))throw new TypeError("JSON ConfigData are supposed to be an Object!");n.forEach(a=>{G[a]=c[a]??""})},y.pseudoInput.oninput=()=>G.pseudo=y.pseudoInput.value,y.badgeInput.oninput=()=>G.text=y.badgeInput.value,G.pseudo,G.text,n.forEach(c=>{let a=r[c],p=document.createElement("span");p.style.margin="5px";let f=document.createElement("span");f.className="exlg-regbadge-fronttitle",f.innerText=a.name;let x=document.createElement("input");if(x.setAttribute("keyId",c),x.setAttribute("exlg-badge-register",""),x.setAttribute("type","text"),x.setAttribute("placeholder",a.default),x.style.padding=".1em",x.value=Z[c],p.append(f),p.append(x),s.jsdom.content.querySelector("#regbadge-settings").append(p),s.jsdom.content.querySelector("#regbadge-settings").append(document.createElement("br")),x.oninput=()=>{G[c]=x.value},r[c].inputdom=x,c==="bg"){let h=document.createElement("button");h.innerHTML="+",p.append(h),h.onclick=()=>y.setMode("setbgColor")}if(c==="fg"){let h=document.createElement("span");h.classList.add("exlg-colpicker"),h.id=`exlg-pickr-${c}`;let m=document.createElement("style");m.innerHTML=GM_getResourceText("pickr_resource_css"),p.append(m),p.append(h);let b=Pickr.create({el:`#exlg-pickr-${c}`,theme:"nano",swatches:["rgba(244, 67, 54, 1)","rgba(233, 30, 99, 0.95)","rgba(156, 39, 176, 0.9)","rgba(103, 58, 183, 0.85)","rgba(63, 81, 181, 0.8)","rgba(33, 150, 243, 0.75)","rgba(3, 169, 244, 0.7)","rgba(0, 188, 212, 0.7)","rgba(0, 150, 136, 0.75)","rgba(76, 175, 80, 0.8)","rgba(139, 195, 74, 0.85)","rgba(205, 220, 57, 0.9)","rgba(255, 235, 59, 0.95)","rgba(255, 193, 7, 1)"],components:{preview:!0,opacity:!0,hue:!0,interaction:{hex:!1,rgba:!1,hsla:!1,hsva:!1,cmyk:!1,input:!0,clear:!0,save:!0}}});r[c].isUserSave=!1,b.on("init",w=>{console.log('Event: "init"',w);let k=G[c];b.setColor(y.pseudoPreview.lastElementChild.style[r[c].css]),G[c]=k}).on("save",(w,k)=>{console.log('Event: "save"',w,k),w!==null&&(G[c]=w.toHEXA().toString(),r[c].isUserSave=!0)}),r[c].cp=b,x.oninput=()=>{G[c]=x.value,r[c].isUserSave||b.setColor(null),r[c].isUserSave=!1}}}),Object.keys(Ce).forEach(c=>{let a=document.createElement("div");a.innerHTML=Ce[c].title,a.className="exlg-regbadge-preset-title",y.badgePresetList.append(a),Object.keys(Ce[c].presetList).forEach((p,f)=>{let{name:x,config:h}=Ce[c].presetList[p],m=JSON.parse(h);console.log(x,h,m);let b=document.createElement("span");b.style.border="2px solid rgba(0, 0, 0, 0)",b.append(Fe(C.uid,ct[C.color],"luogu4",{text:"exlg",...m},!1).exlg);let w=document.createElement("span");w.append(x),b.append(w),b.onclick=()=>{y.presetInput.value=h,y.presetInput.oninput(),b.style.border="2px solid grey",u=b},y.badgePresetList.append(b),f&1&&y.badgePresetList.append(document.createElement("br"))})}),A.regBadge=y,A.configProxy=G,y.bgModeSelect=s.jsdom.content.querySelector("#regbadge-setbgColor-type-select"),y.bgBoard={},y.bgModeSelect.childNodes.forEach(c=>{let a=c.value;!a||(y.bgBoard[a]=s.jsdom.content.querySelector(`.exlg-regbadge-bg-box[mode="${a}"]`),console.log(c,a,y.bgBoard[a]))}),y.bgModeSelect.onchange=function(){Object.keys(y.bgBoard).forEach(c=>{y.bgBoard[c].style.display="none"}),y.bgBoard[this.value].style.display=""},y.bgtextInput=y.bgBoard.text.querySelector("input"),y.bgtextInput.value=G.bg,y.bgsingle=Pickr.create({el:'span[exlg="bg-single-pickr"]',theme:"nano",swatches:["rgba(244, 67, 54, 1)","rgba(233, 30, 99, 0.95)","rgba(156, 39, 176, 0.9)","rgba(103, 58, 183, 0.85)","rgba(63, 81, 181, 0.8)","rgba(33, 150, 243, 0.75)","rgba(3, 169, 244, 0.7)","rgba(0, 188, 212, 0.7)","rgba(0, 150, 136, 0.75)","rgba(76, 175, 80, 0.8)","rgba(139, 195, 74, 0.85)","rgba(205, 220, 57, 0.9)","rgba(255, 235, 59, 0.95)","rgba(255, 193, 7, 1)"],components:{preview:!0,opacity:!0,hue:!0,interaction:{hex:!1,rgba:!1,hsla:!1,hsva:!1,cmyk:!1,input:!0,clear:!0,save:!0}}})},oncancel:()=>{switch(y.mode){case"success":case"main":return!0;default:return y.setMode("main"),!1}},onconfirm:async s=>{if(y.mode==="success")return location.reload(),!0;if(y.mode==="showError")return y.setMode("main"),!1;if(y.mode==="getPreset"){try{y.assignData(JSON.parse(y.presetInput.value))}catch(c){return s.title="[Err] 无效json",Q(c),setTimeout(()=>s.title=i,1500),!1}return y.setMode("main"),!1}if(y.mode==="setbgColor"){switch(y.bgModeSelect.value){case"text":G.bg=y.bgtextInput.value;break;case"lgdefault":G.bg="${luogu-default}";break;case"single":console.log(y.bgsingle.getColor().toHEXA().toString()),G.bg=y.bgsingle.getColor().toHEXA().toString();break;case"xncolorpicker":G.bg=y.bgxnpicker.getColor();break}return y.setMode("main"),!1}if(!(c=>c?(s.title=c,setTimeout(()=>s.title=i,1500),!1):!0)((c=>{if(c.fs)if(/^\d{0,}(.\d{0,})?(px|em)$/.test(c.fs)&&!["px","em"].includes(c.fs)){let a=c.fs.slice(0,-2);if(c.fs.includes("px")&&a>16||c.fs.includes("em")&&a>1)return"[Err] 字号过大,应不超过 16px/1em"}else return"[Err] 字号应以 px/em 为单位且合法"})(Z)))return!1;s.title="获取并验证令牌...",v.execute("token"),console.log(Z);let l={uid:C.uid,token:I["^token"].token,data:Z};y.isactive||(l.activation=y.activeInput.value),s.title="请求中...";let d=await K("https://exlg.piterator.com/badge/set",l).data;if("error"in d)return s.title="[Err] 失败",y.showError(d.error),!1;let u=JSON.parse(I["sponsor-tag"].badges);return u[l.uid]=Z,u[l.uid].ts=W(),I["sponsor-tag"].badges=JSON.stringify(u),s.title="badge 激活成功",s.content="badge 激活成功!感谢您对 exlg 的<del>打钱</del>支持。",y.mode="success",!1}},{width:"800px",min_height:"400px"}),y},wr),He=Eo;var No=oe.reg("get-latest","获取最新版本",{fetch_preview:{ty:"boolean",dft:!1,info:["Update preview versions","预览版更新"]}},null,({msto:e})=>new Promise((t,r)=>{ne({url:`https://api.github.com/repos/extend-luogu/extend-luogu/releases${e.fetch_preview?"?per_page=1":"/latest"}`,onload:n=>{let o=JSON.parse(n.responseText),i=(Array.isArray(o)?o[0]:o).tag_name,{version:s}=GM_info.script,l=pe(s,i),d=`Comparing version: ${s} ${l} ${i}`;M(d),t([i,l])},onerror:n=>{U(n),r(n)}})})),Qe=No;var _r='#exlg-dash{margin-right:5px;position:relative;display:inline-block;padding:1px 10px 3px;color:#fff;border-radius:6px;box-shadow:0 0 7px #1e90ff;cursor:pointer}#exlg-dash>.exlg-warn{position:absolute;top:-.5em;right:-.5em}.exlg-icon:before{display:inline-block;width:1.3em;height:1.3em;margin-left:3px;text-align:center;border-radius:50%}.exlg-icon:hover:after{display:inline-block}.exlg-icon:after{display:none;content:attr(name);margin-left:5px;padding:0 3px;background-color:#fff;box-shadow:0 0 7px #00bfff;border-radius:7px}.exlg-icon.exlg-info:before{content:"i";color:#fff;background-color:#00bfff;font-style:italic}.exlg-icon.exlg-warn:before{content:"!";color:#fff;background-color:#e74c3c;font-style:normal}.exlg-unselectable{-webkit-user-select:none;-moz-user-select:none;-o-user-select:none;user-select:none}:root{--exlg-azure: #7bb8eb;--exlg-aqua: #03a2e8;--exlg-indigo: #3f48cb;--std-mediumturquoise: #48d1cc;--std-cornflowerblue: #6495ed;--std-dodgerblue: #1e90ff;--std-white: #fff;--std-black: #000;--lg-gray: #bbb;--lg-gray-2: #7f7f7f;--lg-gray-3: #6c757d;--lg-gray-4: #414345;--lg-gray-5: #333;--lg-gray-6: #000000bf;--lg-blue: #3498db;--lg-blue-button: #0e90d2;--lg-blue-dark: #34495e;--lg-blue-2: #7cb5ecbf;--lg-green: #5eb95e;--lg-green-dark: #054310c9;--lg-green-light: #5eb95e26;--lg-green-light-2: #c9e7c9;--lg-yellow: #f1c40f;--lg-orange: #e67e22;--lg-red: #e74c3c;--lg-red-light: #dd514c26;--lg-red-light-2: #f5cecd;--lg-red-button: #dd514c;--lg-purple: #8e44ad;--argon-indigo: #5e72e4;--argon-red: #f80031;--argon-red-button: #f5365c;--argon-green: #1aae6f;--argon-green-button: #2dce89;--argon-cyan: #03acca;--argon-yellow: #ff9d09;--lg-red-problem: #fe4c61;--lg-orange-problem: #f39c11;--lg-yellow-problem: #ffc116;--lg-green-problem: #52c41a;--lg-blue-problem: #3498db;--lg-purple-problem: #9d3dcf;--lg-black-problem: #0e1d69;--lg-gray-problem: #bfbfbf}.exlg-difficulty-color{font-weight:700}.exlg-difficulty-color.color-0{color:#bfbfbf!important}.exlg-difficulty-color.color-1{color:#fe4c61!important}.exlg-difficulty-color.color-2{color:#f39c11!important}.exlg-difficulty-color.color-3{color:#ffc116!important}.exlg-difficulty-color.color-4{color:#52c41a!important}.exlg-difficulty-color.color-5{color:#3498db!important}.exlg-difficulty-color.color-6{color:#9d3dcf!important}.exlg-difficulty-color.color-7{color:#0e1d69!important}#exlg-dash-wrapper:hover>.exlg-window{visibility:visible;opacity:1;transition-delay:unset}#exlg-dash-wrapper>.exlg-window{position:absolute;top:35px;left:0;z-index:148;visibility:hidden;opacity:0;width:340px;padding:15px;background:white;color:#000;border-radius:7px;box-shadow:#bbe3ff 0 0 7px;transition:.233s ease all}.exlg-window{position:absolute;top:35px;left:0;width:340px;padding:15px;z-index:6;background:white;color:#000;border-radius:7px;box-shadow:#bbe3ff 0 0 7px;transition:.233s ease all}.exlg-windiv-left-tag{height:2em;width:18%;margin-right:10px;display:inline-block;text-align:center}.exlg-windiv-btn{font-size:.9em;display:inline-block;flex:none;outline:0;cursor:pointer;color:#fff;font-weight:inherit;line-height:1.5;text-align:center;vertical-align:middle;background:0 0;border-radius:5px;border:1px solid;margin:4px}.dropdown>.center{padding:4px 1px 10px}';var $r="#exlg-dash{padding:3px 19px 4px;box-shadow:0 2px 5px #0009;font-family:Fira Code,Fira Mono,Consolas}#exlg-dash-window[class=exlg-window]{box-shadow:0 3px 8px #0009;font-family:Fira Code,Fira Mono,Consolas;padding:10px;width:400px!important;left:-175px!important;background:rgba(255,255,255,.6);backdrop-filter:blur(12px);margin-top:10px}#exlg-windiv,#exlg-dash-window>h2{padding:5px}#exlg-dash-window>h2 g{fill:#3f48cc}.exlg-windiv-btn{background-color:#3f48cc!important;border:none!important;padding:.313em 1em}.exlg-windiv-btn:hover{background-color:#607ee8!important;transition:background-color .25s}#link-div .exlg-windiv-btnspan:nth-child(2) svg{fill:#fff;padding-right:3px;height:16px;width:16px;line-height:1.5}";var dt='.ops>a.color-none{width:auto;display:flex;align-items:center;justify-content:space-between;padding:6px 14px;border-radius:8px;color:var(--text2);font-size:14px;cursor:pointer;transition:background-color .3s;margin:2px}.ops>a.color-none:hover{background-color:#e3e5e7;border-radius:8px}.ops>a.color-none>.link-title{display:flex;align-items:center}.ops>a.color-none>.link-title>svg{margin-right:8px;width:16px}.ops>a.color-none:after{transform:rotate(-90deg);content:url("data:image/svg+xml;charset=UTF-8, %3Csvg%20style=%22fill:%20rgb(108,%20117,%20125);%22%20width=%2210%22%20height=%2210%22%20viewBox=%220%200%209%209%22%20fill=%22rgb(108,%20117,%20125)%22%20xmlns=%22http://www.w3.org/2000/svg%22%20class=%22link-icon--right%22%3E%3Cpath%20fill-rule=%22evenodd%22%20clip-rule=%22evenodd%22%20d=%22M7.50588%203.40623C7.40825%203.3086%207.24996%203.3086%207.15232%203.40623L4.41244%206.14612L1.67255%203.40623C1.57491%203.3086%201.41662%203.3086%201.31899%203.40623C1.22136%203.50386%201.22136%203.66215%201.31899%203.75978L4.11781%206.5586C4.28053%206.72132%204.54434%206.72132%204.70706%206.5586L7.50588%203.75978C7.60351%203.66215%207.60351%203.50386%207.50588%203.40623Z%22%20fill=%22rgb(108,%20117,%20125)%22%3E%3C/path%3E%3Cpath%20d=%22M7.15232%203.40623L7.50588%203.75978L7.50588%203.75978L7.15232%203.40623ZM7.50588%203.40623L7.15232%203.75978L7.15233%203.75978L7.50588%203.40623ZM4.41244%206.14612L4.05888%206.49967C4.15265%206.59344%204.27983%206.64612%204.41244%206.64612C4.54504%206.64612%204.67222%206.59344%204.76599%206.49967L4.41244%206.14612ZM1.67255%203.40623L2.0261%203.05268L2.0261%203.05268L1.67255%203.40623ZM1.31899%203.40623L0.965439%203.05268L0.965439%203.05268L1.31899%203.40623ZM1.31899%203.75978L1.67255%203.40623V3.40623L1.31899%203.75978ZM4.11781%206.5586L3.76425%206.91215L4.11781%206.5586ZM4.70706%206.5586L4.35351%206.20505L4.70706%206.5586ZM7.50588%203.75978L7.15233%203.40623L7.15232%203.40623L7.50588%203.75978ZM7.50588%203.75978C7.40825%203.85742%207.24996%203.85742%207.15232%203.75978L7.85943%203.05268C7.56654%202.75978%207.09166%202.75978%206.79877%203.05268L7.50588%203.75978ZM4.76599%206.49967L7.50588%203.75978L6.79877%203.05268L4.05888%205.79257L4.76599%206.49967ZM1.31899%203.75978L4.05888%206.49967L4.76599%205.79257L2.0261%203.05268L1.31899%203.75978ZM1.67254%203.75979C1.57491%203.85742%201.41662%203.85742%201.31899%203.75979L2.0261%203.05268C1.73321%202.75978%201.25833%202.75978%200.965439%203.05268L1.67254%203.75979ZM1.67255%203.40623C1.77018%203.50386%201.77018%203.66215%201.67255%203.75978L0.965439%203.05268C0.672546%203.34557%200.672546%203.82044%200.965439%204.11334L1.67255%203.40623ZM4.47136%206.20505L1.67255%203.40623L0.965439%204.11334L3.76425%206.91215L4.47136%206.20505ZM4.35351%206.20505C4.38605%206.1725%204.43882%206.1725%204.47136%206.20505L3.76425%206.91215C4.12223%207.27013%204.70264%207.27013%205.06062%206.91215L4.35351%206.20505ZM7.15232%203.40623L4.35351%206.20505L5.06062%206.91215L7.85943%204.11334L7.15232%203.40623ZM7.15233%203.75978C7.05469%203.66215%207.05469%203.50386%207.15233%203.40623L7.85943%204.11334C8.15233%203.82045%208.15233%203.34557%207.85943%203.05268L7.15233%203.75978Z%22%20fill=%22rgb(108,%20117,%20125)%22%3E%3C/path%3E%3C/svg%3E")}.ops>a:hover{background-color:#e3e5e7;border-radius:1em}.wrapper.hover>.dropdown>.center{font-size:1.1em}.exlg-dropdown.field{display:inline-block;border-left:none;padding:0 .8em}.exlg-dropdown.field:hover{color:#00aeec!important}.exlg-dropdown.field:hover>.value{color:#00aeec!important}.exlg-dropdown.field:hover>.key{color:#00aeec!important}.exlg-dropdown.field>.value{display:block;text-align:center;line-height:1.5;font-weight:700;color:#6c757d;font-size:1.1em;transition:color .2s}.exlg-dropdown.field>.key{display:block;text-align:center;color:#9499a0;font-weight:400;font-size:.75em;transition:color .2s}.exlg-dropdown.field>.key-small{display:block;text-align:center;color:#9499a0;font-weight:400;font-size:.5em;transition:color .2s}.ops[data-v-78704ac9]{padding:.5em .9em}.header[data-v-78704ac9]{margin-bottom:-.6em}';v.reg_main("dash-board","控制面板",v.path_dash_board,{msg:{ty:"object",priv:!0,lvs:{queue:{ty:"array",itm:{ty:"object",lvs:{text:{ty:"string"},id:{ty:"number"}}}},last_id:{ty:"number",dft:0}}},lang:{ty:"enum",dft:"zh",vals:["zh","en"],info:["Language of descriptions in the dashboard","控制面板提示语言"]},load_speed:{ty:"number",dft:10,min:0,max:10,info:["Dash animation speed","dash 动画速度"]}},()=>{let e=(r,n=[])=>Object.entries(r).filter(([o,i])=>(n.length||o!=="on")&&!i.priv).flatMap(([o,i])=>i.ty==="object"?e(i.lvs,n.concat(o)):{name:n.concat(o),displayName:o.split("_").map(s=>s.toInitialCase()).join(" "),description:i.info,type:{number:"SLIDER",boolean:"CHECKBOX",string:"TEXTBOX",enum:"SELECTBOX"}[i.ty],...i.ty==="number"&&{minValue:i.min,maxValue:i.max,increment:i.step},...i.ty==="enum"&&{acceptableValues:i.vals}}),t=[...Y._].map(([r,{description:n,alias:o,icon:i,unclosable:s}])=>({name:r,description:n,icon:i,children:(r==="component"?[...oe._]:[...v._].filter(([,l])=>l.cate===r)).map(([l,d])=>({rawName:o+l,name:l,description:d.info,unclosable:s,settings:e(J[o+l]?.lvs??{})}))}));console.log(t),A.guiStart(t)});v.reg_hook_new("dash-bridge","控制桥","@/.*",{source:{ty:"enum",vals:["exlg","gh_index","debug"],dft:"exlg",info:["The website to open when clicking the exlg button","点击 exlg 按钮时打开的网页"]},beautify_dropdown:{ty:"boolean",dft:!0,info:["Beautify Dropdown","右上角用户信息卡美化"]},beautify_dash:{ty:"boolean",dft:!0,info:["Beautify Dash Board","控制桥面板美化"]},enable_rclick:{ty:"boolean",dft:!0,info:["Use Right Click to change source","右键点击按钮换源"]},latest_ignore:{ty:"string",dft:"0.0.0"}},({msto:e,args:t})=>{["exlg","gh_index","debug"].indexOf(e.source)===-1&&(e.source="exlg");let{$tar:r}=t,n=()=>A.exlg.dash=A.open({exlg:"https://dash.exlg.cc/index.html",gh_index:"https://extend-luogu.github.io/exlg-setting-new/index.html",debug:"localhost:1634/index.html"}[e.source]);if(e.beautify_dropdown){GM_addStyle(dt);let d=(c,a,p=g(a))=>{p.addClass("exlg-dash-options"),a.innerHTML=`<div class="link-title">${a.innerHTML}</div>`};if(t.type===2){d(0,r[0],r);return}let u=(c,a)=>{c.children(".header").after(`<style>${dt}</style>`).after(`
                <div style="margin-top: 0.4em;">
                    <a class="exlg-dropdown field" href="//www.luogu.com.cn/user/${C.uid}#following.following">
                        <span class="value">${C.followingCount}</span>
                        <span data-v-3c4577b8="" class="key">关注</span>
                    </a>
                    <a class="exlg-dropdown field" href="//www.luogu.com.cn/user/${C.uid}#following.follower">
                        <span class="value">${C.followerCount}</span>
                        <span data-v-3c4577b8="" class="key">粉丝</span>
                    </a>
                    <a class="exlg-dropdown field" href="//www.luogu.com.cn/user/notification">
                        <span class="value">${C.unreadNoticeCount+C.unreadMessageCount}</span>
                        <span data-v-3c4577b8="" class="key">动态</span>
                    </a>
                </div>
                `).after(`
                <div class="exlg-dropdown field">
                    <span data-v-3c4577b8="" class="key-small">CCF 评级: <strong>${C.ccfLevel}</strong> | 咕值排行: <strong>${C.ranking}</strong></span>
                </div>
                `),a.each(d);let p=g(a[5]).clone().attr("href","javascript:void 0");p.on("click",n),g(a[5]).after(p),p.children("div.link-title").html(`${mr} 插件设置`)};(r.hasClass("user-nav")||r.parent().hasClass("user-nav"))&&u(r.find(".dropdown > .center"),r.find(".ops > a"))}let o=g('<span id="exlg-dash-wrapper"></span>').prependTo(r),i=g('<span id="exlg-dash-window" class="exlg-window"></span>').css("left","-125px"),s=g('<div id="exlg-dash" exlg="exlg">exlg</div>').prependTo(o).css("backgroundColor",{exlg:"cornflowerblue",gh_index:"darkblue",debug:"steelblue"}[e.source]).css("margin-top",r.hasClass("nav-container")?"5px":"0px");if(e.enable_rclick?s.on("contextmenu",!1).on("mousedown",d=>{d.button?d.button===2&&(e.source={exlg:"gh_index",gh_index:"debug",debug:"exlg"}[e.source],s.css("backgroundColor",{exlg:"cornflowerblue",gh_index:"darkblue",debug:"steelblue"}[e.source])):n()}):s.on("click",n),!r.parent().hasClass("mobile-nav-container")){e.beautify_dash&&i.append(`<style>${$r}</style>`),i.appendTo(o),g(`<h2 align="center" style="margin-top: 5px;margin-bottom: 10px;">${qe}</h2>`).appendTo(i);let d=g('<div id="exlg-windiv"></div>').appendTo(i);[{tag:"vers",title:"版本",buttons:[]},{tag:"source",title:"源码",buttons:[{html:"OSS",url:"https://exlg.oss-cn-shanghai.aliyuncs.com/latest/dist/extend-luogu.min.user.js"},{html:"JsDelivr",url:`https://fastly.jsdelivr.net/gh/extend-luogu/extend-luogu${I["#get-latest"].fetch_preview?"@preview":""}/dist/extend-luogu.min.user.js`},{html:"Raw",url:`https://github.com/extend-luogu/extend-luogu/raw/${I["#get-latest"].fetch_preview?"preview":"latest"}/dist/extend-luogu.min.user.js`}]},{tag:"link",title:"链接",buttons:[{html:"官网",url:"https://exlg.cc"},{col:"#666",html:`<a style="height: 8px;width: 8px;">${hr}</a>Github`,url:"https://github.com/extend-luogu/extend-luogu"},{html:"爱发电",url:"https://afdian.net/@extend-luogu"}]},{tag:"help",title:"帮助",buttons:[{html:"官方文档",url:"https://docs.exlg.cc"},{html:"用户协议",url:"https://docs.exlg.cc/POLICY.html"}]},{tag:"debug",title:"debug",buttons:[{html:"清除数据",title:"清空所有数据,无法恢复。",onclick:()=>{N('你确定要这么做吗?<br/><strong style="color: red;">数据将不可恢复!</strong>',"exlg 警告!",()=>(GM_listValues().forEach(GM_deleteValue),location.reload(),!0))}},{html:"刷新 token",title:"多用于犇犇、badge 等服务不能正确运行时。",onclick:()=>{N("点击确定以刷新用户 token。","exlg 提醒您",async()=>(await v.execute("token"),location.reload(),!0))}}]},{tag:"badge",title:"badge",buttons:[{html:"注册/修改",onclick:()=>He()},{html:"刷新 badge",title:"多用于 badge 数据滞后时。",onclick:()=>{N("点击确定以刷新 badge 数据。","exlg 提醒您",async()=>(I["sponsor-tag"].badges="{}",location.reload(),!0))}}]}].forEach(c=>{let a=g(`<div id="${c.tag}-div"><span class="exlg-windiv-left-tag">${c.title}</span></div>`).appendTo(d),p=g("<span></span>").appendTo(a);if(c.buttons.forEach(f=>{let x=f.col??"#66ccff";g('<span class="exlg-windiv-btnspan"></span>').append(g(`<button class="exlg-windiv-btn"${f.title?` title="${f.title}"`:""}" style="background-color: ${x};border-color: ${x};">${f.html}</button>`).on("click",f.onclick??(()=>location.href=f.url))).appendTo(p)}),c.tag==="vers"){p.append(g(`<span id="version-text" style="min-width: 60%; margin-left: 5px;">
    <span title="当前版本">${GM_info.script.version}</span>
    <span id="vers-comp-operator" style="margin-left: 5px;"></span>
    <span id="latest-version" style="margin-left: 5px;"></span>
    <span id="annoyingthings"></span></span>"`));let f=g('<button class="exlg-windiv-btn" style="background-color: red;border-color: red;float: right;margin: 0 20px 0 0;">刷新</button>'),x=p.find("#vers-comp-operator"),h=p.find("#latest-version"),m=p.find("#annoyingthings"),b=async()=>{x.text(""),h.text(""),m.html("");let[w,k]=await Qe();if(x.html(k).css("color",{"<<":"#fe4c61","==":"#52c41a",">>":"#3498db"}[k]),h.text(w).attr("title","最新版本"),m.html({"<<":'<i class="exlg-icon exlg-info" name="有新版本"></i>',">>":'<i class="exlg-icon exlg-info" name="内测中!"></i>'}[k]||"").children().css("cssText","position: absolute;display: inline-block;"),k==="<<"&&pe(e.latest_ignore,w)==="<<"){let L=g(`<span style="color: red;margin-left: 30px;">${br}</span>`).on("click",()=>{e.latest_ignore=w,L.hide()}).appendTo(m)}k==="=="&&(e.latest_ignore=GM_info.script.version)};f.on("click",b).appendTo(p)}})}},e=>{let t=g(e.target);if(e.target.tagName.toLowerCase()==="a"&&t.hasClass("color-none")&&t.parent().hasClass("ops")&&!t.hasClass("exlg-dash-options"))return{result:2,args:{$tar:g(e.target),type:2}};let r=t.find(".user-nav, .nav-container");return r.length&&!r.find("#exlg-dash-window").length?{result:r.length,args:{$tar:r[0].tagName==="DIV"?g(r[0].firstChild):r,type:1}}:{result:0}},()=>({$tar:g("nav.user-nav, div.user-nav > nav, .nav-container"),type:0}),_r,"core");var Lr=".am-btn-warning{border-color:#ffc116;background-color:#ffc116;color:#fff}.am-btn-warning:hover{border-color:#f37b1d;background-color:#f37b1d;color:#fff}.am-btn{outline:none}";v.reg("discussion-save","讨论保存",["@/discuss/\\d+(\\?page\\=\\d+)*$"],{auto_save_discussion:{ty:"boolean",dft:!1,strict:!0,info:["Discussion Auto Save","自动保存讨论"]}},({msto:e})=>{let t=g('<button class="am-btn am-btn-success am-btn-sm" name="save-discuss">保存讨论</button>'),r=window.location.pathname.split("/")[2];t.on("click",()=>{t.prop("disabled",!0),t.text("保存中..."),ne({url:`https://lda.piterator.com/${r}`,onload:o=>{o.status<=400?(M("Discuss saved"),t.text("保存成功"),setTimeout(()=>{t.text("保存讨论"),t.removeAttr("disabled")},1e3)):(M("Fail to save discuss: ",o),t.text("保存失败"),t.toggleClass("am-btn-success").toggleClass("am-btn-danger"),setTimeout(()=>{t.text("保存讨论"),t.removeAttr("disabled"),t.toggleClass("am-btn-success").toggleClass("am-btn-danger")},1e3))},onerror:o=>{M(`Error:${o}`),t.removeAttr("disabled")}})}).css("margin-top","5px");let n=g(`<a class="am-btn am-btn-warning am-btn-sm" name="save-discuss" href="https://lglg.top/${r}">查看备份</a>`).css("margin-top","5px");g("section.lg-summary").find("p").append(g("<br>")).append(t).append(g("<span>&nbsp;</span>")).append(n),e.auto_save_discussion&&t.click()},Lr,"module");var Tr=".mp-editor-ground.exlg-ext.exlg-show-emo.exlg-show-emo-long{top:8.25em!important}.mp-editor-ground.exlg-ext.exlg-show-emo.exlg-show-emo-short{top:4.75em!important}.mp-editor-menu>br~li{position:relative;display:inline-block;margin:0;padding:5px 1px}.mp-editor-menu.exlg-show-emo.exlg-show-emo-long{height:6em!important;overflow:auto;background-color:#fff}.mp-editor-menu.exlg-show-emo.exlg-show-emo-short{height:2.5em!important;overflow:auto;background-color:#fff}.exlg-emo-btn{position:relative;top:0;border:none;background-color:#eee;border-radius:.7em;margin:.1em;transition:all .4s;height:2em}.exlg-emo-btn:hover{background-color:#f3f3f3;top:-3px}.exlg-emo,.exlg-ext{transition:all .15s}";var he={EMO:1,TXT:2};v.reg("emoticon","表情输入",["@/paste","@/discuss/.*","@/"],{benben:{ty:"boolean",dft:!0,info:["Show in benben","犇犇表情"]},show:{ty:"boolean",dft:!0,info:["Show in default","是否默认显示表情栏"]},src:{ty:"enum",vals:["图.tk","啧.tk"],dft:"图.tk",info:["Emoticon Source","表情源"]},height_limit:{ty:"boolean",dft:!0,info:["Expand in default","是否默认展开表情"]}},({msto:e})=>{let t=["kk","jk","se","qq","xyx","xia","cy","ll","xk","qiao","qiang","ruo","mg","dx","youl","baojin","shq","lb","lh","qd","fad","dao","cd","kun","px","ts","kl","yiw","dk",{name:["sto"],slug:"gg",name_display:"sto",width:40},{name:["orz"],slug:"gh",name_display:"orz",width:40},{name:["qwq"],slug:"g5",name_display:"qwq",width:40},{name:["hqlm"],slug:"l0",name_display:"火前留名"},{name:["sqlm"],slug:"l1",name_display:"山前留名"},{name:["xbt"],slug:"g1",name_display:"屑标题"},{name:["iee","wee"],slug:"g2",name_display:"我谔谔"},{name:["kg"],slug:"g3",name_display:"烤咕"},{name:["gl"],slug:"g4",name_display:"盖楼"},{name:["wyy"],slug:"g6",name_display:"无意义"},{name:["wgzs"],slug:"g7",name_display:"违规紫衫"},{name:["tt"],slug:"g8",name_display:"贴贴"},{name:["jbl"],slug:"g9",name_display:"举报了"},{name:["%%%","mmm"],slug:"ga",name_display:"%%%"},{name:["ngrb"],slug:"gb",name_display:"你谷日爆"},{name:["qpzc","qp","zc"],slug:"gc",name_display:"前排资瓷"},{name:["cmzz"],slug:"gd",name_display:"臭名昭著"},{name:["zyx"],slug:"ge",name_display:"致远星"},{name:["zh"],slug:"gf",name_display:"祝好"}].filter(u=>e.src!=="啧.tk"||typeof u!="object").map((u,c)=>typeof u=="string"?{type:he.EMO,name:[u],slug:c>=10?String.fromCharCode(97+(c-10)):String.fromCharCode(48+c)}:{type:he.TXT,...u}),r=e.src==="啧.tk"?({name:u})=>`//${e.src}/${u[0]}`:({slug:u,type:c})=>`//${e.src}/${u+(c===he.TXT?"!25":"")}`;if(e.benben&&location.pathname==="/"){let u=g("#feed-content"),c=u[0];g("#feed-content").before("<div id='emo-lst'></div>"),t.forEach(a=>{let p=g(a.type===he.EMO?`<button class="exlg-emo-btn" exlg="exlg"><img src="${r(a)}" /></button>`:`<button class="exlg-emo-btn" exlg="exlg">${a.name_display}</button>`);p.on("click",()=>{let f=c.value,x=c.selectionStart,h=`${f.slice(0,x)}![](${r(a)})`;c.value=h+f.slice(c.selectionEnd),c.focus(),c.setSelectionRange(h.length,h.length)}).appendTo("#emo-lst"),a.width?p.css("width",`${a.width}px`):a.type===he.EMO?p.css("width","40px"):p.css("width","83px")}),g("#feed-content").before("<br>")}let n=g(".mp-editor-menu"),o=g(".CodeMirror-wrap textarea");if(!n.length)return;let i=n.clone().addClass("exlg-emo").text("");n.after(i).append("<br />");let s=g(".mp-editor-ground").addClass("exlg-ext"),l=n.children().first().clone(!0).addClass("exlg-unselectable"),d=n.children().first().clone(!0).addClass("exlg-unselectable");n.children().last().before(l),n.children().last().before(d),l.children().attr("title","").text(e.show?"隐藏":"显示"),e.show&&(i.addClass("exlg-show-emo"),s.addClass("exlg-show-emo")),l.on("click",()=>{l.children()[0].innerHTML=["显示","隐藏"][["隐藏","显示"].indexOf(l.children()[0].innerHTML)],i.toggleClass("exlg-show-emo"),s.toggleClass("exlg-show-emo"),e.show=!e.show}),d.children().attr("title","").text(e.height_limit?"展开":"收起"),e.height_limit?(i.addClass("exlg-show-emo-short"),s.addClass("exlg-show-emo-short")):(i.addClass("exlg-show-emo-long"),s.addClass("exlg-show-emo-long")),d.on("click",()=>{d.children()[0].innerHTML=["收起","展开"][["展开","收起"].indexOf(d.children()[0].innerHTML)],i.toggleClass("exlg-show-emo-short").toggleClass("exlg-show-emo-long"),s.toggleClass("exlg-show-emo-short").toggleClass("exlg-show-emo-long"),e.height_limit=!e.height_limit}),t.forEach(u=>{let c=g(u.type===he.EMO?`<button class="exlg-emo-btn" exlg="exlg"><img src="${r(u)}" /></button>`:`<button class="exlg-emo-btn" exlg="exlg">${u.name_display}</button>`);c.on("click",()=>o.trigger("focus").val(`![](${r(u)})`).trigger("input")).appendTo(i),u.width?c.css("width",`${u.width}px`):u.type===he.EMO?c.css("width","40px"):c.css("width","83px")}),i.append("<div style='height: .35em'></div>")},Tr,"module");var Cr=".juan-rnkitm:nth-child(1)::marker{color:var(--lg-red);font-weight:900}.juan-rnkitm:nth-child(2)::marker{color:var(--lg-orange);font-weight:900}.juan-rnkitm:nth-child(3)::marker{color:var(--lg-yellow);font-weight:900}.juan-rnkitm>span{display:flex;flex-wrap:wrap}.juan-rnkitm>span>span{margin-left:auto}";v.reg_v2({name:"juan-inspector",info:"卷王监视器",path:"@/",cate:"module"},{lastFetched:{ty:"string",dft:"[]",priv:!0}},e=>{e.hook({name:"login-hooker",info:"签到 Hook"},{},({gsto:t,args:r})=>{g(r).on("click",async()=>{let{users:n}=await g.get(`/api/user/followings?user=${C.uid}`),{result:o,perPage:i}=n,s=[],l=n.count-i;for(let f=2;l>0;f++,l-=i)s.push((async()=>o.push(...(await g.get(`/api/user/followings?user=${C.uid}&page=${f}`)).users.result))());await Promise.all(s);let d=new Map;o.forEach(({uid:f,name:x,passedProblemCount:h},m,b)=>{b[m]=[f,h],d.set(f,x)}),o.sort((f,x)=>f[0]-x[0]);let u=JSON.parse(t.lastFetched),c=[];t.lastFetched=JSON.stringify(o);let a=0,p=0;for(;a<o.length&&p<u.length;)o[a][0]===u[p][0]?(c.push([o[a][0],o[a][1]-u[p][1]]),a++,p++):o[a][0]>u[p][0]?p++:a++;c.sort((f,x)=>x[1]-f[1]),c.length&&N(`<p>从上一次打卡到现在,关注用户中卷题量前三为:</p>
                <ol style="margin: 0 25% 0 15%;">
                    ${c.slice(0,3).map(([f,x])=>`<li class="juan-rnkitm"><span><a href="/user/${f}">${d.get(f)}</a><span>${x} 道</span></span></li>`).join("")}
                </ol>`)})},_e("a[title=hello]"))},Cr);var Sr="#exlg-cli{position:fixed;top:0;z-index:134;display:none;width:100%;height:40px;background-color:#fff;box-shadow:0 0 7px #1e90ff}#exlg-cli-input{display:block;height:100%;width:100%;border:none;outline:none;font-family:Fira Code,Fira Mono,consolas,Courier New;color:#000}#exlg-cli-input.error{background-color:#cd5c5c}";v.reg("keyboard-and-cli","键盘操作与命令行","@/.*",{lang:{ty:"enum",dft:"en",vals:["en","zh"]}},({msto:e})=>{let t=g('<div id="exlg-cli" exlg="exlg"></div>').appendTo(g("body")),r=g('<input id="exlg-cli-input" />').appendTo(t),n=!1,o=[],i=0,s=["en","zh"],l={".":["。"],",":[","],"!":["!"],"?":["?"],cli:["命令行"],current:["当前"],language:["语言"],available:["可用"],command:["命令"],commands:["命令"],unknown:["未知"],forum:["板块"],target:["目标"],mod:["模块"],action:["操作"],illegal:["错误"],param:["参数"],expected:["期望"],type:["类型"],lost:["缺失"],essential:["必要"],user:["用户"]},d=s.indexOf(e.lang)||0,u=(h,...m)=>{n=!0;let b=h.map((w,k)=>w.split(/\b/).map(L=>l[L]?.[d-1]??L).join("")+(m[k]||"")).join("");return r.val(b)},c=(h,...m)=>Q(u(h,...m).addClass("error").val()),a=()=>(n=!1,r.val("").removeClass("error")),p=(h,m,b,w)=>(m=m.replace(/ /g,"").split(",").map(k=>{let L={};return k[0]==="["?(L.essential=!1,k=k.slice(1,-1)):L.essential=!0,[L.name,L.type]=k.split(":"),L}),{name:h,arg:m,help:b,fn:w}),f=[p("help","[cmd: string]",["get the help of <cmd>. or list all cmds.","获取 <cmd> 的帮助。空则列出所有"],h=>{if(!h)u`exlg cli. current language: ${d}, available commands: ${Object.keys(f).join(", ")}`;else{let m=f[h];if(!m)return c`help: unknown command "${h}"`;let b=m.arg.map(w=>{let k=`${w.name}: ${w.type}`;return w.essential?`<${k}>`:`[${k}]`}).join(" ");u`${h} ${b} ${m.help[d]}`}}),p("cd","path: string",["jump to <path>, relative path is OK.","跳转至 <path>,支持相对路径。"],h=>{let m;if(h[0]==="/")m=h;else{let b=location.pathname.replace(/^\/+/,"").split("/");h.split("/").forEach(k=>{k!=="."&&(k===".."?b.pop():b.push(k))}),m=b.join("/")}location.href=`${location.origin}/${m.replace(/^\/+/,"")}`}),p("cdd","forum: string",["jump to the forum named <forum> of discussion. use all the names you can think of.","跳转至名为 <forum> 的讨论板块,你能想到的名字基本都有用。"],h=>{let m=[["relevantaffairs","gs","gsq","灌水","灌水区","r","ra"],["academics","xs","xsb","学术","学术版","a","ac"],["siteaffairs","zw","zwb","站务","站务版","s","sa"],["problem","tm","tmzb","题目","题目总版","p"],["service","fk","fksqgd","反馈","反馈、申请、工单专版","se"]];if(h=m.find(b=>b.includes(h))?.[0],!m)return c`cdd: unknown forum "${h}"`;location.href=`https://www.luogu.com.cn/discuss/lists?forumname=${h}`}),p("cc","[name: char]",['jump to <name>, "h|p|c|r|d|i|m|n" stands for home|problem|contest|record|discuss|I myself|message|notification. or jump home.','跳转至 [name],"h|p|c|r|d|i|m|n" 代表:主页 | 题目 | 比赛 | 评测记录 | 讨论 | 个人中心 | 私信 | 通知。空则跳转主页。'],h=>{h=h||"h";let m={h:"/",p:"/problem/list",c:"/contest/list",r:"/record/list",d:"/discuss/lists",i:`/user/${C.uid}`,m:"/chat",n:"/user/notification"}[h];m?f.cd.fn(m):c`cc: unknown target "${h}"`}),p("mod","action: string, [name: string]",['for <action> "enable|disable|toggle", opearte the mod named <name>.','当 <action> 为 "enable|disable|toggle",对名为 <name> 的模块执行对应操作:启用 | 禁用 | 切换。'],(h,m)=>{switch(h){case"enable":case"disable":case"toggle":if(!v.has(m))return c`mod: unknown mod "${m}"`;I[m].on={enable:()=>!0,disable:()=>!1,toggle:b=>!b}[h](I[m].on);break;default:return c`mod: unknown action "${h}"`}}),p("lang","lang: string",['for <lang> "en|zh" switch current cli language.','当 <lang> 为 "en|zh",切换当前语言。'],h=>{try{e.lang=h,d=s.indexOf(h)}catch{return c`lang: unknown language ${h}`}}),p("go","type: string, [extinfo: string]",["jumps to any corner of Luogu.","跳转到洛谷的任何一个角落。"],(h,m)=>{let b=0,w={h:["home","homepage"],p:["problem"],c:["contest"],r:["record","submission"],d:["discuss","forum"],i:["user"],m:["chat","message"],n:["notification"]},k={h:"/",p:"/problem/list",c:"/contest/list",r:"/record/list",d:"/discuss/lists",i:`/user/${C.uid}`,m:"/chat",n:"/user/notification"},L=O=>H=>isNaN(+H)?(b=1,c`unable to parse integer "${H}"`):`/${O}/${+H}`,S={p(O){return O=O.toUpperCase(),ye(O)?`/problem/${O}`:(b=1,c`unable to judge pid ${O}`)},c:L("contest"),r:L("record"),d(O){let ee=[["relevantaffairs","gs","gsq","灌水","灌水区","r","ra"],["academics","xs","xsb","学术","学术版","a","ac"],["siteaffairs","zw","zwb","站务","站务版","s","sa"],["problem","tm","tmzb","题目","题目总版","p"],["service","fk","fksqgd","反馈","反馈、申请、工单专版","se"]].find(fe=>fe.includes(O))?.[0];return ee?`/discuss/lists?forumname=${ee}`:L("discuss")},i:L("user")},R="",j=h;for(let[O,H]of Object.entries(w))if(H.includes(h)){j=O;break}if(m){if(!(j in S))return c`category ${h} has no arguments`;R=S[j](m)}else R=k[j];b||(location.href=R)}),p("uid","uid: integer",["jumps to homepage of user whose uid is <uid>.","跳转至 uid 为 <uid> 的用户主页。"],h=>location.href=`/user/${h}`),p("un","name: string",["jumps to homepage of user whose username is like <name>.","跳转至用户名与 <name> 类似的用户主页。"],h=>{g.get(`/api/user/search?keyword=${h}`,m=>{m.users[0]?location.href=`/user/${m.users[0].uid}`:c`un: unknown user "${h}".`})})].reduce((h,m)=>(h[m.name]=m,h),{}),x=h=>{M(`Parsing command: "${h}"`);let m=h.trim().replace(/^\//,"").split(" "),b=m.shift();if(!b)return;let w=f[b];if(!w)return c`exlg: unknown command "${b}"`;let k=-1,L;for([k,L]of m.entries()){let S=w.arg[k].type;if((S==="number"||S==="integer")&&(m[k]=+L),!(S==="char"&&L.length===1||S==="number"&&!isNaN(m[k])||S==="integer"&&!isNaN(m[k])&&!(m[k]%1)||S==="string"))return c`${b}: illegal param "${L}", expected type ${S}.`}if(w.arg[k+1]?.essential)return c`${b}: lost essential param "${w.arg[k+1].name}"`;w.fn(...m)};r.on("keydown",h=>{switch(h.key){case"Enter":if(n)return a();let m=r.val();if(o.push(m),i=o.length,x(m),!n)return a();break;case"/":n&&a();break;case"Escape":t.hide();break;case"ArrowUp":case"ArrowDown":let b=i+{ArrowUp:-1,ArrowDown:1}[h.key];if(b<0||b>=o.length)return;i=b,r.val(o[b]);break}}),g(A).on("keydown",h=>{let m=g(document.activeElement);if(m.is("body")){let b={ArrowLeft:"prev",ArrowRight:"next"}[h.key];if(b)return g(`a[rel=${b}]`)[0].click();if(h.shiftKey){let w={ArrowUp:0,ArrowDown:1e6}[h.key];w!==void 0&&A.scrollTo(0,w)}h.key==="/"&&(t.show(),a().trigger("focus"))}else m.is("[name=captcha]")&&h.key==="Enter"&&g("#submitpost, #submit-reply")[0].click()})},Sr,"module");var Mr=".exlg-high-risk{color:#dd514c}.exlg-med-risk{color:#ff5722}.exlg-low-risk{color:#8c8c8c}";v.reg("malicious-code-identifier","有害代码检查器",["@/discuss/\\d+(\\?page\\=\\d+)*$"],{strength:{ty:"number",dft:3,min:1,max:5,step:1,info:["Strength","强度"],strict:!0}},({msto:e})=>{let t=g("code").text().toLowerCase(),r=e.strength,n=[],o=t.match("system")&&!(t.match("System.out")||t.match("import java"));r>=1&&(o&&t.match("net user")&&n.push("高危 操作用户"),o&&t.match("shutdown")&&n.push("高危 关机"),o&&t.match("socksorkstation")&&n.push("高危 锁定桌面"),o&&t.match("reg add")&&n.push("高危 注册进程")),r>=2&&(o&&t.match("taskkill")&&n.push("危险 关闭进程"),o&&t.match("setcursorpos")&&n.push("危险 修改光标")),r>=3&&(t.match("windows.h")&&n.push("可疑 引用 windows.h"),o&&(n.push("可疑 调用系统函数"),o=!0),o&&(t.match("encode")||t.match("decode"))&&n.push("高危 存在加密字符串")),n.length!==0&&N(n.join("</br>").replaceAll("高危",'<a class = "exlg-high-risk">[高危]</a>').replaceAll("危险",'<a class = "exlg-med-risk">[危险]</a>').replaceAll("可疑",'<a class = "exlg-low-risk">[可疑]</a>'),"发现有害代码")},Mr,"module");var Ar="#exlg-messages-outter{z-index:129;position:fixed;right:1em;bottom:96px;border-radius:.6em;padding:6px;width:calc(18em + 12px);overflow:hidden}#exlg-messages-container{position:relative;width:100%;height:100%}.exlg-message-outter{background-color:#faf9fa;border-radius:.6em;width:18em;height:6em;box-shadow:0 0 6px #e1e1e2;position:relative;margin:.4em 0}.exlg-message-inner{width:100%;height:100%}.exlg-message-avatar{position:absolute;top:.8em;left:.8em;border-radius:100%;width:3.4em;height:3.4em}.exlg-message-username{position:absolute;top:.4em;left:4.7em;width:10.5em;font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.exlg-message-content{position:absolute;top:2em;left:4.7em;width:12em;line-height:1;word-break:break-all;text-overflow:ellipsis;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}.exlg-message-type{position:absolute;top:5.45em;left:1.1em;font-size:.8em;color:#bbbabb}.exlg-message-close{position:absolute;top:.4em;right:.4em;width:1.2em;height:1.2em;border-radius:100%;text-align:center;background-color:#f1f0f1;box-sizing:border-box;transition:ease-in-out background-color .1s;cursor:pointer}.exlg-message-close:hover{background-color:#e3e2e3}.exlg-message-close>svg{position:relative;top:40%;transform:translateY(-50%)}.exlg-message-show{display:block;color:#000;text-decoration:none;position:absolute;top:5.2em;right:.5em;font-size:.8em;padding-top:.1em;width:5em;height:1.7em;border-radius:.3em;text-align:center;background-color:#f1f0f1;box-sizing:border-box;transition:ease-in-out background-color .1s}.exlg-message-show:hover{background-color:#e3e2e3;color:#000}.exlg-message-show:visited{color:#000}";v.reg("messages","新消息提醒",["@/.*"],{message_tab_disappear_time:{ty:"number",dft:30,min:0,max:60,info:["The Time before message tab disappear (second)","信息自动消失时间(秒)"],strict:!0}},e=>{if(window.location.href.search("/chat")!==-1)return;let t=g("body").append('<div id="exlg-messages-outter"><div id="exlg-messages-container"></div></div>').find("#exlg-messages-container");function r(o){let i=g(`
        <div class="exlg-message-outter" id="exlg-message-${o.id}">
            <div class="exlg-message-inner">
                <a href="https://www.luogu.com.cn/user/${o.sender.uid}" target="_blank">
                    <img class="exlg-message-avatar" src="https://cdn.luogu.com.cn/upload/usericon/${o.sender.uid}.png">
                </a>
                <div class="exlg-message-username" id="exlg-message-username-${o.id}"></div>
                <div class="exlg-message-content" id="exlg-message-content-${o.id}"></div>
                <div class="exlg-message-type">私信</div>
            </div>
            <div class="exlg-message-close" msg-id="${o.id}" id="exlg-message-close-${o.id}">
                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-x" viewBox="0 0 16 16">
                    <path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/>
                </svg>
            </div>
            <a class="exlg-message-show" id="exlg-message-show-${o.id}" href="https://www.luogu.com.cn/chat" target="_blank">查看</a>
        </div>`);t.append(i).on("click",`#exlg-message-close-${o.id}, #exlg-message-show-${o.id}`,()=>{i.remove()}),g(`#exlg-message-username-${o.id}`).text(o.sender.name),g(`#exlg-message-content-${o.id}`).text(o.content),e.msto.message_tab_disappear_time!==0&&window.setTimeout(()=>{i.remove()},e.msto.message_tab_disappear_time*1e3)}Xt("exlg-message",o=>{let i=new WebSocket("wss://ws.luogu.com.cn/ws");i.onopen=()=>i.send(JSON.stringify({type:"join_channel",channel:"chat",channel_param:String(C.uid),exclusive_key:null})),i.onmessage=s=>{o.postMessage(s.data);let l=JSON.parse(s.data);l._ws_type==="server_broadcast"&&l.message instanceof Object&&l.message.sender.uid!==C.uid&&r(l.message)}},o=>{let i=JSON.parse(o);i._ws_type==="server_broadcast"&&i.message instanceof Object&&i.message.sender.uid!==C.uid&&r(i.message)});let n=o=>{let i=new BroadcastChannel(o);i.onmessage=s=>{M(o,": ",s.data)}};n("exlg-message-ctrl"),n("exlg-message-data")},Ar,"module");var Er='<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="door-open" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" class="svg-inline--fa fa-door-open fa-w-20"><path data-v-450d4937="" data-v-303bbf52="" fill="currentColor" d="M624 448h-80V113.45C544 86.19 522.47 64 496 64H384v64h96v384h144c8.84 0 16-7.16 16-16v-32c0-8.84-7.16-16-16-16zM312.24 1.01l-192 49.74C105.99 54.44 96 67.7 96 82.92V448H16c-8.84 0-16 7.16-16 16v32c0 8.84 7.16 16 16 16h336V33.18c0-21.58-19.56-37.41-39.76-32.17zM264 288c-13.25 0-24-14.33-24-32s10.75-32 24-32 24 14.33 24 32-10.75 32-24 32z"></path></svg>';var Nr="a.sol-btn{transition:opacity 2s cubic-bezier(.8,0,.9,-.03);opacity:0}a.sol-btn:hover{opacity:1}.item-row{display:none}";var zr=".exlg-back-to-contest{text-decoration:none;float:right;color:#e74c3c}.exlg-back-to-contest:hover{color:#e74c3c}";v.reg("dbc-jump","双击题号跳题","@/.*",null,()=>{g(document).on("dblclick",e=>{let t=window.getSelection().toString().trim().toUpperCase(),r=e.ctrlkey?`${g(".ops > a[href*=blog]").attr("href")}solution-`:"https://www.luogu.com.cn/problem/";ye(t)&&window.open(r+t)})},null,"module");v.reg("hide-solution","隐藏题解",["@/problem/[A-Z0-9]+","@/problem/solution/.*"],{hidesolu:{ty:"boolean",dft:!1,priv:!0},on:{ty:"boolean",dft:!1}},()=>/@\/problem\/[A-Z0-9]+/g.test(location.href)&&g('a[href^="/problem/solution"]').addClass("sol-btn"),Nr,"module");v.reg_hook_new("back-to-contest","返回比赛列表",["@/problem/[A-Z0-9]+\\?contestId=[1-9][0-9]{0,}"],null,({args:e})=>{let{$info_rows:t}=e,r=g('<a class="exlg-back-to-contest"></a>'),{cid:n}=e,{pid:o}=e;!o||!n||t.children(".exlg-back-to-contest").length>0||r.attr("href",`/contest/${n}#problems`).html(`${Er}返回列表`).appendTo(t)},e=>{let t=e.target,r=E.contest.id,{pid:n}=E.problem;return{args:{cid:r,pid:n,$info_rows:g(t.parentNode)},result:t.tagName.toLowerCase()==="a"&&(t.href||"").includes("/record/list")&&t.href.slice(t.href.indexOf("/record/list"))===`/record/list?pid=${n}&contestId=${r}`}},()=>({cid:E.contest.id,pid:E.problem.pid,$info_rows:g(".info-rows").parent()}),zr,"module");v.reg_hook_new("submission-color","记录难度可视化","@/record/list.*",null,e=>{(e.pid_taglist??document.body.querySelectorAll(".pid")).forEach(t=>{g(t).addClass("exlg-difficulty-color").addClass(`color-${_feInstance.currentData.records.result.filter(r=>r.problem.pid===t.innerText.trim())[0].problem.difficulty}`)})},e=>e.target&&e.target.tagName.toLowerCase()==="a"&&/^\/problem\/[A-Z][A-Z0-9]+$/.exec(new URL(e.target.href).pathname)?{result:!0,args:{pid_taglist:[e.target.firstChild]}}:{result:!1},()=>null,null,"module");v.reg("mainpage-discuss-limit","主页讨论个数限制",["@/"],{max_discuss:{ty:"number",dft:12,min:4,max:16,step:1,info:["Max Discussions On Show","主页讨论显示上限"],strict:!0}},({msto:e})=>{let t;location.href.includes("blog")||(g(".lg-article").each((r,n,o=g(n))=>{let i=n.childNodes[1];i&&i.tagName.toLowerCase()==="h2"&&i.innerText.includes("讨论")&&(t=o.children(".am-panel"))}),t.each((r,n,o=g(n))=>{r>=e.max_discuss&&o.hide()}))},null,"module");v.reg("user-css","自定义样式表",".*",{css:{ty:"string"}},({msto:e})=>GM_addStyle(e.css),null,"module");v.reg_v2({name:"original-difficulty",info:"显示原始难度",path:["@/problem/CF.*","@/problem/AT.*"],cate:"module"},{cf_src:{ty:"enum",dft:"codeforces.com",vals:["codeforces.com","codeforces.ml","codeforc.es"],info:["Codeforces problem source","CF 题目源"],migration:!0},atdiff:{ty:"string",priv:!0}},e=>{e.chore({name:"atdiff-fetch",info:"获取 AtCoder 难度",period:"10D"},null,({gsto:n})=>{let o={};ne({url:"https://kenkoooo.com/atcoder/resources/problem-models.json",onload:i=>{let s=JSON.parse(i.responseText);for(let l in s)o[l]=s[l].difficulty;n.atdiff=JSON.stringify(o)}})});let t=null;e.preload({name:"load-difficulty",info:"加载原始难度"},null,({gsto:n})=>t=new Promise((o,i)=>{let s=location.pathname.match(/(CF|AT)([0-9]|[A-Z])*$/g)[0].substring(2);if(location.pathname.includes("CF")){let l=s.match(/^[0-9]*/g)[0],d=s.substring(l.length);ne({url:`https://${n.cf_src}/problemset/problem/${l}/${d}`,onload:u=>{let c=g(u.responseText).find("span[title=Difficulty]").text().trim();o(c?c.substring(1):null)},onerror:u=>{U(u),i(u)}})}else{let l=JSON.parse(n.atdiff),d=E.problem.description.match(/^.{22}[-./A-Za-z0-9_]*/g)[0].match(/[^/]*$/g)[0];d in l?o(Math.round(l[d]>=400?l[d]:400/Math.exp(1-l[d]/400))):o(null)}}));let r="";e.hook({name:"difficulty-display",info:"显示原始难度"},null,({result:n,target:o})=>{if(!n)return;let i=g(o[3]),s=i.clone(!0);i.after(s);let[l,d]=s.find("span").get().map(u=>g(u));l.text("原始难度"),d.text("获取中"),t.then(u=>d.text(u??"不可用"))},n=>{if(r===location.href)return!1;let o=n.target.querySelectorAll&&n.target.querySelectorAll("div.stat > div.field");return o?.length>0?(r=location.href,{result:!0,target:o}):{result:!1}})});var Ir=".exlg-rand-settings{display:inline-block;padding:1px 5px;background-color:#fff;border:1px solid #6495ED;color:#6495ed;border-radius:6px;font-size:12px;position:relative;top:-2px}.exlg-rand-settings.selected{background-color:#6495ed;border:1px solid #6495ED;color:#fff}.exlg-rand-settings:hover{box-shadow:0 0 7px #1e90ff}.exlg-smallbtn{position:relative;display:inline-block;padding:1px 5px;color:#fff;border-radius:6px;font-size:12px;margin-left:1px;margin-right:1px}.exrand-enabled{width:49%;float:left}.exrand-disabled{width:49%;float:right}#exlg-dash-0>#exlg-exrand-window{width:250px;height:300px;z-index:6;background:white;color:#000;border-radius:7px;box-shadow:#bbe3ff 0 0 7px;visibility:hidden;opacity:0;transition:.233s ease all;position:absolute;left:-100px;top:20px;z-index:9;font-weight:initial}#exlg-dash-0:hover>#exlg-exrand-window{visibility:visible;opacity:1;transition-delay:unset}";v.reg("rand-problem-ex","随机跳题_ex","@/",{exrand_difficulty:{ty:"tuple",lvs:at(Array.from("11111111").map(e=>!!e),{ty:"boolean",strict:!0}),priv:!0},exrand_source:{ty:"tuple",lvs:at(Array.from("10000").map(e=>!!e),{ty:"boolean",strict:!0}),priv:!0}},({msto:e})=>{let t=[["入门","red"],["普及-","orange"],["普及/提高-","yellow"],["普及 +/提高","green"],["提高 +/省选-","blue"],["省选/NOI-","purple"],["NOI/NOI+/CTSC","black"],["暂无评定","gray"]].map((m,b,w)=>({text:m[0],color:m[1],id:(b+1)%w.length})),r=[{text:"洛谷题库",color:"red",id:"P"},{text:"Codeforces",color:"orange",id:"CF"},{text:"SPOJ",color:"yellow",id:"SP"},{text:"AtCoder",color:"green",id:"AT"},{text:"UVA",color:"blue",id:"UVA"}],n=m=>{ye(m)&&(m=m.toUpperCase()),m===""||typeof m>"u"?A.show_alert("提示","请输入题号"):location.href=`https://www.luogu.com.cn/problemnew/show/${m}`},o=g("input[name='toproblem']");o.after(o.clone()).remove(),o=g("input[name='toproblem']");let i=g(".am-btn[name='goto']");i.after(i.clone()).remove(),i=g(".am-btn[name='goto']");let s=i.parent(),l=g('<div id="exlg-dash-0" class="exlg-rand-settings">ex 设置</div>');g(".am-btn[name='gotorandom']").text("随机");let d=g('<button class="am-btn am-btn-success am-btn-sm" name="gotorandomex">随机 ex</button>').appendTo(s);i.on("click",()=>{/^[0-9]+.?[0-9]*$/.test(o.val())&&o.val(`P${o.val()}`),n(o.val())}),o.on("keydown",m=>{m.keyCode===13&&i.click()});let u=g(`<span id="exlg-exrand-window" class="exlg-window" style="display: block;">
    <br>
    <ul></ul>
    </span>`).appendTo(l).css({position:"absolute",left:"-100px",top:"20px","z-index":9,"font-weight":"initial"});g(".lg-index-stat>h2").text("问题跳转 ").append(l);let c=u.children("ul").css("list-style-type","none"),a=g('<div id="exlg-exrand-menu"></div>').appendTo(c);g("<br>").appendTo(c);let p=g('<div id="exlg-exrand-diff" class="smallbtn-list"></div>').appendTo(c),f=g('<div id="exlg-exrand-srce" class="smallbtn-list"></div>').appendTo(c).hide(),x=g.double(m=>g(`<div class="exlg-rand-settings exlg-unselectable exrand-entry">${m}</div>`).appendTo(a),"题目难度","题目来源");x[0].after(g('<span class="exlg-unselectable">&nbsp;&nbsp;</span>')),x[0].addClass("selected").css("margin-right","38px"),g.double(([m,b])=>{m.on("click",()=>{g(".exrand-entry").removeClass("selected"),m.addClass("selected"),g(".smallbtn-list").hide(),b.show()})},[x[0],p],[x[1],f]),g.double(([m,b,w])=>{let k=g.double(([L,S])=>g(`<span class="${L}">
        <span class="lg-small lg-inline-up exlg-unselectable">${S}</span>
        <br>
        </span>`).appendTo(m),["exrand-enabled","已选择"],["exrand-disabled","未选择"]);b.forEach((L,S)=>{let R=g.double(j=>g(`<div class="exlg-smallbtn exlg-unselectable">${L.text}</div>`).css("background-color",`var(--lg-${L.color}-problem)`).appendTo(j),k[0],k[1]);g.double(j=>{R[j].on("click",()=>{R[j].hide(),R[1-j].show(),w[S]=!!j}),w[S]===!!j&&R[j].hide()},0,1)})},[p,t,e.exrand_difficulty],[f,r,e.exrand_source]),g("#exlg-dash-0").on("mouseenter",()=>{g.double(([m,b])=>{g.double(([w,k])=>{m.children(w).children(".exlg-smallbtn").each((L,S,R=g(S))=>b[L]===k?R.show():R.hide())},[".exrand-enabled",!0],[".exrand-disabled",!1])},[p,e.exrand_difficulty],[f,e.exrand_source])});let h=async()=>{let m=g.double(([O,H,ee])=>{let fe=[];return O.forEach((We,Xe)=>{H[Xe]&&fe.push(We.id)}),fe.length||(fe=ee,O.forEach((We,Xe)=>H[Xe]=ee.includes(We.id))),fe[Math.floor(Math.random()*fe.length)]},[t,e.exrand_difficulty,[0,1,2,3,4,5,6,7]],[r,e.exrand_source,["P"]]),b=await X(`/problem/list?difficulty=${m[0]}&type=${m[1]}&page=1`),w=b.currentData.problems.count,k=Math.ceil(w/50),L=Math.floor(Math.random()*k)+1;b=await X(`/problem/list?difficulty=${m[0]}&type=${m[1]}&page=${L}`);let S=b.currentData.problems.result,R=Math.floor(Math.random()*S.length),{pid:j}=S[R];location.href=`/problem/${j}`};d.on("click",h)},Ir,"module");var jr=".exlg-rand-training-problem-btn{border-color:#343434;background-color:#343434}";v.reg_hook_new("rand-training-problem","题单内随机跳题","@/training/[0-9]+(#.*)?",{mode:{ty:"enum",vals:["unac only","unac and new","new only"],dft:"unac and new",info:["Preferences about problem choosing","随机跳题的题目种类"]}},({msto:e,args:t})=>{let r=e.mode.startsWith("unac")+e.mode.endsWith("only")*-1+2;!t.length||g(t[0].firstChild).clone(!0).appendTo(t).text("随机跳题").addClass("exlg-rand-training-problem-btn").on("click",()=>{let n=E.training,o=[];for(let s of n.problems)if(n.userScore===null)o.push(s.problem.pid);else{let l=n.userScore.score[s.problem.pid];(l===null&&r&1||l<s.problem.fullScore&&r&2)&&o.push(s.problem.pid)}if(!n.problemCount)return N("题单不能为空");if(!o.length)return r===1?N("您已经做完所有新题啦!"):r===2?N("您已经订完所有错题啦!"):N("您已经切完所有题啦!");let i=~~(Math.random()*1e6)%o.length;location.href=`https://www.luogu.com.cn/problem/${o[i]}`})},e=>{let t=g(e.target).find("div.operation");return{result:t.length>0,args:t}},()=>g("div.operation"),jr,"module");var Or="iframe{border:none;display:block;width:100%;height:100%}iframe::-webkit-scrollbar{display:none}";v.reg_main("springboard","跨域跳板",["@bili/robots.txt?.*","@/robots.txt?.*"],null,()=>{let e=new URLSearchParams(location.search);switch(e.get("type")){case"update":A.addEventListener("message",r=>{r.data.unshift("update"),A.parent.postMessage(r.data,"*")});break;case"page":let t=e.get("url");(!e.get("confirm")||confirm(`是否加载来自 ${t} 的页面?`))&&(document.body.innerHTML=`<iframe src="${t}" exlg="exlg"></iframe>`);break;case"dash":break}},Or);var Dr=".exlg-rand-tasklist-problem-btn{margin-left:.5em}";v.reg("tasklist-ex","任务计划 ex","@/",{auto_clear:{ty:"boolean",dft:!0,info:["Hide accepted problems","隐藏已经 AC 的题目"]},rand_problem_in_tasklist:{ty:"boolean",dft:!0,info:["Random problem in tasklist","任务计划随机跳题"]}},({msto:e})=>{let t=[];g.each(g("div.tasklist-item"),(i,s,l=g(s))=>{let d=l.attr("data-pid");s.innerHTML.search(/check/g)===-1&&e.rand_problem_in_tasklist&&t.push(d),l.find("i").hasClass("am-icon-check")&&l.addClass("tasklist-ac-problem")});let r=g('<div>[<a id="toggle-button">隐藏已 AC</a>]</div>');g("button[name=task-edit]").parent().after(r);let n=g(".tasklist-ac-problem"),o=g("#toggle-button").on("click",()=>{n.toggle(),o.text(`${["隐藏","显示"][+(e.auto_clear=!e.auto_clear)]}已 AC`)});if(e.auto_clear&&o.click(),e.rand_problem_in_tasklist){let i=g('<button name="task-rand" class="am-btn am-btn-sm am-btn-success lg-right">随机</button>');g("button[name='task-edit']").before(i),i.addClass("exlg-rand-tasklist-problem-btn").click(()=>{let s=~~(Math.random()*1e6)%t.length;location.href+=`problem/${t[s]}`})}},Dr,"module");v.reg_chore("token","exlg 令牌","10m","@/.*",{token:{ty:"string",priv:!0}},async({msto:e})=>{if(unsafeWindow._feInjection.currentUser){if(e.token){let r=(await K("https://exlg.piterator.com/token/ttl",{uid:unsafeWindow._feInjection.currentUser.uid,token:e.token})).data;if(r.status!==401&&r.data>=60*15)return!1}let t=(await K("https://www.luogu.com.cn/paste/new?_contentOnly",{data:(await st("https://exlg.piterator.com/token/generate")).data.data,public:!0},{"x-csrf-token":g("[name='csrf-token']").attr("content"),referer:"https://www.luogu.com.cn/paste"})).data.id;e.token=(await st(`https://exlg.piterator.com/token/verify/${t}`)).data.data.token,await K(`https://www.luogu.com.cn/paste/delete/${t}?_contentOnly`,{},{"x-csrf-token":g("[name='csrf-token']").attr("content"),referer:"https://www.luogu.com.cn/paste"})}else return!0});var Rr=`^M discussion-save
 : 更新了帖子保存的接口,现在能正确显示保存成功啦~
 : 离新版保存站发布越来越近了呢!是不是很期待?`;var Pr=".exlg-update-log-text{overflow-x:auto;white-space:nowrap;text-align:left}";v.reg_chore("update","检查更新","1D",".*",null,async()=>{let[e,t]=await Qe();t==="<<"&&N(`<p>检测到新版本 ${e},点击确定将安装。</p>`,"检测到新版本",()=>location.href="https://exlg.oss-cn-shanghai.aliyuncs.com/latest/dist/extend-luogu.min.user.js")});var Xo=["$","#<@"].map(e=>e.split("")),qr={Ty:{"-":"添加",x:"删减","!":"重大","*":"修改","^":"修复",$:"重构"},Op:{"-":"特性","?":"文档","#":"依赖","<":"代码风格",">":"命令",M:"模块",H:"钩子","@":"GitHub Action"}};v.reg("update-log","更新日志显示","@/.*",{last_version:{ty:"string",priv:!0},style:{ty:"enum",vals:["Commit Message","自然语言"],get:"id",info:["The way to display log","显示 Log 的方式"]},keep_dev:{ty:"boolean",dft:!0,info:["Keep developer messages","保留开发者更新信息"]}},({msto:e})=>{if(location.href.includes("blog"))return;let{version:t}=GM_info.script,r=!1,n=o=>{let i=`<div class="exlg-update-log-text" style="font-family: ${I["code-block-ex"].copy_code_font};">`;return o.split(`
`).forEach(s=>{let l=s.trimStart();if(l.length){if(e.keep_dev)r=!1;else if(l[0]===":"){if(!r)return}else if(Xo.some((d,u)=>d.includes(l[u]))){r=!1;return}else r=!0;e.style===1&&l[0]!==":"&&(s=" ".repeat(s.length-l.length)+qr.Ty[l[0]]+qr.Op[l[1]]+l.substring(2))}i+=`<div>${s.replace(/ /g,"&nbsp;")}</div>`}),`${i}</div>`};switch(pe(e.last_version,t)){case"==":break;case"<<":N(n(Rr),`extend-luogu ver. ${t} 更新日志`);case">>":e.last_version=t}},Pr,"module");var Br='<svg class="icon exlg-usercom-edit" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="942"><path d="M295.384615 708.923077h433.23077a19.692308 19.692308 0 1 1 0 39.384615h-433.23077a19.692308 19.692308 0 1 1 0-39.384615zM590.769231 303.576615L618.653538 275.692308l72.979693 73.019077-27.844923 27.844923L590.769231 303.576615z m-236.307693 196.923077L382.345846 472.615385l89.284923 89.324307-27.844923 27.844923L354.461538 500.499692zM401.329231 616.841846l308.342154-308.342154-55.689847-55.689846-308.381538 308.342154-8.152615 63.881846 63.881846-8.192z m-93.065846-74.043077l317.833846-317.833846a39.384615 39.384615 0 0 1 55.729231 0l55.689846 55.689846a39.384615 39.384615 0 0 1 0 55.689846l-317.833846 317.833847-127.763693 16.344615 16.344616-127.724308z" p-id="943"></path></svg>';var Ur=".exlg-usercom-edit{transition:all .4s;opacity:0;border-radius:.2em;margin-left:.2em;width:1em;height:1em;vertical-align:middle;fill:currentColor;overflow:hidden}.exlg-usercom-edit:hover{transition:all .4s;opacity:.8;background:rgba(0,0,0,.1)}.exlg-usercom-tag{color:#8a7b7b;font-weight:lighter;font-size:.9em;margin-left:.3em;line-height:1em;font-family:Consolas}";var V=null;v.reg_hook_new("user-comment","用户备注",".*",{comments:{ty:"string",dft:"{}",priv:!0},direct_display:{ty:"boolean",dft:!0,info:["Directly replace username","直接替换用户名"]}},({msto:e,result:t,args:r})=>{let n=(o,i,s=o.attr("exlg-usercom"))=>{if(!o||!o.length)return;if(o.length>1){o.each((d,u)=>n(g(u),i));return}let l=o;o.children("span[style]").length&&(l=o.children("span[style]")),e.direct_display?(l.css("white-space","pre"),l.text(i??s)):(l.children("span.exlg-usercom-tag").remove(),i&&l.append(`<span class="exlg-usercmt exlg-usercom-tag">(${i})</span>`))};if(!t){V=JSON.parse(e.comments);for(let[o,i]of Object.entries(V))(i===null||i==="")&&delete V[o];if(/^\/user\/[1-9][0-9]{0,}$/.test(location.pathname)){let o=g(".user-header-top > .user-info > div.user-name"),i=o[0].firstElementChild.innerText.trim(),s=+location.pathname.slice(6),l=g('<span id="exlg-name-text" style="font-size: medium;color: #ddd;" class="exlg-usercom-tag"></span>').appendTo(o);l.text(`(${s in V?V[s]:i})`),s in V||l.hide();let d=g(`<span title="修改用户备注">${Br}</span>`);o.append(d),d.on("click",()=>{N(`<div>请设置用户 <span style="font-family: Consolas;">${i} (uid: ${s})</span> 的备注名。<br/><small>留空则清除备注。</small></div><input exlg-badge-register type="text" style="font-family: Consolas;line-height: 1.5;padding: .1em;" class="am-form-field exlg-badge-input" placeholder="${i}" name="username" id="exlg-user-com-input">`,"exlg 用户备注",()=>{let u=g("#exlg-user-com-input").val();return u.trim()===""?(delete V[s],u=i,l.hide()):(V[s]=u.trim(),l.show()),n(g(`a[href="/user/${s}"][target=_blank]`),e.direct_display?V[s]??i:V[s],i),e.comments=JSON.stringify(V),l.text(`(${s in V?V[s]:i})`),!0}),g("#exlg-user-com-input").val(s in V?V[s]:i)})}}r.forEach(o=>{let i=g(o),s=o.href.split("/").at(-1);typeof i.attr("exlg-usercom")<"u"||(i.attr("exlg-usercom",i.text().trim()),(!s||s==="javascript:void 0")&&(s=g(o.parentNode.parentNode.previousElementSibling??o.parentNode.parentNode.parentNode.parentNode.parentNode.previousElementSibling).find("img").attr("src").replace(/[^0-9]/ig,"")),s in V&&n(i,V[s]))})},e=>{if(/^\/user\/[0-9]{0,}.*$/.test(location.pathname)&&(g(e.target).hasClass("feed")&&!g(e.target).hasClass("exlg-badge-feed")||/^#following/.test(location.hash)&&g(e.target).parent().hasClass("sub-body"))){let r=Array.from(e.target.querySelectorAll("a[target='_blank']")).filter(n=>!n.querySelectorAll("svg").length);return{result:r.length,args:r}}let t=e.target.querySelectorAll('a[href^="/user"][target=_blank]');return{result:t.length>0,args:t}},()=>{if(/^\/user\/[0-9]{0,}.*$/.test(location.pathname)){if(location.hash==="#activity")return Array.from(document.querySelectorAll(".feed .wrapper>a[target='_blank']")).filter(e=>!e.querySelectorAll("svg").length);if(/^#following/.test(location.hash))return Array.from(document.querySelectorAll(".follow-container .wrapper>a[target='_blank']")).filter(e=>!e.querySelectorAll("svg").length)}return document.querySelectorAll("a[target='_blank'][href^='/user/']")},Ur,"module");var Zr="iframe{border:none;display:block}iframe::-webkit-scrollbar{display:none}";v.reg_user_tab("user-intro-ins","用户首页_HTML_显示","main",null,null,()=>{function e(){g(".introduction > *").each(async(r,n,o=g(n))=>{let i,s=o.children()[0];g(s).length!==0&&s.nodeName==="A"?i=g(s).attr("href"):i=o.text();let[,,l,d]=i.match(/^(exlg.|%)([a-z|-]+):([^]+)$/)??[];if(!l)return;let u=d.split(/(?<!!)%/g).map(a=>a.replace(/!%/g,"%")),c=g(g(".user-action").children()[0]);if(l==="html")o.replaceWith(g(`<p exlg="exlg">${me.process(u[0])}</p>`));else if(l==="frame")o.replaceWith(Pe({type:"page",url:encodeURI(u[0]),confirm:!0},`width: ${u[1]}; height: ${u[2]};`));else if(l==="blog"){if(c.text().trim()!=="个人博客")return;c.attr("href",u),o.remove()}else if(l==="html-paste"){if(u[0].trim()==="")return;let a=await X(`/paste/${u[0].trim()}`);o.replaceWith(g(`<p exlg="exlg">${me.process(a.currentData.paste.data)}</p>`))}})}e();function t(r,n){return n===0?r:g(t(g(r.children()[0]),n-1))}t(g("section.main"),3).addClass("lg-editt"),g("button.lg-editt").on("click",()=>{let r=t(g("section.main"),2),n=g(r.children()[0]),o=g(r.children()[1]);n.addClass("lg-editt"),o.addClass("lg-editt"),e()})},Zr,"module");var gt={NOT_AT_PRACTICE_PAGE:-1,NONE:-2,COMMENT_TAG:-3,NOT_A_PROBLEM_ELEMENT:-4,PRIVATE_INFORMATION:-5,ADD_COMPARE:1};var Gr=".main>.card>h3{display:inline-block}";v.reg_v2({name:"user-problem-color",info:"题目颜色数量和比较",path:"@/user/[0-9]{0,}.*",cate:"module"},{problem_compare:{ty:"boolean",strict:!0,dft:!0,info:["AC compare","AC 题目比较"],migration:!0}},e=>{let t={SUBMITTED_PROBLEMS:0,PASSED_PROBLEMS:1},r=[[191,191,191],[254,76,97],[243,156,17],[255,193,22],[82,196,26],[52,152,219],[157,61,207],[14,29,105]],n,o,i=-1,s=t.SUBMITTED_PROBLEMS,l=-1;e.preload({name:"compare-problem",info:"比较通过的题目"},null,({gsto:d})=>{n=typeof E.submittedProblems>"u"||typeof E.passedProblems>"u",d.problem_compare&&!n&&(o=new Promise(u=>{X(`/user/${C.uid}`).then(c=>{u(new Set(c.currentData.passedProblems.map(a=>a.pid)))}).catch(c=>{U(c)})}))}),e.hook({name:"problem-color",info:"题目颜色"},null,({gsto:d,args:u})=>{if(n)return;let c=a=>`rgb(${r[a][0]},${r[a][1]},${r[a][2]})`;u.forEach(a=>{if(a.target.href!=="javascript:void 0"&&(a.target.style.setProperty("color",c([(a.board_id?E.passedProblems:E.submittedProblems)[a.position].difficulty]),"important"),a.board_id===t.PASSED_PROBLEMS&&a.position===E.passedProblems.length-1||E.passedProblems.length===0&&a.board_id===t.SUBMITTED_PROBLEMS&&a.position===E.submittedProblems.length-1)){g(".exlg-counter").remove();let p=a.target.parentNode.parentNode.parentNode.parentNode,f=[p.firstChild.childNodes[2],p.lastChild.childNodes[2]];for(let x=0;x<2;++x){let h=f[x],m=E[["submittedProblems","passedProblems"][x]];h.before(g(`<span id="exlg-problem-count-${x}" class="exlg-counter" exlg="exlg">${m.length}</span>`)[0])}if(!d.problem_compare||E.user.uid===C.uid)return;o.then(x=>{let h=0,m=f[1],b=E.passedProblems;m.querySelectorAll("a").forEach((w,k)=>{k<b.length&&x.has(b[k].pid)&&(h++,w.style.backgroundColor="rgba(82, 196, 26, 0.3)")}),g("#exlg-problem-count-1").html(`<span class="exlg-counter" exlg="exlg">${b.length} <> ${x.size} : ${h}<i class="exlg-icon exlg-info" name="ta 的 &lt;&gt; 我的 : 相同"></i></span>`)})}})},d=>{if(location.hash!=="#practice")return{result:!1};if(!E.submittedProblems.length&&!E.passedProblems.length){if(d.target.className==="card padding-default")if(g(d.target).children(".problems").length){let p=E[["submittedProblems","passedProblems"][l]];if(g(d.target.firstChild).after(`<span id="exlg-problem-count-${l}" class="exlg-counter" exlg="exlg" style="margin-left: 5px">${p.length}</span>`),++l>1)return{result:!0,args:{message:gt.ADD_COMPARE}}}else g(d.target).children(".difficulty-tags").length&&(l=0);return{result:!1,args:{message:gt.NONE}}}if(d.target.tagName.toLowerCase()!=="a"||d.target.className!=="color-default"||!d.target.pathname.startsWith("/problem/"))return{result:!1};let u=d.target,c=[E.submittedProblems[0]?.pid,E.passedProblems[0]?.pid].indexOf(u.href.slice(33)),a=c!==-1;return a?(s=c,i=0):i++,{result:!0,args:[{onchange:a,board_id:s,position:i,target:u}]}})},Gr);var Vr="#exlg-vp{margin-right:.5em;display:inline-block;flex:none;outline:0;cursor:pointer;color:#fff;font-weight:inherit;line-height:1.5;text-align:center;vertical-align:middle;background:0 0;border-radius:3px;border:1px solid;border-color:#52c41a;background-color:#52c41a}";v.reg("virtual-participation","创建重现赛","@/contest/[0-9]*(#.*)?",{},()=>{if(E.contest.name.match("Virtual Participation")){let e=g(".items"),t=()=>{(location.hash||"#main")==="#problems"&&g(".pid").length!==0&&E.contest.startTime>W()&&(g("a.title.color-default").on("click",()=>{N("比赛尚未开始,请开始后再查看题目")}),g("a.title.color-default").removeAttr("href"))};e.on("click",t),t();return}if(E.contest.endTime>W()){Q("Contest has not started or ended.");return}g("<button id='exlg-vp' class='lfe-form-sz-middle'>重现比赛</button>").appendTo(g("div.operation")).click(async()=>{N(`<div>
                <p>设置「${E.contest.name}」的重现赛</p>
                <p>开始时间:<input type="date" id="vpTmDt"/> <input type="time" id="vpTmClk"/></p>
            </div><br>`,"创建重现赛",async()=>{let e=g("#vpTmDt")[0].value.split("-"),t=g("#vpTmClk")[0].value.split(":"),r=new Date(e[0],e[1]-1,e[2],t[0],t[1],0,0);r=r.getTime()/1e3;let n=[],o={};g.each(E.contestProblems,(d,u)=>{n.push(String(u.problem.pid)),o[u.problem.pid]=u.problem.fullScore});let i=null;i=await we("/fe/api/contest/new",JSON.stringify({settings:{name:`Virtual Participation for ${E.contest.name}`,description:E.contest.description,visibilityType:5,invitationCodeType:1,ruleType:E.contest.ruleType,startTime:r,endTime:r+E.contest.endTime-E.contest.startTime,rated:!1,ratingGroup:null},hostID:C.uid}));let s=i.id.toString();(i.status??200)===200?s=i.id.toString():U(`Failed to modify contest ${s} with status code ${i.status}.`),i=null;try{i=await we(`/fe/api/contest/editProblem/${s}`,JSON.stringify({pids:n,scores:o}))}catch{return setTimeout(N("<p>本场比赛的题目不公开</p>","重现赛创建失败"),500),!1}let l=await X(`/contest/edit/${s}`);return await we(`/fe/api/contest/join/${s}`,`{"code": "${l.currentData.contest.joinCode}"}`),N(`<p>邀请码: ${l.currentData.contest.joinCode}</p>
                    <p>点确定自动跳转</p>`,"重现赛创建成功",()=>(location.href=`https://www.luogu.com.cn/contest/${s}`,!0)),!1})})},Vr,"module");v.reg("meta-discussion-guiding","元讨论引导",["@/discuss/lists\\?forumname=(siteaffairs|problem|academics|relevantaffairs|service).*"],{},()=>{let e="432028",t=[/exlg/i,/ex(tend)?[- ]luogu/i,/badge/i],r=g("#newpost"),n=g(".lg-input-title"),o=g("#submitpost"),i=o.wrap("<div></div>").parent(),s=`<a href="/discuss/${e}"><b>专贴</b></a>`;r.after(`<p>exlg 相关问题请在 ${s} 讨论</p>`),i[0].addEventListener("click",l=>{let d=A.markdownPalettes.content?.toLowerCase(),u=n.val(),c=t.map(a=>[d.match(a)?.[0],u.match(a)?.[0]]).flat().filter((a,p,f)=>a&&a!==f[p+1]);if(c.length){l.stopPropagation();let a;N(`检测到您将要发送的讨论内容包含与 exlg 有关的关键词:<br />${c.map(p=>`“${p}”`).join(", ")}<br />建议前往 ${s} 讨论。<br />这是为了防止占用讨论资源,营造一个更高质量的社区。 <br />我们很担心 exlg 相关讨论霸占版面,造成负面影响。<br /><span style="color: orange"><b>洛谷管理员提醒您:发布无意义讨论可能导致禁言。</b> <br /><small>* 无意义讨论包括但不限于“大家看得到我的 badge 吗” 等等</small></span> <br />如果您确定要发送,请在下方输入框键入 “放心” 后确定。<br/><small comment>(输入框过一会才会出现)</small>`,"exlg 提醒您",{onconfirm:()=>!a||a.val()!=="放心"?!1:(setTimeout(o.trigger("click"),500),!0),onopen:async({dom:{$main:p}})=>{await Jt(3e3),p.find("small[comment]").remove(),a=g('<input type="text" />').appendTo(p)}})}},!0)},"","module");v.reg_lfe("import-problem-to-cph","添加到 cph",["@/problem/[A-Z]+[0-9]+[A-Z]*(#.*)?(\\?contestId=[0-9]*)?","@/record/.*","@/contest/[0-9]+(#.*)?"],{auto_hide_button:{ty:"boolean",dft:!0,strict:!0,info:["Auto Hide Button","自动隐藏按钮"]},problem_template:{ty:"string",dft:"Luogu_${pid}",info:["Custom Problemname Template (Not Contest)","自定义题目名称格式(非比赛)"]},contest_template:{ty:"string",dft:"Luogu_${cid}_${pid}",info:["Custom Problemname Template (Contest)","自定义题目名称格式(比赛)"]}},async e=>{function t(i,s="problem",l=0){g.get(i,d=>{function u(f){return new DOMParser().parseFromString(f,"text/html").documentElement}let c;d=u(d);for(let f of d.querySelectorAll("script")){let x=f.textContent;if(x.startsWith("window._feInjection")){let h=x.indexOf('"'),m=x.substr(h+1).indexOf('"'),b=x.substr(h+1,m);c=JSON.parse(decodeURIComponent(b)).currentData.problem}}if(c==null)throw new Error("Failed to find problem data");let a=[];for(let f of c.samples)a.push({input:f[0],output:f[1]});let p=e.msto.problem_template;s==="contest"&&(p=e.msto.contest_template,p=p.replace("${cid}",`${l}`)),p=p.replace("${pid}",c.pid),GM_xmlhttpRequest({url:"http://localhost:27121/",method:"POST",data:JSON.stringify({batch:{id:"exlg",size:1},name:p,group:"Luogu",url:i,interactive:"false",memoryLimit:Math.floor(Math.max(...c.limits.memory)/1024),timeLimit:Math.max(...c.limits.time),tests:a,input:{type:"stdin"},output:{type:"stdout"},language:{java:{mainClass:"Main",taskClass:c.pid}},testType:"single"}),onload(f){f.status===502&&N("未启动 cph,传输失败!")},onerror(){N("未启动 cph,传输失败!")}})})}if(!g("div.operation").length||window.location.href.search("/record/")!==-1)return;let r=new Promise(i=>{GM_xmlhttpRequest({url:"http://localhost:27121/",method:"POST",onload(){i(!0)},onerror(){i(!1)}})});function n(){window.location.href.search("/contest/")!==-1?g("button.exlg-cph").click(async()=>{let{href:i}=window.location,s=i.search("#");s!==-1&&(i=i.substring(0,s));let l=await X(i);l.currentData.contestProblems===null&&N("比赛未开始!");let d=l.currentData.contestProblems;for(let u=0;u<d.length;u++)t(`https://www.luogu.com.cn/problem/${d[u].problem.pid}`,"contest",l.currentData.contest.id)}):g("button.exlg-cph").click(()=>{t(window.location.href)})}!await r&&e.msto.auto_hide_button||(g("button.lfe-form-sz-middle").addClass("lg-btm"),window.location.href.search("#submit")===-1&&g("button.exlg-cph").length===0&&(g("div.operation").append('<button data-v-7ade990c="" data-v-43063e73="" type="button" class="exlg-cph lfe-form-sz-middle" data-v-2dfcfd35="" style="border-color: rgb(52, 152, 219); background-color: rgb(52, 152, 219);"> 传送至 cph </button>'),n()),g("button.lg-btm").click(()=>{window.location.href.search("#submit")===-1?g("button.exlg-cph").length===0&&(g("button.lfe-form-sz-middle").addClass("lg-btm"),g("div.operation").append('<button data-v-7ade990c="" data-v-43063e73="" type="button" class="exlg-cph lfe-form-sz-middle" data-v-2dfcfd35="" style="border-color: rgb(52, 152, 219); background-color: rgb(52, 152, 219);"> 传送至 cph </button>'),n()):g("button.exlg-cph").remove()}))},null,"module");var Fr=e=>JSON.parse(JSON.stringify(e)),F=(e,t)=>{throw globalThis[e](`[ProxyData] ${t}`)},pt=()=>({get:GM_getValue,set:GM_setValue,del:GM_deleteValue,list:GM_listValues});var ni=e=>e?.__scm__?.ty?e.__scm__.ty:e===null?"null":Array.isArray(e)?"array":e instanceof RegExp?"regexp":typeof e,oi={string_number:e=>+e,string_regexp:e=>e,number_string:e=>""+e,number_boolean:e=>!!e},ii={object:{layer:1,container:()=>({})},tuple:{layer:1,container:()=>[]},array:{layer:2,container:()=>[],api:(e,t,r=t.__scm__,n=t.__tar__)=>({$new(o,i=1){for(let s=o+i;o<s;o++){let l=e.scm.lvs[s];l&&F("ReferenceError",`Leaf @ ${l.path} already exists, but was attempted to re-new.`),Je(e,s,t,n,!0)}},get $length(){return r.lvs.length},$push(...o){return o.forEach(i=>t[r.lvs.length]=i),r.lvs.length},$fill(o,i=0,s=r.lvs.length){for(;i<s;i++)t[i]=o;return t},$pop(){let o=r.lvs.length,i=t[o-1];return delete t[o-1],r.lvs.length--,i},$splice(o,i){let s=r.lvs.length;for(i??=s,i=Math.min(s-o,i);o<s;o++)t[o]=o+i<s?t[o+i]:void 0;r.lvs.length-=i},$swap(o,i){t.__tmp__=t[o],t[o]=t[i],t[i]=t.__tmp__,delete t.__tmp__},$reverse(){let o=r.lvs.length,i=Math.floor(o/2);for(let s=0;s<i;s++)s in r.lvs&&t.$swap(s,o-s-1);return t},$includes(o){let i=r.lvs.length;for(let s=0;s<i;s++)if(s in r.lvs&&o===t[s])return!0;return!1},$indexOf(o){let i=r.lvs.length;for(let s=0;s<i;s++)if(s in r.lvs&&o===t[s])return+s;return-1},$lastIndexOf(o){for(let i=r.lvs.length-1;i>=0;i--)if(i in r.lvs&&o===t[i])return+i;return-1},$find(o){let i=r.lvs.length;for(let s=0;s<i;s++)if(s in r.lvs){let l=t[s];if(o(l))return l}},$findIndex(o){let i=r.lvs.length;for(let s=0;s<i;s++)if(s in r.lvs){let l=t[s];if(o(l))return+s}},$forEach(o){let i=r.lvs.length;for(let s=0;s<i;s++)s in r.lvs&&o(t[s],+s,t)},$at(o){return o<0?t[r.lvs.length+o]:t[o]},*[Symbol.iterator](){for(let o in r.lvs)yield t[o]}})}},Je=(e,t,r,n,o)=>{let{data:i,map:s,scm:l,oldRoot:d,old:u,access:c}=e;o&&(l.lvs[t]=Fr(l.itm));let a=l.lvs[t];a.path=(l.path??"")+"."+t;let p=ii[a.ty];a.layer=p?.layer??0,a.layer&&(i[t]=p.container(),a.layer>1&&(a.lvs=p.container()));let f=h=>JSON.stringify(h,null,2)+": ";a.ty==="enum"&&(a.get??="val",a.set??="val",a.fromOld=h=>{let m=a.set;a.set="id",r[t]=h,a.set=m},a.get!=="both"&&a.set==="both"&&F("SyntaxError",f(a)+"{ ty: 'enum' → ¬(get: 'both' ∧ set: ¬ 'both') }")),a.ty==="tuple"&&(a.lvs=a.lvs.flatMap(h=>{let m=1;return"repeat"in h&&(m=h.repeat,(typeof m!="number"||m<0||m%1)&&F("SyntaxError",f(a)+"{ ty: 'tuple' → itm: [ ∀ i: { repeat?: integer } } ]"),delete h.repeat),Array.from({length:m},()=>Fr(h))})),s(a),a.pathRoot=a.root?"#"+a.path:l.pathRoot??t,a.raw=(a.root?null:l.raw)??(()=>i[t]);let x={data:i[t],map:s,scm:a,oldRoot:d,old:u?.[t],access:c};if(a.layer)n[t]=Hr(x);else{let h=a.root?d[a.pathRoot]:u?.[t];h!==void 0?a.ty==="enum"?a.fromOld(h):r[t]=h:"dft"in a&&(r[t]=a.dft)}p?.api&&(a.api=p.api(x,n[t]))},Hr=e=>{let{data:t,scm:r,oldRoot:n,old:o,access:i}=e,s={},l=`Parent ${r.ty} @ ${r.path}`,d=c=>{if(typeof c!="symbol"&&r.ty==="array"){let a=l+` requires the index to be in [ ${r.minIdx??=0}, ${r.maxIdx??=1/0} ], but got ${c}. `;(c<r.minIdx||c>r.maxIdx)&&F("RangeError",a)}},u=new Proxy(s,{get:(c,a)=>{if(a==="__scm__")return r;if(a==="__tar__")return s;if(r.api&&a in r.api)return r.api[a];if(d(a),!r.lvs[a])switch(r.layer){case 1:F("TypeError",l+` doesn't have leaf ${a}.`);break;case 2:return}let p=r.lvs[a];if(p.ty==="enum")switch(p.get){case"id":return s[a];case"val":return p.vals[s[a]];case"both":return{get id(){return s[a]},set id(f){let x=p.set;p.set="id",u[a]=f,p.set=x},get val(){return p.vals[s[a]]},set val(f){let x=p.set;p.set="val",u[a]=f,p.set=x}}}return s[a]},set:(c,a,p)=>{if(d(a),!r.lvs[a])switch(r.layer){case 1:F("TypeError",l+` doesn't have leaf ${a}.`);break;case 2:Je(e,a,u,s,!0);break}r.api&&a in r.api&&F("TypeError",l+` has API ${a}. Failed.`);let f=r.lvs[a],x=`Leaf @ ${f.path}`,h="Failed strictly.",m=x+` is ${["simple","fixed complex","flexible complex"][f.layer]} type, `;f.locked&&F("TypeError",x+" is locked, but was attempted to modify.");let b=ni(p);if(b==="undefined"){if(r.layer===1&&!r.del&&F("TypeError",m+"but its "+x+" was attempted to delete."),f.layer){f.del=!0;for(let w in f.lvs)delete s[a][w]}delete r.lvs[a]}else if(b==="array"&&f.lvs instanceof Array||b==="object"&&f.lvs&&!(f.lvs instanceof Array)){for(let w in f.lvs)s[a][w]=p[w];return!0}else if(!["any","enum"].includes(f.ty)&&b!==f.ty){let w=x+` requires type ${f.ty}, but got ${b}: ${p}. `;f.strict&&F("TypeError",w+h);let k=oi[`${b}_${f.ty}`];k?p=k(p):F("TypeError",w+"Failed to convert.")}if(f.ty==="number"){let w=x+` requires to be in [ ${f.min??-1/0}, ${f.max??1/0} ], but got ${p}. `;(p<f.min||p>f.max)&&F("RangeError",w),f.int&&p%1&&(f.strict&&F("RangeError",x+" requires to be an integer. "+h),p=Math.floor(p))}else if(f.ty==="enum")switch(f.set){case"id":(typeof p!="number"||!p in f.vals)&&F("RangeError",x+` requires to be an enum index in [ ${0}, ${f.vals.length} ], but got ${p}.`);break;case"val":p=f.vals.findIndex(w=>w===p),p<0&&F("RangeError",x+` requires to be in the enum { ${f.vals.join(", ")} }, but got ${p}.`);break;case"both":F("TypeError",x+" is an enum accepting both id and value ways of modification, but was attempted to modify without using any setter.")}if(s[a]=t[a]=p,f.quick||f.root){let w=f.raw();w===void 0?i.del(f.pathRoot):i.set(f.pathRoot,w)}return!0},deleteProperty:(c,a)=>(u[a]=void 0,!0),has:(c,a)=>a in r.lvs});switch(r.layer){case 1:for(let a in r.lvs)Je(e,a,u,s);break;case 2:(r.itmRoot?Object.keys(n).map(a=>a.match(String.raw`^#${r.path}\.([^.]+)`)?.[1]).filter(a=>a):Object.keys(o??{})).forEach(a=>Je(e,a,u,s,!0));break}return u},Se=class{loadData(t,{access:r,autoSave:n,old:o,map:i}){return this.raw&&F("Error","ProxyData cannot be loaded multiple times."),this.raw={},this.access=r,o??=r.list().reduce((s,l)=>(s[l]=r.get(l),s),{}),n&&window.addEventListener("beforeunload",()=>this.saveData()),Hr({data:this.raw,scm:{lvs:t,layer:1},map:i??(s=>s),old:o,oldRoot:o,access:r})}saveData(t=this.raw){Object.keys(t).forEach(r=>this.access.set(r,t[r]))}clearData(){this.raw=null,this.access.list().forEach(this.access.del)}};M("Exposing");var Jr=new Se,Qr=Jr.loadData({settingVersion:{ty:"number",dft:0}},{access:pt()});Qr.settingVersion===0&&(GM_listValues().every(e=>typeof GM_getValue(e)=="string")&&GM_listValues().forEach(e=>GM_setValue(e,JSON.parse(GM_getValue(e)))),Qr.settingVersion=1,Jr.saveData());var Wr=new Se,Xr=Wr.loadData(J,{access:pt(),map:e=>{e.root=!e.rec,e.itmRoot=e.rec===2}});v.fake_sto=oe.sto=Xr;Object.assign(A,{exlg:{mod:v,compo:oe,scm:J,log:M,error:U,queues:ce,springboard:Pe,version_cmp:pe,lg_alert:Wt,lg_content:X,register_badge:He,lg_post:we,exlg_alert:N,xss:me,pd:Wr,sto:Xr},GM:{GM_info,GM_addStyle,GM_setClipboard,GM_xmlhttpRequest,GM_getValue,GM_setValue,GM_deleteValue,GM_listValues},$$:g});v.preload();v.execute_v2();ce.preload.apply();g(()=>{M("Launching"),ce.onload.apply()});})();

2. Luogu Search AnyWhere

// ==UserScript==
// @name         Luogu Search AnyWhere
// @namespace    https://greasyfork.org/users/829530
// @version      0.3.4
// @description  Search AnyWhere in Luogu!
// @author       tiger2005
// @match        https://www.luogu.com.cn/*
// @icon         https://cdn.luogu.com.cn/upload/usericon/3.png
// @grant        none
// @license      MIT
// @require      https://code.jquery.com/jquery-3.6.0.min.js
// @downloadURL https://update.greasyfork.org/scripts/442416/Luogu%20Search%20AnyWhere.user.js
// @updateURL https://update.greasyfork.org/scripts/442416/Luogu%20Search%20AnyWhere.meta.js
// ==/UserScript==
 
(function($, undefined) {
    'use strict';
    $(function(){
    Date.prototype.pattern = function(format) {
        var date = {
            "y+": this.getYear(),
            "M+": this.getMonth() + 1,
            "d+": this.getDate(),
            "h+": this.getHours(),
            "m+": this.getMinutes(),
            "s+": this.getSeconds(),
            "q+": Math.floor((this.getMonth() + 3) / 3),
            "S+": this.getMilliseconds()
        };
        if (/(y+)/i.test(format)) {
            format = format.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length));
        }
        for (var k in date) {
            if (new RegExp("(" + k + ")").test(format)) {
                format = format.replace(RegExp.$1, RegExp.$1.length == 1
                  ? date[k] : ("00" + date[k]).substr(("" + date[k]).length));
            }
        }
        return format;
    };
    var settings = localStorage.getItem("lsaw_settings");
    if(settings == undefined || settings == 'undefined')
        settings = {};
    else
        settings = JSON.parse(settings);
    var majorSettings = [
        ["lsawUserDisplay", true],
        ["lsawProblemDisplay", true],
        ["lsawOfficialListDisplay", false],
        ["lsawSelectListDisplay", false],
        ["lsawProblemDisplayNumber", 50],
        ["lsawListDisplayNumber", 50],
    ];
    for(var i=0; i<majorSettings.length; i++){
        var key = majorSettings[i][0], val = majorSettings[i][1];
        if(settings[key] == undefined || typeof(settings[key]) != typeof(val))
            settings[key] = val;
    }
    localStorage.setItem("lsaw_settings", JSON.stringify(settings));
    $("body").append(`
        <style>
            .searchAnywhere{
                position: fixed;
                top: 0px;
                left: 0px;
                height: 100%;
                width: 100%;
                background-color: rgba(0, 0, 0, 0.8);
                z-index: 999;
                transition: 0.2s;
                color: white;
            }
            .searchAnywhereMain{
                height: min(600px, 100% - 10px);
                width: min(750px, 100% - 10px);
                position: absolute;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                display: flex;
                flex-direction: column;
            }
            .inputArea{
                display: block;
                width: 100%;
                height: 48px;
                color: #aaa;
                position: relative;
                transition: 0.2s;
                margin-bottom: 10px;
            }
            .inputArea > input{
                border-radius: 5px;
                border: 2px solid #aaa;
                height: 48px;
                width: 100%;
                font-size: 18px;
                color: #aaa;
                padding: 14px 24px;
                outline: 0;
                background: transparent;
                box-sizing: border-box;
            }
            .inputArea.onHover > input, .inputArea.onFocus > input{
                border: 2px solid white;
            }
            .inputArea.onHover, .inputArea.onFocus, .inputArea.withContent{
                color: white !important;
            }
            .inputArea.onHover > input, .inputArea.onFocus > input, .inputArea.withContent > input{
                color: white !important;
            }
            .inputArea.withIconLeft > input{
                padding-left: 42px;
            }
            .inputArea.withIconRight > input{
                padding-right: 42px;
            }
            .inputArea > div.iconLeft{
                height: 48px;
                width: 48px;
                position: absolute;
                display: grid;
                place-items: center;
                top: 0px;
                left: 0px;
            }
            .inputArea > div.iconRight{
                height: 48px;
                width: 48px;
                position: absolute;
                display: grid;
                place-items: center;
                top: 0px;
                right: 0px;
            }
            .inputArea > div.iconLeft > svg{
                width: 20px !important;
                height: 20px !important;
            }
            .inputArea > div.iconRight > svg{
                width: 20px !important;
                height: 20px !important;
            }

            .inputAreaSmall{
                display: block;
                width: 100%;
                height: 30px;
                color: #aaa;
                position: relative;
                transition: 0.2s;
            }
            .inputAreaSmall > input{
                border-radius: 5px;
                border: 2px solid #aaa;
                height: 30px;
                width: 100%;
                font-size: 18px;
                color: #aaa;
                padding: 5px 10px;
                outline: 0;
                background: transparent;
                box-sizing: border-box;
            }
            .inputAreaSmall.onHover > input, .inputAreaSmall.onFocus > input{
                border: 2px solid white;
            }
            .inputAreaSmall.onHover, .inputAreaSmall.onFocus, .inputAreaSmall.withContent{
                color: white !important;
            }
            .inputAreaSmall.onHover > input, .inputAreaSmall.onFocus > input, .inputAreaSmall.withContent > input{
                color: white !important;
            }

            .userPurple{
                color: #cf5bff;
                font-weight: bold;
            }
            .userRed{
                color: #e74c3c;
                font-weight: bold;
            }
            .userOrange{
                color: #e67e22;
                font-weight: bold;
            }
            .userYellow{
                color: #d9a71d;
                font-weight: bold;
            }
            .userGreen{
                color: #5eb95e;
                font-weight: bold;
            }
            .userGray{
                color: #aaa;
                font-weight: bold;
            }
            .userCheater{
                color: #d3961c;
                font-weight: bold;
            }
            .userBlue{
                color: #07a2f1;
                font-weight: bold;
            }
            .userGold{
                color: #f1c40f;
                font-weight: bold;
            }
            .badgePurple{
                background-color: #cf5bff;
            }
            .badgeRed{
                background-color: #e74c3c;
            }
            .badgeOrange{
                background-color: #e67e22;
            }
            .badgeYellow{
                background-color: #d9a71d;
            }
            .badgeGreen{
                background-color: #5eb95e;
            }
            .badgeGray{
                background-color: #999;
            }
            .badgeCheater{
                background-color: #d3961c;
            }
            .badgeBlue{
                background-color: #07a2f1;
            }
            .badgeBlack{
                background-color: #0e1d69;
            }
            .badgeGold{
                background-color: #f1c40f;
            }
            .searchAnywhereContent{
                color: white;
                flex: 1;
                scrollbar-width: none;
                -ms-overflow-style: none;
                overflow-x: hidden;
                overflow-y: auto;
            }
            .searchAnywhereContent::-webkit-scrollbar { width: 0 !important; }
            .searchUserCard{
                background: #444;
                border-radius: 10px;
                display: flex;
                flex-direction: column;
                cursor: pointer;
                color: white;
                padding: 10px;
                line-height: 1;
                margin-bottom: 10px;
                border: 2px solid #888;
                box-sizing: border-box;
            }
            .searchCard.light{
                border: 2px solid white;
            }
            .searchUserCard > div{
                width: 100%;
            }
            .searchUserCardBody{
                display: flex;
                flex-direction: row;
            }
            .searchUserCardImg{
                height: 36px;
                width: 36px;
                border-radius: 50%;
                margin-right: 10px;
            }
            .searchUserCardInfo > span:first-child{
                font-size: 14px;
                margin-bottom: 3px;
                display: inline-block;
                color: #bbb;
            }
            .searchUserCardInfo > span:last-child{
                font-size: 20px;
            }
            .searchUserCardMedia{
                display: flex;
                flex-direction: row;
            }
            .searchUserCardMedia > div{
                margin-top: 5px;
                flex: 1;
                display: inline-block;
                height: 23px;
                verticle-align: center;
            }
            .searchUserCardMedia > div > div{
                padding: 4px;
                position: relative;
                display: inline-block;
                background: #777;
                margin-right: 15px;
            }
            .searchUserCardMedia > div > div:after{
                width: 10px;
                height: 100%;
                content: "";
                border: 5px;
                position: absolute;
                top: 0px;
                left: 100%;
                clip-path: polygon(0 0,100% 50%,0 100%);
                background-color: inherit;
            }
            .userBadgeInfo{
                font-size: 14px;
                padding: 2px 5px;
                border-radius: 5px;
                color: white;
                font-weight: bold;
                margin: 0px 3px;
                display: inline-block;
            }
            .searchProblemCard{
                background: #444;
                border-radius: 10px;
                display: flex;
                flex-direction: column;
                cursor: pointer;
                color: white;
                padding: 10px;
                line-height: 1;
                margin-bottom: 10px;
                border: 2px solid #888;
                box-sizing: border-box;
            }
            .searchProblemCard > div{
                width: 100%;
                display: flex;
                flex-direction: row;
            }
            .searchProblemCard > div:last-child{
                margin-top: 5px;
            }
            .searchProblemCardTag{
                margin-right: 12px;
                display: inline-flex;
                flex-direction: row;
                line-height: 24px;
                height: 24px;
                overflow: hidden;
            }
            .searchProblemCardTag > div{
                padding: 2px;
                position: relative;
                display: inline-block;
                margin-right: 5px;
            }
            .searchListCardTag{
                margin-right: 12px;
                display: inline-flex;
                flex-direction: row;
                line-height: 24px;
                height: 24px;
                overflow: hidden;
            }
            .searchListCardTag > div{
                padding: 2px;
                position: relative;
                display: inline-block;
                margin-right: 5px;
            }
            .searchListCardTag svg, .searchProblemCardTag svg{
                height: 20px !important;
                width: 20px !important;
            }
            .problemTagInfo{
                font-size: 16px;
                padding: 4px 7px;
                border-radius: 5px;
                color: white;
                display: inline-block;
            }
            .searchProblemCardBody > div:first-child{
                margin-right: 5px;
            }

            .searchListCard{
                background: #444;
                border-radius: 10px;
                display: flex;
                flex-direction: column;
                cursor: pointer;
                color: white;
                padding: 10px;
                line-height: 1;
                margin-bottom: 10px;
                border: 2px solid #888;
                box-sizing: border-box;
                position: relative;
            }
            .searchListCard > div{
                width: 100%;
                display: flex;
                flex-direction: row;
            }
            .searchListCardBody > div:first-child{
                margin-right: 5px;
            }
            .searchListCardBody{
                margin-bottom: 5px;
            }

            .searchAnywhereSettings{
                position: fixed;
                top: 0px;
                left: 0px;
                height: 100%;
                width: 100%;
                background-color: rgba(0, 0, 0, 0.8);
                z-index: 1000;
                transition: 0.2s;
                color: white;
            }
            .searchAnywhereSettingsMain{
                height: min(300px, 100% - 10px);
                width: min(450px, 100% - 10px);
                position: absolute;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                display: flex;
                flex-direction: column;
            }
            .searchAnywhereSettingsContent{
                flex: 1;
                scrollbar-width: none;
                -ms-overflow-style: none;
                overflow-x: hidden;
                overflow-y: auto;
            }
            .searchAnywhereSettingsBag{
                margin-bottom: 8px;
                user-select: none;
                font-size: 20px;
                position: relative;
                line-height: 30px;
            }
            .searchAnywhereClicky{
                position: relative;
                height: 30px;
                width: 30px;
            }
            .searchAnywhereClicky div{
                 transition: 0.2s;
                 position: absolute;
                 top: 0px;
                 left: 0px;
                 cursor: pointer;
            }
            .searchAnywhereClicky div svg{
                height: 30px;
                width: 30px;
            }
            .searchAnywhereClicky[flag=false] .falseBlock{
                opacity: 1;
            }
            .searchAnywhereClicky[flag=true] .falseBlock{
                opacity: 0;
            }
            .searchAnywhereClicky[flag=true] .trueBlock{
                opacity: 1;
            }
            .searchAnywhereClicky[flag=false] .trueBlock{
                opacity: 0;
            }
            .searchAnywhereSettingsContent::-webkit-scrollbar { width: 0 !important; }
            .searchAnywhereCloseSettings{
                display: inline-grid;
                place-items: center;
                width: 32px;
                height: 32px;
                cursor: pointer;
                border-radius: 50%;
                background: white;
            }
            .searchAnywhereCloseSettings:hover{
                background: #aaa;
            }
            .searchAnywhereEntrance{
                z-index: 99;
                background: white;
                display: grid;
                place-items: center;
                width: 50px;
                height: 50px;
                position: fixed;
                right: 20px;
                bottom: 30px;
                transition: 0.2s;
                border-radius: 5px;
                cursor: pointer;
            }
            .searchAnywhereEntrance:hover{
                background: #ccc;
            }
            .searchAnywhereEntrance svg{
                height: 30px;
                width: 30px;
            }
            .searchCard.light, .searchCard{
                color: white !important;
            }
            .searchAnywhereContent > div > div > a:hover{
                color: #83c4ef;
            }
        </style>
        <div class='searchAnywhere' style="opacity: 0; display: none;">
            <div class='searchAnywhereMain'>
                <div class='inputArea withIconLeft withIconRight searchAnywhereMainInput'>
                    <input spellcheck="false" placeholder="Search AnyWhere"/>
                    <div class="iconLeft"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="svg-inline--fa fa-search fa-w-24"><path data-v-1ad550c8="" data-v-303bbf52="" fill="currentColor" d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z" class=""></path></svg></div>
                    <div class="iconRight"><svg style="cursor: pointer" class="searchAnywhereSettingsLink" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" aria-hidden="true" role="img"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M495.9 166.6C499.2 175.2 496.4 184.9 489.6 191.2L446.3 230.6C447.4 238.9 448 247.4 448 256C448 264.6 447.4 273.1 446.3 281.4L489.6 320.8C496.4 327.1 499.2 336.8 495.9 345.4C491.5 357.3 486.2 368.8 480.2 379.7L475.5 387.8C468.9 398.8 461.5 409.2 453.4 419.1C447.4 426.2 437.7 428.7 428.9 425.9L373.2 408.1C359.8 418.4 344.1 427 329.2 433.6L316.7 490.7C314.7 499.7 307.7 506.1 298.5 508.5C284.7 510.8 270.5 512 255.1 512C241.5 512 227.3 510.8 213.5 508.5C204.3 506.1 197.3 499.7 195.3 490.7L182.8 433.6C167 427 152.2 418.4 138.8 408.1L83.14 425.9C74.3 428.7 64.55 426.2 58.63 419.1C50.52 409.2 43.12 398.8 36.52 387.8L31.84 379.7C25.77 368.8 20.49 357.3 16.06 345.4C12.82 336.8 15.55 327.1 22.41 320.8L65.67 281.4C64.57 273.1 64 264.6 64 256C64 247.4 64.57 238.9 65.67 230.6L22.41 191.2C15.55 184.9 12.82 175.3 16.06 166.6C20.49 154.7 25.78 143.2 31.84 132.3L36.51 124.2C43.12 113.2 50.52 102.8 58.63 92.95C64.55 85.8 74.3 83.32 83.14 86.14L138.8 103.9C152.2 93.56 167 84.96 182.8 78.43L195.3 21.33C197.3 12.25 204.3 5.04 213.5 3.51C227.3 1.201 241.5 0 256 0C270.5 0 284.7 1.201 298.5 3.51C307.7 5.04 314.7 12.25 316.7 21.33L329.2 78.43C344.1 84.96 359.8 93.56 373.2 103.9L428.9 86.14C437.7 83.32 447.4 85.8 453.4 92.95C461.5 102.8 468.9 113.2 475.5 124.2L480.2 132.3C486.2 143.2 491.5 154.7 495.9 166.6V166.6zM256 336C300.2 336 336 300.2 336 255.1C336 211.8 300.2 175.1 256 175.1C211.8 175.1 176 211.8 176 255.1C176 300.2 211.8 336 256 336z"/></svg></div>
                </div>
                <div class='searchAnywhereContent'>
                </div>
            </div>
        </div>
        <div class='searchAnywhereSettings' style="opacity: 0; display: none;">
            <div class='searchAnywhereSettingsMain'>
                <div class="searchAnywhereSettingsContent">
                    <div>
                        <div class="searchAnywhereSettingsBag" for="lsawUserDisplay">
                            显示用户
                            <div style="float: right" class="searchAnywhereClicky" flag="${settings.lsawUserDisplay}">
                                <div class="falseBlock">
                                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M384 32C419.3 32 448 60.65 448 96V416C448 451.3 419.3 480 384 480H64C28.65 480 0 451.3 0 416V96C0 60.65 28.65 32 64 32H384zM384 80H64C55.16 80 48 87.16 48 96V416C48 424.8 55.16 432 64 432H384C392.8 432 400 424.8 400 416V96C400 87.16 392.8 80 384 80z"/></svg>
                                </div>
                                <div class="trueBlock">
                                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M384 32C419.3 32 448 60.65 448 96V416C448 451.3 419.3 480 384 480H64C28.65 480 0 451.3 0 416V96C0 60.65 28.65 32 64 32H384zM339.8 211.8C350.7 200.9 350.7 183.1 339.8 172.2C328.9 161.3 311.1 161.3 300.2 172.2L192 280.4L147.8 236.2C136.9 225.3 119.1 225.3 108.2 236.2C97.27 247.1 97.27 264.9 108.2 275.8L172.2 339.8C183.1 350.7 200.9 350.7 211.8 339.8L339.8 211.8z"/></svg>
                                </div>
                            </div>
                        </div>
                        <div class="searchAnywhereSettingsBag" for="lsawProblemDisplay">
                            显示题目
                            <div style="float: right" class="searchAnywhereClicky" flag="${settings.lsawProblemDisplay}">
                                <div class="falseBlock">
                                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M384 32C419.3 32 448 60.65 448 96V416C448 451.3 419.3 480 384 480H64C28.65 480 0 451.3 0 416V96C0 60.65 28.65 32 64 32H384zM384 80H64C55.16 80 48 87.16 48 96V416C48 424.8 55.16 432 64 432H384C392.8 432 400 424.8 400 416V96C400 87.16 392.8 80 384 80z"/></svg>
                                </div>
                                <div class="trueBlock">
                                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M384 32C419.3 32 448 60.65 448 96V416C448 451.3 419.3 480 384 480H64C28.65 480 0 451.3 0 416V96C0 60.65 28.65 32 64 32H384zM339.8 211.8C350.7 200.9 350.7 183.1 339.8 172.2C328.9 161.3 311.1 161.3 300.2 172.2L192 280.4L147.8 236.2C136.9 225.3 119.1 225.3 108.2 236.2C97.27 247.1 97.27 264.9 108.2 275.8L172.2 339.8C183.1 350.7 200.9 350.7 211.8 339.8L339.8 211.8z"/></svg>
                                </div>
                            </div>
                        </div>
                        <div class="searchAnywhereSettingsBag" for="lsawOfficialListDisplay">
                            显示官方题单
                            <div style="float: right" class="searchAnywhereClicky" flag="${settings.lsawOfficialListDisplay}">
                                <div class="falseBlock">
                                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M384 32C419.3 32 448 60.65 448 96V416C448 451.3 419.3 480 384 480H64C28.65 480 0 451.3 0 416V96C0 60.65 28.65 32 64 32H384zM384 80H64C55.16 80 48 87.16 48 96V416C48 424.8 55.16 432 64 432H384C392.8 432 400 424.8 400 416V96C400 87.16 392.8 80 384 80z"/></svg>
                                </div>
                                <div class="trueBlock">
                                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M384 32C419.3 32 448 60.65 448 96V416C448 451.3 419.3 480 384 480H64C28.65 480 0 451.3 0 416V96C0 60.65 28.65 32 64 32H384zM339.8 211.8C350.7 200.9 350.7 183.1 339.8 172.2C328.9 161.3 311.1 161.3 300.2 172.2L192 280.4L147.8 236.2C136.9 225.3 119.1 225.3 108.2 236.2C97.27 247.1 97.27 264.9 108.2 275.8L172.2 339.8C183.1 350.7 200.9 350.7 211.8 339.8L339.8 211.8z"/></svg>
                                </div>
                            </div>
                        </div>
                        <div class="searchAnywhereSettingsBag" for="lsawSelectListDisplay">
                            显示用户题单
                            <div style="float: right" class="searchAnywhereClicky" flag="${settings.lsawSelectListDisplay}">
                                <div class="falseBlock">
                                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M384 32C419.3 32 448 60.65 448 96V416C448 451.3 419.3 480 384 480H64C28.65 480 0 451.3 0 416V96C0 60.65 28.65 32 64 32H384zM384 80H64C55.16 80 48 87.16 48 96V416C48 424.8 55.16 432 64 432H384C392.8 432 400 424.8 400 416V96C400 87.16 392.8 80 384 80z"/></svg>
                                </div>
                                <div class="trueBlock">
                                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M384 32C419.3 32 448 60.65 448 96V416C448 451.3 419.3 480 384 480H64C28.65 480 0 451.3 0 416V96C0 60.65 28.65 32 64 32H384zM339.8 211.8C350.7 200.9 350.7 183.1 339.8 172.2C328.9 161.3 311.1 161.3 300.2 172.2L192 280.4L147.8 236.2C136.9 225.3 119.1 225.3 108.2 236.2C97.27 247.1 97.27 264.9 108.2 275.8L172.2 339.8C183.1 350.7 200.9 350.7 211.8 339.8L339.8 211.8z"/></svg>
                                </div>
                            </div>
                        </div>
                        <div class="searchAnywhereSettingsBag" for="lsawProblemDisplayNumber">
                            题目展示数量
                            <div style="float: right; width: 150px">
                                <div class="inputAreaSmall withContent">
                                    <input type="number" min="1" max="50" value="${settings.lsawProblemDisplayNumber}" class="withContent"/>
                                </div>
                            </div>
                        </div>
                        <div class="searchAnywhereSettingsBag" for="lsawListDisplayNumber">
                            题单展示数量
                            <div style="float: right; width: 150px">
                                <div class="inputAreaSmall withContent">
                                    <input type="number" min="1" max="50" value="${settings.lsawListDisplayNumber}" class="withContent"/>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <div style="margin-top: 10px; text-align: center; width: 100%;">
                    <div class="searchAnywhereCloseSettings">
                        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 352 512" class="icon svg-inline--fa fa-times fa-w-11" data-v-303bbf52="" style="transform: scale(1.2); width: 16px; height: 16px; color: rgb(231, 76, 60);"><path data-v-1b44b3e6="" fill="currentColor" d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z" class=""></path></svg>
                    </div>
                </div>
            </div>
        </div>
        <div class="searchAnywhereEntrance">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="black" d="M236 176c0 15.46-12.54 28-28 28S180 191.5 180 176S192.5 148 208 148S236 160.5 236 176zM500.3 500.3c-15.62 15.62-40.95 15.62-56.57 0l-119.7-119.7c-40.41 27.22-90.9 40.65-144.7 33.46c-91.55-12.23-166-87.28-177.6-178.9c-17.24-136.2 97.29-250.7 233.4-233.4c91.64 11.6 166.7 86.07 178.9 177.6c7.19 53.8-6.236 104.3-33.46 144.7l119.7 119.7C515.9 459.3 515.9 484.7 500.3 500.3zM294.1 182.2C294.1 134.5 255.6 96 207.1 96C160.4 96 121.9 134.5 121.9 182.2c0 38.35 56.29 108.5 77.87 134C201.8 318.5 204.7 320 207.1 320c3.207 0 6.26-1.459 8.303-3.791C237.8 290.7 294.1 220.5 294.1 182.2z"/></svg>
        </div>
    `);
    $(".inputArea > input").focus(function(){
        $(this).parent().addClass("onFocus");
    });
    $(".inputArea > input").blur(function(){
        $(this).parent().removeClass("onFocus");
        if($(this).val().length != 0)
            $(this).parent().addClass("withContent");
        else
            $(this).parent().removeClass("withContent");
    });
    $(".inputArea").mouseenter(function(){
        $(this).addClass("onHover");
    });
    $(".inputArea").mouseleave(function(){
        $(this).removeClass("onHover");
    });
    $(".inputAreaSmall > input").focus(function(){
        $(this).parent().addClass("onFocus");
    });
    $(".inputAreaSmall > input").blur(function(){
        $(this).parent().removeClass("onFocus");
        if($(this).val().length != 0)
            $(this).parent().addClass("withContent");
        else
            $(this).parent().removeClass("withContent");
    });
    $(".inputAreaSmall").mouseenter(function(){
        $(this).addClass("onHover");
    });
    $(".inputAreaSmall").mouseleave(function(){
        $(this).removeClass("onHover");
    });
    $(".searchAnywhereClicky").click(function(){
        var a = $(this).attr("flag");
        if(a == "false"){
            a = true;
            $(this).attr("flag", a);
        }
        else{
            a = false;
            $(this).attr("flag", a);
        }
        var settingsName = $(this).parent().attr("for");
        settings[settingsName] = a;
        localStorage.setItem("lsaw_settings", JSON.stringify(settings));
    })
    $(".inputAreaSmall input").change(function(){
        var a = Number($(this).val());
        var settingsName = $(this).parent().parent().parent().attr("for");
        settings[settingsName] = a;
        localStorage.setItem("lsaw_settings", JSON.stringify(settings));
    })
    $(".searchAnywhereCloseSettings").click(function(){
        $(".searchAnywhereSettings").css("opacity", "0");
        setTimeout(() => {
            $(".searchAnywhereSettings").css("display", "none");
        }, 200);
    })
    $(".searchAnywhereSettingsLink").click(function(){
        $(".searchAnywhereSettings").css("display", "block");
        setTimeout(() => {
            $(".searchAnywhereSettings").css("opacity", "1");
        }, 20);
    })

    const getColorFromPercent = (x, opa) => {
        let r = 0, g = 0, b = 0;
        let rr = 231, gg = 76, bb = 60;
        let rrr = 82, ggg = 196, bbb = 26;
        // if(x < 0.5){
        //  r = 255;
        //  g = one * x;
        // }
        // else{
        //  r = 255 - ((x - 0.5) * one);
        //  g = 255;
        // }
        r = rr + (rrr - rr) * x;
        g = gg + (ggg - gg) * x;
        b = bb + (bbb - bb) * x;
        r += (60 - r) * (1 - opa);
        g += (60 - g) * (1 - opa);
        b += (60 - b) * (1 - opa);
        r = Math.floor(r);
        g = Math.floor(g);
        b = Math.floor(b);
        return `rgb(${r}, ${g}, ${b})`;
    };

    const problemColors = [ "Gray", "Red", "Orange", "Yellow", "Green", "Blue", "Purple", "Black" ];
    const problemNames = [ "暂无评定", "入门", "普及-", "普及/提高-", "普及+/提高", "提高+/省选-", "省选/NOI-", "NOI/NOI+/CTSC" ];
    var searchTimeout = null;
    var currentHoverCard = undefined;
    const changeHoverCard = (x, scroll = true, align = false) => {
        $(".searchCard.light").removeClass("light");
        if(x != undefined && scroll)
            $(".searchAnywhereMainInput > input").blur();
        if(x != undefined){
            x.addClass("light").focus(align);
            if(scroll){
                var heg = x[0].offsetTop;
                var prr = x.parent().parent();
                var scr = prr.scrollTop();
                var r = heg - x.outerHeight() + 5, l = r - prr.outerHeight() + x.outerHeight() + 10;
                scr = Math.max(l, Math.min(r, scr));
                prr.scrollTop(scr);
            }
        }
        if(scroll){ // remove mouse event
            $(".searchCard").unbind('mouseenter').unbind('mouseleave');
            $(document).mousemove(() => {
                $(document).unbind('mousemove');
                $(".searchCard").unbind('mouseenter').unbind('mouseleave').hover(function(){
                    changeHoverCard($(this), false);
                }, function(){
                    changeHoverCard(undefined, false);
                });
            })
        }
        currentHoverCard = x;
    }
    const searchInfo = () => {
        searchTimeout = null;
        var info = $(".inputArea > input").val();
        info = $.trim(info);
        if(info == ""){
            $(".searchAnywhereContent").html("");
            return;
        }
        $(".searchAnywhereContent").html(`<div><div style='text-align: center; margin-bottom: 10px; width: 100%; font-size: 20px;'>加载中……</div></div>`);
        $(".searchAnywhereContent > div").unbind('click').click((event) => {
            event.stopPropagation();
        })
        var userHtml = "";
        var problemHtml = "";
        var officialHtml = "";
        var selectHtml = "";
        var finishWorks = 0;
        var networkError = false;
        var workCnt = (settings.lsawUserDisplay != false)
                    + (settings.lsawProblemDisplay != false)
                    + (settings.lsawOfficialListDisplay != false)
                    + (settings.lsawSelectListDisplay != false);
        const finishWork = () => {
            ++ finishWorks;
            if(finishWorks == workCnt){
                if(networkError)
                    $(".searchAnywhereContent").html(`<div><div style='text-align: center; margin-bottom: 10px; width: 100%; font-size: 20px;'>网络错误</div></div>`);
                else if(userHtml == "" && problemHtml == "" && officialHtml == "" && selectHtml == "")
                    $(".searchAnywhereContent").html(`<div><div style='text-align: center; margin-bottom: 10px; width: 100%; font-size: 20px;'>未搜索到相关内容</div></div>`);
                else{
                    changeHoverCard(undefined, false);
                    $(".searchAnywhereContent").html(`<div>` + userHtml + problemHtml + officialHtml + selectHtml + `</div>`);
                    $(".searchAnywhereContent > div").unbind('click').click((event) => {
                        event.stopPropagation();
                    })
                    $(".searchCard").unbind('mouseenter').unbind("mouseleave").hover(function(){
                        changeHoverCard($(this), false);
                    }, function(){
                        changeHoverCard(undefined, false);
                    });
                }
            }
        };
        const getProblemStatus = (x, y) => {
            if(!x && !y)
                return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="icon svg-inline--fa fa-minus fa-w-14" data-v-303bbf52="" style="width: 16px; height: 16px; color: #aaa"><path data-v-1b44b3e6="" fill="currentColor" d="M416 208H32c-17.67 0-32 14.33-32 32v32c0 17.67 14.33 32 32 32h384c17.67 0 32-14.33 32-32v-32c0-17.67-14.33-32-32-32z" class=""></path></svg>`;
            if(!y)
                return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 352 512" class="icon svg-inline--fa fa-times fa-w-11" data-v-303bbf52="" style="transform: scale(1.2); width: 16px; height: 16px; color: rgb(231, 76, 60);"><path data-v-1b44b3e6="" fill="currentColor" d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z" class=""></path></svg>`;
            return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="icon svg-inline--fa fa-check fa-w-16" data-v-303bbf52="" style="width: 16px; height: 16px; color: rgb(82, 196, 26);"><path data-v-1b44b3e6="" fill="currentColor" d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z" class=""></path></svg>`;
        }
        const getCCFLevel = (x) => {
            if(x == null || x < 3)
                return "";
            var color = "";
            if(x <= 5)
                color = "#5eb95e";
            else if(x <= 7)
                color = "#07a2f1";
            else
                color = "#f1c40f";
            return `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 16 16" style="margin: 0px 3px;" fill="${color}" style="margin-bottom: -3px;"><path d="M16 8C16 6.84375 15.25 5.84375 14.1875 5.4375C14.6562 4.4375 14.4688 3.1875 13.6562 2.34375C12.8125 1.53125 11.5625 1.34375 10.5625 1.8125C10.1562 0.75 9.15625 0 8 0C6.8125 0 5.8125 0.75 5.40625 1.8125C4.40625 1.34375 3.15625 1.53125 2.34375 2.34375C1.5 3.1875 1.3125 4.4375 1.78125 5.4375C0.71875 5.84375 0 6.84375 0 8C0 9.1875 0.71875 10.1875 1.78125 10.5938C1.3125 11.5938 1.5 12.8438 2.34375 13.6562C3.15625 14.5 4.40625 14.6875 5.40625 14.2188C5.8125 15.2812 6.8125 16 8 16C9.15625 16 10.1562 15.2812 10.5625 14.2188C11.5938 14.6875 12.8125 14.5 13.6562 13.6562C14.4688 12.8438 14.6562 11.5938 14.1875 10.5938C15.25 10.1875 16 9.1875 16 8ZM11.4688 6.625L7.375 10.6875C7.21875 10.8438 7 10.8125 6.875 10.6875L4.5 8.3125C4.375 8.1875 4.375 7.96875 4.5 7.8125L5.3125 7C5.46875 6.875 5.6875 6.875 5.8125 7.03125L7.125 8.34375L10.1562 5.34375C10.3125 5.1875 10.5312 5.1875 10.6562 5.34375L11.4688 6.15625C11.5938 6.28125 11.5938 6.5 11.4688 6.625Z"></path></svg>`
        }
        if(settings.lsawUserDisplay != false)
        $.ajax({
            url: `/api/user/search?keyword=${info}`,
            type: 'GET',
            success: function(json){
                json = json.users;
                if(json.length != 0 && json[0] != null){
                    userHtml = `
                        <div style='text-align: left; margin-bottom: 10px; width: 100%; font-size: 18px; font-weight: bold'>用户</div>
                    `;
                    json.forEach((item) => {
                        if(item == null)
                            return;
                        if(item.color == "Cheater")
                            item.badge = "作弊者";
                        userHtml += `
                            <a class="searchCard searchUserCard" href='https://www.luogu.com.cn/user/${item.uid}'>
                            <div class="searchUserCardBody">
                                <div class="searchUserCardImg" style="background: url(https://cdn.luogu.com.cn/upload/usericon/${item.uid}.png); background-size: 36px 36px;"></div>
                                <div class="searchUserCardInfo"><span>UID ${item.uid}</span><br/><div style='display: flex; flex-direction: row'><span class="user${item.color}" style="line-height: 20px">${item.name}</span>${getCCFLevel(item.ccfLevel)}${item.badge != null && item.badge != "" ? `<div class='userBadgeInfo badge${item.color}'>${item.badge}</div>` : ""}</div></div>
                            </div>
                        </a>`
                    });
                }
                finishWork();
            },
            error: () => {
                networkError = true;
                finishWork();
            }
        });
        if(settings.lsawProblemDisplay != false)
        $.ajax({
            url: `/problem/list`,
            type: 'GET',
            headers: {"x-luogu-type": "content-only"},
            data: {
                keyword: info,
                page: 1,
                type: "P|B|CF|SP|AT|UVA"
            },
            success: function(json){
                if(json.code != 200){
                    finishWork();
                    return;
                }
                json = json.currentData.problems;
                if(json.count != 0){
                    problemHtml = `
                        <div style='text-align: left; margin-bottom: 10px; width: 100%; font-size: 18px; font-weight: bold'>题目<a href="/problem/list?keyword=${info}&page=1&type=P%7CB%7CCF%7CSP%7CAT%7CUVA" style="cursor: pointer; float: right; font-weight: normal !important" class="searchShowProblems">查看所有 ${json.count} 道题目</a></div>
                    `;
                    for(var i=0; i<json.result.length && i<settings.lsawProblemDisplayNumber; i++){
                        let item = json.result[i];
                        problemHtml += `
                         <a class="searchCard searchProblemCard" href='/problem/${item.pid}'>
                            <div class="searchProblemCardBody">
                                <div>${getProblemStatus(item.submitted, item.accepted)}</div>
                                <div>${item.title}</div>
                            </div>
                            <div>
                                <div class='searchProblemCardTag'><div><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="svg-inline--fa fa-book fa-w-14"><path data-v-639bc19b="" fill="currentColor" d="M448 360V24c0-13.3-10.7-24-24-24H96C43 0 0 43 0 96v320c0 53 43 96 96 96h328c13.3 0 24-10.7 24-24v-16c0-7.5-3.5-14.3-8.9-18.7-4.2-15.4-4.2-59.3 0-74.7 5.4-4.3 8.9-11.1 8.9-18.6zM128 134c0-3.3 2.7-6 6-6h212c3.3 0 6 2.7 6 6v20c0 3.3-2.7 6-6 6H134c-3.3 0-6-2.7-6-6v-20zm0 64c0-3.3 2.7-6 6-6h212c3.3 0 6 2.7 6 6v20c0 3.3-2.7 6-6 6H134c-3.3 0-6-2.7-6-6v-20zm253.4 250H96c-17.7 0-32-14.3-32-32 0-17.6 14.4-32 32-32h285.4c-1.9 17.1-1.9 46.9 0 64z" class=""></path></svg></div>${item.pid}</div>
                                <div class='searchProblemCardTag'><div><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 544 512" class="svg-inline--fa fa-chart-pie fa-w-17"><path data-v-639bc19b="" fill="currentColor" d="M527.79 288H290.5l158.03 158.03c6.04 6.04 15.98 6.53 22.19.68 38.7-36.46 65.32-85.61 73.13-140.86 1.34-9.46-6.51-17.85-16.06-17.85zm-15.83-64.8C503.72 103.74 408.26 8.28 288.8.04 279.68-.59 272 7.1 272 16.24V240h223.77c9.14 0 16.82-7.68 16.19-16.8zM224 288V50.71c0-9.55-8.39-17.4-17.84-16.06C86.99 51.49-4.1 155.6.14 280.37 4.5 408.51 114.83 513.59 243.03 511.98c50.4-.63 96.97-16.87 135.26-44.03 7.9-5.6 8.42-17.23 1.57-24.08L224 288z" class=""></path></svg></div>${item.totalSubmit}</div>
                                <div class='searchProblemCardTag'><div><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="icon svg-inline--fa fa-check fa-w-16" data-v-303bbf52="" style="width: 16px; height: 16px; color: rgb(82, 196, 26);"><path data-v-1b44b3e6="" fill="currentColor" d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z" class=""></path></svg></div>${item.totalAccepted}</div>
                                <div style='flex: 1; text-align: right'>
                                    <div class="problemTagInfo badge${problemColors[item.difficulty]}">${problemNames[item.difficulty]}</div>
                                </div>
                            </div>
                        </a>
                        `;
                    };
                }
                finishWork();
            },
            error: () => {
                networkError = true;
                finishWork();
            }
        });
        if(settings.lsawOfficialListDisplay != false)
        $.ajax({
            url: `/training/list`,
            type: "GET",
            data: {
                keyword: info,
                page: 1,
                type: "official"
            },
            headers: {"x-luogu-type": "content-only"},
            success: (json) => {
                if(json.code != 200){
                    finishWork();
                    return;
                }
                json = json.currentData;
                if(json.trainings.result.length != 0){
                    officialHtml = `<div style='text-align: left; margin-bottom: 10px; width: 100%; font-size: 18px; font-weight: bold'>官方题单<a href="/training/list?keyword=${info}&page=1&type=official" style="cursor: pointer; float: right; font-weight: normal !important" class="searchShowProblems">查看所有 ${json.trainings.count} 份题单</a></div>`;
                    for(var i=0; i<json.trainings.result.length && i<settings.lsawListDisplayNumber; i++){
                        var item = json.trainings.result[i];
                        var acs = json.acceptedCounts[item.id];
                        if(acs == undefined)
                            acs = 0;
                        officialHtml += `
                         <a class="searchCard searchListCard" href='/training/${item.id}' style="background: ${getColorFromPercent(acs / item.problemCount, 0.4)} !important">
                            <div class="searchListCardProgress" style="height: 5px; position: absolute; top: -1px; left: 0px; overflow: hidden; border-top-left-radius: 5px; border-top-right-radius: 5px">
                                <div style="width: ${acs / item.problemCount * 100}%; background: ${getColorFromPercent(acs / item.problemCount, 1)}; height: 5px; content: ""></div>
                                <div style="flex: 1"></div>
                            </div>
                            <div class="searchListCardBody">
                                <div>#${item.id}</div>
                                <div>${item.title}</div>
                            </div>
                            <div>
                                <div class='searchListCardTag'><div><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="svg-inline--fa fa-book fa-w-14"><path data-v-639bc19b="" fill="currentColor" d="M448 360V24c0-13.3-10.7-24-24-24H96C43 0 0 43 0 96v320c0 53 43 96 96 96h328c13.3 0 24-10.7 24-24v-16c0-7.5-3.5-14.3-8.9-18.7-4.2-15.4-4.2-59.3 0-74.7 5.4-4.3 8.9-11.1 8.9-18.6zM128 134c0-3.3 2.7-6 6-6h212c3.3 0 6 2.7 6 6v20c0 3.3-2.7 6-6 6H134c-3.3 0-6-2.7-6-6v-20zm0 64c0-3.3 2.7-6 6-6h212c3.3 0 6 2.7 6 6v20c0 3.3-2.7 6-6 6H134c-3.3 0-6-2.7-6-6v-20zm253.4 250H96c-17.7 0-32-14.3-32-32 0-17.6 14.4-32 32-32h285.4c-1.9 17.1-1.9 46.9 0 64z" class=""></path></svg></div>${acs} / ${item.problemCount}</div>
                                <div class='searchListCardTag'><div><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="#f1c40f" d="M381.2 150.3L524.9 171.5C536.8 173.2 546.8 181.6 550.6 193.1C554.4 204.7 551.3 217.3 542.7 225.9L438.5 328.1L463.1 474.7C465.1 486.7 460.2 498.9 450.2 506C440.3 513.1 427.2 514 416.5 508.3L288.1 439.8L159.8 508.3C149 514 135.9 513.1 126 506C116.1 498.9 111.1 486.7 113.2 474.7L137.8 328.1L33.58 225.9C24.97 217.3 21.91 204.7 25.69 193.1C29.46 181.6 39.43 173.2 51.42 171.5L195 150.3L259.4 17.97C264.7 6.954 275.9-.0391 288.1-.0391C300.4-.0391 311.6 6.954 316.9 17.97L381.2 150.3z"/></svg></div>${item.markCount}</div>
                            </div>
                        </a>
                        `;
                    }
                }
                finishWork();
            },
            error: () => {
                networkError = true;
                finishWork();
            }
        })
        if(settings.lsawSelectListDisplay != false)
        $.ajax({
            url: `/training/list`,
            type: "GET",
            data: {
                keyword: info,
                page: 1,
                type: "select"
            },
            headers: {"x-luogu-type": "content-only"},
            success: (json) => {
                if(json.code != 200){
                    finishWork();
                    return;
                }
                json = json.currentData;
                if(json.trainings.result.length != 0){
                    selectHtml = `<div style='text-align: left; margin-bottom: 10px; width: 100%; font-size: 18px; font-weight: bold'>用户题单<a href="/training/list?keyword=${info}&page=1&type=select" style="cursor: pointer; float: right; font-weight: normal !important" class="searchShowProblems">查看所有 ${json.trainings.count} 份题单</a></div>`;
                    for(var i=0; i<json.trainings.result.length && i<settings.lsawListDisplayNumber; i++){
                        var item = json.trainings.result[i];
                        if(item.provider.color == "Cheater")
                            item.provider.badge = "作弊者";
                        selectHtml += `
                         <a class="searchCard searchListCard" href='/training/${item.id}'>
                            <div class="searchListCardBody">
                                <div>#${item.id}</div>
                                <div>${item.title}</div>
                            </div>
                            <div>
                                <div class='searchListCardTag'><div><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="svg-inline--fa fa-book fa-w-14"><path data-v-639bc19b="" fill="currentColor" d="M448 360V24c0-13.3-10.7-24-24-24H96C43 0 0 43 0 96v320c0 53 43 96 96 96h328c13.3 0 24-10.7 24-24v-16c0-7.5-3.5-14.3-8.9-18.7-4.2-15.4-4.2-59.3 0-74.7 5.4-4.3 8.9-11.1 8.9-18.6zM128 134c0-3.3 2.7-6 6-6h212c3.3 0 6 2.7 6 6v20c0 3.3-2.7 6-6 6H134c-3.3 0-6-2.7-6-6v-20zm0 64c0-3.3 2.7-6 6-6h212c3.3 0 6 2.7 6 6v20c0 3.3-2.7 6-6 6H134c-3.3 0-6-2.7-6-6v-20zm253.4 250H96c-17.7 0-32-14.3-32-32 0-17.6 14.4-32 32-32h285.4c-1.9 17.1-1.9 46.9 0 64z" class=""></path></svg></div>${item.problemCount}</div>
                                <div class='searchListCardTag'><div><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M256 512C114.6 512 0 397.4 0 256C0 114.6 114.6 0 256 0C397.4 0 512 114.6 512 256C512 397.4 397.4 512 256 512zM232 256C232 264 236 271.5 242.7 275.1L338.7 339.1C349.7 347.3 364.6 344.3 371.1 333.3C379.3 322.3 376.3 307.4 365.3 300L280 243.2V120C280 106.7 269.3 96 255.1 96C242.7 96 231.1 106.7 231.1 120L232 256z"/></svg></div>${(new Date(item.createTime * 1000)).pattern("yyyy/MM/dd")}</div>
                                <div class='searchListCardTag'><div><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="#f1c40f" d="M381.2 150.3L524.9 171.5C536.8 173.2 546.8 181.6 550.6 193.1C554.4 204.7 551.3 217.3 542.7 225.9L438.5 328.1L463.1 474.7C465.1 486.7 460.2 498.9 450.2 506C440.3 513.1 427.2 514 416.5 508.3L288.1 439.8L159.8 508.3C149 514 135.9 513.1 126 506C116.1 498.9 111.1 486.7 113.2 474.7L137.8 328.1L33.58 225.9C24.97 217.3 21.91 204.7 25.69 193.1C29.46 181.6 39.43 173.2 51.42 171.5L195 150.3L259.4 17.97C264.7 6.954 275.9-.0391 288.1-.0391C300.4-.0391 311.6 6.954 316.9 17.97L381.2 150.3z"/></svg></div>${item.markCount}</div>
                                <div style='flex: 1; text-align: right'>
                                    <object style='display: inline-block'><a href="/user/${item.provider.uid}">
                                        <div style='display: flex; flex-direction: row'>
                                            <span class="user${item.provider.color}" style="line-height: 20px">${item.provider.name}</span>
                                            ${getCCFLevel(item.provider.ccfLevel)}
                                            ${item.provider.badge != null && item.provider.badge != "" ? `<div class='userBadgeInfo badge${item.provider.color}'>${item.provider.badge}</div>` : ""}
                                        </div>
                                    </a></object>
                                </div>
                            </div>
                        </a>
                        `;
                    }
                }
                finishWork();
            },
            error: () => {
                networkError = true;
                finishWork();
            }
        })
    };
    $(".searchAnywhereMainInput > input").unbind('input propertychange').on('input propertychange', function(){
        if(searchTimeout != null)
            clearTimeout(searchTimeout);
        searchTimeout = setTimeout(searchInfo, 1000);
    });
    let searchAnywhereOpen = false;
    $(".searchAnywhereEntrance").unbind('click').click(function(){
        if(! searchAnywhereOpen){
            $(".searchAnywhere").css("display", "block");
            setTimeout(() => {
                $(".searchAnywhere").css("opacity", "1");
                $(".searchAnywhereMainInput > input").focus();
            }, 20);
        }
        else{
            $(".searchAnywhere").css("opacity", "0");
            setTimeout(() => {
                $(".searchAnywhere").css("display", "none");
            }, 200);
        }
        searchAnywhereOpen = !searchAnywhereOpen;
    });
    $(".searchAnywhere").unbind('click').click(() => {
        $(".searchAnywhere").css("opacity", "0");
        setTimeout(() => {
            $(".searchAnywhere").css("display", "none");
        }, 200);
        searchAnywhereOpen = false;
    })
    $(".searchAnywhereMainInput").unbind('click').click((event) => {
        event.stopPropagation();
    })
    $(document).keydown(function(event){
        if((event.keyCode == 186)
            && (event.ctrlKey || event.metaKey)){
            if(! searchAnywhereOpen){
                $(".searchAnywhere").css("display", "block");
                setTimeout(() => {
                    $(".searchAnywhere").css("opacity", "1");
                    $(".searchAnywhereMainInput > input").focus();
                }, 20);
            }
            else{
                $(".searchAnywhere").css("opacity", "0");
                setTimeout(() => {
                    $(".searchAnywhere").css("display", "none");
                }, 200);
            }
            searchAnywhereOpen = !searchAnywhereOpen;
            event.preventDefault();
        }
        if(searchAnywhereOpen){
            if(event.keyCode == 38){ // Up
                if(currentHoverCard == undefined){
                    var lis = $(".searchCard");
                    if(lis.length == 0)
                         $(".searchAnywhereMainInput > input").focus();
                    else{
                        currentHoverCard = lis.eq(lis.length - 1);
                        changeHoverCard(currentHoverCard);
                    }
                }
                else{
                    currentHoverCard = currentHoverCard.prev();
                    while(1){
                        if(currentHoverCard.length == 0 || currentHoverCard.hasClass("searchCard"))
                            break;
                        currentHoverCard = currentHoverCard.prev();
                    }
                    if(currentHoverCard.length == 0){
                        $(".searchAnywhereMainInput > input").focus();
                        currentHoverCard = undefined;
                    }
                    changeHoverCard(currentHoverCard);
                }
                event.preventDefault();
            }
            else if(event.keyCode == 40){ // Down
                if(currentHoverCard == undefined){
                    var lis = $(".searchCard");
                    if(lis.length == 0)
                         $(".searchAnywhereMainInput > input").focus();
                    else{
                        currentHoverCard = lis.eq(0);
                        changeHoverCard(currentHoverCard);
                    }
                }
                else{
                    currentHoverCard = currentHoverCard.next();
                    while(1){
                        if(currentHoverCard.length == 0 || currentHoverCard.hasClass("searchCard"))
                            break;
                        currentHoverCard = currentHoverCard.next();
                    }
                    if(currentHoverCard.length == 0){
                        $(".searchAnywhereMainInput > input").focus();
                        currentHoverCard = undefined;
                    }
                    changeHoverCard(currentHoverCard);
                }
                event.preventDefault();
            }
            else if(event.keyCode == 13){ // Enter
                if(currentHoverCard != undefined)
                    window.open(currentHoverCard.attr("href"), "_blank");
                event.preventDefault();
            }
            else if(event.keyCode == 9)
                event.preventDefault();
            else
                $(".searchAnywhereMainInput > input").focus();
        }
    })
    });
})(window.jQuery.noConflict(true));

3. Floating Luogu

// ==UserScript==
// @name         Floating Luogu
// @namespace    http://tampermonkey.net/
// @icon         https://cdn.luogu.com.cn/upload/usericon/3.png
// @author       yurzhang & tiger2005
// @homepage     https://github.com/Nikaidou-Shinku/floating-luogu
// @description  A plugin to decorate Luogu with exquisite user card.
// @include      https://www.luogu.com.cn/*
// @version      0.4.0
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

GM_addStyle(`._center_1c669_1{display: flex; justify - content: center}._container_1y67s_1{display: flex; flex - direction: column; text - align: left}._container_1y67s_1 > ._header_1y67s_6{display: flex; flex - direction: row; width: 100 %; position: relative; height: 52px}._avatar_1y67s_14{box - shadow: 0 0 5px 1px #999; width: 60px; height: 60px; border - radius: 30px; position: absolute; top: -16px; left: 10px}._ccfbadge_1y67s_24{position: absolute; background: #fff; border - radius: 100 %; width: 20px; height: 20px; top: 27px; left: 10px}._badge_1y67s_34{color: #fff; display: inline - block; padding: 0 8px; box - sizing: border - box; font - weight: 400; line - height: 20px; border - radius: 2px; font - size: 14px; margin - left: 5px}._blog_1y67s_46{position: absolute; right: 0; top: -2px; font - size: 14px; display: flex; flex - direction: row; transform: scale(.8); margin - bottom: 5px; background: #eee; border - radius: 5px; padding: 3px 5px; line - height: 20px; cursor: pointer; color: #3498db}._blog_1y67s_46 > svg{width: 16px; height: 16px; margin: 2px 5px 2px 2px; fill: #3498db}._blog_1y67s_46: hover{color: #0056b3}._blog_1y67s_46: hover > svg{fill: #0056b3}._slogan_1y67s_75{font - size: 14px; margin: .25em 1.5em; word - break: break - all; font - weight: 400}._stat_1y67s_82{display: flex; flex - direction: row; width: 100 % }._stat_1y67s_82 > div{flex: 1; margin: 10px}._stat_1y67s_82 > div > div{text - align: center}._stat_1y67s_82 > div._canclick_1y67s_94{transition: .2s; color: #000; cursor: pointer}._stat_1y67s_82 > div: hover._canclick_1y67s_94{transition: .2s; color: #777}._bottom - button_1y67s_104{font - size: 16px; height: 22px; flex: 1; border - radius: 10px; padding: 3px 0; text - align: center; line - height: 18px; cursor: pointer; color: #3498db}._bottom - button_1y67s_104 > svg{width: 16px; height: 16px; margin - bottom: 3px; margin - right: 5px; vertical - align: middle; fill: #3498db}._bottom - button_1y67s_104: hover{color: #0056b3}._bottom - button_1y67s_104: hover > svg{fill: #0056b3}._card_1b53a_1{width: 300px; border - radius: 5px; overflow: hidden; display: flex; flex - direction: column; box - shadow: 0 0 5px 1px #999; background: #fff; color: #000; font - weight: 700; text - align: center; text - size - adjust: 100 %; line - height: 25.6px; text - decoration: none; font - size: 16px}._rotatable_14mxb_1{-webkit - transition - property: -webkit - transform; -webkit - transition - duration: 1s; -moz - transition - property: -moz - transform; -moz - transition - duration: 1s; -webkit - animation: _rotate_14mxb_1 1s linear infinite; -moz - animation: _rotate_14mxb_1 1s linear infinite; -o - animation: _rotate_14mxb_1 1s linear infinite; animation: _rotate_14mxb_1 1s linear infinite}@ -webkit - keyframes _rotate_14mxb_1{0 % {-webkit - transform: rotate(0deg)} to{-webkit - transform: rotate(360deg)}}@ -moz - keyframes _rotate_14mxb_1{0 % {-moz - transform: rotate(0deg)} to{-moz - transform: rotate(359deg)}}@ -o - keyframes _rotate_14mxb_1{0 % {-o - transform: rotate(0deg)} to{-o - transform: rotate(359deg)}}@keyframes _rotate_14mxb_1{0 % {transform: rotate(0)} to{transform: rotate(359deg)}}._container_14mxb_54{width: 100 %; text - align: center}._svg_14mxb_59{width: 40px; height: 40px; margin: 20px}.card - fade - enter - active, .card - fade - exit - active{transition: opacity .1s, transform .1s}.card - fade - enter, .card - fade - exit - to{transform: translateY(10px); opacity: 0}.card - fade - enter{transform: translateY(10px)}`);
(function() {
	"use strict";
	const R = {context: void 0, registry: void 0};
	function oe(t) {
		R.context = t
	}
	const tn = (t, e) => t == = e, W = Symbol("solid-proxy"), De = Symbol("solid-track"), ve = {equals: tn};
	let st = ht;
	const z = 1, ue = 2, rt = {owned: null, cleanups: null, context: null, owner: null}, Me = {};
	var A = null;
	let h = null, x = null, M = null, U = null, me = 0;
	const[nn, it] = j(!1);
	function le(t, e) {
		const n = x, s = A, r = t.length == = 0, i = r ? rt : {owned : null, cleanups : null, context : null, owner : e == = void 0 ? s : e}, o = r ? t : () => t(() => N(() => X(i)));
		A = i, x = null;
		try {
			return B(o, !0)
		}
		finally{x = n, A = s}
	}
	function j(t, e) {
		e = e ? Object.assign({}, ve, e): ve;
		const n = {value: t, observers: null, observerSlots: null, comparator: e.equals || void 0}, s = r => (typeof r == "function" && (h&&h.running&&h.sources.has(n) ? r = r(n.tValue) : r = r(n.value)), ft(n, r));
		return[ct.bind(n), s]
	}
	function se(t, e, n) {
		const s = we(t, e, !0, z);
		re(s)
	}
	function T(t, e, n) {
		const s = we(t, e, !1, z);
		re(s)
	}
	function ot(t, e, n) {
		st = dn;
		const s = we(t, e, !1, z), r = Z&&Se(A, Z.id);
		r&&(s.suspense = r), (!n || !n.render) && (s.user = !0), U ? U.push(s) : re(s)
	}
	function E(t, e, n) {
		n = n ? Object.assign({}, ve, n): ve;
		const s = we(t, e, !0, 0);
		return s.observers = null, s.observerSlots = null, s.comparator = n.equals || void 0, re(s), ct.bind(s)
	}
	function sn(t, e, n) {
		let s, r, i;
		arguments.length == = 2 && typeof e == "object" || arguments.length == = 1 ? (s = !0, r = t, i = e || {}): (s = t, r = e, i = n || {});
		let o = null, u = Me, l = null, c = !1, a = !1, f = "initialValue"in i, g = typeof s == "function" && E(s);
		const y = new Set, [d, p] = (i.storage || j)(i.initialValue), [v, b] = j(void 0), [P, w] = j(void 0, {equals: !1}), [$, q] = j(f ? "ready" : "unresolved");
		if (R.context) {
			l = `$ {R.context.id}$ {R.context.count++}`;
			let _;
			i.ssrLoadFrom == = "initial" ? u = i.initialValue : R.load&&(_ = R.load(l)) && (u = _[0])
		}
		function C(_, k, L, ne) {
			return o == = _&&(o = null, f = !0, (_ == = u || k == = u) && i.onHydrated&&queueMicrotask(() => i.onHydrated(ne, {
value:
				k
			})), u = Me, h&&_&&c ? (h.promises.delete(_), c = !1, B(() => {
				h.running = !0, O(k, L)
			}, !1)): O(k, L)), k
		}
		function O(_, k) {
			B(() => {
				k == = void 0 && p(() => _), q(k != = void 0 ? "errored" : "ready"), b(k);
				for (const L of y.keys())L.decrement();
				y.clear()
			}, !1)
		}
		function m() {
			const _ = Z&&Se(A, Z.id), k = d(), L = v();
			if (L != = void 0 && !o)throw L;
			return x&&!x.user&&_&&se(() => {
				P(), o&&(_.resolved&&h&&c ? h.promises.add(o) : y.has(_) || (_.increment(), y.add(_)))
			}), k
		}
		function Q(_ = !0) {
			if (_ != = !1 && a)return;
			a = !1;
			const k = g ? g() : s;
			if (c = h && h.running, k == null || k == = !1) {
				C(o, N(d));
				return
			}
			h&&o&&h.promises.delete(o);
			const L = u != = Me ? u : N(() => r(k, {
value:
d(), refetching:
				_
			}));
			return typeof L != "object" || !(L&&"then"in L) ? (C(o, L, void 0, k), L) : (o = L, a = !0, queueMicrotask(() => a = !1), B(() => {
				q(f ? "refreshing" : "pending"), w()
			}, !1), L.then(ne => C(L, ne, void 0, k), ne => C(L, void 0, hn(ne), k)))
		}
		return Object.defineProperties(m, {state: {get: () => $()}, error: {get: () => v()}, loading: {get() {
			const _ = $();
			return _ == = "pending" || _ == = "refreshing"
		}
		}, latest: {get() {
			if (!f)return m();
			const _ = v();
			if (_ && !o)throw _;
			return d()
		}
		                                                                                                         }
		                                  }), g ? se(() => Q(!1)) : Q(!1), [m, {refetch:Q, mutate:p}]
	}
	function ae(t) {
		return B(t, !1)
	}
	function N(t) {
		if (x == = null)return t();
		const e = x;
		x = null;
		try {
			return t()
		}
		finally{x = e}
	}
	function rn(t, e, n) {
		const s = Array.isArray(t);
		let r, i = n&&n.defer;
		return o => {
			let u;
			if (s) {
				u = Array(t.length);
				for (let c = 0; c < t.length; c++)u[c] = t[c]()
				} else u = t();
			if (i) {
				i = !1;
				return
			}
			const l = N(() => e(u, r, o));
			return r = u, l
		}
	}
	function ut(t) {
		ot(() => N(t))
	}
	function J(t) {
		return A == = null || (A.cleanups == = null ? A.cleanups = [t] : A.cleanups.push(t)), t
	}
	function lt() {
		return x
	}
	function at() {
		return A
	}
	function on(t) {
		if (h && h.running)return t(), h.done;
		const e = x, n = A;
		return Promise.resolve().then(() => {
			x = e, A = n;
			let s;
			return Z&&(s = h || (h = {sources: new Set, effects: [], promises: new Set, disposed: new Set, queue: new Set, running: !0}), s.done || (s.done = new Promise(r => s.resolve = r)), s.running = !0), B(t, !1), x = A = null, s ? s.done : void 0
		})
	}
	function un() {
		return[nn, on]
	}
	function ln(t) {
		U.push.apply(U, t), t.length = 0
	}
	function ce(t, e) {
		const n = Symbol("context");
		return{id: n, Provider: yn(n), defaultValue: t}
	}
	function be(t) {
		let e;
		return (e = Se(A, t.id)) != = void 0 ? e : t.defaultValue
	}
	function an(t) {
		const e = E(t), n = E(() => Ue(e()));
		return n.toArray = () => {const s = n(); return Array.isArray(s) ? s : s != null ? [s] : []}, n
	}
	let Z;
	function cn() {
		return Z || (Z = ce({}))
	}
	function ct() {
		const t = h&&h.running;
		if (this.sources && (t ? this.tState : this.state))if ((t ? this.tState : this.state) == = z)re(this);
			else {
				const e = M;
				M = null, B(() => Oe(this), !1), M = e
			}
		if (x) {
			const e = this.observers ? this.observers.length : 0;
			x.sources ? (x.sources.push(this), x.sourceSlots.push(e)) : (x.sources = [this], x.sourceSlots = [e]), this.observers ? (this.observers.push(x), this.observerSlots.push(x.sources.length - 1)) : (this.observers = [x], this.observerSlots = [x.sources.length - 1])
		}
		return t&&h.sources.has(this) ? this.tValue : this.value
	}
	function ft(t, e, n) {
		let s = h&&h.running&&h.sources.has(t) ? t.tValue : t.value;
		if (!t.comparator || !t.comparator(s, e)) {
			if (h) {
				const r = h.running;
				(r || !n&&h.sources.has(t))&&(h.sources.add(t), t.tValue = e), r || (t.value = e)
			} else t.value = e;
			t.observers&&t.observers.length&&B(() => {
				for (let r = 0; r < t.observers.length; r += 1) {
				const i = t.observers[r], o = h&&h.running;
					o&&h.disposed.has(i) || ((o ? !i.tState : !i.state) && (i.pure ? M.push(i) : U.push(i), i.observers&&yt(i)), o ? i.tState = z : i.state = z)
				}
				if (M.length > 1e6)throw M = [], new Error
			}, !1)
		}
		return e
	}
	function re(t) {
		if (!t.fn)return;
		X(t);
		const e = A, n = x, s = me;
		x = A = t, dt(t, h&&h.running&&h.sources.has(t) ? t.tValue : t.value, s), h&&!h.running&&h.sources.has(t) && queueMicrotask(() => {
			B(() => {
				h&&(h.running = !0), x = A = t, dt(t, t.tValue, s), x = A = null
			}, !1)
		}), x = n, A = e
	}
	function dt(t, e, n) {
		let s;
		try {
			s = t.fn(e)
		} catch (r) {
			return t.pure&&(h&&h.running ? (t.tState = z, t.tOwned&&t.tOwned.forEach(X), t.tOwned = void 0) : (t.state = z, t.owned&&t.owned.forEach(X), t.owned = null)), t.updatedAt = n + 1, gt(r)
		}
		(!t.updatedAt || t.updatedAt <= n)&&(t.updatedAt != null&&"observers"in t ? ft(t, s, !0) : h&&h.running&&t.pure ? (h.sources.add(t), t.tValue = s) : t.value = s, t.updatedAt = n)
	}
	function we(t, e, n, s = z, r) {
		const i = {fn: t, state: s, updatedAt: null, owned: null, sources: null, sourceSlots: null, cleanups: null, value: e, owner: A, context: null, pure: n};
		return h&&h.running&&(i.state = 0, i.tState = s), A == = null || A != = rt&&(h&&h.running&&A.pure ? A.tOwned ? A.tOwned.push(i) : A.tOwned = [i] : A.owned ? A.owned.push(i) : A.owned = [i]), i
	}
	function Ce(t) {
		const e = h&&h.running;
		if ((e ? t.tState : t.state) == = 0)return;
		if ((e ? t.tState : t.state) == = ue)return Oe(t);
		if (t.suspense && N(t.suspense.inFallback))return t.suspense.effects.push(t);
		const n = [t];
		for (; (t = t.owner) && (!t.updatedAt || t.updatedAt < me);) {
			if (e && h.disposed.has(t))return;
			(e ? t.tState : t.state)&&n.push(t)
		}
		for (let s = n.length - 1; s >= 0; s--) {
			if (t = n[s], e) {
				let r = t, i = n[s + 1];
				for (; (r = r.owner) && r != = i;)if (h.disposed.has(r))return
					}
			if ((e ? t.tState : t.state) == = z)re(t);
			else if ((e ? t.tState : t.state) == = ue) {
				const r = M;
				M = null, B(() => Oe(t, n[0]), !1), M = r
			}
		}
	}
	function B(t, e) {
		if (M)return t();
		let n = !1;
		e || (M = []), U ? n = !0 : U = [], me++;
		try {
			const s = t();
			return fn(n), s
		} catch (s) {
			n || (U = null), M = null, gt(s)
		}
	}
	function fn(t) {
		if (M && (ht(M), M = null), t)return;
		let e;
		if (h) {
			if (!h.promises.size && !h.queue.size) {
				const s = h.sources, r = h.disposed;
				U.push.apply(U, h.effects), e = h.resolve;
				for (const i of U)"tState"in i && (i.state = i.tState), delete i.tState;
				h = null, B(() => {
					for (const i of r)X(i);
					for (const i of s) {
						if (i.value = i.tValue, i.owned)for (let o = 0, u = i.owned.length; o < u; o++)X(i.owned[o]);
							i.tOwned&&(i.owned = i.tOwned), delete i.tValue, delete i.tOwned, i.tState = 0
						}
					it(!1)
				}, !1)
			} else if (h.running) {
				h.running = !1, h.effects.push.apply(h.effects, U), U = null, it(!0);
				return
			}
		}
		const n = U;
		U = null, n.length&&B(() => st(n), !1), e&&e()
	}
	function ht(t) {
		for (let e = 0; e < t.length; e++)Ce(t[e])
		}
	function dn(t) {
		let e, n = 0;
		for (e = 0; e < t.length; e++) {
			const s = t[e];
			s.user ? t[n++] = s : Ce(s)
		}
		for (R.context && oe(), e = 0; e < n; e++)Ce(t[e])
		}
	function Oe(t, e) {
		const n = h&&h.running;
		n ? t.tState = 0 : t.state = 0;
		for (let s = 0; s < t.sources.length; s += 1) {
			const r = t.sources[s];
			if (r.sources) {
				const i = n ? r.tState : r.state;
				i == = z ? r != = e&&(!r.updatedAt || r.updatedAt < me) && Ce(r) : i == = ue&&Oe(r, e)
			}
		}
	}
	function yt(t) {
		const e = h&&h.running;
		for (let n = 0; n < t.observers.length; n += 1) {
			const s = t.observers[n];
			(e ? !s.tState : !s.state)&&(e ? s.tState = ue : s.state = ue, s.pure ? M.push(s) : U.push(s), s.observers&&yt(s))
		}
	}
	function X(t) {
		let e;
		if (t.sources)for (; t.sources.length;) {
				const n = t.sources.pop(), s = t.sourceSlots.pop(), r = n.observers;
				if (r && r.length) {
					const i = r.pop(), o = n.observerSlots.pop();
					s < r.length&&(i.sourceSlots[o] = s, r[s] = i, n.observerSlots[s] = o)
				}
			}
		if (h && h.running && t.pure) {
			if (t.tOwned) {
				for (e = t.tOwned.length - 1; e >= 0; e--)X(t.tOwned[e]);
				delete t.tOwned
			}
			pt(t, !0)
		} else if (t.owned) {
			for (e = t.owned.length - 1; e >= 0; e--)X(t.owned[e]);
			t.owned = null
		}
		if (t.cleanups) {
			for (e = t.cleanups.length - 1; e >= 0; e--)t.cleanups[e]();
			t.cleanups = null
		}
		h&&h.running ? t.tState = 0 : t.state = 0, t.context = null
	}
	function pt(t, e) {
		if (e || (t.tState = 0, h.disposed.add(t)), t.owned)for (let n = 0; n < t.owned.length; n++)pt(t.owned[n])
			}
	function hn(t) {
		return t instanceof Error ? t : new Error(typeof t == "string" ? t : "Unknown error", {cause : t})
	}
	function gt(t) {
		throw t
	}
	function Se(t, e) {
		return t ? t.context&&t.context[e] != = void 0 ? t.context[e] : Se(t.owner, e) : void 0
	}
	function Ue(t) {
		if (typeof t == "function" && !t.length)return Ue(t());
		if (Array.isArray(t)) {
			const e = [];
			for (let n = 0; n < t.length; n++) {
				const s = Ue(t[n]);
				Array.isArray(s) ? e.push.apply(e, s) : e.push(s)
			}
			return e
		}
		return t
	}
	function yn(t, e) {
		return function(s) {
			let r;
			return T(() => r = N(() => (A.context = {[t]: s.value}, an(() => s.children))), void 0), r
		}
	}
	const pn = Symbol("fallback");
	function vt(t) {
		for (let e = 0; e < t.length; e++)t[e]()
		}
	function gn(t, e, n = {}) {
		let s = [], r = [], i = [], o = 0, u = e.length > 1 ? [] : null;
		return J(() => vt(i)), () => {
			let l = t() || [], c, a;
			return l[De], N(() => {
				let g = l.length, y, d, p, v, b, P, w, $, q;
				if (g == = 0)o != = 0 && (vt(i), i = [], s = [], r = [], o = 0, u && (u = [])), n.fallback && (s = [pn], r[0] = le(C => (i[0] = C, n.fallback())), o = 1);
				else if (o == = 0) {
					for (r = new Array(g), a = 0; a < g; a++)s[a] = l[a], r[a] = le(f);
					o = g
				} else {
					for (p = new Array(g), v = new Array(g), u && (b = new Array(g)), P = 0, w = Math.min(o, g); P < w && s[P] == = l[P]; P++);
					for (w = o - 1, $ = g - 1; w >= P && $ >= P && s[w] == = l[$]; w--, $--)p[$] = r[w], v[$] = i[w], u && (b[$] = u[w]);
					for (y = new Map, d = new Array($ +1), a = $; a >= P; a--)q = l[a], c = y.get(q), d[a] = c == = void 0 ? -1 : c, y.set(q, a);
					for (c = P; c <= w; c++)q = s[c], a = y.get(q), a != = void 0 && a != = -1 ? (p[a] = r[c], v[a] = i[c], u && (b[a] = u[c]), a = d[a], y.set(q, a)) : i[c]();
					for (a = P; a < g; a++)a in p ? (r[a] = p[a], i[a] = v[a], u && (u[a] = b[a], u[a](a))) : r[a] = le(f);
					r = r.slice(0, o = g), s = l.slice(0)
				}
				return r
			});
			function f(g) {
				if (i[a] = g, u) {
					const[y, d] = j(a);
					return u[a] = d, e(l[a], y)
				}
				return e(l[a])
			}
		}
	}
	function S(t, e) {
		return N(() => t(e || {}))
	}
	function Pe() {
		return!0
	}
	const vn = {get(t, e, n){return e == = W ? n : t.get(e)}, has(t, e){return e == = W ? !0 : t.has(e)}, set: Pe, deleteProperty: Pe, getOwnPropertyDescriptor(t, e) {
		return{configurable: !0, enumerable: !0, get(){return t.get(e)}, set: Pe, deleteProperty: Pe}
	}, ownKeys(t) {
		return t.keys()
	}
	           };
	function Ie(t) {
		return (t = typeof t == "function" ? t() : t) ? t : {}
	}
	function mn(...t) {
		let e = !1;
		for (let s = 0; s < t.length; s++) {
			const r = t[s];
			e = e || !!r&&W in r, t[s] = typeof r == "function" ? (e = !0, E(r)) : r
		}
		if (e)return new Proxy({get(s) {
			for (let r = t.length - 1; r >= 0; r--) {
				const i = Ie(t[r])[s];
				if (i != = void 0)return i
				}
		}, has(s) {
			for (let r = t.length - 1; r >= 0; r--)if (s in Ie(t[r]))return!0;
			return!1
		}, keys() {
			const s = [];
			for (let r = 0; r < t.length; r++)s.push(...Object.keys(Ie(t[r])));
			return[...new Set(s)]
		}}, vn);
		const n = {};
		for (let s = t.length - 1; s >= 0; s--)if (t[s]) {
				const r = Object.getOwnPropertyDescriptors(t[s]);
				for (const i in r)i in n || Object.defineProperty(n, i, {enumerable: !0, get() {
					for (let o = t.length - 1; o >= 0; o--) {
						const u = (t[o] || {})[i];
						if (u != = void 0)return u
						}
				}
				                                                        })
			}
		return n
	}
	let bn = 0;
	function wn() {
		const t = R.context;
		return t ? `$ {t.id}$ {t.count++}`:
		       `cl - $ {bn++}`
	}
	const Cn = t => `Stale read from < $ {
		t
	} > .`;
	function On(t) {
		const e = "fallback"in t&& {fallback: () => t.fallback};
		return E(gn(() => t.each, t.children, e || void 0))
	}
	function ee(t) {
		const e = t.keyed, n = E(() => t.when, void 0, {
equals:
			(s, r) => e ? s == = r : !s == !r
		});
		return E(() => {
			const s = n();
			if (s) {
			const r = t.children;
			return typeof r == "function" && r.length > 0 ? N(() => r(e ? s : () => {
				if (!N(n))throw Cn("Show");
					return t.when
				})): r
				}
				return t.fallback
			}, void 0, void 0)
		}
		const Sn = ce();
	function Pn(t) {
		let e = 0, n, s, r, i, o;
		const[u, l] = j(!1), c = cn(), a = {increment: () => {++e == = 1 && l(!0)}, decrement: () => {--e == = 0 && l(!1)}, inFallback: u, effects: [], resolved: !1}, f = at();
		if (R.context && R.load) {
			const d = R.context.id + R.context.count;
			let p = R.load(d);
			if (p && (r = p[0]) && r != = "$$f") {
				(typeof r != "object" || !("then"in r))&&(r = Promise.resolve(r));
				const[v, b] = j(void 0, {equals: !1});
				i = v, r.then(P => {
					if (P || R.done)return P && (o = P), b();
					R.gather(d), oe(s), b(), oe()
				})
			}
		}
		const g = be(Sn);
		g&&(n = g.register(a.inFallback));
		let y;
		return J(() => y&&y()), S(c.Provider, {value: a, get children() {
			return E(() => {
				if (o)throw o;
				if (s = R.context, i)return i(), i = void 0;
					s&&r == = "$$f" && oe();
					const d = E(() => t.children);
					return E(p => {
const v = a.inFallback(), {showContent: b = !0, showFallback: P = !0} = n ? n() : {};
					if ((!v || r && r != = "$$f") && b)return a.resolved = !0, y && y(), y = s = r = void 0, ln(a.effects), d();
							if (P)return y ? p : le(w => (y = w, s && (oe( {
							                                  id:
							                                  s.id + "f", count: 0
							                              }), s = void 0), t.fallback), f)
							})
			})
		}
		})
	}
	function $n(t, e, n) {
		let s = n.length, r = e.length, i = s, o = 0, u = 0, l = e[r - 1].nextSibling, c = null;
		for (; o < r || u < i;) {
			if (e[o] == = n[u]) {
				o++, u++;
				continue
			}
			for (; e[r - 1] == = n[i - 1];)r--, i--;
			if (r == = o) {
				const a = i < s ? u ? n[u - 1].nextSibling : n[i - u] : l;
				for (; u < i;)t.insertBefore(n[u++], a)
				} else if (i == = u)for (; o < r;)(!c || !c.has(e[o])) && e[o].remove(), o++;
			else if (e[o] == = n[i - 1] && n[u] == = e[r - 1]) {
				const a = e[--r].nextSibling;
				t.insertBefore(n[u++], e[o++].nextSibling), t.insertBefore(n[--i], a), e[r] = n[i]
			} else {
				if (!c) {
					c = new Map;
					let f = u;
					for (; f < i;)c.set(n[f], f++)
					}
				const a = c.get(e[o]);
				if (a != null)if (u < a && a < i) {
						let f = o, g = 1, y;
						for (; ++f < r && f < i && !((y = c.get(e[f])) == null || y != = a + g);)g++;
						if (g > a - u) {
							const d = e[o];
							for (; u < a;)t.insertBefore(n[u++], d)
							} else t.replaceChild(n[u++], e[o++])
						} else o++;
				else e[o++].remove()
				}
		}
	}
	const mt = "_$DX_DELEGATE";
	function An(t, e, n, s = {}) {
		let r;
		return le(i => {
			r = i, e == = document ? t() : F(e, t(), e.firstChild ? null : void 0, n)
		}, s.owner), () => {
			r(), e.textContent = ""
		}
	}
	function D(t, e, n) {
		let s;
		const r = () => {const o = document.createElement("template"); return o.innerHTML = t, n ? o.content.firstChild.firstChild : o.content.firstChild}, i = e ? () => (s || (s = r())).cloneNode(!0) : () => N(() => document.importNode(s || (s = r()), !0));
		return i.cloneNode = i, i
	}
	function $e(t, e = window.document) {
		const n = e[mt] || (e[mt] = new Set);
		for (let s = 0, r = t.length; s < r; s++) {
			const i = t[s];
			n.has(i) || (n.add(i), e.addEventListener(i, En))
		}
	}
	function bt(t, e, n) {
		n == null ? t.removeAttribute(e) : t.setAttribute(e, n)
	}
	function K(t, e) {
		e == null ? t.removeAttribute("class") : t.className = e
	}
	function _n(t, e, n, s) {
		if (s)Array.isArray(n) ? (t[`$$$ {e}`] = n[0], t[`$$$ {e} Data`] = n[1]): t[`$$$ {e}`] = n;
		else if (Array.isArray(n)) {
			const r = n[0];
			t.addEventListener(e, n[0] = i => r.call(t, n[1], i))
		} else t.addEventListener(e, n)
		}
	function xn(t, e, n = {}) {
		const s = Object.keys(e || {}), r = Object.keys(n);
		let i, o;
		for (i = 0, o = r.length; i < o; i++) {
			const u = r[i];
			!u || u == = "undefined" || e[u] || (wt(t, u, !1), delete n[u])
		}
		for (i = 0, o = s.length; i < o; i++) {
			const u = s[i], l = !!e[u];
			!u || u == = "undefined" || n[u] == = l || !l || (wt(t, u, !0), n[u] = l)
		}
		return n
	}
	function fe(t, e, n) {
		if (!e)return n ? bt(t, "style") : e;
		const s = t.style;
		if (typeof e == "string")return s.cssText = e;
		typeof n == "string"&&(s.cssText = n = void 0), n || (n = {}), e || (e = {});
		let r, i;
		for (i in n)e[i] == null && s.removeProperty(i), delete n[i];
		for (i in e)r = e[i], r != = n[i] && (s.setProperty(i, r), n[i] = r);
		return n
	}
	function F(t, e, n, s) {
		if (n != = void 0 && !s && (s = []), typeof e != "function")return Ae(t, e, s, n);
		T(r => Ae(t, e(), r, n), s)
	}
	function wt(t, e, n) {
		const s = e.trim().split( / \s + / );
		for (let r = 0, i = s.length; r < i; r++)t.classList.toggle(s[r], n)
		}
	function En(t) {
		const e = `$$$ {t.type}`;
		let n = t.composedPath&&t.composedPath()[0] || t.target;
		for (t.target != = n && Object.defineProperty(t, "target", {configurable: !0, value: n}), Object.defineProperty(t, "currentTarget", {configurable: !0, get() {
		return n || document
	}
	                                                                                                                                    }), R.registry&&!R.done&&(R.done = _$HY.done = !0); n;) {
			const s = n[e];
			if (s && !n.disabled) {
				const r = n[`$ {e} Data`];
				if (r != = void 0 ? s.call(n, r, t) : s.call(n, t), t.cancelBubble)return
				}
			n = n._$host || n.parentNode || n.host
		}
	}
	function Ae(t, e, n, s, r) {
		if (R.context) {
			!n&&(n = [...t.childNodes]);
			let u = [];
			for (let l = 0; l < n.length; l++) {
				const c = n[l];
				c.nodeType == = 8 && c.data.slice(0, 2) == = "!$" ? c.remove() : u.push(c)
			}
			n = u
		}
		for (; typeof n == "function";)n = n();
		if (e == = n)return n;
		const i = typeof e, o = s != = void 0;
		if (t = o && n[0] && n[0].parentNode || t, i == = "string" || i == = "number") {
			if (R.context)return n;
			if (i == = "number" && (e = e.toString()), o) {
				let u = n[0];
				u&&u.nodeType == = 3 ? u.data = e : u = document.createTextNode(e), n = ie(t, n, s, u)
			} else n != = "" && typeof n == "string" ? n = t.firstChild.data = e : n = t.textContent = e
			} else if (e == null || i == = "boolean") {
			if (R.context)return n;
			n = ie(t, n, s)
		} else {
			if (i == = "function")return T(() => {
				let u = e();
				for (; typeof u == "function";)u = u();
				n = Ae(t, u, n, s)
			}), () => n;
			if (Array.isArray(e)) {
				const u = [], l = n&&Array.isArray(n);
				if (Ne(u, e, n, r))return T(() => n = Ae(t, u, n, s, !0)), () => n;
				if (R.context) {
					if (!u.length)return n;
					for (let c = 0; c < u.length; c++)if (u[c].parentNode)return n = u
						}
				if (u.length == = 0) {
					if (n = ie(t, n, s), o)return n
					} else l ? n.length == = 0 ? Ct(t, u, s) : $n(t, n, u) : (n && ie(t), Ct(t, u));
				n = u
			} else if (e.nodeType) {
				if (R.context && e.parentNode)return n = o ? [e] : e;
				if (Array.isArray(n)) {
					if (o)return n = ie(t, n, s, e);
					ie(t, n, null, e)
				} else n == null || n == = "" || !t.firstChild ? t.appendChild(e) : t.replaceChild(e, t.firstChild);
				n = e
			} else console.warn("Unrecognized value. Skipped inserting", e)
			}
		return n
	}
	function Ne(t, e, n, s) {
		let r = !1;
		for (let i = 0, o = e.length; i < o; i++) {
			let u = e[i], l = n&&n[i], c;
			if (!(u == null || u == = !0 || u == = !1))if ((c = typeof u) == "object" && u.nodeType)t.push(u);
				else if (Array.isArray(u))r = Ne(t, u, l) || r;
				else if (c == = "function")if (s) {
						for (; typeof u == "function";)u = u();
						r = Ne(t, Array.isArray(u) ? u : [u], Array.isArray(l) ? l : [l]) || r
					} else t.push(u), r = !0;
				else {
					const a = String(u);
					l&&l.nodeType == = 3 && l.data == = a ? t.push(l) : t.push(document.createTextNode(a))
				}
		}
		return r
	}
	function Ct(t, e, n = null) {
		for (let s = 0, r = e.length; s < r; s++)t.insertBefore(e[s], n)
		}
	function ie(t, e, n, s) {
		if (n == = void 0)return t.textContent = "";
		const r = s || document.createTextNode("");
		if (e.length) {
			let i = !1;
			for (let o = e.length - 1; o >= 0; o--) {
				const u = e[o];
				if (r != = u) {
					const l = u.parentNode == = t;
					!i&&!o ? l ? t.replaceChild(r, u) : t.insertBefore(r, n) : l&&u.remove()
				} else i = !0
				}
		} else t.insertBefore(r, n);
		return[r]
	}
	class de {
		constructor() {
			this.listeners = [], this.subscribe = this.subscribe.bind(this)
		} subscribe(e) {
			return this.listeners.push(e), this.onSubscribe(), () => {
				this.listeners = this.listeners.filter(n => n != = e), this.onUnsubscribe()
			}
		} hasListeners() {
			return this.listeners.length > 0
		} onSubscribe() {} onUnsubscribe() {}
	} const he = typeof window > "u" || "Deno"in window;
	function H() {} function Rn(t, e) {
		return typeof t == "function" ? t(e) : t
	}
	function je(t) {
		return typeof t == "number" && t >= 0 && t != = 1 / 0
	}
	function Ot(t, e) {
		return Math.max(t + (e || 0) - Date.now(), 0)
	}
	function _e(t, e, n) {
		return Ee(t) ? typeof e == "function" ? {...n, queryKey : t, queryFn : e}: {
		           ...e, queryKey:
		           t
		       }:
		       t
	}
	function Y(t, e, n) {
		return Ee(t) ? [ {...e, queryKey:t}, n]:
		       [t || {}, e]
	}
	function St(t, e) {
		const{type: n = "all", exact: s, fetchStatus: r, predicate: i, queryKey: o, stale: u} = t;
		if (Ee(o)) {
			if (s) {
				if (e.queryHash != = Ke(o, e.options))return!1
				} else if (!xe(e.queryKey, o))return!1
			}
		if (n != = "all") {
			const l = e.isActive();
			if (n == = "active" && !l || n == = "inactive" && l)return!1
			}
		return!(typeof u == "boolean" && e.isStale() != = u || typeof r < "u" && r != = e.state.fetchStatus || i&&!i(e))
	}
	function Pt(t, e) {
		const{exact: n, fetching: s, predicate: r, mutationKey: i} = t;
		if (Ee(i)) {
			if (!e.options.mutationKey)return!1;
			if (n) {
				if (te(e.options.mutationKey) != = te(i))return!1
				} else if (!xe(e.options.mutationKey, i))return!1
			}
		return!(typeof s == "boolean" && e.state.status == = "loading" != = s || r&&!r(e))
	}
	function Ke(t, e) {
		return (e ? .queryKeyHashFn || te)(t)
	}
	function te(t) {
		return JSON.stringify(t, (e, n) => Be(n) ? Object.keys(n).sort().reduce((s, r) => (s[r] = n[r], s), {}): n)
	}
	function xe(t, e) {
		return $t(t, e)
	}
	function $t(t, e) {
		return t == = e ? !0 : typeof t != typeof e ? !1 : t&&e&&typeof t == "object" && typeof e == "object" ? !Object.keys(e).some(n => !$t(t[n], e[n])) : !1
	}
	function At(t, e) {
		if (t == = e)return t;
		const n = xt(t) && xt(e);
		if (n || Be(t) && Be(e)) {
			const s = n ? t.length : Object.keys(t).length, r = n ? e : Object.keys(e), i = r.length, o = n ? [] : {};
			let u = 0;
			for (let l = 0; l < i; l++) {
				const c = n ? l : r[l];
				o[c] = At(t[c], e[c]), o[c] == = t[c] && u++
			}
			return s == = i&&u == = s ? t : o
		}
		return e
	}
	function _t(t, e) {
		if (t && !e || e && !t)return!1;
		for (const n in t)if (t[n] != = e[n])return!1;
		return!0
	}
	function xt(t) {
		return Array.isArray(t) && t.length == = Object.keys(t).length
	}
	function Be(t) {
		if (!Et(t))return!1;
		const e = t.constructor;
		if (typeof e > "u")return!0;
		const n = e.prototype;
		return!(!Et(n) || !n.hasOwnProperty("isPrototypeOf"))
	}
	function Et(t) {
		return Object.prototype.toString.call(t) == = "[object Object]"
	}
	function Ee(t) {
		return Array.isArray(t)
	}
	function Rt(t) {
		return new Promise(e => {
			setTimeout(e, t)
		})
	}
	function Ft(t) {
		Rt(0).then(t)
	}
	function Fn() {
		if (typeof AbortController == "function")return new AbortController
		}
	function He(t, e, n) {
		return n.isDataEqual != null&&n.isDataEqual(t, e) ? t : typeof n.structuralSharing == "function" ? n.structuralSharing(t, e) : n.structuralSharing != = !1 ? At(t, e) : e
	}
	class kn extends de {
		constructor() {
			super(), this.setup = e => {
				if (!he && window.addEventListener) {
				const n = () => e();
					return window.addEventListener("visibilitychange", n, !1), window.addEventListener("focus", n, !1), () => {
						window.removeEventListener("visibilitychange", n), window.removeEventListener("focus", n)
					}
				}
			}
		} onSubscribe() {
			this.cleanup || this.setEventListener(this.setup)
		} onUnsubscribe() {
			if (!this.hasListeners()) {
				var e;
				(e = this.cleanup) == null || e.call(this), this.cleanup = void 0
			}
		} setEventListener(e) {
			var n;
			this.setup = e, (n = this.cleanup) == null || n.call(this), this.cleanup = e(s => {
				typeof s == "boolean" ? this.setFocused(s) : this.onFocus()
			})
		} setFocused(e) {
			this.focused = e, e&&this.onFocus()
		} onFocus() {
			this.listeners.forEach(e => {
				e()
			})
		} isFocused() {
			return typeof this.focused == "boolean" ? this.focused : typeof document > "u" ? !0 : [void 0, "visible", "prerender"].includes(document.visibilityState)
		}
	} const Re = new kn;
	class Tn extends de {
		constructor() {
			super(), this.setup = e => {
				if (!he && window.addEventListener) {
				const n = () => e();
					return window.addEventListener("online", n, !1), window.addEventListener("offline", n, !1), () => {
						window.removeEventListener("online", n), window.removeEventListener("offline", n)
					}
				}
			}
		} onSubscribe() {
			this.cleanup || this.setEventListener(this.setup)
		} onUnsubscribe() {
			if (!this.hasListeners()) {
				var e;
				(e = this.cleanup) == null || e.call(this), this.cleanup = void 0
			}
		} setEventListener(e) {
			var n;
			this.setup = e, (n = this.cleanup) == null || n.call(this), this.cleanup = e(s => {
				typeof s == "boolean" ? this.setOnline(s) : this.onOnline()
			})
		} setOnline(e) {
			this.online = e, e&&this.onOnline()
		} onOnline() {
			this.listeners.forEach(e => {
				e()
			})
		} isOnline() {
			return typeof this.online == "boolean" ? this.online : typeof navigator > "u" || typeof navigator.onLine > "u" ? !0 : navigator.onLine
		}
	} const Fe = new Tn;
	function qn(t) {
		return Math.min(1e3 * 2 * *t, 3e4)
	}
	function ke(t) {
		return (t ?? "online") == = "online" ? Fe.isOnline() : !0
	}
	class kt {
		constructor(e) {
			this.revert = e ? .revert, this.silent = e ? .silent
		}
	} function Te(t) {
		return t instanceof kt
	}
	function Tt(t) {
		let e = !1, n = 0, s = !1, r, i, o;
		const u = new Promise((v, b) => {
			i = v, o = b
		}), l = v => {s || (y(new kt(v)), t.abort == null || t.abort())}, c = () => {e = !0}, a = () => {e = !1}, f = () => !Re.isFocused() || t.networkMode != = "always" && !Fe.isOnline(), g = v => {s || (s = !0, t.onSuccess == null || t.onSuccess(v), r ? .(), i(v))}, y = v => {s || (s = !0, t.onError == null || t.onError(v), r ? .(), o(v))}, d = () => new Promise(v => {
			r = b => {const P = s || !f(); return P&&v(b), P}, t.onPause == null || t.onPause()
		}).then(() => {
			r = void 0, s || t.onContinue == null || t.onContinue()
		}), p = () => {
			if (s)return;
			let v;
			try {
				v = t.fn()
				} catch (b) {
					v = Promise.reject(b)
				}
			Promise.resolve(v).then(g).catch(b => {
				var P, w;
				if (s)return;
				const $ = (P = t.retry) != null ? P : 3, q = (w = t.retryDelay) != null ? w : qn, C = typeof q == "function" ? q(n, b) : q, O = $ == = !0 || typeof $ == "number" && n < $ || typeof $ == "function" && $(n, b);
				if (e || !O) {
					y(b);
					return
				}
				n++, t.onFail == null || t.onFail(n, b), Rt(C).then(() => {
					if (f())return d()
				}).then(() => {
					e ? y(b) : p()
				})
			})
		};
		return ke(t.networkMode) ? p() : d().then(p), {promise : u, cancel : l, continue : () => r ? .() ? u : Promise.resolve(), cancelRetry : c, continueRetry : a}
	}
	const Ve = console;
	function Qn() {
		let t = [], e = 0, n = a => {a()}, s = a => {
			a()
		};
		const r = a => {let f; e++; try {
				f = a()
			} finally{e--, e || u()} return f}, i = a => {e ? t.push(a) : Ft(() => {n(a)})}, o = a => (...f) => {i(() => {a(...f)})}, u = () => {
			const a = t;
			t = [], a.length&&Ft(() => {
				s(() => {
					a.forEach(f => {
						n(f)
					})
				})
			})
		};
		return{batch: r, batchCalls: o, schedule: i, setNotifyFunction: a => {n = a}, setBatchNotifyFunction: a => {s = a}}
	}
	const I = Qn();
	class qt {
		destroy() {
			this.clearGcTimeout()
		} scheduleGc() {
			this.clearGcTimeout(), je(this.cacheTime)&&(this.gcTimeout = setTimeout(() => {
				this.optionalRemove()
			}, this.cacheTime))
		} updateCacheTime(e) {
			this.cacheTime = Math.max(this.cacheTime || 0, e ?? (he ? 1 / 0 : 5 * 60 * 1e3))
		} clearGcTimeout() {
			this.gcTimeout&&(clearTimeout(this.gcTimeout), this.gcTimeout = void 0)
		}
	} class Ln extends qt {
		constructor(e) {
			super(), this.abortSignalConsumed = !1, this.defaultOptions = e.defaultOptions, this.setOptions(e.options), this.observers = [], this.cache = e.cache, this.logger = e.logger || Ve, this.queryKey = e.queryKey, this.queryHash = e.queryHash, this.initialState = e.state || Dn(this.options), this.state = this.initialState, this.scheduleGc()
		} get meta() {
			return this.options.meta
		} setOptions(e) {
			this.options = {...this.defaultOptions, ...e}, this.updateCacheTime(this.options.cacheTime)
		} optionalRemove() {
			!this.observers.length&&this.state.fetchStatus == = "idle" && this.cache.remove(this)
		} setData(e, n) {
			const s = He(this.state.data, e, this.options);
			return this.dispatch({data: s, type: "success", dataUpdatedAt: n ? .updatedAt, manual : n ? .manual}), s
		} setState(e, n) {
			this.dispatch({type: "setState", state: e, setStateOptions: n})
		} cancel(e) {
			var n;
			const s = this.promise;
			return (n = this.retryer) == null || n.cancel(e), s ? s.then(H).catch(H) : Promise.resolve()
		} destroy() {
			super.destroy(), this.cancel({silent: !0})
		} reset() {
			this.destroy(), this.setState(this.initialState)
		} isActive() {
			return this.observers.some(e => e.options.enabled != = !1)
		} isDisabled() {
			return this.getObserversCount() > 0 && !this.isActive()
		} isStale() {
			return this.state.isInvalidated || !this.state.dataUpdatedAt || this.observers.some(e => e.getCurrentResult().isStale)
		} isStaleByTime(e = 0) {
			return this.state.isInvalidated || !this.state.dataUpdatedAt || !Ot(this.state.dataUpdatedAt, e)
		} onFocus() {
			var e;
			const n = this.observers.find(s => s.shouldFetchOnWindowFocus());
			n&&n.refetch({cancelRefetch: !1}), (e = this.retryer) == null || e.continue()
		} onOnline() {
			var e;
			const n = this.observers.find(s => s.shouldFetchOnReconnect());
			n&&n.refetch({cancelRefetch: !1}), (e = this.retryer) == null || e.continue()
		} addObserver(e) {
			this.observers.indexOf(e) == = -1 && (this.observers.push(e), this.clearGcTimeout(), this.cache.notify({type: "observerAdded", query: this, observer: e}))
		} removeObserver(e) {
			this.observers.indexOf(e) != = -1 && (this.observers = this.observers.filter(n => n != = e), this.observers.length || (this.retryer&&(this.abortSignalConsumed ? this.retryer.cancel({revert: !0}): this.retryer.cancelRetry()), this.scheduleGc()), this.cache.notify({type: "observerRemoved", query: this, observer: e}))
		} getObserversCount() {
			return this.observers.length
		} invalidate() {
			this.state.isInvalidated || this.dispatch({type: "invalidate"})
		} fetch(e, n) {
			var s, r;
			if (this.state.fetchStatus != = "idle") {
				if (this.state.dataUpdatedAt && n != null && n.cancelRefetch)this.cancel({silent: !0});
				else if (this.promise) {
					var i;
					return (i = this.retryer) == null || i.continueRetry(), this.promise
				}
			}
			if (e && this.setOptions(e), !this.options.queryFn) {
				const y = this.observers.find(d => d.options.queryFn);
				y&&this.setOptions(y.options)
			}
			Array.isArray(this.options.queryKey);
			const o = Fn(), u = {queryKey: this.queryKey, pageParam: void 0, meta: this.meta}, l = y => {
				Object.defineProperty(y, "signal", {enumerable: !0, get: () => {if (o)return this.abortSignalConsumed = !0, o.signal}})
		};
		l(u);
			const c = () => this.options.queryFn ? (this.abortSignalConsumed = !1, this.options.queryFn(u)) : Promise.reject("Missing queryFn"), a = {fetchOptions : n, options : this.options, queryKey : this.queryKey, state : this.state, fetchFn : c};
			if (l(a), (s = this.options.behavior) == null || s.onFetch(a), this.revertState = this.state, this.state.fetchStatus == = "idle" || this.state.fetchMeta != = ((r = a.fetchOptions) == null ? void 0 : r.meta)) {
				var f;
				this.dispatch({type: "fetch", meta: (f = a.fetchOptions) == null ? void 0 : f.meta})
			}
			const g = y => {
if (Te(y) && y.silent || this.dispatch({type: "error", error: y}), !Te(y)) {
				var d, p, v, b;
				(d = (p = this.cache.config).onError) == null || d.call(p, y, this), (v = (b = this.cache.config).onSettled) == null || v.call(b, this.state.data, y, this)
				}
				this.isFetchingOptimistic || this.scheduleGc(), this.isFetchingOptimistic = !1
			};
			return this.retryer = Tt({fn: a.fetchFn, abort: o ? .abort.bind(o), onSuccess : y => {var d, p, v, b; if (typeof y > "u") {
					g(new Error(this.queryHash + " data is undefined"));
					return
				} this.setData(y), (d = (p = this.cache.config).onSuccess) == null || d.call(p, y, this), (v = (b = this.cache.config).onSettled) == null || v.call(b, y, this.state.error, this), this.isFetchingOptimistic || this.scheduleGc(), this.isFetchingOptimistic = !1
			}, onError: g, onFail: (y, d) => {this.dispatch({type: "failed", failureCount: y, error: d})}, onPause: () => {this.dispatch({type: "pause"})}, onContinue: () => {this.dispatch({type: "continue"})}, retry: a.options.retry, retryDelay: a.options.retryDelay, networkMode: a.options.networkMode}), this.promise = this.retryer.promise, this.promise
		} dispatch(e) {
			const n = s => {
				var r, i;
				switch (e.type) {
					case"failed":
						return{...s, fetchFailureCount: e.failureCount, fetchFailureReason: e.error};
					case"pause":
						return{...s, fetchStatus: "paused"};
					case"continue":
						return{...s, fetchStatus: "fetching"};
					case"fetch":
						return{...s, fetchFailureCount: 0, fetchFailureReason: null, fetchMeta: (r = e.meta) != null ? r : null, fetchStatus : ke(this.options.networkMode) ? "fetching" : "paused", ...!s.dataUpdatedAt&&{error : null, status : "loading"}};
					case"success":
						return{...s, data: e.data, dataUpdateCount: s.dataUpdateCount + 1, dataUpdatedAt: (i = e.dataUpdatedAt) != null ? i : Date.now(), error : null, isInvalidated : !1, status : "success", ...!e.manual&&{fetchStatus : "idle", fetchFailureCount : 0, fetchFailureReason : null}};
					case"error":
						const o = e.error;
						return Te(o) && o.revert&&this.revertState ? {...this.revertState}: {
						           ...s, error:
						           o, errorUpdateCount:
						           s.errorUpdateCount + 1, errorUpdatedAt:
						           Date.now(), fetchFailureCount:
						           s.fetchFailureCount + 1, fetchFailureReason:
						           o, fetchStatus: "idle"
						           , status: "error"
					       };
					case"invalidate":
						return{...s, isInvalidated: !0};
					case"setState":
						return{...s, ...e.state}
				}
			};
			this.state = n(this.state), I.batch(() => {
				this.observers.forEach(s => {
					s.onQueryUpdate(e)
				}), this.cache.notify({query: this, type: "updated", action: e})
			})
		}
	} function Dn(t) {
		const e = typeof t.initialData == "function" ? t.initialData() : t.initialData, n = typeof e < "u", s = n ? typeof t.initialDataUpdatedAt == "function" ? t.initialDataUpdatedAt() : t.initialDataUpdatedAt : 0;
		return{data: e, dataUpdateCount: 0, dataUpdatedAt: n ? s ?? Date.now() : 0, error : null, errorUpdateCount : 0, errorUpdatedAt : 0, fetchFailureCount : 0, fetchFailureReason : null, fetchMeta : null, isInvalidated : !1, status : n ? "success" : "loading", fetchStatus : "idle"}
	}
	class Mn extends de {
		constructor(e) {
			super(), this.config = e || {}, this.queries = [], this.queriesMap = {}
		} build(e, n, s) {
			var r;
			const i = n.queryKey, o = (r = n.queryHash) != null ? r : Ke(i, n);
			let u = this.get(o);
			return u || (u = new Ln({cache: this, logger: e.getLogger(), queryKey: i, queryHash: o, options: e.defaultQueryOptions(n), state: s, defaultOptions: e.getQueryDefaults(i)}), this.add(u)), u
		} add(e) {
			this.queriesMap[e.queryHash] || (this.queriesMap[e.queryHash] = e, this.queries.push(e), this.notify({type: "added", query: e}))
		} remove(e) {
			const n = this.queriesMap[e.queryHash];
			n&&(e.destroy(), this.queries = this.queries.filter(s => s != = e), n == = e&&delete this.queriesMap[e.queryHash], this.notify({type: "removed", query: e}))
		} clear() {
			I.batch(() => {
				this.queries.forEach(e => {
					this.remove(e)
				})
			})
		} get(e) {
			return this.queriesMap[e]
		} getAll() {
			return this.queries
		} find(e, n) {
			const[s] = Y(e, n);
			return typeof s.exact > "u" && (s.exact = !0), this.queries.find(r => St(s, r))
		} findAll(e, n) {
			const[s] = Y(e, n);
			return Object.keys(s).length > 0 ? this.queries.filter(r => St(s, r)) : this.queries
		} notify(e) {
			I.batch(() => {
				this.listeners.forEach(n => {
					n(e)
				})
			})
		} onFocus() {
			I.batch(() => {
				this.queries.forEach(e => {
					e.onFocus()
				})
			})
		} onOnline() {
			I.batch(() => {
				this.queries.forEach(e => {
					e.onOnline()
				})
			})
		}
	} class Un extends qt {
		constructor(e) {
			super(), this.defaultOptions = e.defaultOptions, this.mutationId = e.mutationId, this.mutationCache = e.mutationCache, this.logger = e.logger || Ve, this.observers = [], this.state = e.state || In(), this.setOptions(e.options), this.scheduleGc()
		} setOptions(e) {
			this.options = {...this.defaultOptions, ...e}, this.updateCacheTime(this.options.cacheTime)
		} get meta() {
			return this.options.meta
		} setState(e) {
			this.dispatch({type: "setState", state: e})
		} addObserver(e) {
			this.observers.indexOf(e) == = -1 && (this.observers.push(e), this.clearGcTimeout(), this.mutationCache.notify({type: "observerAdded", mutation: this, observer: e}))
		} removeObserver(e) {
			this.observers = this.observers.filter(n => n != = e), this.scheduleGc(), this.mutationCache.notify({type: "observerRemoved", mutation: this, observer: e})
		} optionalRemove() {
			this.observers.length || (this.state.status == = "loading" ? this.scheduleGc() : this.mutationCache.remove(this))
		} continue() {
			var e, n;
			return (e = (n = this.retryer) == null ? void 0 : n.continue()) != null ? e : this.execute()
		} async execute() {
			const e = () => {var O; return this.retryer = Tt({fn: () => this.options.mutationFn ? this.options.mutationFn(this.state.variables) : Promise.reject("No mutationFn found"), onFail : (m, Q) => {this.dispatch({type: "failed", failureCount: m, error: Q})}, onPause: () => {this.dispatch({type: "pause"})}, onContinue: () => {this.dispatch({type: "continue"})}, retry: (O = this.options.retry) != null ? O : 0, retryDelay : this.options.retryDelay, networkMode : this.options.networkMode}), this.retryer.promise}, n = this.state.status == = "loading";
			try {
				var s, r, i, o, u, l, c, a;
				if (!n) {
					var f, g, y, d;
					this.dispatch({type: "loading", variables: this.options.variables}), await((f = (g = this.mutationCache.config).onMutate) == null ? void 0 : f.call(g, this.state.variables, this));
					const m = await((y = (d = this.options).onMutate) == null ? void 0 : y.call(d, this.state.variables));
					m != = this.state.context&&this.dispatch({type: "loading", context: m, variables: this.state.variables})
				}
				const O = await e();
				return await((s = (r = this.mutationCache.config).onSuccess) == null ? void 0 : s.call(r, O, this.state.variables, this.state.context, this)), await((i = (o = this.options).onSuccess) == null ? void 0 : i.call(o, O, this.state.variables, this.state.context)), await((u = (l = this.mutationCache.config).onSettled) == null ? void 0 : u.call(l, O, null, this.state.variables, this.state.context, this)), await((c = (a = this.options).onSettled) == null ? void 0 : c.call(a, O, null, this.state.variables, this.state.context)), this.dispatch({type: "success", data: O}), O
			} catch (O) {
				try {
					var p, v, b, P, w, $, q, C;
					throw await((p = (v = this.mutationCache.config).onError) == null ? void 0 : p.call(v, O, this.state.variables, this.state.context, this)), await((b = (P = this.options).onError) == null ? void 0 : b.call(P, O, this.state.variables, this.state.context)), await((w = ($ = this.mutationCache.config).onSettled) == null ? void 0 : w.call($, void 0, O, this.state.variables, this.state.context, this)), await((q = (C = this.options).onSettled) == null ? void 0 : q.call(C, void 0, O, this.state.variables, this.state.context)), O
				}
				finally{this.dispatch({type: "error", error: O})}
			}
		} dispatch(e) {
			const n = s => {
				switch (e.type) {
				case"failed":
					return{...s, failureCount: e.failureCount, failureReason: e.error};
				case"pause":
					return{...s, isPaused: !0};
				case"continue":
					return{...s, isPaused: !1};
				case"loading":
					return{...s, context: e.context, data: void 0, failureCount: 0, failureReason: null, error: null, isPaused: !ke(this.options.networkMode), status: "loading", variables: e.variables};
				case"success":
					return{...s, data: e.data, failureCount: 0, failureReason: null, error: null, status: "success", isPaused: !1};
				case"error":
					return{...s, data: void 0, error: e.error, failureCount: s.failureCount + 1, failureReason: e.error, isPaused: !1, status: "error"};
				case"setState":
					return{...s, ...e.state}
			}
		};
		this.state = n(this.state), I.batch(() => {
				this.observers.forEach(s => {
					s.onMutationUpdate(e)
				}), this.mutationCache.notify({mutation: this, type: "updated", action: e})
			})
		}
	} function In() {
		return{context: void 0, data: void 0, error: null, failureCount: 0, failureReason: null, isPaused: !1, status: "idle", variables: void 0}
	}
	class Nn extends de {
		constructor(e) {
			super(), this.config = e || {}, this.mutations = [], this.mutationId = 0
		} build(e, n, s) {
			const r = new Un({mutationCache: this, logger: e.getLogger(), mutationId: ++this.mutationId, options: e.defaultMutationOptions(n), state: s, defaultOptions: n.mutationKey ? e.getMutationDefaults(n.mutationKey) : void 0});
			return this.add(r), r
		} add(e) {
			this.mutations.push(e), this.notify({type: "added", mutation: e})
		} remove(e) {
			this.mutations = this.mutations.filter(n => n != = e), this.notify({type: "removed", mutation: e})
		} clear() {
			I.batch(() => {
				this.mutations.forEach(e => {
					this.remove(e)
				})
			})
		} getAll() {
			return this.mutations
		} find(e) {
			return typeof e.exact > "u" && (e.exact = !0), this.mutations.find(n => Pt(e, n))
		} findAll(e) {
			return this.mutations.filter(n => Pt(e, n))
		} notify(e) {
			I.batch(() => {
				this.listeners.forEach(n => {
					n(e)
				})
			})
		} resumePausedMutations() {
			var e;
			return this.resuming = ((e = this.resuming) != null ? e : Promise.resolve()).then(() => {
				const n = this.mutations.filter(s => s.state.isPaused);
				return I.batch(() => n.reduce((s, r) => s.then(() => r.continue().catch(H)), Promise.resolve()))
			}).then(() => {
				this.resuming = void 0
			}), this.resuming
		}
	} function jn() {
		return{onFetch: t => {t.fetchFn = () => {var e, n, s, r, i, o; const u = (e = t.fetchOptions) == null || (n = e.meta) == null ? void 0 : n.refetchPage, l = (s = t.fetchOptions) == null || (r = s.meta) == null ? void 0 : r.fetchMore, c = l ? .pageParam, a = l ? .direction == = "forward", f = l ? .direction == = "backward", g = ((i = t.state.data) == null ? void 0 : i.pages) || [], y = ((o = t.state.data) == null ? void 0 : o.pageParams) || []; let d = y, p = !1; const v = C => {Object.defineProperty(C, "signal", {enumerable: !0, get: () => {var O; if ((O = t.signal) != null && O.aborted)p = !0; else {
							var m;
							(m = t.signal) == null || m.addEventListener("abort", () => {
								p = !0
							})
						} return t.signal
					}
					                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     })
}, b = t.options.queryFn || (() => Promise.reject("Missing queryFn")), P = (C, O, m, Q) => (d = Q ? [O, ...d] : [...d, O], Q ? [m, ...C] : [...C, m]), w = (C, O, m, Q) => {if (p)return Promise.reject("Cancelled"); if (typeof m > "u" && !O && C.length)return Promise.resolve(C); const _ = {queryKey: t.queryKey, pageParam: m, meta: t.options.meta}; v(_); const k = b(_); return Promise.resolve(k).then(ne => P(C, m, ne, Q))}; let $; if (!g.length)$ = w([]); else if (a) {
					const C = typeof c < "u", O = C ? c : Qt(t.options, g);
					$ = w(g, C, O)
				} else if (f) {
					const C = typeof c < "u", O = C ? c : Kn(t.options, g);
					$ = w(g, C, O, !0)
				} else {
					d = [];
					const C = typeof t.options.getNextPageParam > "u";
					$ = (u&&g[0] ? u(g[0], 0, g) : !0) ? w([], C, y[0]) : Promise.resolve(P([], y[0], g[0]));
					for (let m = 1; m < g.length; m++)$ = $.then(Q => {
						if (u && g[m] ? u(g[m], m, g) : !0) {
							const k = C ? y[m] : Qt(t.options, Q);
							return w(Q, C, k)
						}
						return Promise.resolve(P(Q, y[m], g[m]))
					})
} return $.then(C => ({pages: C, pageParams: d}))}}}
	}
	function Qt(t, e) {
		return t.getNextPageParam == null ? void 0 : t.getNextPageParam(e[e.length - 1], e)
	}
	function Kn(t, e) {
		return t.getPreviousPageParam == null ? void 0 : t.getPreviousPageParam(e[0], e)
	}
	class Bn {
		constructor(e = {}) {
			this.queryCache = e.queryCache || new Mn, this.mutationCache = e.mutationCache || new Nn, this.logger = e.logger || Ve, this.defaultOptions = e.defaultOptions || {}, this.queryDefaults = [], this.mutationDefaults = [], this.mountCount = 0
		} mount() {
			this.mountCount++, this.mountCount == = 1 && (this.unsubscribeFocus = Re.subscribe(() => {
				Re.isFocused()&&(this.resumePausedMutations(), this.queryCache.onFocus())
			}), this.unsubscribeOnline = Fe.subscribe(() => {
				Fe.isOnline()&&(this.resumePausedMutations(), this.queryCache.onOnline())
			}))
		} unmount() {
			var e, n;
			this.mountCount--, this.mountCount == = 0 && ((e = this.unsubscribeFocus) == null || e.call(this), this.unsubscribeFocus = void 0, (n = this.unsubscribeOnline) == null || n.call(this), this.unsubscribeOnline = void 0)
		} isFetching(e, n) {
			const[s] = Y(e, n);
			return s.fetchStatus = "fetching", this.queryCache.findAll(s).length
		} isMutating(e) {
			return this.mutationCache.findAll({...e, fetching: !0}).length
		} getQueryData(e, n) {
			var s;
			return (s = this.queryCache.find(e, n)) == null ? void 0 : s.state.data
		} ensureQueryData(e, n, s) {
			const r = _e(e, n, s), i = this.getQueryData(r.queryKey);
			return i ? Promise.resolve(i) : this.fetchQuery(r)
		} getQueriesData(e) {
			return this.getQueryCache().findAll(e).map(({queryKey: n, state: s}) => {
				const r = s.data;
				return[n, r]
			})
		} setQueryData(e, n, s) {
			const r = this.queryCache.find(e), i = r ? .state.data, o = Rn(n, i);
			if (typeof o > "u")return;
			const u = _e(e), l = this.defaultQueryOptions(u);
			return this.queryCache.build(this, l).setData(o, {...s, manual: !0})
		} setQueriesData(e, n, s) {
			return I.batch(() => this.getQueryCache().findAll(e).map(({queryKey: r}) => [r, this.setQueryData(r, n, s)]))
		} getQueryState(e, n) {
			var s;
			return (s = this.queryCache.find(e, n)) == null ? void 0 : s.state
		} removeQueries(e, n) {
			const[s] = Y(e, n), r = this.queryCache;
			I.batch(() => {
				r.findAll(s).forEach(i => {
					r.remove(i)
				})
			})
		} resetQueries(e, n, s) {
			const[r, i] = Y(e, n, s), o = this.queryCache, u = {type: "active", ...r};
			return I.batch(() => (o.findAll(r).forEach(l => {
				l.reset()
			}), this.refetchQueries(u, i)))
		} cancelQueries(e, n, s) {
			const[r, i = {}] = Y(e, n, s);
			typeof i.revert > "u"&&(i.revert = !0);
			const o = I.batch(() => this.queryCache.findAll(r).map(u => u.cancel(i)));
			return Promise.all(o).then(H).catch(H)
		} invalidateQueries(e, n, s) {
			const[r, i] = Y(e, n, s);
			return I.batch(() => {
				var o, u;
				if (this.queryCache.findAll(r).forEach(c => {
				c.invalidate()
			}), r.refetchType == = "none")return Promise.resolve();
				const l = {...r, type: (o = (u = r.refetchType) != null ? u : r.type) != null ? o : "active"};
				return this.refetchQueries(l, i)
			})
		} refetchQueries(e, n, s) {
			const[r, i] = Y(e, n, s), o = I.batch(() => this.queryCache.findAll(r).filter(l => !l.isDisabled()).map(l => {
				var c;
				return l.fetch(void 0, {...i, cancelRefetch: (c = i ? .cancelRefetch) != null ? c : !0, meta : {refetchPage : r.refetchPage}})
			}));
			let u = Promise.all(o).then(H);
			return i != null&&i.throwOnError || (u = u.catch(H)), u
		} fetchQuery(e, n, s) {
			const r = _e(e, n, s), i = this.defaultQueryOptions(r);
			typeof i.retry > "u"&&(i.retry = !1);
			const o = this.queryCache.build(this, i);
			return o.isStaleByTime(i.staleTime) ? o.fetch(i) : Promise.resolve(o.state.data)
		} prefetchQuery(e, n, s) {
			return this.fetchQuery(e, n, s).then(H).catch(H)
		} fetchInfiniteQuery(e, n, s) {
			const r = _e(e, n, s);
			return r.behavior = jn(), this.fetchQuery(r)
		} prefetchInfiniteQuery(e, n, s) {
			return this.fetchInfiniteQuery(e, n, s).then(H).catch(H)
		} resumePausedMutations() {
			return this.mutationCache.resumePausedMutations()
		} getQueryCache() {
			return this.queryCache
		} getMutationCache() {
			return this.mutationCache
		} getLogger() {
			return this.logger
		} getDefaultOptions() {
			return this.defaultOptions
		} setDefaultOptions(e) {
			this.defaultOptions = e
		} setQueryDefaults(e, n) {
			const s = this.queryDefaults.find(r => te(e) == = te(r.queryKey));
			s ? s.defaultOptions = n : this.queryDefaults.push({queryKey: e, defaultOptions: n})
		} getQueryDefaults(e) {
			if (!e)return;
			const n = this.queryDefaults.find(s => xe(e, s.queryKey));
			return n ? .defaultOptions
		} setMutationDefaults(e, n) {
			const s = this.mutationDefaults.find(r => te(e) == = te(r.mutationKey));
			s ? s.defaultOptions = n : this.mutationDefaults.push({mutationKey: e, defaultOptions: n})
		} getMutationDefaults(e) {
			if (!e)return;
			const n = this.mutationDefaults.find(s => xe(e, s.mutationKey));
			return n ? .defaultOptions
		} defaultQueryOptions(e) {
			if (e != null && e._defaulted)return e;
			const n = {...this.defaultOptions.queries, ...this.getQueryDefaults(e ? .queryKey), ...e, _defaulted: !0};
			return!n.queryHash&&n.queryKey&&(n.queryHash = Ke(n.queryKey, n)), typeof n.refetchOnReconnect > "u" && (n.refetchOnReconnect = n.networkMode != = "always"), typeof n.useErrorBoundary > "u" && (n.useErrorBoundary = !!n.suspense), n
		} defaultMutationOptions(e) {
			return e != null&&e._defaulted ? e : {...this.defaultOptions.mutations, ...this.getMutationDefaults(e ? .mutationKey), ...e, _defaulted : !0}
		} clear() {
			this.queryCache.clear(), this.mutationCache.clear()
		}
	} class Hn extends de {
		constructor(e, n) {
			super(), this.client = e, this.options = n, this.trackedProps = new Set, this.selectError = null, this.bindMethods(), this.setOptions(n)
		} bindMethods() {
			this.remove = this.remove.bind(this), this.refetch = this.refetch.bind(this)
		} onSubscribe() {
			this.listeners.length == = 1 && (this.currentQuery.addObserver(this), Lt(this.currentQuery, this.options) && this.executeFetch(), this.updateTimers())
		} onUnsubscribe() {
			this.listeners.length || this.destroy()
		} shouldFetchOnReconnect() {
			return ze(this.currentQuery, this.options, this.options.refetchOnReconnect)
		} shouldFetchOnWindowFocus() {
			return ze(this.currentQuery, this.options, this.options.refetchOnWindowFocus)
		} destroy() {
			this.listeners = [], this.clearStaleTimeout(), this.clearRefetchInterval(), this.currentQuery.removeObserver(this)
		} setOptions(e, n) {
			const s = this.options, r = this.currentQuery;
			if (this.options = this.client.defaultQueryOptions(e), _t(s, this.options) || this.client.getQueryCache().notify({type: "observerOptionsUpdated", query: this.currentQuery, observer: this}), typeof this.options.enabled < "u" && typeof this.options.enabled != "boolean")throw new Error("Expected enabled to be a boolean");
			this.options.queryKey || (this.options.queryKey = s.queryKey), this.updateQuery();
			const i = this.hasListeners();
			i&&Dt(this.currentQuery, r, this.options, s)&&this.executeFetch(), this.updateResult(n), i&&(this.currentQuery != = r || this.options.enabled != = s.enabled || this.options.staleTime != = s.staleTime) && this.updateStaleTimeout();
			const o = this.computeRefetchInterval();
			i&&(this.currentQuery != = r || this.options.enabled != = s.enabled || o != = this.currentRefetchInterval) && this.updateRefetchInterval(o)
		} getOptimisticResult(e) {
			const n = this.client.getQueryCache().build(this.client, e);
			return this.createResult(n, e)
		} getCurrentResult() {
			return this.currentResult
		} trackResult(e) {
			const n = {};
			return Object.keys(e).forEach(s => {
				Object.defineProperty(n, s, {configurable: !1, enumerable: !0, get: () => (this.trackedProps.add(s), e[s])})
			}), n
		} getCurrentQuery() {
			return this.currentQuery
		} remove() {
			this.client.getQueryCache().remove(this.currentQuery)
		} refetch({refetchPage: e, ...n} = {}) {
			return this.fetch({...n, meta: {refetchPage: e}})
		} fetchOptimistic(e) {
			const n = this.client.defaultQueryOptions(e), s = this.client.getQueryCache().build(this.client, n);
			return s.isFetchingOptimistic = !0, s.fetch().then(() => this.createResult(s, n))
		} fetch(e) {
			var n;
			return this.executeFetch({...e, cancelRefetch: (n = e.cancelRefetch) != null ? n : !0}).then(() => (this.updateResult(), this.currentResult))
		} executeFetch(e) {
			this.updateQuery();
			let n = this.currentQuery.fetch(this.options, e);
			return e != null&&e.throwOnError || (n = n.catch(H)), n
		} updateStaleTimeout() {
			if (this.clearStaleTimeout(), he || this.currentResult.isStale || !je(this.options.staleTime))return;
			const n = Ot(this.currentResult.dataUpdatedAt, this.options.staleTime) + 1;
			this.staleTimeoutId = setTimeout(() => {
				this.currentResult.isStale || this.updateResult()
			}, n)
		} computeRefetchInterval() {
			var e;
			return typeof this.options.refetchInterval == "function" ? this.options.refetchInterval(this.currentResult.data, this.currentQuery) : (e = this.options.refetchInterval) != null ? e : !1
		} updateRefetchInterval(e) {
			this.clearRefetchInterval(), this.currentRefetchInterval = e, !(he || this.options.enabled == = !1 || !je(this.currentRefetchInterval) || this.currentRefetchInterval == = 0) && (this.refetchIntervalId = setInterval(() => {
				(this.options.refetchIntervalInBackground || Re.isFocused())&&this.executeFetch()
			}, this.currentRefetchInterval))
		} updateTimers() {
			this.updateStaleTimeout(), this.updateRefetchInterval(this.computeRefetchInterval())
		} clearStaleTimeout() {
			this.staleTimeoutId&&(clearTimeout(this.staleTimeoutId), this.staleTimeoutId = void 0)
		} clearRefetchInterval() {
			this.refetchIntervalId&&(clearInterval(this.refetchIntervalId), this.refetchIntervalId = void 0)
		} createResult(e, n) {
			const s = this.currentQuery, r = this.options, i = this.currentResult, o = this.currentResultState, u = this.currentResultOptions, l = e != = s, c = l ? e.state : this.currentQueryInitialState, a = l ? this.currentResult : this.previousQueryResult, {state : f} = e;
			let{dataUpdatedAt: g, error: y, errorUpdatedAt: d, fetchStatus: p, status: v} = f, b = !1, P = !1, w;
			if (n._optimisticResults) {
				const m = this.hasListeners(), Q = !m&&Lt(e, n), _ = m&&Dt(e, s, n, r);
				(Q || _)&&(p = ke(e.options.networkMode) ? "fetching" : "paused", g || (v = "loading")), n._optimisticResults == = "isRestoring" && (p = "idle")
			}
			if (n.keepPreviousData && !f.dataUpdatedAt && a != null && a.isSuccess && v != = "error")w = a.data, g = a.dataUpdatedAt, v = a.status, b = !0;
			else if (n.select && typeof f.data < "u")if (i && f.data == = o ? .data && n.select == = this.selectFn)w = this.selectResult;
				else try {
						this.selectFn = n.select, w = n.select(f.data), w = He(i ? .data, w, n), this.selectResult = w, this.selectError = null
					} catch (m) {
						this.selectError = m
					} else w = f.data;
			if (typeof n.placeholderData < "u" && typeof w > "u" && v == = "loading") {
				let m;
				if (i != null && i.isPlaceholderData && n.placeholderData == = u ? .placeholderData)m = i.data;
				else if (m = typeof n.placeholderData == "function" ? n.placeholderData() : n.placeholderData, n.select && typeof m < "u")try {
						m = n.select(m), this.selectError = null
					} catch (Q) {
						this.selectError = Q
					}
				typeof m < "u"&&(v = "success", w = He(i ? .data, m, n), P = !0)
			}
			this.selectError&&(y = this.selectError, w = this.selectResult, d = Date.now(), v = "error");
			const $ = p == = "fetching", q = v == = "loading", C = v == = "error";
			return{status: v, fetchStatus: p, isLoading: q, isSuccess: v == = "success", isError: C, isInitialLoading: q&&$, data: w, dataUpdatedAt: g, error: y, errorUpdatedAt: d, failureCount: f.fetchFailureCount, failureReason: f.fetchFailureReason, errorUpdateCount: f.errorUpdateCount, isFetched: f.dataUpdateCount > 0 || f.errorUpdateCount > 0, isFetchedAfterMount: f.dataUpdateCount > c.dataUpdateCount || f.errorUpdateCount > c.errorUpdateCount, isFetching: $, isRefetching: $ && !q, isLoadingError: C&&f.dataUpdatedAt == = 0, isPaused: p == = "paused", isPlaceholderData: P, isPreviousData: b, isRefetchError: C&&f.dataUpdatedAt != = 0, isStale: Ge(e, n), refetch: this.refetch, remove: this.remove}
		} updateResult(e) {
			const n = this.currentResult, s = this.createResult(this.currentQuery, this.options);
			if (this.currentResultState = this.currentQuery.state, this.currentResultOptions = this.options, _t(s, n))return;
			this.currentResult = s;
			const r = {cache: !0}, i = () => {
				if (!n)return!0;
const{notifyOnChangeProps: o} = this.options;
				if (o == = "all" || !o && !this.trackedProps.size)return!0;
					const u = new Set(o ?? this.trackedProps);
					return this.options.useErrorBoundary&&u.add("error"), Object.keys(this.currentResult).some(l => {
					const c = l;
					return this.currentResult[c] != = n[c] && u.has(c)
					})
			};
			e ? .listeners != = !1 && i() && (r.listeners = !0), this.notify({...r, ...e})
		} updateQuery() {
			const e = this.client.getQueryCache().build(this.client, this.options);
			if (e == = this.currentQuery)return;
			const n = this.currentQuery;
			this.currentQuery = e, this.currentQueryInitialState = e.state, this.previousQueryResult = this.currentResult, this.hasListeners() && (n ? .removeObserver(this), e.addObserver(this))
		} onQueryUpdate(e) {
			const n = {};
			e.type == = "success" ? n.onSuccess = !e.manual : e.type == = "error" && !Te(e.error) && (n.onError = !0), this.updateResult(n), this.hasListeners() && this.updateTimers()
		} notify(e) {
			I.batch(() => {
				if (e.onSuccess) {
				var n, s, r, i;
				(n = (s = this.options).onSuccess) == null || n.call(s, this.currentResult.data), (r = (i = this.options).onSettled) == null || r.call(i, this.currentResult.data, null)
				} else if (e.onError) {
				var o, u, l, c;
				(o = (u = this.options).onError) == null || o.call(u, this.currentResult.error), (l = (c = this.options).onSettled) == null || l.call(c, void 0, this.currentResult.error)
				}
				e.listeners&&this.listeners.forEach(a => {
					a(this.currentResult)
				}), e.cache&&this.client.getQueryCache().notify({query: this.currentQuery, type: "observerResultsUpdated"})
			})
		}
	} function Vn(t, e) {
		return e.enabled != = !1 && !t.state.dataUpdatedAt&&!(t.state.status == = "error" && e.retryOnMount == = !1)
	}
	function Lt(t, e) {
		return Vn(t, e) || t.state.dataUpdatedAt > 0 && ze(t, e, e.refetchOnMount)
	}
	function ze(t, e, n) {
		if (e.enabled != = !1) {
			const s = typeof n == "function" ? n(t) : n;
			return s == = "always" || s != = !1 && Ge(t, e)
		}
		return!1
	}
	function Dt(t, e, n, s) {
		return n.enabled != = !1 && (t != = e || s.enabled == = !1) && (!n.suspense || t.state.status != = "error") && Ge(t, n)
	}
	function Ge(t, e) {
		return t.isStaleByTime(e.staleTime)
	}
	const We = Symbol("store-raw"), ye = Symbol("store-node");
	function Mt(t) {
		let e = t[W];
		if (!e && (Object.defineProperty(t, W, {value: e = new Proxy(t, Wn)}), !Array.isArray(t))) {
			const n = Object.keys(t), s = Object.getOwnPropertyDescriptors(t);
			for (let r = 0, i = n.length; r < i; r++) {
				const o = n[r];
				s[o].get&&Object.defineProperty(t, o, {enumerable: s[o].enumerable, get: s[o].get.bind(e)})
			}
		}
		return e
	}
	function qe(t) {
		let e;
		return t != null&&typeof t == "object" && (t[W] || !(e = Object.getPrototypeOf(t)) || e == = Object.prototype || Array.isArray(t))
	}
	function G(t, e = new Set) {
		let n, s, r, i;
		if (n = t != null && t[We])return n;
		if (!qe(t) || e.has(t))return t;
		if (Array.isArray(t)) {
			Object.isFrozen(t) ? t = t.slice(0) : e.add(t);
			for (let o = 0, u = t.length; o < u; o++)r = t[o], (s = G(r, e)) != = r && (t[o] = s)
			} else {
			Object.isFrozen(t) ? t = Object.assign({}, t): e.add(t);
			const o = Object.keys(t), u = Object.getOwnPropertyDescriptors(t);
			for (let l = 0, c = o.length; l < c; l++)i = o[l], !u[i].get && (r = t[i], (s = G(r, e)) != = r && (t[i] = s))
			}
		return t
	}
	function Xe(t) {
		let e = t[ye];
		return e || Object.defineProperty(t, ye, {value: e = Object.create(null)}), e
	}
	function Ye(t, e, n) {
		return t[e] || (t[e] = It(n))
	}
	function zn(t, e) {
		const n = Reflect.getOwnPropertyDescriptor(t, e);
		return!n || n.get || !n.configurable || e == = W || e == = ye || (delete n.value, delete n.writable, n.get = () => t[W][e]), n
	}
	function Ut(t) {
		if (lt()) {
			const e = Xe(t);
			(e._ || (e._ = It()))()
		}
	}
	function Gn(t) {
		return Ut(t), Reflect.ownKeys(t)
	}
	function It(t) {
		const[e, n] = j(t, {equals: !1, internal: !0});
		return e.$ = n, e
	}
	const Wn = {get(t, e, n){if (e == = We)return t; if (e == = W)return n; if (e == = De)return Ut(t), n; const s = Xe(t), r = s[e]; let i = r ? r() : t[e]; if (e == = ye || e == = "__proto__")return i; if (!r) {
			const o = Object.getOwnPropertyDescriptor(t, e);
			lt()&&(typeof i != "function" || t.hasOwnProperty(e))&&!(o&&o.get)&&(i = Ye(s, e, i)())
		} return qe(i) ? Mt(i) : i
	}, has(t, e){return e == = We || e == = W || e == = De || e == = ye || e == = "__proto__" ? !0 : (this.get(t, e, t), e in t)}, set(){return!0}, deleteProperty(){return!0}, ownKeys: Gn, getOwnPropertyDescriptor: zn};
	function Qe(t, e, n, s = !1) {
		if (!s && t[e] == = n)return;
		const r = t[e], i = t.length;
		n == = void 0 ? delete t[e] : t[e] = n;
		let o = Xe(t), u;
		if ((u = Ye(o, e, r)) && u.$(() => n), Array.isArray(t) && t.length != = i) {
			for (let l = t.length; l < i; l++)(u = o[l]) && u.$();
			(u = Ye(o, "length", i)) && u.$(t.length)
		}
		(u = o._) && u.$()
	}
	function Nt(t, e) {
		const n = Object.keys(e);
		for (let s = 0; s < n.length; s += 1) {
			const r = n[s];
			Qe(t, r, e[r])
		}
	}
	function Xn(t, e) {
		if (typeof e == "function" && (e = e(t)), e = G(e), Array.isArray(e)) {
			if (t == = e)return;
			let n = 0, s = e.length;
			for (; n < s; n++) {
				const r = e[n];
				t[n] != = r&&Qe(t, n, r)
			}
			Qe(t, "length", s)
		} else Nt(t, e)
		}
	function pe(t, e, n = []) {
		let s, r = t;
		if (e.length > 1) {
			s = e.shift();
			const o = typeof s, u = Array.isArray(t);
			if (Array.isArray(s)) {
				for (let l = 0; l < s.length; l++)pe(t, [s[l]].concat(e), n);
				return
			} else if (u && o == = "function") {
				for (let l = 0; l < t.length; l++)s(t[l], l) && pe(t, [l].concat(e), n);
				return
			} else if (u && o == = "object") {
				const{from: l = 0, to: c = t.length - 1, by: a = 1} = s;
				for (let f = l; f <= c; f += a)pe(t, [f].concat(e), n);
				return
			} else if (e.length > 1) {
				pe(t[s], e, [s].concat(n));
				return
			}
			r = t[s], n = [s].concat(n)
		}
		let i = e[0];
		typeof i == "function"&&(i = i(r, n), i == = r) || s == = void 0 && i == null || (i = G(i), s == = void 0 || qe(r) && qe(i) && !Array.isArray(i) ? Nt(r, i) : Qe(t, s, i))
	}
	function jt(...[t, e]) {
		const n = G(t || {}), s = Array.isArray(n), r = Mt(n);
		function i(...o) {
			ae(() => {
				s&&o.length == = 1 ? Xn(n, o[0]) : pe(n, o)
			})
		}
		return[r, i]
	}
	function Yn(t) {
		return typeof t == "function"
	}
	function Kt(t, e, n) {
		if (!Yn(t)) {
			const{queryKey: s, ...r} = t;
			return s ? {...r, queryKey : s()}:
			       t
		}
		return typeof e == "function" ? {...n, queryKey : t(), queryFn : e}: {
		           ...e, queryKey:
		           t()
		       }
	}
	function Jn(t, e) {
		return typeof t == "function" ? t(...e) : !!t
	}
	const Bt = ce(void 0), Ht = ce(!1);
	function Vt(t, e) {
		return t || (e&&typeof window < "u" ? (window.SolidQueryClientContext || (window.SolidQueryClientContext = Bt), window.SolidQueryClientContext) : Bt)
	}
	const Zn = ({context: t} = {}) => {const e = be(Vt(t, be(Ht))); if (!e)throw new Error("No QueryClient set, use QueryClientProvider to set one"); return e}, es = t => {
	const e = mn({contextSharing: !1}, t);
	ut(() => {
			e.client.mount()
		}), J(() => e.client.unmount());
		const n = Vt(e.context, e.contextSharing);
		return S(Ht.Provider, {get value() {
			return!e.context&&e.contextSharing
		}, get children() {
			return S(n.Provider, {get value() {
				return e.client
			}, get children() {
				return e.children
			}
			                     })
		}
		                      })
	};
	function ts(t, e) {
		const n = Zn({context: t.context}), s = Symbol("empty"), r = n.defaultQueryOptions(t);
		r._optimisticResults = "optimistic";
		const i = new e(n, r), [o, u] = jt(i.getOptimisticResult(r)), [l, {refetch:c, mutate:a}] = sn(() => new Promise(d => {
			o.isFetching&&o.isLoading || (G(o.data) == = s&&d(void 0), d(G(o.data)))
		}));
		ae(() => {
			a(() => G(o.data)), c()
		});
		let f = [];
		const g = i.subscribe(d => {
			f.push(() => {
				ae(() => {
					const p = {...G(d)};
					p.data == = void 0 && (p.data = s), u(G(p)), a(() => G(d.data)), c()
				})
			}), queueMicrotask(() => {
				const p = f.pop();
				p&&p(), f = []
			})
		});
		J(() => g()), ut(() => {
			i.setOptions(r, {listeners: !1})
		}), se(() => {
			const d = n.defaultQueryOptions(t);
			i.setOptions(d)
		}), se(rn(() => o.status, () => {
			if (o.isError && !o.isFetching && Jn(i.options.useErrorBoundary, [o.error, i.getCurrentQuery()]))throw o.error
			}));
		const y = {get(d, p) {
			return p == = "data" ? l() : Reflect.get(d, p)
		}
		          };
		return new Proxy(o, y)
	}
	function zt(t, e, n) {
		const[s, r] = jt(Kt(t, e, n));
		return se(() => {
			const i = Kt(t, e, n);
			r(i)
		}), ts(s, Hn)
	}
	const ns = "https://cdn.luogu.org/images/bg/fe/DSCF0530-shrink.jpg", ss = "https://cdn.luogu.com.cn/images/banned.png", ge = {purple: "#8e44ad", red: "#e74c3c", orange: "#e67e22", green: "#5eb95e", bluelight: "#0e90d2", gray: "#bbb", brown: "#996600", blue: "#3498db", gold: "#f1c40f"}, rs = [ / ^user\ / (\d + )$ /, / ^\ / user\ / (\d + )$ /, / ^https:\ / \ / www\.luogu\.com\.cn\ / user\ / (\d + )$ /, / ^space\ / show\ ? uid = (\d + )$ /, / ^\ / space\ / show\ ? uid = (\d + )$ /, / ^https : \ / \ / www\.luogu\.com\.cn\ / space\ / show\ ? uid = (\d + )$ / ], is = ["user-nav", "lg-punch"], Je = (...t) => console.warn("[FLG]", ...t), Ze = (...t) => console.info("[FLG]", ...t), et = t => t == = "blue" ? ge.bluelight : t == = "cheater" ? ge.brown : ge[t], os = t => {for (const e of rs) {
		const n = t.match(e);
			if (n != = null)return Number(n[1])
			} return null
	}, us = t => {let e = t; const n = []; for (; e;)n.push(e), e = e.parentElement; return n}, sr = {center: "_center_1c669_1"}, Gt = ce(() => ( {
selfUid:
null, csrfToken: ""
})), ls = t => {const e = () => t.state; return S(Gt.Provider, {value: e, get children() {
				return t.children
			}
		})
	}, as = () => be(Gt), cs = D("<div>"), fs = t => (() => {
		const e = cs();
		return e.style.setProperty("width", "100%"), e.style.setProperty("height", "60px"), T(() => `url($ {
			t.url
		}) center center / cover no - repeat` != null ? e.style.setProperty("background", `url($ {t.url}) center center / cover no - repeat`): e.style.removeProperty("background")), e
})(), V = {container: "_container_1y67s_1", header: "_header_1y67s_6", avatar: "_avatar_1y67s_14", ccfbadge: "_ccfbadge_1y67s_24", badge: "_badge_1y67s_34", blog: "_blog_1y67s_46", slogan: "_slogan_1y67s_75", stat: "_stat_1y67s_82", canclick: "_canclick_1y67s_94", bottomButton: "_bottom-button_1y67s_104"}, ds = D('<div><svg viewBox="0 0 512 512"><path d="M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM371.8 211.8l-128 128C238.3 345.3 231.2 348 224 348s-14.34-2.719-19.81-8.188l-64-64c-10.91-10.94-10.91-28.69 0-39.63c10.94-10.94 28.69-10.94 39.63 0L224 280.4l108.2-108.2c10.94-10.94 28.69-10.94 39.63 0C382.7 183.1 382.7 200.9 371.8 211.8z">'), hs = D("<div><div>"), ys = t => {const e = E(() => t.level <= 5 ? "green" : t.level <= 7 ? "blue" : "gold"); return (() => {const n = ds(), s = n.firstChild; return T(r => {const i = V.ccfbadge, o = ge[e()]; return i != = r._v$ && K(n, r._v$ = i), o != = r._v$2 && ((r._v$2 = o) != null ? s.style.setProperty("fill", o) : s.style.removeProperty("fill")), r}, {_v$: void 0, _v$2: void 0}), n})()}, ps = t => {
		const e = E(() => t.user.isBanned ? ss : `https : //cdn.luogu.com.cn/upload/usericon/${t.user.uid}.png`),n=E(()=>t.user.ccfLevel);return(()=>{const s=hs(),r=s.firstChild;return s.style.setProperty("width","80px"),F(s,S(ee,{get when(){return n()>=3},get children(){return S(ys,{get level(){return n()}})}}),null),T(i=>{const o=V.avatar,u=`url(${e()}) 0% 0% / cover no-repeat rgb(255, 255, 255)`;return o!==i._v$3&&K(r,i._v$3=o),u!==i._v$4&&((i._v$4=u)!=null?r.style.setProperty("background",u):r.style.removeProperty("background")),i},{_v$3:void 0,_v$4:void 0}),s})()},gs=D("<span>"),vs=D("<div>"),ms=t=>(()=>{const e=gs();return F(e,()=>t.badge),T(n=>{const s=V.badge,r=et(t.color);return s!==n._v$&&K(e,n._v$=s),r!==n._v$2&&((n._v$2=r)!=null?e.style.setProperty("background-color",r):e.style.removeProperty("background-color")),n},{_v$:void 0,_v$2:void 0}),e})(),bs=t=>{const e=E(()=>t.user.color.toLowerCase()),n=E(()=>{if(e()==="cheater")return"作弊者";const s=t.user.badge;return s===null||s===""?null:t.user.badge});return(()=>{const s=vs();return s.style.setProperty("font-weight","bold"),s.style.setProperty("font-size","1em"),s.style.setProperty("margin-top","3px"),F(s,()=>t.user.name,null),F(s,S(ee,{get when(){return n()},children:r=>S(ms,{get color(){return e()},get badge(){return r()}})}),null),T(()=>et(e())!=null?s.style.setProperty("color",et(e())):s.style.removeProperty("color")),s})()},ws=D('<span><svg viewBox="0 0 512 512"><path d="M25.57 176.1C12.41 175.4 .9117 185.2 .0523 198.4s9.173 24.65 22.39 25.5c120.1 7.875 225.7 112.7 233.6 233.6C256.9 470.3 267.4 480 279.1 480c.5313 0 1.062-.0313 1.594-.0625c13.22-.8438 23.25-12.28 22.39-25.5C294.6 310.3 169.7 185.4 25.57 176.1zM32 32C14.33 32 0 46.31 0 64s14.33 32 32 32c194.1 0 352 157.9 352 352c0 17.69 14.33 32 32 32s32-14.31 32-32C448 218.6 261.4 32 32 32zM63.1 351.9C28.63 351.9 0 380.6 0 416s28.63 64 63.1 64s64.08-28.62 64.08-64S99.37 351.9 63.1 351.9z"></path></svg>个人博客'),Cs=t=>(()=>{const e=ws();return e.$$click=()=>window.open(t.url),T(()=>K(e,V.blog)),e})();$e(["click"]);const Os=D("<div><p>"),Ss=t=>(()=>{const e=Os(),n=e.firstChild;return F(n,()=>t.slogan),T(()=>K(e,V.slogan)),e})(),Ps=D("<div><div></div><div><span>"),Le=t=>{const e=E(()=>!(t.value==="-"||typeof t.link>"u")),n=E(()=>{if(e())return()=>window.open(t.link)});return(()=>{const s=Ps(),r=s.firstChild,i=r.nextSibling,o=i.firstChild;return s.$$click=()=>n()?.(),r.style.setProperty("font-size","12px"),F(r,()=>t.name),i.style.setProperty("font-size","16px"),F(o,()=>t.value),T(u=>xn(s,{[V.canclick]:e()},u)),s})()};$e(["click"]);const $s=D('<div><svg viewBox="0 0 512 512"><path d="M256 352c-16.53 0-33.06-5.422-47.16-16.41L0 173.2V400C0 426.5 21.49 448 48 448h416c26.51 0 48-21.49 48-48V173.2l-208.8 162.5C289.1 346.6 272.5 352 256 352zM16.29 145.3l212.2 165.1c16.19 12.6 38.87 12.6 55.06 0l212.2-165.1C505.1 137.3 512 125 512 112C512 85.49 490.5 64 464 64h-416C21.49 64 0 85.49 0 112C0 125 6.01 137.3 16.29 145.3z"></path></svg>私信'),As=t=>(()=>{const e=$s();return e.$$click=()=>window.open(`https://www.luogu.com.cn/chat?uid=${t.uid}`),T(()=>K(e,V.bottomButton)),e})();$e(["click"]);const _s=D("<div>"),xs=D('<svg viewBox="0 0 512 512"><path d="M472.1 270.5l-193.1 199.7c-12.64 13.07-33.27 13.08-45.91 .0107l-193.2-199.7C-16.21 212.5-13.1 116.7 49.04 62.86C103.3 15.88 186.4 24.42 236.3 75.98l19.7 20.27l19.7-20.27c49.95-51.56 132.1-60.1 187.3-13.12C525.1 116.6 528.2 212.5 472.1 270.5z">'),Es=D('<svg viewBox="0 0 512 512"><path d="M480 128c0 8.188-3.125 16.38-9.375 22.62l-256 256C208.4 412.9 200.2 416 192 416s-16.38-3.125-22.62-9.375l-128-128C35.13 272.4 32 264.2 32 256c0-18.28 14.95-32 32-32c8.188 0 16.38 3.125 22.62 9.375L192 338.8l233.4-233.4C431.6 99.13 439.8 96 448 96C465.1 96 480 109.7 480 128z">'),Rs=D('<svg viewBox="0 0 512 512"><path d="M432 256c0 17.69-14.33 32.01-32 32.01H256v144c0 17.69-14.33 31.99-32 31.99s-32-14.3-32-31.99v-144H48c-17.67 0-32-14.32-32-32.01s14.33-31.99 32-31.99H192v-144c0-17.69 14.33-32.01 32-32.01s32 14.32 32 32.01v144h144C417.7 224 432 238.3 432 256z">'),Fs=t=>{const[e,n]=j(!1),s=E(()=>t.relationship&1?[{color:"#bbb"},{fill:"#bbb"}]:[void 0,void 0]),r=E(()=>{const o=t.relationship;return o&1?e()?"取消关注":(o&2)===2?"已互关":"已关注":"关注"}),i=async()=>{const o=t.relationship&1^1,u=await fetch("https://www.luogu.com.cn/api/user/updateRelationShip",{method:"POST",headers:{"content-type":"application/json","x-csrf-token":t.state.csrfToken},body:JSON.stringify({relationship:o,uid:t.uid})});if(u.ok||Je("Failed to update relationship"),(await u.json())._empty){Ze("Successfully updated relationship"),t.refetch();return}Je("Failed to update relationship")};return(()=>{const o=_s();return o.addEventListener("mouseleave",()=>n(!1)),o.addEventListener("mouseenter",()=>n(!0)),o.$$click=i,F(o,(()=>{const u=E(()=>t.relationship===3);return()=>u()?(()=>{const l=xs();return T(c=>fe(l,s()[1],c)),l})():(()=>{const l=E(()=>(t.relationship&1)!==0);return()=>l()?(()=>{const c=Es();return T(a=>fe(c,s()[1],a)),c})():(()=>{const c=Rs();return T(a=>fe(c,s()[1],a)),c})()})()})(),null),F(o,r,null),T(u=>{const l=V.bottomButton,c=s()[0];return l!==u._v$&&K(o,u._v$=l),u._v$2=fe(o,c,u._v$2),u},{_v$:void 0,_v$2:void 0}),o})()};$e(["click"]);const Wt={card:"_card_1b53a_1"},ks=D("<div>"),Ts=D("<div><div><div><div><div>#</div></div></div><div>"),qs=t=>{const e=E(()=>{const i=t.user.background;return i===""?ns:i}),n=as(),s=E(()=>{const i=n().selfUid;return i!==null&&i!==t.user.uid}),r=E(()=>{const i=t.user.reverseUserRelationship,o=t.user.userRelationship;if(typeof i>"u"||typeof o>"u")throw new Error("unreachable");return i*2+o});return(()=>{const i=Ts(),o=i.firstChild,u=o.firstChild,l=u.firstChild,c=l.firstChild;c.firstChild;const a=u.nextSibling;return F(i,S(fs,{get url(){return e()}}),o),F(u,S(ps,{get user(){return t.user}}),l),l.style.setProperty("flex","1"),F(l,S(bs,{get user(){return t.user}}),c),c.style.setProperty("font-size","14px"),c.style.setProperty("position","relative"),c.style.setProperty("line-height","14px"),F(c,()=>t.user.uid,null),F(c,S(ee,{get when(){return t.user.blogAddress},children:f=>S(Cs,{get url(){return f()}})}),null),F(o,S(ee,{get when(){return t.user.slogan},children:f=>S(Ss,{get slogan(){return f()}})}),a),F(a,S(Le,{name:"关注",get value(){return`${t.user.followingCount}`},get link(){return`https://www.luogu.com.cn/user/${t.user.uid}#following.following`}}),null),F(a,S(Le,{name:"粉丝",get value(){return`${t.user.followerCount}`},get link(){return`https://www.luogu.com.cn/user/${t.user.uid}#following.follower`}}),null),F(a,S(Le,{name:"通过题数",get value(){return`${t.user.passedProblemCount??"-"}`},get link(){return`https://www.luogu.com.cn/user/${t.user.uid}#practice`}}),null),F(a,S(Le,{name:"咕值排名",get value(){return`${t.user.ranking??"-"}`}}),null),F(o,S(ee,{get when(){return s()},get children(){const f=ks();return f.style.setProperty("margin-bottom","10px"),F(f,S(As,{get uid(){return t.user.uid}}),null),F(f,S(Fs,{get state(){return n()},get uid(){return t.user.uid},get relationship(){return r()},get refetch(){return t.refetch}}),null),T(()=>K(f,V.stat)),f}}),null),T(f=>{const g=Wt.card,y=V.container,d=V.header,p=ge.gray,v=V.stat;return g!==f._v$&&K(i,f._v$=g),y!==f._v$2&&K(o,f._v$2=y),d!==f._v$3&&K(u,f._v$3=d),p!==f._v$4&&((f._v$4=p)!=null?c.style.setProperty("color",p):c.style.removeProperty("color")),v!==f._v$5&&K(a,f._v$5=v),f},{_v$:void 0,_v$2:void 0,_v$3:void 0,_v$4:void 0,_v$5:void 0}),i})()},tt={rotatable:"_rotatable_14mxb_1",rotate:"_rotate_14mxb_1",container:"_container_14mxb_54",svg:"_svg_14mxb_59"},Qs=D('<div><div><svg fill="#aaa" viewBox="0 0 512 512"><path d="M512 256c0 141.2-114.8 256-256 256s-256-114.8-256-256c0-112.4 75.19-213.4 182.9-245.4c16.94-5.047 34.75 4.641 39.78 21.55c5.062 16.94-4.594 34.75-21.53 39.8C120.4 95.97 64 171.7 64 256c0 105.9 86.13 192 192 192s192-86.13 192-192c0-84.34-56.38-160-137.1-184c-16.94-5.047-26.59-22.86-21.53-39.8c5.031-16.91 22.84-26.56 39.78-21.55C436.8 42.64 512 143.6 512 256z">'),Ls=()=>(()=>{const t=Qs(),e=t.firstChild,n=e.firstChild;return T(s=>{const r=Wt.card,i=tt.container,o=`${tt.rotatable} ${tt.svg}`;return r!==s._v$&&K(t,s._v$=r),i!==s._v$2&&K(e,s._v$2=i),o!==s._v$3&&bt(n,"class",s._v$3=o),s},{_v$:void 0,_v$2:void 0,_v$3:void 0}),t})(),Ds=async({queryKey:t})=>{const e=t[1],n=await fetch(`https://www.luogu.com.cn/user/${e}`,{headers:{"x-luogu-type":"content-only"}});if(!n.ok)throw new Error(`failed to get userinfo, uid = ${e}`);return(await n.json()).currentData.user},Ms=async()=>{const t=await fetch("https://www.luogu.com.cn/user/3",{headers:{"x-luogu-type":"content-only"}});if(!t.ok)throw new Error("failed to get self");const n=(await t.json()).currentUser;return typeof n>"u"?null:n.uid},Us=t=>{const e=zt({queryKey:()=>["userinfo",t.uid],queryFn:Ds,staleTime:1e4});return S(Pn,{get fallback(){return S(Ls,{})},get children(){return S(ee,{get when(){return e.data},children:n=>S(qs,{get user(){return n()},get refetch(){return e.refetch}})})}})};var Xt=(t,e)=>{let n;const s=()=>clearTimeout(n);return at()&&J(s),Object.assign((...i)=>{n!==void 0&&s(),n=setTimeout(()=>t(...i),e)},{clear:s})},Is=()=>{},Yt=(t,e)=>e();function Ns(t,e){const n=N(t),s=n?[n]:[],{onEnter:r=Yt,onExit:i=Yt}=e,[o,u]=j(e.appear?[]:s),[l]=un();let c,a=!1;function f(d,p){if(!d)return p&&p();a=!0,i(d,()=>{ae(()=>{a=!1,u(v=>v.filter(b=>b!==d)),p&&p()})})}function g(d){const p=c;if(!p)return d&&d();c=void 0,u(v=>[p,...v]),r(p,d??Is)}const y=e.mode==="out-in"?d=>a||f(d,g):e.mode==="in-out"?d=>g(()=>f(d)):d=>{g(),f(d)};return se(d=>{const p=t();return N(l)?(l(),d):(p!==d&&(c=p,ae(()=>N(()=>y(d)))),p)},e.appear?void 0:n),o}var Jt=t=>t instanceof Element;function nt(t,e){if(e(t))return t;if(typeof t=="function"&&!t.length)return nt(t(),e);if(Array.isArray(t))for(const n of t){const s=nt(n,e);if(s)return s}return null}function js(t,e=Jt,n=Jt){const s=E(t);return E(()=>nt(s(),e))}function Ks(t){return E(()=>{const e=t.name||"s";return{enterActiveClasses:(t.enterActiveClass||e+"-enter-active").split(" "),enterClasses:(t.enterClass||e+"-enter").split(" "),enterToClasses:(t.enterToClass||e+"-enter-to").split(" "),exitActiveClasses:(t.exitActiveClass||e+"-exit-active").split(" "),exitClasses:(t.exitClass||e+"-exit").split(" "),exitToClasses:(t.exitToClass||e+"-exit-to").split(" "),moveClasses:(t.moveClass||e+"-move").split(" ")}})}function Zt(t){requestAnimationFrame(()=>requestAnimationFrame(t))}function Bs(t,e,n,s){const{enterClasses:r,enterActiveClasses:i,enterToClasses:o}=t,{onBeforeEnter:u,onEnter:l,onAfterEnter:c}=e;u?.(n),n.classList.add(...r),n.classList.add(...i),queueMicrotask(()=>{if(!n.parentNode)return s?.();l?.(n,()=>a())}),Zt(()=>{n.classList.remove(...r),n.classList.add(...o),(!l||l.length<2)&&(n.addEventListener("transitionend",a),n.addEventListener("animationend",a))});function a(f){(!f||f.target===n)&&(s?.(),n.removeEventListener("transitionend",a),n.removeEventListener("animationend",a),n.classList.remove(...i),n.classList.remove(...o),c?.(n))}}function Hs(t,e,n,s){const{exitClasses:r,exitActiveClasses:i,exitToClasses:o}=t,{onBeforeExit:u,onExit:l,onAfterExit:c}=e;if(!n.parentNode)return s?.();u?.(n),n.classList.add(...r),n.classList.add(...i),l?.(n,()=>a()),Zt(()=>{n.classList.remove(...r),n.classList.add(...o),(!l||l.length<2)&&(n.addEventListener("transitionend",a),n.addEventListener("animationend",a))});function a(f){(!f||f.target===n)&&(s?.(),n.removeEventListener("transitionend",a),n.removeEventListener("animationend",a),n.classList.remove(...i),n.classList.remove(...o),c?.(n))}}var Vs={inout:"in-out",outin:"out-in"},zs=t=>{const e=Ks(t);return Ns(js(()=>t.children),{mode:Vs[t.mode],appear:t.appear,onEnter(n,s){Bs(e(),t,n,s)},onExit(n,s){Hs(e(),t,n,s)}})};let en=2000100;const Gs=(t,e)=>{en+=100;const n=document.body.clientWidth,s=t+169-n,r=218-t,i=e+30;let o=t-150;return s>0&&(o-=s),r>0&&(o+=r),{position:"absolute","z-index":en,top:`${i}px`,left:`${o}px`}},br="",Ws=D("<div>"),Xs=t=>{const[e,n]=j(),[s,r]=j(!1),i=Xt(()=>r(!1),300),o=u=>{if(s()){i.clear();return}Xt(()=>{n(Gs(u.pageX,u.pageY)),r(!0)},100)()};return ot(()=>{t.anchor.addEventListener("mouseenter",o),t.anchor.addEventListener("mouseleave",i),J(()=>{t.anchor.removeEventListener("mouseenter",o),t.anchor.removeEventListener("mouseleave",i)})}),S(zs,{name:"card-fade",get children(){return S(ee,{get when(){return s()},get children(){const u=Ws();return _n(u,"mouseleave",i),u.addEventListener("mouseenter",o),F(u,S(Us,{get uid(){return t.uid}})),T(l=>fe(u,e(),l)),u}})}})},Ys=t=>{const e=t.getAttribute("href");if(e===null)return null;const n=os(e);if(n===null)return null;const s=us(t);for(const i of is)if(s.find(o=>o.classList.contains(i)))return null;return t.getAttribute("uid")===`${n}`?null:n},Js=t=>{const e=wn();t.setAttribute(`card-${e}`,"");const n=t.parentElement;if(n===null||n.tagName!=="SPAN")return t;const s=document.createElement("template");s.innerHTML=n.outerHTML;const r=s.content.querySelector(`a[card-${e}]`);return r===null?(Je("不小心把新 node 玩丢了!"),t):(n.replaceWith(s.content),r)},Zs=()=>{const t=zt({queryKey:()=>["self"],queryFn:Ms,staleTime:1/0}),e=(()=>{const o=document.querySelector('meta[name="csrf-token"]');return o===null?"":o.getAttribute("content")??""})(),n=E(()=>{let o=t.data;return typeof o>"u"&&(o=null),{selfUid:o,csrfToken:e}}),[s,r]=j([]),i=setInterval(()=>{const o=[],u=document.getElementsByTagName("a");for(const l of u){const c=Ys(l);c!==null&&(l.setAttribute("uid",`${c}`),o.push({anchor:Js(l),uid:c}))}r(l=>[...l,...o])},500);return J(()=>clearInterval(i)),S(ls,{get state(){return n()},get children(){return S(On,{get each(){return s()},children:o=>S(Xs,o)})}})},er=()=>{Ze("Starting...")},tr=()=>{const t=document.getElementsByTagName("body");if(t.length<=0)throw new Error("?");const e=document.createElement("div");e.setAttribute("style","position: absolute; top: 0; left: 0"),t[0].append(e);const n=new Bn;An(()=>S(es,{client:n,get children(){return S(Zs,{})}}),e)};(()=>{er(),tr(),Ze("脚本已加载√","Floating Luogu 用户群 885149235 欢迎来玩~")})()})();

4. luogu_bot1

// ==UserScript==
// @name         luogu_bot1
// @namespace    http://tampermonkey.net/
// @version      1.8.0
// @description  自制洛谷插件
// @author       realskc
// @match        https://www.luogu.com.cn/*
// @grant        none
// @run-at       document-end
// ==/UserScript==

'use strict';
(function(){
	const featureDict = new Map([
		['lgbot1RemoveAd', '屏蔽广告'],
		['lgbot1Removeback', '移除网页出错跳转'],
		['lgbot1AddMessageLink', '私信界面 Ctrl+Click 打开用户主页'],
		['lgbot1RemoveCover', '移除主页遮盖'],
		['lgbot1AddProblemsColor', '显示题目颜色']
	]);
	const problemDifficultyToColor = [[191,191,191],[254,76,97],[243,156,17],[255,193,22],[82,196,26],[52,152,219],[157,61,207],[14,29,105]];
	function addSettingButton(){//增加插件设置
		const navElement = document.querySelector('nav.lfe-body');
		if(!navElement){
			console.log("未找到侧边导航栏");
			console.log(document);
			return;
		}
		const settingElement = document.createElement('a');//外壳,链接功能
		settingElement.setAttribute('data-v-0640126c', '');
		settingElement.setAttribute('data-v-639bc19b', '');
		settingElement.setAttribute('data-v-33633d7e', '');
		settingElement.className = 'color-none';
		settingElement.style.color = 'inherit';
		const settingText = document.createElement('span');
		settingText.setAttribute('data-v-639bc19b', '');
		settingText.className = 'text';
		settingText.textContent = '插件设置';
		settingElement.appendChild(settingText);
		navElement.appendChild(settingElement);
		settingElement.addEventListener('click', () => {//点击后进入临时页面
			function initializeSettings(){
				for(let i of featureDict.keys()){
					if(localStorage.getItem(i) === null){
						localStorage.setItem(i, 'true');
					}
				}
			}
			initializeSettings();
			const pageContent = `
				<!DOCTYPE html>
				<html lang="en">
				<head>
					<meta charset="UTF-8">
					<meta name="viewport" content="width=device-width, initial-scale=1.0">
					<title>luogu_bot1 功能设置</title>
					<style>
						body { font-family: Arial, sans-serif; }
						#featureBox {
							position: fixed;
							top: 20%;
							left: 50%;
							transform: translate(-50%, -50%);
							background-color: white;
							border: 1px solid #ccc;
							padding: 20px;
							z-index: 10001;
						}
					</style>
				</head>
				<body>
					<div id="featureBox">
						<h3>选择插件功能</h3>
						${Array.from(featureDict).map(([key, description]) => `
							<div>
								<label><input type="checkbox" id="${key}">${description}</label>
							</div>
						`).join('')}
						<button id="selectAll">全选</button>
						<button id="saveFeatures">保存</button>
					</div>
					<script>
						document.addEventListener('DOMContentLoaded', () => {//加载设置
							${Array.from(featureDict.keys()).map(key => `
								const ${key} = localStorage.getItem('${key}') === 'true';
								document.getElementById('${key}').checked = ${key};
							`).join('')}
						});
						document.getElementById('saveFeatures').addEventListener('click', () => {//保存设置
							${Array.from(featureDict.keys()).map(key => `
								const ${key} = document.getElementById('${key}').checked;
								localStorage.setItem('${key}', String(${key}));
							`).join('')}
							alert('功能已保存');
						});
						document.getElementById('selectAll').addEventListener('click', () => {//全选
							${Array.from(featureDict.keys()).map(key => `
								document.getElementById('${key}').checked = true;
							`).join('')}
						});
					</script>
				</body>
				</html>
			`;
			const newWindow = window.open();
			newWindow.document.write(pageContent);
			newWindow.document.close();
		});
	}
	function removeAd(){//屏蔽广告
		const adElement = document.querySelector('div[data-v-0a593618][data-v-1143714b]');
		if(adElement) {
			adElement.remove();
			console.log('广告已被删除');
		}
		else console.log('没有找到广告');
	}
	function removeBack(){//移除网页跳转
		function disableRedirect(){
			window.history.go = function() {// 根据实际测试,洛谷使用的是 history.go
				console.log("luogu_bot1:已为您屏蔽 history.go()");
			};
		}
		function checkError(){
			if(document.title === '错误 - 洛谷 | 计算机科学教育新生态'){
				disableRedirect();
				return true;
			}
			return false;
		}
		const observer = new MutationObserver((mutations, obs) => {
			checkError();
		});
		const config = {
			childList: true,
			subtree: true,
		};
		observer.observe(document, config);// 监视 title 变化
		if(document.readyState === 'complete' || document.readyState === 'interactive'){
			checkError();
		}
	}
	async function getUidByUsername(username){// 获取 uid
		const apiUrl = `https://www.luogu.com.cn/api/user/search?keyword=${username}`;
		return await fetch(apiUrl)
		.then(response => response.json())
		.then(data => {
            if(data.users && data.users.length > 0){
        		return data.users[0].uid;
			}
		})
		.catch(error => {
			console.error('Error fetching user data:', error);
		});
	}
	async function processItem(item) { // 添加 eventListener
		item.parentElement.parentElement.addEventListener('click', (event) => {
			if(event.ctrlKey){
				getUidByUsername(item.textContent.trim())
				.then(uid => {
					const userLink = `/user/${uid}`;
					if(userLink){
						window.open(userLink, '_blank');
					}
				})
			}
		});
    }
	function addMessageLink(){ // Ctrl+Click 触发,动态修改网页
		let items = document.querySelectorAll('span[data-v-5b9e5f50] > span[slot="trigger"]');
		console.log("asdf");
		console.log(items);
		for(let i of items){
			processItem(i);
		}
		const callback = async function(mutationsList, observer) {
			for (const mutation of mutationsList) {
				if (mutation.type === 'childList') {
					for (const addedNode of mutation.addedNodes) {
						if (addedNode.nodeType === Node.ELEMENT_NODE) {
							const newItems = addedNode.querySelectorAll('span[data-v-5b9e5f50] > span[slot="trigger"]');
							for (const newItem of newItems) {
								processItem(newItem);
							}
						}
					}
				}
			}
		};
		const observer = new MutationObserver(callback);
		observer.observe(document, { childList: true, subtree: true });
	}
	function removeCover(){
		let profile = document.querySelector(".introduction.marked");
		if(profile && profile.style.display === "none"){
			profile.style.display = "block";
			for(let i=0;i<profile.parentElement.children.length;++i){
				if(profile.parentElement.children[i].innerText === "系统维护,该内容暂不可见。"){
					profile.parentElement.children[i].remove();
				}
			}
		}
	}
	function alwaysRemoveCover(){
		const observer = new MutationObserver(() => removeCover());
		observer.observe(document,{
			childList: true,
			subtree: true
		});
		removeCover();
	}
	const problemToColorMap = new Map();
	class FetchRateLimiter{
		constructor(limit) { // limit 以毫秒为单位,表示相邻两次 fetch 操作间的最小间隔
			this.limit = limit;
			this.queue = [];
			this.queuePrior = [];
			this.active = false;
			this.requestCache = new Map();
		}
		process() {
			this.active = 1;
			let resolve, reject, url;
			if(this.queuePrior.length > 0){
				({resolve, reject, url} = this.queuePrior.shift());
			}
			else if(this.queue.length > 0){
				({resolve, reject, url} = this.queue.shift());
			}
			else{
				this.active = 0;
				return;
			}
			console.log(url);
			fetch(url)
			.then(resolve)
			.catch(reject);
			setTimeout(this.process.bind(this), this.limit);
		}
		push(url, prior) {
			if(this.requestCache.has(url)) return this.requestCache.get(url);
			const request = new Promise((resolve, reject) => {
				if(prior) this.queuePrior.push({url, resolve, reject});
				else this.queue.push({url, resolve, reject});
				if(!this.active) this.process();
			})
			.then((response) => {
				return response.text();
			});
			this.requestCache.set(url,request);
			return request;
		}
	}
	const limiter = new FetchRateLimiter(300);
	async function getProblemColor(problemid, prior=false){
		if(window.location.href.startsWith('https://www.luogu.com.cn/record')){
			const resultList = _feInstance.currentData.records.result;
			if(!resultList.lgbot1Visited){
				for(const item of resultList){
					problemToColorMap.set(item.problem.pid,`rgb(${problemDifficultyToColor[item.problem.difficulty].join(',')})`);
				}
				resultList.lgbot1Visited = true;
			}
		}
		if(/^https:\/\/www\.luogu\.com\.cn\/user\/\d+#practice$/.test(window.location.href)) {
			let problemList = _feInstance.currentData.submittedProblems;
			if(!problemList.lgbot1Visited){
				for(const item of problemList){
					problemToColorMap.set(item.pid,`rgb(${problemDifficultyToColor[item.difficulty].join(',')})`);
				}
				problemList.lgbot1Visited = true;
			}
			problemList = _feInstance.currentData.passedProblems;
			if(!problemList.lgbot1Visited){
				for(const item of problemList){
					problemToColorMap.set(item.pid,`rgb(${problemDifficultyToColor[item.difficulty].join(',')})`);
				}
				problemList.lgbot1Visited = true;
			}
		}
		if(problemToColorMap.has(problemid)) return problemToColorMap.get(problemid);
		const url = '/problem/'+problemid;
		let data;
		try{
			data = await limiter.push(url, prior);
		} catch (error) {
			console.error('Error fetching user data:', error);
			console.log(problemid);
			return;
		}
		const parser = new DOMParser();
		const doc = parser.parseFromString(data, 'text/html');
		let scriptText = doc.querySelector("script").textContent;
		scriptText = scriptText.match(/window\._feInjection = JSON\.parse\(decodeURIComponent\("(.+)"\)\);/);
		if(!scriptText) return;
		scriptText = scriptText[1];
		const _feInjection = JSON.parse(decodeURIComponent(scriptText));
		const problemDifficulty = _feInjection.currentData.problem.difficulty;
		problemToColorMap.set(problemid,`rgb(${problemDifficultyToColor[problemDifficulty].join(',')})`);
		return problemToColorMap.get(problemid);
	}
	function isProblemId(problemid){
		if(problemid.startsWith('AT_')) return true;
		if(!/[a-zA-Z]/.test(problemid)) return false;
		if(!/[0-9]/.test(problemid)) return false;
		return true;
	}
	async function addProblemColor(item){
		let problemid = item.href.split('/').pop();
		let prior = false;
		if(problemid.includes('?forum=')){
			problemid = problemid.split('=').pop();
			prior = true;
		}
		problemid = problemid.split('?')[0];
		problemid = problemid.split('=').pop();
		if(item.matches('a[data-v-bade3303][data-v-4842157a]'))
			if(problemid === "javascript:void 0")
				problemid = item.innerText.split(' ')[0];
		if(!isProblemId(problemid)) return;
		if(item.innerText.startsWith(problemid)){
			const spanItem = item.children[0];
			if(spanItem && spanItem.matches('span.pid') && spanItem.innerText === problemid){
				const color = await getProblemColor(problemid, prior);
				spanItem.style.color = color;
				spanItem.style.fontWeight = 'bold';
			}
			else{
				const color = await getProblemColor(problemid, prior);
				const content = item.innerHTML;
				item.innerHTML = content.replace(problemid,`<b style="color: ${color};">${problemid}</b>`);
			}
		}
	}
	async function addProblemsColor(){
		const observer = new MutationObserver(async (mutationsList) => {
			for (const mutation of mutationsList) {
				if (mutation.type === 'childList') {
					for (const addedNode of mutation.addedNodes) {
						if (addedNode.nodeType === Node.ELEMENT_NODE) {
							const newItems = addedNode.querySelectorAll('a[href]');
							if(addedNode.matches('a[href]')) addProblemColor(addedNode);
							for (const newItem of newItems) {
								addProblemColor(newItem);
							}
						}
					}
				}
				else if(mutation.type === 'characterData') {
					if(mutation.target.parentElement.matches('span.pid')){
						mutation.target.parentElement.style.color = await getProblemColor(mutation.target.textContent);
						mutation.target.parentElement.style.fontWeight = 'bold';
					}
				}
			}
		});
		observer.observe(document, {
			childList: true,
			subtree: true,
			characterData: true,
		});
		const nodelist = document.querySelectorAll('a[href]');
		for(const i of nodelist){
			addProblemColor(i);
		}
	}
	setTimeout(addSettingButton, 500);
	if(localStorage.getItem('lgbot1RemoveAd') === 'true'){
		setTimeout(removeAd, 500);
	}
	if(localStorage.getItem('lgbot1Removeback') === 'true'){
		removeBack();
	}
	if(localStorage.getItem('lgbot1AddMessageLink') === 'true'){
		if(window.location.href.startsWith('https://www.luogu.com.cn/chat')){
			setTimeout(addMessageLink,500);
		}
	}
	if(localStorage.getItem('lgbot1RemoveCover') === 'true'){
		setTimeout(alwaysRemoveCover, 500);
	}
	if(localStorage.getItem('lgbot1AddProblemsColor') === 'true'){
		setTimeout(addProblemsColor, 500);
	}
})();

5. Luogu-CopyMarkdown

// ==UserScript==
// @name         Luogu-CopyMarkdown
// @namespace    https://github.com/Luogu-Extended-Org/Luogu-CopyMarkdown
// @description  获取洛谷部分页面源代码
// @author       BlackPanda
// @license      MIT
// @version      1.4.2
// @match          https://*.luogu.com.cn/*
// @match          https://*.luogu.org/*
// @grant        none
// @downloadURL https://update.greasyfork.org/scripts/469489/Luogu-CopyMarkdown.user.js
// @updateURL https://update.greasyfork.org/scripts/469489/Luogu-CopyMarkdown.meta.js
// ==/UserScript==
 
(function() {
    'use strict';
    var url = window.location.href;
    async function user_detail(){
        var button = document.createElement('button');
        button.textContent = '复制Md';
        button.style.position = "absolute";
        button.style.top = "100px";button.style.right = "100px";
        window.addEventListener('scroll', function() {
            var scrollY = window.scrollY;
            button.style.top = (100 + scrollY) + 'px';
        });
        button.classList.add('button-lgcm');
        button.addEventListener('click', async function() {
            var introduction = _feInstance.currentData.user.introduction;
            await navigator.clipboard.writeText(introduction);
            alert('复制成功');
        });
        document.body.appendChild(button);
    }
    async function blog(){
            var button = document.createElement('button');
            button.textContent = '复制Md';
            button.style.position = "absolute";
            button.style.top = "100px";button.style.right = "100px";
            window.addEventListener('scroll', function() {
                var scrollY = window.scrollY;
                button.style.top = (100 + scrollY) + 'px';
            });
            button.classList.add('button-lgcm');
            button.addEventListener('click', async function() {
                fetch('/api/blog/detail/' + BlogGlobals.blogID).then(res => res.json()).then(res => navigator.clipboard.writeText(res.data.Content));
                alert('复制成功');
            });
            document.body.appendChild(button);
        };
    async function contest_detail(){
        var button = document.createElement('button');
        button.textContent = '复制Md';
        button.style.position = "absolute";
        button.style.top = "100px";button.style.right = "100px";
        window.addEventListener('scroll', function() {
            var scrollY = window.scrollY;
            button.style.top = (100 + scrollY) + 'px';
        });
        button.classList.add('button-lgcm');
        button.addEventListener('click', async function() {
            var introduction = _feInstance.currentData.contest.description;
            await navigator.clipboard.writeText(introduction);
            alert('复制成功');
        });
        document.body.appendChild(button);
    };
    async function training_detail(){
        var button = document.createElement('button');
        button.textContent = '复制Md';
        button.style.position = "absolute";
        button.style.top = "100px";button.style.right = "100px";
        window.addEventListener('scroll', function() {
            var scrollY = window.scrollY;
            button.style.top = (100 + scrollY) + 'px';
        });
        button.classList.add('button-lgcm');
        button.addEventListener('click', async function() {
            var introduction = _feInstance.currentData.training.description;
            await navigator.clipboard.writeText(introduction);
            alert('复制成功');
        });
        document.body.appendChild(button);
    };
    var style = document.createElement('style');
    style.textContent = `
        .button-lgcm {
            outline:none !important;
            cursor: pointer;
            line-height: 1.25;
            position: relative;
            display: block;
            margin-left: -.0625rem;
            padding: .5rem .75rem;
            color: #fff !important;
            border: .0625rem solid #dee2e6;
            font-size: 15px;
            font-weight: unset;
            display: flex;
            min-width: 36px;
            height: 36px;
            margin: 0 3px;
            border-radius: 100px!important;
            align-items: center;
            justify-content: center;
            transition:all .3s;
            background-color: #5e72e4;
        }
        .button-lgcm:hover {
            box-shadow: 0 7px 14px rgba(50,50,93,.1), 0 3px 6px rgba(0,0,0,.08);
            transform: translateY(-1px);
        }
        `;
    document.head.appendChild(style);
    if (url.includes('blog') && !url.includes('Admin') && !url.includes('admin')) {
        var parsedUrl = new URL(url);
        if (url.includes('org')) {
            var path = parsedUrl.pathname.split('/');
            if (path.length >= 2 && path[1] != '') {
                window.addEventListener('load', blog);
            }
        } else {
            var path = parsedUrl.pathname.split('/');
            if (path.length >= 4) {
                console.log('a');
                window.addEventListener('load', blog);
            }
        }
    }
    if (url.includes('user') && !url.includes('notification')) {
        window.addEventListener('load', user_detail);
    }
    if (url.includes('contest') && !url.includes('list') && !url.includes('edit') && !url.includes('contestId')) {
        window.addEventListener('load', contest_detail);
    }
    if (url.includes('training') && !url.includes('edit') && !url.includes('list')) {
        window.addEventListener('load', training_detail);
    }
})();

6. 洛谷讨论区显示时间改成具体时间

// ==UserScript==
// @name          洛谷讨论区显示时间改成具体时间
// @namespace     https://www.luogu.com.cn/user/542457
// @description   将原本的“……前”改为具体的时间
// @author        cff_0102
// @run-at        document-end
// @version       1.2.3
// @license       MIT
// @match         https://www.luogu.com/*
// @match         https://www.luogu.com.cn/*
// @icon          https://www.luogu.com.cn/favicon.ico
// @downloadURL https://update.greasyfork.org/scripts/474901/%E6%B4%9B%E8%B0%B7%E8%AE%A8%E8%AE%BA%E5%8C%BA%E6%98%BE%E7%A4%BA%E6%97%B6%E9%97%B4%E6%94%B9%E6%88%90%E5%85%B7%E4%BD%93%E6%97%B6%E9%97%B4.user.js
// @updateURL https://update.greasyfork.org/scripts/474901/%E6%B4%9B%E8%B0%B7%E8%AE%A8%E8%AE%BA%E5%8C%BA%E6%98%BE%E7%A4%BA%E6%97%B6%E9%97%B4%E6%94%B9%E6%88%90%E5%85%B7%E4%BD%93%E6%97%B6%E9%97%B4.meta.js
// ==/UserScript==

(function() {

    'use strict';

    // 下面这个变量控制是否要修改个人主页动态中的时间为具体时间,可手动更改为 0
    const chbb = 1;

    // 下面这个函数可以关闭广告。不想关的话注释掉就行了。
    function closeDivsWithAttributes() {
        let attributeValue = '';
        var divElements = document.getElementsByTagName('div');
        for (var i = 0; i < divElements.length; i++) {
            var div = divElements[i];
            if (div.getAttribute('data-v-0a593618') === attributeValue) {
                div.remove();
            }
        }
    }
    setInterval(closeDivsWithAttributes, 250);

    function formatTime(isoDatetime) {
        const date = new Date(isoDatetime);
        const year = date.getFullYear();
        const month = (date.getMonth() + 1).toString().padStart(2, '0');
        const day = date.getDate().toString().padStart(2, '0');
        const hours = date.getHours().toString().padStart(2, '0');
        const minutes = date.getMinutes().toString().padStart(2, '0');
        const seconds = date.getSeconds().toString().padStart(2, '0');
        return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
    }

    // 更改时间显示

    function changeTimeText() {
        let timeElements = document.querySelectorAll('time[data-v-f9624136][datetime]');
        timeElements.forEach((timeElement) => {
            const datetime = timeElement.getAttribute('datetime');
            const formattedTime = formatTime(datetime);
            timeElement.textContent = formattedTime;
        });
        timeElements = document.querySelectorAll('time[data-v-65a77fa1][datetime]');
        timeElements.forEach((timeElement) => {
            const datetime = timeElement.getAttribute('datetime');
            const formattedTime = formatTime(datetime);
            timeElement.textContent = formattedTime;
        });
        timeElements = document.querySelectorAll('time[data-v-85ede732][datetime]');
        timeElements.forEach((timeElement) => {
            const datetime = timeElement.getAttribute('datetime');
            const formattedTime = formatTime(datetime);
            timeElement.textContent = formattedTime;
        });
        timeElements = document.querySelectorAll('time[data-v-6784177c][datetime]');
        timeElements.forEach((timeElement) => {
            const datetime = timeElement.getAttribute('datetime');
            const formattedTime = formatTime(datetime);
            timeElement.textContent = formattedTime;
        });
    }

    setInterval(changeTimeText, 500);

    function extractTimeText(element) {
        const timeElement = element.querySelector('.time');
        return timeElement ? timeElement.getAttribute('title') : null;
    }

    // 更新时间文本

    function updateLastReplyText() {
        const elements = document.querySelectorAll('div[data-v-b0d342de][data-v-f9624136].row.row-space-between.bottom');
        elements.forEach((element) => {
            const linkElement = element.querySelector('a.row.content-left.title.link.color-default');
            const linkElement1 = linkElement.querySelector('div.time');
            const timeText = linkElement1.title;
            if (timeText) {
                const titleText = `最后回复于 ${timeText}`;
                if (linkElement1) {
                    linkElement1.textContent = titleText;
                }
            }
        });
        elements.forEach((element) => {
            const linkElement = element.querySelector('div.row.content-left');
            const linkElement1 = linkElement.querySelector('div.time');
            const timeText = linkElement1.title;
            if (timeText) {
                const titleText = `发表于 ${timeText}`;
                if (linkElement1) {
                    linkElement1.textContent = titleText;
                }
            }
        });
    }

    setInterval(updateLastReplyText, 500);

    function benben(){
        // 找到所有 class 为 span.lfe-caption 的元素
        var elements = document.querySelectorAll('span.lfe-caption');

        // 遍历每个元素并进行操作
        elements.forEach(function(element) {
            var temp = element.textContent;
            // 提取 title 属性中的数字
            var title = element.getAttribute('title');
            if(title){
                var numbers = title.match(/\d+/g);

                if (numbers && numbers.length >= 5) {
                    // 提取第三个数字和第四个数字
                    var thirdNumber = parseInt(numbers[2]);
                    var fourthNumber = parseInt(numbers[3]);

                    // 处理日凌晨的情况
                    if (title.includes("日凌晨")) {
                        if (fourthNumber === 12) {
                            fourthNumber = "0";
                        } else {
                            //fourthNumber = fourthNumber < 10 ? "0" + fourthNumber : fourthNumber.toString();
                        }
                    }

                    // 处理日早上、日上午、日下午、日晚上的情况
                    if (title.includes("日早上") || title.includes("日上午")) {
                        //fourthNumber = fourthNumber < 10 ? "0" + fourthNumber : fourthNumber.toString();
                    } else if (title.includes("日下午") || title.includes("日晚上")) {
                        fourthNumber += 12;
                    }

                    // 补全第二个数和第三个数并更新 text
                    var secondNumber = parseInt(numbers[1]);
                    var secondNumberStr = secondNumber < 10 ? "0" + secondNumber : secondNumber.toString();
                    var thirdNumberStr = thirdNumber < 10 ? "0" + thirdNumber : thirdNumber.toString();
                    var fourthNumberStr = parseInt(fourthNumber) < 10 ? "0" + fourthNumber : fourthNumber.toString();
                    if(fourthNumberStr=="24")fourthNumberStr="12";

                    var newText = `${numbers[0]}-${secondNumberStr}-${thirdNumberStr} ${fourthNumberStr}:${numbers[4]}`;

                    // 更新元素的 text
                    element.textContent = newText;
                    element.setAttribute('title',temp);
                }
            }
        });

    }
    if(chbb)setInterval(benben, 500);

})();

Atcoder 脚本

1. Atcoder Better

// ==UserScript==
// @name         Atcoder Better!
// @namespace    https://greasyfork.org/users/747162
// @version      1.17.0
// @description  一个适用于 AtCoder 的 Tampermonkey 脚本,增强功能与界面。
// @author       北极小狐
// @match        *://atcoder.jp/*
// @run-at       document-start
// @connect      www2.deepl.com
// @connect      api-free.deepl.com
// @connect      api.deepl.com
// @connect      api.deeplx.org
// @connect      www.iflyrec.com
// @connect      dict.youdao.com
// @connect      api.interpreter.caiyunai.com
// @connect      translate.google.com
// @connect      openai.api2d.net
// @connect      api.openai.com
// @connect      www.luogu.com.cn
// @connect      vjudge.net
// @connect      clist.by
// @connect      greasyfork.org
// @connect      sustech.edu.cn
// @connect      aowuucdn.oss-cn-beijing.aliyuncs.com
// @connect      aowuucdn.oss-accelerate.aliyuncs.com
// @connect      127.0.0.1
// @connect      *
// @grant        GM_xmlhttpRequest
// @grant        GM_info
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_listValues
// @grant        GM_deleteValue
// @grant        GM_addStyle
// @grant        GM_setClipboard
// @grant        GM_getResourceText
// @icon         https://aowuucdn.oss-accelerate.aliyuncs.com/atcoder.png
// @require      https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/turndown/7.2.0/turndown.min.js#sha512-sJzEecN5Nk8cq81zKtGq6/z9Z/r3q38zV9enY75IVxiG7ybtlNUt864sL4L1Kf36bYIwxTMVKQOtU4VhD7hGrw==
// @require      https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/markdown-it/13.0.2/markdown-it.js#sha512-2LtYcLGnCbAWz9nDIrfG2pHFiFu9n+3oGecQlzLuYsLgen/oxiYscGWnDST9J9EZanlsQkDD0ZP2n/6peDuALQ==
// @require      https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/crypto-js/4.2.0/crypto-js.min.js#sha512-a+SUDuwNzXDvz4XrIcXHuCf089/iJAoN4lmrXJg18XnduKK6YlDHNRalv4yd1N40OKI80tFidF+rqTFKGPoWFQ==
// @require      https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/chroma-js/2.4.2/chroma.min.js#sha512-zInFF17qBFVvvvFpIfeBzo7Tj7+rQxLeTJDmbxjBz5/zIr89YVbTNelNhdTT+/DCrxoVzBeUPVFJsczKbB7sew==
// @require      https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/xterm/5.5.0/xterm.js#sha512-Gujw5GajF5is3nMoGv9X+tCMqePLL/60qvAv1LofUZTV9jK8ENbM9L+maGmOsNzuZaiuyc/fpph1KT9uR5w3CQ==
// @require      https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/dexie/4.0.7/dexie.min.js#sha512-882VotT07mOQRzqIxsyxHzJX0XUaoeee3qXp4THg1A0KI0XFnWFAaLFQm0x6OW3pHSIipVZW+gzQ1w9b6uvkVw==
// @require      https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/i18next/23.11.5/i18next.min.js#sha512-3RSGkmT48HnO+hlmzGYDx5/w2LIBX0O5hSuYX6KWAxmvVlSjFgoxIaWa2tlMExheGvt3lLyxeTsXfpC47yb8CQ==
// @require      https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/i18next-http-backend/2.5.2/i18nextHttpBackend.min.js#sha512-bBb+wrGRTx4MvHpksYb1Iv5oJ1o8ineCqpc0cnTgdJQhuAFJJ93SEVXxUOCptvt0vAqYdjzWO5emorYUBt6Ceg==
// @require      https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/jquery-i18next/1.2.1/jquery-i18next.min.js#sha512-79RgNpOyaf8AvNEUdanuk1x6g53UPoB6Fh2uogMkOMGADBG6B0DCzxc+dDktXkVPg2rlxGvPeAFKoZxTycVooQ==
// @require      https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/highlight.js/11.9.0/highlight.min.js#sha512-D9gUyxqja7hBtkWpPWGt9wfbfaMGVt9gnyCvYa+jojwwPHLCzUm5i8rpk7vD7wNee9bA35eYIjobYPaQuKS1MQ==
// @require      https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/dialog-polyfill/0.5.6/dialog-polyfill.min.js#sha512-qUIG93zKzcLBVD5RGRbx2PBmbVRu+tJIl+EPLTus0z8I1AMru9sQYdlf6cBacSzYmZVncB9rcc8rYBnazqgrxA==
// @require      https://update.greasyfork.org/scripts/484742/1311040/i18nextChainedBackendjs.js#sha512-JYm2AqU8EvoEOnCucDItAsNtmGcjbxccOXjnwNFp87zdlyclpEephXrgR2sMlWj/gL4DCJUN3X0JhI1omaRO0A==
// @require      https://update.greasyfork.org/scripts/484743/1311041/i18next-localstorage-backendjs.js#sha512-kY1lU3DCvgzkWkOl47sIlmLKdgDcO4T3NYN6p/ET4oi3fnKO74sHUt1xYGtksIHXciKF8Jt+N4RDqG3CRoeYww==
// @resource     acwing_cpp_code_completer https://aowuucdn.oss-accelerate.aliyuncs.com/acwing_cpp_code_completer-0.0.11.json#sha512-DQVpao4qMMExToRdid0g/S0nbO/C9hwCECjI5aW8A0g7nvi8hEcD2Lw3QIqdJBV7haP15oJOocfwuiw7ryTO9w==
// @resource     wandboxlist https://wandbox.org/api/list.json
// @resource     xtermcss https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/xterm/5.5.0/xterm.min.css#sha512-XpXUuzg5afNt1bsgnrOesXP70TLH8tXYYK5sK+Y0UV+YBvJn9EfRFYWy4HT3TVDfH0nl1CO0lwOxIrt2gk9qjg==
// @resource     selectpagecss https://aowuucdn.oss-accelerate.aliyuncs.com/css/selectpage.css#sha512-cRXJfA2tEcAxHEKylJfxteY17N7j9fia3waahHOVnvl63uVZT9OQ7jjjpofZMVZ4JSX3BRET+mI8UvKnsXd3NA==
// @resource     dialogpolyfillcss https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/dialog-polyfill/0.5.6/dialog-polyfill.min.css#sha512-J2+1q+RsZuJXabBfH1q/fgRr6jMy9By5SwVLk7bScEW7NFJkMUXxfeOyyxtDe6fsaJ4jsciexSlGrPYn9YbBIg==
// @license      GPL3
// @compatible	 Chrome
// @compatible	 Firefox
// @compatible	 Edge
// @incompatible safari
// @supportURL   https://github.com/beijixiaohu/OJBetter/issues
// @name:zh-TW   Atcoder Better!
// @name:en      Atcoder Better!
// @name:de      Atcoder Better!
// @name:fr      Atcoder Better!
// @name:ko      Atcoder Better!
// @name:pt      Atcoder Better!
// @name:ja      Atcoder Better!
// @name:es      Atcoder Better!
// @name:it      Atcoder Better!
// @name:hi      Atcoder Better!
// @description         一个适用于 AtCoder 的 Tampermonkey 脚本,增强功能与界面。
// @description:zh-TW   一個適用於 AtCoder 的 Tampermonkey 腳本,增強功能與界面。
// @description:en      A Tampermonkey script for AtCoder that enhances functionality and interface.
// @description:de      Ein Tampermonkey-Skript für AtCoder, das Funktionalität und Benutzeroberfläche verbessert.
// @description:fr      Un script Tampermonkey pour AtCoder qui améliore les fonctionnalités et l'interface.
// @description:ko      AtCoder를 위한 Tampermonkey 스크립트로 기능과 인터페이스를 개선합니다.
// @description:pt      Um script Tampermonkey para AtCoder que aprimora a funcionalidade e a interface.
// @description:ja      AtCoder用のTampermonkeyスクリプトで機能とインターフェースを強化します。
// @description:es      Un script Tampermonkey para AtCoder que mejora la funcionalidad y la interfaz.
// @description:it      Uno script Tampermonkey per AtCoder che migliora la funzionalità e l'interfaccia.
// @description:hi      AtCoder के लिए एक Tampermonkey स्क्रिप्ट जो कार्यक्षमता और इंटरफ़ेस को बेहतर बनाता है।
// @downloadURL https://update.greasyfork.org/scripts/471106/Atcoder%20Better%21.user.js
// @updateURL https://update.greasyfork.org/scripts/471106/Atcoder%20Better%21.meta.js
// ==/UserScript==

/**
 * @namespace OJBetter
 * @desc 主命名空间
 */
const OJBetter = {};

/**
 * @namespace state
 * @desc 描述脚本的当前状态。
 * @memberof OJBetter
 */
OJBetter.state = {
    /** @type {string} 脚本名*/
    name: GM_info.script.name,
    /** @type {string} 格式化后的脚本名*/
    formatName: undefined,
    /** @type {string} 版本号*/
    version: GM_info.script.version,
    /** @type {boolean?} 是否跳过页面加载等待 */
    notWaiteLoaded: undefined,
    /** @type {string} 最后公告版本,用于标识版本更新完成提示 */
    lastAnnounceVer: undefined,
    /** @type {string} 最后读取的有效公告版本 */
    lastReadAnnounceVer: undefined,
    /** @type {number} 当前已打开的模态对话框数量*/
    openDialogCount: 0
};

/**
 * @namespace common
 * @desc 通用设置和属性。
 * @memberof OJBetter
 */
OJBetter.common = {
    /** @type {string} 网站的主机地址 */
    hostAddress: location.origin,
    /** @type {string?} Atcoder的CSRF令牌 */
    at_csrf_token: undefined,
    /** @type {Array?} 任务队列 */
    taskQueue: undefined,
    /** @type {object} OJBetter数据库连接实例*/
    database: undefined,
    /** @type {object} turndownService实例*/
    turndownService: undefined,
};

/**
 * @namespace basic
 * @desc 基本的用户界面设置。
 * @memberof OJBetter
 */
OJBetter.basic = {
    /** @type {string} 黑暗模式设置 */
    darkMode: undefined,
    /** @type {boolean?} 是否展开折叠块 */
    expandFoldingblocks: undefined,
    /** @type {boolean?} 是否开启折叠块渲染性能优化 */
    renderPerfOpt: undefined,
    /** @type {boolean?} 是否开启下拉选择框性能优化 */
    selectElementPerfOpt: undefined,
    /** @type {boolean?} 评论区分页 */
    commentPaging: undefined,
    /** @type {boolean?} 显示跳转到Luogu按钮 */
    showJumpToLuogu: undefined,
    /** @type {boolean?} 显示跳转到Virtual Judge按钮 */
    showCF2vjudge: undefined,
    /** @type {boolean?} 比赛排行榜重新着色 */
    standingsRecolor: undefined
};

/**
 * @namespace typeOfPage
 * @desc 页面类型判断。
 * @memberof OJBetter
 */
OJBetter.typeOfPage = {
    /** @type {boolean?} 是否是轻量站 */
    is_mSite: false,
    /** @type {boolean?} 是否是acmsguru页面 */
    is_acmsguru: false,
    /** @type {boolean?} 是否是旧版LaTeX页面 */
    is_oldLatex: false,
    /** @type {boolean?} 是否是题目集页面 */
    is_contest: undefined,
    /** @type {boolean?} 是否是题目页面 */
    is_problem: undefined,
    /** @type {boolean?} 是否是完整的问题集页面 */
    is_completeProblemset: false,
    /** @type {boolean?} 是否是问题集中的问题页面 */
    is_problemset_problem: false,
    /** @type {boolean?} 是否是问题集页面 */
    is_problemset: false,
    /** @type {boolean?} 是否是Codeforces排名页面 */
    is_cfStandings: false,
    /** @type {boolean?} 是否是提交页面 */
    is_submitPage: false,
    /** @type {boolean?} 是否是代码状态页面 */
    is_statePage: false,
    /** @type {boolean?} 是否是提交记录页面 */
    is_submissions: false,
    /** @type {boolean?} 是否是主页 */
    is_homepage: undefined,
    /** @type {boolean?} 是否选择的是英语页面 */
    isEnglishLanguage: undefined,
    /** @type {boolean?} 是否是题解页面 */
    isEditorial: undefined,
};

/**
 * @namespace localization
 * @desc 本地化设置。
 * @memberof OJBetter
 */
OJBetter.localization = {
    /** @type {string?} 网站语言 */
    websiteLang: undefined,
    /** @type {string?} 脚本语言 */
    scriptLang: undefined
};

/**
 * @namespace translation
 * @desc 翻译设置。
 * @memberof OJBetter
 */
OJBetter.translation = {
    /** @type {string?} 翻译服务选择 */
    choice: undefined,
    /** @type {string?} 目标语言 */
    targetLang: undefined,
    comment: {
        /** @type {string?} 评论翻译服务选择 */
        choice: undefined,
        /** @type {string?} 评论翻译模式 */
        transMode: undefined
    },
    auto: {
        /** @type {boolean?} 自动翻译开关 */
        enabled: undefined,
        /** @type {number?} 短文本长度限制 */
        shortTextLength: undefined,
        mixTrans: {
            /** @type {boolean?} 混合翻译开关 */
            enabled: undefined,
            /** @type {Array?} 混合翻译服务列表 */
            servers: undefined
        }
    },
    memory: {
        /** @type {boolean?} 翻译记忆开关 */
        enabled: undefined,
        /** @type {Object?} 翻译记忆树 */
        ttTree: undefined
    },
    /** @type {string?} 重翻译时的行为 */
    retransAction: undefined,
    /** @type {number?} 等待时间 */
    waitTime: undefined,
    /** @type {boolean?} 替换符 */
    replaceSymbol: undefined,
    /** @type {boolean?} 过滤文本中的*号 */
    filterTextWithoutEmphasis: undefined
};

/**
 * @namespace clist
 * @desc Clist相关设置。
 * @memberof OJBetter
 */
OJBetter.clist = {
    enabled: {
        /** @type {boolean?} 比赛页面开关 */
        contest: undefined,
        /** @type {boolean?} 问题页面开关 */
        problem: undefined,
        /** @type {boolean?} 问题集页面开关 */
        problemset: undefined
    },
    /** @type {boolean?} Rating数据防剧透 */
    ratingHidden: undefined,
    /** @type {string?} Clist key */
    authorization: undefined
};

/**
 * @namespace monaco
 * @desc Monaco编辑器配置。
 * @memberof OJBetter
 */
OJBetter.monaco = {
    /** @type {boolean?} 在问题页面上启用Monaco编辑器 */
    enableOnProblemPage: undefined,
    /** @type {boolean?} 美化pre代码块 */
    beautifyPreBlocks: undefined,
    /** @type {boolean} Monaco编辑器加载完成标志 */
    loaderOnload: false,
    lsp: {
        /** @type {Array?} LSP套接字数组 */
        socket: [],
        /** @type {boolean?} 是否启用LSP */
        enabled: undefined,
        /** @type {string?} 工作路径 */
        workUri: undefined,
        /** @type {string?} 套接字URL */
        socketUrl: undefined
    },
    complet: {
        /** @type {boolean?} 是否启用C++代码补全模板 */
        cppCodeTemplate: undefined,
        /** @type {Object?} 自定义配置 */
        customConfig: undefined
    },
    /** @type {Object?} Monaco编辑器实例 */
    editor: null,
    /** @type {string?} 在线编译器选择 */
    onlineCompilerChoice: undefined,
    /** @type {string?} 记忆编译器语言选择 */
    compilerSelection: undefined,
    /** @type {string?}  当前选择的语言 */
    nowLangSelect: undefined,
    setting: {
        /** @type {Array?} 语言设置数组 */
        language: [],
        /** @type {string?} 位置 */
        position: undefined,
        /** @type {boolean} 位置初始化标志 */
        position_initialized: false,
        /** @type {number?} 字体大小 */
        fontsize: undefined,
        /** @type {boolean?} 鼠标滚动锁定 */
        alwaysConsumeMouseWheel: undefined,
        /** @type {boolean?} 提交代码二次确认 */
        isCodeSubmitDoubleConfirm: undefined,
        /** @type {boolean?} 测试通过后自动提交 */
        autoSubmitAfterPass: undefined,
        /** @type {string?} 提交按钮位置 */
        submitButtonPosition: undefined,
        /** @type {boolean?} 自动保存代码 */
        autoMemoryCode: undefined
    }
};

/**
 * @namespace deepl
 * @desc DeepL翻译服务配置。
 * @memberof OJBetter
 */
OJBetter.deepl = {
    /** @type {Object?} DeepL配置对象 */
    configs: undefined,
    config: {
        /** @type {string?} 类型 */
        type: undefined,
        /** @type {string?} 名称 */
        name: undefined,
        /** @type {string?} API类型 */
        apiGenre: undefined,
        /** @type {string?} API密钥 */
        key: undefined,
        /** @type {string?} 代理 */
        proxy: undefined,
        /** @type {Object?} 额外请求头 */
        header: undefined,
        /** @type {Object?} 额外请求数据 */
        data: undefined,
        quota: {
            /** @type {string?} 余额URL */
            url: undefined,
            /** @type {string?} 余额请求方法 */
            method: undefined,
            /** @type {Object?} 余额请求头 */
            header: undefined,
            /** @type {Object?} 余额请求数据 */
            data: undefined,
            /** @type {number?} 剩余配额 */
            surplus: undefined
        }
    },
    /** @type {boolean?} 启用重点保护 */
    enableEmphasisProtection: undefined,
    /** @type {boolean?} 启用链接保护 */
    enableLinkProtection: undefined
};

/**
 * @namespace chatgpt
 * @desc ChatGPT服务配置。
 * @memberof OJBetter
 */
OJBetter.chatgpt = {
    /** @type {Object?} ChatGPT配置对象 */
    configs: undefined,
    config: {
        /** @type {string?} 名称 */
        name: undefined,
        /** @type {string?} 模型 */
        model: undefined,
        /** @type {string?} API密钥 */
        key: undefined,
        /** @type {string?} 代理 */
        proxy: undefined,
        /** @type {Object?} 额外请求头 */
        header: undefined,
        /** @type {Object?} 额外请求数据 */
        data: undefined,
        quota: {
            /** @type {string?} 余额URL */
            url: undefined,
            /** @type {string?} 余额请求方法 */
            method: undefined,
            /** @type {Object?} 余额请求头 */
            header: undefined,
            /** @type {Object?} 余额请求数据 */
            data: undefined,
            /** @type {number?} 剩余配额 */
            surplus: undefined
        }
    },
    /** @type {boolean?} 是否为流式传输 */
    isStream: undefined,
    /** @type {string?} 是否使用自定义Prompt */
    customPrompt: undefined,
    /** @type {boolean?} 是否作为系统Prompt */
    asSystemPrompt: undefined
};

/**
 * @namespace preference
 * @desc 偏好设置
 * @memberof OJBetter
 */
OJBetter.preference = {
    /** @type {boolean?} 是否显示加载动画 */
    showLoading: undefined,
    /** @type {boolean?} 是否显示悬停目标区域 */
    hoverTargetAreaDisplay: undefined,
    /** @type {string?} 按钮图标大小 */
    iconButtonSize: undefined,
};

/**
 * @namespace dev
 * @desc 维护
 * @memberof OJBetter
 */
OJBetter.dev = {
    /** @type {boolean?} 是否显示规则标记 */
    isRuleMarkingEnabled: undefined,
};

/**
 * @namespace about
 * @desc 关于页信息
 * @memberof OJBetter
 */
OJBetter.about = {
    /** @type {string?} 更新通道 */
    updateChannel: undefined,
    /** @type {string?} 更新源 */
    updateSource: undefined
};

/**
 * @namespace supportList
 * @desc 支持列表
 * @memberof OJBetter
 */
OJBetter.supportList = {
    /** @type {object} 翻译支持列表和对应语言代码*/
    translationSupport: {
        'deepl': { 'zh': 'ZH', 'de': 'DE', 'fr': 'FR', 'ko': 'KO', 'pt': 'PT', 'ja': 'JA', 'es': 'ES', 'it': 'IT' },
        'iflyrec': { 'zh': '1' },
        'youdao': { 'zh': 'zh-CHS', 'zh-Hant': 'zh-CHT', 'de': 'de', 'fr': 'fr', 'ko': 'ko', 'pt': 'pt', 'ja': 'ja', 'es': 'es', 'it': 'it', 'hi': 'hi' },
        'google': { 'zh': 'zh-CN', 'zh-Hant': 'zh-TW', 'de': 'de', 'fr': 'fr', 'ko': 'ko', 'pt': 'pt', 'ja': 'ja', 'es': 'es', 'it': 'it', 'hi': 'hi' },
        'caiyun': { 'zh': 'auto2zh', 'ja': 'auto2ja', 'ko': 'auto2ko', 'es': 'auto2es', 'fr': 'auto2fr' },
        'openai': { 'zh': 'Chinese', 'zh-Hant': 'Traditional Chinese', 'de': 'German', 'fr': 'French', 'ko': 'Korean', 'pt': 'Portuguese', 'ja': 'Japanese', 'es': 'Spanish', 'it': 'Italian', 'hi': 'Hindi' }
    },
    /** @type {object} 更新源支持列表*/
    updateSourceSupportList: {
        'greasyfork': {
            'release': true,
            'dev': false
        },
        'github': {
            'release': true,
            'dev': true
        },
        'aliyunoss': {
            'release': true,
            'dev': true
        }
    }
}

// ------------------------------
// 一些工具函数
// ------------------------------

/**
 * 延迟函数 
 * @param {number} ms 延迟时间(毫秒) 
 * @returns {Promise<void>}
 */
function OJB_delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

/**
 * 等待直到指定的条件函数返回true。
 * 
 * @param {() => boolean} conditionCheck 一个无参数的函数,用于检查条件是否满足。当函数返回true时,表示条件已满足。
 * @param {number} [interval=100] 检查条件的间隔时间,单位为毫秒。默认为100毫秒。
 * @returns {Promise<void>} 返回一个Promise,在条件满足时解决。
 */
async function OJB_waitUntilTrue(conditionCheck, interval = 100) {
    return new Promise((resolve) => {
        const checkCondition = async () => {
            if (conditionCheck()) {
                resolve();
            } else {
                await OJB_delay(interval);
                checkCondition();
            }
        };
        checkCondition();
    });
}

/**
 * 动态加载JavaScript库并返回一个Promise,该Promise在脚本加载完成后解决。
 *
 * @param {string} url - 要加载的JavaScript库的URL地址。
 * @param {string} [expectedHash] - 可选的Base64编码的SHA-512哈希值,用于校验脚本内容。格式为 "sha512-<Base64编码的哈希值>"。
 * @returns {Promise<void>} 一个Promise,它在脚本加载并执行完成后解决。
 */
async function OJB_LoadJS(url, expectedHash) {
    /**
     * 计算给定数据的SHA-512哈希值,并将其转换为十六进制字符串。
     *
     * @param {string} data - 要计算哈希值的数据。
     * @returns {Promise<string>} 一个Promise,它解析为数据的SHA-512哈希值的十六进制字符串。
     */
    const calculateHash = async (data) => {
        const encoder = new TextEncoder();
        const dataBuffer = encoder.encode(data);
        const hashBuffer = await crypto.subtle.digest('SHA-512', dataBuffer);
        const hashArray = Array.from(new Uint8Array(hashBuffer));
        return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
    };

    /**
     * 将Base64编码的字符串转换为十六进制字符串。
     *
     * @param {string} base64 - Base64编码的字符串。
     * @returns {string} 转换后的十六进制字符串。
     */
    const base64ToHex = (base64) => {
        const binaryString = atob(base64);
        const byteArray = new Uint8Array(binaryString.length);
        for (let i = 0; i < binaryString.length; i++) {
            byteArray[i] = binaryString.charCodeAt(i);
        }
        return Array.from(byteArray).map(b => b.toString(16).padStart(2, '0')).join('');
    };

    try {
        const response = await fetch(url);
        if (!response.ok) throw new Error(`Failed to fetch script: ${response.statusText}`);
        const scriptContent = await response.text();

        if (expectedHash) {
            // 去掉前缀 "sha512-"
            const base64Hash = expectedHash.replace(/^sha512-/, '');
            const actualHash = await calculateHash(scriptContent);
            const expectedHashHex = base64ToHex(base64Hash);
            if (actualHash !== expectedHashHex) throw new Error('SHA-512 hash mismatch');
        }

        const scriptElement = document.createElement("script");
        scriptElement.textContent = scriptContent;
        document.head.prepend(scriptElement);

        return Promise.resolve();
    } catch (error) {
        return Promise.reject(error);
    }
}

/**
 * 安全地创建JQuery对象
 * @description 通过字符串创建JQuery对象时,如果字符串以空格开头,在某些Jquery版本中会发生错误,过滤空格以安全的创建元素。
 * @param {string} string - 字符串。
 * @returns {JQuery} JQuery对象
 */
const OJB_safeCreateJQElement = function (string) {
    return $(string.replace(/^\s+/, ""));
}


/**
 * 将数字或者字符串解析为数字。
 * @memberof OJBetter.common
 * @param {string} val 要解析的字符串
 * @param {boolean} [strict=false] 是否进行严格类型检查
 * @returns {number} 解析结果
 * @throws {Error} 如果解析失败,则抛出错误
 */
const OJB_parseNumber = (val, strict = false) => {
    const num = Number(val);
    if (isNaN(num) || (strict && val.toString() !== num.toString())) {
        throw new Error('Invalid number');
    }
    return num;
};

/**
 * 将字符串解析为布尔值
 * @param {string} val - 要解析的字符串
 * @param {boolean} strict - 是否进行严格类型检查
 * @returns {boolean} - 解析结果
 * @throws {Error} - 如果解析失败,则抛出错误
 */
const OJB_parseBoolean = (val, strict) => {
    if (strict) {
        if (val === true || val === false) return val;
        throw new Error('Invalid boolean');
    }
    return val === 'true' ? true : val === 'false' ? false : val;
};

/**
 * 将字符串解析为对象
 * @param {string} val - 要解析的字符串
 * @returns {Object} - 解析结果
 * @throws {Error} - 如果解析失败,则抛出错误
 */
const OJB_parseObject = val => {
    try {
        return JSON.parse(val);
    } catch {
        throw new Error('Invalid JSON');
    }
};

/**
 * 将字符串解析为键值对数组
 * @param {string} val - 要解析的字符串
 * @returns {Object[]} - 解析结果
 * @throws {Error} - 如果解析失败,则抛出错误
 */
const OJB_parseLinePairArray = val => {
    if (typeof val !== 'string' || val.trim() === '') return [];
    return val.split("\n").filter(line => line.trim() !== '').map(line => {
        const indexOfFirstColon = line.indexOf(":");
        if (indexOfFirstColon === -1) throw new Error('Invalid LinePairArray format: ":" is missing');
        const key = line.substring(0, indexOfFirstColon).trim();
        const value = line.substring(indexOfFirstColon + 1).trim();
        return { [key]: value };
    });
};

/**
 * 移除文本中的HTML标签
 * @param {string} text - 包含HTML标签的文本
 * @returns {string} - 移除HTML标签后的文本
 */
const OJB_removeHTMLTags = function (text) {
    return text.replace(/<\/?[a-zA-Z]+("[^"]*"|'[^']*'|[^'">])*>/g, '');
}

/**
 * 获取对象中指定路径表达式的值
 * @param {Object} obj - 要计算的对象
 * @param {string} pathOrExpression - 要计算的路径表达式
 * @returns {any} - 计算结果
 * @example
 * const obj = {
 *   "a": {
 *     "b": 1
 *   },
 *   "c": 2
 * };
 * OJB_evaluatePathOrExpression(obj, "a.b"); // 1
 * OJB_evaluatePathOrExpression(obj, "a.b + c"); // 3
 * OJB_evaluatePathOrExpression(obj, "a.b + a.c"); // 1 
 */
function OJB_evaluatePathOrExpression(obj, pathOrExpression) {
    const hasOperator = /[\+\-\*\/]/.test(pathOrExpression);
    const getPathValue = (obj, path) => {
        return path.split('.').reduce((acc, part) => {
            return acc !== undefined && acc !== null && acc.hasOwnProperty(part) ? acc[part] : undefined;
        }, obj);
    };
    const evaluateExpression = (obj, expression) => {
        const tokens = expression.split(/([\+\-\*\/])/).map(token => token.trim());
        const values = tokens.map(token => {
            if (/[\+\-\*\/]/.test(token)) {
                return token;
            } else {
                const value = getPathValue(obj, token);
                return value !== undefined ? value : 0;
            }
        });
        const evaluatedExpression = values.join(' ');
        try {
            return Function(`'use strict'; return (${evaluatedExpression});`)();
        } catch (e) {
            console.error('Expression evaluation error:', e);
            return undefined;
        }
    };
    return hasOperator ? evaluateExpression(obj, pathOrExpression) : getPathValue(obj, pathOrExpression);
}

/**
 * 获取 GM 存储的值并根据类型进行处理
 * @param {string} key - 要检索的值的键。
 * @param {any} defaultValue - 如果值未找到,则返回的默认值。
 * @param {Object} [options={}] - 配置选项对象。
 * @param {string} [options.type='string'] - 期望的值的类型。可选值:'string', 'number', 'boolean', 'object', 'array', 'linePairArray'。
 * @param {boolean} [options.strict=false] - 用于数字和布尔类型,表示是否进行严格类型检查。
 * @param {string} [options.pathOrExpression=''] - 用于对象或数组类型,表示路径表达式或获取元素的索引。
 * @returns {any} - 检索到的值。
 */
const OJB_getGMValue = (key, defaultValue, { type = 'string', strict = false, pathOrExpression = '' } = {}) => {
    let value = GM_getValue(key);
    if (value === undefined || value === null || value === "") {
        GM_setValue?.(key, defaultValue);
        return defaultValue;
    }

    const parsers = {
        string: val => val,
        number: (val) => OJB_parseNumber(val, strict),
        boolean: (val) => OJB_parseBoolean(val, strict),
        object: OJB_parseObject,
        array: OJB_parseObject,
        linePairArray: OJB_parseLinePairArray
    };

    if (!(type in parsers)) {
        console.error(`Unsupported type: ${type}`);
        return defaultValue;
    }

    try {
        value = parsers[type](value);
    } catch (e) {
        console.error('Error:', e.message);
        return defaultValue;
    }

    // The pathOrExpression processing is not applicable to linePairArray type
    if ((type === 'object' || type === 'array') && pathOrExpression) {
        const evaluated = OJB_evaluatePathOrExpression(value, pathOrExpression);
        if (evaluated === undefined) {
            console.error('Path or expression evaluation returned undefined');
            return defaultValue;
        }
        value = evaluated;
    }

    return value;
};

/**
 * 版本号比较方法
 * @param {string} version1 版本号1
 * @param {string} version2 版本号2
 * @returns {number} -1: version1 < version2, 0: version1 = version2, 1: version1 > version2
 */
const OJB_compareVersions = function (version1 = "0", version2 = "0") {
    const v1Array = version1.split(".").map(Number);
    const v2Array = version2.split(".").map(Number);
    const length = Math.max(v1Array.length, v2Array.length);
    for (let i = 0; i < length; i++) {
        const diff = (v1Array[i] || 0) - (v2Array[i] || 0);
        if (diff) return Math.sign(diff);
    }
    return 0;
}

/**
 * 获取上一个主版本号
 * @param {string} currentVersion 当前版本号
 * @returns {string} 上一个主版本号
 */
const OJB_getPreviousVersion = function (currentVersion) {
    const versionArray = currentVersion.split(".").map(Number);
    let lastNonZeroIndex = versionArray.length - 1;
    while (lastNonZeroIndex >= 0 && versionArray[lastNonZeroIndex] === 0) {
        lastNonZeroIndex--;
    }
    if (lastNonZeroIndex >= 0) {
        versionArray[lastNonZeroIndex]--;
        for (let i = lastNonZeroIndex + 1; i < versionArray.length; i++) {
            versionArray[i] = 0;
        }
    }
    return versionArray.join(".");
};

/**
 * 在指定根节点下观察指定选择器的元素,当元素存在时,执行回调函数
 * @param {Object} options - 配置对象
 * @param {string} options.selector - CSS选择器文本
 * @param {Function} options.callback - 回调函数,接收变动的节点作为参数
 * @param {Boolean} [options.triggerOnExist=true] - 如果为true,元素已存在时立即触发一次回调
 * @param {Element} [options.root=document.body] - 在哪个根节点下监听变化
 * @param {Boolean} [options.subtree=false] - 是否监听子树变化(即非直接子元素)
 */
function OJB_observeElement({
    selector,
    callback,
    triggerOnExist = true,
    root = document.body,
    subtree = false
}) {
    // 尝试获取选择器指定的元素
    const targetNode = root.querySelector(selector);

    if (targetNode) {
        // 如果元素已存在,直接开始观察
        observeAndReport(targetNode, callback);
        // 如果triggerOnExist为true,则立即触发一次回调
        if (triggerOnExist) {
            callback(targetNode);
        }
    } else {
        // 如果元素不存在,监听DOM变化直到该元素被添加
        const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                mutation.addedNodes.forEach((node) => {
                    if (node.nodeType === Node.ELEMENT_NODE && node.matches(selector)) {
                        observeAndReport(node, callback);
                        if (triggerOnExist) {
                            callback(node);
                        }
                        observer.disconnect(); // 停止监听
                    }
                });
            });
        });

        observer.observe(root, { childList: true, subtree, attributes: false });
    }

    function observeAndReport(node, callback) {
        const childObserver = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                mutation.addedNodes.forEach((addedNode) => {
                    if (addedNode.nodeType === Node.ELEMENT_NODE) {
                        callback(addedNode); // 执行回调函数
                    }
                });
            });
        });

        childObserver.observe(node, { childList: true, subtree: true, attributes: false });
    }
}

/**
 * 初始化全局变量
 */
async function initVar() {
    const { hostname, href } = window.location;
    OJBetter.state.formatName = (() => OJBetter.state.name
        .toLowerCase()
        .replace(/\s+/g, '-')
        .replace(/[^a-z0-9-]/g, ''))();
    OJBetter.state.lastAnnounceVer = OJB_getGMValue("lastAnnounceVer", "0");
    OJBetter.state.lastReadAnnounceVer = OJB_getGMValue("lastReadAnnounceVer", "0");
    OJBetter.typeOfPage.is_contest = /\/contests\/[^\/]+\/tasks\/?$/.test(href);
    OJBetter.typeOfPage.is_problem = href.includes('/tasks/');
    OJBetter.typeOfPage.is_homepage = (href === 'https://atcoder.jp/' || href === 'https://atcoder.jp/?lang=ja');
    OJBetter.typeOfPage.isEnglishLanguage = $('meta[http-equiv="Content-Language"]').attr('content') === 'en';
    OJBetter.typeOfPage.isEditorial = href.includes("editorial");
    OJBetter.localization.websiteLang = OJB_getGMValue("localizationLanguage", "zh");
    OJBetter.localization.scriptLang = OJB_getGMValue("scriptL10nLanguage", "zh");
    OJBetter.basic.renderPerfOpt = OJB_getGMValue("renderPerfOpt", false);
    OJBetter.basic.selectElementPerfOpt = OJB_getGMValue("selectElementPerfOpt", true);
    OJBetter.basic.commentPaging = OJB_getGMValue("commentPaging", true);
    OJBetter.basic.showJumpToLuogu = OJB_getGMValue("showJumpToLuogu", true);
    OJBetter.basic.showCF2vjudge = OJB_getGMValue("showCF2vjudge", true);
    OJBetter.basic.standingsRecolor = OJB_getGMValue("standingsRecolor", true);
    OJBetter.state.notWaiteLoaded = OJB_getGMValue("notWaiteLoaded", false);
    OJBetter.translation.targetLang = OJB_getGMValue("transTargetLang", "zh");
    OJBetter.translation.choice = OJB_getGMValue("translation", "deepl");
    OJBetter.translation.comment.transMode = OJB_getGMValue("commentTranslationMode", "0");
    OJBetter.translation.comment.choice = OJB_getGMValue("commentTranslationChoice", "0");
    OJBetter.translation.memory.enabled = OJB_getGMValue("memoryTranslateHistory", true);
    OJBetter.translation.auto.enabled = OJB_getGMValue("autoTranslation", false);
    OJBetter.translation.auto.shortTextLength = OJB_getGMValue("shortTextLength", "2000");
    OJBetter.translation.retransAction = OJB_getGMValue("retransAction", "0");
    OJBetter.translation.waitTime = OJB_getGMValue("transWaitTime", "200");
    OJBetter.translation.auto.mixTrans.enabled = OJB_getGMValue("allowMixTrans", true);
    OJBetter.translation.auto.mixTrans.servers = OJB_getGMValue("mixedTranslation", ['deepl', 'iflyrec', 'youdao', 'caiyun']);
    OJBetter.common.taskQueue = new TaskQueue();
    OJBetter.translation.replaceSymbol = OJB_getGMValue("replaceSymbol", "2");
    OJBetter.translation.filterTextWithoutEmphasis = OJB_getGMValue("filterTextWithoutEmphasis", false);
    OJBetter.clist.enabled.contest = OJB_getGMValue("showClistRating_contest", false);
    OJBetter.clist.enabled.problem = OJB_getGMValue("showClistRating_problem", false);
    OJBetter.clist.enabled.problemset = OJB_getGMValue("showClistRating_problemset", false);
    OJBetter.clist.ratingHidden = OJB_getGMValue("RatingHidden", false);
    OJBetter.clist.authorization = OJB_getGMValue("clist_Authorization", "");
    //deepl
    OJBetter.deepl.config.type = OJB_getGMValue("deepl_type", "free");
    OJBetter.deepl.configs = OJB_getGMValue("deepl_config", {
        "choice": "",
        "configurations": []
    });
    if (OJBetter.deepl.configs.choice !== "" && OJBetter.deepl.configs.configurations.length !== 0) {
        const choice = OJBetter.deepl.configs.choice;
        const configuration = OJBetter.deepl.configs.configurations.find(obj => obj.name === choice);;
        if (configuration == undefined) {
            let existingConfig = GM_getValue('deepl_config');
            existingConfig.choice = "";
            GM_setValue('deepl_config', existingConfig);
            location.reload();
        }
        OJBetter.deepl.config.name = configuration.name;
        OJBetter.deepl.config.apiGenre = configuration.apiGenre;
        OJBetter.deepl.config.key = configuration.key;
        OJBetter.deepl.config.proxy = configuration.proxy;
        OJBetter.deepl.config.header = OJB_parseLinePairArray(configuration._header);
        OJBetter.deepl.config.data = OJB_parseLinePairArray(configuration._data);
        OJBetter.deepl.config.quota.url = configuration.quota_url;
        OJBetter.deepl.config.quota.method = configuration.quota_method;
        OJBetter.deepl.config.quota.header = OJB_parseLinePairArray(configuration.quota_header);
        OJBetter.deepl.config.quota.data = OJB_parseLinePairArray(configuration.quota_data);
        OJBetter.deepl.config.quota.surplus = configuration.quota_surplus;
    }
    OJBetter.deepl.enableEmphasisProtection = OJB_getGMValue("enableEmphasisProtection", true);
    OJBetter.deepl.enableLinkProtection = OJB_getGMValue("enableLinkProtection", true);
    //openai
    OJBetter.chatgpt.isStream = OJB_getGMValue("openai_isStream", true);
    OJBetter.chatgpt.customPrompt = OJB_getGMValue("openai_customPrompt", '');
    OJBetter.chatgpt.asSystemPrompt = OJB_getGMValue("openai_asSystemPrompt", false);
    OJBetter.chatgpt.configs = OJB_getGMValue("chatgpt_config", {
        "choice": "",
        "configurations": []
    });
    if (OJBetter.chatgpt.configs.choice !== "" && OJBetter.chatgpt.configs.configurations.length !== 0) {
        const choice = OJBetter.chatgpt.configs.choice;
        const configuration = OJBetter.chatgpt.configs.configurations.find(obj => obj.name === choice);;
        if (configuration == undefined) {
            let existingConfig = GM_getValue('chatgpt_config');
            existingConfig.choice = "";
            GM_setValue('chatgpt_config', existingConfig);
            location.reload();
        }
        OJBetter.chatgpt.config.name = configuration.name;
        OJBetter.chatgpt.config.model = configuration.model;
        OJBetter.chatgpt.config.key = configuration.key;
        OJBetter.chatgpt.config.proxy = configuration.proxy;
        OJBetter.chatgpt.config.header = OJB_parseLinePairArray(configuration._header);
        OJBetter.chatgpt.config.data = OJB_parseLinePairArray(configuration._data);
        OJBetter.chatgpt.config.quota.url = configuration.quota_url;
        OJBetter.chatgpt.config.quota.method = configuration.quota_method;
        OJBetter.chatgpt.config.quota.header = OJB_parseLinePairArray(configuration.quota_header);
        OJBetter.chatgpt.config.quota.data = OJB_parseLinePairArray(configuration.quota_data);
        OJBetter.chatgpt.config.quota.surplus = configuration.quota_surplus;
    }
    // 编辑器
    // if (!OJBetter.typeOfPage.is_mSite) OJBetter.common.cf_csrf_token = Codeforces.getCsrfToken();
    // else OJBetter.common.cf_csrf_token = "";
    OJBetter.common.at_csrf_token = csrfToken;
    // OJBetter.monaco.compilerSelection = OJB_getGMValue("compilerSelection", "61");
    OJBetter.monaco.compilerSelection = OJB_getGMValue("compilerSelection", "5001");
    OJBetter.monaco.setting.fontsize = OJB_getGMValue("editorFontSize", "15");
    OJBetter.monaco.enableOnProblemPage = OJB_getGMValue("problemPageCodeEditor", true);
    OJBetter.monaco.beautifyPreBlocks = OJB_getGMValue("beautifyPreBlocks", true);
    OJBetter.monaco.complet.cppCodeTemplate = OJB_getGMValue("cppCodeTemplateComplete", true);
    OJBetter.monaco.onlineCompilerChoice = OJB_getGMValue("onlineCompilerChoice", "official");
    OJBetter.monaco.setting.isCodeSubmitDoubleConfirm = OJB_getGMValue("isCodeSubmitConfirm", true);
    OJBetter.monaco.setting.autoSubmitAfterPass = OJB_getGMValue("autoSubmitAfterPass", false);
    OJBetter.monaco.setting.alwaysConsumeMouseWheel = OJB_getGMValue("alwaysConsumeMouseWheel", true);
    OJBetter.monaco.setting.submitButtonPosition = OJB_getGMValue("submitButtonPosition", "bottom");
    OJBetter.monaco.setting.autoMemoryCode = OJB_getGMValue("autoMemoryCode", true);
    //自定义补全
    OJBetter.monaco.complet.customConfig = OJB_getGMValue("Complet_config", {
        "choice": -1,
        "configurations": []
    });
    // monaco
    OJBetter.monaco.lsp.enabled = OJB_getGMValue("useLSP", false);
    OJBetter.monaco.setting.position = OJB_getGMValue("monacoEditor_position", "initial");
    OJBetter.monaco.lsp.workUri = OJB_getGMValue("OJBetter_Bridge_WorkUri", "C:/OJBetter_Bridge");
    OJBetter.monaco.lsp.socketUrl = OJB_getGMValue("OJBetter_Bridge_SocketUrl", "ws://127.0.0.1:2323/");
    OJBetter.preference.showLoading = OJB_getGMValue("showLoading", true);
    OJBetter.preference.hoverTargetAreaDisplay = OJB_getGMValue("hoverTargetAreaDisplay", false);
    OJBetter.basic.expandFoldingblocks = OJB_getGMValue("expandFoldingblocks", true);
    OJBetter.preference.iconButtonSize = OJB_getGMValue("iconButtonSize", "16");
    OJBetter.dev.isRuleMarkingEnabled = OJB_getGMValue("isRuleMarkingEnabled", false);
    OJBetter.about.updateChannel = OJB_getGMValue("updateChannel", "release");
    OJBetter.about.updateSource = OJB_getGMValue("updateSource", "greasyfork");
}

/**
 * 显示警告消息
 */
function showWarnMessage() {
    if (OJBetter.typeOfPage.is_oldLatex) {
        const loadingMessage = new LoadingMessage();
        loadingMessage.updateStatus(`${OJBetter.state.name} —— ${i18next.t('warning.is_oldLatex', { ns: 'alert' })}`, 'warning');
    }
    if (OJBetter.typeOfPage.is_acmsguru) {
        const loadingMessage = new LoadingMessage();
        loadingMessage.updateStatus(`${OJBetter.state.name} —— ${i18next.t('warning.is_acmsguru', { ns: 'alert' })}`, 'warning');
    }
    if (OJBetter.translation.comment.transMode == "1") {
        const loadingMessage = new LoadingMessage();
        loadingMessage.updateStatus(`${OJBetter.state.name} —— ${i18next.t('warning.trans_segment', { ns: 'alert' })}`, 'warning');
    }
    if (OJBetter.translation.comment.transMode == "2") {
        const loadingMessage = new LoadingMessage();
        loadingMessage.updateStatus(`${OJBetter.state.name} —— ${i18next.t('warning.trans_select', { ns: 'alert' })}`, 'warning');
    }
    if (OJBetter.typeOfPage.is_submitPage && OJBetter.monaco.enableOnProblemPage) {
        const loadingMessage = new LoadingMessage();
        loadingMessage.updateStatus(`${OJBetter.state.name} —— ${i18next.t('warning.is_submitPage', { ns: 'alert' })}`, 'warning');
    }
}

// 常量
const helpCircleHTML = '<div class="help-icon"><svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M512 64a448 448 0 1 1 0 896 448 448 0 0 1 0-896zm23.744 191.488c-52.096 0-92.928 14.784-123.2 44.352-30.976 29.568-45.76 70.4-45.76 122.496h80.256c0-29.568 5.632-52.8 17.6-68.992 13.376-19.712 35.2-28.864 66.176-28.864 23.936 0 42.944 6.336 56.32 19.712 12.672 13.376 19.712 31.68 19.712 54.912 0 17.6-6.336 34.496-19.008 49.984l-8.448 9.856c-45.76 40.832-73.216 70.4-82.368 89.408-9.856 19.008-14.08 42.24-14.08 68.992v9.856h80.96v-9.856c0-16.896 3.52-31.68 10.56-45.76 6.336-12.672 15.488-24.64 28.16-35.2 33.792-29.568 54.208-48.576 60.544-55.616 16.896-22.528 26.048-51.392 26.048-86.592 0-42.944-14.08-76.736-42.24-101.376-28.16-25.344-65.472-37.312-111.232-37.312zm-12.672 406.208a54.272 54.272 0 0 0-38.72 14.784 49.408 49.408 0 0 0-15.488 38.016c0 15.488 4.928 28.16 15.488 38.016A54.848 54.848 0 0 0 523.072 768c15.488 0 28.16-4.928 38.72-14.784a51.52 51.52 0 0 0 16.192-38.72 51.968 51.968 0 0 0-15.488-38.016 55.936 55.936 0 0 0-39.424-14.784z"></path></svg></div>';
const closeIcon = `<svg t="1696693011050" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4322" width="14" height="14"><path d="M0 0h1024v1024H0z" fill-opacity="0" p-id="4323"></path><path d="M240.448 168l2.346667 2.154667 289.92 289.941333 279.253333-279.253333a42.666667 42.666667 0 0 1 62.506667 58.026666l-2.133334 2.346667-279.296 279.210667 279.274667 279.253333a42.666667 42.666667 0 0 1-58.005333 62.528l-2.346667-2.176-279.253333-279.253333-289.92 289.962666a42.666667 42.666667 0 0 1-62.506667-58.005333l2.154667-2.346667 289.941333-289.962666-289.92-289.92a42.666667 42.666667 0 0 1 57.984-62.506667z" p-id="4324"></path></svg>`;
const translateIcon = `<svg t="1696837407077" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6325" width="22" height="22"><path d="M536.380952 121.904762a73.142857 73.142857 0 0 1 73.142858 73.142857v219.428571h219.428571a73.142857 73.142857 0 0 1 73.142857 73.142858v341.333333a73.142857 73.142857 0 0 1-73.142857 73.142857H487.619048a73.142857 73.142857 0 0 1-73.142858-73.142857v-219.428571H195.047619a73.142857 73.142857 0 0 1-73.142857-73.142858V195.047619a73.142857 73.142857 0 0 1 73.142857-73.142857h341.333333zM243.809524 682.666667v97.523809h97.523809v73.142857h-97.523809a73.142857 73.142857 0 0 1-73.142857-73.142857v-97.523809h73.142857z m585.142857-195.047619h-219.428571v48.761904a73.142857 73.142857 0 0 1-73.142858 73.142858h-48.761904v219.428571h341.333333V487.619048z m-115.760762 89.526857L787.21219 780.190476h-62.025142l-14.043429-42.715428h-76.068571L620.739048 780.190476h-60.854858l74.605715-203.044571h78.701714z m-38.034286 50.029714h-3.510857l-21.065143 63.488h45.348572l-20.772572-63.488zM536.380952 195.047619H195.047619v341.333333h341.333333V195.047619z 
m-195.072 49.883429l44.78781 1.072762v37.278476h87.698286v145.359238h-87.698286v65.974857h-44.78781v-65.974857h-87.698285v-145.359238h87.698285v-38.351238z m0 83.139047h-44.787809v56.05181h44.787809v-56.05181z m89.307429 0h-44.519619v56.05181h44.519619v-56.05181zM780.190476 170.666667a73.142857 73.142857 0 0 1 73.142857 73.142857v97.523809h-73.142857v-97.523809h-97.523809V170.666667h97.523809z" p-id="6326"></path></svg>`;
const clistIcon = `<svg width="37.7pt" height="10pt" viewBox="0 0 181 48" version="1.1" xmlns="http://www.w3.org/2000/svg"><g id="#0057b8ff"><path fill="#0057b8" opacity="1.00" d=" M 17.36 0.00 L 18.59 0.00 C 23.84 6.49 30.28 11.92 36.01 17.98 C 34.01 19.99 32.01 21.99 30.00 23.99 C 26.02 19.97 22.02 15.98 18.02 11.99 C 14.01 15.98 10.01 19.99 6.00 23.99 C 4.16 22.04 2.30 20.05 0.00 18.61 L 0.00 17.37 C 3.44 15.11 6.00 11.84 8.96 9.03 C 11.79 6.05 15.09 3.47 17.36 0.00 Z" /></g><g id="#a0a0a0ff"><path fill="#a0a0a0" opacity="1.00" d=" M 56.76 13.74 C 61.48 4.80 76.07 3.90 81.77 12.27 C 83.09 13.94 83.44 16.10 83.91 18.12 C 81.53 18.23 79.16 18.24 76.78 18.23 C 75.81 15.72 73.99 13.31 71.14 12.95 C 67.14 12.02 63.45 15.29 62.48 18.99 C 61.30 23.27 61.71 28.68 65.34 31.70 C 67.82 34.05 72.19 33.93 74.61 31.55 C 75.97 30.18 76.35 28.23 76.96 26.48 C 79.36 26.43 81.77 26.44 84.17 26.56 C 83.79 30.09 82.43 33.49 79.89 36.02 C 74.14 41.35 64.17 40.80 58.77 35.25 C 53.52 29.56 53.18 20.38 56.76 13.74 Z" />
<path fill="#a0a0a0" opacity="1.00" d=" M 89.01 7.20 C 91.37 7.21 93.74 7.21 96.11 7.22 C 96.22 15.71 96.10 24.20 96.18 32.69 C 101.25 32.76 106.32 32.63 111.39 32.79 C 111.40 34.86 111.41 36.93 111.41 39.00 C 103.94 39.00 96.47 39.00 89.00 39.00 C 89.00 28.40 88.99 17.80 89.01 7.20 Z" /><path fill="#a0a0a0" opacity="1.00" d=" M 115.00 7.21 C 117.33 7.21 119.66 7.21 121.99 7.21 C 122.01 17.81 122.00 28.40 122.00 39.00 C 119.67 39.00 117.33 39.00 115.00 39.00 C 115.00 28.40 114.99 17.80 115.00 7.21 Z" /><path fill="#a0a0a0" opacity="1.00" d=" M 133.35 7.47 C 139.11 5.56 146.93 6.28 150.42 11.87 C 151.42 13.39 151.35 15.31 151.72 17.04 C 149.33 17.05 146.95 17.05 144.56 17.03 C 144.13 12.66 138.66 11.12 135.34 13.30 C 133.90 14.24 133.54 16.87 135.35 17.61 C 139.99 20.02 145.90 19.54 149.92 23.19 C 154.43 26.97 153.16 35.36 147.78 37.72 C 143.39 40.03 137.99 40.11 133.30 38.69 C 128.80 37.34 125.34 32.90 125.91 28.10 C 128.22 28.10 130.53 28.11 132.84 28.16 C 132.98 34.19 142.68 36.07 145.18 30.97 C 146.11 27.99 142.17 27.05 140.05 26.35 C 135.54 25.04 129.83 24.33 127.50 19.63 C 125.30 14.78 128.42 9.00 133.35 7.47 Z" />
<path fill="#a0a0a0" opacity="1.00" d=" M 153.31 7.21 C 161.99 7.21 170.67 7.21 179.34 7.21 C 179.41 9.30 179.45 11.40 179.48 13.50 C 176.35 13.50 173.22 13.50 170.09 13.50 C 170.05 21.99 170.12 30.48 170.05 38.98 C 167.61 39.00 165.18 39.00 162.74 39.00 C 162.64 30.52 162.73 22.04 162.69 13.55 C 159.57 13.49 156.44 13.49 153.32 13.50 C 153.32 11.40 153.31 9.31 153.31 7.21 Z" /></g><g id="#ffd700ff"><path fill="#ffd700" opacity="1.00" d=" M 12.02 29.98 C 14.02 27.98 16.02 25.98 18.02 23.98 C 22.01 27.99 26.03 31.97 30.00 35.99 C 34.01 31.99 38.01 27.98 42.02 23.99 C 44.02 25.98 46.02 27.98 48.01 29.98 C 42.29 36.06 35.80 41.46 30.59 48.00 L 29.39 48.00 C 24.26 41.42 17.71 36.08 12.02 29.98 Z" /></g></svg>`;

/**
 * 连接数据库
 */
async function initDB() {
    OJBetter.common.database = new Dexie('OJBetterDB');
    OJBetter.common.database.version(3).stores({
        samplesData: '&url',
        editorCode: '&url',
        translateData: '&url',
        localizeSubsData: '&lang'
    });

    // 等待数据库打开
    await OJBetter.common.database.open();
}

/**
 * 清空数据库
 */
async function clearDatabase() {
    const isConfirmed = await OJB_createDialog(
        i18next.t('isClearDatabase.title', { ns: 'dialog' }),
        i18next.t('isClearDatabase.content', { ns: 'dialog' }),
        [
            i18next.t('isClearDatabase.buttons.0', { ns: 'dialog' }),
            i18next.t('isClearDatabase.buttons.1', { ns: 'dialog' })
        ]
    );
    if (!isConfirmed) {
        try {
            // 开启一个读写事务,包含数据库中的所有表
            await OJBetter.common.database.transaction('rw', OJBetter.common.database.tables, async () => {
                // 遍历所有表
                for (const table of OJBetter.common.database.tables) {
                    // 清空当前表
                    await table.clear();
                }
            });
            console.log("All tables in the database have been cleared.");
            alert("All tables in the database have been cleared.");
        } catch (error) {
            console.error("Error clearing the database:", error);
        }
    }
}

/**
 * 导出数据库
 * @returns {Promise<string>} 数据库的JSON字符串
 */
async function exportDatabase() {
    try {
        // 创建一个存储数据的对象
        const exportData = {};
        // 获取数据库中所有表的名称
        const tableNames = OJBetter.common.database.tables.map(table => table.name);

        // 遍历每一个表,获取数据
        for (const tableName of tableNames) {
            const tableData = await OJBetter.common.database.table(tableName).toArray();
            exportData[tableName] = tableData;
        }

        // 将数据对象转换为JSON字符串
        const jsonData = JSON.stringify(exportData, null, 4);
        return jsonData;
    } catch (error) {
        console.error("Error exporting database:", error);
    }
}

/**
 * 导入数据库
 * @param {string} jsonData 数据库的JSON字符串
 */
async function importDatabase(jsonData) {
    const isConfirmed = await OJB_createDialog(
        i18next.t('isImportDatabase.title', { ns: 'dialog' }),
        i18next.t('isImportDatabase.content', { ns: 'dialog' }),
        [
            i18next.t('isImportDatabase.buttons.0', { ns: 'dialog' }),
            i18next.t('isImportDatabase.buttons.1', { ns: 'dialog' })
        ]
    );
    if (!isConfirmed) {
        try {
            // 将JSON字符串解析为对象
            const importData = JSON.parse(jsonData);

            // 开启一个事务,并清空现有数据
            await OJBetter.common.database.transaction('rw', OJBetter.common.database.tables, async () => {
                // 清空所有表的数据
                for (const tableName of OJBetter.common.database.tables.map(table => table.name)) {
                    await OJBetter.common.database.table(tableName).clear();
                }

                // 插入新数据
                for (const [tableName, rows] of Object.entries(importData)) {
                    await OJBetter.common.database.table(tableName).bulkAdd(rows);
                }
            });
            alert("Data imported successfully");
        } catch (error) {
            console.error("Error importing database:", error);
        }
    }
}

/**
 * 将数据下载为文件
 * @param {string} data 数据
 * @param {string} filename 文件名,默认为'export.json'
 * @param {string} fileType 文件MIME类型,默认为'application/json'
 * @returns {void}
 */
function downloadDataAsFile(data, filename = 'export.json', fileType = 'application/json') {
    // 创建一个blob对象,指定文件类型
    const blob = new Blob([data], { type: fileType });
    const url = URL.createObjectURL(blob);

    // 创建一个隐藏的a标签,模拟点击进行下载
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    document.body.appendChild(a);
    a.click();

    // 清理
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
}


/**
 * 从文件中读取数据
 * @param {Function} callback 回调函数
 * @returns {void}
 */
function readFileInput(callback) {
    const fileInput = document.createElement('input');
    fileInput.type = 'file';
    fileInput.accept = '.json';
    fileInput.style.display = 'none'; // 隐藏input元素

    fileInput.onchange = (e) => {
        const file = e.target.files[0];
        if (file) {
            const reader = new FileReader();
            reader.onload = (e) => {
                const fileContent = e.target.result;
                if (callback && typeof callback === 'function') {
                    callback(fileContent); // 调用回调函数,传入文件内容
                }
            };
            reader.readAsText(file);
        }
    };

    document.body.appendChild(fileInput);
    fileInput.click();
    document.body.removeChild(fileInput);
}

/**
 * 清除所有设置
 */
async function deleteAllConfigSettings() {
    const isConfirmed = await OJB_createDialog(
        i18next.t('isDeleteAllConfigSettings.title', { ns: 'dialog' }),
        i18next.t('isDeleteAllConfigSettings.content', { ns: 'dialog' }),
        [
            i18next.t('isDeleteAllConfigSettings.buttons.0', { ns: 'dialog' }),
            i18next.t('isDeleteAllConfigSettings.buttons.1', { ns: 'dialog' })
        ]
    );
    if (!isConfirmed) {
        const keys = GM_listValues();

        keys.forEach(key => {
            GM_deleteValue(key);
        });

        alert('All settings have been deleted.');
        window.location.reload();
    }
}

/**
 * 导出设置到JSON
 * @returns {string} JSON字符串
 */
function exportSettingsToJSON() {
    const keys = GM_listValues();
    let settings = {};

    keys.forEach(key => {
        settings[key] = GM_getValue(key);
    });

    return JSON.stringify(settings, null, 4);
}

/**
 * 从JSON导入设置
 * @param {string} jsonData JSON字符串
 * @returns {void}
 */
async function importSettingsFromJSON(jsonData) {
    const isConfirmed = await OJB_createDialog(
        i18next.t('isImportSettings.title', { ns: 'dialog' }),
        i18next.t('isImportSettings.content', { ns: 'dialog' }),
        [
            i18next.t('isImportSettings.buttons.0', { ns: 'dialog' }),
            i18next.t('isImportSettings.buttons.1', { ns: 'dialog' })
        ]
    );
    if (!isConfirmed) {
        let settings;
        try {
            settings = JSON.parse(jsonData);
        } catch (e) {
            console.error('JSON parsing error:', e);
            return;
        }

        Object.keys(settings).forEach(key => {
            GM_setValue(key, settings[key]);
        });

        alert('Settings imported successfully!');
        window.location.reload();
    }
}

/**
 * 加载元素本地化语言数据
 * @param {JQuery} element jQuery元素
 * @param {number} [retries=10] 重试次数
 * @param {number} [interval=50] 重试间隔
 */
function elementLocalize(element, retries = 10, interval = 50) {
    if ($.isFunction(element.localize)) {
        element.localize();
    } else if (retries > 0) {
        setTimeout(elementLocalize, interval, element, retries - 1, interval);
    } else {
        console.error('Unable to localize', element);
    }
}

// 切换系统黑暗监听
const mediaQueryList = window.matchMedia('(prefers-color-scheme: dark)');
const changeEventListeners = [];
function handleColorSchemeChange(event) {
    event.matches ? $('html').attr('data-theme', 'dark') : $('html').attr('data-theme', 'light');
    if (!event.matches) {
        var originalColor = $(this).data("original-color");
        $(this).css("background-color", originalColor);
        const intervalId = setinterval(() => {
            if (OJBetter.monaco && OJBetter.monaco.editor) {
                monaco.editor.setTheme('vs');
                clearInterval(intervalId);
            }
        }, 100);
    } else {
        const intervalId = setInterval(() => {
            if (OJBetter.monaco && OJBetter.monaco.editor) {
                monaco.editor.setTheme('vs-dark');
                clearInterval(intervalId);
            }
        }, 100);
    }
}

// 黑暗模式
(function setDark() {
    // 初始化
    function setDarkTheme() {
        const htmlElement = document.querySelector('html');
        if (htmlElement) {
            htmlElement.setAttribute('data-theme', 'dark');
            const intervalId = setInterval(() => {
                if (OJBetter.monaco && OJBetter.monaco.editor) {
                    monaco.editor.setTheme('vs-dark');
                    clearInterval(intervalId);
                }
            }, 100);
        } else {
            setTimeout(setDarkTheme, 100);
        }
    }
    OJBetter.basic.darkMode = OJB_getGMValue("darkMode", "follow")
    if (OJBetter.basic.darkMode == "dark") {
        setDarkTheme();
    } else if (OJBetter.basic.darkMode == "follow") {
        // 添加事件监听器
        changeEventListeners.push(handleColorSchemeChange);
        mediaQueryList.addEventListener('change', handleColorSchemeChange);

        if (window.matchMedia('(prefers-color-scheme: dark)').matches) setDarkTheme();
    }

    // 定义全局变量
    GM_addStyle(`
        /* 黑暗支持 */
        html[data-theme=dark]:root {
            color-scheme: light dark;
        }
        /* 颜色 */
        :root {
            /* 文字颜色 */
            --ojb-color-text-primary: #a0adb9; /* 主要文字颜色 */
            --ojb-color-text-secondary: #9AA4B1; /* 次要文字颜色 */
            --ojb-color-text-tertiary: #9BA5B2; /* 第三级文字颜色 */
            --ojb-color-text-success: #43A047; /* 成功状态文字颜色 */
            --ojb-color-text-highlight: #cbd6e2; /* 高亮文字颜色 */
            --ojb-color-text-disabled: #506778; /* 禁用状态文字颜色 */
            --ojb-color-text-icon-success: #2e7d32; /* 成功状态图标颜色 */
            --ojb-color-text-link: #4b8eda; /* 链接颜色 */

            /* 背景颜色 */
            --ojb-color-bg-primary: #22272e; /* 主背景颜色 */
            --ojb-color-bg-secondary: #2d333b; /* 次级背景颜色 */
            --ojb-color-bg-disabled: #24292e; /* 禁用元素背景颜色 */

            /* 边框颜色 */
            --ojb-color-border-primary: #48535F; /* 主要边框颜色 */
            --ojb-color-border-disabled: #404950; /* 禁用状态边框颜色 */
            --ojb-color-border-dashed-hover: #03A9F4; /* 虚线边框悬浮颜色 */
            --ojb-color-border-radio-checked: #326154; /* 选中的单选框边框颜色 */

            /* 阴影颜色 */
            --ojb-shadow-standard: 0px 0px 0.5px 0.5px #3A4048; /* 标准阴影 */
            --ojb-shadow-menu-modal: 0px 0px 0px 4px #2d333b; /* 菜单和模态框阴影 */

            /* 区域遮罩颜色 */
            --ojb-overlay-background: repeating-linear-gradient(135deg, #49525f6e, #49525f6e 30px, #49525f29 0px, #49525f29 55px); /* 区域遮罩背景 */

            /* 文字阴影 */
            --ojb-text-shadow-icon: 1px 1px 0px #2d333b, 1px -1px 0px #2d333b, -1px -1px 0px #2d333b, -1px 1px 0px #2d333b; /* 图标文字阴影 */
        }
        /* 边框样式 */
        :root {
            /* 边框样式 */
            --ojb-border-width: 1px; /* 边框宽度 */
            --ojb-border-style-solid: solid; /* 实线样式 */
            --ojb-border-style-dashed: dashed; /* 虚线样式 */
            --ojb-border-radius-small: 4px; /* 小圆角 */
            --ojb-border-radius-medium: 8px; /* 中圆角 */
            --ojb-border-radius-large: 12px; /* 大圆角 */
            
            /* 组合边框样式 */
            --ojb-border-solid-primary: var(--ojb-border-width) var(--ojb-border-style-solid) var(--ojb-color-border-primary); /* 主要实线边框 */
            --ojb-border-dashed: var(--ojb-border-width) var(--ojb-border-style-dashed) var(--ojb-color-border-primary); /* 主要虚线边框 */
            --ojb-border-dashed-hover: var(--ojb-border-width) var(--ojb-border-style-dashed) var(--ojb-color-border-dashed-hover); /* 悬浮虚线边框 */
            --ojb-border-solid-disabled: var(--ojb-border-width) var(--ojb-border-style-solid) var(--ojb-color-border-disabled); /* 禁用状态实线边框 */
        }
    `);

    // OJBetter界面样式
    GM_addStyle(`
        /* 主要文字颜色 */
        html[data-theme=dark] .alert-success, html[data-theme=dark] .alert-info, html[data-theme=dark] .alert-error,
        html[data-theme=dark] .alert-warning, html[data-theme=dark] .markItUpEditor, 
        html[data-theme=dark] .translate-problem-statement, html[data-theme=dark] .OJBetter_setting_menu, 
        html[data-theme=dark] .help_tip .tip_text,
        html[data-theme=dark] .OJBetter_setting_menu input, html[data-theme=dark] .OJBetter_setting_menu textarea,
        html[data-theme=dark] #OJBetter_SubmitForm input, html[data-theme=dark] #OJBetter_SubmitForm textarea, html[data-theme=dark] #OJBetter_SubmitForm select,
        html[data-theme=dark] #items-per-page, html[data-theme=dark] #pagBar, 
        html[data-theme=dark] .OJBetter_setting_sidebar li a:link,
        html[data-theme=dark] .popup .content{
            color: var(--ojb-color-text-primary);
        }
        /* 次要文字颜色 */
        html[data-theme=dark] .ojb_btn:hover, html[data-theme=dark] .OJBetter_modal button, html[data-theme=dark] #OJBetter_statusBar,
        html[data-theme=dark] #RunTestButton, html[data-theme=dark] #programTypeId, html[data-theme=dark] #addCustomTest,
        html[data-theme=dark] #customTestBlock, html[data-theme=dark] .OJBetter_setting_list.alert_info{
            color: var(--ojb-color-text-secondary);
        }
        /* 文字颜色3 */
        html[data-theme=dark] .ojb_btn{
            color: var(--ojb-color-text-tertiary);
        }
        /* 文字颜色 浅绿 */
        html[data-theme=dark] #SubmitButton{
            color: var(--ojb-color-text-success);
        }
        /* 禁止文字颜色 */
        html[data-theme=dark] .ojb_btn[disabled]{
            color: var(--ojb-color-text-disabled);
        }
        /* 主要背景层次 */
        html[data-theme=dark] .OJBetter_setting_menu, html[data-theme=dark] .help_tip .tip_text, html[data-theme=dark] li#add_button:hover,
        html[data-theme=dark] .ojb_btn:hover, 
        html[data-theme=dark] .OJBetter_setting_menu input, html[data-theme=dark] .OJBetter_setting_menu textarea,
        html[data-theme=dark] #OJBetter_SubmitForm input, 
        html[data-theme=dark] .OJBetter_setting_menu input[type="checkbox"], html[data-theme=dark] .OJBetter_setting_menu input[type="checkbox"]:checked,
        html[data-theme=dark] #OJBetter_SubmitForm textarea, html[data-theme=dark] #OJBetter_SubmitForm select, 
        html[data-theme=dark] .OJBetter_setting_sidebar li a.active, html[data-theme=dark] .OJBetter_setting_sidebar li,
        html[data-theme=dark] .OJBetter_setting_menu::-webkit-scrollbar-track, html[data-theme=dark] .OJBetter_setting_content::-webkit-scrollbar-track,
        html[data-theme=dark] .OJBetter_modal, html[data-theme=dark] .OJBetter_modal button:hover,
        html[data-theme=dark] .popup .content, 
        html[data-theme=dark] .config_bar_list, html[data-theme=dark] #LSPLog,
        html[data-theme=dark] .OJBetter_setting_menu .OJBetter_checkboxs,
        html[data-theme=dark] .OJBetter_setting_menu .OJBetter_checkboxs input[type="checkbox"]::before,
        html[data-theme=dark] .OJBetter_setting_menu a, html[data-theme=dark] .OJBetter_setting_menu .OJBetter_setting_list button:hover,
        html[data-theme=dark] .OJBetter_setting_menu select{
            background-color: var(--ojb-color-bg-primary);
            background-image: none;
        }
        /* 次要背景层次 */
        html[data-theme=dark] .ojb_btn, 
        html[data-theme=dark] .alert-success, html[data-theme=dark] .alert-info, html[data-theme=dark] .alert-error,
        html[data-theme=dark] .alert-warning, html[data-theme=dark] .SumoSelect>.optWrapper>.options li.opt:hover,
        html[data-theme=dark] .translate-problem-statement-panel,
        html[data-theme=dark] .translate-problem-statement, 
        html[data-theme=dark] .OJBetter_setting_list,
        html[data-theme=dark] .OJBetter_setting_menu hr, 
        html[data-theme=dark] .OJBetter_setting_sidebar li a,
        html[data-theme=dark] .OJBetter_setting_menu::-webkit-scrollbar-thumb, html[data-theme=dark] .OJBetter_setting_content::-webkit-scrollbar-thumb,
        html[data-theme=dark] .OJBetter_modal button, html[data-theme=dark] .test-for-popup pre,
        html[data-theme=dark] .popup .content pre, html[data-theme=dark] .popup .content pre code,
        html[data-theme=dark] ul.config_bar_ul::-webkit-scrollbar-thumb,  html[data-theme=dark] #OJBetter_statusBar,
        html[data-theme=dark] #RunTestButton, html[data-theme=dark] #programTypeId, html[data-theme=dark] .sampleDiv,
        html[data-theme=dark] #addCustomTest, html[data-theme=dark] #LSPLog li:nth-child(odd),
        html[data-theme=dark] .OJBetter_setting_menu .OJBetter_checkboxs input[type="checkbox"]:checked::before,
        html[data-theme=dark] .config::before, html[data-theme=dark] .config li.tempConfig_add_button:hover,
        html[data-theme=dark] .OJBetter_setting_menu details, html[data-theme=dark] #config_bar_menu,
        html[data-theme=dark] .OJBetter_setting_menu .OJBetter_setting_list button, 
        html[data-theme=dark] .OJBetter_setting_menu .badge, html[data-theme=dark] #OJBetter_SubmitForm #SubmitButton{
            background-color: var(--ojb-color-bg-secondary);
        }
        /* 禁止背景层次 */
        html[data-theme=dark] .ojb_btn[disabled]{
            background-color: var(--ojb-color-bg-disabled);
        }
        /* 实线边框颜色-圆角 */
        html[data-theme=dark] .alert-success, html[data-theme=dark] .alert-info, html[data-theme=dark] .alert-error,
        html[data-theme=dark] .alert-warning, html[data-theme=dark] .translate-problem-statement{
            border: var(--ojb-border-solid-primary);
            border-radius: 2px;
        }
        /* 实线边框颜色-无圆角 */
        html[data-theme=dark] .ojb_btn, 
        html[data-theme=dark] .OJBetter_setting_list, html[data-theme=dark] .config_bar_list,
        html[data-theme=dark] label.config_bar_ul_li_text, 
        html[data-theme=dark] .OJBetter_setting_sidebar li, html[data-theme=dark] .OJBetter_setting_menu select,
        html[data-theme=dark] .translate-problem-statement-panel, html[data-theme=dark] .OJBetter_modal button, html[data-theme=dark] #OJBetter_SubmitForm select,
        html[data-theme=dark] #OJBetter_editor, html[data-theme=dark] #OJBetter_statusBar,
        html[data-theme=dark] #OJBetter_SubmitForm #RunTestButton, html[data-theme=dark] #programTypeId, html[data-theme=dark] #customTestBlock,
        html[data-theme=dark] #OJBetter_SubmitForm #addCustomTest, html[data-theme=dark] #OJBetter_SubmitForm #SubmitButton,
        html[data-theme=dark] .OJBetter_setting_menu input,
        html[data-theme=dark] .OJBetter_setting_menu input[type="checkbox"], html[data-theme=dark] .OJBetter_setting_menu input[type="checkbox"]:checked,
        html[data-theme=dark] .OJBetter_setting_menu textarea,
        html[data-theme=dark] #OJBetter_SubmitForm input, html[data-theme=dark] #OJBetter_SubmitForm textarea,
        html[data-theme=dark] #CompilerSetting select, html[data-theme=dark] #CompilerSetting textarea, html[data-theme=dark] #CompilerBox,
        html[data-theme=dark] .OJBetter_setting_menu .OJBetter_checkboxs,
        html[data-theme=dark] .help_tip .tip_text, html[data-theme=dark] .config::before,
        html[data-theme=dark] #statePanel, html[data-theme=dark] .test-case, html[data-theme=dark] .OJBetter_setting_menu .badge{
            border: var(--ojb-border-solid-primary);
        }
        html[data-theme=dark] #customTestBlock #customTests{
            border-top: var(--ojb-border-solid-primary);
        }
        html[data-theme=dark] .OJBetter_setting_sidebar {
            border-right: var(--ojb-border-solid-primary);
        }
        /* 实线边框-禁止 */
        html[data-theme=dark] .ojb_btn[disabled]{
            border: var(--ojb-border-solid-disabled);
        }
        /* 虚线边框 */
        html[data-theme=dark] li#add_button,
        html[data-theme=dark] .OJBetter_setting_menu_label_text{
            border: var(--ojb-border-dashed);
        }
        /* 虚线边框-悬浮 */
        html[data-theme=dark] li#add_button:hover{
            border: var(--ojb-border-dashed-hover);
            background-color: var(--ojb-color-bg-secondary);
            color: var(--ojb-color-border-dashed-hover);
        }
        /* 无边框 */
        html[data-theme=dark] .translate-problem-statement-panel .ojb_btn{
            border: none;
        }
        /* 区域遮罩 */
        html[data-theme=dark] .overlay::before {
            background: var(--ojb-overlay-background);
            color: var(--ojb-color-text-secondary);
            text-shadow: 0px 0px 2px #000000;
        }
        /* 阴影 */
        html[data-theme=dark] .translate-problem-statement-panel, html[data-theme=dark] .translate-problem-statement{
            box-shadow: var(--ojb-shadow-standard);
        }
        /* 图标按钮状态样式 */
        html[data-theme=dark] .ojb_btn_popover.success i:before, html[data-theme=dark] .ojb_btn_popover.success i {
            color: var(--ojb-color-text-icon-success);
        }
        html[data-theme=dark] .ojb_btn_popover i:before {
            text-shadow: var(--ojb-text-shadow-icon);
        }
        /* 其他样式 */
        html[data-theme=dark] .OJBetter_setting_menu, html[data-theme=dark] .OJBetter_modal{
            box-shadow: var(--ojb-shadow-menu-modal);
            border: 1px solid var(--ojb-color-bg-secondary);
        }
        html[data-theme=dark] input[type="radio"]:checked+.OJBetter_setting_menu_label_text {
            color: var(--ojb-color-text-primary);
            border: 1px solid var(--ojb-color-border-radio-checked);
        }
        html[data-theme=dark] .alert{
            text-shadow: none;
        }
    `);

    // 网站界面样式
    GM_addStyle(`
        /* 文字颜色1 */
        html[data-theme=dark] body, html[data-theme=dark] .float-container>#main-container,
        html[data-theme=dark] .panel-default>.panel-heading, html[data-theme=dark] #header a,
        html[data-theme=dark] .pagination>li>a, html[data-theme=dark] .pagination>li>span, html[data-theme=dark] .dropdown-menu,
        html[data-theme=dark] .select2-container--bootstrap .select2-selection--single .select2-selection__rendered,
        html[data-theme=dark] .ace-tm .ace_gutter, html[data-theme=dark] .translate-problem-statement-panel,
        html[data-theme=dark] .select2-container--bootstrap .select2-results__option--highlighted[aria-selected],
        html[data-theme=dark] .nav-pills>li.active>a, html[data-theme=dark] .user-unrated, html[data-theme=dark] #header .header-page li.is-active a,
        html[data-theme=dark] .m-box_inner, html[data-theme=dark] .m-list-job_item, html[data-theme=dark] .a-btn_arrow,
        html[data-theme=dark] #header, html[data-theme=dark] #header .header-sub_page li a,
        html[data-theme=dark] .select2-container--default .select2-selection--single .select2-selection__rendered, html[data-theme=dark] .select2-results{
            color: var(--ojb-color-text-primary) !important;
        }
        /* 文字颜色2 */
        html[data-theme=dark] pre, html[data-theme=dark] .html2mdButton, html[data-theme=dark] .btn-default, html[data-theme=dark] .btn-pre,
        html[data-theme=dark] small.contest-duration, html[data-theme=dark] .select2-container--bootstrap .select2-results__option,
        html[data-theme=dark] #ace_settingsmenu, #kbshortcutmenu, html[data-theme=dark] code{
            color: var(--ojb-color-text-secondary) !important;
        }
        /* 文字颜色3 */
        html[data-theme=dark] input, html[data-theme=dark] #header .header-page li a:hover{
            color: var(--ojb-color-text-secondary);
        }
        /* 文字颜色4 */
        html[data-theme=dark] .katex{
            color: var(--ojb-color-text-highlight) !important;
        }
        /* 链接颜色 */
        html[data-theme=dark] a:link {
            color: var(--ojb-color-text-link);
        }
        html[data-theme=dark] a:visited {
            color: var(--ojb-color-text-secondary);
        }
        /* 按钮 */
        html[data-theme=dark] input:hover, html[data-theme=dark] .btn-default:hover{
            background-color: var(--ojb-color-bg-primary) !important;
        } 
        /* 背景层次1 */
        html[data-theme=dark] body, html[data-theme=dark] #main-div.float-container, html[data-theme=dark] pre,
        html[data-theme=dark] .html2mdButton:hover, html[data-theme=dark] .pagination>.active>a, html[data-theme=dark] .ace-tm,
        html[data-theme=dark] .dropdown-menu>li>a:hover, html[data-theme=dark] .dropdown-menu>li>a:focus,
        html[data-theme=dark] .dropdown-menu .divider, html[data-theme=dark] .select2-container--bootstrap .select2-selection,
        html[data-theme=dark] .ace-tm .ace_gutter-active-line, html[data-theme=dark] .select2-dropdown,
        html[data-theme=dark] input, html[data-theme=dark] button, html[data-theme=dark] select, html[data-theme=dark] textarea,
        html[data-theme=dark] code, html[data-theme=dark] #keyvisual .keyvisual-inner:before, html[data-theme=dark] .m-box_inner,
        html[data-theme=dark] .m-list-job_item, html[data-theme=dark] .select2-container--default .select2-selection--single,
        html[data-theme=dark] ol.linenums, html[data-theme=dark] li.L0, html[data-theme=dark] li.L1, html[data-theme=dark] li.L2,
        html[data-theme=dark] li.L3, html[data-theme=dark] li.L4, html[data-theme=dark] li.L5, html[data-theme=dark] li.L6,
        html[data-theme=dark] li.L7, html[data-theme=dark] li.L8, html[data-theme=dark] li.L9{
            background-color: var(--ojb-color-bg-primary) !important;
        }
        /* 背景层次2 */
        html[data-theme=dark] .float-container>#main-container, html[data-theme=dark] #contest-nav-tabs,
        html[data-theme=dark] .btn-default, html[data-theme=dark] .html2mdButton,
        html[data-theme=dark] .nav-tabs>li.active>a, html[data-theme=dark] .nav-tabs>li.active>a:hover, html[data-theme=dark] .nav-tabs>li.active>a:focus,
        html[data-theme=dark] .nav>li>a:hover, html[data-theme=dark] .nav>li>a:focus, html[data-theme=dark] .panel, 
        html[data-theme=dark] .table-striped>tbody>tr:nth-of-type(odd), html[data-theme=dark] .insert-participant-box,
        html[data-theme=dark] .btn-pre, html[data-theme=dark] .alert-success, html[data-theme=dark] .alert-info, html[data-theme=dark] .alert-danger,
        html[data-theme=dark] .alert-warning, html[data-theme=dark] .panel-default>.panel-heading,
        html[data-theme=dark] .pagination>li>a, html[data-theme=dark] .pagination>li>span, html[data-theme=dark] .dropdown-menu,
        html[data-theme=dark] .ace-tm .ace_gutter, html[data-theme=dark] .select2-container--bootstrap .select2-results__option[aria-selected=true],
        html[data-theme=dark] #ace_settingsmenu, #kbshortcutmenu, html[data-theme=dark] #header .header-inner,
        html[data-theme=dark] ul#config_bar_ul::-webkit-scrollbar-thumb, html[data-theme=dark] .panel-info>.panel-heading,
        html[data-theme=dark] .post-footer, html[data-theme=dark] .a-btn_arrow:before,
        html[data-theme=dark] .table-hover>tbody>tr:hover,
        html[data-theme=dark] li.L1, html[data-theme=dark] li.L3, html[data-theme=dark] li.L5, html[data-theme=dark] li.L7,
        html[data-theme=dark] li.L9{
            background-color: var(--ojb-color-bg-secondary) !important;
        }
        /* 实线边框颜色-圆角 */
        html[data-theme=dark] input{
            border: var(--ojb-border-solid-primary) !important;
            border-radius: 2px;
        }
        /* 实线边框颜色-无圆角 */
        html[data-theme=dark] .btn-default, html[data-theme=dark] .html2mdButton, html[data-theme=dark] .nav-tabs>li>a:hover,
        html[data-theme=dark] .nav-tabs>li.active>a, html[data-theme=dark] .nav-tabs>li.active>a:hover,
        html[data-theme=dark] .nav-tabs>li.active>a:focus, html[data-theme=dark] .btn-pre, html[data-theme=dark] .btn-pre:hover,
        html[data-theme=dark] pre, html[data-theme=dark] .pagination>li>a, html[data-theme=dark] .pagination>li>span,
        html[data-theme=dark] .table-bordered>thead>tr>th, html[data-theme=dark] .table-bordered>tbody>tr>th, html[data-theme=dark] .table-bordered>tfoot>tr>th,
        html[data-theme=dark] .table-bordered>thead>tr>td, html[data-theme=dark] .table-bordered>tbody>tr>td, html[data-theme=dark] .table-bordered>tfoot>tr>td,
        html[data-theme=dark] .panel, html[data-theme=dark] #editor, html[data-theme=dark] div#config_bar_list, html[data-theme=dark] label.config_bar_ul_li_text,
        html[data-theme=dark] .select2-container--bootstrap .select2-selection, html[data-theme=dark] .select2-container--default .select2-selection--single{
            border: var(--ojb-border-solid-primary) !important;
        }
        html[data-theme=dark] hr, html[data-theme=dark] .panel-footer,
        html[data-theme=dark] .table>thead>tr>th, html[data-theme=dark] .table>tbody>tr>th, html[data-theme=dark] .table>tfoot>tr>th,
        html[data-theme=dark] .table>thead>tr>td, html[data-theme=dark] .table>tbody>tr>td, html[data-theme=dark] .table>tfoot>tr>td{
            border-top: var(--ojb-border-solid-primary) !important;
        }
        html[data-theme=dark] .nav-tabs, html[data-theme=dark] .panel-info>.panel-heading, html[data-theme=dark] .panel-default>.panel-heading,
        html[data-theme=dark] .a-btn_arrow{
            border-bottom: var(--ojb-border-solid-primary) !important;
        }
        html[data-theme=dark] .table>thead>tr>th{
            border-bottom: 2px solid var(--ojb-color-border-primary) !important;
        }
        /* 双实线边框 */
        html[data-theme=dark] #header .header-inner{
            border-bottom: 5px double var(--ojb-color-border-primary) !important;
        }
        /* 阴影 */
        html[data-theme=dark] .float-container>#main-container{
            box-shadow: 0px 0px 10px 5px #fff0;
        }
        /* 图片-亮度 */
        html[data-theme=dark] img{
            opacity: .75; 
        }
        /* 反转 */
        html[data-theme=dark] .ace_content, html[data-theme=dark] #header .header-logo img, html[data-theme=dark] pre code{
            filter: invert(1) hue-rotate(.5turn);
        }
        /* 区域遮罩 */
        html[data-theme=dark] .overlay {
            background: repeating-linear-gradient(135deg, #49525f6e, #49525f6e 30px, #49525f29 0px, #49525f29 55px);
            color: #9099a3;
            text-shadow: 0px 0px 2px #000000;
        }
        /* 其他样式 */
        html[data-theme=dark] .nav-tabs>li.active>a, html[data-theme=dark] .nav-tabs>li.active>a:hover, html[data-theme=dark] .nav-tabs>li.active>a:focus{
            border-bottom-color: transparent !important;
        }
        html[data-theme=dark] .collapsible-topic.collapsed .content .collapsible-topic-options:before{
            background-image: linear-gradient(#22272e00, #22272e);
        }
        html[data-theme=dark] .alert{
            text-shadow: none;
        }
        html[data-theme=dark] .m-box-news_post:before{
            background: linear-gradient(0deg, #22272e 50%, rgba(255,255,255,0) 100%);
        }
        html[data-theme=dark] #header .header-sub_page li a:before, html[data-theme=dark] #header .header-page li a:before{
            background-color: #9e9e9e !important;
        }
        html[data-theme=dark] .standings-score{
            color: #2196f3;
        }
        html[data-theme=dark] pre code{
            background-color: transparent !important;
        }
        html[data-theme=dark] #fixed-server-timer {
            color: #000;
        }
    `);
})()

/**
 * 黑暗模式额外的处理事件
 */
function darkModeStyleAdjustment() {

}

/**
 * 初始化monaco编辑器资源
 */
async function initMonacoEditor() {
    if (OJBetter.monaco.enableOnProblemPage || OJBetter.monaco.beautifyPreBlocks) {
        try {
            // 等待Monaco Editor加载器脚本加载完成
            await OJB_LoadJS("https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/monaco-editor/0.49.0/min/vs/loader.min.js", "sha512-ZG31AN9z/CQD1YDDAK4RUAvogwbJHv6bHrumrnMLzdCrVu4HeAqrUX7Jsal/cbUwXGfaMUNmQU04tQ8XXl5Znw==");

            // 配置Monaco Editor
            require.config({
                paths: { vs: "https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/monaco-editor/0.49.0/min/vs" },
                "vs/nls": { availableLanguages: { "*": "zh-cn" } },
            });

            // 加载Monaco Editor主脚本
            require(["vs/editor/editor.main"], () => {
                OJBetter.monaco.loaderOnload = true;
            });
        } catch (error) {
            console.error("Failed to load Monaco Editor: ", error);
        }
    }
}

/**
 * 美化代码块
 */
async function beautifyPreBlocksWithMonaco() {
    // 等待 MonacoLoader 加载完毕
    await OJB_waitUntilTrue(() => OJBetter.monaco.loaderOnload);

    const LINE_HEIGHT = 20; // 每行代码的高度
    const MIN_HEIGHT = 100; // 容器的最小高度

    /**
     * 计算容器的高度
     * @param {number} lineCount 代码的行数
     * @param {string} [maxHeightStr='1000px'] 最大高度,为 'none' 时表示不限制
     * @returns {string} 容器的高度
     */
    const calculateContainerHeight = (lineCount, maxHeightStr = '1000px') => {
        const maxHeight = parseInt(maxHeightStr, 10); // 最大高度
        const calculatedHeight = Math.max(lineCount * LINE_HEIGHT, MIN_HEIGHT);
        return `${isNaN(maxHeight) ? calculatedHeight : Math.min(calculatedHeight, maxHeight)}px`;
    };

    /**
     * 用于替换 <pre> 标签为 Monaco 编辑器的函数
     * @param {HTMLElement} pre <pre> 元素
     */
    const replacePreWithMonaco = (pre) => {
        if (pre.classList.contains('source-code-for-copy')) return; // 跳过复制块

        const code = OJB_getCodeFromPre(pre); // 获取 <pre> 标签中的代码
        if (!code) return;

        const language = OJB_codeLangDetect(code); // 检测代码语言
        const container = document.createElement('div'); // 创建一个用于 Monaco 编辑器的容器
        const lineCount = code.split('\n').length; // 计算代码的行数

        // 设置容器的样式
        container.style.height = calculateContainerHeight(lineCount);
        container.style.width = '100%';
        pre.style.display = 'none';
        pre.insertAdjacentElement('afterend', container);

        // 折叠/展开按钮点击事件
        const toggleExpandCollapse = (() => {
            let isExpanded = false;
            return () => {
                console.log(calculateContainerHeight(lineCount, isExpanded ? '1000px' : 'none'));
                container.style.height = calculateContainerHeight(lineCount, isExpanded ? '1000px' : 'none');
                isExpanded = !isExpanded;
            };
        })();

        // 重新注册 submission-code-expand-btn 按钮的点击事件
        document.querySelectorAll('.submission-code-expand-btn').forEach(button => {
            button.addEventListener('click', toggleExpandCollapse);
        });

        // 初始化 Monaco 编辑器
        monaco.editor.create(container, {
            value: code,
            language,
            readOnly: true,
            tabSize: 4,
            theme: OJBetter.basic.darkMode === "dark" ? "vs-dark" : "vs",
            scrollbar: {
                verticalScrollbarSize: 10,
                horizontalScrollbarSize: 10,
                alwaysConsumeMouseWheel: false
            },
            automaticLayout: true,
            scrollBeyondLastLine: false
        });
    };

    // 全局替换页面上所有的 <pre> 元素
    document.querySelectorAll('pre').forEach(replacePreWithMonaco);

    // 监听页面上的提交状态页面窗口的 <pre> 元素
    if (OJBetter.typeOfPage.is_statePage || OJBetter.typeOfPage.is_submissions) {
        OJB_observeElement({
            selector: '#facebox',
            callback: (node) => {
                // 如果 facebox 中存在 pre 元素,则替换它们
                node.querySelectorAll('pre').forEach(replacePreWithMonaco);
            }
        });
    }
}

// 样式
GM_addStyle(`
/*动画*/
@keyframes shake {
    0% { transform: translateX(-5px); }
    100% { transform: translateX(5px); }
}
@keyframes rotate {
    from {
        transform: rotate(0deg);
    }

    to {
        transform: rotate(360deg);
    }
}
@keyframes rippleout {
    0% {
        box-shadow: 0 0 0 0 rgba(96, 98, 102, 0.2);
    }

    100% {
        box-shadow: 0 0 0 6px rgba(0, 0, 0, 0);
    }
}
@keyframes bounce-in {
	20%,40%,60%,80%,from,to {
		animation-timing-function: cubic-bezier(.215,.61,.355,1);
	}

	0% {
		opacity: 0;
		transform: scale3d(.995,.995,.995);
	}

	20% {
        opacity: 1;
		transform: scale3d(1.005,1.005,1.005);
	}

	40% {
		transform: scale3d(.998,.998,.998);
	}

	60% {
		transform: scale3d(1.002,1.002,1.002);
	}

	80% {
		transform: scale3d(.995,.995,.995);
	}

	to {
		opacity: 1;
		transform: scale3d(1,1,1);
	}
}
/*iconfont图标*/
.iconfont {
    font-family: "iconfont" !important;
    font-size: 16px;
    font-style: normal !important;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}
@font-face {
  font-family: 'iconfont';  /* Project id 4284341 */
  src: url('//aowuucdn.oss-accelerate.aliyuncs.com/iconfont/iconfont.woff2') format('woff2'),
       url('//aowuucdn.oss-accelerate.aliyuncs.com/iconfont/iconfont.woff2.ttf') format('truetype');
}
html {
    scroll-behavior: smooth;
}
:root {
    --vp-font-family-base: "Chinese Quotes", "Inter var", "Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}
span.mdViewContent {
    white-space: pre-wrap;
}

/* dialog */
dialog {
    margin: 0px !important;
}
dialog::backdrop {
    background-color: rgba(0, 0, 0, 0.4);
}

/*题目页链接栏样式*/
#problemToolbar {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    overflow: auto;
    height: 100%;
    margin: 0.5em;
}

/*html2md面板*/
.html2md-panel {
    display: flex;
    justify-content: flex-end;
    align-items: center;
}
.html2md-panel a {
    text-decoration: none;
}
.html2md-panel > button {
    margin: 5px;
}
.html2md-panel.is_simple {
    position: absolute;
    right: 2%;
}

/*通用按钮*/
.ojb_btn {
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    background-color: #ffffff;
    color: #606266;
    width: auto;
    font-size: 13px;
    border-radius: 0.3rem;
    padding: 2px 5px;
    margin: 0px 5px;
    border: 1px solid #dcdfe6;
}
.ojb_btn[disabled] {
    cursor: not-allowed !important;
    color: rgb(168, 171, 178) !important;
    border: 1px solid #e4e7ed;
    background-color: #ffffff;
}
.ojb_btn:hover {
    color: #409eff;
    border-color: #409eff;
    background-color: #f1f8ff;
    z-index: 150;
}
.ojb_btn.primary {
    color: #ffffff;
    border: 1px solid #409eff;
    background-color: #409eff;
}
.ojb_btn.primary:hover {
    color: #ffffff;
    border: 1px solid #79bbff;
    background-color: #79bbff;
}
.ojb_btn.success {  
    color: #4caf50;
    border: 1px solid #C8E6C9;
    background-color: #f0f9eb;
}
.ojb_btn.warning {
    color: #e6a23c;
    border: 1px solid #f3d19e;
    background-color: #fdf6ec;
}
.ojb_btn.error {
    color: #f56c6c;
    border: 1px solid #fab6b6;
    background-color: #fef0f0;
}
.ojb_btn.enabled {
    color: #42A5F5;
    border: 1px solid #90CAF9;
    background-color: #fafbff;
}
.ojb_btn.active {
    animation: rippleout 0.5s ease-in-out;
}
a.ojb_btn {
    text-decoration: none;
}
a.ojb_btn:link {
    color: #606266;
}
a.ojb_btn span {
    margin-left: 2px;
}
/*按钮图标和popover*/
.ojb_btn_popover {
    display: flex;
    justify-content: center;
    position: relative;
    outline: none;
    appearance: none;
}
.ojb_btn_popover:hover span {
    opacity: 1;
    visibility: visible;
}
.ojb_btn_popover i:before {
    position: absolute;
    text-shadow: 1px 1px 0px #ffffff, 1px -1px 0px #ffffff, -1px -1px 0px #ffffff, -1px 1px 0px #ffffff;
}
.ojb_btn_popover span {
    cursor: initial;
    position: absolute;
    left: 50%;
    opacity: 0;
    visibility: hidden;
    padding: 4px 8px;
    background-color: rgba(33, 33, 33, 0.8);
    color: rgba(255, 255, 255, 0.9019607843);
    font-size: 12px;
    border-radius: 6px;
    line-height: 1.6;
    text-align: left;
    white-space: nowrap;
    transition: all 0.15s ease-in-out;
    z-index: 999;
}
.ojb_btn_popover span:hover {
    opacity: 0;
    visibility: hidden;
}
.ojb_btn_popover.top:hover span {
    transform: translate(-50%, 0);
}
.ojb_btn_popover.top span {
    bottom: 100%;
    transform: translate(-50%, -20%);
    margin-bottom: 4px;
}
.ojb_btn_popover.top span:hover {
    transform: translate(-50%, -20%);
}
.ojb_btn_popover.bottom:hover span {
    transform: translate(-50%, 105%);
}
.ojb_btn_popover.bottom span {
    bottom: -2%;
    transform: translate(-50%, 100%);
    margin-top: 4px;
}
.ojb_btn_popover.bottom span:hover {
    transform: translate(-50%, 50%);
}
.ojb_btn_popover.loading i {
    color: rgba(33, 33, 33, 0.1);
}
.ojb_btn_popover.loading i:before {
    content: "\\e640";
    color: rgb(168, 171, 178);
    animation: rotate 2s cubic-bezier(0.65, 0.05, 0.36, 1) infinite;
}
.ojb_btn_popover.running i {
    color: rgba(33, 33, 33, 0.1);
}
.ojb_btn_popover.running i:before {
    content: "\\e600";
    color: rgb(168, 171, 178);
    animation: rotate 1s linear infinite;
}
.ojb_btn_popover.warning i {
    color: rgba(230, 162, 61, 0.8);
}
.ojb_btn_popover.warning i:before {
    content: "\\e68b";
    font-size: 15px;
    left: 10px;
    bottom: 0%;
    color: #ff9800;
}
.ojb_btn_popover.error i {
    color: rgba(245, 108, 108, 0.8);
}
.ojb_btn_popover.error i:before {
    content: "\\e651";
    font-size: 15px;
    left: 10px;
    bottom: 0%;
    color: #F44336;
}
.ojb_btn_popover.success i {
    color: rgba(76, 175, 80, 0.9);
}
.ojb_btn_popover.success i:before {
    content: "\\e61e";
    font-size: 15px;
    left: 10px;
    bottom: 0%;
    color: #4caf50;
}
.ojb_btn_popover.enabled i {
    color: rgba(33, 150, 243, 0.6);
}
.ojb_btn_popover.enabled i:before {
    content: "\\e6f4";
    font-size: 15px;
    left: 10px;
    bottom: 0%;
    color: #2196F3;
}
.ojb_btn_popover.redo i {
    color: rgba(33, 33, 33, 0.1);
}
.ojb_btn_popover.redo i:before {
    content: "\\e831";
    color: #616161;
}
.ojb_btn_popover.reverse i {
    transform: rotate(180deg);
}

/*translateDiv样式*/
.translateDiv .topText {
    display: flex;
    margin-left: 5px;
    color: #9e9e9e;
    font-size: 13px;
    align-items: center;
}
.translateDiv .borderlessButton{
    display: flex;
    align-items: center;
    margin: 2.5px 7px;
    fill: #9E9E9E;
}
.translateDiv .borderlessButton:hover{
    cursor: pointer;
    fill: #059669;
}
.translateDiv.bounce-in {
    animation: bounce-in 1s forwards;
}
html:not([data-theme='dark']) .translateDiv {
    box-shadow: 0px 0px 0.5px 0.5px #defdf378;
}
.translate-problem-statement {
    justify-items: start;
    letter-spacing: 1.8px;
    color: #059669;
    background-color: #f9f9fa;
    border: 1px solid #c5ebdf;
    border-radius: 0rem 0rem 0.3rem 0.3rem;
    padding: 5px;
    margin: -5px 0px 6px 0px;
    width: 100%;
    box-sizing: border-box;
    font-size: 13px;
}
.translate-problem-statement h2 {
    font-size: 1.6em;
    font-weight: 700;
}
.translate-problem-statement h3 {
    font-size: 1.3em;
    font-weight: 700;
}
.translate-problem-statement-panel{
    display: flex;
    justify-content: space-between;
    background-color: #f9f9fa;
    border: 1px solid #c5ebdf;
    border-radius: 0.3rem;
    margin: 4px 0px;
}
.translate-problem-statement-panel .ojb_btn {
    background: none;
    border: none;
    color: #9e9e9e;
}
.translate-problem-statement-panel.error, .translate-problem-statement.error {
    color: red;
    border-color: red;
}
.translate-problem-statement a, .translate-problem-statement a:link {
    color: #10b981;
    font-weight: 600;
    background: 0 0;
    text-decoration: none;
}
.translate-problem-statement ol, .translate-problem-statement ul {
    display: grid;
    margin-inline-start: 0.8em;
    margin-block-start: 0em;
    margin: 0.5em 0 0 3em;
    padding-inline-start: 0px;
}
.translate-problem-statement li {
    display: list-item;
    height: auto;
    word-wrap: break-word;
}
.translate-problem-statement ol li {
    list-style-type: auto;
}
.translate-problem-statement ul li {
    list-style-type: disc;
}
.translate-problem-statement img {
    max-width: 100.0%;
    max-height: 100.0%;
}
#task-statement .translate-problem-statement .MathJax {
    color: #059669!important;
}
.translate-problem-statement span.math {
    margin: 0px 2.5px !important;
}
.translate-problem-statement a:hover {
    background-color: #800;
    color: #fff;
    text-decoration: none;
}
.translate-problem-statement table {
    border: 1px #ccc solid !important;
    margin: 1.5em 0 !important;
    color: #059669 !important;
}
.translate-problem-statement table thead th {
    border: 1px #ccc solid !important;
    color: #059669 !important;
}
.translate-problem-statement table td {
    border-right: 1px solid #ccc;
    border-top: 1px solid #ccc;
    padding: 0.7143em 0.5em;
}
.translate-problem-statement table th {
    padding: 0.7143em 0.5em;
}
.translate-problem-statement p:not(:first-child) {
    margin: 1.5em 0 0;
}
.translate-problem-statement p {
    line-height: 20px !important;
    word-wrap: break-word;
    font-size: 13px !important
}
.problem-statement p:last-child {
    margin-bottom: 0px !important;
}

/*设置按钮*/
header .enter-or-register-box, header .languages {
    position: absolute;
    right: 170px;
}
.ojb_btn.OJBetter_setting {
    float: right;
    height: 30px;
    background: #60a5fa;
    color: white;
    margin: 10px;
    border: 1px solid #60a5fa;
}
.ojb_btn.OJBetter_setting.open {
    background-color: #e6e6e6;
    color: #727378;
    cursor: not-allowed;
}

/*设置面板*/
.OJBetter_setting_menu {
    box-shadow: 0px 0px 0px 4px #ffffff;
    position: fixed;
    top: 50%;
    left: 50%;
    width: 600px;
    min-height: 600px;
    transform: translate(-50%, -50%);
    border-radius: 6px;
    background-color: #f0f4f9;
    border-collapse: collapse;
    border: 1px solid #ffffff;
    color: #697e91;
    font-family: var(--vp-font-family-base);
    padding: 10px 20px 20px 10px;
    box-sizing: content-box;
}
.OJBetter_setting_menu h3 {
    margin-top: 10px;
    font-size: 1.4em;
    font-weight: 700;
}
.OJBetter_setting_menu h4 {
    margin: 15px 0px 10px 0px;
}
.OJBetter_setting_menu h4,.OJBetter_setting_menu h5 {
    font-weight: 600;
}
.OJBetter_setting_menu hr {
    border: none;
    height: 1px;
    background-color: #ccc;
    margin: 10px 0;
}
.OJBetter_setting_menu details {
    padding: 10px;
    margin-bottom: 5px;
    background-color: #ffffff;
    border-bottom: 1px solid #c9c6c696;
    border-radius: 8px;
}
.OJBetter_setting_menu .badge {
    border-radius: 4px;
    border: 1px solid #009688;
    color: #009688;
    background-color: #fff;
    padding: 0.5px 4px;
    margin-left: 5px;
    margin-right: auto;
    line-height: initial;
    font-weight: initial;
}
.OJBetter_setting_menu .missing {
    box-shadow: inset 0px 0px 1px 1px red;
}
/* 页面切换 */
.OJBetter_setting_menu .settings-page {
    display: none;
}
.OJBetter_setting_menu .settings-page.active {
    display: block;
}
.OJBetter_setting_container {
    display: flex;
}
.OJBetter_setting_sidebar {
    flex: 0 0 auto;
    min-width: 110px;
    padding: 6px 10px 6px 6px;
    margin: 20px 0px;
    border-right: 1px solid #d4d8e9;
}
.OJBetter_setting_content {
    flex-grow: 1;
    margin: 20px 0px 0px 12px;
    padding-right: 10px;
    max-height: 580px;
    overflow-y: auto;
    box-sizing: border-box;
}
.OJBetter_setting_sidebar h3 {
    margin-top: 0;
}
.OJBetter_setting_sidebar hr {
    margin-top: 10px;
    margin-bottom: 10px;
    border: none;
    border-top: 1px solid #DADCE0;
}
.OJBetter_setting_sidebar ul {
    list-style-type: none;
    margin: 0;
    padding: 0;
}
.OJBetter_setting_sidebar li {
    margin: 5px 0px;
    background-color: #ffffff;
    border: 1px solid #d4d8e9;
    border-radius: 4px;
    font-size: 16px;
}
.OJBetter_setting_sidebar li a {
    text-decoration: none;
    display: flex;
    width: 100%;
    font-size: 16px;
    color: gray;
    background-color: #ffffff;
    border: none;
    letter-spacing: 2px;
    padding: 7px;
    margin: 0px;
    border-radius: 4px;
    align-items: center;
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}
.OJBetter_setting_sidebar li a.active {
    background-color: #eceff1c7;
}
/* 链接样式 */
.OJBetter_setting_menu a {
    font-size: 13px;
    color: #009688 !important;
    background-color: #E0F2F1;
    border: 1px solid #009688;
    border-radius: 4px;
    padding: 0px 5px;
    margin: 0px 5px;
    text-decoration: none;
}
/* 下拉选择框 */
.OJBetter_setting_menu select {
    appearance: none;
    padding: 5px 10px;
    margin: -5px 0px;
    border-radius: 6px;
    border-style: solid;
    border: 1px solid #ced4da;
    color: #009688;
    background: #ffffff;
    font-size: 15px;
}
.OJBetter_setting_menu select:focus-visible {
    outline: none;
}
.OJBetter_setting_menu select option:disabled {
    color: #EEEEEE;
}
/* 数值输入框 */
.OJBetter_setting_menu input[type="number"] {
    width: 70px;
    color: #009688;
    font-size: 15px;
    appearance: none;
    padding: 5px 10px;
    margin: -5px 3px;
    border-radius: 6px;
    border-style: solid;
    border: 1px solid #ced4da;
}
.OJBetter_setting_menu input[type="number"]:focus-visible {
    outline: none;
}
.OJBetter_setting_menu input[type="number"]::-webkit-inner-spin-button,
.OJBetter_setting_menu input[type="number"]::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
/*设置面板-滚动条*/
.OJBetter_setting_menu::-webkit-scrollbar, .OJBetter_setting_content::-webkit-scrollbar,
.OJBetter_modal .content::-webkit-scrollbar {
    width: 5px;
    height: 7px;
    background-color: #aaa;
}
.OJBetter_setting_menu::-webkit-scrollbar-thumb, .OJBetter_setting_content::-webkit-scrollbar-thumb,
.OJBetter_modal .content::-webkit-scrollbar-thumb {
    background-clip: padding-box;
    background-color: #d7d9e4;
}
.OJBetter_setting_menu::-webkit-scrollbar-track, .OJBetter_setting_content::-webkit-scrollbar-track,
.OJBetter_modal .content::-webkit-scrollbar-track {
    background-color: #f1f1f1;
}
/*设置面板-关闭按钮*/
.OJBetter_setting_menu .tool-box {
    position: absolute;
    width: 20px;
    height: 20px;
    top: 3px;
    right: 3px;
}
.OJBetter_setting_menu .btn-close {
    width: 20px;
    height: 20px;
    border-radius: 50%;
    border: none;
    margin: 0px;
    padding: 0px;
    background-color: #ff000080;
    transition: .15s ease all;
    box-sizing: border-box;
    text-align: center;
    color: transparent;
}
.OJBetter_setting_menu .iconfont {
    font-size: 10px;
    font-weight: bolder;
}
.OJBetter_setting_menu .btn-close:hover {
    color: #ffffff;
    background-color: #ff0000cc;
    box-shadow: 0 5px 5px 0 #00000026;
}
.OJBetter_setting_menu .btn-close:active {
    color: #ffffffde;
    background-color: #ff000080;
}
/*设置面板-checkbox*/
.OJBetter_setting_menu input[type=checkbox]:focus {
    outline: 0px;
}
.OJBetter_setting_menu .OJBetter_setting_list input[type="checkbox"] {
    margin: 0px;
	appearance: none;
    -webkit-appearance: none;
	width: 40px;
	height: 20px;
	border: 1.5px solid #D7CCC8;
    padding: 0px !important;
	border-radius: 20px;
	background: #efebe978;
	position: relative;
	box-sizing: border-box;
}
.OJBetter_setting_menu .OJBetter_setting_list input[type="checkbox"]::before {
	content: "";
    width: 17px;
    height: 17px;
    background: #D7CCC8;
    border: 1.5px solid #BCAAA4;
    border-radius: 50%;
    position: absolute;
    top: 0;
    left: 0;
    transform: translate(2%, 2%);
    transition: all 0.3s ease-in-out;
    box-sizing: border-box;
}
.OJBetter_setting_menu .OJBetter_setting_list input[type="checkbox"]::after {
	content: url("data:image/svg+xml,%3Csvg xmlns='://www.w3.org/2000/svg' width='23' height='23' viewBox='0 0 23 23' fill='none'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M6.55021 5.84315L17.1568 16.4498L16.4497 17.1569L5.84311 6.55026L6.55021 5.84315Z' fill='%23EA0707' fill-opacity='0.89'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M17.1567 6.55021L6.55012 17.1568L5.84302 16.4497L16.4496 5.84311L17.1567 6.55021Z' fill='%23EA0707' fill-opacity='0.89'/%3E%3C/svg%3E");
	position: absolute;
	top: 0;
	left: 24px;
}
.OJBetter_setting_menu .OJBetter_setting_list input[type="checkbox"]:checked {
	border: 1.5px solid #C5CAE9;
	background: #E8EAF6;
}
.OJBetter_setting_menu .OJBetter_setting_list input[type="checkbox"]:checked::before {
    background: #C5CAE9;
    border: 1.5px solid #7986CB;
    transform: translate(122%, 2%);
    transition: all 0.3s ease-in-out;
}
.OJBetter_setting_menu .OJBetter_setting_list input[type="checkbox"]:checked::after {
    content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 15 13' fill='none'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M14.8185 0.114533C15.0314 0.290403 15.0614 0.605559 14.8855 0.818454L5.00187 12.5L0.113036 6.81663C-0.0618274 6.60291 -0.0303263 6.2879 0.183396 6.11304C0.397119 5.93817 0.71213 5.96967 0.886994 6.18339L5.00187 11L14.1145 0.181573C14.2904 -0.0313222 14.6056 -0.0613371 14.8185 0.114533Z' fill='%2303A9F4' fill-opacity='0.9'/%3E%3C/svg%3E");
    position: absolute;
    top: 1.5px;
    left: 4.5px;
}
.OJBetter_setting_menu .OJBetter_setting_list button {
    cursor: pointer;
    color: #7986cb;
    background-color: #e8eaf6;
    border: 1px solid #7986cb;
    border-radius: 6px;
    width: 100px;
    margin: -5px 2px;
    padding: 5px 10px;
}
.OJBetter_setting_menu .OJBetter_setting_list button:hover {
    color: #e8eaf6;
    background-color: #7986cb;
    border: 1px solid #7986cb;
}
.OJBetter_setting_menu label, #darkMode_span, #loaded_span {
    font-size: 16px;
}
.OJBetter_setting_list {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    padding: 10px;
    margin: 5px 0px;
    background-color: #ffffff;
    border: 1px solid #c9c6c642;
    border-bottom-color: #c9c6c696;
    border-radius: 8px;
    justify-content: space-between;
}
.OJBetter_setting_list.alert_danger {
    color: #F44336;
    background-color: #FFEBEE;
    border: 1px solid #F44336;
    margin: 10px 0px;
}
.OJBetter_setting_list.alert_warn {
    color: #E65100;
    background-color: #FFF3E0;
    border: 1px solid #FF9800;
    margin: 10px 0px;
}
.OJBetter_setting_list.alert_tip {
    color: #009688;
    background-color: #E0F2F1;
    border: 1px solid #009688;
    margin: 10px 0px;
}
.OJBetter_setting_list.alert_info {
    color: #ffffff;
    background-color: #009688;
    margin: 10px 0px;
    box-shadow: rgba(0, 0, 0, 0.06) 0px 2px 4px 0px inset;
}
.OJBetter_setting_list p:not(:last-child) {
    margin-bottom: 10px;
}
.OJBetter_setting_list p:not(:first-child) {
    margin-top: 10px;
}
/*设置面板-checkboxs*/
.OJBetter_setting_menu .OJBetter_checkboxs {
    flex-basis: 100%;
    display: flex;
    padding: 8px;
    margin: 10px 0px 0px 0px;
    border-bottom: 1px solid #c9c6c696;
    border-radius: 8px;
    border: 1px solid #c5cae9;
    background-color: #f0f8ff;
}
.OJBetter_setting_menu .OJBetter_checkboxs label {
    font-size: 13px;
    margin: 0px 6px 0px 3px;
}
.OJBetter_setting_menu .OJBetter_checkboxs input[type=checkbox]:checked+label{
    color: #7986cb;
}
.OJBetter_setting_menu .OJBetter_checkboxs input[type="checkbox"] {
    border: none;
    width: 16px;
    height: 16px;
}
.OJBetter_setting_menu .OJBetter_checkboxs input[type="checkbox"]::before{
    background: #ffffff;
    transform: none;
    width: 16px;
    height: 16px;
}
.OJBetter_setting_menu .OJBetter_checkboxs input[type="checkbox"]:checked {
	background: none;
    border: none;
}
.OJBetter_setting_menu .OJBetter_checkboxs input[type="checkbox"]:checked::before {
    border: 1.5px solid #95a2de;
    background: #e8eaf6;
	transform: none;
}
.OJBetter_setting_menu .OJBetter_checkboxs input[type="checkbox"]:checked::after {
    content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='9' height='9' viewBox='0 0 15 13' fill='none'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M14.8185 0.114533C15.0314 0.290403 15.0614 0.605559 14.8855 0.818454L5.00187 12.5L0.113036 6.81663C-0.0618274 6.60291 -0.0303263 6.2879 0.183396 6.11304C0.397119 5.93817 0.71213 5.96967 0.886994 6.18339L5.00187 11L14.1145 0.181573C14.2904 -0.0313222 14.6056 -0.0613371 14.8185 0.114533Z' fill='%2303A9F4' fill-opacity='0.9'/%3E%3C/svg%3E");
    top: 0px;
    left: 3.5px;
}
.OJBetter_setting_menu .OJBetter_checkboxs input[type="checkbox"]:disabled+label {
    color: #BDBDBD;
}
/*设置面板-radio*/
.OJBetter_setting_menu label {
    display: block;
    font-weight: initial;
    list-style-type: none;
    padding-inline-start: 0px;
    overflow-x: auto;
    max-width: 100%;
    margin: 3px 0px;
    overflow-x: visible;
}
.OJBetter_setting_menu_label_text {
    display: flex;
    border: 1px dashed #00aeeccc;
    height: 35px;
    width: 100%;
    color: #6e6e6e;
    font-weight: 300;
    font-size: 14px;
    letter-spacing: 2px;
    padding: 7px;
    margin-bottom: 4px;
    align-items: center;
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}
input[type="radio"]:checked+.OJBetter_setting_menu_label_text {
    background: #41e49930;
    border: 1px solid green;
    color: green;
    text-shadow: 0px 0px 0.5px green;
}
input[type="radio"]:disabled+.OJBetter_setting_menu_label_text {
    background: #fafafa00;
    border: 1px solid #e0e0e07a;
    color: #e0e0e0;
}
.OJBetter_setting_menu label input[type="radio"], .OJBetter_contextmenu label input[type="radio"]{
    appearance: none;
    list-style: none;
    padding: 0px !important;
    margin: 0px;
    clip: rect(0 0 0 0);
    -webkit-clip-path: inset(100%);
    clip-path: inset(100%);
    height: 1px;
    overflow: hidden;
    position: absolute;
    white-space: nowrap;
    width: 1px;
}
/*设置面板-文本输入框*/
.OJBetter_setting_menu input[type="text"] {
    display: block;
    height: 25px !important;
    width: 100%;
    background-color: #ffffff;
    color: #727378;
    font-size: 12px;
    border-radius: 0.3rem;
    padding: 1px 5px !important;
    box-sizing: border-box;
    margin: 5px 0px 5px 0px;
    border: 1px solid #00aeeccc;
    box-shadow: 0 0 1px #0000004d;
}
.OJBetter_setting_menu .OJBetter_setting_list input[type="text"] {
    margin-left: 5px;
}
.OJBetter_setting_menu input[type="text"]:focus-visible{
    border-style: solid;
    border-color: #3f51b5;
    outline: none;
}
.OJBetter_setting_menu_config_box {
    width: 100%;
    display: grid;
    margin-top: 5px;
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}
.OJBetter_setting_menu input::placeholder {
    color: #727378;
}
.OJBetter_setting_menu input.no_default::placeholder{
    color: #BDBDBD;
}
.OJBetter_setting_menu input.is_null::placeholder{
    color: red;
    border-width: 1.5px;
}
.OJBetter_setting_menu input.is_null{
    border-color: red;
}
.OJBetter_setting_menu textarea {
    resize: vertical;
    display: block;
    width: 100%;
    height: 60px;
    background-color: #ffffff;
    color: #727378;
    font-size: 12px;
    padding: 1px 5px !important;
    box-sizing: border-box;
    margin: 5px 0px 5px 0px;
    border: 1px solid #00aeeccc;
    box-shadow: 0 0 1px #0000004d;
}
.OJBetter_setting_menu textarea:focus-visible{
    border-style: solid;
    border-color: #3f51b5;
    outline: none;
}
.OJBetter_setting_menu textarea::placeholder{
    color: #BDBDBD;
    font-size: 14px;
}
.OJBetter_setting_menu #tempConfig_save {
    cursor: pointer;
	display: inline-flex;
    padding: 5px;
	background-color: #1aa06d;
	color: #ffffff;
	font-size: 14px;
	line-height: 1.5rem;
	font-weight: 500;
	justify-content: center;
	width: 100%;
	border-radius: 0.375rem;
	border: none;
	box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
    margin-top: 20px
}
.OJBetter_setting_menu button#debug_button.debug_button {
    width: 18%;
}
.OJBetter_setting_menu span.tip {
    color: #999;
    font-size: 12px;
    font-weight: 500;
    padding: 5px 0px;
}
/*设置面板-tip*/
.help_tip {
    margin-right: auto;
}
span.input_label {
    font-size: 14px;
}
.help_tip .tip_text {
    display: none;
    position: absolute;
    color: #697e91;
    font-weight: 400;
    font-size: 14px;
    letter-spacing: 0px;
    background-color: #ffffff;
    padding: 10px;
    margin: 5px 0px;
    border-radius: 4px;
    border: 1px solid #e4e7ed;
    box-shadow: 0px 0px 12px rgba(0, 0, 0, .12);
    z-index: 100;
}
.help_tip .tip_text p {
    margin-bottom: 5px;
}
.help_tip .tip_text:before {
    content: "";
    position: absolute;
    top: -20px;
    right: -10px;
    bottom: -10px;
    left: -10px;
    z-index: -1;
}
.help-icon {
    cursor: help;
    width: 15px;
    color: #b4b9d4;
    margin-left: 5px;
    margin-top: 3px;
}
.OJBetter_setting_menu .OJBetter_setting_menu_label_text .help_tip .help-icon {
    color: #7fbeb2;
}
.help_tip .help-icon:hover + .tip_text, .help_tip .tip_text:hover {
    display: block;
    cursor: help;
    width: 250px;
}
/* 版本信息 */
.OJBetter_setting_menu .versionInfo{
    display: grid;
    justify-items: center;
    font-size: 16px;
    padding: 10px;
}
.OJBetter_setting_menu .versionInfo>* {
    margin: 10px 0px;
}

/* 配置管理面板 */
.config{
    width: 100%;
    margin: 10px 0px;
}
.config li.tempConfig_add_button {
    cursor: pointer;
    height: 40px;
    border: 1px dashed #BDBDBD;
    border-radius: 8px;
    background-color: #fcfbfb36;
    color: #bdbdbd;
    font-size: 14px;
    align-items: center;
    justify-content: center;
}
.config li.tempConfig_add_button:hover {
    border: 1px dashed #03A9F4;
    background-color: #d7f0fb8c;
    color: #03A9F4;
}
.config .config_bar_list {
    display: flex;
    width: 100%;
    padding-bottom: 2px;
    border: 1px solid #c5cae9;
    background-color: #f0f8ff;
    box-sizing: border-box;
    border-radius: 0px 0px 8px 8px;
}
.config .config_bar_list input[type="radio"] {
    appearance: none;
    width: 0;
    height: 0;
    overflow: hidden;
}
.config .config_bar_list input[type="radio"] {
    margin: 0px;
}
.config .config_bar_list input[type=radio]:focus {
    outline: 0px;
}
.config .config_bar_ul_li_text {
    display: flex;
    align-items: center;
    justify-content: center;
    max-width: 100%;
    height: 40px;
    overflow-x: auto;
    font-size: 14px;
    font-weight: 400;
    margin: 0px 4px;
    padding: 3px;
    border: 1px solid #dedede;
    border-radius: 10px;
    box-shadow: 0px 2px 4px 0px rgba(0,0,0,.05);
    box-sizing: border-box;
}
.config .config_bar_ul li button {
    background-color: #e6e6e6;
    color: #727378;
    height: 23px;
    font-size: 14px;
    border-radius: 0.3rem;
    padding: 1px 5px;
    margin: 5px;
    border: none;
    box-shadow: 0 0 1px #0000004d;
}
.config .config_bar_ul {
    display: flex;
    align-items: center;
    list-style-type: none;
    padding-inline-start: 0px;
    overflow-x: auto;
    max-width: 100%;
    margin: 0px;
    padding: 5px;
}
.config .config_bar_ul li {
    width: 80px;
    display: grid;
    margin: 4px 4px;
    min-width: 100px;
    box-sizing: border-box;
}
.config .config_bar_ul_li_text:hover {
    background-color: #eae4dc24;
}
input[type="radio"]:checked + .config_bar_ul_li_text {
    background: #41b3e430;
    border: 1px solid #5e7ce0;
    color: #5e7ce0;
}
.config .config_bar_ul::-webkit-scrollbar {
    width: 5px;
    height: 4px;
}
.config .config_bar_ul::-webkit-scrollbar-thumb {
    background-clip: padding-box;
    background-color: #d7d9e4;
    border-radius: 8px;
}
.config .config_bar_ul::-webkit-scrollbar-button:start:decrement {
    width: 4px;
    background-color: transparent;
}
.config .config_bar_ul::-webkit-scrollbar-button:end:increment {
    width: 4px;
    background-color: transparent;
}
.config .config_bar_ul::-webkit-scrollbar-track {
    border-radius: 5px;
}
.config .config_bar_ul_li_text::-webkit-scrollbar {
    width: 5px;
    height: 7px;
    background-color: #aaa;
}
.config .config_bar_ul_li_text::-webkit-scrollbar-thumb {
    background-clip: padding-box;
    background-color: #d7d9e4;
}
.config .config_bar_ul_li_text::-webkit-scrollbar-track {
    background-color: #f1f1f1;
}
.config .config_bar_list_add_div {
    display: flex;
    height: 40px;
    margin: 4px 2px;
}

/* 修改菜单 */
#config_bar_menu {
    z-index: 400;
    position: fixed;
    width: 60px;
    background: #ffffff;
    box-shadow: 1px 1px 4px 0px #0000004d;
    border: 0px solid rgba(0,0,0,0.04);
    border-radius: 4px;
    padding: 8px 0;
}
.config_bar_menu_item {
    cursor: pointer;
    padding: 2px 6px;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 32px;
    font-size: 14px;
    font-weight: 500;
    box-shadow: inset 0px 0px 0px 0px #8bb2d9;
}
#config_bar_menu_edit:hover {
    background-color: #00aeec;
    color: white;
}
#config_bar_menu_delete:hover {
    background-color: #FF5722;
    color: white;
}

/* 配置编辑页面 */
#config_edit_menu {
    z-index: 300;
    width: 450px;
}

/* 黑暗模式选项按钮 */
.dark-mode-selection {
    display: flex;
    justify-content: center;
    align-items: center;
    max-width: 350px;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}
.dark-mode-selection label {
    margin: 8px 0px 8px 8px;
}
.dark-mode-selection > * {
    margin: 6px;
}
.dark-mode-selection .OJBetter_setting_menu_label_text {
    border-radius: 8px;
    margin-bottom: 0px;
}

/*确认弹窗*/
.OJBetter_modal {
    z-index: 600;
    display: grid;
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-size: 12px;
    font-family: var(--vp-font-family-base);
    width: max-content;
    padding: 10px 20px;
    box-shadow: 0px 0px 0px 4px #ffffff;
    border-radius: 6px;
    background-color: #f0f4f9;
    border-collapse: collapse;
    border: 1px solid #ffffff;
    color: #697e91;
}
.OJBetter_modal h2 {
    font-size: 1.6em;
    font-weight: 700;
}
.OJBetter_modal .content{
    white-space: nowrap;
    max-height: 500px;
    overflow-y: auto;
}
.OJBetter_modal .buttons{
    display: flex;
    padding-top: 15px;
}
.OJBetter_modal button {
    display: inline-flex;
    justify-content: center;
    align-items: center;
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
    text-align: center;
    box-sizing: border-box;
    outline: none;
    transition: .1s;
    user-select: none;
    vertical-align: middle;
    -webkit-appearance: none;
    height: 24px;
    padding: 5px 11px;
    margin-right: 15px;
    font-size: 12px;
    border-radius: 4px;
    color: #ffffff;
    background: #009688;
    border-color: #009688;
    border: none;
}
.OJBetter_modal button.secondary{
    background-color:#4DB6AC;
}
.OJBetter_modal button:hover{
    background-color:#4DB6AC;
}
.OJBetter_modal button.secondary:hover {
    background-color: #80CBC4;
}
.OJBetter_modal .help-icon {
    margin: 0px 8px 0px 0px;
    height: 1em;
    width: 1em;
    line-height: 1em;
    display: inline-flex;
    justify-content: center;
    align-items: center;
    position: relative;
    fill: currentColor;
    font-size: inherit;
}
.OJBetter_modal p {
    margin: 5px 0px;
}

/* 右键菜单 */
.OJBetter_contextmenu {
    z-index: 500;
    display: grid;
    position: absolute;
    background-color: #f0f4f9;
    border-collapse: collapse;
    color: #697e91;
    font-family: var(--vp-font-family-base);
    overflow: hidden;
    box-sizing: content-box;
    box-shadow: 0px 0px 0px 2px #eddbdb4d;
}
.OJBetter_contextmenu label {
    margin: 0px;
}
input[type="radio"]:checked+.OJBetter_contextmenu_label_text {
    background: #41e49930;
    border: 1px solid green;
    color: green;
    font-weight: 500;
}
.OJBetter_contextmenu_label_text {
    display: flex;
    border: 1px dashed #80cbc4;
    height: 26px;
    width: 100%;
    color: gray;
    font-size: 13px;
    font-weight: initial;
    padding: 4px;
    align-items: center;
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}
.OJBetter_contextmenu_label_text:hover {
    color: #F44336;
    border: 1px dashed #009688;
    background-color: #ffebcd;
}

/* RatingByClist */
.ratingBadge, html[data-theme=dark] button.ratingBadge{
    display: block;
    font-weight: 700;
    font-size: 11px;
    margin-top: 5px;
    padding: 2px;
    border-radius: 4px;
    color: #B0BEC5;
    border: 1px solid #cccccc66;
}

/* 多选翻译 */
.block_selected{
    box-shadow: 0px 0px 0px 1px #FF9800;
    outline: none;
}

/* 悬浮菜单 */
.OJBetter_MiniTranslateButton {
    z-index: 100;
    display: grid;
    position: absolute;
    border-collapse: collapse;
    fill: #F57C00;
    background-color: #FFF3E0;
    overflow: hidden;
    box-sizing: content-box;
    box-shadow: 0px 0px 0px 2px #FFE0B2;
    border-radius: 100%;
}
.OJBetter_MiniTranslateButton:hover {
    cursor: pointer;
    box-shadow: 0px 0px 0px 2px #FFB74D;
}

/* acmsguru划分块 */
.OJBetter_acmsguru {
    margin: 0 0 1em!important;
}

/* 代码提交表单 */
#OJBetter_SubmitForm.input-output-copier:hover {
    background-color: #ffffff00;
}
#OJBetter_SubmitForm input[type="number"] {
    width: 40px;
    color: #009688;
    appearance: none;
    border-radius: 6px;
    border-style: solid;
    border: none;
    background-color: #ffffff00;
}
#OJBetter_SubmitForm :focus-visible {
    outline: none;
}
#OJBetter_SubmitForm .topDiv {
    height: 50px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 0px;
    box-sizing: border-box;
}
#OJBetter_SubmitForm .topDiv .topRightDiv {
    height: 100%;
    display: flex;
    flex-wrap: wrap;
    gap: 0px;
}
#OJBetter_SubmitForm input[type="checkbox"], #OJBetter_SubmitForm label {
    margin: 0px;
    font-weight: initial;
}
#OJBetter_SubmitForm #fontSizeInput {
    border: none;
    background-color: #ffffff00;
}

/* 顶部区域 */
#OJBetter_SubmitForm .topRightDiv>* {
    height: 100%;
    box-sizing: border-box;
}
#OJBetter_SubmitForm .topRightDiv>button{
    padding: 0px 8px;
}
#OJBetter_SubmitForm .topRightDiv {
    display: flex;
    flex-wrap: wrap;
    gap: 0px;
    align-items: center;
}

/* LSP连接Log */
#LSPLog{
    width: 500px;
    height: 500px;
    position: fixed;
    top: 50%;
    left: 50%;
    padding: 10px;
    transform: translate(-50%, -50%);
    border: 1px solid;
    z-index: 200;
    background-color: #ffffff;
}
#LSPLog button{
    position: fixed;
    top: 10px;
    right: 10px;
    z-index: 200;
}
#LSPLog #LSPLogList{
    width: 500px;
    height: 500px;
    overflow: auto;
    color: #424242;
}
#LSPLog li:nth-child(odd){
    background-color: #f5f5f5;
}
#LSPLog details{
    padding: 2px;
}

/* 代码编辑器 */
#OJBetter_editor{
    box-sizing: border-box;
    height: 600px;
    border: 1px solid #d3d3d3;
    width: 100%;
    resize: vertical;
    display: flex;
    flex-direction: column;
}
#OJBetter_editor.fullscreen{
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100vh;
    z-index: 2000;
}
#OJBetter_editor.bottom{
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 50vh;
    z-index: 2000;
}
.ojb_btn.exit_button_bottom {
    position: fixed;
    bottom: 30px;
    right: 15px;
    z-index: 2000;
    height: 28px;
}

/* monaco */
#OJBetter_monaco {
    flex: 1;
    min-height: 0;
    width: 100%;
}
#OJBetter_monaco .highlight {
    border: 1px solid #ffffff00;
    background-color: #ffffff00!important
}
.monaco-hover hr {
    margin: 4px -8px 4px !important;
}

/* 状态底栏 */
#OJBetter_statusBar{
    height: 22px;
    font-size: 12px;
    color: #757575;
    border: 1px solid #d3d3d3;
    background-color: #f8f8f8;
    padding: 3px;
    box-sizing: border-box;
}

/* 提交 */
#OJBetter_submitDiv{
    display: flex;
    padding-top: 15px;
    height: 50px;
    box-sizing: border-box;
}
#OJBetter_submitDiv >* {
    border-radius: 6px;
}
#OJBetter_submitDiv > button {
    height: 100%;
    aspect-ratio: 1 / 1;
}
#SubmitButton {
    color: #fff;
    background-color: #209978;
    border-color: #17795E;
}
#SubmitButton:hover {
    background-color: #17795e;
}
#SubmitButton.disabled {
    background-color: red;
    animation: shake 0.07s infinite alternate;
}
#programTypeId{
    height: 100%;
    padding: 5px 10px;
    border-radius: 6px;
    border-style: solid;
    border: 1px solid #ced4da;
    color: #212529;
}

/* 调试 */
.OJBetter_loding{
    padding: 6px 0px 0px 5px;
    height: 22px;
}
#CompilerArgsInput{
    flex-grow: 1;
    width: 100%;
    height: 100%;
    margin-bottom: 10px;
    padding: 5px 10px;
    border-radius: 6px;
    box-sizing: border-box;
    border: 1px solid #ccc;
    box-shadow: inset 0px 1px 1px rgba(0,0,0,.075);
}
#CompilerArgsInput[disabled] {
    cursor: not-allowed;
}
#CompilerSetting{
    font-size: 14px;
    margin-top: 10px;
    display: none;
}
#CompilerSetting select, #CompilerSetting textarea{
    padding: 4px 10px;
    border-radius: 6px;
    border-style: solid;
    border: 1px solid #ced4da;
    color: #212529;
}
#CompilerBox{
    display: grid;
    margin-top: 10px;
    border: #d0d7de solid 1px;
    border-radius: 6px;
}
#CompilerBox > * {  
    margin: 5px;
}

/* 自定义样例 */
#customTestBlock {
    margin-top: 10px;
    font-size: 14px;
    color: #616161;
    border: 1px solid #d3d3d3;
    box-sizing: border-box;
    position: relative;
}
#customTestBlock #customTests{
    border-top: 1px solid #d3d3d3;
    margin: 0px 0px 40px 0px;
}
#customTestBlock summary {
    cursor: pointer;
    padding: 10px;
}
#customTestBlock textarea {
    resize: vertical;
}
.sampleDiv {
    color: #727378;
    background-color: #FAFAFA;
    padding: 5px;
    margin-bottom: 10px;
    box-shadow: inset 0 0 1px #0000004d;
    position: relative;
}
.dynamicTextarea {
    width: 98%;
    height: 120px;
    margin: 10px 5px;
    border: 1px solid #E0E0E0;
}
.deleteCustomTest {
    cursor: pointer;
    position: absolute;
    top: 5px;
    right: 5px;
    display: flex;
    fill: #9E9E9E;
    padding: 2px 2px;
    border-radius: 4px;
    border: 1px solid #ffffff00;
    background-color: #ffffff00;
    align-items: center;
}
.deleteCustomTest:hover {
    fill: #EF5350;
    border: 1px solid #ef9a9a;
    background-color: #FFEBEE;
}
#addCustomTest {
    cursor: pointer;
    position: absolute;
    bottom: 5px;
    right: 5px;
    padding: 3px 10px;
    color: #795548;
    border: 1px solid #ccc;
    border-radius: 4px;
    background-color: #FAFAFA;
}
#addCustomTest:hover {
    background-color: #f5f5f5;
}

/* 调试结果 */
#statePanel{
    display: none;
    padding: 5px;
    margin-top: 10px;
    border: 1px solid #ddd;
    border-radius: 4px;
}
.test-case {
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 4px;
}
.test-case:not(:first-child){
    margin-top: 5px;
}
.test-case > * {
    margin: 5px 0px;
}
.test-case > :first-child {
    margin-top: 0px;
}
.test-case > :last-child {
    margin-bottom: 0px;
}
.test-case-title, .test-case-status {
    font-size: 16px;
    display: inline;
}
.test-case-status{
    margin-left: 5px;
}
.test-case-status.error{
    color: red;
}
.test-case-status.success{
    color: #449d44;
}
.test-case-judge {
    font-size: 13px;
}

/* 差异对比 */
.output_diff {
    color: #5d4037;
    margin: 5px 0px;
    display: grid;
    border: 1px solid #bcaaa4;
    font-size: 13px;
    font-family: Consolas, "Lucida Console", "Andale Mono", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace;
    overflow: auto;
}
.output_diff .added {
    background-color: #c8f7c5;
    user-select: none;
}
.output_diff .removed {
    background-color: #f7c5c5; 
}
.output_diff .diffLine {
    display: flex;
}
.output_diff .diffLine:nth-child(odd) {
    background-color: #f5f5f5;
}
.lineNo {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 17px;
    color: #BDBDBD;
    font-size: 10px;
    border-right: 1px solid;
    user-select: none;
}
.lineContent {
    display: grid;
    width: 100%;
}
.lineContent>span {
    height: 16px;
    padding-left: 3px;
}
.output_no_diff {
    padding: 5px;
    border: 1px solid #ddd;
}
.diff_note {
    font-size: 10px;
}

/* 网站本地化替换规则标记 */
.markingTextReplaceRule{
    color: #FFF3E0;
    background-color: #FF9800;
}

/* SelectPage样式 */
.sp_input {
    padding: 4px 6px px !important;
    height: 20px !important;
    min-height: 20px !important;
    line-height: 20px !important;
}
div.sp_clear_btn {
    padding: 0px !important;
}

/* 移动设备 */
@media (max-device-width: 450px) {
    .ojb_btn{
        height: 2em;
        font-size: 1.2em;
    }
    .ojb_btn.OJBetter_setting{
        height: 2.5em;
        font-size: 1em;
    }
    .OJBetter_setting_menu{
        width: 90%;
    }
    .OJBetter_setting_menu label, #darkMode_span, #loaded_span, .OJBetter_setting_menu_label_text,
    .OJBetter_setting_sidebar li{
        font-size: 1em;
    }
    .translate-problem-statement{
        font-size: 1.2em;
    }
    .OJBetter_modal{
        font-size: 1.5em;
    }
    .OJBetter_setting_list, .translate-problem-statement{
        padding: 0.5em;
    }
    .OJBetter_setting_menu_label_text{
        height: 2.5em;
        padding: 0.5em;
    }
    #pagBar #jump-input, #pagBar #items-per-page, .OJBetter_modal button{
        height: 2.5em;
        font-size: 1em;
    }
    .translate-problem-statement p, .translate-problem-statement ul li{
        line-height: 1.5em !important;
    }
    .OJBetter_contextmenu_label_text{
        height: 3em;
        font-size: 1em;
    }
}

/* 覆盖网站原本的样式 */
div#select-lang {
    padding: 0px;
}
`);

/**
 * 添加一些依赖库和条件加载的css样式
 */
function addDependencyStyles() {
    GM_addStyle(GM_getResourceText("xtermcss"));
    GM_addStyle(GM_getResourceText("selectpagecss"));
    GM_addStyle(GM_getResourceText("dialogpolyfillcss"));
    // 自定义图标大小
    GM_addStyle(`
        .iconfont {
            font-size: ${OJBetter.preference.iconButtonSize}px;
        }
    `);
}

/**
 * 添加包含i18n内容的css样式
 */
function addI18nStyles() {
    GM_addStyle(`
    /* 加载鼠标悬浮覆盖层css */
    .overlay::before {
        content: '';
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: repeating-linear-gradient(135deg, rgb(77 208 225 / 30%), rgb(77 208 225 / 30%) 30px, rgb(77 208 225 / 10%) 0px, rgb(77 208 225 / 10%) 55px);
        z-index: 100;
    }
    .overlay::after {
        content: '${i18next.t('targetArea', { ns: 'common' })}';
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        color: #00695C;
        font-size: 16px;
        font-weight: bold;
        z-index: 100;
    }

    .config::before {
        content: "${i18next.t('common.configManageTitle', { ns: 'settings' })}";
        display: block;
        height: 20px;
        background-color: #f0f8ff;
        border: 1px solid #c5cae9;
        border-bottom: 0px;
        line-height: 20px;
        padding: 2px 10px;
        border-radius: 8px 8px 0px 0px;
        box-sizing: content-box;
    }
    .config.missing::before {
        content: "${i18next.t('common.missing.radio', { ns: 'settings' })}";
        background-color: #fef0f0;
        color: #f56c6c;
        border: 1px solid #fab6b6;
    }
    `);
}

// ------------------------------
// 一些工具类
// ------------------------------


/**
 * 自定义错误类,以区分不同的错误类型
 */
class OJB_GMError extends Error {
    constructor(type, message, originalError) {
        super(message);
        this.name = 'GMError';
        this.type = type;
        this.stack = originalError.stack;
        Object.assign(this, originalError);
    }
}

/**
 * 文本块替换/恢复类
 */
class TextBlockReplacer {
    constructor() {
        /** @type {string[]} 匹配项 */
        this.matches = [];
        /** @type {Map<string, string>} 待还原项 */
        this.replacements = new Map();
        /** @type {Map<string, string>} 暂时未找到的待还原项 */
        this.tempReplacements = new Map();
        /** @type {string} 替换符号 */
        this.replaceSymbol = OJBetter.translation.replaceSymbol;
    }

    /**
     * 替换文本
     * @param {string} text 原文本
     * @param {RegExp} regex 匹配规则
     * @returns {string} 替换后的文本
     */
    replace(text, regex) {
        this.matches = text.match(regex) || [];
        try {
            for (let i = 0; i < this.matches.length; i++) {
                const match = this.matches[i];
                const id = OJB_getRandomNumber(8);
                let replacement = '';
                switch (this.replaceSymbol) {
                    case "1":
                        replacement = `【${id}】`;
                        break;
                    case "2":
                        replacement = `{${id}}`;
                        break;
                    case "3":
                        replacement = `[${id}]`;
                        break;
                    default:
                        replacement = `【${id}】`;
                        break;
                }
                text = text.replace(match, replacement);
                this.replacements.set(id, match);
            }
        } catch (e) { }
        return text;
    }


    /**
     * 恢复替换的文本
     * @param {string} text 还原前的文本
     * @returns {string} 还原后的文本
     */
    recover(text) {
        let textCopy = text;

        /**
         * 替换回文本
         * @param {string} replacement 替换的文本
         * @param {string} regexPattern 匹配规则
         * @returns {void}
         */
        const replaceText = (replacement, regexPattern) => {
            const latexMatch = '(?<latex_block>\\$\\$(\\\\\\$|[^\\$])*?\\$\\$)|(?<latex_inline>\\$(\\\\\\$|[^\\$])*?\\$)|';
            const regex = new RegExp(latexMatch + regexPattern, 'g');
            textCopy = textCopy.replace(regex, (match, ...args) => {
                // LaTeX中的不替换
                const groups = args[args.length - 1]; // groups是replace方法的最后一个参数
                if (groups.latex_block || groups.latex_inline) return match;
                // 没有空格则加一个
                const offset = args[args.length - 3]; // offset是replace方法的倒数第三个参数
                let leftSpace = "", rightSpace = "";
                if (!/\s/.test(textCopy[offset - 1])) leftSpace = " ";
                if (!/\s/.test(textCopy[offset + match.length])) rightSpace = " ";
                return leftSpace + replacement + rightSpace;
            });
        };

        /**
         * 尝试还原
         * @param {string} replacement 替换的文本
         * @param {string} id 替换的 id
         * @returns {boolean} 是否替换成功
         */
        const tryRecover = (replacement, id) => {
            // 尝试还原,如果还原成功,则从 replacements 中删除
            const originalText = textCopy;
            replaceText(replacement, `【\\s*${id}\\s*】|\\[\\s*${id}\\s*\\]|{\\s*${id}\\s*}`); // 替换符完整匹配(考虑了多出空格的情况)
            replaceText(replacement, `【\\s*${id}(?![】\\d])|(?<![【\\d])${id}\\s*】|\\[\\s*${id}(?![\\]\\d])|(?<![\\[\\d])${id}\\s*\\]|{\\s*${id}(?![}\\d])|(?<![{\\d])${id}\\s*}`); // 替换符部分匹配

            if (textCopy === originalText) {
                // 如果文本没有变化,说明没有找到,加入到 tempReplacements
                this.tempReplacements.set(id, replacement);
                return false;
            } else {
                // 如果文本变化了,说明找到并成功替换,则删除
                this.replacements.delete(id);
                this.tempReplacements.delete(id);
                return true;
            }
        }

        // 处理 replacements 中的项
        this.replacements.forEach((replacement, id) => {
            tryRecover(replacement, id);
        });

        // 处理 tempReplacements 中的项
        while (this.tempReplacements.size > 0) {
            let found = false;
            this.tempReplacements.forEach((replacement, id) => {
                found = tryRecover(replacement, id) || found;
            });
            if (!found) break; // 如果这一轮没有找到任何项,终止循环
        }

        // 如果 tempReplacements 还有未找到的项
        if (this.tempReplacements.size > 0) {
            console.warn("There are still some replacements not found:", this.tempReplacements);
        }

        return textCopy;
    }
}

// ------------------------------
// 一些工具函数
// ------------------------------

/**
 * 格式化链接格式
 * @param {string} url 链接字符串
 * @returns {string} 清理后的链接字符串
 */
function OJB_cleanLink(url) {
    if (url === null || url === undefined) return "";

    // 替换'http://'为'https://'
    let cleanUrl = url.replace(/^http:\/\//i, 'https://');

    // 移除末尾的斜杠
    cleanUrl = cleanUrl.replace(/\/$/, '');

    return cleanUrl;
}

/**
 * 深度比较两个对象或数组是否完全相等。
 * @param {any} a - 第一个比较对象。
 * @param {any} b - 第二个比较对象。
 * @returns {boolean} - 如果两个对象或数组深度相等,则返回true,否则返回false。
 */
function OJB_deepEquals(a, b) {
    if (a === b) return true;
    if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) return false;
    const keysA = Object.keys(a);
    const keysB = Object.keys(b);
    if (keysA.length !== keysB.length) return false;
    for (let key of keysA) {
        if (!b.hasOwnProperty(key)) return false;
        if (!OJB_deepEquals(a[key], b[key])) return false;
    }
    return true;
}

/**
 * 用于封装需要重试的异步函数
 * @param {Function} task 需要封装的异步函数
 * @param {Object} options 配置项
 * @param {Number} options.maxRetries 重试次数,默认为 5
 * @param {Number} options.retryInterval 重试时间间隔,默认为 0 毫秒
 * @param {Function} options.errorHandler 错误处理函数,默认为抛出错误
 * @param {...any} args task 函数的参数
 * @returns {Promise} 返回 Promise
 */
async function OJB_promiseRetryWrapper(task, {
    maxRetries = 5,
    retryInterval = 0,
    errorHandler = (err) => { throw err }
} = {}, ...args) {
    let attemptsLeft = maxRetries;
    while (attemptsLeft--) {
        try {
            return await task(...args);
        } catch (err) {
            if (attemptsLeft <= 0) {
                return errorHandler(err, maxRetries, attemptsLeft);
            }
            if (retryInterval > 0) {
                await OJB_delay(retryInterval);
            }
        }
    }
}

/**
 * GM_xmlhttpRequest 的 Promise 封装
 * @param {Object} options GM_xmlhttpRequest 的参数
 * @param {Boolean} isStream 是否为流式请求
 * @returns {Promise<OJB_GMError>} 返回 Promise
 */
function OJB_GMRequest(options, isStream = false) {
    return new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
            ...options,
            ...(isStream ? {
                onloadstart: resolve
            } : {
                onload: resolve
            }),
            onerror: (error) => reject(new OJB_GMError('error', 'An error occurred during the request.', error)),
            ontimeout: (error) => reject(new OJB_GMError('timeout', 'The request timed out.', error)),
            onabort: (error) => reject(new OJB_GMError('abort', 'The request was aborted.', error)),
        });
    });
}

/**
 * 获取cookie
 * @param {string} name cookie名称
 * @returns {string} cookie值
 */
function OJB_getCookie(name) {
    const cookies = document.cookie.split(";");
    for (let i = 0; i < cookies.length; i++) {
        const cookie = cookies[i].trim();
        const [cookieName, cookieValue] = cookie.split("=");

        if (cookieName === name) {
            return decodeURIComponent(cookieValue);
        }
    }
    return "";
}

/**
 * 检查是否仍在同一浏览器会话中
 * @param {string} sessionKey - 会话键名,用于标识会话
 * @returns {boolean} - 如果在当前会话中之前已经设置过这个键,则返回true,否则返回false
 */
function OJB_isSameBrowserSession(sessionKey) {
    const fullCookieName = `OJB_Session_${sessionKey}`;
    const sessionValue = OJB_getCookie(fullCookieName);
    if (sessionValue === "") {
        document.cookie = `${fullCookieName}=true; path=/`;
        return false;
    }
    return true;
}

/**
 * 随机数生成
 * @param {number} numDigits 位数
 * @returns {number} 一个随机数
 */
function OJB_getRandomNumber(numDigits) {
    let min = Math.pow(10, numDigits - 1);
    let max = Math.pow(10, numDigits) - 1;
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

/**
 * 随机数生成-范围内
 * @param {number} min 最小值
 * @param {number} max 最大值
 * @returns {number} 一个随机数
 */
function OJB_getRandomNumberInRange(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

/**
 * 防抖函数
 * @param {Function} callback 回调函数
 * @returns {Function}
 */
function OJB_debounce(callback) {
    let timer;
    let immediateExecuted = false;
    const delay = 500;
    return function () {
        clearTimeout(timer);
        if (!immediateExecuted) { callback.call(this); immediateExecuted = true; }
        timer = setTimeout(() => { immediateExecuted = false; }, delay);
    };
}

/**
 * 为元素添加鼠标拖拽支持
 * @param {JQuery<HTMLElement>} element 要添加拖拽支持的元素
 * @returns {void}
 */
function OJB_addDraggable(element) {
    let isDragging = false;
    let x, y, l, t, nl, nt;
    let isSpecialMouseDown = false; // 选取某些元素时不拖动

    element.on('mousedown', function (e) {
        isSpecialMouseDown = $(e.target).is('label, p, input, textarea, span, select, details, summary');
        if (isSpecialMouseDown) return;

        isDragging = true;
        x = e.clientX;
        y = e.clientY;
        l = element.offset().left - $(window).scrollLeft();
        t = element.offset().top - $(window).scrollTop();

        element.css({ left: l + 'px', top: t + 'px', transform: 'none' });

        $(document).on("mousemove", drag);
        $(document).on("mouseup", stopDrag);
        element.css('cursor', 'all-scroll');
    });

    const drag = (e) => {
        if (!isDragging) return;
        // 不执行拖动操作
        if ($(e.target).is('label, p, input, textarea, span') || isSpecialMouseDown && !$(e.target).is('input, textarea')) return;
        e.preventDefault();

        const nx = e.clientX;
        const ny = e.clientY;
        nl = nx - (x - l);
        nt = ny - (y - t);
        element.css({ transform: `translate(${nx - x}px, ${ny - y}px)` });
    };

    const stopDrag = () => {
        isDragging = false;
        isSpecialMouseDown = false;
        element.css('cursor', 'default');

        // 在停止拖拽后,设置元素的left和top,并还原transform
        element.css({ left: nl + 'px', top: nt + 'px', transform: 'none' });
        $(document).off("mousemove", drag);
        $(document).off("mouseup", stopDrag);
    };
}

/**
 * 切换元素的折叠/展开过渡动画
 * @param {HTMLElement} element
 */
function OJB_toggleCollapseExpand(element) {
    // 设置transitionend事件监听器的函数
    const setTransitionListener = (listener) => {
        const listenerName = `transitionEndListener${Date.now()}`;
        window[listenerName] = listener;
        element.addEventListener('transitionend', listener);
        element.setAttribute('data-transition-end-listener', listenerName);
    };

    // 移除事件监听器的函数
    const removeTransitionListener = () => {
        const transitionEndListenerName = element.getAttribute('data-transition-end-listener');
        if (transitionEndListenerName) {
            element.removeEventListener('transitionend', window[transitionEndListenerName]);
            element.removeAttribute('data-transition-end-listener');
        }
    };

    const collapsed = element.getAttribute('data-collapsed') === 'true';
    const sectionHeight = element.scrollHeight;

    // 移除事件监听器
    removeTransitionListener();

    // 设置初始样式
    element.style.overflow = 'hidden';
    element.style.transition = 'height 0.3s ease-out 0s';
    element.style.height = collapsed ? `0px` : `${sectionHeight}px`;
    element.style.opacity = collapsed ? '' : '1';

    // 需要立即开始动画
    requestAnimationFrame(() => {
        // 设置结束样式
        element.style.height = collapsed ? `${sectionHeight}px` : `0px`;
    });

    const transitionEndListener = (event) => {
        if (event.propertyName === 'height') {
            if (collapsed) {
                // 展开后的设置
                element.style.height = '';
                element.style.overflow = '';
            } else {
                // 折叠后的设置
                element.style.opacity = '0';
            }
            removeTransitionListener();
        }
    };

    setTransitionListener(transitionEndListener);

    // 更新data-collapsed属性
    element.setAttribute('data-collapsed', collapsed ? 'false' : 'true');
}

/**
 * 获取外部JSON并转换为Object
 * @param {string} url JSON Url
 * @param {boolean} [nacache=true] 是否不使用缓存
 * @returns {Promise<Object>} JSON Object
 */
async function OJB_getExternalJSON(url, nacache = true) {
    const response = await OJB_GMRequest({
        method: "GET",
        url: url,
        nocache: nacache
    });
    try {
        return JSON.parse(response.responseText);
    } catch (e) {
        throw new Error(`JSON parse error\n${e}`);
    }
}

/**
 * 创建确认对话框dialog
 * @param {string} title 标题
 * @param {string} content 内容
 * @param {string[]} buttons 按钮 (取消 确定) 可以为null
 * @param {boolean} renderMarkdown 是否使用markdown渲染文本
 * @returns {Promise<boolean>} 用户点击了确定按钮返回true, 否则返回false
 */
function OJB_createDialog(title, content, buttons, renderMarkdown = false) {
    return new Promise(resolve => {
        let contentHtml = content;

        if (renderMarkdown) {
            const md = window.markdownit();
            contentHtml = md.render(content);
        }

        const dialog = OJB_safeCreateJQElement(`
        <dialog class="OJBetter_modal">
            <h2>${title}</h2>
            <div class="content">${contentHtml}</div>
        </dialog>
        `);
        const buttonbox = OJB_safeCreateJQElement(`<div class="buttons"></div>`);
        const cancelButton = OJB_safeCreateJQElement(`<button class="cancelButton">${buttons[0]}</button>`)
            .addClass("secondary");
        const continueButton = OJB_safeCreateJQElement(`<button class="continueButton">${buttons[1]}</button>`);
        if (buttons[0] !== null) buttonbox.append(cancelButton);
        if (buttons[1] !== null) buttonbox.append(continueButton);
        dialog.append(buttonbox);
        $('body').append(dialog);

        OJB_showModal(dialog);
        OJB_addDraggable(dialog);

        continueButton.click(function () {
            OJB_closeAndRemoveModal(dialog);
            resolve(true);
        });

        cancelButton.click(function () {
            OJB_closeAndRemoveModal(dialog);
            resolve(false);
        });
    });
}

/**
 * 显示模态对话框并阻止页面滚动,同时考虑滚动条宽度变化和原始marginRight
 * @param {JQuery<HTMLElement>} element
 */
function OJB_showModal(element) {
    const dialog = element.get(0);
    dialogPolyfill.registerDialog(dialog);
    dialog.showModal();
    OJBetter.state.openDialogCount++;

    if (OJBetter.state.openDialogCount === 1) {
        const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
        // 获取原始的html marginRight,考虑到可能的非数字值,比如auto
        const originalMarginRight = window.getComputedStyle(document.documentElement).marginRight;
        const marginRightValue = parseFloat(originalMarginRight) || 0; // 将非数字值转换为0

        if (scrollbarWidth > 0) {
            // 保存原始的marginRight,并设置新的值以补偿滚动条宽度
            document.documentElement.style.setProperty('--original-margin-right', originalMarginRight);
            document.documentElement.style.marginRight = `${marginRightValue + scrollbarWidth}px`;
        }

        // 保存原始的overflow样式
        document.documentElement.setAttribute('data-original-overflow', document.documentElement.style.overflow);
        document.documentElement.style.overflow = 'hidden';
    }

    const allowScrollIfNeeded = () => {
        OJBetter.state.openDialogCount--;
        if (OJBetter.state.openDialogCount === 0) {
            // 恢复原始的html marginRight和overflow样式
            const originalMarginRight = document.documentElement.style.getPropertyValue('--original-margin-right');
            document.documentElement.style.marginRight = originalMarginRight;
            document.documentElement.style.removeProperty('--original-margin-right');

            const originalOverflow = document.documentElement.getAttribute('data-original-overflow');
            document.documentElement.style.overflow = originalOverflow;
            document.documentElement.removeAttribute('data-original-overflow');
        }
    };

    dialog.addEventListener('close', allowScrollIfNeeded);
}

/**
 * 关闭并移除模态对话框
 * @param {JQuery<HTMLElement>} element
 */
function OJB_closeAndRemoveModal(element) {
    const dialog = element.get(0);
    dialog.close();
    dialog.remove();
}

/**
 * 关闭并移除模态对话框
 * @param {JQuery<HTMLElement>} element
 */
function OJB_closeModal(element) {
    const dialog = element.get(0);
    dialog.close();
}

/**
 * 清除i18next的缓存数据并刷新
 */
function clearI18nextCache() {
    Object.keys(localStorage)
        .filter(key => key.startsWith('i18next_res_'))
        .forEach(key => localStorage.removeItem(key));
    window.location.reload();
}

/**
 * 清除网站本地化数据
 */
async function clearWebsiteL10nData() {
    OJBetter.common.database.localizeSubsData.clear().then(() => {
        console.log('localizeSubsData table has been cleared');
        window.location.reload();
    }).catch((error) => {
        console.error('Failed to clear localizeSubsData table:', error);
    });
}

/**
 * 从Pre代码块中获取原始代码
 * @param {HTMLElement} element pre代码块元素
 * @returns {string|null} 代码文本
 */
function OJB_getCodeFromPre(element) {
    /**
     * 从Ace格式化的代码块中获取原始代码
     * @param {HTMLElement} element pre代码块元素
     * @returns {string} 代码文本
     */
    const getCodeFromAcePre = function (element) {
        const editor = ace.edit(element);
        return editor.getValue();
    }

    /**
     * 从Pretty格式化的代码块中获取原始代码-1
     * 代码直接存放在 pre 元素中
     * @param {HTMLElement} element pre代码块元素
     * @returns {string} 代码文本
     */
    const getCodeFromPrettyPre = function (element) {
        return Array.from(element.querySelectorAll('li')).map(function (li) {
            return li.textContent;
        }).join('\n');
    }

    /**
     * 从Pretty格式化的代码块中获取原始代码-2
     * 代码存放在子元素 code 中
     * @param {HTMLElement} element pre代码块元素
     * @returns {string} 代码文本
     */
    const getCodeFromPreChild = function (element) {
        const code = element.querySelector("code.prettyprint");
        if (code.classList.contains("linenums")) {
            return getCodeFromPrettyPre(element);
        } else {
            return element.querySelector("code.prettyprint").textContent;
        }
    }

    let result;
    if (element.id === "submission-code") {
        result = getCodeFromAcePre(element);
    } else if (element.classList.contains("prettyprint")) {
        result = getCodeFromPrettyPre(element);
    } else if (element.querySelector("code.prettyprint")) {
        result = getCodeFromPreChild(element);
    } else {
        result = "";
    }
    result = result.replace(/\u00A0/g, ''); // 过滤文本中的U+00a0字符(由&nbsp;造成的)
    return result;
}

/**
 * 判断代码的语言
 * @param {string} code 代码文本
 * @returns {string} 可能的语言
 */
function OJB_codeLangDetect(code) {
    result = hljs.highlightAuto(code);
    return result.language;
}

/**
 * 获取指定命名空间下的所有i18n翻译键值对。
 * 
 * @param {string} namespace - 要获取键值对的i18next命名空间。
 * @returns {Map<string, string>} 一个包含命名空间下所有键值对的Map对象。
 */
function OJB_getAllI18nKeysForNamespace(namespace) {
    const language = i18next.language; // 获取当前语言
    const resources = i18next.store.data[language]; // 获取当前语言的所有资源
    const nsResources = resources[namespace]; // 获取特定命名空间的资源
    const resultMap = new Map();

    if (nsResources) {
        // 遍历命名空间下的所有键值对,并添加到Map中
        Object.keys(nsResources).forEach(key => {
            resultMap.set(key, nsResources[key]);
        });
    } else {
        console.log(`No resources found for namespace "${namespace}"`);
    }

    return resultMap;
}

/**
 * 更新检查
 */
async function checkScriptVersion() {
    try {
        const versionResponse = await OJB_GMRequest({
            method: "GET",
            url: "https://aowuucdn.oss-accelerate.aliyuncs.com/script/versions.json",
            timeout: 10 * 1e3,
            nocache: true
        });
        const versionData = JSON.parse(versionResponse.responseText);
        const { [OJBetter.state.formatName]: { dev: version_dev, release: version_release } } = versionData;
        const baseUrls = {
            // greasyfork: 'https://update.greasyfork.org/scripts/465777/Codeforces%20Better%21.user.js',
            greasyfork: 'https://update.greasyfork.org/scripts/471106/Atcoder%20Better%21.user.js',
            github: `https://github.com/beijixiaohu/OJBetter/raw/main/script/${OJBetter.about.updateChannel}/${OJBetter.state.formatName}.user.js`,
            aliyunoss: `https://aowuucdn.oss-accelerate.aliyuncs.com/script/${OJBetter.about.updateChannel}/${OJBetter.state.formatName}.user.js`
        };
        /** @type {string} 更新跳转url */
        const updateUrl = baseUrls[OJBetter.about.updateSource];
        /** @type {string} 是否暂时跳过cookie */
        const skipUpdate = OJB_getCookie("skipUpdate");
        /** @type {string} 当前更新频道的最新版本 */
        const version = OJBetter.about.updateChannel == "release" ? version_release : version_dev;
        if (OJB_compareVersions(version, OJBetter.state.version) === 1 && skipUpdate !== "true") {
            const updateConfirmed = await OJB_createDialog(
                i18next.t('update.title', { ns: 'dialog', scriptName: OJBetter.state.name }),
                i18next.t('update.content', { ns: 'dialog', oldVersion: OJBetter.state.version, newVersion: version }),
                [
                    i18next.t('update.buttons.0', { ns: 'dialog' }),
                    i18next.t('update.buttons.1', { ns: 'dialog' })
                ],
                true
            );

            if (updateConfirmed) {
                window.location.href = updateUrl;
            } else {
                document.cookie = "skipUpdate=true; path=/";
            }
        }
    } catch (error) {
        console.error("Update check failed: ", error);
    }
}

/**
 * 公告
 */
async function showAnnounce() {
    /** @type {string} 最新公告版本*/
    const lastAnnounceVer = i18next.t('lastVersion', { ns: 'announce' });
    if (OJB_compareVersions(OJBetter.state.version, OJBetter.state.lastAnnounceVer) === 1) {
        const title = `🎉${i18next.t('announce.title', { ns: 'dialog' })} ${OJBetter.state.version}`;
        /** @type {Boolean} 是否是新的公告 */
        const isNewAnnounceVer = OJB_compareVersions(lastAnnounceVer, OJBetter.state.lastReadAnnounceVer) === 1;
        /** @type {Boolean} 是否展示新的公告(高于当前版本的测试公告不展示) */
        const showNewAnnounceVer = OJB_compareVersions(lastAnnounceVer, OJBetter.state.version) !== 1;
        /**
         * 获取公告的内容
         * @returns {string} 公告内容
         */
        const getAnnounceContent = function () {
            // 获取公告
            const announceMap = OJB_getAllI18nKeysForNamespace('announce');
            // 移除 'lastVersion' 键
            announceMap.delete('lastVersion');
            // 将 Map 转换为数组并根据版本号排序
            const sortedVersions = [...announceMap.keys()].sort(OJB_compareVersions).reverse();
            let content = "";
            sortedVersions.forEach(version => {
                content += `### ${version}\n\n`; // 使用版本号作为标题
                content += announceMap.get(version); // 添加对应版本的公告内容
                content += "\n\n";
            });

            return content;
        };

        const content = (() => {
            if (isNewAnnounceVer && showNewAnnounceVer) {
                return `${i18next.t('announce.prefix', { ns: 'dialog' })}\n\n${getAnnounceContent()}`;
            } else {
                return i18next.t('announce.divContent', { ns: 'dialog' });
            }
        })();
        const ok = await OJB_createDialog(
            title,
            content,
            [
                null,
                i18next.t('announce.buttons.0', { ns: 'dialog' })
            ],
            true
        ); //跳过折叠块确认
        if (ok) {
            if (isNewAnnounceVer && showNewAnnounceVer) {
                GM_setValue('lastReadAnnounceVer', lastAnnounceVer);
            }
            GM_setValue('lastAnnounceVer', OJBetter.state.version);
        }
    }
};

/**
 * 页面顶部提示信息alert类
 */
class LoadingMessage {
    constructor() {
        this._statusElement = null;
        this._isDisplayed = false;
        this.init();
    }

    /**
     * 初始化加载提示信息
     */
    init() {
        this._statusElement = this.createStatusElement();
        this.insertStatusElement();
    }

    /**
     * 创建提示信息元素
     */
    createStatusElement() {
        const statusElement = $("<div></div>").addClass("alert OJBetter_alert")
            .css({
                "margin": "1em",
                "text-align": "center",
                "position": "relative"
            }).hide();
        return statusElement;
    }

    /**
     * 插入提示信息
     * @returns {void}
     */
    insertStatusElement() {
        // (OJBetter.typeOfPage.is_mSite ? $("header") : $(".menu-box:first").next()).after(this._statusElement);
        $("#main-container").prepend(this._statusElement);
    }

    /**
     * 显示提示信息
     */
    showStatus() {
        this._statusElement.show();
        this._isDisplayed = true;
    }

    /**
     * 隐藏提示信息
     */
    hideStatus() {
        this._statusElement.fadeOut(500);
        this._isDisplayed = false;
    }

    /**
     * 移除提示信息
     */
    removeStatus() {
        this._statusElement.remove();
        this._isDisplayed = false;
    }

    /**
     * 更新提示信息
     * @param {string} text 提示信息文本
     * @param {string} type 提示信息类型,可选值:info, success, warning, error
     * @param {number} timeout 提示信息显示的持续时间(毫秒), 默认为无限长
     */
    updateStatus(text, type = 'info', timeout = Infinity, isMarkdown = false) {
        if (isMarkdown) {
            let md = window.markdownit({
                html: !is_escapeHTML,
            });
            text = md.render(text);
        }
        this._statusElement.html(text).removeClass("alert-info alert-success alert-warning alert-error").addClass(`alert-${type}`);
        if (!this._isDisplayed) {
            this.showStatus();
        }
        if (timeout !== Infinity) {
            setTimeout(() => {
                this.hideStatus();
            }, timeout);
        }
    }
}

/**
 * 获取网站本地化的数据
 * @param {*} localizationLanguage 本地化语言
 * @returns {Promise<Object>} 本地化数据
 */
async function getLocalizeWebsiteJson(localizationLanguage) {
    let data = await OJBetter.common.database.localizeSubsData.get(localizationLanguage);
    let url = localizationLanguage === "zh" ?
        `https://aowuucdn.oss-accelerate.aliyuncs.com/resources/subs/${OJBetter.state.formatName}.json` :
        `https://aowuucdn.oss-accelerate.aliyuncs.com/i18n/${localizationLanguage}/resources/subs/${OJBetter.state.formatName}.json`;
    if (data) data = data.data;
    if (!data) {
        // 如果本地没有数据,从远端获取并保存
        data = await OJB_getExternalJSON(url);
        await OJBetter.common.database.localizeSubsData.put({ lang: localizationLanguage, data: data });
    } else {
        // 如果本地有数据,检查是否已经在当前会话中尝试过更新
        const sessionKey = `ojb_updateL10nWebsiteJson_${localizationLanguage}`;
        if (!OJB_isSameBrowserSession(sessionKey)) {
            // 如果尚未更新,则在后台更新
            (async () => {
                try {
                    const newData = await OJB_getExternalJSON(url);
                    await OJBetter.common.database.localizeSubsData.put({ lang: localizationLanguage, data: newData });
                    console.log("Website local data has been refreshed!");
                } catch (error) {
                    console.error('Failed to update localization data:', error);
                }
            })();
        }
    }
    return data;
}

/**
 * 网站本地化替换
 * @returns 
 */
async function localizeWebsite() {
    if (OJBetter.localization.websiteLang === "initial") return;

    // 设置网页语言
    var htmlTag = document.getElementsByTagName("html")[0];
    htmlTag.setAttribute("lang", OJBetter.localization.websiteLang);

    // 获取网站本地化的数据
    var subs = await getLocalizeWebsiteJson(OJBetter.localization.websiteLang);

    /**
     * 文本节点遍历替换
     * @param {JQuery} $nodes jQuery对象
     * @param {Object} textReplaceRules 文本替换规则对象
     * @param {string} key 应用的规则集的名字
     */
    const traverseTextNodes = ($nodes, textReplaceRules, key) => {
        if (!$nodes) return;

        $nodes.each((_, node) => {
            if (node.nodeType === Node.TEXT_NODE) {
                Object.entries(textReplaceRules).forEach(([match, replace]) => {
                    try {
                        const regex = new RegExp(match, 'g');
                        const beforeText = node.textContent;
                        node.textContent = node.textContent.replace(regex, replace);
                        if (node.textContent !== beforeText && OJBetter.dev.isRuleMarkingEnabled) {
                            $(node).after(`<span class="markingTextReplaceRule">${key}</span>`);
                        }
                    } catch (error) {
                        console.error(`Error processing text replacement for match: ${match}`, error);
                    }
                });
            } else if (node.nodeType === Node.ELEMENT_NODE && node.tagName.toLowerCase() !== 'iframe') {
                $(node).contents().each((_, childNode) => {
                    traverseTextNodes($(childNode), textReplaceRules, key);
                });
            }
        });
    };

    /**
     * value替换
     * @param {JQuery} $nodes jQuery对象
     * @param {Object} valueReplaceRules 值替换规则对象
     * @param {string} key 应用的规则集的名字
     */
    function traverseValueNodes($nodes, valueReplaceRules, key) {
        if (!$nodes) return;

        $nodes.each(function () {
            let $node = $(this);
            if ($node.is('[value]')) {
                Object.keys(valueReplaceRules).forEach(match => {
                    const replace = valueReplaceRules[match];
                    const regex = new RegExp(match, 'g');
                    let currentValue = $node.val();
                    let newValue = currentValue.replace(regex, replace);
                    $node.val(newValue);
                    if (OJBetter.dev.isRuleMarkingEnabled) {
                        if (newValue !== currentValue) $($node).after(`<span class="markingTextReplaceRule">${key}</span>`);
                    }
                });
            } else {
                $node.children().each(function () {
                    traverseValueNodes($(this), valueReplaceRules, key);
                });
            }
        });
    }

    /**
     * 严格的文本节点遍历替换
     * 要求被替换文本严格与规则文本一致
     * @param {JQuery} $nodes jQuery对象
     * @param {Object} textReplaceRules 文本替换规则对象
     * @param {string} key 应用的规则集的名字
     */
    const strictTraverseTextNodes = ($nodes, textReplaceRules, key) => {
        if (!$nodes) return;

        $nodes.each((_, node) => {
            if (node.nodeType === Node.TEXT_NODE) {
                const trimmedNodeText = node.textContent.trim();
                for (const [match, replacement] of Object.entries(textReplaceRules)) {
                    if (trimmedNodeText === match) {
                        const beforeText = node.textContent;
                        node.textContent = replacement;
                        if (node.textContent !== beforeText && OJBetter.dev.isRuleMarkingEnabled) {
                            $(node).after(`<span class="markingTextReplaceRule">${key}</span>`);
                        }
                    }
                }
            } else if (node.nodeType === Node.ELEMENT_NODE && node.tagName.toLowerCase() !== 'iframe') {
                $(node).contents().each((_, childNode) => {
                    strictTraverseTextNodes($(childNode), textReplaceRules, key);
                });
            }
        });
    };

    /**
     * 应用文本替换
     */
    let commonReplacements = subs.commonReplacements;
    Object.entries(commonReplacements).forEach(([key, value]) => {
        const classSelectors = Array.isArray(value.class) ? value.class : [value.class]; // 兼容,class的值可以为数组或者字符串
        classSelectors.forEach(classSelector => {
            if (value.isStrict) {
                strictTraverseTextNodes(OJB_safeCreateJQElement(`${classSelector}`), value.rules, key);
            } else {
                traverseTextNodes(OJB_safeCreateJQElement(`${classSelector}`), value.rules, key);
            }
        });
    });

    /**
     * 应用value替换
     */
    let InputValueReplacements = subs.InputValueReplacements;
    Object.entries(InputValueReplacements).forEach(([key, value]) => {
        const classSelectors = Array.isArray(value.class) ? value.class : [value.class];
        classSelectors.forEach(classSelector => {
            traverseValueNodes(OJB_safeCreateJQElement(`${classSelector}`), value.rules, key);
        });
    });

    /**
     * 动态添加的文本的替换
     */
    let dynamicReplacements = subs.dynamicReplacements;
    Object.entries(dynamicReplacements).forEach(([key, value]) => {
        const classSelectors = Array.isArray(value.class) ? value.class : [value.class]; // 兼容,class的值可以为数组或者字符串
        classSelectors.forEach(classSelector => {
            OJB_observeElement({
                selector: classSelector,
                callback: (node) => {
                    // let popupContent = node.textContent.replace(/^×/, ''); // 去除开头多余的 '×' 字符
                    if (value.isStrict) {
                        strictTraverseTextNodes(OJB_safeCreateJQElement(`${classSelector}`), value.rules, key);
                    } else {
                        traverseTextNodes(OJB_safeCreateJQElement(`${classSelector}`), value.rules, key);
                    }
                }
            });
        });
    });

    // // 杂项
    // (function () {
    //     // 选项汉化input[type="radio"]
    //     var translations = {
    //         "as individual participant": "个人",
    //         "as a team member": "作为一个团队成员",
    //     };
    //     $('input[type="radio"]').each(function () {
    //         var tag = $(this).parent().contents().filter(function () {
    //             return this.nodeType === Node.TEXT_NODE;
    //         });
    //         for (var i = 0; i < tag.length; i++) {
    //             var text = tag[i].textContent.trim();
    //             if (translations.hasOwnProperty(text)) {
    //                 $(this).addClass(text);
    //                 tag[i].replaceWith(translations[text]);
    //                 break;
    //             }
    //         }
    //     });
    // })();
    // (function () {
    //     var translations = {
    //         "(standard input\/output)": "标准输入/输出",
    //     };
    //     $("div.notice").each(function () {
    //         var tag = $(this).children().eq(0).text();
    //         for (var property in translations) {
    //             if (tag.match(property)) {
    //                 $(this).children().eq(0).text(translations[property]);
    //                 break;
    //             }
    //         }
    //     });
    // })();

    // // 轻量站特殊
    // if (OJBetter.typeOfPage.is_mSite) {
    //     traverseTextNodes($('nav'), commonReplacements['.second-level-menu']['rules']);
    // }
    // if (OJBetter.typeOfPage.is_mSite) {
    //     (function () {
    //         var translations = {
    //             "Announcements": "公告",
    //             "Submissions": "提交记录",
    //             "Contests": "比赛",
    //         };
    //         $(".caption").each(function () {
    //             var optionValue = $(this).text();
    //             if (translations[optionValue]) {
    //                 $(this).text(translations[optionValue]);
    //             }
    //         });
    //     })();
    // }
};

/**
 * i18next初始化
 */
async function initI18next() {
    return new Promise((resolve, reject) => {
        i18next
            .use(i18nextChainedBackend)
            .init({
                lng: OJBetter.localization.scriptLang,
                ns: ['common', 'settings', 'config', 'dialog', 'alert', 'translator',
                    'button', 'codeEditor', 'comments', 'announce', 'logMessage'], // 命名空间列表
                defaultNS: 'settings',
                fallbackLng: ['zh', OJBetter.translation.targetLang],
                load: 'currentOnly',
                debug: false,
                backend: {
                    backends: [
                        i18nextLocalStorageBackend,
                        i18nextHttpBackend
                    ],
                    backendOptions: [{
                        prefix: 'i18next_res_',
                        expirationTime: 7 * 24 * 60 * 60 * 1000,
                        defaultVersion: `v${OJBetter.state.version}`,
                        store: typeof window !== 'undefined' ? window.localStorage : null
                    }, {
                        /* options for secondary backend */
                        loadPath: (lng, ns) => {
                            if (lng[0] === 'zh' || lng[0] === 'zh-Hans') {
                                return `https://aowuucdn.oss-accelerate.aliyuncs.com/resources/locales/${OJBetter.state.formatName}/${ns}.json`;
                            }
                            return `https://aowuucdn.oss-accelerate.aliyuncs.com/i18n/${lng}/resources/locales/${OJBetter.state.formatName}/${ns}.json`;
                        }
                    }]
                }
            }, (err, t) => {
                if (err) {
                    reject(err);
                } else {
                    jqueryI18next.init(i18next, $, {
                        useOptionsAttr: true
                    });
                    resolve(t);
                }
            });
    });
};

/**
 * 抽象命令类
 */
class Command {
    execute() { }
    undo() { }
}

/**
 * 命令调用者
 */
class CommandInvoker {
    constructor() {
        this.history = [];
    }

    /**
     * 执行命令
     * @param {Command} command 命令对象
     */
    execute(command) {
        this.history.push(command);
        command.execute();
    }

    /**
     * 撤销命令
     */
    undo() {
        const command = this.history.pop();
        if (command) {
            command.undo();
        }
    }
}

/**
 * 接收者
 */
class DOMContainer {
    /**
     * @param {JQueryObject} element 容器对象
     */
    constructor(element) {
        this.containerElement = element;
    }

    /**
     * 添加元素
     * @param {JQueryObject} element 元素对象
     * @returns {JQueryObject} 添加的元素对象
     */
    add(element) {
        this.containerElement.append(element);
        return this.containerElement.children().last();
    }

    /**
     * 删除元素
     * @param {JQueryObject} element 元素对象
     */
    remove(element) {
        $(element).remove();
    }
}

/**
 * 具体命令类:添加元素
 */
class AddElementCommand extends Command {
    /**
     * @param {DOMContainer} receiver 接收者
     * @param {JQueryObject} element 元素对象
     */
    constructor(receiver, element) {
        super();
        this.receiver = receiver;
        this.element = element;
        this.addedElement = null;
    }

    execute() {
        this.addedElement = this.receiver.add(this.element);
    }

    undo() {
        if (this.addedElement) {
            this.receiver.remove(this.addedElement);
        }
    }
}

/**
 * 具体命令类:删除元素
 */
class RemoveElementCommand extends Command {
    /**
     * @param {DOMContainer} receiver 接收者
     * @param {JQueryObject} element 元素对象
     */
    constructor(receiver, element) {
        super();
        this.receiver = receiver;
        this.element = element;
        this.parent = $(element).parent();
        this.nextSibling = $(element).next();
    }

    execute() {
        this.receiver.remove(this.element);
    }

    undo() {
        if (this.nextSibling.length > 0) {
            $(this.element).insertBefore(this.nextSibling);
        } else {
            this.parent.append(this.element);
        }
    }
}

/**
 * 验证器
 */
class Validator {
    /**
     * 表单必填项空值校验
     */
    static required(structure) {
        let config = {};
        let allFieldsValid = true;
        for (const key in structure) {
            let value = key.type == 'checkbox' ?
                $(key).prop("checked") : $(key).val();

            config[structure[key].value] = value;

            if (value || structure[key].require === false) {
                $(key).removeClass('is_null');
            } else {
                $(key).addClass('is_null');
                allFieldsValid = false;
            }
        }
        return {
            valid: allFieldsValid,
            config: config
        };
    }

    /**
     * 表单合法性校验
     */
    static checkKeyValuePairs(structure, config) {
        let errorKeys = [];
        let allFieldsValid = true;

        for (const key in structure) {
            const { check, value } = structure[key];
            const fieldValue = config[value];

            // 如果字段没有值或校验类型不匹配,则跳过当前迭代
            if (!fieldValue) continue;

            let isValid = true;
            switch (check) {
                case 'keyValuePairs':
                    isValid = Validator.keyValuePairs(fieldValue);
                    break;
                case 'dotSeparatedPath':
                    isValid = Validator.validateDotSeparatedPath(fieldValue);
                    break;
                default:
                    // 没有匹配的校验类型
                    continue;
            }

            Validator.toggleErrorDisplay(key, isValid);
            if (!isValid) {
                allFieldsValid = false;
                errorKeys.push(key);
            }
        }

        return {
            valid: allFieldsValid,
            errorKeys: errorKeys
        };
    }

    /**
     * 切换错误信息的显示和隐藏
     * @param {string} key - 字段的键
     * @param {boolean} isValid - 字段值是否有效
     */
    static toggleErrorDisplay(key, isValid) {
        const errorMessage = i18next.t('common.unValid', { ns: 'settings' });
        const $errorSpan = $(key).prev('span.text-error');
        if (!isValid) {
            if (!$errorSpan.length) {
                $(key).before(`<span class="text-error" style="color: red;">${errorMessage}</span>`);
            }
        } else {
            $errorSpan.remove();
        }
    }

    /**
     * 键值对合法性校验
     * @param {string} value
     * @returns {boolean}
     */
    static keyValuePairs(value) {
        const keyValuePairs = value.split('\n');
        // 允许值中包含空格和冒号
        const regex = /^[a-zA-Z0-9_-]+\s*:\s*.+$/;
        return keyValuePairs.every(pair => regex.test(pair));
    }


    /**
     * 点分隔符路径格式校验,允许加减运算
     * @param {string} path
     * @returns {boolean}
     */
    static validateDotSeparatedPath(path) {
        // 正则表达式允许标识符之间有点号,标识符可以包含加减运算
        const regex = /^([a-zA-Z0-9_-]+(\s*[\+\-]\s*[a-zA-Z0-9_-]+)*\.)*([a-zA-Z0-9_-]+(\s*[\+\-]\s*[a-zA-Z0-9_-]+)*)$/;
        return regex.test(path);
    }
}

/**
 * 配置管理
 */
class ConfigManager {
    /**
     * @param {HTMLElement} element - 挂载容器
     * @param {string} prefix - 前缀
     * @param {object} tempConfig - 配置内容
     * @param {object} structure - 配置结构
     * @param {object} configHTML - 配置编辑页面HTML
     * @param {boolean} allowChoice - 是否允许选择列表项
     */
    constructor(element, prefix, tempConfig, structure, configHTML, allowChoice = true) {
        /** @param 设置面板DIV */
        this.settingMenuDiv = $('#OJBetter_setting_menu');
        this.element = $(element);
        this.prefix = prefix;
        this.tempConfig = tempConfig;
        this.structure = structure;
        this.configHTML = configHTML;
        this.allowChoice = allowChoice;

        this.controlTip = null;
        this.config_bar_list = null;
        this.config_bar_ul = null;
        this.config_add_button = null;
        this.menu = null;
        this.editItem = null;
        this.deleteItem = null;

        // 绑定方法
        this.onAdd = this.onAdd.bind(this);
        this.onEdit = this.onEdit.bind(this);
        this.onDelete = this.onDelete.bind(this);
        this.createListItemElement = this.createListItemElement.bind(this);

        this.lastItemId = 0; // 列表中当前最后一个元素的id号
        this.init();
    }

    init() {
        this.createControlBar();
        this.createContextMenu();
        this.renderList();
    }

    /**
     * 创建控制栏
     */
    createControlBar() {
        this.controlTip = OJB_safeCreateJQElement(`<div id='${this.prefix}configControlTip' style='color:red;'></div>`);
        this.config_bar_list = OJB_safeCreateJQElement(`<div class='config_bar_list' id='${this.prefix}config_bar_list'></div>`);
        this.config_bar_ul = OJB_safeCreateJQElement(`<ul class='config_bar_ul' id='${this.prefix}config_bar_ul'></ul>`);
        this.element.append(this.controlTip);
        this.element.append(this.config_bar_list);
        this.config_bar_list.append(this.config_bar_ul);
    }

    /**
     * 创建右键菜单
     */
    createContextMenu() {
        const menu = OJB_safeCreateJQElement(`<div id='config_bar_menu' style='display: none;'></div>`);
        const editItem = OJB_safeCreateJQElement(`
        <div class='config_bar_menu_item' id='config_bar_menu_edit'>
            ${i18next.t('contextMenu.edit', { ns: 'translator' })}
        </div>`);
        const deleteItem = OJB_safeCreateJQElement(`
        <div class='config_bar_menu_item' id='config_bar_menu_delete'>
            ${i18next.t('contextMenu.delete', { ns: 'translator' })}
        </div>`);
        menu.append(editItem);
        menu.append(deleteItem);
        this.editItem = editItem;
        this.deleteItem = deleteItem;
        this.menu = menu;
        this.settingMenuDiv.append(menu);
    }

    /**
     * 关闭右键菜单
     */
    closeContextMenu() {
        this.menu.css({ display: "none" });
    }

    /**
     * 创建列表项
     * @param {string} text - 列表项文本
     * @returns {HTMLElement} - 列表项
     */
    createListItemElement(text) {
        const id = OJB_getRandomNumber(4);
        const li = $("<li></li>");
        const radio = OJB_safeCreateJQElement(`<input type='radio' name='${this.prefix}config_item'></input>`)
            .attr("value", text)
            .attr("id", id)
            .attr("prev_id", this.lastItemId)
            .appendTo(li);
        if (!this.allowChoice) {
            radio.prop("disabled", true);
        }
        const label = OJB_safeCreateJQElement(`<label for='${id}' class='config_bar_ul_li_text'>${text}</label>`).appendTo(li);


        this.lastItemId = id;

        // 添加右键菜单
        li.on("contextmenu", (event) => {
            event.preventDefault();
            this.menu.css({
                display: "block",
                left: event.pageX, top: event.pageY
            });

            const deleteItem = this.deleteItem;
            const editItem = this.editItem;

            // 移除旧事件
            deleteItem.off("click");
            editItem.off("click");

            // 获取 li 在 ul 中的索引
            const index = li.index();

            deleteItem.on("click", () => this.onDelete(index, li));
            editItem.on("click", () => this.onEdit(index, li));

            $(document).one("click", (event) => {
                if (!this.menu.get(0).contains(event.target)) {
                    this.closeContextMenu();
                    deleteItem.off("click", () => this.onDelete);
                    editItem.off("click", () => this.onEdit);
                }
            });
        });

        return li;
    }

    /**
     * 渲染配置列表
     */
    renderList() {
        const list = this.config_bar_ul;
        list.empty(); // 清空
        this.tempConfig.configurations.forEach((item) => {
            list.append(this.createListItemElement(item['name']));
        });

        // 添加按钮
        let addButton = OJB_safeCreateJQElement(`<li id='${this.prefix}add_button' class="tempConfig_add_button">
            <span>+ ${i18next.t('add', { ns: 'common' })}</span>
        </li>`);
        this.config_add_button = addButton;
        list.append(addButton);
        addButton.on("click", this.onAdd);
    }

    /**
     * 添加配置项
     */
    onAdd() {
        const configMenu = this.createConfigHTML();
        const structure = this.structure;

        configMenu.on("click", "#tempConfig_save", () => {

            // 检查必填字段
            const { valid, config } = Validator.required(structure);
            if (!valid) return;

            // 检查键值对
            const { valid: checkOk, errorKey } = Validator.checkKeyValuePairs(structure, config);
            if (!checkOk) return;

            this.tempConfig.configurations.push(config);

            this.createListItemElement(config.name).insertBefore(this.config_add_button);

            configMenu.remove();
        });

        configMenu.on("click", ".btn-close", () => {
            configMenu.remove();
        });
    }

    /**
     * 修改配置项
     * @param {number} index - 配置项索引
     * @param {HTMLElement} li - 配置项
     * @returns {void}
     */
    onEdit(index, li) {
        const configMenu = this.createConfigHTML();
        const structure = this.structure;

        this.closeContextMenu();

        // 填充表单
        for (const [key, { value, type }] of Object.entries(this.structure)) {
            const configValue = this.tempConfig.configurations[index][value];
            const $element = $(key);
            if (type === 'checkbox') {
                $element.prop("checked", configValue);
            } else {
                $element.val(configValue);
            }
        }

        configMenu.on("click", "#tempConfig_save", () => {
            // 检查必填字段
            const { valid, config } = Validator.required(structure);
            if (!valid) return;

            // 检查键值对
            const { valid: checkOk, errorKey } = Validator.checkKeyValuePairs(structure, config);
            if (!checkOk) return;

            // 更新配置
            this.tempConfig.configurations[index] = config;
            li.find('label').text(config.name);

            OJB_closeAndRemoveModal(configMenu);
        });

        configMenu.on("click", ".btn-close", () => {
            OJB_closeAndRemoveModal(configMenu);
        });
    }

    /**
     * 删除配置项
     * @param {number} index - 配置项索引
     * @param {HTMLElement} li - 配置项
     * @returns {void}
     */
    onDelete(index, li) {
        this.closeContextMenu();
        this.tempConfig.configurations.splice(index, 1);
        li.remove();
    }

    /**
     * 创建配置编辑页面
     * @returns {JQuery<HTMLElement>} 返回配置编辑页面
     */
    createConfigHTML() {
        const configMenu = OJB_safeCreateJQElement(this.configHTML);
        this.settingMenuDiv.after(configMenu);
        OJB_showModal(configMenu);
        OJB_addDraggable(configMenu);
        elementLocalize(configMenu);
        return configMenu;
    }

    /**
     * 获取配置内容
     * @returns {object} - 配置内容
     */
    getTempConfig() {
        return this.tempConfig;
    }

    /**
     * 注册列表项选中改变监听
     */
    registerChoiceChange() {
        this.config_bar_ul.on("change", "input[type='radio']", (event) => {
            const value = event.target.value;
            this.tempConfig.choice = value;
        });
    }
}

const OJBetter_setting_sidebar_HTML = `
<div class="OJBetter_setting_sidebar">
    <ul>
        <li><a href="#basic-settings" id="sidebar-basic-settings" class="active" data-i18n="settings:sidebar.basic"></a></li>
        <li><a href="#preference-settings" id="sidebar-preference-settings" data-i18n="settings:sidebar.preference"></a></li>
        <li><a href="#translation-settings" id="sidebar-translation-settings" data-i18n="settings:sidebar.translation"></a></li>
        <li><a href="#clist_rating-settings" id="sidebar-clist_rating-settings" data-i18n="settings:sidebar.clist"></a></li>
        <li><a href="#code_editor-settings" id="sidebar-code_editor-settings" data-i18n="settings:sidebar.monaco"></a></li>
        <li><a href="#dev-settings" id="sidebar-dev-settings" data-i18n="settings:sidebar.dev"></a></li>
        <li><a href="#about-settings" id="sidebar-about-settings" data-i18n="settings:sidebar.about"></a></li>
    </ul>
</div>
`;

const basic_settings_HTML = `
<div id="basic-settings" class="settings-page active">
    <h3 data-i18n="settings:basic.title"></h3>
    <hr>
    <div class='OJBetter_setting_list' style="padding: 0px 10px;">
        <span id="darkMode_span" data-i18n="settings:basic.darkMode.name"></span>
        <div class="dark-mode-selection">
            <label>
                <input class="radio-input" type="radio" name="darkMode" value="dark" />
                <span class="OJBetter_setting_menu_label_text"
                    data-i18n="settings:basic.darkMode.options.dark"></span>
                <span class="radio-icon"> </span>
            </label>
            <label>
                <input checked="" class="radio-input" type="radio" name="darkMode" value="light" />
                <span class="OJBetter_setting_menu_label_text"
                    data-i18n="settings:basic.darkMode.options.light"></span>
                <span class="radio-icon"> </span>
            </label>
            <label>
                <input class="radio-input" type="radio" name="darkMode" value="follow" />
                <span class="OJBetter_setting_menu_label_text"
                    data-i18n="settings:basic.darkMode.options.system"></span>
                <span class="radio-icon"> </span>
            </label>
        </div>
    </div>
    <div class='OJBetter_setting_list'>
        <label for="scriptL10nLanguage" style="display: flex;" data-i18n="settings:localization.scriptLanguageLabel"></label>
        <select id="scriptL10nLanguage" name="scriptL10nLanguage">
            <option value="zh">简体中文</option>
            <option value="zh-Hant">繁體中文</option>
            <option value="en">English</option>
            <option value="de">Deutsch</option>
            <option value="fr">Français</option>
            <option value="ko">한국어</option>
            <option value="pt">Português</option>
            <option value="ja">日本語</option>
            <option value="es">Español</option>
            <option value="it">Italiano</option>
            <option value="hi">हिन्दी</option>
        </select>
    </div>
    <div class='OJBetter_setting_list'>
        <label for="localizationLanguage" style="display: flex;" data-i18n="settings:localization.websiteLanguageLabel"></label>
        <select id="localizationLanguage" name="localizationLanguage">
            <option value="initial">——</option>
            <option value="zh">简体中文</option>
            <option value="zh-Hant">繁體中文</option>
            <option value="de">Deutsch</option>
            <option value="fr">Français</option>
            <option value="ko">한국어</option>
            <option value="pt">Português</option>
            <option value="ja">日本語</option>
            <option value="es">Español</option>
            <option value="it">Italiano</option>
            <option value="hi">हिन्दी</option>
        </select>
    </div>
    <div class='OJBetter_setting_list alert_tip'>
        <div data-i18n="[html]settings:localization.notice.1"></div>
    </div>
    <div class='OJBetter_setting_list alert_tip'>
        <div data-i18n="[html]settings:localization.notice.2"></div>
    </div>
</div>
`;


const translation_settings_HTML = `
<div id="translation-settings" class="settings-page">
    <h3 data-i18n="settings:translation.title"></h3>
    <hr>
    <h4 data-i18n="settings:translation.options.title"></h4>
    <div class='OJBetter_setting_list'>
        <label for="transTargetLang" style="display: flex;" data-i18n="settings:translation.preference.target.title"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.preference.target.helpText"></div>
        </div>
        <select id="transTargetLang" name="transTargetLang">
            <option value="zh">简体中文</option>
            <option value="zh-Hant">繁體中文</option>
            <option value="de">Deutsch</option>
            <option value="fr">Français</option>
            <option value="ko">한국어</option>
            <option value="pt">Português</option>
            <option value="ja">日本語</option>
            <option value="es">Español</option>
            <option value="it">Italiano</option>
            <option value="hi">हिन्दी</option>
        </select>
    </div>
    <div id="translationServices">
        <label>
            <input type='radio' name='translation' value='deepl'>
            <span class='OJBetter_setting_menu_label_text'
                data-i18n="settings:translation.options.services.deepl"></span>
        </label>
        <label>
            <input type='radio' name='translation' value='iflyrec'>
            <span class='OJBetter_setting_menu_label_text'
                data-i18n="settings:translation.options.services.iflyrec"></span>
        </label>
        <label>
            <input type='radio' name='translation' value='youdao'>
            <span class='OJBetter_setting_menu_label_text'
                data-i18n="settings:translation.options.services.youdao"></span>
        </label>
        <label>
            <input type='radio' name='translation' value='google'>
            <span class='OJBetter_setting_menu_label_text'
                data-i18n="settings:translation.options.services.google"></span>
        </label>
        <label>
            <input type='radio' name='translation' value='caiyun'>
            <span class='OJBetter_setting_menu_label_text'
                data-i18n="settings:translation.options.services.caiyun"></span>
        </label>
        <label>
            <input type='radio' name='translation' value='openai'>
            <span class='OJBetter_setting_menu_label_text'
                data-i18n="settings:translation.options.services.openai.name">
                <div class="help_tip">
                    ${helpCircleHTML}
                    <div class="tip_text"
                        data-i18n="[html]settings:translation.options.services.openai.helpText"></div>
                </div>
            </span>
        </label>
    </div>
    <hr>
    <h4>DeepL</h4>
    <div class='OJBetter_setting_list'>
        <label for="deepl_type" style="display: flex;" data-i18n="settings:translation.deepl.mode.title"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.deepl.mode.helpText"></div>
        </div>
        <select id="deepl_type" name="deepl_type">
            <option value="free" data-i18n="settings:translation.deepl.mode.select.free"></option>
            <option value="api" data-i18n="settings:translation.deepl.mode.select.api"></option>
        </select>
    </div>
    <div id="deepl_config" class="config"></div>
    <div class='OJBetter_setting_list'>
        <label for="enableEmphasisProtection" data-i18n="settings:translation.deepl.enableEmphasisProtection.title"></label>
        <div class="help_tip" style="margin-right: initial;">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.deepl.enableEmphasisProtection.helpText"></div>
        </div>
        <div class="badge">Official API Only</div>
        <input type="checkbox" id="enableEmphasisProtection" name="enableEmphasisProtection">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="enableLinkProtection" data-i18n="settings:translation.deepl.enableLinkProtection.title"></label>
        <div class="help_tip" style="margin-right: initial;">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.deepl.enableLinkProtection.helpText"></div>
        </div>
        <div class="badge">Official API Only</div>
        <input type="checkbox" id="enableLinkProtection" name="enableLinkProtection">
    </div>
    <hr>
    <h4>ChatGPT</h4>
    <div id="chatgpt_config" class="config"></div>
    <div class='OJBetter_setting_list'>
        <label for="openai_isStream" data-i18n="settings:translation.chatgpt.isStream.name"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.chatgpt.isStream.helpText"></div>
        </div>
        <input type="checkbox" id="openai_isStream" name="openai_isStream">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="openai_asSystemPrompt" data-i18n="settings:translation.chatgpt.asSystemPrompt.name"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.chatgpt.asSystemPrompt.helpText"></div>
        </div>
        <input type="checkbox" id="openai_asSystemPrompt" name="openai_asSystemPrompt">
    </div>
    <div class="OJBetter_setting_list">
        <label for='openai_customPrompt'>
            <div style="display: flex;align-items: center;">
                <span class="input_label" data-i18n="settings:translation.chatgpt.customPrompt.name"></span>
                <div class="help_tip">
                    ${helpCircleHTML}
                    <div class="tip_text" data-i18n="[html]settings:translation.chatgpt.customPrompt.helpText"></div>
                </div>
            </div>
        </label>
        <textarea id="openai_customPrompt" placeholder='' require = false data-i18n="[placeholder]settings:translation.chatgpt.customPrompt.placeholder"></textarea>
    </div>
    <hr>
    <h4 data-i18n="settings:translation.preference.title"></h4>
    <div class='OJBetter_setting_list'>
        <label for="comment_translation_choice" style="display: flex;"
            data-i18n="settings:translation.preference.comment_translation_choice.title">
        </label>
        <select id="comment_translation_choice" name="comment_translation_choice">
            <option value="0" data-i18n="settings:translation.preference.comment_translation_choice.services.follow"></option>
            <option value="deepl" data-i18n="settings:translation.preference.comment_translation_choice.services.deepl"></option>
            <option value="iflyrec" data-i18n="settings:translation.preference.comment_translation_choice.services.iflyrec"></option>
            <option value="youdao" data-i18n="settings:translation.preference.comment_translation_choice.services.youdao"></option>
            <option value="google" data-i18n="settings:translation.preference.comment_translation_choice.services.google"></option>
            <option value="caiyun" data-i18n="settings:translation.preference.comment_translation_choice.services.caiyun"></option>
            <option value="openai" data-i18n="settings:translation.preference.comment_translation_choice.services.openai"></option>
        </select>
    </div>
    <hr>

    <h4 data-i18n="settings:translation.autoTranslation.title"></h4>
    <div class='OJBetter_setting_list'>
        <label for="autoTranslation" data-i18n="settings:translation.autoTranslation.enable"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.autoTranslation.helpText"></div>
        </div>
        <input type="checkbox" id="autoTranslation" name="autoTranslation">
    </div>
    <div class='OJBetter_setting_list'>
        <label for='shortTextLength'>
            <div style="display: flex;align-items: center;"
                data-i18n="settings:translation.autoTranslation.shortTextLength.name"></div>
        </label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.autoTranslation.shortTextLength.helpText">
            </div>
        </div>
        <input type='number' id='shortTextLength' class='no_default' require=true data-i18n="[placeholder]settings:translation.autoTranslation.shortTextLength.placeholder">
        <span data-i18n="settings:translation.autoTranslation.shortTextLength.end"></span>
    </div>
    <div style="display:none;">
    <div class='OJBetter_setting_list'>
        <label for="allowMixTrans" data-i18n="settings:translation.autoTranslation.allowMixTrans.name"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.autoTranslation.allowMixTrans.helpText">
            </div>
        </div>
        <input type="checkbox" id="allowMixTrans" name="allowMixTrans">
        <div class='OJBetter_checkboxs'>
            <input type="checkbox" id="deepl" name="mixedTranslation" value="deepl">
            <label for="deepl" data-i18n="settings:translation.autoTranslation.allowMixTrans.checkboxs.deepl"></label>
            <input type="checkbox" id="iflyrec" name="mixedTranslation" value="iflyrec">
            <label for="iflyrec" data-i18n="settings:translation.autoTranslation.allowMixTrans.checkboxs.iflyrec"></label>
            <input type="checkbox" id="youdao" name="mixedTranslation" value="youdao">
            <label for="youdao" data-i18n="settings:translation.autoTranslation.allowMixTrans.checkboxs.youdao"></label>
            <input type="checkbox" id="google" name="mixedTranslation" value="google">
            <label for="google" data-i18n="settings:translation.autoTranslation.allowMixTrans.checkboxs.google">Google</label>
            <input type="checkbox" id="caiyun" name="mixedTranslation" value="caiyun">
            <label for="caiyun" data-i18n="settings:translation.autoTranslation.allowMixTrans.checkboxs.caiyun"></label>
        </div>
    </div>
    </div>
    <hr>

    <h4 data-i18n="settings:translation.advanced.name"></h4>
    <div class='OJBetter_setting_list'>
        <label for="comment_translation_mode" style="display: flex;" data-i18n="settings:translation.advanced.mode.name"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.advanced.mode.helpText"></div>
        </div>
        <select id="comment_translation_mode" name="comment_translation_mode">
            <option value="0" data-i18n="settings:translation.advanced.mode.options.0"></option>
            <option value="1" data-i18n="settings:translation.advanced.mode.options.1"></option>
            <option value="2" data-i18n="settings:translation.advanced.mode.options.2"></option>
        </select>
    </div>
    <div class='OJBetter_setting_list'>
        <label for="memoryTranslateHistory" data-i18n="settings:translation.advanced.memory.name"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.advanced.memory.helpText"></div>
        </div>
        <input type="checkbox" id="memoryTranslateHistory" name="memoryTranslateHistory">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="translation_retransAction" style="display: flex;" data-i18n="settings:translation.advanced.retrans.name"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.advanced.retrans.helpText"></div>
        </div>
        <select id="translation_retransAction" name="translation_retransAction">
            <option value=0 data-i18n="settings:translation.advanced.retrans.options.0"></option>
            <option value=1 data-i18n="settings:translation.advanced.retrans.options.1"></option>
        </select>
    </div>
    <div class='OJBetter_setting_list'>
        <label for='transWaitTime'>
            <div style="display: flex;align-items: center;" data-i18n="settings:translation.advanced.transWaitTime.name"></div>
        </label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.advanced.transWaitTime.helpText"></div>
        </div>
        <input type='number' id='transWaitTime' class='no_default' require=true data-i18n="[placeholder]settings:translation.advanced.transWaitTime.placeholder">
        <span data-i18n="settings:translation.advanced.transWaitTime.end"></span>
    </div>
    <div class='OJBetter_setting_list'>
        <label for="translation_replaceSymbol" style="display: flex;" data-i18n="settings:translation.advanced.replaceSymbol.name"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.advanced.replaceSymbol.helpText"></div>
        </div>
        <select id="translation_replaceSymbol" name="translation_replaceSymbol">
            <option value=2 data-i18n="settings:translation.advanced.replaceSymbol.options.2"></option>
            <option value=1 data-i18n="settings:translation.advanced.replaceSymbol.options.1"></option>
            <option value=3 data-i18n="settings:translation.advanced.replaceSymbol.options.3"></option>
        </select>
    </div>
    <div class='OJBetter_setting_list'>
        <label for="filterTextWithoutEmphasis" data-i18n="settings:translation.advanced.filterTextWithoutEmphasis.name"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.advanced.filterTextWithoutEmphasis.helpText"></div>
        </div>
        <input type="checkbox" id="filterTextWithoutEmphasis" name="filterTextWithoutEmphasis">
    </div>
</div>
`;

const clist_rating_settings_HTML = `
<div id="clist_rating-settings" class="settings-page">
    <h3 data-i18n="settings:clist.title"></h3>
    <hr>
    <h4 data-i18n="settings:clist.basics.name"></h4>
    <div class='OJBetter_setting_list alert_tip'>
        <div>
            <p data-i18n="[html]settings:clist.basics.notice"></p>
        </div>
    </div>
    <div class='OJBetter_setting_list'>
        <label for='clist_Authorization'>
            <div style="display: flex;align-items: center;">
                <span class="input_label" data-i18n="settings:clist.basics.key.title"></span>
            </div>
        </label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:clist.basics.key.helpText"></div>
        </div>
        <input type='text' id='clist_Authorization' class='no_default' required="true"
            data-i18n="[placeholder]settings:clist.basics.key.keyPlaceholder">
    </div>
    <hr>
    <h4 data-i18n="settings:clist.displayRating.title"></h4>
    <div class='OJBetter_setting_list'>
        <label for="showClistRating_contest"><span data-i18n="settings:clist.displayRating.contest.name"></span></label>
        <input type="checkbox" id="showClistRating_contest" name="showClistRating_contest">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="showClistRating_problem"><span data-i18n="settings:clist.displayRating.problem.name"></span></label>
        <input type="checkbox" id="showClistRating_problem" name="showClistRating_problem">
    </div>
    <div class='OJBetter_setting_list' style='display:none;'>
        <label for="showClistRating_problemset"><span data-i18n="settings:clist.displayRating.problemset.name"></span></label>
        <input type="checkbox" id="showClistRating_problemset" name="showClistRating_problemset">
    </div>
    <hr>
    <div class='OJBetter_setting_list'>
        <label for="RatingHidden"><span data-i18n="settings:clist.spoilerProtection.title"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:clist.spoilerProtection.helpText"></div>
        </div>
        <input type="checkbox" id="RatingHidden" name="RatingHidden">
    </div>
</div>
`;

const code_editor_settings_HTML = `
<div id="code_editor-settings" class="settings-page">
    <h3 data-i18n="settings:codeEditor.title"></h3>
    <hr>
    <h4 data-i18n="settings:codeEditor.basics"></h4>
    <div class='OJBetter_setting_list'>
        <label for="problemPageCodeEditor"><span
                data-i18n="settings:codeEditor.problemPageCodeEditor.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="settings:codeEditor.problemPageCodeEditor.helpText"></div>
        </div>
        <input type="checkbox" id="problemPageCodeEditor" name="problemPageCodeEditor">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="beautifyPreBlocks"><span
                data-i18n="settings:codeEditor.beautifyPreBlocks.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="settings:codeEditor.beautifyPreBlocks.helpText"></div>
        </div>
        <input type="checkbox" id="beautifyPreBlocks" name="beautifyPreBlocks">
    </div>
    <hr>
    <h4 data-i18n="settings:codeEditor.preferences.title"></h4>
    <div class='OJBetter_setting_list'>
        <label for="isCodeSubmitConfirm"><span
                data-i18n="settings:codeEditor.preferences.isCodeSubmitConfirm.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="settings:codeEditor.preferences.isCodeSubmitConfirm.helpText"></div>
        </div>
        <input type="checkbox" id="isCodeSubmitConfirm" name="isCodeSubmitConfirm">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="autoSubmitAfterPass"><span
                data-i18n="settings:codeEditor.preferences.autoSubmitAfterPass.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="settings:codeEditor.preferences.autoSubmitAfterPass.helpText"></div>
        </div>
        <input type="checkbox" id="autoSubmitAfterPass" name="autoSubmitAfterPass">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="alwaysConsumeMouseWheel"><span
                data-i18n="settings:codeEditor.preferences.alwaysConsumeMouseWheel.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="settings:codeEditor.preferences.alwaysConsumeMouseWheel.helpText"></div>
        </div>
        <input type="checkbox" id="alwaysConsumeMouseWheel" name="alwaysConsumeMouseWheel">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="autoMemoryCode"><span
                data-i18n="settings:codeEditor.preferences.autoMemoryCode.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="settings:codeEditor.preferences.autoMemoryCode.helpText"></div>
        </div>
        <input type="checkbox" id="autoMemoryCode" name="autoMemoryCode">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="submitButtonPosition"><span
                data-i18n="settings:codeEditor.preferences.submitButtonPosition.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="settings:codeEditor.preferences.submitButtonPosition.helpText"></div>
        </div>
        <select id="submitButtonPosition" name="submitButtonPosition">
            <option value="bottom" data-i18n="settings:codeEditor.preferences.submitButtonPosition.options.bottom"></option>
            <option value="top" data-i18n="settings:codeEditor.preferences.submitButtonPosition.options.top"></option>
        </select>
    </div>
    <hr>
    <h4 data-i18n="settings:codeEditor.onlineCodeExecution.title"></h4>
    <label>
        <input type='radio' name='compiler' value='official'>
        <span class='OJBetter_setting_menu_label_text'
            data-i18n="settings:codeEditor.onlineCodeExecution.compilerOptions.codeforces"></span>
    </label>
    <label>
        <input type='radio' name='compiler' value='wandbox'>
        <span class='OJBetter_setting_menu_label_text'
            data-i18n="settings:codeEditor.onlineCodeExecution.compilerOptions.wandbox"></span>
    </label>
    <label>
        <input type='radio' name='compiler' value='rextester'>
        <span class='OJBetter_setting_menu_label_text'
            data-i18n="settings:codeEditor.onlineCodeExecution.compilerOptions.rextester"></span>
    </label>
    <hr>
    <h4 data-i18n="settings:codeEditor.lsp.title"></h4>
    <div class='OJBetter_setting_list'>
        <label for="useLSP"><span data-i18n="settings:codeEditor.lsp.useLSP.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:codeEditor.lsp.useLSP.helpText"></div>
        </div>
        <input type="checkbox" id="useLSP" name="useLSP">
    </div>
    <div class='OJBetter_setting_list'>
        <label for='OJBetter_Bridge_WorkUri'>
            <div style="display: flex;align-items: center;">
                <span class="input_label" data-i18n="settings:codeEditor.lsp.OJBetter_Bridge_WorkUri.label"></span>
            </div>
        </label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:codeEditor.lsp.OJBetter_Bridge_WorkUri.helpText"></div>
        </div>
        <input type='text' id='OJBetter_Bridge_WorkUri' class='no_default'
            require=true data-i18n="[placeholder]settings:codeEditor.lsp.OJBetter_Bridge_WorkUri.placeholder">
    </div>
    <div class='OJBetter_setting_list'>
        <label for='OJBetter_Bridge_SocketUrl'>
            <div style="display: flex;align-items: center;">
                <span class="input_label"
                    data-i18n="settings:codeEditor.lsp.OJBetter_Bridge_SocketUrl.label"></span>
            </div>
        </label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:codeEditor.lsp.OJBetter_Bridge_SocketUrl.helpText"></div>
        </div>
        <input type='text' id='OJBetter_Bridge_SocketUrl' class='no_default'
            require=true data-i18n="[placeholder]settings:codeEditor.lsp.OJBetter_Bridge_SocketUrl.placeholder">
    </div>
    <hr>
    <h4 data-i18n="settings:codeEditor.staticCompletionEnhancement.title"></h4>
    <div class='OJBetter_setting_list'>
        <label for="cppCodeTemplateComplete"><span
                data-i18n="settings:codeEditor.staticCompletionEnhancement.cppCodeTemplateComplete.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:codeEditor.staticCompletionEnhancement.cppCodeTemplateComplete.helpText"></div>
        </div>
        <input type="checkbox" id="cppCodeTemplateComplete" name="cppCodeTemplateComplete">
    </div>
    <hr>
    <h5 data-i18n="settings:codeEditor.staticCompletionEnhancement.customization"></h5>
    <div class='OJBetter_setting_list alert_warn'>
        <div>
            <p data-i18n="settings:codeEditor.staticCompletionEnhancement.performanceWarning"></p>
        </div>
    </div>
    <div id="Complet_config" class="config"></div>
</div>
`;

const preference_settings_HTML = `
<div id="preference-settings" class="settings-page">
    <h3 data-i18n="settings:preference.title"></h3>
    <hr>
    <div class='OJBetter_setting_list' style="display:none;">
        <label for="expandFoldingblocks" data-i18n="settings:basic.expandBlocks"></label>
        <input type="checkbox" id="expandFoldingblocks" name="expandFoldingblocks">
    </div>
    <div class='OJBetter_setting_list' style="display:none;">
        <label for="renderPerfOpt" data-i18n="settings:basic.renderOptimization.label"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:basic.renderOptimization.helpText"></div>
        </div>
        <input type="checkbox" id="renderPerfOpt" name="renderPerfOpt">
    </div>
    <div class='OJBetter_setting_list' style="display:none;">
        <label for="selectElementPerfOpt" data-i18n="settings:basic.selectElementOptimization.label"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:basic.selectElementOptimization.helpText"></div>
        </div>
        <input type="checkbox" id="selectElementPerfOpt" name="selectElementPerfOpt">
    </div>
    <div class='OJBetter_setting_list' style="display:none;">
        <label for="commentPaging" data-i18n="settings:basic.paging.label"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:basic.paging.helpText"></div>
        </div>
        <input type="checkbox" id="commentPaging" name="commentPaging">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="showJumpToLuogu" data-i18n="settings:basic.luoguJump.label"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:basic.luoguJump.helpText"></div>
        </div>
        <input type="checkbox" id="showJumpToLuogu" name="showJumpToLuogu">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="showCF2vjudge" data-i18n="settings:basic.vjudgeJump.label"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:basic.vjudgeJump.helpText"></div>
        </div>
        <input type="checkbox" id="showCF2vjudge" name="showCF2vjudge">
    </div>
    <div class='OJBetter_setting_list' style="display:none;">
        <label for="standingsRecolor" data-i18n="settings:basic.recolor.label"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:basic.recolor.helpText"></div>
        </div>
        <input type="checkbox" id="standingsRecolor" name="standingsRecolor">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="showLoading" data-i18n="settings:preference.loadingInfo.label"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:preference.loadingInfo.helpText" data-i18n-options='{ "scriptName": "${OJBetter.state.name}" }'></div>
        </div>
        <input type="checkbox" id="showLoading" name="showLoading">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="hoverTargetAreaDisplay" data-i18n="settings:preference.targetArea.label"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:preference.targetArea.helpText"></div>
        </div>
        <input type="checkbox" id="hoverTargetAreaDisplay" name="hoverTargetAreaDisplay">
    </div>
    <div class='OJBetter_setting_list'>
        <label for='iconButtonSize'>
            <div style="display: flex;align-items: center;" data-i18n="settings:preference.iconButtonSize.title"></div>
        </label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:preference.iconButtonSize.helpText"></div>
        </div>
        <input type='number' id='iconButtonSize' class='no_default' require=true data-i18n="[placeholder]settings:preference.iconButtonSize.placeholder">
        <span>px</span>
    </div>
</div>
`;

const dev_settings_HTML = `
<div id="dev-settings" class="settings-page">
    <h3 data-i18n="settings:dev.title"></h3>
    <hr>
    <div class='OJBetter_setting_list alert_danger'>
        <div>
            <p data-i18n="[html]settings:dev.notice"></p>
        </div>
    </div>
    <hr>
    <h5 data-i18n="settings:dev.load.title"></h5>
    <div class='OJBetter_setting_list'>
        <label for="notWaiteLoaded"><span data-i18n="settings:dev.load.notWaiteLoaded.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:dev.load.notWaiteLoaded.helpText"></div>
        </div>
        <input type="checkbox" id="notWaiteLoaded" name="notWaiteLoaded">
    </div>
    <hr>
    <h5 data-i18n="settings:dev.l10n.title"></h5>
    <div class='OJBetter_setting_list'>
        <label><span data-i18n="settings:dev.l10n.refreshScrpitCache.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:dev.l10n.refreshScrpitCache.helpText"></div>
        </div>
        <button type="button" id="l10n_refreshScrpitCacheButton" name="l10n_refreshScrpitCacheButton" data-i18n="settings:dev.l10n.refreshScrpitCache.button"></button>
    </div>
    <hr>
    <h5 data-i18n="settings:dev.l10n_web.title"></h5>
    <div class='OJBetter_setting_list'>
        <label><span data-i18n="settings:dev.l10n_web.refreshScrpitCache.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:dev.l10n_web.refreshScrpitCache.helpText"></div>
        </div>
        <button type="button" id="l10n_web_refreshScrpitCacheButton" name="l10n_web_refreshScrpitCacheButton" data-i18n="settings:dev.l10n_web.refreshScrpitCache.button"></button>
    </div>
    <div class='OJBetter_setting_list'>
        <label for="isRuleMarkingEnabled"><span data-i18n="settings:dev.l10n_web.isRuleMarkingEnabled.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:dev.l10n_web.isRuleMarkingEnabled.helpText"></div>
        </div>
        <input type="checkbox" id="isRuleMarkingEnabled" name="isRuleMarkingEnabled">
    </div>
    <hr>
    <h5 data-i18n="settings:dev.indexedDB.title"></h5>
    <div class='OJBetter_setting_list'>
        <label><span data-i18n="settings:dev.indexedDB.clear.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:dev.indexedDB.clear.helpText"></div>
        </div>
        <button type="button" id="indexedDB_clearButton" name="indexedDB_clearButton" data-i18n="settings:dev.indexedDB.clear.button"></button>
    </div>
    <div class='OJBetter_setting_list'>
        <label><span data-i18n="settings:dev.indexedDB.inputOrExport.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:dev.indexedDB.inputOrExport.helpText"></div>
        </div>
        <button type="button" id="indexedDB_exportButton" name="indexedDB_exportButton" data-i18n="settings:dev.indexedDB.inputOrExport.export"></button>
        <button type="button" id="indexedDB_importButton" name="indexedDB_importButton" data-i18n="settings:dev.indexedDB.inputOrExport.import"></button>
    </div>
    <hr>
    <h5 data-i18n="settings:dev.configuration.title"></h5>
    <div class='OJBetter_setting_list'>
        <label><span data-i18n="settings:dev.configuration.clear.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:dev.configuration.clear.helpText"></div>
        </div>
        <button type="button" id="configuration_clearButton" name="configuration_clearButton" data-i18n="settings:dev.configuration.clear.button"></button>
    </div>
    <div class='OJBetter_setting_list'>
        <label><span data-i18n="settings:dev.configuration.inputOrExport.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:dev.configuration.inputOrExport.helpText"></div>
        </div>
        <button type="button" id="configuration_exportButton" name="configuration_exportButton" data-i18n="settings:dev.configuration.inputOrExport.export"></button>
        <button type="button" id="configuration_importButton" name="configuration_importButton" data-i18n="settings:dev.configuration.inputOrExport.import"></button>
    </div>
</div>
`;

const about_settings_HTML = `
<div id="about-settings" class="settings-page">
    <h3 data-i18n="settings:about.title"></h3>
    <hr>
    <div class='versionInfo'>
        <p>${OJBetter.state.name}</p>
        <p><span data-i18n="settings:about.version"></span><span id="nowVersion">${OJBetter.state.version}</span></p>
        <p> @北极小狐 <a target="_blank" href="https://github.com/beijixiaohu/OJBetter">Github</a> 
        <a target="_blank" href="https://greasyfork.org/zh-CN/scripts/465777">GreasyFork</a></p>
    </div>
    <hr>
    <h5 data-i18n="settings:about.update.title"></h5>
    <div id="thanksforDevChannelNotice" class='OJBetter_setting_list alert_info'>
        <div data-i18n="[html]settings:about.update.thanksforDevChannelNotice"} data-i18n-options='{ "scriptName": "${OJBetter.state.name}" }' ></div>
    </div>
    <div class='OJBetter_setting_list'>
        <label for="updateChannel"><span data-i18n="settings:about.update.channel.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:about.update.channel.helpText"></div>
        </div>
        <select id="updateChannel" name="updateChannel">
            <option value="release" data-i18n="settings:about.update.channel.options.release"></option>
            <option value="dev" data-i18n="settings:about.update.channel.options.dev"></option>
        </select>
    </div>
    <div class='OJBetter_setting_list'>
        <label for="updateSource"><span data-i18n="settings:about.update.source.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:about.update.source.helpText"></div>
        </div>
        <select id="updateSource" name="updateSource">
            <option value="greasyfork" data-i18n="settings:about.update.source.options.greasyfork"></option>
            <option value="github" data-i18n="settings:about.update.source.options.github"></option>
            <option value="aliyunoss" data-i18n="settings:about.update.source.options.aliyunoss"></option>
        </select>
    </div>
</div>
`;

const OJBetter_setting_content_HTML = `
<div class="OJBetter_setting_content">
    ${basic_settings_HTML}
    ${translation_settings_HTML}
    ${clist_rating_settings_HTML}
    ${code_editor_settings_HTML}
    ${preference_settings_HTML}
    ${dev_settings_HTML}
    ${about_settings_HTML}
</div>
`;

// 设置界面HTML
const OJBetterSettingMenu_HTML = `
    <dialog class='OJBetter_setting_menu' id='OJBetter_setting_menu'>
        <div class="tool-box">
            <button class='ojb_btn ojb_btn_popover top btn-close'>
                <i class="iconfont">&#xe614;</i>
            </button>
        </div>
        <div class="OJBetter_setting_container">
            ${OJBetter_setting_sidebar_HTML}
            ${OJBetter_setting_content_HTML}
        </div>
    </dialog>
`;

const apiCustomConfigHTML = (prefix) => {
    return `
    <div class="OJBetter_setting_list">
        <label for='${prefix}_header'>
            <div style="display: flex;align-items: center;">
                <span class="input_label" data-i18n="config:common.advanced.header.label"></span>
                <div class="help_tip">
                    ${helpCircleHTML}
                    <div class="tip_text" data-i18n="[html]config:common.advanced.header.tipText"></div>
                </div>
            </div>
        </label>
        <textarea id="${prefix}_header" placeholder='' require = false data-i18n="[placeholder]config:common.advanced.header.placeholder"></textarea>
    </div>
    <div class="OJBetter_setting_list">
        <label for='${prefix}_data'>
            <div style="display: flex;align-items: center;">
                <span class="input_label" data-i18n="config:common.advanced.data.label"></span>
                <div class="help_tip">
                    ${helpCircleHTML}
                    <div class="tip_text" data-i18n="[html]config:common.advanced.data.tipText"></div>
                </div>
            </div>
        </label>
        <textarea id="${prefix}_data" placeholder='' require = false data-i18n="[placeholder]config:common.advanced.data.placeholder"></textarea>
    </div>
    `;
};

const apiQuotaConfigHTML = (prefix) => {
    return `
    <div class="OJBetter_setting_list">
        <label for='${prefix}_quota_url'>
        <div style="display: flex;align-items: center;">
                <span class="input_label" data-i18n="config:common.quota.url.label"></span>
                <div class="help_tip">
                    ${helpCircleHTML}
                    <div class="tip_text" data-i18n="[html]config:common.quota.url.tipText"></div>
                </div>
            </div>
        </label>
        <input type='text' id='${prefix}_quota_url' class='no_default' placeholder='' require = true data-i18n="[placeholder]config:common.quota.url.placeholder">
    </div>
    <div class="OJBetter_setting_list">
        <label for="${prefix}_quota_method" style="display: flex;" data-i18n="config:common.quota.method.label"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]config:common.quota.method.tipText"></div>
        </div>
        <select id="${prefix}_quota_method" name="${prefix}_quota_method">
            <option value="get">GET</option>
            <option value="post">POST</option>
        </select>
    </div>
    <div class="OJBetter_setting_list">
        <label for='${prefix}_quota_header'>
            <div style="display: flex;align-items: center;">
                <span class="input_label" data-i18n="config:common.quota.header.label"></span>
                <div class="help_tip">
                    ${helpCircleHTML}
                    <div class="tip_text" data-i18n="[html]config:common.quota.header.tipText"></div>
                </div>
            </div>
        </label>
        <textarea id="${prefix}_quota_header" placeholder='' require = false data-i18n="[placeholder]config:common.quota.header.placeholder"></textarea>
    </div>
    <div class="OJBetter_setting_list">
        <label for='${prefix}_quota_data'>
            <div style="display: flex;align-items: center;">
                <span class="input_label" data-i18n="config:common.quota.data.label"></span>
                <div class="help_tip">
                    ${helpCircleHTML}
                    <div class="tip_text" data-i18n="[html]config:common.quota.data.tipText"></div>
                </div>
            </div>
        </label>
        <textarea id="${prefix}_quota_data" placeholder='' require = false data-i18n="[placeholder]config:common.quota.data.placeholder"></textarea>
    </div>
    <div class="OJBetter_setting_list">
        <div style="display: flex;align-items: center;">
                <span class="input_label" data-i18n="config:common.quota.surplus.label"></span>
                <div class="help_tip">
                    ${helpCircleHTML}
                    <div class="tip_text" data-i18n="[html]config:common.quota.surplus.tipText"></div>
                </div>
            </div>
        </label>
        <input type='text' id='${prefix}_quota_surplus' class='no_default' placeholder='' require = true data-i18n="[placeholder]config:common.quota.surplus.placeholder">
    </div>
    `;
}

const deeplConfigEditHTML = `
    <dialog class='OJBetter_setting_menu' id='config_edit_menu'>
    <div class='OJBetter_setting_content'>
        <div class="tool-box">
            <button class='ojb_btn ojb_btn_popover top btn-close'>
                <i class="iconfont">&#xe614;</i>
            </button>
        </div>
        <h4 data-i18n="config:deepl.title"></h4>
        <h5 data-i18n="config:deepl.basic.title"></h5>
        <hr>
        <div class="OJBetter_setting_list">
            <label for='name'>
                <span class="input_label" data-i18n="config:deepl.basic.name.label"></span>
            </label>
            <input type='text' id='name' class='no_default' placeholder='' require = true data-i18n="[placeholder]config:deepl.basic.name.placeholder">
        </div>
        <div class='OJBetter_setting_list'>
            <label for="deepl_apiGenre" style="display: flex;" data-i18n="config:deepl.genre.label"></label>
            <div class="help_tip">
                ${helpCircleHTML}
                <div class="tip_text" data-i18n="[html]config:deepl.genre.tipText"></div>
            </div>
            <select id="deepl_apiGenre" name="deepl_apiGenre">
                <option value="api-free">api-free</option>
                <option value="api-pro">api-pro</option>
                <option value="deeplx">deeplx</option>
            </select>
        </div>
        <div class="OJBetter_setting_list">
            <label for='deepl_key'>
                <div style="display: flex;align-items: center;">
                    <span class="input_label" data-i18n="config:deepl.basic.key.label"></span>
                    <div class="help_tip">
                        ${helpCircleHTML}
                        <div class="tip_text" data-i18n="[html]config:deepl.basic.key.tipText"></div>
                    </div>
                </div>
            </label>
            <input type='text' id='deepl_key' class='no_default' placeholder='' require = true data-i18n="[placeholder]config:deepl.basic.key.placeholder">
        </div>
        <div class="OJBetter_setting_list">
            <label for='deepl_proxy'>
                <div style="display: flex;align-items: center;">
                    <span class="input_label" data-i18n="config:deepl.basic.proxy.label">Proxy API:</span>
                    <div class="help_tip">
                        ${helpCircleHTML}
                        <div class="tip_text" data-i18n="[html]config:deepl.basic.proxy.tipText"></div>
                    </div>
                </div>
            </label>
            <input type='text' id='deepl_proxy' placeholder='' require = false>
        </div>
        <hr>
        <details>
            <summary data-i18n="config:common.advanced.title"></summary>
            ${apiCustomConfigHTML('deepl')}
        </details>
        <details>
            <summary data-i18n="config:common.quota.title"></summary>
            ${apiQuotaConfigHTML('deepl')}
        </details>
        <button id='tempConfig_save' data-i18n="common:save"></button>
    </div>
    </dialog>
`;

const chatgptConfigEditHTML = `
    <dialog class='OJBetter_setting_menu' id='config_edit_menu'>
    <div class='OJBetter_setting_content'>
        <div class="tool-box">
            <button class='ojb_btn ojb_btn_popover top btn-close'>
                <i class="iconfont">&#xe614;</i>
            </button>
        </div>
        <h4 data-i18n="config:chatgpt.title"></h4>
        <h5 data-i18n="config:chatgpt.basic.title"></h5>
        <hr>
        <div class="OJBetter_setting_list">
            <label for='name'>
                <span class="input_label" data-i18n="config:chatgpt.basic.name.label"></span>
            </label>
            <input type='text' id='name' class='no_default' placeholder='' require = true data-i18n="[placeholder]config:chatgpt.basic.name.placeholder">
        </div>
        <div class="OJBetter_setting_list">
            <label for='chatgpt_model'>
                <div style="display: flex;align-items: center;">
                    <span class="input_label" data-i18n="[html]config:chatgpt.basic.model.label"></span>
                    <div class="help_tip">
                        ${helpCircleHTML}
                        <div class="tip_text" data-i18n="[html]config:chatgpt.basic.model.tipText"></div>
                    </div>
                </div>
            </label>
            <input type='text' id='chatgpt_model' placeholder='gpt-3.5-turbo' require = false>
        </div>
        <div class="OJBetter_setting_list">
            <label for='chatgpt_key'>
                <div style="display: flex;align-items: center;">
                    <span class="input_label" data-i18n="config:chatgpt.basic.key.label"></span>
                    <div class="help_tip">
                        ${helpCircleHTML}
                        <div class="tip_text" data-i18n="[html]config:chatgpt.basic.key.tipText"></div>
                    </div>
                </div>
            </label>
            <input type='text' id='chatgpt_key' class='no_default' placeholder='' require = true data-i18n="[placeholder]config:chatgpt.basic.key.placeholder">
        </div>
        <div class="OJBetter_setting_list">
            <label for='chatgpt_proxy'>
                <div style="display: flex;align-items: center;">
                    <span class="input_label" data-i18n="config:chatgpt.basic.proxy.label">Proxy API:</span>
                    <div class="help_tip">
                        ${helpCircleHTML}
                        <div class="tip_text" data-i18n="[html]config:chatgpt.basic.proxy.tipText"></div>
                    </div>
                </div>
            </label>
            <input type='text' id='chatgpt_proxy' placeholder='https://api.openai.com/v1/chat/completions' require = false>
        </div>
        <hr>
        <details>
            <summary data-i18n="config:common.advanced.title"></summary>
            ${apiCustomConfigHTML('chatgpt')}
        </details>
        <details>
            <summary data-i18n="config:common.quota.title"></summary>
            ${apiQuotaConfigHTML('chatgpt')}
        </details>
        <button id='tempConfig_save' data-i18n="common:save"></button>
    </div>
    </dialog>
`;

const CompletConfigEditHTML = `
    <dialog class='OJBetter_setting_menu' id='config_edit_menu'>
    <div class='OJBetter_setting_content'>
        <div class="tool-box">
            <button class='ojb_btn ojb_btn_popover top btn-close'>
                <i class="iconfont">&#xe614;</i>
            </button>
        </div>
        <h4 data-i18n="config:complet.title"></h4>
        <hr>
        <div class="OJBetter_setting_list">
            <label for='name'>
                <span class="input_label" data-i18n="config:complet.name.label"></span>
            </label>
            <input type='text' id='name' class='no_default' placeholder='' require = true  data-i18n="[placeholder]config:complet.name.placeholder">
        </div>
        <div class='OJBetter_setting_list'>
            <label for="complet_isChoose"><span id="loaded_span" data-i18n="config:complet.choose.label"></span></label>
            <input type="checkbox" id="complet_isChoose" name="complet_isChoose" require = false>
        </div>
        <div class='OJBetter_setting_list'>
            <label for="complet_genre" style="display: flex;" data-i18n="config:complet.genre.label"></label>
            <div class="help_tip">
                ${helpCircleHTML}
                <div class="tip_text" data-i18n="[html]config:complet.genre.tipText"></div>
            </div>
            <select id="complet_genre" name="complet_genre">
                <option value="monaco">monaco</option>
                <option value="ace">ace</option>
            </select>
        </div>
        <div class='OJBetter_setting_list'>
            <label for="complet_language" style="display: flex;" data-i18n="config:complet.language.label"></label>
            <select id="complet_language" name="complet_language">
                <option value="cpp">cpp</option>
                <option value="python">python</option>
                <option value="java">java</option>
                <option value="c">c</option>
            </select>
        </div>
        <div class="OJBetter_setting_list">
            <label for='complet_jsonUrl'>
                <div style="display: flex;align-items: center;">
                    <span class="input_label">JSON URL:</span>
                    <div class="help_tip">
                        ${helpCircleHTML}
                        <div class="tip_text" data-i18n="[html]config:complet.jsonurl.tipText"></div>
                    </div>
                </div>
            </label>
            <div class='OJBetter_setting_list alert_warn' data-i18n="[html]config:complet.jsonurl.alert"></div>
            <input type='text' id='complet_jsonUrl' class='no_default' placeholder='' require = true data-i18n="[placeholder]config:complet.jsonurl.placeholder">
        </div>
        <button id='tempConfig_save' data-i18n="common:save"></button>
    </div>
    </dialog>
`;

/**
 * 加载设置按钮面板
 */
async function initSettingsPanel() {
    /**
     * 添加右上角设置按钮
     * @param {string} location 位置选择器
     * @param {string} method 插入方法
     */
    function insertOJBetterSettingButton(location, method) {
        $(location)[method](`<button class='ojb_btn OJBetter_setting'>
        ${OJBetter.state.name} ${i18next.t('settings', { ns: 'common' })}</button>`);
    }

    /**
     * ============================================
     * 该网站插入设置按钮的位置和方式
     */
    if (OJBetter.typeOfPage.isEnglishLanguage) {
        insertOJBetterSettingButton("#navbar-collapse > ul:nth-child(2) > li:last-child", "after");
    } else {
        if ($('.header-mypage').length > 0) insertOJBetterSettingButton(".header-mypage", "after");
        else insertOJBetterSettingButton("#navbar-collapse > ul:nth-child(2) > li:last-child", "after");
    }
    /**
     * ============================================
     */

    const $settingBtns = $(".OJBetter_setting");
    $settingBtns.click(() => {
        $settingBtns.prop("disabled", true).addClass("open");

        // 设置面板div
        const settingMenu = OJB_safeCreateJQElement(OJBetterSettingMenu_HTML);
        $("body").append(settingMenu);

        elementLocalize(settingMenu); // 加载i18n
        OJB_showModal(settingMenu);
        OJB_addDraggable($('#OJBetter_setting_menu')); // 窗口支持拖拽

        // help帮助悬浮窗位置更新
        $(document).on('mouseenter', '.help-icon', function (event) {
            var menuOffset = $('.OJBetter_setting_menu:last').offset();
            var mouseX = event.pageX - menuOffset.left;
            var mouseY = event.pageY - menuOffset.top;

            $('.tip_text').css({
                'top': mouseY + 'px',
                'left': mouseX + 'px'
            });
        });

        // 选项卡切换
        $('.OJBetter_setting_sidebar a').click(function (event) {
            event.preventDefault();
            $('.OJBetter_setting_sidebar a').removeClass('active');
            $('.settings-page').removeClass('active');
            $(this).addClass('active');
            const targetPageId = $(this).attr('href').substring(1);
            $('#' + targetPageId).addClass('active');
        });

        /**
         * 更新单选按钮组的可用状态
         * @param {string} selector 单选按钮组的选择器
         * @param {string} targetLanguage 目标语言
         * @param {Object} translationSupport 翻译支持的语言对应表
         */
        const updateRadioButtonsAvailability = (selector, targetLanguage) => {
            Object.entries(OJBetter.supportList.translationSupport).forEach(([service, languages]) => {
                const radioButton = $(selector).find(`input[value="${service}"]`);
                const isEnabled = languages[targetLanguage];
                $(radioButton).prop('disabled', !isEnabled);
                if (!isEnabled) {
                    $(radioButton).prop('checked', false);
                }
            });
        };

        /**
         * 检查下拉框选中项是否有效,若无效则清空
         * @param {string} selector 下拉框的选择器
         */
        const validateSelectOption = (selector) => {
            const selectedValue = $(selector).val();
            if (!selectedValue) {
                $(selector).val('');
            }
        };

        /**
         * 更新翻译目标语言下拉框的可用状态
         * @param {string} selector 下拉框的选择器
         * @param {string} targetLanguage 目标语言
         * @param {Object} translationSupport 翻译支持的语言对应表
         */
        const updateSelectOptionsAvailability = (selector, targetLanguage) => {
            $(selector).children('option').each(function () {
                const optionValue = $(this).val();
                const isEnabled = OJBetter.supportList.translationSupport[optionValue] ? OJBetter.supportList.translationSupport[optionValue][targetLanguage] : true;
                $(this).prop('disabled', !isEnabled);
            });
            validateSelectOption(selector);
        };

        /**
         * 更新翻译服务复选框的可用状态
         * @param {string} selector 复选框的选择器
         * @param {string} targetLanguage 目标语言
         * @param {Object} translationSupport 翻译支持的语言对应表
         */
        const updateCheckboxesAvailability = (selector, targetLanguage) => {
            $(selector).children('input').each(function () {
                const checkboxValue = $(this).val();
                const isEnabled = OJBetter.supportList.translationSupport[checkboxValue][targetLanguage];
                $(this).prop('disabled', !isEnabled);
                if (!isEnabled) {
                    $(this).prop('checked', false);
                }
            });
        };

        /**
         * 更新更新源下拉框的可用状态
         * @param {string} selector 下拉框的选择器
         * @param {string} targetLanguage 目标语言
         * @param {Object} translationSupport 翻译支持的语言对应表
         */
        const updateUpdateSourceSelectOptionsAvailability = (selector, updateChannel) => {
            $(selector).children('option').each(function () {
                const optionValue = $(this).val();
                const isEnabled = OJBetter.supportList.updateSourceSupportList[optionValue][updateChannel];
                $(this).prop('disabled', !isEnabled);
            });
            validateSelectOption(selector);
        };

        /**
         * 创建配置结构
         * @param {string} type - 该字段的在表单中的类型
         * @param {string} value - 在配置中的键值
         * @param {boolean} require - 是否是表单的必填项
         * @param {string} [check=""] check - 调用的合法性检查
         */
        function createStructure(type, value, require, check = "") {
            return { type, value, require, check };
        }

        // deepl配置
        const deeplStructure = {
            '#name': createStructure('text', 'name', true),
            '#deepl_apiGenre': createStructure('text', 'apiGenre', true),
            '#deepl_key': createStructure('text', 'key', false),
            '#deepl_proxy': createStructure('text', 'proxy', false),
            '#deepl_header': createStructure('text', '_header', false, 'keyValuePairs'),
            '#deepl_data': createStructure('text', '_data', false, 'keyValuePairs'),
            '#deepl_quota_url': createStructure('text', 'quota_url', false),
            '#deepl_quota_method': createStructure('text', 'quota_method', false),
            '#deepl_quota_header': createStructure('text', 'quota_header', false, 'keyValuePairs'),
            '#deepl_quota_data': createStructure('text', 'quota_data', false, 'keyValuePairs'),
            '#deepl_quota_surplus': createStructure('text', 'quota_surplus', false, 'dotSeparatedPath'),
        };
        let tempConfig_deepl = GM_getValue('deepl_config'); // 获取配置信息
        const configManager_deepl = new ConfigManager('#deepl_config', 'deepl_config_', tempConfig_deepl, deeplStructure, deeplConfigEditHTML);
        configManager_deepl.registerChoiceChange();

        // chatgpt配置
        const chatgptStructure = {
            '#name': createStructure('text', 'name', true),
            '#chatgpt_model': createStructure('text', 'model', false),
            '#chatgpt_key': createStructure('text', 'key', true),
            '#chatgpt_proxy': createStructure('text', 'proxy', false),
            '#chatgpt_header': createStructure('text', '_header', false, 'keyValuePairs'),
            '#chatgpt_data': createStructure('text', '_data', false, 'keyValuePairs'),
            '#chatgpt_quota_url': createStructure('text', 'quota_url', false),
            '#chatgpt_quota_header': createStructure('text', 'quota_header', false, 'keyValuePairs'),
            '#chatgpt_quota_data': createStructure('text', 'quota_data', false, 'keyValuePairs'),
            '#chatgpt_quota_surplus': createStructure('text', 'quota_surplus', false, 'dotSeparatedPath'),
            '#chatgpt_quota_method': createStructure('text', 'quota_method', false),
        };
        let tempConfig_chatgpt = GM_getValue('chatgpt_config'); // 获取配置信息
        const configManager_chatgpt = new ConfigManager('#chatgpt_config', 'chatgpt_config_', tempConfig_chatgpt, chatgptStructure, chatgptConfigEditHTML);
        configManager_chatgpt.registerChoiceChange();

        // Complet配置
        const CompletStructure = {
            '#name': createStructure('text', 'name', true),
            '#complet_isChoose': createStructure('checkbox', 'isChoose', true),
            '#complet_genre': createStructure('text', 'genre', true),
            '#complet_language': createStructure('text', 'language', true),
            '#complet_jsonUrl': createStructure('text', 'jsonUrl', true)
        };
        let tempConfig_Complet = GM_getValue('Complet_config'); // 获取配置信息
        const configManager_complet = new ConfigManager('#Complet_config', 'complet_config_', tempConfig_Complet, CompletStructure, CompletConfigEditHTML, false);

        // 状态更新
        $("input[name='darkMode'][value='" + OJBetter.basic.darkMode + "']").prop("checked", true);
        $("#showLoading").prop("checked", GM_getValue("showLoading") === true);
        $("#expandFoldingblocks").prop("checked", GM_getValue("expandFoldingblocks") === true);
        $("#renderPerfOpt").prop("checked", GM_getValue("renderPerfOpt") === true);
        $("#selectElementPerfOpt").prop("checked", GM_getValue("selectElementPerfOpt") === true);
        $("#commentPaging").prop("checked", GM_getValue("commentPaging") === true);
        $("#standingsRecolor").prop("checked", GM_getValue("standingsRecolor") === true);
        $("#showJumpToLuogu").prop("checked", GM_getValue("showJumpToLuogu") === true);
        $("#showCF2vjudge").prop("checked", GM_getValue("showCF2vjudge") === true);
        $("#hoverTargetAreaDisplay").prop("checked", GM_getValue("hoverTargetAreaDisplay") === true);
        $("#showClistRating_contest").prop("checked", GM_getValue("showClistRating_contest") === true);
        $("#showClistRating_problemset").prop("checked", GM_getValue("showClistRating_problemset") === true);
        $("#showClistRating_problem").prop("checked", GM_getValue("showClistRating_problem") === true);
        $("#RatingHidden").prop("checked", GM_getValue("RatingHidden") === true);
        $('#scriptL10nLanguage').val(GM_getValue("scriptL10nLanguage"));
        $('#localizationLanguage').val(GM_getValue("localizationLanguage"));
        $("input[name='translation'][value='" + OJBetter.translation.choice + "']").prop("checked", true);
        $("input[name='translation']").css("color", "gray");
        $('#deepl_type').val(GM_getValue("deepl_type"));
        $("#deepl_config_config_bar_ul").find(`input[name='deepl_config_config_item'][value='${tempConfig_deepl.choice}']`).prop("checked", true);
        $('#enableEmphasisProtection').prop("checked", GM_getValue("enableEmphasisProtection") === true);
        $('#enableLinkProtection').prop("checked", GM_getValue("enableLinkProtection") === true);
        $("#chatgpt_config_config_bar_ul").find(`input[name='chatgpt_config_config_item'][value='${tempConfig_chatgpt.choice}']`).prop("checked", true);
        $("#openai_isStream").prop("checked", GM_getValue("openai_isStream") === true);
        $("#openai_asSystemPrompt").prop("checked", GM_getValue("openai_asSystemPrompt") === true);
        $('#openai_customPrompt').val(GM_getValue("openai_customPrompt"));
        $('#comment_translation_choice').val(GM_getValue("commentTranslationChoice"));
        $('#iconButtonSize').val(GM_getValue("iconButtonSize"));
        $("#autoTranslation").prop("checked", GM_getValue("autoTranslation") === true);
        $('#shortTextLength').val(GM_getValue("shortTextLength"));
        $("#allowMixTrans").prop("checked", GM_getValue("allowMixTrans") === true);
        $('.OJBetter_checkboxs').find('input[type="checkbox"][name="mixedTranslation"]').each(function () {
            if (OJBetter.translation.auto.mixTrans.servers.indexOf($(this).val()) > -1) {
                $(this).prop('checked', true);
            }
        });
        // 翻译目标语言下拉框
        $('#transTargetLang').change(function () {
            var selectedLang = $(this).val();
            updateRadioButtonsAvailability('#translationServices', selectedLang);
            updateSelectOptionsAvailability('#comment_translation_choice', selectedLang);
            updateCheckboxesAvailability('.OJBetter_checkboxs', selectedLang);
        });
        $('#transTargetLang').val(GM_getValue("transTargetLang"));
        $('#transTargetLang').change();
        // 
        $('#comment_translation_mode').val(GM_getValue("commentTranslationMode"));
        $("#memoryTranslateHistory").prop("checked", GM_getValue("memoryTranslateHistory") === true);
        $('#transWaitTime').val(GM_getValue("transWaitTime"));
        $('#translation_replaceSymbol').val(GM_getValue("replaceSymbol"));
        $("#filterTextWithoutEmphasis").prop("checked", GM_getValue("filterTextWithoutEmphasis") === true);
        $('#translation_retransAction').val(GM_getValue("retransAction"));
        $("#clist_Authorization").val(GM_getValue("clist_Authorization"));
        $("#problemPageCodeEditor").prop("checked", GM_getValue("problemPageCodeEditor") === true);
        $("#beautifyPreBlocks").prop("checked", GM_getValue("beautifyPreBlocks") === true);
        $("#isCodeSubmitConfirm").prop("checked", GM_getValue("isCodeSubmitConfirm") === true);
        $("#autoSubmitAfterPass").prop("checked", GM_getValue("autoSubmitAfterPass") === true);
        $("#alwaysConsumeMouseWheel").prop("checked", GM_getValue("alwaysConsumeMouseWheel") === true);
        $("#autoMemoryCode").prop("checked", GM_getValue("autoMemoryCode") === true);
        $("#submitButtonPosition").val(GM_getValue("submitButtonPosition"));
        $("#cppCodeTemplateComplete").prop("checked", GM_getValue("cppCodeTemplateComplete") === true);
        $("#useLSP").prop("checked", GM_getValue("useLSP") === true);
        $("#OJBetter_Bridge_WorkUri").val(GM_getValue("OJBetter_Bridge_WorkUri"));
        $("#OJBetter_Bridge_SocketUrl").val(GM_getValue("OJBetter_Bridge_SocketUrl"));
        $("input[name='compiler'][value='" + OJBetter.monaco.onlineCompilerChoice + "']").prop("checked", true);
        $("input[name='compiler']").css("color", "gray");
        // 调试
        $("#notWaiteLoaded").prop("checked", GM_getValue("notWaiteLoaded") === true);
        $('#l10n_refreshScrpitCacheButton').click(clearI18nextCache);
        $('#l10n_web_refreshScrpitCacheButton').click(clearWebsiteL10nData);
        $("#isRuleMarkingEnabled").prop("checked", GM_getValue("isRuleMarkingEnabled") === true);
        $('#indexedDB_clearButton').click(async () => { await clearDatabase(); });
        $('#indexedDB_exportButton').click(async () => { downloadDataAsFile(await exportDatabase(), 'database_export.json') });
        $('#indexedDB_importButton').click(() => { readFileInput(async (fileContent) => { await importDatabase(fileContent); }); });
        $('#configuration_clearButton').click(deleteAllConfigSettings);
        $('#configuration_exportButton').click(() => { downloadDataAsFile(exportSettingsToJSON(), 'configuration_export.json') });
        $('#configuration_importButton').click(() => { readFileInput((fileContent) => { importSettingsFromJSON(fileContent); }) });
        // 关于
        $('#updateChannel').val(GM_getValue("updateChannel"));
        $('#updateSource').val(GM_getValue("updateSource"));
        $('#updateChannel').change(function () {
            var selectedLang = $(this).val();
            updateUpdateSourceSelectOptionsAvailability('#updateSource', selectedLang);
            if (selectedLang == "dev") $('#thanksforDevChannelNotice').show();
            else $('#thanksforDevChannelNotice').hide();
        });
        $('#updateChannel').change();

        // 关闭
        const $settingMenu = $(".OJBetter_setting_menu");
        $settingMenu.on("click", ".btn-close", async () => {
            // 设置的数据
            const settings = {
                darkMode: $("input[name='darkMode']:checked").val(),
                showLoading: $("#showLoading").prop("checked"),
                hoverTargetAreaDisplay: $("#hoverTargetAreaDisplay").prop("checked"),
                expandFoldingblocks: $("#expandFoldingblocks").prop("checked"),
                renderPerfOpt: $("#renderPerfOpt").prop("checked"),
                selectElementPerfOpt: $("#selectElementPerfOpt").prop("checked"),
                commentPaging: $("#commentPaging").prop("checked"),
                standingsRecolor: $("#standingsRecolor").prop("checked"),
                showJumpToLuogu: $("#showJumpToLuogu").prop("checked"),
                showCF2vjudge: $("#showCF2vjudge").prop("checked"),
                scriptL10nLanguage: $('#scriptL10nLanguage').val(),
                localizationLanguage: $('#localizationLanguage').val(),
                transTargetLang: $('#transTargetLang').val(),
                translation: $("input[name='translation']:checked").val(),
                deepl_type: $('#deepl_type').val(),
                enableEmphasisProtection: $("#enableEmphasisProtection").prop("checked"),
                enableLinkProtection: $("#enableLinkProtection").prop("checked"),
                openai_isStream: $("#openai_isStream").prop("checked"),
                openai_asSystemPrompt: $("#openai_asSystemPrompt").prop("checked"),
                openai_customPrompt: $('#openai_customPrompt').val(),
                commentTranslationChoice: $('#comment_translation_choice').val(),
                iconButtonSize: $('#iconButtonSize').val(),
                autoTranslation: $("#autoTranslation").prop("checked"),
                shortTextLength: $('#shortTextLength').val(),
                allowMixTrans: $("#allowMixTrans").prop("checked"),
                mixedTranslation: (() => {
                    let mixedTranslation = [];
                    $('.OJBetter_checkboxs').find('input[type="checkbox"][name="mixedTranslation"]').each(function () {
                        if ($(this).is(":checked")) {
                            mixedTranslation.push($(this).val());
                        }
                    });
                    return mixedTranslation;
                })(),
                commentTranslationMode: $('#comment_translation_mode').val(),
                memoryTranslateHistory: $('#memoryTranslateHistory').prop("checked"),
                transWaitTime: $('#transWaitTime').val(),
                replaceSymbol: $('#translation_replaceSymbol').val(),
                filterTextWithoutEmphasis: $('#filterTextWithoutEmphasis').prop("checked"),
                retransAction: $('#translation_retransAction').val(),
                showClistRating_contest: $('#showClistRating_contest').prop("checked"),
                showClistRating_problemset: $('#showClistRating_problemset').prop("checked"),
                showClistRating_problem: $('#showClistRating_problem').prop("checked"),
                RatingHidden: $('#RatingHidden').prop("checked"),
                clist_Authorization: $('#clist_Authorization').val(),
                problemPageCodeEditor: $("#problemPageCodeEditor").prop("checked"),
                beautifyPreBlocks: $("#beautifyPreBlocks").prop("checked"),
                isCodeSubmitConfirm: $("#isCodeSubmitConfirm").prop("checked"),
                autoSubmitAfterPass: $("#autoSubmitAfterPass").prop("checked"),
                alwaysConsumeMouseWheel: $("#alwaysConsumeMouseWheel").prop("checked"),
                autoMemoryCode: $("#autoMemoryCode").prop("checked"),
                submitButtonPosition: $('#submitButtonPosition').val(),
                cppCodeTemplateComplete: $("#cppCodeTemplateComplete").prop("checked"),
                useLSP: $("#useLSP").prop("checked"),
                OJBetter_Bridge_WorkUri: $('#OJBetter_Bridge_WorkUri').val().replace(/\\/g, '/').replace(/\/$/, ''),
                OJBetter_Bridge_SocketUrl: $('#OJBetter_Bridge_SocketUrl').val(),
                onlineCompilerChoice: $("input[name='compiler']:checked").val(),
                notWaiteLoaded: $("#notWaiteLoaded").prop("checked"),
                isRuleMarkingEnabled: $("#isRuleMarkingEnabled").prop("checked"),
                updateChannel: $('#updateChannel').val(),
                updateSource: $('#updateSource').val()
            };
            // tempConfigs的数据
            const tempConfigs = {
                'deepl_config': configManager_deepl.getTempConfig(),
                'chatgpt_config': configManager_chatgpt.getTempConfig(),
                'Complet_config': configManager_complet.getTempConfig()
            }

            // 判断是否改变
            let changes = {};
            const combinedConfigs = Object.assign({}, settings, tempConfigs); // 合并settings和tempConfigs对象
            for (const [key, value] of Object.entries(combinedConfigs)) {
                const storedValue = GM_getValue(key);
                if (!OJB_deepEquals(value, storedValue)) {
                    changes[key] = { oldValue: storedValue, newValue: value };
                }
            }

            // 如果changes对象不为空,则有变化
            if (Object.keys(changes).length > 0) {
                console.log("Changes detected:", changes);
                const shouldSave = await OJB_createDialog(
                    i18next.t('saveSetting.title', { ns: 'dialog' }),
                    i18next.t('saveSetting.content', { ns: 'dialog' }),
                    [
                        i18next.t('saveSetting.buttons.0', { ns: 'dialog' }),
                        i18next.t('saveSetting.buttons.1', { ns: 'dialog' })
                    ]
                ); // 配置改变保存确认
                if (shouldSave) {
                    // 数据校验
                    // TODO
                    if (settings.deepl_type !== 'free') {
                        let selectedIndex = $('input[name="deepl_config_config_item"]:checked').length > 0;
                        if (!selectedIndex) {
                            $('.deepl_config a').removeClass('active');
                            $('.settings-page').removeClass('active');
                            $('#sidebar-translation-settings').addClass('active');
                            $('#translation-settings').addClass('active');

                            $('#deepl_config').addClass('missing');
                            return;
                        } else {
                            $('#deepl_config').removeClass('missing');
                        }
                    }
                    if (settings.translation === "openai") {
                        let selectedIndex = $('input[name="chatgpt_config_config_item"]:checked').length > 0;
                        if (!selectedIndex) {
                            $('.chatgpt_config a').removeClass('active');
                            $('.settings-page').removeClass('active');
                            $('#sidebar-translation-settings').addClass('active');
                            $('#translation-settings').addClass('active');

                            $('#chatgpt_config').addClass('missing');
                            return;
                        } else {
                            $('#chatgpt_config').removeClass('missing');
                        }
                    }
                    {
                        let selectedIndex = $('input[name="translation"]:checked').length > 0;
                        if (!selectedIndex) {
                            $('.OJBetter_setting_sidebar a').removeClass('active');
                            $('.settings-page').removeClass('active');
                            $('#sidebar-translation-settings').addClass('active');
                            $('#translation-settings').addClass('active');

                            $('#translationServices').addClass('missing');
                            return;
                        } else {
                            $('#translationServices').removeClass('missing');
                        }
                    }

                    // 保存数据
                    let refreshPage = false; // 是否需要刷新页面
                    for (const [key, value] of Object.entries(settings)) {
                        if (!refreshPage && !(key == 'translation' || key == 'darkMode' || key == 'commentTranslationChoice')) {
                            if (GM_getValue(key) != value) refreshPage = true;
                        }
                        GM_setValue(key, value);
                    }
                    for (const [key, value] of Object.entries(tempConfigs)) {
                        if (!refreshPage && (JSON.stringify(GM_getValue(key)) != JSON.stringify(value))) refreshPage = true;
                        GM_setValue(key, value);
                    }

                    if (refreshPage) location.reload();
                    else {
                        // 切换黑暗模式
                        if (OJBetter.basic.darkMode != settings.darkMode) {
                            OJBetter.basic.darkMode = settings.darkMode;
                            // 移除旧的事件监听器
                            changeEventListeners.forEach(listener => {
                                mediaQueryList.removeEventListener('change', listener);
                            });

                            if (OJBetter.basic.darkMode == "follow") {
                                changeEventListeners.push(handleColorSchemeChange);
                                mediaQueryList.addEventListener('change', handleColorSchemeChange);
                                $('html').removeAttr('data-theme');
                            } else if (OJBetter.basic.darkMode == "dark") {
                                $('html').attr('data-theme', 'dark');
                                if (OJBetter.monaco.editor) {
                                    monaco.editor.setTheme('vs-dark');
                                }
                            } else {
                                $('html').attr('data-theme', 'light');
                                if (OJBetter.monaco.editor) {
                                    monaco.editor.setTheme('vs');
                                }
                                // 移除旧的事件监听器
                                const mediaQueryList = window.matchMedia('(prefers-color-scheme: dark)');
                                window.matchMedia('(prefers-color-scheme: dark)');
                            }
                        }
                        // 更新配置信息
                        OJBetter.translation.choice = settings.translation;
                        OJBetter.translation.comment.choice = settings.commentTranslationChoice;
                    }
                }
            }
            OJB_closeAndRemoveModal(settingMenu);
            $settingBtns.prop("disabled", false).removeClass("open");
        });
    });
};

/**
 * 初始化html2markdown转换器
 */
async function initHTML2MarkDown() {
    OJBetter.common.turndownService = new TurndownService({ bulletListMarker: '-' });

    // 保留原始
    OJBetter.common.turndownService.keep(['del']);

    OJBetter.common.turndownService.addRule('removeByClass', {
        filter: function (node) {
            return node.classList.contains('html2md-panel') ||
                node.classList.contains('div-btn-copy') ||
                node.classList.contains('btn-copy') ||
                node.classList.contains('overlay') ||
                node.classList.contains('monaco-editor') ||
                node.nodeName === 'SCRIPT';
        },
        replacement: function () {
            return '';
        }
    });

    // inline math
    OJBetter.common.turndownService.addRule('inline-math', {
        filter: function (node, options) {
            return node.tagName.toLowerCase() == "span" && node.className == "katex";
        },
        replacement: function (content, node) {
            var latex = $(node).find('annotation').text();
            latex = latex.replace(/</g, "&lt;").replace(/>/g, "&gt;");
            return "$" + latex + "$";
        }
    });

    // block math
    OJBetter.common.turndownService.addRule('block-math', {
        filter: function (node, options) {
            return node.tagName.toLowerCase() == "span" && node.className == "katex-display";
        },
        replacement: function (content, node) {
            var latex = $(node).find('annotation').text();
            latex = latex.replace(/</g, "&lt;").replace(/>/g, "&gt;");
            return "\n$$\n" + latex + "\n$$\n";
        }
    });

    // pre
    OJBetter.common.turndownService.addRule('pre', {
        filter: function (node, options) {
            return node.tagName.toLowerCase() == "pre";
        },
        replacement: function (content, node) {
            if (!node.classList.contains('source-code-for-copy') && !node.classList.contains('prettyprint')) {
                return "```\n" + content + "```\n";
            } else {
                return "";
            }
        }
    });

    // bordertable
    OJBetter.common.turndownService.addRule('bordertable', {
        filter: 'table',
        replacement: function (content, node) {
            if (node.classList.contains('table')) {
                var output = [],
                    thead = '',
                    trs = node.querySelectorAll('tr');
                if (trs.length > 0) {
                    var ths = trs[0].querySelectorAll('th, td');
                    if (ths.length > 0) {
                        thead = '| ' + Array.from(ths).map(th => OJBetter.common.turndownService.turndown(th.innerHTML.trim())).join(' | ') + ' |\n'
                        thead += '| ' + Array.from(ths).map(() => ' --- ').join('|') + ' |\n';
                    }
                }
                var rows = node.querySelectorAll('tr');
                Array.from(rows).forEach(function (row, i) {
                    if (i > 0) {
                        var cells = row.querySelectorAll('td,th');
                        var trow = '| ' + Array.from(cells).map(cell => OJBetter.common.turndownService.turndown(cell.innerHTML.trim())).join(' | ') + ' |';
                        output.push(trow);
                    }
                });
                return thead + output.join('\n');
            } else {
                return content;
            }
        }
    });
};

/**
 * 任务队列
 */
class TaskQueue {
    constructor() {
        this.taskQueues = {};
        this.isProcessing = {}; // 处理状态
        this.delays = {}; // 等待时间(毫秒)
    }

    getDelay(type) {
        if (type === 'openai') {
            return 0;
        } else {
            return OJBetter.translation.waitTime;
        }
    }

    /**
     * 添加任务
     * @param {string} type 任务类型
     * @param {function} fn 任务函数
     * @param {boolean} isNonQueueTask 是否为非队列任务
     */
    addTask(type, fn, isNonQueueTask = false) {
        if (!this.taskQueues[type]) {
            this.taskQueues[type] = [];
        }

        if (isNonQueueTask) {
            fn();
        } else {
            this.taskQueues[type].push(fn);

            if (!this.isProcessing[type]) {
                this.processQueue(type);
            }
        }
    }

    async processQueue(type) {
        this.isProcessing[type] = true;

        while (this.taskQueues[type].length > 0) {
            const task = this.taskQueues[type].shift();
            await task();

            if (this.taskQueues[type].length > 0) {
                await this.wait(this.getDelay(type));
            }
        }

        this.isProcessing[type] = false;
    }

    wait(delay) {
        return new Promise(resolve => {
            setTimeout(resolve, delay);
        });
    }
}

/**
 * 检测文本是否可能为代码片段
 * @param {string} text 待检测的文本
 * @returns {boolean} 是否可能为代码片段
 */
/**
 * 检测为空文本
 * @param {string} text 待检测的文本
 * @returns {boolean} 是否为空文本
 */
const isEmptyText = text => text.trim() === '';

/**
 * 加载按钮相关函数
 */
async function initButtonFunc() {
    // 鼠标悬浮时为目标元素区域添加一个覆盖层
    $.fn.addHoverOverlay = function (target) {
        let position = $(target).css('position');
        let display = $(target).css('display');

        this.hover(() => {
            $(target)
                .addClass('overlay')
                .css('position', 'relative');
            if (display == "inline" || display == "contents") {
                $(target).css('display', 'block');
            }
        }, () => {
            $(target)
                .removeClass('overlay')
                .css('position', position);
            if (display == "inline" || display == "contents") {
                $(target).css('display', display);
            }
        })
    }

    /**
     * 为按钮设置图标
     * @param {string} icon 图标
     * @returns {JQuery<HTMLElement>} 按钮
     */
    $.fn.setButtonIcon = function (icon) {
        let i = this.find("i");
        if (i.length != 0 && i.hasClass("iconfont")) {
            i.html(icon);
        } else {
            i = OJB_safeCreateJQElement(`<i>${icon}</i>`);
            this.prepend(i);
        }
        return this;
    }

    /** 
     * 设置按钮为加载等待状态
     */
    $.fn.setButtonLoading = function () {
        this.addClass("loading");
        this.prop("disabled", true);
        return this;
    }

    /**
     * 解除按钮的加载等待状态
     */
    $.fn.setButtonLoaded = function () {
        this.removeClass("loading");
        this.prop("disabled", false);
        return this;
    }

    /**
     * 为按钮设置popover提示文本
     * @param {string} text 文本
     * @returns {JQuery<HTMLElement>} 按钮
     */
    $.fn.setButtonPopover = function (text) {
        // find if has popover_content class element
        let popover_content = this.find(".popover_content");
        if (popover_content.length != 0) {
            popover_content.text(text);
        } else {
            popover_content = OJB_safeCreateJQElement(`<span class="popover_content">${text}</span>`);
            this.append(popover_content);
        }
        return this;
    }

    /**
     * 获取MarkDown
     * @returns {string} MarkDown
     */
    $.fn.getMarkdown = function () {
        const markdown = this.data('markdown');
        if (markdown === undefined) {
            const htmlContent = this.html();
            const newMarkdown = OJBetter.common.turndownService.turndown(htmlContent);
            this.data('markdown', newMarkdown);
            return newMarkdown;
        }
        return markdown;
    }

    // 设置按钮状态
    $.fn.setButtonState = function (state, popoverText = null, disabled = false) {
        this.data('buttonState', state)
            .prop('disabled', disabled)
            .css('cursor', disabled ? 'not-allowed' : 'pointer')
            .removeClass('running success enabled error loading redo');
        if (popoverText) this.setButtonPopover(popoverText);

        if (state !== 'initial') this.addClass(state);
        return this;
    };

    // 为按钮添加鼠标悬浮重试
    $.fn.setHoverRedo = function () {
        this.hover(() => {
            prevState = this.getButtonState();
            if (prevState !== "normal" && prevState !== "running") {
                this.setButtonState('redo');
            }
        }, () => {
            const currentState = this.getButtonState();
            if (prevState !== "normal" && ["normal", "redo"].includes(currentState)) {
                this.setButtonState(prevState);
                prevState = null;
            }
        });
    };

    // 获取按钮状态
    $.fn.getButtonState = function () {
        return this.data('buttonState') || 'normal';
    };

    // 设置翻译按钮状态
    $.fn.setTransButtonState = function (state, text = null) {
        const popoverText = text || i18next.t(`trans.${state}`, { ns: 'button' });
        const disabled = state === 'running' || state === 'loading';
        this.setButtonState(state, popoverText, disabled);
        return this;
    };

    // 存翻译结果
    $.fn.pushResultToTransButton = function (result) {
        let resultStack = this.data('resultStack');
        if (!resultStack) resultStack = [];
        resultStack.push(result);
        this.data('resultStack', resultStack);
    }

    // 获取翻译结果
    $.fn.getResultFromTransButton = function () {
        return this.data('resultStack');
    }

    // 标记为不自动翻译
    $.fn.setNotAutoTranslate = function () {
        this.data('notAutoTranslate', true);
    }

    // 获取是否为不自动翻译
    $.fn.getNotAutoTranslate = function () {
        return this.data('notAutoTranslate');
    }

    // 判断是否已经翻译
    $.fn.IsTranslated = function () {
        if (this.hasAttr('translated')) {
            return true;
        } else {
            return false;
        }
    }

    // 判断是否为评论区按钮
    $.fn.IsCommentButton = function () {
        let isCommentButton = this.data('isCommentButton');
        if (isCommentButton == undefined) {
            this.parents('.comments').length > 0 ? isCommentButton = true : isCommentButton = false;
            this.data('isCommentButton', isCommentButton);
        }
        return isCommentButton;
    }

    // 按钮点击效果
    $(document).on('mousedown', '.ojb_btn', function () {
        $(this).addClass('active').on('animationend', () => $(this).removeClass('active'));
    });
}

/**
 * 添加题目markdown转换/复制/翻译按钮面板
 * @param {HTMLElement} element 需要添加按钮面板的元素
 * @param {string} suffix 按钮面板id后缀
 * @param {string} type 按钮面板添加位置
 * @param {boolean} is_simple 是否是简单模式
 * @returns {object} 返回按钮面板元素
 */
function addButtonPanel(element, suffix, type, is_simple = false) {
    let text;
    if (OJBetter.translation.comment.transMode == "1") text = i18next.t('trans.segment', { ns: 'button' });
    else if (OJBetter.translation.comment.transMode == "2") text = i18next.t('trans.select', { ns: 'button' });
    else text = i18next.t('trans.normal', { ns: 'button' });

    let panel = OJB_safeCreateJQElement(`<div class='html2md-panel input-output-copier ${is_simple ? 'is_simple' : ''}'></div>`);
    let viewButton = OJB_safeCreateJQElement(`
        <button class='ojb_btn ojb_btn_popover top' id='html2md-view${suffix}'>
            <i class="iconfont">&#xe7e5;</i>
            <span class="popover_content">${i18next.t('md.normal', { ns: 'button' })}</span>
        </button>`);
    let copyButton = OJB_safeCreateJQElement(`
        <button class='ojb_btn ojb_btn_popover top' id='html2md-cb${suffix}'>
            <i class="iconfont">&#xe608;</i>
            <span class="popover_content">${i18next.t('copy.normal', { ns: 'button' })}</span>
        </button>`);
    let translateButton = OJB_safeCreateJQElement(`
        <button class='ojb_btn translateButton ojb_btn_popover top' id='translateButton${suffix}'>
            <i class="iconfont">&#xe6be;</i>
            <span class="popover_content">${text}</span>
        </button>`);
    if (!is_simple) panel.append(viewButton);
    if (!is_simple) panel.append(copyButton);
    panel.append(translateButton);
    if (type === "this_level") {
        $(element).before(panel);
    } else if (type === "child_level") {
        $(element).prepend(panel);
    }

    return {
        panel: panel,
        viewButton: viewButton,
        copyButton: copyButton,
        translateButton: translateButton
    }
}

/**
 * 添加MD视图按钮
 * @param {JQuery<HTMLElement>} button 按钮
 * @param {JQuery<HTMLElement>} element 目标元素
 * @param {string} suffix id后缀
 * @param {string} type 类型
 * @returns {void}
 */
async function addButtonWithHTML2MD(button, element, suffix, type) {
    /**
     * 改变按钮状态
     * @param {string} state 状态
     */
    const changeButtonState = (state) => {
        if (state == "loading") {
            button.setButtonLoading();
            button.setButtonPopover(i18next.t('state.waitMathJax', { ns: 'button' }));
        } else if (state == "loaded") {
            button.setButtonLoaded();
            button.setButtonPopover(i18next.t('md.normal', { ns: 'button' }));
        } else if (state == "normal") {
            button.removeClass("enabled");
            button.setButtonPopover(i18next.t('md.normal', { ns: 'button' }));
        } else if (state == "mdView") {
            button.addClass("enabled");
            button.setButtonPopover(i18next.t('md.reduction', { ns: 'button' }));
        } else if (state == "disabled") {
            button.prop("disabled", true);
            button.setButtonPopover(i18next.t('md.disabled', { ns: 'button' }));
        }
    }

    /**
     * 存放目标元素的 JQueryObject
     */
    const target = (() => {
        if (type = "child_level") {
            return $(element).children(':not(.html2md-panel)');
        } else {
            return $(element);
        }
    })();

    if (OJBetter.typeOfPage.is_oldLatex || OJBetter.typeOfPage.is_acmsguru) {
        changeButtonState("disabled");
        return;
    } else {
        changeButtonState("loading");
        await waitForMathJaxIdle();
        changeButtonState("loaded");
    }

    button.click(OJB_debounce(function () {
        /**
         * 检查是否是MarkDown视图 
         * @returns {boolean} 是否是MarkDown视图
         */
        function checkViewmd() {
            if ($(element).attr("viewmd") === "true") {
                return true;
            } else {
                return false;
            }
        }

        /**
         * 设置是否是MarkDown视图
         * @param {boolean} value 是否是MarkDown视图
         * @returns {void}
         */
        function setViewmd(value) {
            $(element).attr("viewmd", value);
            if (value) {
                changeButtonState("mdView");
            } else {
                changeButtonState("normal");
            }
        }

        if (checkViewmd()) {
            setViewmd(false);
            target.last().next(".mdViewContent").remove();
            target.show();
        } else {
            setViewmd(true);
            const markdown = $(element).getMarkdown();
            const mdViewContent = OJB_safeCreateJQElement(`<span class="mdViewContent" style="width:auto; height:auto;">${markdown}</span>`);
            target.last().after(mdViewContent);
            target.hide();
        }
    }));

    if (OJBetter.preference.hoverTargetAreaDisplay && !OJBetter.typeOfPage.is_oldLatex && !OJBetter.typeOfPage.is_acmsguru) {
        button.addHoverOverlay($(element));
    }
}

/**
 * 添加复制按钮
 * @param {JQuery<HTMLElement>} button 按钮
 * @param {JQuery<HTMLElement>} element 目标元素
 * @param {string} suffix 后缀
 * @param {string} type 类型
 */
async function addButtonWithCopy(button, element, suffix, type) {
    /**
     * 改变按钮状态
     * @param {string} state 状态
     */
    function changeButtonState(state) {
        if (state == "loading") {
            button.setButtonLoading();
            button.setButtonPopover(i18next.t('state.waitMathJax', { ns: 'button' }));
        } else if (state == "loaded") {
            button.setButtonLoaded();
            button.setButtonPopover(i18next.t('copy.normal', { ns: 'button' }));
        } else if (state == "normal") {
            button.setButtonPopover(i18next.t('copy.normal', { ns: 'button' }));
        } else if (state == "copied") {
            button.setButtonPopover(i18next.t('copy.copied', { ns: 'button' }));
        } else if (state == "disabled") {
            button.prop("disabled", true);
            button.setButtonPopover(i18next.t('copy.disabled', { ns: 'button' }));
        }
    }

    // 等待MathJax队列完成
    if (OJBetter.typeOfPage.is_oldLatex || OJBetter.typeOfPage.is_acmsguru) {
        changeButtonState("disabled");
        return;
    } else {
        changeButtonState("loading");
        await waitForMathJaxIdle();
        changeButtonState("loaded");
    }

    button.click(OJB_debounce(function () {
        var target = $(element).get(0);

        var markdown = $(element).getMarkdown();

        GM_setClipboard(markdown);

        $(this).addClass("success");
        changeButtonState("copied");


        // 更新复制按钮文本
        setTimeout(() => {
            $(this).removeClass("success");
            changeButtonState("normal")
        }, 2000);
    }));

    if (OJBetter.preference.hoverTargetAreaDisplay && !OJBetter.typeOfPage.is_oldLatex && !OJBetter.typeOfPage.is_acmsguru) {
        button.addHoverOverlay($(element));
    }
}

/**
 * 添加翻译按钮
 * @param {JQuery<HTMLElement>} button 按钮
 * @param {JQuery<HTMLElement>} element 目标元素
 * @param {string} suffix 后缀
 * @param {string} type 类型
 * @param {boolean} is_comment 是否是评论
 */
async function addButtonWithTranslation(button, element, suffix, type, is_comment = false) {
    /**
     * 添加可指定翻译服务的方法调用
     * @param {string} translation 翻译服务
     */
    button.data("translatedItBy", function (translation) {
        button.setTransButtonState('running', i18next.t('trans.wait', { ns: 'button' }));
        OJBetter.common.taskQueue.addTask(translation, () => transTask(button, element, type, is_comment, translation), translation == 'openai');
    });

    // 等待MathJax队列完成
    button.setButtonLoading();
    await waitForMathJaxIdle();
    button.setButtonLoaded();

    // 标记目标文本区域不自动翻译
    {
        let text;
        if (OJBetter.typeOfPage.is_oldLatex || OJBetter.typeOfPage.is_acmsguru) {
            text = $(element).html();
        } else {
            text = $(element).getMarkdown();
        }
        let length = text.length;
        if (length > OJBetter.translation.auto.shortTextLength || isEmptyText(text) || $(element).find('.spoiler').length > 0) {
            button.setNotAutoTranslate();
        }
        // button.after(`<span>${length}</span>`); // 显示字符数
    }

    button.click(OJB_debounce(async function () {
        // 重新翻译
        let resultStack = $(this).getResultFromTransButton();
        if (resultStack) {
            let pElements = $(element).find("p.block_selected:not(li p), li.block_selected");
            for (let item of resultStack) {
                if (OJBetter.translation.retransAction == "0") {
                    // 选段翻译不直接移除旧结果
                    if (OJBetter.translation.comment.transMode == "2") {
                        // 只移除即将要翻译的段的结果
                        if (pElements.is(item.translateDiv.getDiv().prev())) {
                            item.translateDiv.close();
                        }
                    } else {
                        item.translateDiv.close();
                        $($(element)).find(".translate-problem-statement, .translate-problem-statement-panel").remove();
                    }
                } else {
                    item.translateDiv.fold();
                }
            }
        }

        // 翻译
        button.setTransButtonState('running', i18next.t('trans.wait', { ns: 'button' }));
        OJBetter.common.taskQueue.addTask(OJBetter.translation.choice, () => transTask(button, element, type, is_comment), OJBetter.translation.choice == 'openai');
    }));

    // 重新翻译提示
    let prevState;
    button.hover(() => {
        prevState = button.getButtonState();
        if (prevState !== "normal" && prevState !== "running") {
            button.setTransButtonState('redo');
        }
    }, () => {
        const currentState = button.getButtonState();
        if (prevState !== "normal" && ["normal", "redo"].includes(currentState)) {
            button.setTransButtonState(prevState);
            prevState = null;
        }
    });

    // 目标区域指示
    if (OJBetter.preference.hoverTargetAreaDisplay) {
        button.addHoverOverlay($(element));
    }

    // 翻译右键切换菜单
    $(document).on('contextmenu', '#translateButton' + suffix, function (e) {
        e.preventDefault();

        // 是否为评论的翻译
        let is_comment = button.IsCommentButton();

        // 移除旧的
        if (!$(e.target).closest('.OJBetter_contextmenu').length) {
            $('.OJBetter_contextmenu').remove();
        }

        var menu = $('<div class="OJBetter_contextmenu"></div>');
        var translations = [
            { value: 'deepl', name: i18next.t('translation.options.services.deepl', { ns: 'settings' }) },
            { value: 'iflyrec', name: i18next.t('translation.options.services.iflyrec', { ns: 'settings' }) },
            { value: 'youdao', name: i18next.t('translation.options.services.youdao', { ns: 'settings' }) },
            { value: 'google', name: i18next.t('translation.options.services.google', { ns: 'settings' }) },
            { value: 'caiyun', name: i18next.t('translation.options.services.caiyun', { ns: 'settings' }) },
            { value: 'openai', name: i18next.t('translation.options.services.openai.name', { ns: 'settings' }) }
        ];

        // Function to check if the service supports the target language
        function supportsTargetLanguage(service, targetLang) {
            return OJBetter.supportList.translationSupport[service] && OJBetter.supportList.translationSupport[service][targetLang] !== undefined;
        }

        if (is_comment) {
            var label = OJB_safeCreateJQElement(`<label><input type="radio" name="translation" value="0">
            <span class="OJBetter_contextmenu_label_text">
            ${i18next.t('translation.preference.comment_translation_choice.services.follow', { ns: 'settings' })}
            </span></label>`);
            menu.append(label);
        }
        translations.forEach(function (translation) {
            if (supportsTargetLanguage(translation.value, OJBetter.translation.targetLang)) {
                var label = OJB_safeCreateJQElement(`<label><input type="radio" name="translation" value="${translation.value}">
                <span class="OJBetter_contextmenu_label_text">${translation.name}</span></label>`);
                menu.append(label);
            }
        });

        // 初始化
        if (is_comment) {
            menu.find(`input[name="translation"][value="${OJBetter.translation.comment.choice}"]`).prop('checked', true);
        } else {
            menu.find(`input[name="translation"][value="${OJBetter.translation.choice}"]`).prop('checked', true);
        }
        menu.css({
            top: e.pageY + 'px',
            left: e.pageX + 'px'
        }).appendTo('body');

        $(document).one('change', 'input[name="translation"]', function () {
            if (is_comment) {
                OJBetter.translation.comment.choice = $('input[name="translation"]:checked').val();
                GM_setValue("commentTranslationChoice", OJBetter.translation.comment.choice);
            } else {
                OJBetter.translation.choice = $('input[name="translation"]:checked').val();
                GM_setValue("translation", OJBetter.translation.choice);
            }
            $('.OJBetter_contextmenu').remove();
        });

        // 点击区域外关闭菜单
        function handleClick(event) {
            if (!$(event.target).closest('.OJBetter_contextmenu').length) {
                $('.OJBetter_contextmenu').remove();
                $(document).off('change', 'input[name="translation"]');
            } else {
                $(document).one('click', handleClick);
            }
        }
        $(document).one('click', handleClick);
    });
}

/**
 * 创建翻译任务
 * @param {JQuery<HTMLElement>} button 按钮
 * @param {HTMLElement} element 目标元素
 * @param {string} type 类型
 * @param {boolean} is_comment 是否是评论
 * @param {string} overrideTrans 覆盖全局翻译服务设定
 */
async function transTask(button, element, type, is_comment, overrideTrans) {
    /** @type {HTMLElement} 目标元素 */
    let target;
    /**
     * 错误计数数据结构
     * @typedef {Object} count
     * @property {number} errerNum 错误数量
     * @property {number} skipNum 跳过数量
     */
    const count = {
        errerNum: 0,
        skipNum: 0
    };
    if (OJBetter.translation.comment.transMode == "1") {
        // 分段翻译
        let pElements = $(element).find("p:not(li p), li, .OJBetter_acmsguru");
        for (let i = 0; i < pElements.length; i++) {
            target = $(pElements[i]).eq(0).clone();
            element_node = pElements[i];
            await process(button, target, element_node, type, is_comment, count, overrideTrans);
        }
    } else if (OJBetter.translation.comment.transMode == "2") {
        // 选段翻译
        let pElements = $(element).find("p.block_selected:not(li p), li.block_selected, .OJBetter_acmsguru");
        for (let i = 0; i < pElements.length; i++) {
            target = $(pElements[i]).eq(0).clone();
            element_node = pElements[i];
            await process(button, target, element_node, type, is_comment, count, overrideTrans);
        }
        $(element).find("p.block_selected:not(li p), li.block_selected").removeClass('block_selected');
    } else {
        // 普通翻译
        target = $(element).eq(0).clone();
        if (type === "child_level") $(target).children(':first').remove();
        element_node = $($(element)).get(0);
        await process(button, target, element_node, type, is_comment, count, overrideTrans);
    }

    // 翻译完成
    if (!count.errerNum && !count.skipNum) {
        button.setTransButtonState('success');
    }
}

/**
 * 翻译处理
 * @param {JQuery<HTMLElement>} button 按钮
 * @param {HTMLElement} target 目标元素
 * @param {HTMLElement} element_node 目标节点
 * @param {string} type 类型
 * @param {boolean} is_comment 是否是评论
 * @param {string} overrideTrans 覆盖全局翻译服务设定
 */
async function process(button, target, element_node, type, is_comment, count, overrideTrans) {
    if (type === "child_level") {
        let div = $("<div>");
        $(element_node).append(div);
        element_node = div.get(0);
    }

    //是否跳过折叠块
    if ($(target).find('.spoiler').length > 0) {
        const shouldSkip = await OJB_createDialog(
            i18next.t('skipFold.title', { ns: 'dialog' }),
            i18next.t('skipFold.content', { ns: 'dialog' }),
            [
                i18next.t('skipFold.buttons.0', { ns: 'dialog' }),
                i18next.t('skipFold.buttons.1', { ns: 'dialog' })
            ],
            true
        ); //跳过折叠块确认
        if (shouldSkip) {
            $(target).find('.spoiler').remove();
        } else {
            $(target).find('.html2md-panel').remove();
        }
    }

    // 等待并获取结果
    button.setTransButtonState('running');
    const result = await blockProcessing(button, target, element_node, is_comment, overrideTrans);
    button.pushResultToTransButton(result);

    if (result.status == "error") count.errerNum += 1;
    else if (result.status == "skip") count.skipNum += 1;
    $(target).remove();
}

/**
 * 块处理
 * @param {JQuery<HTMLElement>} button 
 * @param {HTMLElement} target 目标元素
 * @param {HTMLElement} element_node 目标节点
 * @param {boolean} is_comment 是否是评论
 * @param {string} overrideTrans 覆盖全局翻译服务设定
 * @returns {TranslateResult} 翻译结果对象
 */
async function blockProcessing(button, target, element_node, is_comment, overrideTrans) {
    if (OJBetter.typeOfPage.is_oldLatex || OJBetter.typeOfPage.is_acmsguru) {
        target.markdown = $(target).html();
    } else if (!target.markdown) {
        target.markdown = OJBetter.common.turndownService.turndown($(target).html());
    }

    const result = await translateProblemStatement(target.markdown, element_node, is_comment, overrideTrans);
    if (result.status == "skip") {
        button.setTransButtonState('error', i18next.t('trans.tooLong', { ns: 'button' }));
        result.translateDiv.close();
    } else if (result.status == "error" || !result.rawData.done) {
        result.translateDiv.setError();
        result.translateDiv.setRawData(result.rawData);
        result.translateDiv.showDebugButton();
        button.setTransButtonState('error', i18next.t('trans.error', { ns: 'button' }));
        $(target).remove();
    }
    return result;
}

/**
 * 选段翻译支持
 */
async function multiChoiceTranslation() {
    GM_addStyle(`
        .topic .content #task-statement {
            overflow: initial;
        }
    `);

    $(document).on('click', 'p, li:not(:has(.comment)), .OJBetter_acmsguru', function (e) {
        let $this = $(this);
        e.stopPropagation();
        if ($this.hasClass('block_selected')) {
            $this.removeClass('block_selected');
            // 移除对应的按钮 
            $('.OJBetter_MiniTranslateButton').remove("#translateButton_selected_" + $this.attr('OJBetter_p_id'));
        } else {
            let id = OJB_getRandomNumber(8);
            $this.attr('OJBetter_p_id', id);
            $this.addClass('block_selected');
            // 添加按钮 
            let menu = OJB_safeCreateJQElement(`<div class="OJBetter_MiniTranslateButton" id='translateButton_selected_${id}'>${translateIcon}</div>`)
                .css({
                    left: $($this).outerWidth(true) + $($this).position().left + 10 + 'px',
                });
            $this.before(menu);

            $("#translateButton_selected_" + id).click(async function () {
                // 处理旧的结果
                if ($this.attr('translated')) {
                    let result = $this.data("resultData");
                    if (OJBetter.translation.retransAction == "0") {
                        result.translateDiv.close();
                    } else {
                        result.translateDiv.fold();
                    }
                }
                // 翻译
                let target = $this.eq(0).clone();
                let result = await blockProcessing(OJBetter.translation.choice, target, $this.eq(0), $("#translateButton_selected_" + id), false);
                $this.data("resultData", result);
                $this.removeClass('block_selected');
                // 移除对应的按钮 
                $('.OJBetter_MiniTranslateButton').remove("#translateButton_selected_" + id);
                $this.attr('translated', '1'); // 标记已翻译
            });
        }
    });
}

/**
 * 添加MD/复制/翻译按钮
 */
async function addConversionButton() {
    // TODO 7
    let promises = []; // 用于收集所有的 Promise

    // 基本添加
    if (!OJBetter.typeOfPage.is_homepage) {
        $('section').each((i, e) => {
            let id = "_" + OJB_getRandomNumber(8);
            let panel = addButtonPanel(e, id, "this_level");
            promises.push(addButtonWithHTML2MD(panel.viewButton, e, id, "this_level"));
            promises.push(addButtonWithCopy(panel.copyButton, e, id, "this_level"));
            promises.push(addButtonWithTranslation(panel.translateButton, e, id, "this_level"));
        });
    }

    // 添加按钮到题解部分
    if (OJBetter.typeOfPage.isEditorial) {
        let contestNavTabs = $("#contest-nav-tabs");
        let nextElement = contestNavTabs.next();
        let id = "_editorial_1_" + OJB_getRandomNumber(8);
        let panel = addButtonPanel(nextElement, id, "this_level");
        panel.panel.css('width', '100%'); // 加个样式,不然不显示
        promises.push(addButtonWithHTML2MD(panel.viewButton, nextElement, id, "this_level"));
        promises.push(addButtonWithCopy(panel.copyButton, nextElement, id, "this_level"));
        promises.push(addButtonWithTranslation(panel.translateButton, nextElement, id, "this_level"));
    }

    // 添加按钮到折叠块部分
    $('details').each((i, e) => {
        // 自定义测试样例折叠块不添加
        if ($(e).attr('id') !== "customTestBlock") {
            let id = "_details_" + OJB_getRandomNumber(8);
            let panel = addButtonPanel(e, id, "child_level");
            promises.push(addButtonWithHTML2MD(panel.viewButton, e, id, "child_level"));
            promises.push(addButtonWithCopy(panel.copyButton, e, id, "child_level"));
            promises.push(addButtonWithTranslation(panel.translateButton, e, id, "child_level"));
        }
    });

    // 添加到contest-statement部分
    $('#contest-statement').each((i, e) => {
        let id = "_contest-statement_" + OJB_getRandomNumber(8);
        let panel = addButtonPanel(e, id, "this_level");
        promises.push(addButtonWithHTML2MD(panel.viewButton, e, id, "this_level"));
        promises.push(addButtonWithCopy(panel.copyButton, e, id, "this_level"));
        promises.push(addButtonWithTranslation(panel.translateButton, e, id, "this_level"));
    });

    // 添加到blog-post部分
    $('.blog-post').each((i, e) => {
        let id = "_blog-post_" + OJB_getRandomNumber(8);
        let panel = addButtonPanel(e, id, "this_level");
        promises.push(addButtonWithHTML2MD(panel.viewButton, e, id, "this_level"));
        promises.push(addButtonWithCopy(panel.copyButton, e, id, "this_level"));
        promises.push(addButtonWithTranslation(panel.translateButton, e, id, "this_level"));
    });

    return Promise.all(promises).catch(error => {
        console.error("One or more of the Add Button operations failed: ", error);
    });
};

/**
 * 等待LaTeX渲染队列全部完成
 * @returns {Promise} 完成渲染
 */
function waitForMathJaxIdle() {
    return true;
    // return new Promise((resolve, reject) => {
    //     // 检查MathJax对象是否存在
    //     const checkMathJaxExists = () => {
    //         if (typeof MathJax === 'undefined') {
    //             // 如果MathJax不存在,稍后再次检查
    //             OJB_delay(100).then(checkMathJaxExists);
    //         } else {
    //             // MathJax存在,开始监视渲染队列
    //             startMonitoringQueue();
    //         }
    //     };

    //     // 开始监视MathJax渲染队列
    //     const startMonitoringQueue = () => {
    //         const intervalId = setInterval(() => {
    //             const queue = MathJax.Hub.queue;
    //             if (queue.pending === 0 && queue.running === 0) {
    //                 clearInterval(intervalId);
    //                 resolve();
    //             }
    //         }, 100);
    //     };

    //     // 开始检查MathJax对象
    //     checkMathJaxExists();
    // });
}

/**
 * 翻译结果面板
 */
class TranslateDiv {
    /**
     * 构造函数
     * @param {string} id 指定翻译框的id
     */
    constructor(id) {
        this.id = id;
        this.div = $('<div>').attr('id', id).addClass('translateDiv bounce-in');
        if (!OJBetter.typeOfPage.is_completeProblemset) {
            this.div.addClass('input-output-copier');
        }
        this.panelDiv = $('<div>').addClass('translate-problem-statement-panel');
        this.div.append(this.panelDiv);

        // 主要信息
        this.mainDiv = $('<div>').addClass('translate-problem-statement');
        this.span = $('<span>');
        this.mainDiv.append(this.span);
        this.div.append(this.mainDiv);
        this.mainDivState = {
            current: 'transHTML',
            transHTML: '',
            rawDataHTML: ''
        };

        // 顶栏信息
        this.topText = $('<div>').addClass('topText');
        this.panelDiv.append(this.topText);

        // 右侧
        this.rightDiv = $('<div>').css('display', 'flex');
        this.panelDiv.append(this.rightDiv);
        this.debugButton = OJB_safeCreateJQElement(`
        <button class='ojb_btn ojb_btn_popover top'>
            <i class="iconfont">&#xe641;</i>
            <span class="popover_content">${i18next.t('rawData.normal', { ns: 'button' })}</span>
        </button>`).hide();
        this.rightDiv.append(this.debugButton);
        this.queryBalanceButton = OJB_safeCreateJQElement(`
        <button class='ojb_btn ojb_btn_popover top'>
            <i class="iconfont">&#xe6ae;</i>
            <span class="popover_content">${i18next.t('queryBalance.normal', { ns: 'button' })}</span>
        </button>`).hide();
        this.rightDiv.append(this.queryBalanceButton);
        this.copyButton = OJB_safeCreateJQElement(`
        <button class='ojb_btn ojb_btn_popover top'>
            <i class="iconfont">&#xe608;</i>
            <span class="popover_content">${i18next.t('copy.normal', { ns: 'button' })}</span>
        </button>`);
        this.rightDiv.append(this.copyButton);
        this.upButton = OJB_safeCreateJQElement(`
        <button class='ojb_btn ojb_btn_popover top'>
            <i class="iconfont">&#xe601;</i>
            <span class="popover_content">${i18next.t('fold.normal', { ns: 'button' })}</span>
        </button>`);
        this.rightDiv.append(this.upButton);
        this.closeButton = OJB_safeCreateJQElement(`
        <button class='ojb_btn ojb_btn_popover top'>
            <i class="iconfont">&#xe614;</i>
            <span class="popover_content">${i18next.t('close.normal', { ns: 'button' })}</span>
        </button>`);
        this.rightDiv.append(this.closeButton);
    }

    /**
     * 获取翻译框
     * @returns {JQuery<HTMLElement>} 返回翻译框
     */
    getDiv() {
        return this.div;
    }

    /**
     * 设置翻译框顶部的文本
     * @param {string} text 翻译框顶部的文本
     */
    setTopText(text) {
        this.div.attr("data-topText", text);
        this.topText.text(text);
    }

    /**
     * 获取翻译框顶部的文本
     * @returns {string} 返回翻译框顶部的文本
     */
    getTopText() {
        return this.topText.text();
    }

    /**
     * 渲染一个元素内的LaTeX公式
     * @param {*} element 
     */
    renderLaTeX(element) {
        const latexRenderOptions = {
            delimiters: [
                { left: "$$", right: "$$", display: true },
                { left: "\$$", right: "\\$$", display: true },
                { left: "$", right: "$", display: false },
                { left: "\$$", right: "\\$$", display: false }
            ]
        };

        if (typeof renderMathInElement === 'function') {
            renderMathInElement(element, latexRenderOptions);
        }
    }

    /**
     * 更新翻译框内容
     * @param {string} text 文本内容
     * @param {boolean} is_escapeHTML 是否转义HTML标签,为true则HTML标签将作为普通文本处理,默认为true
     * @param {boolean} is_renderLaTeX 是否渲染LaTeX,为true则会渲染LaTeX,默认为true
     */
    updateTranslateDiv(text, is_escapeHTML = true, is_renderLaTeX = true,) {
        // 渲染MarkDown
        let md = window.markdownit({
            html: !is_escapeHTML,
        });
        if (!text) text = "";
        let html = md.render(text);
        this.mainDiv.html(html);

        // 渲染Latex
        if (is_renderLaTeX) {
            // MathJax.Hub.Queue(["Typeset", MathJax.Hub, this.mainDiv.get(0)]);
            this.renderLaTeX(this.mainDiv.get(0));
        }
        // 渲染代码块中的公式 (AtCoder)
        this.mainDiv.find('pre code').each((index, element) => {
            const codeText = $(element).text();
            const latexPattern = /\$\$([^]*?)\$\$|\$(\\\$|[^\$])*?\$/;
            if (latexPattern.test(codeText)) {
                this.renderLaTeX(element);
            }
        });
    }

    /**
     * 关闭元素
     */
    close() {
        this.closeButton.click();
    }

    /**
     * 收起元素
     */
    fold() {
        if (!this.upButton.hasClass("reverse")) {
            this.upButton.click();
        }
    }

    /**
     * 注册收起按钮事件
     */
    registerUpButtonEvent() {
        this.upButton.on("click", () => {
            // 如果没有reverse类,说明是展开状态
            if (!this.upButton.hasClass("reverse")) {
                // 执行收起操作
                this.upButton.addClass("reverse");
                this.upButton.setButtonState('initial', i18next.t('fold.unfold', { ns: 'button' }));
                OJB_toggleCollapseExpand(this.mainDiv.get(0));
            } else {
                // 执行展开操作
                this.upButton.removeClass("reverse");
                this.upButton.setButtonState('initial', i18next.t('fold.normal', { ns: 'button' }));
                OJB_toggleCollapseExpand(this.mainDiv.get(0));
            }
        });
    }

    /**
     * 注册关闭按钮事件
     */
    registerCloseButtonEvent() {
        this.closeButton.on("click", () => {
            $(this.div).remove();
            $(this.panelDiv).remove();
            if (OJBetter.typeOfPage.is_problem && OJBetter.translation.memory.enabled) {
                OJBetter.translation.memory.ttTree.rmTransResultMap(this.id); // 移除ttTree中的数据
                OJBetter.translation.memory.ttTree.refreshNode("#task-statement");
                updateTransDBData(OJBetter.translation.memory.ttTree.getNodeData(), OJBetter.translation.memory.ttTree.getTransResultMap()); // 更新DB中的数据
            }
        });
    }

    /**
     * 注册复制按钮事件
     * @param {string} text 复制的文本
     */
    registerCopyButtonEvent(text) {
        this.copyButton.on("click", () => {
            GM_setClipboard(text);
            this.copyButton.setButtonState('success', i18next.t('copy.copied', { ns: 'button' }));
            // 复制提示
            setTimeout(() => {
                this.copyButton.setButtonState('initial', i18next.t('copy.normal', { ns: 'button' }));
            }, 2000);
        });
    }

    /**
     * 禁用复制按钮
     */
    disableCopyButton() {
        this.copyButton.css({ 'fill': '#ccc' });
        this.copyButton.off("click");
    }

    /**
     * 设置面板为error状态
     */
    setError() {
        this.div.addClass('error');
        this.panelDiv.addClass('error');
        this.mainDiv.addClass('error');
    }

    /**
     * 设置原始数据数据
     * @param {Object} Object 原始数据
     */
    setRawData(Object) {
        this.mainDivState.rawDataHTML = $("<pre>").text(JSON.stringify(Object, null, 4)).get(0);
        if (this.mainDivState.current === 'rawDataHTML') {
            this.renderMainDiv();
        }
    }

    /**
     * 切换结果面板与原始数据面板
     */
    switchMainDiv() {
        // 在切换之前,保存当前内容的状态
        this.mainDivState[this.mainDivState.current] = this.mainDiv.html();
        // 切换当前状态
        this.debugButton.setButtonState(this.mainDivState.current === 'transHTML' ? 'enabled' : 'initial');
        this.mainDivState.current = this.mainDivState.current === 'transHTML' ? 'rawDataHTML' : 'transHTML';
        // 渲染新的当前状态
        this.renderMainDiv();
    }

    // 渲染当前内容到 mainDiv
    renderMainDiv() {
        requestAnimationFrame(() => {
            this.mainDiv.html(this.mainDivState[this.mainDivState.current]);
        });
    }

    /**
     * 注册debug按钮事件
     */
    registerDebugButtonEvent() {
        this.debugButton.on("click", () => {
            this.switchMainDiv();
        });
    }

    /**
     * 显示debug按钮
     */
    showDebugButton() {
        this.debugButton.show();
        this.registerDebugButtonEvent();
    }

    /**
     * 注册查询余额按钮事件
     * @param {function} callback 查询回调函数
     */
    registerQueryBalanceButtonEvent(callback) {
        this.queryBalanceButton.on("click", async () => {
            this.queryBalanceButton.setButtonState('loading', i18next.t('queryBalance.loading', { ns: 'button' }));
            try {
                const balance = await callback();
                this.queryBalanceButton.setButtonState('success', `${i18next.t('queryBalance.success', { ns: 'button' })} ${balance}`);
            } catch (error) {
                this.queryBalanceButton.setButtonState('error', `${i18next.t('queryBalance.error', { ns: 'button' })} ${error.message}`);
            }
        });
    }

    /**
     * 显示余额查询按钮
     * @param {string} server 服务名称
     */
    showQueryBalanceButton(server) {
        if (server == 'deepl') {
            const quotaConfig = OJBetter.deepl.config.quota;
            if (quotaConfig.url && quotaConfig.surplus && quotaConfig.header) {
                this.queryBalanceButton.show();
                this.registerQueryBalanceButtonEvent(() => {
                    return queryServerBalance(OJBetter.deepl.config.quota);
                });
            }
        } else if (server == 'openai') {
            const quotaConfig = OJBetter.chatgpt.config.quota;
            if (quotaConfig.url && quotaConfig.surplus && quotaConfig.header) {
                this.queryBalanceButton.show();
                this.registerQueryBalanceButtonEvent(() => {
                    return queryServerBalance(OJBetter.chatgpt.config.quota);
                });
            }
        }
    }
}

// 元素关系树
class ElementsTree {
    constructor(elements) {
        this.node = [];
        this.transResultMap = {};
        this.index = 0;
        // this.tagNames = ["DIV", "P", "UL", "LI"]
        this.tagNames = ["DIV", "P", "UL", "LI", "SECTION", "SPAN"]
        this.init($(elements));
    }

    // Iterate through all elements, because there may be multiple ttypography
    init(elements) {
        elements.each((i, e) => {
            this.node.push({}); // add one element
            this.index = 0; // reset index
            this.create(i, $(e));
        });
    }

    // 刷新关系树
    refreshNode(elements) {
        this.node = [];
        this.index = 0;
        this.init($(elements));
    }

    // 创建节点间的关系树
    create(i_, element) {
        var prev = null;
        var node = this.node[i_];
        element.children().each((i, e) => {
            // only add element with tagNames
            if (this.tagNames.includes($(e).prop("tagName"))) {
                prev = this.addNode(i_, prev, e);
            }
            // recursively child element
            if ($(e).children().length > 0 && prev !== null) {
                node[prev].firstChild = this.index;
                this.create(i_, $(e));
            }
        });
    }

    // 向树中添加一个节点
    addNode(i_, prev, e) {
        let node = this.node[i_];
        node[this.index] = {
            prev: prev,
            next: null,
            firstChild: null,
            type: $(e).prop("tagName"),
            isTranslateDiv: $(e).hasClass("translateDiv"),
            topText: $(e).attr("data-topText"),
            id: $(e).attr("id"),
        };

        if (prev !== null) {
            node[prev].next = this.index;
        }

        prev = this.index;

        this.index++;
        return prev;
    }

    getNodeData() {
        return this.node;
    }

    setNodeData(node) {
        this.node = node;
    }

    getTransResultMap() {
        return this.transResultMap;
    }

    setTransResultMap(transResultMap) {
        this.transResultMap = transResultMap;
    }

    rmTransResultMap(id) {
        delete this.transResultMap[id];
    }

    addTransResultMap(id, text) {
        this.transResultMap[id] = text;
    }

    getTranslateDivNum(ttTree) {
        var num = 0;
        for (var i in ttTree) {
            if (ttTree[i].isTranslateDiv) {
                num++;
            }
        }
        return num;
    }

    // 恢复目标元素中的translateDiv
    recover(elements) {
        elements.each((i, e) => {
            var ttTreeNode = this.node[i];
            var missingTranslateDivs = this.getTranslateDivNum(ttTreeNode);
            if (missingTranslateDivs > 0) {
                this.recoverOneElement($(e), ttTreeNode);
            }
        });
    }

    recoverOneElement(element, ttTreeNode) {
        this.recoverOneFork(element.children().eq(0), ttTreeNode, 0);
    }

    // 恢复一个分支
    recoverOneFork(pElement, ttTreeNode, index) {
        do {
            // only recover element with tagNames
            if (!this.tagNames.includes(pElement.prop("tagName"))) {
                if (pElement.next().length > 0) {
                    pElement = pElement.next();
                } else {
                    return;
                }
            }
            if (!ttTreeNode[index] || pElement.prop("tagName") !== ttTreeNode[index].type) {
                // console.warn(`元素不存在或类型不同, 元素结构可能已经发生了变化: \nindex: ${index}`, pElement);
                return;
            } else {
                // recursively child element
                var node = ttTreeNode[index];
                if (node.firstChild !== null) {
                    this.recoverOneFork(
                        pElement.children().eq(0),
                        ttTreeNode,
                        node.firstChild
                    );
                }
                // check if next node is translateDiv
                if (node.next !== null) {
                    index = node.next;

                    var ne_node = ttTreeNode[index];
                    if (ne_node.isTranslateDiv) {
                        var id = ne_node.id;
                        var topText = ne_node.topText;
                        var text = this.transResultMap[id];
                        // create element after pElement
                        this.reCreateTransDiv(pElement, id, text, topText, node.isTranslateDiv); // 如果前面一个也是翻译结果,则该结果折叠
                    }
                    pElement = pElement.next(); // go to next element
                }
            }
        } while (node.next !== null);
    }

    /**
     * 重新创建translateDiv
     * @param {*} pElement 
     * @param {*} id 
     * @param {*} translatedText 
     * @param {*} topText 
     * @param {Boolean} isFold 是否折叠
     */
    reCreateTransDiv(pElement, id, translatedText, topText, isFold) {
        const translateDiv = new TranslateDiv(id);
        pElement.after(translateDiv.getDiv());
        translateDiv.setTopText(topText);
        translateDiv.registerUpButtonEvent();
        translateDiv.registerCloseButtonEvent();
        if (!OJBetter.typeOfPage.is_oldLatex && !OJBetter.typeOfPage.is_acmsguru) {
            translateDiv.registerCopyButtonEvent(translatedText);
        } else {
            translateDiv.disableCopyButton();
        }
        translateDiv.updateTranslateDiv(translatedText, !(OJBetter.typeOfPage.is_oldLatex || OJBetter.typeOfPage.is_acmsguru));
        // 标记已翻译并添加到翻译按钮的结果栈中
        let transButton = pElement.prev('.html2md-panel').find('.translateButton');
        if (transButton.length == 0) {
            // 如果没有找到,则应该是得在父元素中找到
            transButton = pElement.parent().prev('.html2md-panel').find('.translateButton');
        }
        if (isFold) translateDiv.fold(); // 是否折叠该翻译
        transButton.pushResultToTransButton({
            translateDiv: translateDiv,
            status: 0
        });
        transButton.setTransButtonState('success');
    }
}

// 更新TransDB中的翻译数据
async function updateTransDBData(nodeDate, transResultMap) {
    var url = window.location.href.replace(/#/, "");
    try {
        await OJBetter.common.database.translateData.put({ url, transResultMap, nodeDate });
        return 'translateData saved successfully';
    } catch (error) {
        throw new Error(`Failed to save translateData: ${error}`);
    }
}

// 获取TransDB中保存的翻译数据
async function getTransDBData() {
    var url = window.location.href.replace(/#/, "");
    try {
        const result = await OJBetter.common.database.translateData.get(url);
        return result;
    } catch (error) {
        throw new Error(`Failed to get translateData: ${error}`);
    }
}

/**
 * 翻译结果恢复功能初始化
 * @returns 
 */
async function initTransResultsRecover() {
    OJBetter.translation.memory.ttTree = new ElementsTree("#task-statement"); // 初始化当前页面#task-statement元素的结构树
    let result = await getTransDBData();
    if (!result) return;
    OJBetter.translation.memory.ttTree.setNodeData(result.nodeDate);
    OJBetter.translation.memory.ttTree.setTransResultMap(result.transResultMap);
    OJBetter.translation.memory.ttTree.recover($("#task-statement"));
}

/**
 * 自动翻译
 */
async function initTransWhenViewable() {
    await waitForMathJaxIdle();

    // const elements = $('.ttypography, .comments').find('.translateButton');
    const elements = $('#task-statement').find('.translateButton');
    const observers = [];

    // Use a single Intersection Observer for all elements
    const observer = new IntersectionObserver((entries, obs) => {
        entries.forEach((entry) => {
            if (entry.isIntersecting) {
                const button = $(entry.target);
                const state = button.getButtonState();
                const notAutoTranslate = button.getNotAutoTranslate();
                // Check if the button meets the criteria
                if (state === 'normal' && !notAutoTranslate) {
                    let trans = OJBetter.translation.choice;

                    if (OJBetter.translation.auto.mixTrans.enabled && button.IsCommentButton() && OJBetter.translation.auto.mixTrans.servers.length > 0) {
                        const randomIndex = Math.floor(Math.random() * OJBetter.translation.auto.mixTrans.servers.length);
                        trans = OJBetter.translation.auto.mixTrans.servers[randomIndex];
                    }
                    button.data("translatedItBy")(trans);
                }

                // Stop observing the element
                obs.unobserve(entry.target);
            }
        });
    });

    // Observe each element
    elements.each((i, e) => {
        observer.observe(e);
    });

    // Store the observer in case you need to disconnect it later
    observers.push(observer);
}

/** 
 * 翻译返回结果结构体
 * @typedef {Object} TranslateResult
 * @property {string} status 翻译状态
 * @property {TranslateDiv} translateDiv 翻译结果面板
 * @property {TransRawData} rawData 原始翻译数据
 */

/**
 * 翻译主方法
 * @param {string} text 待翻译文本
 * @param {HTMLElement} element_node 元素节点
 * @param {Boolean} is_comment 是否为评论区文本
 * @param {string} overrideTrans 覆盖全局翻译服务设定
 * @returns {TranslateResult} 翻译结果对象
 */
async function translateProblemStatement(text, element_node, is_comment, overrideTrans) {
    /** @type {number} 翻译结果的ID*/
    const id = OJB_getRandomNumber(8);
    /** @type {TextBlockReplacer} 文本块替换/恢复实例*/
    const textBlockReplacer = new TextBlockReplacer();
    /** @type {string} 翻译结果文本*/
    let translatedText = "";

    /** @type {string} 当前实际应用的翻译服务 */
    const realTransServer = overrideTrans ||
        (is_comment && OJBetter.translation.comment.choice != "0" ?
            OJBetter.translation.comment.choice :
            OJBetter.translation.choice);

    /** @type {TranslateResult} 翻译结果对象 */
    const translateResult = {
        status: "ok",
        rawData: {
            done: false
        }
    }

    /**
     * LaTeX替换
     * @param {string} text 待翻译文本
     * @returns {string} 处理后的文本
     */
    const replaceLatex = function (text) {
        if (OJBetter.typeOfPage.is_oldLatex) {
            const regex = /<span\s+class="tex-span">.*?<\/span>/gi;
            text = textBlockReplacer.replace(text, regex);
            text = text.replace(/<p>(.*?)<\/p>/g, "$1\n\n"); // <p/>标签换为换行
        } else if (OJBetter.typeOfPage.is_acmsguru) {
            const regex = /<i>.*?<\/i>|<sub>.*?<\/sub>|<sup>.*?<\/sup>|<pre>.*?<\/pre>/gi;
            text = textBlockReplacer.replace(text, regex);
        } else if (realTransServer != "openai") {
            // 使用GPT翻译时不必替换latex公式
            const regex = /\$\$([^]*?)\$\$|\$(\\\$|[^\$])*?\$/g;
            text = textBlockReplacer.replace(text, regex);

            // 替换行间代码块```
            const regex2 = /```[\s\S]*?```/g;
            text = textBlockReplacer.replace(text, regex2);
        }
        return text;
    }

    /**
     * LaTeX恢复
     * @param {string} text 已翻译的文本
     * @returns {string} 恢复后的文本
     */
    const recoverLatex = function (text) {
        // 两个公式之间加个空格,防止有些LaTeX解析器解析错误
        let resultText = text
            .replace(/】【/g, '】 【')
            .replace(/\]\[/g, '] [')
            .replace(/\}\{/g, '} {');

        if (OJBetter.typeOfPage.is_oldLatex) {
            resultText = resultText.replace(/(.+?)(\n\n|$)/g, "<p>$1</p>"); // 换行符还原为<p/>标签
            resultText = textBlockReplacer.recover(resultText);
        } else if (OJBetter.typeOfPage.is_acmsguru) {
            resultText = textBlockReplacer.recover(resultText);
        } else if (realTransServer != "openai") {
            resultText = textBlockReplacer.recover(resultText);
        }
        return resultText;
    }

    /**
     * 格式化翻译结果
     * @param {string} text 
     * @returns {string} 处理后的翻译结果
     */
    const formatText = function (text) {
        // 转义LaTex中的特殊符号
        if (!OJBetter.typeOfPage.is_oldLatex && !OJBetter.typeOfPage.is_acmsguru) {

            // 先替换掉行间代码块
            const replacer = new TextBlockReplacer();
            text = replacer.replace(text, /```[\s\S]*?```/g);

            // 处理LaTeX公式
            const escapeRules = [
                { pattern: /(?<!\\)>(?!\s)/g, replacement: " &gt; " }, // >符号
                { pattern: /(?<!\\)</g, replacement: " &lt; " }, // <符号
                { pattern: /(?<!\\)\*/g, replacement: " &#42; " }, // *符号
                { pattern: /(?<!\\)_/g, replacement: " &#95; " }, // _符号
                { pattern: /(?<!\\)\\\\(?=\s)/g, replacement: "\\\\\\\\" }, // \\符号
                { pattern: /(?<!\\)\\(?![\\a-zA-Z0-9])/g, replacement: "\\\\" }, // \符号
            ];

            let latexMatches = [...text.matchAll(/\$\$([\s\S]*?)\$\$|\$(.*?)\$|\$([\s\S]*?)\$/g)];
            for (const match of latexMatches) {
                const matchedText = match[0];
                let escapedText = matchedText;

                for (const rule of escapeRules) {
                    escapedText = escapedText.replaceAll(rule.pattern, rule.replacement);
                }
                escapedText = escapedText.replace(/\$\$/g, "$$$$$$$$");// $$符号(因为后面需要作为replacement,双倍消耗)
                text = text.replace(matchedText, escapedText);
            }

            // 恢复行间代码块
            text = replacer.recover(text);
        }

        // // 使符合mathjx的转换语法
        // const mathjaxRuleMap = [
        //     { pattern: /\$/g, replacement: "$$$$$$" }, // $$ 行间
        // ];
        // mathjaxRuleMap.forEach(({ pattern, replacement }) => {
        //     text = text.replace(pattern, replacement);
        // });

        // markdown修正
        const mdRuleMap = [
            { pattern: /(\s_[\u4e00-\u9fa5]+_)([\u4e00-\u9fa5]+)/g, replacement: "$1 $2" }, // 斜体
            { pattern: /(_[\u4e00-\u9fa5]+_\s)([\u4e00-\u9fa5]+)/g, replacement: " $1$2" },
            { pattern: /(_[\u4e00-\u9fa5]+_)([\u4e00-\u9fa5]+)/g, replacement: " $1 $2" },
            { pattern: /(([\s\S]*?))/g, replacement: "($1)" }, // 中文()
            // { pattern: /:/g, replacement: ":" }, // 中文:
            { pattern: /\*\* (.*?) \*\*/g, replacement: "\*\*$1\*\*" } // 加粗
        ];
        mdRuleMap.forEach(({ pattern, replacement }) => {
            text = text.replace(pattern, replacement);
        });

        return text;
    }

    // 创建翻译结果元素并放在element_node的后面
    translateResult.translateDiv = new TranslateDiv(id);
    $(element_node).after(translateResult.translateDiv.getDiv());

    // 顶栏左侧信息
    translateResult.translateDiv.setTopText(i18next.t('servers.' + realTransServer, { ns: 'translator' }) +
        i18next.t('translateDiv.topTextSuffix', { ns: 'translator' }));

    // 注册按钮
    translateResult.translateDiv.registerUpButtonEvent();
    translateResult.translateDiv.registerCloseButtonEvent();
    if (OJBetter.translation.choice == 'openai' || OJBetter.translation.choice == 'deepl') {
        translateResult.translateDiv.showQueryBalanceButton(OJBetter.translation.choice); // 显示额度查询
    }

    // 翻译内容是否可能为代码片段
    if (isEmptyText(text)) {
        const shouldContinue = await OJB_createDialog(
            i18next.t('isEmptyText.title', { ns: 'dialog' }),
            i18next.t('isEmptyText.content', { ns: 'dialog' }),
            [
                i18next.t('isEmptyText.buttons.0', { ns: 'dialog' }),
                i18next.t('isEmptyText.buttons.1', { ns: 'dialog' })
            ],
            true
        );
        if (shouldContinue) {
            translateResult.status = "skip";
            return translateResult;
        }
    }

    // 替换latex公式
    text = replaceLatex(text);

    // 过滤**号
    if (OJBetter.translation.filterTextWithoutEmphasis && GM_getValue("translation") !== "openai") { // TODO
        text = text.replace(/\*\*/g, "");
    }

    // 字符数上限
    const translationLimits = {
        deepl: 5000,
        iflyrec: 2000,
        youdao: 5000,
        google: 5000,
        caiyun: 5000
    };
    if (translationLimits.hasOwnProperty(realTransServer) && text.length > translationLimits[realTransServer]) {
        let textLength = translationLimits[realTransServer];
        let realTextLength = text.length;
        const shouldContinue = await OJB_createDialog(
            i18next.t('transTextLimits.title', { ns: 'dialog' }),
            i18next.t('transTextLimits.content', { ns: 'dialog', textLength: textLength, realTextLength: realTextLength }),
            [
                i18next.t('transTextLimits.buttons.0', { ns: 'dialog' }),
                i18next.t('transTextLimits.buttons.1', { ns: 'dialog' })
            ],
            true
        ); // 字数超限确认
        if (shouldContinue) {
            translateResult.status = "skip";
            return translateResult;
        }
    }

    /**
     * 调用各个翻译服务
     * @param {string} transServer 翻译服务
     * @returns {TransRawData} 原始翻译数据
     */
    async function translate(transServer) {
        const is_renderLaTeX = !(OJBetter.typeOfPage.is_oldLatex || OJBetter.typeOfPage.is_acmsguru);
        const servername = i18next.t('servers.' + realTransServer, { ns: 'translator' });
        /** @type {TransRawData} 原始翻译数据*/
        let rawData = {};
        try {
            if (transServer == "deepl") {
                if (OJBetter.deepl.config.type == 'free') {
                    translateResult.translateDiv.updateTranslateDiv(`${i18next.t('transingTip.basic', { ns: 'translator', server: servername })}`, is_renderLaTeX);
                    rawData = await translate_deepl(text);
                } else if (OJBetter.deepl.config.type == 'api') {
                    translateResult.translateDiv.updateTranslateDiv(`${i18next.t('transingTip.deeplApi', { ns: 'translator', deepl_configName: OJBetter.deepl.config.name })}`, is_renderLaTeX);
                    if (OJBetter.deepl.config.apiGenre == 'deeplx') {
                        rawData = await translate_deeplx(text);
                    } else {
                        if (OJBetter.deepl.enableEmphasisProtection) text = convertBoldMarkdownToHTML(text);
                        if (OJBetter.deepl.enableLinkProtection) text = convertLinksMarkdownToHTML(text);
                        if (OJBetter.deepl.config.apiGenre == 'api-free') {
                            rawData = await translate_deepl_api_free(text);
                        } else if (OJBetter.deepl.config.apiGenre == 'api-pro') {
                            rawData = await translate_deepl_api_pro(text);
                        }
                        if (OJBetter.deepl.enableEmphasisProtection) rawData.text = convertBoldHTMLToMarkdown(rawData.text);
                        if (OJBetter.deepl.enableLinkProtection) rawData.text = convertLinksHTMLToMarkdown(rawData.text);
                    }
                }
            } else if (transServer == "iflyrec") {
                translateResult.translateDiv.updateTranslateDiv(`${i18next.t('transingTip.basic', { ns: 'translator', server: servername })}`, is_renderLaTeX);
                rawData = await translate_iflyrec(text);
            } else if (transServer == "youdao") {
                translateResult.translateDiv.updateTranslateDiv(`${i18next.t('transingTip.basic', { ns: 'translator', server: servername })}`, is_renderLaTeX);
                rawData = await translate_youdao_mobile(text);
            } else if (transServer == "google") {
                translateResult.translateDiv.updateTranslateDiv(`${i18next.t('transingTip.basic', { ns: 'translator', server: servername })}`, is_renderLaTeX);
                rawData = await translate_gg(text);
            } else if (transServer == "caiyun") {
                translateResult.translateDiv.updateTranslateDiv(`${i18next.t('transingTip.basic', { ns: 'translator', server: servername })}`, is_renderLaTeX);
                rawData = await translate_caiyun(text);
            } else if (transServer == "openai") {
                translateResult.translateDiv.updateTranslateDiv(`${i18next.t('transingTip.openai', { ns: 'translator', openai_name: OJBetter.chatgpt.config.name })}${!OJBetter.chatgpt.isStream
                    ? i18next.t('transingTip.openai_isStream', { ns: 'translator' }) : ""}`,
                    is_renderLaTeX);
                if (OJBetter.chatgpt.isStream) {
                    // 流式传输
                    rawData = await translate_openai_stream(text, translateResult.translateDiv);
                } else {
                    // 普通模式
                    rawData = await translate_openai(text);
                }
            }
            translatedText = rawData.text;
            if (!rawData.done) {
                translateResult.status = "error";
            }
        } catch (e) {
            translateResult.status = "error";
            rawData.message = i18next.t('error.unexpected', { ns: 'translator' });
            console.warn(e);
        }
        return rawData;
    }
    translateResult.rawData = await translate(realTransServer);

    if (translateResult.status == "error") {
        translateResult.translateDiv.updateTranslateDiv(translateResult.rawData.message);
        return translateResult;
    }

    // 还原latex公式
    translatedText = recoverLatex(translatedText);

    // 注册结果复制按钮
    if (!OJBetter.typeOfPage.is_oldLatex && !OJBetter.typeOfPage.is_acmsguru) {
        translateResult.translateDiv.registerCopyButtonEvent(translatedText);
    } else {
        translateResult.translateDiv.disableCopyButton();
    }

    // 翻译结果格式化
    translatedText = formatText(translatedText);

    // 保存翻译结果
    if ((OJBetter.typeOfPage.is_problem || OJBetter.typeOfPage.is_completeProblemset) && OJBetter.translation.memory.enabled) {
        // OJBetter.translation.memory.ttTree.refreshNode(".ttypography"); // 刷新当前页面.ttypography元素的结构树实例
        OJBetter.translation.memory.ttTree.refreshNode("#task-statement"); // 刷新当前页面.ttypography元素的结构树实例
        OJBetter.translation.memory.ttTree.addTransResultMap(id, translatedText);
        updateTransDBData(OJBetter.translation.memory.ttTree.getNodeData(), OJBetter.translation.memory.ttTree.getTransResultMap()); // 更新翻译结果到transDB
    }

    // 翻译结果面板更新
    translateResult.translateDiv.updateTranslateDiv(translatedText, !(OJBetter.typeOfPage.is_oldLatex || OJBetter.typeOfPage.is_acmsguru));

    return translateResult;
}

//弹窗翻译
function alertZh() {
    // var _alert = window.alert;
    // window.alert = async function (msg) {
    //     _alert(msg + "\n=========翻译=========\n" + await translate_deepl(msg));
    //     return true;
    // }
};

/**
 * 折叠块展开
 */
function ExpandFoldingblocks() {
    $('.spoiler').addClass('spoiler-open');
    $('.spoiler-content').attr('style', '');
};

/**
 * 折叠块渲染优化
 */
function RenderPerfOpt() {
    GM_addStyle(`
        .spoiler-content {
            contain: layout style;
        }
    `);
}

/**
 * 下拉选择框性能优化
 */
async function SelectElementPerfOpt() {
    // TODO 10
    // 加载库资源
    await OJB_LoadJS("https://aowuucdn.oss-accelerate.aliyuncs.com/js/selectpage.min.js", "sha512-HhBheWc9nbTuTG0oVYtY9c3nkJAAiuk899lycOtB8NALvp20CNOjlYdTAYbRy9/0zXnLl0LZpiwhfLZurvK1XQ==");
    /**
     * 将一个<select>元素转换为SelectPage控件
     * @param {HTMLElement|string} selector - 要转换的<select>元素或其选择器
     */
    const OJB_transformSelectToSelectPage = (selector) => {
        const $select = $(selector);
        if ($select.length === 0 || !$select.is('select')) {
            console.error('Invalid select element provided.');
            return;
        }

        // 隐藏原生的<select>元素
        $select.hide();

        // 创建一个新的<input>元素用于SelectPage控件
        const $inputForSelectPage = $('<input>', {
            type: 'text',
            class: 'selectpage-input',
            autocomplete: 'off'
        });
        $select.after($inputForSelectPage);

        // 准备SelectPage所需的数据格式
        const data = $select.find('option').map((_, option) => ({
            id: option.value,
            text: option.text
        })).get();

        // 初始化SelectPage
        $inputForSelectPage.selectPage({
            showField: 'text',
            keyField: 'id',
            data,
            lang: 'en',
            // 当选中一个选项时,更新隐藏的<select>元素的值
            eSelect: (data) => {
                $select.val(data.id).trigger('change');
            },
            // 初始化时根据<select>的当前值设置SelectPage
            initRecord: $select.val()
        });
    };

    // 遍历页面上的所有select
    $('select').each((_, select) => {
        // 选项大于500才优化
        if ($(select).find('option').length > 500) {
            OJB_transformSelectToSelectPage(select);
        }
    });
}

/**
 * 题目页相关链接栏
 */
class ProblemPageLinkbar {
    constructor() {
        this.containerElement = this.createToolbar();
        this.commandInvoker = new CommandInvoker();
    }

    /**
     * 创建工具栏
     */
    createToolbar() {
        // const toolbarElement = $("<div>").attr("id", "problemToolbar").insertBefore($(".problemindexholder"));
        const toolbarElement = $("<div>").attr("id", "problemToolbar").insertBefore($(".h2"));
        return new DOMContainer(toolbarElement);
    }

    /**
     * 添加按钮
     * @param {string} id 按钮id
     * @param {string} url 按钮链接
     * @param {string} text 按钮文字
     * @param {JQueryObject} icon 按钮图标
     * @param {string} iconHeight 图标高度
     * @returns {object} 按钮对象
     */
    addLinkButton(id, url, text, icon = $('<div>'), iconHeight = "22px") {
        const linkElement = $("<a>")
            .attr("href", url)
            .attr("target", "_blank")
            .addClass("ojb_btn")
            .attr("id", id);

        linkElement.append(icon);
        icon.css("height", iconHeight);

        const textSpan = $("<span>").html(text);
        linkElement.append(textSpan);

        this.commandInvoker.execute(new AddElementCommand(this.containerElement, linkElement));
        return {
            element: linkElement,
            text: textSpan,
            icon: icon
        };
    }

    /**
     * 更新链接
     * @param {object} button 按钮对象
     * @param {string} url 按钮链接
     */
    updateUrl(button, url) {
        button.element.attr("href", url);
    }

    /**
     * 更新文字
     * @param {object} button 按钮对象
     * @param {string} text 按钮文字
     */
    updateText(button, text) {
        button.text.html(text);
    }

    /**
     * 设置文字为粗体
     * @param {object} button 按钮对象
     */
    setBold(button) {
        button.text.css("font-weight", "bold");
    }

    /**
     * 更新图标
     * @param {object} button 按钮对象
     * @param {JQueryObject} icon 按钮图标
     * @param {string} iconHeight 图标高度
     */
    updateIcon(button, icon, iconHeight = "16px") {
        button.icon.remove();
        button.text.prepend(icon);
        icon.css("height", iconHeight);
        button.icon = icon;
    }

    /**
     * 添加类
     * @param {object} button 按钮对象
     * @param {string} className 类名
     */
    addClass(button, className) {
        button.element.addClass(className);
    }

    /**
     * 禁用链接按钮
     * @param {object} button 按钮对象
     */
    disableButton(button) {
        button.element.addClass("disabled");
    }

    /**
     * 启用链接按钮
     * @param {object} button 按钮对象
     */
    enableButton(button) {
        button.element.removeClass("disabled");
    }
}

/**
 * 获取题目的id
 * @param {String} url 题目的链接 
 * @returns 题目的id,形如2000A
 */
function getProblemId(url) {
    const regex = /\/contests\/([A-Za-z\d\-]+)\/tasks\/([A-Za-z\d\_]+)/;
    const matchResult = url.match(regex);
    return matchResult && matchResult.length >= 3
        ? `${matchResult[2]}`
        : '';
};

/**
 * 跳转到洛谷
 * @param {ProblemPageLinkbar} problemToolbar 
 */
async function CF2luogu(problemToolbar) {
    const url = window.location.href;
    const problemId = getProblemId(url);
    const luoguButton = problemToolbar.addLinkButton(
        "luoguButton",
        "https://www.luogu.com.cn/",
        i18next.t('state.loading', { ns: 'button' }),
        $("<img>").attr("src", "https://cdn.luogu.com.cn/fe/logo.png")
    );

    const checkLinkExistence = async (url) => {
        return OJB_promiseRetryWrapper(async () => {
            const response = await OJB_GMRequest({
                method: "GET",
                url
            });
            return !response.responseText.match(/出错了/g);
        }, {
            maxRetries: 3,
            retryInterval: 1000
        });
    };

    const LuoguUrl = `https://www.luogu.com.cn/problem/AT_${problemId}`;
    try {
        const result = await checkLinkExistence(LuoguUrl);
        if (problemId && result) {
            problemToolbar.updateText(luoguButton, "");
            problemToolbar.updateUrl(luoguButton, LuoguUrl);
        } else {
            problemToolbar.updateText(luoguButton, i18next.t('state.404', { ns: 'button' }));
            problemToolbar.disableButton(luoguButton);
        }
    } catch (error) {
        if (error instanceof OJB_GMError && error.type == "error") {
            problemToolbar.updateText(luoguButton, i18next.t('state.netError', { ns: 'button' }));
            problemToolbar.disableButton(luoguButton);
        }
    }
}

/**
 * 跳转到 Virtual Judge
 * @param {ProblemPageLinkbar} problemToolbar 
 */
async function CF2vjudge(problemToolbar) {
    const url = window.location.href;
    const problemId = getProblemId(url);
    const vjudgeButton = problemToolbar.addLinkButton(
        "vjudgeButton",
        "https://vjudge.net/",
        i18next.t('state.loading', { ns: 'button' }),
        $("<img>").attr("src", "https://aowuucdn.oss-accelerate.aliyuncs.com/vjudge.ico")
    );

    const checkLinkExistence = async (url) => {
        return OJB_promiseRetryWrapper(async () => {
            const response = await OJB_GMRequest({
                method: "HEAD",
                url: url,
            });
            if (response.status >= 200 && response.status < 300) return true;
            else if (response.status == 404) return false;
            else throw new OJB_GMError('network', 'An unknown network error occurred!', response);
        }, {
            maxRetries: 3,
            retryInterval: 1000
        });
    };

    const VjudgeUrl = `https://vjudge.net/problem/AtCoder-${problemId}`;
    try {
        const result = await checkLinkExistence(VjudgeUrl);
        if (problemId && result) {
            problemToolbar.updateText(vjudgeButton, "VJudge");
            problemToolbar.updateUrl(vjudgeButton, VjudgeUrl);
        } else {
            problemToolbar.updateText(vjudgeButton, i18next.t('state.404', { ns: 'button' }));
            problemToolbar.disableButton(vjudgeButton);
        }
    } catch (error) {
        if (error instanceof OJB_GMError && error.type == "error") {
            problemToolbar.updateText(vjudgeButton, i18next.t('state.netError', { ns: 'button' }));
            problemToolbar.disableButton(vjudgeButton);
        }
    }
}

// RatingClass
const ratingClassMap = {
    NaN: "rating_by_clist_colorNaN",
    0: "rating_by_clist_color0",
    400: "rating_by_clist_color1",
    800: "rating_by_clist_color2",
    1200: "rating_by_clist_color3",
    1600: "rating_by_clist_color4",
    2000: "rating_by_clist_color5",
    2400: "rating_by_clist_color6",
    2800: "rating_by_clist_color7",
    2800: "rating_by_clist_color8",
    2800: "rating_by_clist_color9"
};
const cssMap = {
    "rating_by_clist_colorNaN": "#cccccc",
    "rating_by_clist_color0": "#808080",
    "rating_by_clist_color1": "#804000",
    "rating_by_clist_color2": "#008000",
    "rating_by_clist_color3": "#00c0c0",
    "rating_by_clist_color4": "#0000ff",
    "rating_by_clist_color5": "#c0c000",
    "rating_by_clist_color6": "#fb7e00",
    "rating_by_clist_color7": "#ff0000",
    "rating_by_clist_color8": "#ff0000",
    "rating_by_clist_color9": "#ff0000"
};
// TODO 7
/**
 * clist 访问有效性检查
 * @param {boolean} onlyCookie 是否只检查Cookie
 * @returns {Promise<boolean>} 是否有效
 */
async function validateClistConnection(onlyCookie = false) {
    const clistApiUrl = "https://clist.by:443/api/v4/contest/?limit=1&resource_id=1";
    const requestOptions = {
        method: "GET",
        url: clistApiUrl,
        timeout: 5000,
    };

    // 尝试发送请求
    async function tryRequest(options) {
        try {
            const response = await OJB_GMRequest(options);
            if (response.status === 200) {
                return { ok: true };
            } else if (response.status === 401) {
                throw new Error('unauthorized');
            } else if (response.status === 404) {
                throw new Error('not_found');
            } else {
                throw new Error('other_error');
            }
        } catch (error) {
            console.warn(`Error accessing clist.by: ${error.message}`);
            return { ok: false, error: error.message };
        }
    }

    // 尝试携带Key发送请求
    let result = await tryRequest(requestOptions);
    if (!onlyCookie && !result.ok) {
        requestOptions.headers = { "Authorization": OJBetter.clist.authorization };
        result = await tryRequest(requestOptions);
    }

    // 根据结果显示错误信息
    if (!result.ok) {
        let errorType = result.error;
        const loadingMessage = new LoadingMessage();
        let state;
        if (errorType === 'not_found') {
            state = i18next.t('error.clist.404', { ns: 'alert' });
        } else if (errorType === 'unauthorized') {
            state = i18next.t('error.clist.cookie', { ns: 'alert' });
        } else {
            state = i18next.t('error.clist.other', { ns: 'alert' });
        }
        loadingMessage.updateStatus(`${OJBetter.state.name} —— ${state}`, 'error');
    }
    return result.ok;
}

/**
 * 创建Rating相关css
 * @param {boolean} [hasBorder=true] 是否有边框
 */
function creatRatingCss(hasBorder = true) {
    const defaultBorderColor = '#dcdfe6';
    let dynamicCss = "";
    let hoverSelector = OJBetter.clist.ratingHidden ? ":hover" : "";
    for (let cssClass in cssMap) {
        dynamicCss += `a.${cssClass}${hoverSelector}, a.${cssClass}${hoverSelector}:link {\n`;
        let borderColor = hasBorder ? cssMap[cssClass] : defaultBorderColor;
        dynamicCss += `    color: ${cssMap[cssClass]} ${OJBetter.clist.ratingHidden ? "!important" : ""};\n`;
        dynamicCss += `}\n`;
    }
    GM_addStyle(dynamicCss);
    if (OJBetter.clist.ratingHidden) {
        GM_addStyle(`
        #clistButton {
            color: #ffffff00;
        }
    `);
    }
}

/**
 * 模拟clist网页访问获取rating
 * @param {string} problem 题目名称
 * @param {string} problem_url 题目链接
 * @param {string} contest 比赛名称
 * @returns {Promise<{rating: number, problem: string}>} 题目难度
 */
async function getRatingFromHTML(problem, problem_url, contest = null) {
    // 去除题目名称中的括号,以及首尾的空白符
    problem = problem.replace(/\([\s\S]*?\)/g, '').trim();

    return OJB_promiseRetryWrapper(async () => {
        const queryString = `search=${encodeURIComponent(problem)}&resource=1`;
        const response = await OJB_GMRequest({
            method: 'GET',
            url: `https://clist.by/problems/?${queryString}`,
        });

        if (!response.responseText) throw new OJB_GMError('network', 'An unknown network error occurred!', response);
        const html = response.responseText;
        const cleanedHtml = html.replace(/src=(.|\s)*?"/g, '');
        const parser = new DOMParser();
        const doc = parser.parseFromString(cleanedHtml, 'text/html');
        const trs = doc.querySelectorAll('table tbody tr');

        for (let tr of trs) {
            const rating = tr.querySelector('.problem-rating-column').textContent.trim();
            const link = OJB_cleanLink(tr.querySelector('.problem-name-column a:nth-of-type(2)')?.href);

            if (link === problem_url || link === problem_url + '/') {
                return {
                    rating: parseInt(rating),
                    problem: problem
                };
            } else if (contest !== null) {
                const contestTitles = [...tr.querySelectorAll('.problem-name-column .pull-right a[title], .problem-name-column .pull-right span[title]')].map(el => el.title);
                if (contestTitles.includes(contest)) {
                    return {
                        rating: parseInt(rating),
                        problem: problem
                    };
                }
            }
        }
        console.warn(`No data found for the question: ${problem}`);
    }, {
        maxRetries: 3,
        retryInterval: 500
    });
}

/**
 * 从clist API获取题目的rating
 * @param {string} problem_name 题目名
 * @param {string} problem_url 题目链接
 * @returns {Promise<number>} 题目rating
 * 
 * 使用两个Map对象来存储和快速访问题目信息:
 * - problemsMap: 通过题目的URL作为键来存储题目信息。
 * - nameMap: 通过题目的名称作为键来存储题目信息。
 * 
 * 每个题目信息是一个对象,包含以下属性:
 * @typedef {Object} ProblemInfo
 * @property {string} name 题目名称
 * @property {string} url 题目URL
 * @property {number} rating 题目评分,如果没有评分信息则为NaN
 */
async function getRatingFromApi_problem(problem_name, problem_url) {
    return OJB_promiseRetryWrapper(async () => {
        const response = await OJB_GMRequest({
            method: "GET",
            // url: `https://clist.by:443/api/v4/problem/?name=${encodeURIComponent(problem_name)}&resource__regex=codeforces.com`,
            url: `https://clist.by:443/api/v4/problem/?url__regex=${encodeURIComponent(problem_name)}&resource__regex=atcoder.jp`,
            headers: { "Authorization": OJBetter.clist.authorization }
        });

        if (!response.responseText) throw new OJB_GMError('network', 'An unknown network error occurred!', response);
        let data = JSON.parse(response.responseText);
        /** 
         * 使用题目的URL作为键来存储题目信息。
         * @type {Map<string, ProblemInfo>}
         */
        let problemsMap = new Map();

        /** 
         * 使用题目的名称作为键来存储题目信息。
         * @type {Map<string, ProblemInfo>}
         */
        let nameMap = new Map();

        data.objects.forEach(problem => {
            /** @type {ProblemInfo} 题目信息*/
            let problemInfo = {
                name: problem.name,
                url: problem.url,
                rating: problem.rating ? problem.rating : NaN
            };
            problemsMap.set(OJB_cleanLink(problem.url), problemInfo);
            nameMap.set(problem.name, problemInfo);
        });

        if (problemsMap.has(problem_url)) {
            return problemsMap.get(problem_url).rating;
        } else if (nameMap.has(problem_name)) {
            return nameMap.get(problem_name).rating;
        } else {
            console.warn('Problem not found in the response');
        }
    }, {
        maxRetries: 5,
        retryInterval: 1000
    });
}

/**
 * 获取字符串中的关键词列表
 * @param {string} text 字符串文本
 * @returns {array<string>} 返回关键词列表
 */
function getKeywords(text) {
    // 定义要过滤掉的高频词
    const highFrequencyWords = ['Educational', 'Codeforces', 'Round', 'Div'];

    // 使用正则表达式替换掉特殊符号(保留空格以便分词)
    const sanitizedText = text.replace(/[^\w\s]|_/g, '').replace(/\s+/g, ' ');

    // 将字符串拆分为单词数组
    const words = sanitizedText.split(' ');

    // 过滤掉高频词和空字符串
    const filteredWords = words.filter(word => {
        return word && highFrequencyWords.indexOf(word) === -1;
    });

    // 返回关键词列表
    return filteredWords;
}

/**
 * 根据关键词从 Clist API 中获取实际比赛名称
 * @param {string} contestName 比赛名
 * @param {string} contestUrl 比赛链接
 * @returns {string|null} 该比赛在Clist中的实际名字
 */
async function getContestNameFromApi(contestName, contestUrl) {
    return OJB_promiseRetryWrapper(async () => {
        const options = {
            method: "GET",
            // url: `https://clist.by:443/api/v4/contest/?resource_id=1&event__regex=${encodeURIComponent(contestName)}`,
            url: `https://clist.by:443/api/v4/contest/?resource_id=93&event__regex=${encodeURIComponent(contestName)}`,
            headers: {
                "Authorization": OJBetter.clist.authorization
            }
        };

        let response = await OJB_GMRequest(options);

        if (!response.responseText) throw new OJB_GMError('network', 'An unknown network error occurred!', response);

        let data = JSON.parse(response.responseText);
        let objects = data.objects;

        if (objects.length > 0) {
            for (const contest of objects) {
                // const href = contest.href.replace(/\/contests\//i, '/contest/'); // 链接可能是contests而不是contest,换回来
                const href = contest.href;
                if (OJB_cleanLink(href) == contestUrl) {
                    return contest.event;
                }
            }
        }
        return null;
    }, {
        maxRetries: 5,
        retryInterval: 1000
    });
}

/**
 * 获取在clist中的实际比赛名称
 * @param {string} contestName 待搜索的比赛名称
 * @param {string} contestUrl 比赛的url
 * @returns {Promise<string|null>} 在clist中的实际比赛名称,如果没有找到,则返回null
 */
async function getActualContestName(contestName, contestUrl) {
    // 首先尝试使用完整的比赛名称进行搜索
    let actualContestName = await getContestNameFromApi(contestName, contestUrl);
    if (actualContestName) {
        return actualContestName;
    }

    // 如果使用完整名称没有找到,则尝试使用关键词进行搜索
    const keywords = getKeywords(contestName);
    const maxKeywordAttempts = 1; // 最多尝试到第几个关键词(因为Clist API有频率限制)
    for (let i = 0; i < Math.min(keywords.length, maxKeywordAttempts); i++) {
        actualContestName = await getContestNameFromApi(keywords[i], contestUrl);
        if (actualContestName) {
            return actualContestName;
        }
    }

    // 如果全部尝试后仍没有找到,返回null
    return null;
}

/**
 * 从clist API获取比赛题目集的rating
 * @param {string} contestName 比赛名
 * @returns {Promise<Map<string, number>>} 题目rating
 */
async function getRatingFromApi_contest(contestName, contestUrl) {
    const actualContestName = await getActualContestName(contestName, contestUrl);
    return OJB_promiseRetryWrapper(async () => {
        const options = {
            method: "GET",
            url: `https://clist.by:443/api/v4/contest/?resource_id=93&with_problems=true&event=${encodeURIComponent(actualContestName)}`,
            headers: {
                "Authorization": OJBetter.clist.authorization
            }
        };

        let response = await OJB_GMRequest(options);

        if (!response.responseText) throw new OJB_GMError('network', 'An unknown network error occurred!', response);

        let data = JSON.parse(response.responseText);
        let objects = data.objects;
        let problemsMap = new Map();

        if (objects.length > 0 && objects[0].problems) {
            objects[0].problems.forEach(problem => {
                problemsMap.set(OJB_cleanLink(problem.url), problem.rating ? problem.rating : NaN);
            });
        }

        return problemsMap;
    }, {
        maxRetries: 5,
        retryInterval: 1000
    });
}

/**
 * 根据rating获取对应的颜色class名
 * @param {number} rating 题目rating
 * @returns {string} 颜色class名
 */
function getClassNameByRating(rating) {
    let className = "rating_by_clist_color9";
    if (Number.isNaN(rating)) {
        className = "rating_by_clist_colorNaN";
    } else {
        let keys = Object.keys(ratingClassMap);
        for (let i = 0; i < keys.length; i++) {
            if (rating < keys[i]) {
                className = ratingClassMap[keys[i - 1]];
                break;
            }
        }
    }
    return className;
}

/**
 * problem题目页显示Rating
 * @param {ProblemPageLinkbar} problemToolbar 
 * @returns {Promise<void>}
 */
async function showRatingByClist_problem(problemToolbar) {
    // 题目名
    // const problem = $('.header .title').eq(0).text().replace(/[\s\S]*?. /, '');
    // if (OJBetter.typeOfPage.is_acmsguru) problem = $('h4').eq(0).text().replace(/[\s\S]*?. /, '');
    const url = window.location.href;
    const problemId = getProblemId(url);

    // 创建Rating按钮元素
    creatRatingCss(false);
    // TODO
    // const clistButton = problemToolbar.addLinkButton(
    //     'clistButton',
    //     `https://clist.by/problems/?search=${problem}&resource=1`,
    //     i18next.t('state.wait', { ns: 'button' }),
    //     $("<img>").attr("src", "https://clist.by/static/img/logo-48.png"),
    //     "15px"
    // );
    const clistButton = problemToolbar.addLinkButton(
        'clistButton',
        `https://clist.by/problems/?search=${problemId}&resource=93`,
        i18next.t('state.wait', { ns: 'button' }),
        $("<img>").attr("src", "https://clist.by/static/img/logo-48.png"),
        "15px"
    );

    // 检测clist连接
    if (!await validateClistConnection()) {
        problemToolbar.updateText(clistButton, i18next.t('state.netError', { ns: 'button' }));
        return;
    }

    // 题目链接
    let problem_url = window.location.href;
    if (problem_url.includes('/contest/')) {
        problem_url = problem_url.replace(/\/contest\/(\d+)\/problem\/(\w+)[^\w]*/, '/contest/$1/problem/$2');
    } else {
        problem_url = problem_url.replace(/\/problemset\/problem\/(\d+)\/(\w+)/, '/contest/$1/problem/$2');
    }
    if (OJBetter.typeOfPage.is_mSite) problem_url = problem_url.replace(/\/\/(\w+).codeforces.com/, '//codeforces.com'); // 轻量站

    // 比赛名
    // let contest = $('#sidebar').children().first().find('.rtable th').first().text();

    // rating
    problemToolbar.updateText(clistButton, i18next.t('state.loading', { ns: 'button' }));
    let rating = await getRatingFromApi_problem(problemId, problem_url);
    if (rating) {
        let className = getClassNameByRating(rating);
        problemToolbar.updateText(clistButton, rating);
        problemToolbar.setBold(clistButton);
        problemToolbar.addClass(clistButton, className);
    } else {
        problemToolbar.updateText(clistButton, i18next.t('state.404', { ns: 'button' }));
        problemToolbar.disableButton(clistButton);
    }
}

/**
 * contest页显示Rating
 * @returns {Promise<void>}
 */
async function showRatingByClist_contest() {
    // 创建Rating显示框
    creatRatingCss();
    let ratingBadges = {};
    // $('.datatable .id.left').each(function () {
    //     let href = 'https://codeforces.com' + $(this).find('a').attr('href');
    //     let badge = OJB_safeCreateJQElement(`<a id="clistButton" class="ratingBadge">${i18next.t('state.wait', { ns: 'button' })}</a>`);
    //     $(this).find('a').after(badge);
    //     ratingBadges[href] = badge;
    // });
    $('table tbody tr').each(function () {
        let href = 'https://atcoder.jp' + $(this).find('a').attr('href');
        let badge = OJB_safeCreateJQElement(`<a id="clistButton" class="ratingBadge">${i18next.t('state.wait', { ns: 'button' })}</a>`);
        $(this).find('a:first').after(badge);
        ratingBadges[href] = badge;
    });

    // 检测clist连接
    if (!await validateClistConnection()) {
        for (let href in ratingBadges) {
            ratingBadges[href].text('error').addClass('ratingBadge_error');
        }
        return;
    }

    // 显示loading
    for (let href in ratingBadges) {
        ratingBadges[href].text(i18next.t('state.loading', { ns: 'button' })).addClass('ratingBadge_loading');
    }

    // 获取Rating
    // let contestName = $('#sidebar').children().first().find('.rtable th').first().text();
    let contestName = window.location.href.match(/\/contests\/[^\/]*?(\d+)\/tasks/)?.[1];
    // let contestUrl = OJB_cleanLink(window.location.href);
    let contestUrl = OJB_cleanLink(window.location.href.replace(/\/tasks\/?.*$/, ''));
    try {
        let problemsMap = await getRatingFromApi_contest(contestName, contestUrl);

        // 填充数据
        for (let href in ratingBadges) {
            if (problemsMap.has(href)) {
                let rating = problemsMap.get(href);
                let className = getClassNameByRating(rating);
                ratingBadges[href].text(rating).addClass(className);
            } else {
                ratingBadges[href].text(i18next.t('state.404', { ns: 'button' })).addClass('ratingBadge_no');
            }
        }
    } catch (error) {
        // 填充数据
        for (let href in ratingBadges) {
            ratingBadges[href].text(i18next.t('state.netError', { ns: 'button' })).addClass('ratingBadge_no');
        }
        console.warn(error);
    }
}

/**
 * problemset页显示Rating
 * @returns {Promise<void>}
 */
async function showRatingByClist_problemset() {
    creatRatingCss();
    let ratingBadges = [];
    const $problems = $('.problems');
    const $trs = $problems.find('tbody tr:gt(0)');

    // 先创建Rating显示框,并将关系存进数组ratingBadges
    for (let i = 0; i < $trs.length; i++) {
        const $tds = $($trs[i]).find('td');
        const $firstDiv = $($tds[1]).find('div:first');
        let problem = $firstDiv.text();
        let problem_url = $firstDiv.find('a').attr('href');
        problem_url = problem_url.replace(/^\/problemset\/problem\/(\d+)\/(\w+)/, 'https://codeforces.com/contest/$1/problem/$2');

        const ratingBadge = OJB_safeCreateJQElement(`<a id="clistButton" class="ratingBadge"></a>`);
        const rating = OJB_safeCreateJQElement(`<span class="rating">${i18next.t('state.wait', { ns: 'button' })}</span>`);
        ratingBadge.append(rating);
        $($tds[0]).find('a').after(ratingBadge);
        ratingBadges.push({ ratingBadge, rating, problem, problem_url });
    }

    // 检测clist连接
    if (!await validateClistConnection()) {
        for (let i = 0; i < rating.length; i++) {
            ratingBadges[i].rating.text(i18next.t('state.netError', { ns: 'button' }));
        }
        return;
    }

    // 每次只获取3个rating
    for (let i = 0; i < ratingBadges.length; i += 3) {
        const promises = [];
        const endIndex = Math.min(i + 3, ratingBadges.length);

        for (let j = i; j < endIndex; j++) {
            const ratingBadge = ratingBadges[j];
            // 显示请求中
            ratingBadge.rating.text(i18next.t('state.loading', { ns: 'button' }));
            promises.push(getRatingFromHTML(ratingBadge.problem, ratingBadge.problem_url).catch(error => console.warn(error)));
        }

        const results = await Promise.all(promises);

        for (let j = i; j < endIndex; j++) {
            const result = results[j - i];
            const ratingBadge = ratingBadges[j];
            if (result) {
                let className = getClassNameByRating(result.rating);
                ratingBadge.ratingBadge.addClass(className);
                ratingBadge.rating.text(result.rating);
            } else {
                ratingBadge.rating.text(i18next.t('state.404', { ns: 'button' }));
            }
        }
    }
}

/**
 * 存放编辑器语言select的值与Monaco语言对应关系的map.
 * @type {Object.<string, string>}
 */
// const value_monacoLanguageMap = {
//     "4": "pascal", "6": "php", "7": "python", "9": "csharp", "13": "perl", "20": "scala", "31": "python",
//     "32": "go", "34": "javascript", "36": "java", "40": "python", "41": "python", "43": "cpp",
//     "50": "cpp", "51": "pascal", "52": "cpp", "54": "cpp", "55": "javascript", "59": "cpp", "60": "java",
//     "61": "cpp", "65": "csharp", "67": "ruby", "70": "python", "73": "cpp", "74": "java", "75": "rust",
//     "77": "kotlin", "79": "csharp", "80": "cpp", "83": "kotlin", "87": "java"
// };
const value_monacoLanguageMap = {
    "5001": "cpp", "5002": "go", "5003": "csharp", "5004": "kotlin", "5005": "java",
    "5006": "nim", "5007": "text", "5008": "text", "5009": "javascript", "5010": "javascript",
    "5011": "r", "5012": "d", "5013": "d", "5014": "swift", "5015": "dart",
    "5016": "php", "5017": "cpp", "5018": "ruby", "5019": "crystal", "5020": "text",
    "5021": "fsharp", "5022": "julia", "5023": "sh", "5024": "text", "5025": "haskell",
    "5026": "fortran", "5027": "lua", "5028": "cpp", "5029": "lisp", "5030": "cobol",
    "5031": "cpp", "5032": "sh", "5033": "python", "5034": "sh", "5035": "text",
    "5036": "text", "5037": "perl", "5038": "sh", "5039": "text", "5040": "text",
    "5041": "pascal", "5042": "csharp", "5043": "lua", "5044": "prolog", "5045": "sh",
    "5046": "scheme", "5047": "scala", "5048": "vbscript", "5049": "text", "5050": "clojure",
    "5051": "erlang", "5052": "typescript", "5053": "cpp", "5054": "rust", "5055": "python",
    "5056": "scala", "5057": "text", "5058": "typescript", "5059": "ocaml", "5060": "raku",
    "5061": "text", "5062": "lisp", "5063": "python", "5064": "clojure", "5065": "text",
    "5066": "text", "5067": "text", "5068": "ada", "5069": "text", "5070": "text",
    "5071": "clojure", "5072": "cpp", "5073": "cpp", "5074": "text", "5075": "lisp",
    "5076": "text", "5077": "d", "5078": "python", "5079": "text", "5080": "text",
    "5081": "ocaml", "5082": "python", "5083": "matlab", "5084": "haxe", "5085": "elixir",
    "5086": "text", "5087": "text", "5088": "lisp", "5089": "text", "5090": "cobol"
};

/**
 * 更新代码提交页的HTML
 * @param {string} submitUrl 提交页面的URL
 * @param {string} cacheKey 本地缓存的键名
 * @returns {Promise<jQuery<HTMLElement>>} 返回 jQuery 包装的 HTML 元素
 */
async function CloneOriginalHTML(submitUrl, cacheKey) {
    return OJB_promiseRetryWrapper(async () => {
        const response = await OJB_GMRequest({
            method: 'GET',
            url: submitUrl
        });
        const html = response.responseText;
        const parser = new DOMParser();
        const doc = parser.parseFromString(html, 'text/html');
        const cloneHTML = $(doc.body).html();
        localStorage.setItem(cacheKey, html);
        return $(cloneHTML);
    }, {
        maxRetries: 5,
        retryInterval: 1000,
        errorHandler: (err) => {
            console.error('A network error occurred while retrieving the HTML for the code submission page.', submitUrl);
        }
    });
}

/**
 * 获取代码提交页的HTML元素
 * @param {string} submitUrl 
 * @returns {Promise<jQuery>}
 */
async function getSubmitHTML(submitUrl) {
    const cacheKey = 'OJBetter_CloneOriginalHTML';
    const cookieKey = 'OJBetter_CloneOriginalHTML_time';
    if (OJB_getCookie(cookieKey) === '1') {
        // 存在缓存
        CloneOriginalHTML(submitUrl, cacheKey);
        // 校验
        let cloneHTML = $(localStorage.getItem(cacheKey));
        if (cloneHTML.find('form.submit-form').length > 0) {
            return cloneHTML;
        } else {
            // 存在错误,更新缓存
            console.warn(`Cache error detected!\nattempting to update, cache destination submitUrl:\n${submitUrl}`);
            return await CloneOriginalHTML(submitUrl, cacheKey);
        }

    } else {
        // 没有缓存,更新
        document.cookie = `${cookieKey}=1; path=/`;
        return await CloneOriginalHTML(submitUrl, cacheKey);
    }
}

// 代码自动保存
async function saveCode(url, code) {
    try {
        await OJBetter.common.database.editorCode.put({ url, code });
        return 'Code saved successfully';
    } catch (error) {
        throw new Error('Failed to save code');
    }
}

async function getCode(url) {
    try {
        const result = await OJBetter.common.database.editorCode.get(url);
        return result ? result.code : null;
    } catch (error) {
        throw new Error('Failed to get code');
    }
}

// 创建代码编辑调试表单元素
// async function createCodeEditorForm(submitUrl, cloneHTML) {
async function createCodeEditorForm(submitUrl) {
    // 表单
    let formDiv = $('<form method="post" id="OJBetter_SubmitForm" class="input-output-copier"></form>');
    // $('.ttypography').after(formDiv);
    $('#task-statement').after(formDiv);
    // formDiv.attr('action', submitUrl + "?csrf_token=" + OJBetter.common.at_csrf_token);
    formDiv.attr('action', submitUrl);
    formDiv.attr('method', 'POST');

    // 顶部区域
    let topDiv = OJB_safeCreateJQElement(`<div class="topDiv"></div>`);
    let selectLang = $('#select-lang').clone(); // 语言选择
    // selectLang.css({ 'margin': '10px 0px' }).attr('id', 'programTypeId');
    topDiv.append(selectLang);
    let topRightDiv = OJB_safeCreateJQElement(`<div class="topRightDiv"></div>`);
    topDiv.append(topRightDiv);
    formDiv.append(topDiv);

    // 问题选择/编号
    // let selectProblem = $('<input name="submittedProblemIndex" style="display:none;"></input>');
    // let problemCode;
    // if (OJBetter.typeOfPage.is_acmsguru) {
    //     problemCode = $('h4').eq(0).text();
    //     let matchResult = problemCode.match(/([A-Z0-9]+)/);
    //     problemCode = matchResult[0];
    // } else if (OJBetter.typeOfPage.is_problemset_problem) {
    //     let match = window.location.href.match(/\/problem\/([0-9]+?)\/([A-Za-z0-9]+?)(?!=[A-Za-z0-9])/);
    //     problemCode = match[1] + match[2];
    //     selectProblem.attr('name', 'submittedProblemCode');
    // } else {
    //     problemCode = $('.header .title').eq(0).text();
    //     let matchResult = problemCode.match(/([A-Z0-9]+)/);
    //     problemCode = matchResult[0];
    // }
    // selectProblem.val(problemCode);
    let selectProblem = $('input[name="data.TaskScreenName"]').clone();
    formDiv.append(selectProblem);

    // 隐藏的代码记录
    // let sourceDiv = $('<textarea id="sourceCodeTextarea" name="source" style="display: none;"></textarea>');
    let sourceDiv = $('<textarea id="plain-textarea" name="sourceCode" style="display: none;"></textarea>');
    formDiv.append(sourceDiv);

    // 隐藏的crsf token
    let csrfDiv = $(`<input type="hidden" name="csrf_token" value=${OJBetter.common.at_csrf_token}>`);
    formDiv.append(csrfDiv);

    // 代码编辑器
    let editorDiv = $('<div id="OJBetter_editor"></div>');
    formDiv.append(editorDiv);

    // monaco
    let monaco = $('<div id="OJBetter_monaco"></div>');
    editorDiv.append(monaco);

    // 自定义调试
    let customTestDiv = OJB_safeCreateJQElement(`
        <details id="customTestBlock">
            <summary >${i18next.t('customTestBlock.title', { ns: 'codeEditor' })}</summary>
            <div id="customTests" style="min-height: 30px;"></div>
            <div id="control" style="display:flex;">
                <div style="display: flex;margin: 5px;">
                    <input type="checkbox" id="onlyCustomTest"}><label for="onlyCustomTest">
                    ${i18next.t('customTestBlock.onlyCustom', { ns: 'codeEditor' })}
                    </label>
                </div>
                <div style="display: flex;margin: 5px;">
                    <input type="checkbox" id="DontShowDiff"}>
                    <label for="DontShowDiff">
                        ${i18next.t('customTestBlock.DontShowDiff', { ns: 'codeEditor' })}
                    </label>
                </div>
                <button type="button" id="addCustomTest">${i18next.t('customTestBlock.add', { ns: 'codeEditor' })}</button>
            </div>
        </details>
    `)
    formDiv.append(customTestDiv);

    // 调试/提交
    let submitDiv = $('<div id="OJBetter_submitDiv"></div>');
    let CompilerArgsInput = $('<input type="text" id="CompilerArgsInput">');
    submitDiv.append(CompilerArgsInput);

    let runButton = OJB_safeCreateJQElement(`
        <button type="button" id="RunTestButton" class="ojb_btn ojb_btn_popover top">
            <i class="iconfont">&#xe6c1;</i>
            <span class="popover_content">${i18next.t('runTestButton.initial', { ns: 'codeEditor' })}</span>
        </button>
    `);
    let submitButton = OJB_safeCreateJQElement(`
        <button id="SubmitButton" class="ojb_btn ojb_btn_popover top" type="submit">
            <i class="iconfont">&#xe633;</i>
            <span class="popover_content">${i18next.t('submitButton', { ns: 'codeEditor' })}</span>
        </button>
    `);
    if (OJBetter.monaco.setting.submitButtonPosition == "bottom") {
        // 添加测试/提交按钮到底部
        submitDiv.append(runButton);
        submitDiv.append(submitButton);
    }

    formDiv.append(submitDiv);
    let CompilerSetting = OJB_safeCreateJQElement(`
        <div id="CompilerSetting"></div>
    `);
    formDiv.append(CompilerSetting);
    let statePanel = OJB_safeCreateJQElement(`
        <div id="statePanel"></div>
    `);
    formDiv.append(statePanel);

    //==================================
    // 去除原有的编辑器
    //==================================
    $('.form-code-submit').remove();

    let from = {
        formDiv: formDiv,
        selectLang: selectLang.find('select:first'),
        topRightDiv: topRightDiv,
        sourceDiv: sourceDiv,
        editorDiv: editorDiv,
        monaco: monaco,
        runButton: runButton,
        submitButton: submitButton,
        submitDiv: submitDiv,
        CompilerSetting: CompilerSetting,
        statePanel: statePanel
    };
    return from;
}

// 解析ace格式的补全规则(acwing)
function parseAceCompleter(rules, range) {
    const suggestions = [];
    if (rules && rules.templates && rules.templates.items) {
        const items = rules.templates.items;
        for (let i = 0; i < items.length; i++) {
            const item = items[i];
            const parts = item.caption.split(' ');
            for (let i = 0; i < parts.length; i++) {
                if (item.value.startsWith(parts[i])) {
                    item.value = item.value.replace(parts[i], parts.slice(0, i + 1).join(' '));
                    break;
                }
            }
            const completionItem = {
                label: item.caption,
                kind: monaco.languages.CompletionItemKind.Function,
                insertText: item.value,
                range: range
            };
            suggestions.push(completionItem);
        }
    }
    return { suggestions };
}

// 解析monaco格式的补全规则
function parseMonacoCompleter(rules, range) {
    const suggestions_ = [];
    if (rules && rules.suggestions) {
        const suggestion = rules.suggestions;
        for (let i = 0; i < rules.suggestions.length; i++) {
            const item = suggestion[i];
            const completionItem = {
                ...item,
                range: range
            };
            suggestions_.push(completionItem);
        }
    }
    return { suggestions: suggestions_ };
}

/**
 * 创建monaco编辑器的一个实例
 */
async function createMonacoEditor(language, form, support) {
    // 判断monacoLoader是否加载完毕
    await OJB_waitUntilTrue(() => OJBetter.monaco.loaderOnload);

    /**
     * 通用参数
     */
    var id = 0; // 协议中的id标识
    var workspace = language + "_workspace";
    var rootUri = OJBetter.monaco.lsp.workUri + "/" + workspace;
    // 文件名
    var InstanceID = OJB_getRandomNumber(8).toString();
    var filename = language == "java" ? "hello/src/" + InstanceID : InstanceID;
    // 后缀名
    var fileExtension =
        language === "cpp"
            ? ".cpp"
            : language === "python"
                ? ".py"
                : language === "java"
                    ? ".java"
                    : "";
    var uri = rootUri + "/" + filename + fileExtension;
    var initialized = false; // 是否已初始化
    var serverInfo; // 服务器返回的支持信息
    var model; // model
    var OJBetter_monaco = {};
    window.OJBetter_monaco = OJBetter_monaco; // 全局方法

    /**
     * 一些工具函数
     */
    // 将lsp格式的rang转换为Monaco格式
    OJBetter_monaco.lspRangeToMonacoRange = function (range) {
        const { start, end } = range;
        return new monaco.Range(
            start.line + 1,
            start.character + 1,
            end.line + 1,
            end.character + 1
        );
    };
    // 将Monaco格式的rang转为lsp格式
    OJBetter_monaco.MonacoRangeTolspRange = function (range) {
        return {
            start: {
                line: range.startLineNumber - 1,
                character: range.startColumn - 1,
            },
            end: {
                line: range.endLineNumber - 1,
                character: range.endColumn - 1,
            },
        };
    };
    // 将Monaco格式的position转为lsp格式的
    OJBetter_monaco.MonacoPositionTolspPosition = function (position) {
        return {
            line: position.lineNumber - 1,
            character: position.column - 1,
        };
    };
    // 将Monaco格式的severity转为lsp格式的
    OJBetter_monaco.MonacoSeverityTolspSeverity = function (severity) {
        switch (severity) {
            case 8:
                return 1;
            case 1:
                return 4;
            case 2:
                return 3;
            case 4:
                return 2;
            default:
                return severity;
        }
    };
    // 将lsp格式的severity转为Monaco格式的
    OJBetter_monaco.lspSeverityToMonacoSeverity = function (severity) {
        switch (severity) {
            case 1:
                return 8;
            case 4:
                return 1;
            case 3:
                return 2;
            case 2:
                return 4;
            default:
                return severity;
        }
    };
    // 收集Monaco数据中的rang数据
    OJBetter_monaco.CollectRange = function (item) {
        return {
            startLineNumber: item.startLineNumber,
            startColumn: item.startColumn,
            endLineNumber: item.endLineNumber,
            endColumn: item.endColumn,
        };
    };
    // 收集Monaco position数据中的rang数据
    OJBetter_monaco.CollectRangeByPosition = function (item) {
        var word = model.getWordUntilPosition(item);
        return {
            startLineNumber: item.lineNumber,
            endLineNumber: item.lineNumber,
            startColumn: word.startColumn,
            endColumn: word.endColumn,
        };
    };
    // 将lsp格式的Edit转换为Monaco格式
    OJBetter_monaco.lspEditToMonacoEdit = function (edit) {
        const edits = [];

        if (language == "python") {
            for (const item1 of edit.documentChanges) {
                for (const item2 of item1.edits) {
                    const newElement = {
                        textEdit: {
                            range: OJBetter_monaco.lspRangeToMonacoRange(item2.range),
                            text: item2.newText,
                        },
                        resource: monaco.Uri.parse(item1.textDocument.uri),
                        versionId: model.getVersionId(),
                    };
                    edits.push(newElement);
                }
            }
        } else if (language == "java") {
            for (const item1 in edit.changes) {
                edit.changes[item1].forEach((item2) => {
                    const newElement = {
                        textEdit: {
                            range: OJBetter_monaco.lspRangeToMonacoRange(item2.range),
                            text: item2.newText,
                        },
                        resource: uri,
                        versionId: model.getVersionId(),
                    };
                    edits.push(newElement);
                });
            }
        } else {
            for (const key in edit.changes) {
                const arr = edit.changes[key];
                for (const item of arr) {
                    const newElement = {
                        textEdit: {
                            range: OJBetter_monaco.lspRangeToMonacoRange(item.range),
                            text: item.newText,
                        },
                        resource: monaco.Uri.parse(key),
                        versionId: model.getVersionId(),
                    };
                    edits.push(newElement);
                }
            }
        }
        return { edits: edits };
    };

    /**
     * 实例化一个editor
     */
    uri = monaco.Uri.file(uri);
    model = monaco.editor.createModel('', language, uri);
    OJBetter.monaco.editor = monaco.editor.create(document.getElementById("OJBetter_monaco"), {
        model: model,
        rootUri: rootUri,
        fontSize: 15,
        tabSize: 4,
        theme: OJBetter.basic.darkMode == "dark" ? "vs-dark" : "vs",
        bracketPairColorization: {
            enabled: true,
            independentColorPoolPerBracketType: true,
        },
        automaticLayout: true,
        lineNumbersMinChars: 3,
        matchOnWordStartOnly: false,
        wordWrap: "on",
        wrappingIndent: "same",
        glyphMargin: true,
        formatOnType: true,
        scrollbar: {
            verticalScrollbarSize: 10,
            horizontalScrollbarSize: 10,
            alwaysConsumeMouseWheel: OJBetter.monaco.setting.alwaysConsumeMouseWheel
        },
        suggest: {
            selectionMode: 'never' // 代码建议不自动选择
        }
    });

    /**
     * 添加快捷功能
     */
    (OJBetter_monaco.addShortCuts = async () => {
        // 从配置信息更新字体大小
        OJBetter.monaco.editor.updateOptions({ fontSize: parseInt(OJBetter.monaco.setting.fontsize) });

        // 调整字体大小
        let changeSize = OJB_safeCreateJQElement(`
        <div class="ojb_btn ojb_btn_popover top">
            <input type="number" id="fontSizeInput" value="${OJBetter.monaco.setting.fontsize}">
            <span class="popover_content">${i18next.t('fontSizeInput', { ns: 'codeEditor' })}</span>
        </div>`)
        form.topRightDiv.append(changeSize);
        changeSize.find('input#fontSizeInput').on('input', function () {
            var size = $(this).val();
            OJBetter.monaco.editor.updateOptions({ fontSize: parseInt(size) });
            GM_setValue('editorFontSize', size);
        });

        // 全屏按钮
        let fullscreenButton = OJB_safeCreateJQElement(`
        <button type="button" class="ojb_btn ojb_btn_popover top">
            <i class="iconfont">&#xe606;</i>
            <span class="popover_content">${i18next.t('fullscreenButton', { ns: 'codeEditor' })}</span>
        </button>
        `);
        form.topRightDiv.append(fullscreenButton);
        fullscreenButton.on('click', enterFullscreen);

        // 固定到底部按钮
        let fixToBottomButton = OJB_safeCreateJQElement(`
        <button type="button" class="ojb_btn ojb_btn_popover top">
            <i class="iconfont">&#xe607;</i>
            <span class="popover_content">${i18next.t('fixToBottomButton', { ns: 'codeEditor' })}</span>
        </button>
        `);
        form.topRightDiv.append(fixToBottomButton);
        fixToBottomButton.on('click', fixToBottom);

        // 固定到右侧按钮
        let fixToRightButton = OJB_safeCreateJQElement(`
        <button type="button" class="ojb_btn ojb_btn_popover top">
            <i class="iconfont">&#xe605;</i>
            <span class="popover_content">${i18next.t('fixToRightButton', { ns: 'codeEditor' })}</span>
        </button>
        `);
        // form.topRightDiv.append(fixToRightButton);
        fixToRightButton.on('click', fixToRight);

        // 添加测试/提交按钮到顶部
        if (OJBetter.monaco.setting.submitButtonPosition == "top") {
            form.topRightDiv.append(form.runButton);
            form.topRightDiv.append(form.submitButton);
        }

        // 选择记忆
        if (!OJBetter.monaco.setting.position_initialized) {
            OJBetter.monaco.setting.position_initialized = true; // 标记是否已经初始化过
            if (OJBetter.monaco.setting.position == "full") {
                fullscreenButton.click();
            } else if (OJBetter.monaco.setting.position == "bottom") {
                fixToBottomButton.click();
            } else if (OJBetter.monaco.setting.position == "right") {
                fixToRightButton.click();
            }
        }

        // 禁用按钮
        function disableButtons() {
            fullscreenButton.prop("disabled", true);
            fixToBottomButton.prop("disabled", true);
            fixToRightButton.prop("disabled", true);
        }

        // 启用按钮
        function enableButtons() {
            fullscreenButton.prop("disabled", false);
            fixToBottomButton.prop("disabled", false);
            fixToRightButton.prop("disabled", false);
        }

        // 进入全屏
        function enterFullscreen() {
            let editor = $('#OJBetter_editor');
            editor.addClass('fullscreen');

            // 取消按钮
            let cancelButton = OJB_safeCreateJQElement(`
                <button type="button" class="ojb_btn ojb_btn_popover top primary exit_button_bottom">
                    <i class="iconfont">&#xe60b;</i>
                    <span class="popover_content">${i18next.t('exitFullscreenButton', { ns: 'codeEditor' })}</span>
                </button>
            `).on('click', () => exitFullscreen(cancelButton));
            $('body').append(cancelButton);

            disableButtons();
            GM_setValue("monacoEditor_position", "full");
        }

        // 退出全屏
        const exitFullscreen = (cancelButton) => {
            let editor = $('#OJBetter_editor');
            editor.removeClass('fullscreen');
            cancelButton.remove();
            enableButtons();
            GM_setValue("monacoEditor_position", "initial");
        };

        // 固定到底部
        function fixToBottom() {
            let editor = $('#OJBetter_editor');
            editor.addClass('bottom');

            let halfHeight = $(window).height() * 0.5;
            let blankSpace = $('<div>', {
                'class': 'blank-space',
                'style': 'height: ' + (halfHeight + 30) + 'px;'
            });
            $('body').append(blankSpace);

            let cancelButton = OJB_safeCreateJQElement(`
                <button type="button" class="ojb_btn ojb_btn_popover top enabled exit_button_bottom">
                    <i class="iconfont">&#xe625;</i>
                    <span class="popover_content">${i18next.t('cancelFixButton', { ns: 'codeEditor' })}</span>
                </button>
            `).on('click', () => cancelFixingToBottom(cancelButton, blankSpace));
            $('body').append(cancelButton);

            disableButtons();
            GM_setValue("monacoEditor_position", "bottom");
        }

        // 取消固定到底部
        const cancelFixingToBottom = (cancelButton, blankSpace) => {
            let editor = $('#OJBetter_editor');
            editor.removeClass('bottom');
            cancelButton.remove();
            blankSpace.remove();
            enableButtons();
            GM_setValue("monacoEditor_position", "initial");
        };

        // 固定到右侧边栏
        function fixToRight() {
            const sidebar = $('#sidebar').hide();

            // 添加样式
            const styleElement = GM_addStyle(`
                #body {
                    min-width: 50vw;
                    max-width: 50vw;
                    max-height: 100vh;
                    overflow-x: hidden;
                    overflow-y: auto;
                    padding: 1rem;
                    box-sizing: border-box;
                }
                body {
                    margin: 0px;
                }
                .content-with-sidebar {
                    margin-right: 0px !important;
                }
                .menu-list li {
                    margin-right: 0.5em;
                }
                .menu-list li a {
                    font-size: 1.4rem;
                }
                #OJBetter_editor{
                    height: 100vh;
                    width: 50vw;
                }
            `);

            // 包装一层div
            $('#body').wrap('<div id="right-side-wrapper" style="display:flex; max-width: 100vw; overflow: hidden;"></div>');
            const blankSpace = $('<div>').appendTo('#right-side-wrapper');

            // 移动编辑器
            const editor = $('#OJBetter_editor').prependTo(blankSpace).addClass('right-side');

            // 取消按钮
            const cancelButton = OJB_safeCreateJQElement(`
                <button type="button" class="ojb_btn ojb_btn_popover top enabled exit_button_bottom">
                    <i class="iconfont">&#xe625;</i>
                    <span class="popover_content">${i18next.t('cancelFixButton', { ns: 'codeEditor' })}</span>
                </button>
            `).on('click', () => cancelFixingToRight(sidebar, styleElement, editor, cancelButton)).appendTo('body');

            disableButtons();
            GM_setValue("monacoEditor_position", "right");

            // 补丁:修复固定到右侧导致的样例元素.sample-test相关代码重复执行的问题(具体原因未查)
            $('.sample-test').find('.title').each((i, e) => {
                if ($(e).find('.input-output-copier').length > 1) {
                    $(e).find('.input-output-copier').first().remove();
                }
            });
            darkModeStyleAdjustment();
        }

        const cancelFixingToRight = (sidebar, styleElement, editor, cancelButton) => {
            sidebar.show();
            // 移回来
            editor.insertAfter(form.sourceDiv).removeClass('right-side');

            // 移除包装
            $('#body').unwrap();
            cancelButton.remove();
            styleElement.remove(); // 移除添加的样式

            enableButtons();
            GM_setValue("monacoEditor_position", "initial");
        }

        // 代码同步与保存
        if (OJBetter.monaco.setting.autoMemoryCode) {
            let nowUrl = window.location.href;
            nowUrl = nowUrl.replace(/#/, ""); // 当页面存在更改时url会多出一个#,去掉
            const code = await getCode(nowUrl);
            if (code) {
                OJBetter.monaco.editor.setValue(code); // 恢复代码
                form.sourceDiv.val(code);
            }
            OJBetter.monaco.editor.onDidChangeModelContent(async () => {
                // 将monaco editor的内容同步到sourceDiv
                const code = OJBetter.monaco.editor.getValue();
                form.sourceDiv.val(code);
                await saveCode(nowUrl, code);
            });
        }
    })();

    /**
     * 注册本地自动补全
     */
    (OJBetter_monaco.RegisterLocalComplet = async () => {
        // 补全器注册函数
        function registMyCompletionItemProvider(language, genre, rule) {
            if (genre == "monaco") {
                monaco.languages.registerCompletionItemProvider(language, {
                    provideCompletionItems: function (model, position) {
                        return parseMonacoCompleter(rule, OJBetter_monaco.CollectRangeByPosition(position));
                    }
                })
            } else if (genre == "ace") {
                monaco.languages.registerCompletionItemProvider(language, {
                    provideCompletionItems: function (model, position) {
                        return parseAceCompleter(rule, OJBetter_monaco.CollectRangeByPosition(position));
                    }
                })
            }
        }

        // 注册acwing cpp 模板
        if (language == "cpp" && OJBetter.monaco.complet.cppCodeTemplate) {
            try {
                var acwing_cpp_code_completer = JSON.parse(GM_getResourceText("acwing_cpp_code_completer"));
                registMyCompletionItemProvider('cpp', 'ace', acwing_cpp_code_completer);
            } catch (error) {
                console.error("Error registering acwing cpp template:", error);
            }
        }

        // 注册自定义的补全
        let complet_length = OJBetter.monaco.complet.customConfig.configurations.length;
        if (complet_length > 0) {
            for (let i = 0; i < complet_length; i++) {
                let item = OJBetter.monaco.complet.customConfig.configurations[i];
                if (item.isChoose && item.language == language) {
                    try {
                        let rule = await OJB_getExternalJSON(item.jsonUrl);
                        registMyCompletionItemProvider(item.language, item.genre, rule);
                    } catch (error) {
                        console.error(`Error registering custom completer for ${item.language}:`, error);
                    }
                }
            }
        }
    })();

    if (!support || !OJBetter.monaco.lsp.enabled) { return; } // 如果不支持lsp,则到此为止

    /**
     * LSP连接状态指示
     */
    const lspStateButton = OJB_safeCreateJQElement(`
    <div id="lspStateDiv" class="ojb_btn ojb_btn_popover top loading">
        <i class="iconfont">&#xe658;</i>
        <span class="popover_content">${i18next.t('lsp.connect', { ns: 'codeEditor' })}</span>
    </div>
    `).on('click', () => {
        OJB_showModal(LSPLogDiv);
        LSPLogDiv.show();
    });
    form.topRightDiv.prepend(lspStateButton);

    const LSPLogDiv = OJB_safeCreateJQElement(`
    <dialog id="LSPLog" style="display: none;">
        <button class="ojb_btn">${i18next.t('close', { ns: 'common' })}</button>
        <div id="LSPLogList" style="overflow: auto;"></div>
    <dialog>`);
    $('body').append(LSPLogDiv);

    const LSPLogList = $('<ul></ul>');
    $('#LSPLogList').append(LSPLogList);

    const closeButton = LSPLogDiv.find('button');
    closeButton.on('click', function () {
        OJB_closeModal(LSPLogDiv);
    });

    /**
     * 推送新的消息到LSP日志中
     * @param {'error' | 'warn' | 'info'} status 
     * @param {string} msg 
     * @param {boolean} data 
     */
    function pushLSPLogMessage(status, msg, data) {
        var li = $('<li>').text('[' + new Date().toLocaleString() + '] ' + msg);
        if (status === 'error') {
            li.attr('style', 'color: #f44336;');
        } else if (status === 'warn') {
            li.attr('style', 'color: #ff9800;');
        } else if (status === 'info') {
            li.attr('style', 'color: #616161;');
        }
        if (data) {
            var jsonText = JSON.stringify(data, null, 2);
            var details = $('<details>');
            var summary = $('<summary>').text('Data');
            var pre = $('<pre>').text(jsonText);
            details.append(summary, pre);
            li.append(details);
        }
        LSPLogList.append(li);
    }

    /**
     * 添加状态底栏
     */
    var statusBar = $('<div id="OJBetter_statusBar">');
    form.editorDiv.append(statusBar);

    /**
     * languageSocket
     */
    var url = OJBetter.monaco.lsp.socketUrl;
    var languageSocket = new WebSocket(url + language);
    OJBetter.monaco.lsp.socket.push(languageSocket);
    var languageSocketState = false;
    var responseHandlers = new Map(); // 映射表,需要等待返回数据的请求 -> 对应的事件触发函数

    languageSocket.onopen = () => {
        languageSocketState = true;
        lspStateButton.setButtonPopover(i18next.t('lsp.waitingAnswer', { ns: 'codeEditor' }));
        pushLSPLogMessage("info", `languageSocket ${i18next.t('lsp.socket.open', { ns: 'logMessage' })}`);
    };
    languageSocket.onmessage = (event) => {
        const message = JSON.parse(event.data);
        if (message.id === 0 && message.result) {
            // 初始化完成
            lspStateButton.setButtonState('success', i18next.t('lsp.connected', { ns: 'codeEditor' }));
            pushLSPLogMessage("info", `Initialization ${i18next.t('lsp.state.finished', { ns: 'logMessage' })}`);
            serverInfo = message.result; // 存下服务器支持信息
            OJBetter_monaco.openDocRequest(); // 打开文档
            if (!OJBetter.monaco.setting.language.includes(language)) {
                OJBetter.monaco.setting.language.push(language);
                OJBetter_monaco.RegistrationAfterInit(); // 注册语言及功能
            } else {
                location.reload(); // 这里有问题,先贴个补丁
            }
            OJBetter_monaco.PassiveReceiveHandler(); // 注册被动接收函数
        } else if (message.id === 0 && message.error) {
            pushLSPLogMessage("warn", `Initialization ${i18next.t('lsp.state.error', { ns: 'logMessage' })}`);
        } else if (message.id !== undefined && responseHandlers.has(message.id)) {
            // 如果收到带有id字段的消息,则回传给对应的事件触发函数
            const handler = responseHandlers.get(message.id);
            if (handler) {
                handler(message);
                responseHandlers.delete(message.id); // 删除已处理的事件触发函数
            }
        } else if (message.method == "textDocument/publishDiagnostics") {
            // 接收代码诊断推送
            OJBetter_monaco.updateMarkers(message);
        } else if (message.method == "workspace/applyEdit") {
            // 应用服务器推送的更改
            OJBetter_monaco.applyEdit(message);
        }
    };
    languageSocket.onerror = (error) => {
        pushLSPLogMessage("error", `languageSocket ${i18next.t('lsp.state.error', { ns: 'logMessage' })}`, error);
        console.warn(`Error connecting to languageSocket: ${error}`)
    };
    languageSocket.onclose = (event) => {
        languageSocketState = false;
        lspStateButton.setButtonState('error', i18next.t('lsp.error', { ns: 'codeEditor' }));
        pushLSPLogMessage("warn", `languageSocket ${i18next.t('lsp.socket.close', { ns: 'logMessage' })}`);
    };

    /**
     * 等待LanguageSocketState
     */
    async function waitForLanguageSocketState() {
        return new Promise((resolve) => {
            const checkInitialized = () => {
                if (languageSocketState) {
                    resolve();
                } else {
                    setTimeout(checkInitialized, 100); // 每100毫秒检查一次initialized的值
                }
            };
            checkInitialized();
        });
    }

    // 等待lsp响应初始化结果
    async function waitForInitialized() {
        return new Promise((resolve) => {
            const checkInitialized = () => {
                if (initialized) {
                    resolve();
                } else {
                    setTimeout(checkInitialized, 100); // 每100毫秒检查一次initialized的值
                }
            };
            checkInitialized();
        });
    }

    /**
     * 与languageSocket通信的包装方法
     */
    async function sendMessage(data, requiresResponse, callback) {
        if (!initialized) {
            await waitForInitialized(); // 等待initialized为真
        }
        if (requiresResponse) {
            responseHandlers.set(data.id, callback) // 将事件触发函数与id关联起来
        }
        if (!languageSocketState) await waitForLanguageSocketState();
        languageSocket.send(JSON.stringify(data));
    }
    // 发送消息并等待返回结果
    function fetchData(params, callback) {
        sendMessage(params, true, callback);
    }
    // 发送消息,不需要等待返回结果
    function sendData(data) {
        sendMessage(data, false);
    }

    /**
     * 代码文件更新fileWebSocket
     */
    var fileWebSocket = new WebSocket(url + "file");
    var fileWebSocketState = false;
    OJBetter.monaco.lsp.socket.push(fileWebSocket);
    fileWebSocket.onopen = () => {
        fileWebSocketState = true;
        pushLSPLogMessage("info", `fileWebSocket ${i18next.t('lsp.socket.open', { ns: 'logMessage' })}`);
    };
    fileWebSocket.onclose = (ev) => {
        fileWebSocketState = false;
        pushLSPLogMessage("warn", `fileWebSocket ${i18next.t('lsp.socket.close', { ns: 'logMessage' })}`, ev);
    };
    fileWebSocket.onmessage = (ev) => {
        let message = JSON.parse(ev.data);
        if (message.result !== "ok")
            pushLSPLogMessage("error", `update file failed: ${ev}`);
    };
    fileWebSocket.onerror = (error) => {
        console.warn(`Error connecting to fileWebSocket: ${error}`);
    };
    async function updateFile(workspace, filename, fileExtension, code) {
        async function waitForfileWebSocketState() {
            return new Promise((resolve) => {
                const checkInitialized = () => {
                    if (fileWebSocketState) {
                        resolve();
                    } else {
                        setTimeout(checkInitialized, 100); // 每100毫秒检查一次initialized的值
                    }
                };
                checkInitialized();
            });
        }
        if (!fileWebSocketState) await waitForfileWebSocketState();
        fileWebSocket.send(
            JSON.stringify({
                type: "update",
                workspace,
                filename,
                fileExtension,
                code,
            })
        );
    }

    /**
     * 发送初始化请求
     */
    OJBetter_monaco.Initialize = () => {
        //初始化initialize
        const capabilities = {
            workspace: {
                applyEdit: true,
            },
            textDocument: {
                publishDiagnostics: {
                    relatedInformation: true,
                    versionSupport: false,
                    tagSupport: {
                        valueSet: [1, 2],
                    },
                    codeDescriptionSupport: true,
                },
                completion: {
                    contextSupport: true,
                    completionItem: {
                        snippetSupport: true,
                        commitCharactersSupport: true,
                        documentationFormat: ["markdown", "plaintext"],
                        deprecatedSupport: true,
                        preselectSupport: true,
                        tagSupport: {
                            valueSet: [1],
                        },
                        insertReplaceSupport: true,
                        resolveSupport: {
                            properties: [
                                "documentation",
                                "detail",
                                "additionalTextEdits",
                            ],
                        },
                        insertTextModeSupport: {
                            valueSet: [1, 2],
                        },
                    },
                },
                hover: {
                    dynamicRegistration: true,
                    contentFormat: ["markdown", "plaintext"],
                },
                signatureHelp: {
                    signatureInformation: {
                        documentationFormat: ["markdown", "plaintext"],
                        parameterInformation: {
                            labelOffsetSupport: true,
                        },
                        activeParameterSupport: true,
                    },
                    contextSupport: true,
                },
                definition: {
                    dynamicRegistration: true,
                    linkSupport: true,
                },
                references: {
                    dynamicRegistration: true,
                },
                documentHighlight: {
                    dynamicRegistration: true,
                },
                codeAction: {
                    codeActionLiteralSupport: {
                        codeActionKind: {
                            valueSet:
                                language == "java"
                                    ? []
                                    : [
                                        "",
                                        "quickfix",
                                        "refactor",
                                        "refactor.extract",
                                        "refactor.inline",
                                        "refactor.rewrite",
                                        "source",
                                        "source.organizeImports",
                                    ],
                        },
                    },
                },
                rename: {
                    dynamicRegistration: true,
                    prepareSupport: true,
                    prepareSupportDefaultBehavior: 1,
                    honorsChangeAnnotations: true,
                },
                documentLink: {
                    tooltipSupport: true,
                },
                typeDefinition: {
                    dynamicRegistration: true,
                    linkSupport: true,
                },
                implementation: {
                    dynamicRegistration: true,
                    linkSupport: true,
                },
                colorProvider: {
                    dynamicRegistration: true,
                },
                foldingRange: {
                    dynamicRegistration: true,
                    rangeLimit: 5000,
                    lineFoldingOnly: true,
                },
                declaration: {
                    dynamicRegistration: true,
                    linkSupport: true,
                },
                semanticTokens: {
                    dynamicRegistration: true,
                    tokenTypes: [
                        "namespace",
                        "type",
                        "class",
                        "enum",
                        "interface",
                        "struct",
                        "typeParameter",
                        "parameter",
                        "variable",
                        "property",
                        "enumMember",
                        "event",
                        "function",
                        "method",
                        "macro",
                        "keyword",
                        "modifier",
                        "comment",
                        "string",
                        "number",
                        "regexp",
                        "operator",
                    ],
                    tokenModifiers: [
                        "declaration",
                        "definition",
                        "readonly",
                        "static",
                        "deprecated",
                        "abstract",
                        "async",
                        "modification",
                        "documentation",
                        "defaultLibrary",
                    ],
                    formats: ["relative"],
                    requests: {
                        range: true,
                        full: {
                            delta: true,
                        },
                    },
                    multilineTokenSupport: false,
                    overlappingTokenSupport: false,
                },
                callHierarchy: {
                    dynamicRegistration: true,
                },
            },
            window: {
                showMessage: {
                    messageActionItem: {
                        additionalPropertiesSupport: true,
                    },
                },
                showDocument: {
                    support: true,
                },
                workDoneProgress: true,
            },
            general: {
                regularExpressions: {
                    engine: "ECMAScript",
                    version: "ES2020",
                },
                markdown: {
                    parser: "marked",
                    version: "1.1.0",
                },
            },
        };

        const initializeRequest = {
            id: id++,
            jsonrpc: "2.0",
            method: "initialize",
            params: {
                processId: null,
                clientInfo: {
                    name: "CFMonaco" + InstanceID,
                },
                locale: "zh-CN",
                rootPath: null,
                rootUri: null,
                capabilities: capabilities,
                trace: "off",
                workspaceFolders: [
                    {
                        uri:
                            "file:///" + OJBetter.monaco.lsp.workUri + workspace,
                        name:
                            "file:///" + OJBetter.monaco.lsp.workUri + workspace,
                    },
                ],
            },
        };
        languageSocket.send(JSON.stringify(initializeRequest));

        // 打开文档函数
        OJBetter_monaco.openDocRequest = function () {
            const initializ = {
                jsonrpc: "2.0",
                method: "initialized",
                params: {},
            };
            languageSocket.send(JSON.stringify(initializ));
            const openDocRequest = {
                jsonrpc: "2.0",
                method: "textDocument/didOpen",
                params: {
                    textDocument: {
                        uri: model.uri.toString(),
                        languageId: language,
                        version: model.getVersionId(),
                        text: model.getValue(),
                    },
                },
            };
            languageSocket.send(JSON.stringify(openDocRequest));
            initialized = true; // 初始化完成,这里确认逻辑待完善
        };

        // 初始化更新文件
        updateFile(workspace, filename, fileExtension, model.getValue());
    }

    /**
     * 注册语言及功能
     */
    OJBetter_monaco.RegistrationAfterInit = () => {
        // 注册语言
        monaco.languages.register({ id: language });

        // 注册"Command"
        (function registerCommand() {
            serverInfo.capabilities.executeCommandProvider.commands.forEach(
                (item) => {
                    pushLSPLogMessage("info", `${i18next.t('lsp.server.regist', { ns: 'logMessage' })}`, item);
                    monaco.editor.registerCommand(item, (accessor, ...args) => {
                        sendData({
                            jsonrpc: "2.0",
                            id: id++,
                            method: "workspace/executeCommand",
                            params: {
                                command: item,
                                arguments: args,
                            },
                        });
                    });
                }
            );
        })();

        // 注册"增量更新"
        model.onDidChangeContent((event) => {
            updateFile(workspace, filename, fileExtension, model.getValue()); // 更新文件
            const changeDocRequest = {
                jsonrpc: "2.0",
                method: "textDocument/didChange",
                params: {
                    textDocument: {
                        uri: model.uri.toString(),
                        version: model.getVersionId(),
                    },
                    contentChanges: event.changes.map((change) => ({
                        range: OJBetter_monaco.MonacoRangeTolspRange(change.range),
                        rangeLength: change.rangeLength,
                        text: change.text,
                    })),
                },
            };
            sendData(changeDocRequest);
        });

        //注册"自动补全"
        monaco.languages.registerCompletionItemProvider(language, {
            provideCompletionItems: (model, position, context) => {
                const request = {
                    jsonrpc: "2.0",
                    id: id++,
                    method: "textDocument/completion",
                    params: {
                        textDocument: {
                            uri: model.uri.toString(),
                        },
                        position: OJBetter_monaco.MonacoPositionTolspPosition(position),
                        context: {
                            triggerKind: context.triggerKind + 1, // 这里要+1,两边的定义不一样。。。
                            triggerCharacter: context.triggerCharacter,
                        },
                    },
                };
                return new Promise((resolve, reject) => {
                    fetchData(request, (response) => {
                        const result = response.result;
                        pushLSPLogMessage("info", `completion ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);
                        if (!result) return resolve(null);
                        const CompletionItems = {
                            suggestions: result.items.map(
                                ({
                                    label,
                                    kind,
                                    filterText,
                                    insertText,
                                    insertTextFormat,
                                    sortText,
                                    textEdit,
                                    documentation,
                                    additionalTextEdits,
                                }) => ({
                                    additionalTextEdits: additionalTextEdits
                                        ? additionalTextEdits.map(({ newText, range }) => ({
                                            text: newText,
                                            range: OJBetter_monaco.lspRangeToMonacoRange(range),
                                        }))
                                        : [],
                                    documentation: documentation ? documentation.value : "",
                                    filterText,
                                    insertText: insertText ? insertText : textEdit.newText,
                                    insertTextRules:
                                        insertTextFormat === 2
                                            ? monaco.languages.CompletionItemInsertTextRule
                                                .InsertAsSnippet
                                            : monaco.languages.CompletionItemInsertTextRule
                                                .KeepWhitespace,
                                    kind,
                                    label,
                                    sortText,
                                    range: textEdit
                                        ? textEdit.range
                                            ? OJBetter_monaco.lspRangeToMonacoRange(textEdit.range)
                                            : OJBetter_monaco.lspRangeToMonacoRange(textEdit.insert)
                                        : null,
                                })
                            ),
                        };
                        pushLSPLogMessage("info", `completion ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, CompletionItems);
                        resolve(CompletionItems);
                    });
                });
            },
        });

        // 注册"代码修复"
        monaco.languages.registerCodeActionProvider(language, {
            provideCodeActions: (model, range, context) => {
                const request = {
                    id: id++,
                    jsonrpc: "2.0",
                    method: "textDocument/codeAction",
                    params: {
                        textDocument: {
                            uri: model.uri.toString(),
                        },
                        range: OJBetter_monaco.MonacoRangeTolspRange(range),
                        context: {
                            diagnostics: context.markers.map((item) => ({
                                range: OJBetter_monaco.MonacoRangeTolspRange({
                                    startLineNumber: item.startLineNumber,
                                    startColumn: item.startColumn,
                                    endLineNumber: item.endLineNumber,
                                    endColumn: item.endColumn,
                                }),
                                severity: OJBetter_monaco.MonacoSeverityTolspSeverity(
                                    item.severity
                                ),
                                code: item.code,
                                source: item.source,
                                message: item.message,
                                tags: item.tags,
                                relatedInformation: item.relatedInformation
                                    ? item.relatedInformation.map((item) => ({
                                        location: {
                                            uri: item.resource.toString(),
                                            range: OJBetter_monaco.MonacoRangeTolspRange({
                                                startLineNumber: item.startLineNumber,
                                                startColumn: item.startColumn,
                                                endLineNumber: item.endLineNumber,
                                                endColumn: item.endColumn,
                                            }),
                                        },
                                        message: item.message,
                                    }))
                                    : null,
                            })),
                            only: context.only ? [context.only] : [],
                            triggerKind: context.trigger,
                        },
                    },
                };

                return new Promise((resolve, reject) => {
                    fetchData(request, (response) => {
                        const result = response.result;
                        pushLSPLogMessage("info", `codeAction ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);
                        if (!result) return resolve(null);
                        const codeAction = {
                            actions: result.map((item) => ({
                                title: item.title,
                                kind: item.kind ? item.kind : "quickfix",
                                command: item.command
                                    ? item.command.command
                                        ? {
                                            id: item.command.command,
                                            arguments: item.command.arguments,
                                            title: item.command.title,
                                        }
                                        : null
                                    : null,
                                diagnostics: item.diagnostics
                                    ? item.diagnostics.map((item) => ({
                                        code: item.code,
                                        message: item.message,
                                        range: OJBetter_monaco.lspRangeToMonacoRange(item.range),
                                        severity: OJBetter_monaco.lspSeverityToMonacoSeverity(
                                            item.severity
                                        ),
                                        source: item.source,
                                    }))
                                    : null,
                                edit: item.edit
                                    ? OJBetter_monaco.lspEditToMonacoEdit(item.edit)
                                    : item.arguments
                                        ? {
                                            edits: item.arguments.flatMap(
                                                (item1) => OJBetter_monaco.lspEditToMonacoEdit(item1).edits
                                            ),
                                        }
                                        : null,
                            })),
                            dispose: () => { },
                        };
                        pushLSPLogMessage("info", `codeAction ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, codeAction);

                        resolve(codeAction);
                    });
                });
            },
        });

        // 注册"hover提示"
        monaco.languages.registerHoverProvider(language, {
            provideHover: (model, position) => {
                const request = {
                    jsonrpc: "2.0",
                    id: id++,
                    method: "textDocument/hover",
                    params: {
                        textDocument: {
                            uri: model.uri.toString(),
                        },
                        position: OJBetter_monaco.MonacoPositionTolspPosition(position),
                    },
                };

                return new Promise((resolve, reject) => {
                    fetchData(request, (response) => {
                        pushLSPLogMessage("info", `Hover ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);
                        const result = response.result;

                        if (!result) return resolve(null);
                        const Hover = {
                            range: result.range
                                ? OJBetter_monaco.lspRangeToMonacoRange(result.range)
                                : new monaco.Range(
                                    position.lineNumber,
                                    position.column,
                                    position.lineNumber,
                                    position.column
                                ),
                            contents: Array.isArray(result.contents)
                                ? result.contents.map((item) => ({
                                    value: item.value ? item.value : item,
                                }))
                                : [
                                    {
                                        value: result.contents.value,
                                    },
                                ],
                        };
                        pushLSPLogMessage("info", `Hover ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, Hover);
                        resolve(Hover);
                    });
                });
            },
        });

        // 注册"inlay提示"
        if (language == "cpp" || language == "java")
            monaco.languages.registerInlayHintsProvider(language, {
                provideInlayHints: (model, range, token) => {
                    return new Promise((resolve, reject) => {
                        const request = {
                            jsonrpc: "2.0",
                            id: id++,
                            method: "textDocument/inlayHint",
                            params: {
                                textDocument: {
                                    uri: model.uri.toString(),
                                },
                                range: OJBetter_monaco.MonacoRangeTolspRange(range),
                            },
                        };

                        fetchData(request, (response) => {
                            const result = response.result;
                            pushLSPLogMessage("info", `Inlay Hints ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);

                            if (!result) return resolve(null);

                            const inlayHints = {
                                hints: result.map((item) => {
                                    return {
                                        kind: item.kind,
                                        label: item.label,
                                        paddingLeft: item.paddingLeft,
                                        paddingRight: item.paddingRight,
                                        position: {
                                            lineNumber: item.position.line + 1,
                                            column: item.position.character + 1,
                                        },
                                    };
                                }),
                            };
                            pushLSPLogMessage("info", `Inlay Hints ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, inlayHints);

                            resolve(inlayHints);
                        });
                    });
                },
            });

        // 注册"转到定义"
        monaco.languages.registerDefinitionProvider(language, {
            provideDefinition: (model, position) => {
                const request = {
                    jsonrpc: "2.0",
                    id: id++,
                    method: "textDocument/definition",
                    params: {
                        textDocument: {
                            uri: model.uri.toString(),
                        },
                        position: OJBetter_monaco.MonacoPositionTolspPosition(position),
                    },
                };

                return new Promise((resolve, reject) => {
                    fetchData(request, (response) => {
                        const result = response.result;
                        pushLSPLogMessage("info", `definition ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);

                        if (result.length == 0) return resolve(null);
                        const definition = result.map((item) => ({
                            range: OJBetter_monaco.lspRangeToMonacoRange(item.range),
                            uri: monaco.Uri.parse(item.uri), //
                        }));
                        pushLSPLogMessage("info", `definition ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, definition);

                        resolve(definition);
                    });
                });

            },
        });

        // 注册"转到引用"
        monaco.languages.registerReferenceProvider(language, {
            provideReferences: (model, position, context) => {
                const request = {
                    jsonrpc: "2.0",
                    id: id++,
                    method: "textDocument/references",
                    params: {
                        context: context,
                        textDocument: {
                            uri: model.uri.toString(),
                        },
                        position: OJBetter_monaco.MonacoPositionTolspPosition(position),
                    },
                };

                return new Promise((resolve, reject) => {
                    fetchData(request, (response) => {
                        const result = response.result;
                        pushLSPLogMessage("info", `references ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);

                        if (result.length == 0) return resolve([]);

                        const references = result.map((item) => ({
                            range: OJBetter_monaco.lspRangeToMonacoRange(item.range),
                            uri: monaco.Uri.parse(item.uri), //
                        }));
                        pushLSPLogMessage("info", `references ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, references);
                        resolve(references);
                    });
                });
            },
        });

        // 注册"符号引用点击高亮"
        monaco.languages.registerDocumentHighlightProvider(language, {
            provideDocumentHighlights: (model, position) => {
                const request = {
                    jsonrpc: "2.0",
                    id: id++,
                    method: "textDocument/documentHighlight",
                    params: {
                        textDocument: {
                            uri: model.uri.toString(),
                        },
                        position: OJBetter_monaco.MonacoPositionTolspPosition(position),
                    },
                };

                return new Promise((resolve, reject) => {
                    fetchData(request, (response) => {
                        const result = response.result;
                        pushLSPLogMessage("info", `documentHighlight ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);

                        if (!result || result.length == 0) return resolve([]);
                        const highlights = result.map((item) => ({
                            range: OJBetter_monaco.lspRangeToMonacoRange(item.range),
                            kind: item.kind,
                        }));
                        pushLSPLogMessage("info",
                            `documentHighlight ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`,
                            highlights
                        );

                        resolve(highlights);
                    });
                });
            },
        });

        // 注册"文件链接"
        if (language == "cpp" || language == "java")
            monaco.languages.registerLinkProvider(language, {
                provideLinks: (model) => {
                    const request = {
                        jsonrpc: "2.0",
                        id: id++,
                        method: "textDocument/documentLink",
                        params: {
                            textDocument: {
                                uri: model.uri.toString(),
                            },
                        },
                    };

                    return new Promise((resolve, reject) => {
                        fetchData(request, (response) => {
                            const result = response.result;
                            pushLSPLogMessage("info", `DocumentLink ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);

                            if (!result) return resolve(null);
                            const links = {
                                links: result.map((item) => ({
                                    range: OJBetter_monaco.lspRangeToMonacoRange(item.range),
                                    url: item.target.toString(),
                                    tooltip: item.tooltip ? item.tooltip : null,
                                })),
                            };
                            pushLSPLogMessage("info", `DocumentLink ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, links);
                            resolve(links);
                        });
                    });
                },
            });

        // 注册"格式化"
        monaco.languages.registerDocumentFormattingEditProvider(language, {
            provideDocumentFormattingEdits: (model, options, token) => {
                const request = {
                    jsonrpc: "2.0",
                    id: id++,
                    method: "textDocument/formatting",
                    params: {
                        textDocument: {
                            uri: model.uri.toString(),
                        },
                        options: options,
                    },
                };

                return new Promise((resolve, reject) => {
                    fetchData(request, (response) => {
                        const result = response.result;
                        pushLSPLogMessage("info", `formatting ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);

                        const TextEdit = result.map((edit) => ({
                            range: OJBetter_monaco.lspRangeToMonacoRange(edit.range),
                            text: edit.newText,
                        }));
                        pushLSPLogMessage("info", `formatting ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, TextEdit);
                        resolve(TextEdit);
                    });
                });
            },
        });

        // 注册"部分格式化"
        monaco.languages.registerDocumentRangeFormattingEditProvider(language, {
            provideDocumentRangeFormattingEdits: (model, range, options) => {
                const request = {
                    jsonrpc: "2.0",
                    id: id++,
                    method: "textDocument/rangeFormatting",
                    params: {
                        textDocument: {
                            uri: model.uri.toString(),
                        },
                        range: OJBetter_monaco.MonacoRangeTolspRange(range),
                        options,
                    },
                };

                return new Promise((resolve, reject) => {
                    fetchData(request, (response) => {
                        const result = response.result;
                        pushLSPLogMessage("info", `rangeFormatting ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);

                        if (!result || result.length == 0) return resolve([]);
                        const edits = result.map((item) => ({
                            range: OJBetter_monaco.lspRangeToMonacoRange(item.range),
                            text: item.newText,
                        }));
                        pushLSPLogMessage("info", `rangeFormatting ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, edits);
                        resolve(edits);
                    });
                });
            },
        });

        // 注册"重命名"
        monaco.languages.registerRenameProvider(language, {
            provideRenameEdits: (model, position, newName, token) => {
                const request = {
                    jsonrpc: "2.0",
                    id: id++,
                    method: "textDocument/rename",
                    params: {
                        textDocument: {
                            uri: model.uri.toString(),
                        },
                        position: OJBetter_monaco.MonacoPositionTolspPosition(position),
                        newName: newName,
                    },
                };

                return new Promise((resolve, reject) => {
                    fetchData(request, (response) => {
                        const result = response.result;
                        pushLSPLogMessage("info", `rename ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);

                        const rename = OJBetter_monaco.lspEditToMonacoEdit(result);
                        pushLSPLogMessage("info", `rename ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, rename);
                        resolve(rename);
                    });
                });
            },
        });

        // 注册"折叠范围分析"
        monaco.languages.registerFoldingRangeProvider(language, {
            provideFoldingRanges: (model) => {
                const request = {
                    jsonrpc: "2.0",
                    id: id++,
                    method: "textDocument/foldingRange",
                    params: {
                        textDocument: {
                            uri: model.uri.toString(),
                        },
                    },
                };

                return new Promise((resolve, reject) => {
                    fetchData(request, (response) => {
                        const result = response.result;
                        pushLSPLogMessage("info", `FoldingRange ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);

                        if (!result) return resolve([]);
                        const foldingRanges = result.map((item) => ({
                            start: item.startLine + 1,
                            end: item.endLine + 1,
                            kind: monaco.languages.FoldingRangeKind.fromValue(item.kind),
                        }));
                        pushLSPLogMessage("info", `FoldingRange ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, foldingRanges);
                        resolve(foldingRanges);
                    });
                });
            },
        });

        // 注册"方法签名提示"
        monaco.languages.registerSignatureHelpProvider(language, {
            signatureHelpTriggerCharacters:
                serverInfo.capabilities.signatureHelpProvider.triggerCharacters,
            provideSignatureHelp: (model, position, token, context) => {
                const request = {
                    jsonrpc: "2.0",
                    id: id++,
                    method: "textDocument/signatureHelp",
                    params: {
                        textDocument: {
                            uri: model.uri.toString(),
                        },
                        position: {
                            line: position.lineNumber - 1,
                            character: position.column - 1,
                        },
                        context: {
                            triggerKind: context.triggerKind,
                            triggerCharacter: context.triggerCharacter,
                            isRetrigger: context.isRetrigger,
                            activeSignatureHelp: context.activeSignatureHelp,
                        },
                    },
                };

                return new Promise((resolve, reject) => {
                    fetchData(request, (response) => {
                        const result = response.result;

                        pushLSPLogMessage("info", `signatureHelp ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);

                        if (!result) return resolve(null);
                        const SignatureHelpResult = {
                            value: {
                                activeParameter: result.activeParameter,
                                activeSignature: result.activeSignature,
                                signatures: result.signatures,
                            },
                            dispose: () => { },
                        };

                        pushLSPLogMessage("info",
                            `signatureHelp ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`,
                            SignatureHelpResult
                        );
                        resolve(SignatureHelpResult);
                    });
                });
            },
        });

        // 注册"渐进式自动格式化" 如果server有这个
        if (serverInfo.capabilities.documentOnTypeFormattingProvider)
            monaco.languages.registerOnTypeFormattingEditProvider(language, {
                autoFormatTriggerCharacters: [
                    serverInfo.capabilities.documentOnTypeFormattingProvider
                        .firstTriggerCharacter,
                ],
                provideOnTypeFormattingEdits: (model, position, ch, options) => {
                    const request = {
                        jsonrpc: "2.0",
                        id: id++,
                        method: "textDocument/onTypeFormatting",
                        params: {
                            textDocument: {
                                uri: model.uri.toString(),
                            },
                            position: OJBetter_monaco.MonacoPositionTolspPosition(position),
                            ch,
                            options,
                        },
                    };

                    return new Promise((resolve, reject) => {
                        fetchData(request, (response) => {
                            const result = response.result;
                            pushLSPLogMessage("info", `onTypeFormatting ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);

                            if (!result || result.length == 0) return resolve([]);

                            const edits = result.map((item) => ({
                                range: OJBetter_monaco.lspRangeToMonacoRange(item.range),
                                text: item.newText,
                            }));
                            pushLSPLogMessage("info", `onTypeFormatting ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, edits);
                            resolve(edits);
                        });
                    });
                },
            });
    };

    /**
     * 被动式接收处理
     */
    OJBetter_monaco.PassiveReceiveHandler = () => {

        // "实时代码诊断"
        OJBetter_monaco.updateMarkers = function (message) {
            const params = message.params;
            pushLSPLogMessage("info", `Markers ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, message);

            if (!params) return;
            const markers = params.diagnostics.map((item1) => ({
                code: item1.code,
                message: item1.message,
                ...OJBetter_monaco.lspRangeToMonacoRange(item1.range),
                relatedInformation: item1.relatedInformation
                    ? item1.relatedInformation.map((item2) => ({
                        ...(item2.location.range
                            ? OJBetter_monaco.lspRangeToMonacoRange(item2.location.range)
                            : OJBetter_monaco.lspRangeToMonacoRange(item2.location)),
                        message: item2.message,
                        resource: monaco.Uri.parse(item2.location.uri),
                    }))
                    : null,
                severity: OJBetter_monaco.lspSeverityToMonacoSeverity(item1.severity),
                source: item1.source,
            }));

            pushLSPLogMessage("info", `Markers ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, markers);
            monaco.editor.setModelMarkers(model, "eslint", markers);

            // 更新状态底栏信息
            const nowMarks = monaco.editor.getModelMarkers();
            warningCount = 0;
            errorCount = 0;
            for (const marker of nowMarks) {
                if (marker.severity === monaco.MarkerSeverity.Warning) {
                    warningCount++;
                } else if (marker.severity === monaco.MarkerSeverity.Error) {
                    errorCount++;
                }
            }
            $('#OJBetter_statusBar').text(`Warnings: ${warningCount}, Errors: ${errorCount}`);
        };

        // "应用服务器推送的更改"(代码修复)
        OJBetter_monaco.applyEdit = function (message) {
            const params = message.params;
            pushLSPLogMessage("info", `applyEdit ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, message);

            if (!params) return;
            const operations = Object.values(params.edit.changes)
                .flat()
                .map((item) => ({
                    range: OJBetter_monaco.lspRangeToMonacoRange(item.range),
                    text: item.newText,
                }));

            pushLSPLogMessage("info", `applyEdit ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, operations);
            model.pushEditOperations([], operations, () => null); // 入栈编辑操作
        };
    }

    if (!languageSocketState) await waitForLanguageSocketState();
    OJBetter_monaco.Initialize();
}

// 语言更改
function changeMonacoLanguage(form) {
    let nowSelect = form.selectLang.val();

    // 这里是因为在Chrome上Select2会莫名其妙触发一次不会改变值的change事件,而在其他浏览器中没有,所以贴个补丁
    if (nowSelect === OJBetter.monaco.nowLangSelect) return;
    else OJBetter.monaco.nowLangSelect = nowSelect;

    // 记忆更改
    GM_setValue('compilerSelection', nowSelect);
    // 销毁旧的编辑器
    try {
        if (OJBetter.monaco.editor) OJBetter.monaco.editor.dispose();
    } catch (error) {
        console.warn("Encountered an error while attempting to delete the old editor, but it should not affect your regular usage.", error)
    }
    // 关闭旧的socket
    OJBetter.monaco.lsp.socket.forEach(socket => {
        socket.close();
    });
    // 移除相关元素
    form.topRightDiv.empty();
    $('#LSPLog').remove();
    $('#OJBetter_statusBar').remove();
    // 创建新的编辑器
    if (nowSelect in value_monacoLanguageMap) {
        let language = value_monacoLanguageMap[nowSelect];
        if (language == "python" || language == "cpp") {
            createMonacoEditor(language, form, true);
        } else {
            createMonacoEditor(language, form, false);
        }
    } else {
        createMonacoEditor(null, form, false);
    }
    // 更新在线编译器参数
    changeCompilerArgs(nowSelect);
}

// 收集样例数据
function getTestData() {
    let testData = {};

    /**
     * 从pre中获取文本信息
     * @param {JQuery<HTMLElement>} node 元素
     * @returns {string} 文本信息
     */
    function getTextFromPre(node) {
        let text;
        if (node.find("br").length > 0) {
            text = node.html().replace(/<br>/g, "\n"); // <br>作换行符的情况
        } else {
            text = node.text();
        }
        return text;
    }

    // $('.input').each(function (index) {
    //     var inputText = '';
    //     if ($(this).find('pre').find('div').length > 0) {
    //         $(this).find('pre').find('div').each(function () {
    //             inputText += getTextFromPre($(this)) + '\n';
    //         });
    //     } else {
    //         inputText = getTextFromPre($(this).find('pre'));
    //     }
    //     var outputText = '';
    //     if ($('.output').eq(index).find('pre').find('div').length > 0) {
    //         $('.output').eq(index).find('pre').find('div').each(function () {
    //             inputText += getTextFromPre($(this)) + '\n';
    //         });
    //     } else {
    //         outputText = getTextFromPre($('.output').eq(index).find('pre'));
    //     }

    //     testData[index + 1] = {
    //         input: inputText.trim(),
    //         output: outputText.trim()
    //     };
    // });

    // 需要过滤重复的样例(因为有日文英文两个页面,样例元素重复了两遍)
    let uniqueTestData = [];
    const filteredPreElements = $('pre').clone().filter((index, element) => element.id.startsWith('pre-sample'));
    for (let i = 0; i < filteredPreElements.length; i += 2) {
        if (i + 1 < filteredPreElements.length) {
            const inputElement = $(filteredPreElements[i]);
            const outputElement = $(filteredPreElements[i + 1]);

            const inputText = getTextFromPre(inputElement).trim();
            const outputText = getTextFromPre(outputElement).trim();

            // 检查是否已经存在相同的样例
            let isDuplicate = uniqueTestData.some(testCase => testCase.input === inputText && testCase.output === outputText);
            if (!isDuplicate) {
                uniqueTestData.push({
                    input: inputText,
                    output: outputText
                });
            }
        }
    }

    // 把唯一的测试数据赋值给testData,保持原有的索引风格
    uniqueTestData.forEach((testCase, index) => {
        testData[index + 1] = testCase;
    });

    return testData;
}

// 初始化自定义测试数据面板
function CustomTestInit() {
    const url = window.location.href;

    restoreText();

    // 添加
    $('#addCustomTest').click(function () {
        var sampleDiv = $('<div class="sampleDiv">');
        var inputTextarea = $('<p style="padding: 0px 5px;">input</p><textarea class="dynamicTextarea inputTextarea"></textarea>');
        var outputTextarea = $('<p style="padding: 0px 5px;">output</p><textarea class="dynamicTextarea outputTextarea"></textarea>');
        var deleteCustomTest = OJB_safeCreateJQElement(`<button type="button" class="deleteCustomTest">${closeIcon}</button>`);
        sampleDiv.append(deleteCustomTest);
        sampleDiv.append(inputTextarea);
        sampleDiv.append(outputTextarea);
        $('#customTests').append(sampleDiv);
    });

    // 实时保存文本内容到 IndexedDB 中
    $(document).on('input', '.inputTextarea, .outputTextarea', function () {
        OJBetter.common.database.transaction('rw', OJBetter.common.database.samplesData, function () {
            var objectStore = OJBetter.common.database.samplesData;
            var samples = {
                url: url,
                samples: []
            };
            var index = 0;
            $('.sampleDiv').each(function () {
                var $sampleDiv = $(this);
                var inputTextarea = $sampleDiv.find('.inputTextarea');
                var outputTextarea = $sampleDiv.find('.outputTextarea');
                $sampleDiv.attr('data-index', index);
                inputTextarea.attr('id', 'input' + index);
                outputTextarea.attr('id', 'output' + index);
                var sample = {
                    id: index,
                    input: inputTextarea.val(),
                    output: outputTextarea.val()
                };
                samples.samples.push(sample);
                index++;
            });
            objectStore.put(samples);
        });
    });

    // 删除
    $(document).on('click', '.deleteCustomTest', function () {
        var $sampleDiv = $(this).closest('.sampleDiv');
        OJBetter.common.database.transaction('rw', OJBetter.common.database.samplesData, function () {
            var objectStore = OJBetter.common.database.samplesData;
            var index = parseInt($sampleDiv.attr('data-index'));
            if (!isNaN(index)) {
                objectStore.get(url).then(row => {
                    let samples = row.samples;
                    samples.splice(index, 1); // 移除第index个元素
                    objectStore.put({
                        url: url,
                        samples: samples
                    });
                })
            }
            $sampleDiv.remove();
        });
    });

    // 恢复保存的内容
    function restoreText() {
        OJBetter.common.database.transaction('r', OJBetter.common.database.samplesData, function () {
            return OJBetter.common.database.samplesData.get(url);
        }).then(function (data) {
            if (data.samples && data.samples.length > 0) {
                data.samples.forEach(function (item, index) {
                    var sampleDiv = $('<div class="sampleDiv">');
                    var inputTextarea = OJB_safeCreateJQElement(`<p style="padding: 0px 5px;">input</p><textarea id="input${index}" class="dynamicTextarea inputTextarea"></textarea>`);
                    var outputTextarea = OJB_safeCreateJQElement(`<p style="padding: 0px 5px;">output</p><textarea id="output${index}" class="dynamicTextarea outputTextarea"></textarea>`);
                    var deleteCustomTest = OJB_safeCreateJQElement(`<button type="button" class="deleteCustomTest">${closeIcon}</button>`);

                    inputTextarea.val(item.input);
                    outputTextarea.val(item.output);

                    sampleDiv.append(deleteCustomTest);
                    sampleDiv.append(inputTextarea);
                    sampleDiv.append(outputTextarea);
                    sampleDiv.attr('data-index', index)
                    $('#customTests').append(sampleDiv);
                });
            }
        });
    }
}

// 获取自定义测试数据
function getCustomTestData() {
    const url = window.location.href;

    return new Promise(function (resolve) {
        var customTestData = {};
        OJBetter.common.database.transaction('r', OJBetter.common.database.samplesData, function () {
            return OJBetter.common.database.samplesData.get(url);
        }).then(function (data) {
            if (!data) resolve(customTestData);
            if (data.samples && data.samples.length > 0) {
                data.samples.forEach(function (item, index) {
                    customTestData[index + 1] = {
                        input: item.input,
                        output: item.output
                    };
                });
            }
            resolve(customTestData);
        });
    });
}

// codeforces编译器参数列表
let officialLanguage = "";
function officialCompilerArgsChange(nowSelect) {
    officialLanguage = nowSelect;
    $('#CompilerArgsInput').prop("disabled", true);
}

// codeforces编译器通信
async function officialCompiler(code, input) {
    // const data = new FormData();
    // data.append('csrf_token', OJBetter.common.cf_csrf_token);
    const data = new URLSearchParams();
    data.append('csrf_token', OJBetter.common.at_csrf_token);
    // data.append('source', code);
    // data.append('tabSize', '4');
    // data.append('programTypeId', officialLanguage);
    data.append('data.LanguageId', officialLanguage);
    data.append('input', input);
    // data.append('output', '');
    // data.append('communityCode', '');
    // data.append('action', 'submitSourceCode');
    // data.append('programTypeId', officialLanguage);
    data.append('sourceCode', code);

    const requestOptions = {
        method: 'POST',
        // url: `${OJBetter.common.hostAddress}/data/customtest`,
        url: `${OJBetter.common.hostAddress}/contests/arc172/custom_test/submit/json`,
        data: data,
        headers: {
            // 'X-Csrf-Token': OJBetter.common.cf_csrf_token
            'Content-Type': 'application/x-www-form-urlencoded'
        }
    };

    const result = {
        Errors: '',
        Result: '',
        Stats: ''
    };

    try {
        const submitResponse = await OJB_GMRequest(requestOptions);
        // if (submitResponse.status !== 200 || !submitResponse.response) {
        //     result.Errors = `${i18next.t('compiler.official.pushError', { ns: 'codeEditor' })}`;
        //     return result;
        // }
        if (submitResponse.status !== 200) {
            result.Errors = `${i18next.t('compiler.official.pushError', { ns: 'codeEditor' })}`;
            return result;
        }

        // const submitResult = JSON.parse(submitResponse.response);
        // const customTestSubmitId = submitResult.customTestSubmitId;

        const verdictResponse = await OJB_promiseRetryWrapper(
            getOfficialCompilerVerdict,
            {
                maxRetries: 10,
                retryInterval: 500
            },
            // customTestSubmitId
        );
        return verdictResponse;
    } catch (error) {
        result.Errors = error.message;
        return result;
    }
}

// 获取codeforces编译器的执行结果
// async function getOfficialCompilerVerdict(customTestSubmitId) {
async function getOfficialCompilerVerdict() {
    // const newdata = new FormData();
    // newdata.append('csrf_token', OJBetter.common.cf_csrf_token);
    // newdata.append('action', 'getVerdict');
    // newdata.append('customTestSubmitId', customTestSubmitId);

    // const requestOptions = {
    //     method: 'POST',
    //     url: `${OJBetter.common.hostAddress}/data/customtest`,
    //     data: newdata,
    //     headers: {
    //         'X-Csrf-Token': OJBetter.common.cf_csrf_token
    //     }
    // };
    const requestOptions = {
        method: 'GET',
        url: `https://atcoder.jp/contests/arc172/custom_test/json?reload=true`,
    };

    const responseDetails = await OJB_GMRequest(requestOptions);
    if (responseDetails.status !== 200 || !responseDetails.response) {
        throw new Error(`${i18next.t('compiler.official.getResultError', { ns: 'codeEditor' })}`);
    }

    const response = JSON.parse(responseDetails.response);
    // if (!response.stat) {
    //     throw new Error('Verdict not ready, retrying...');
    // }
    if (response.Interval) {
        throw new Error('Verdict not ready, retrying...');
    }

    // return {
    //     Errors: response.verdict === "OK" ? null : response.verdict + '<br>' + response.output,
    //     Result: response.output.replace(/\r\n/g, "\n"),
    //     Stats: `Status: ${response.stat}`
    // };
    return {
        Errors: response.Result.ExitCode === "0" ? null : response.Stderr,
        Result: response.Stdout.replace(/\r\n/g, "\n"),
        Stats: `Status: ExitCode: ${response.Result.ExitCode}  Exec Time: ${response.Result.TimeConsumption} ms  Memory: ${response.Result.MemoryConsumption} KB`
    };
}

// rextester编译器参数列表
let rextesterLanguage = "";
function rextesterCompilerArgsChange(nowSelect) {
    // let LanguageChoiceList = {
    //     "4": "9", "6": "8", "7": "5", "9": "1", "13": "13", "19": "42", "20": "21", "28": "30", "31": "24", "32": "20",
    //     "34": "17", "36": "4", "43": "6", "45": "7", "46": "4", "50": "7", "51": "9", "52": "27", "54": "7", "55": "23", "60": "4",
    //     "61": "7", "65": "1", "67": "12", "70": "5", "73": "7", "74": "4", "75": "46", "77": "43", "79": "1", "80": "27", "83": "43", "87": "4"
    // }
    let LanguageChoiceList = {
        "5001": "7", "5002": "20", "5003": "1", "5005": "4", "5009": "17", "5016": "8", "5037": "13", "5041": "9",
        "5047": "21", "5055": "5", "5058": "17", "5063": "5", "5078": "5", "5082": "5"
    }

    let CompilerArgsList = {
        "6": "-Wall -std=gnu99 -O2 -o a.out source_file.c",
        "7": "-Wall -std=c++14 -O2 -o a.out source_file.cpp",
        "20": "-o a.out source_file.go",
        "27": "-Wall -std=c++14 -stdlib=libc++ -O2 -o a.out source_file.cpp",
        "30": "source_file.d -ofa.out"
    }
    if (nowSelect in LanguageChoiceList) {
        $('#RunTestButton').prop("disabled", false);
        rextesterLanguage = LanguageChoiceList[nowSelect];
    } else {
        $('#RunTestButton').prop("disabled", true);
    }
    if (rextesterLanguage in CompilerArgsList) {
        const rextesterCompilerArgs = CompilerArgsList[rextesterLanguage];
        $('#CompilerArgsInput').val(rextesterCompilerArgs);
    } else {
        $('#CompilerArgsInput').val("");
    }
}

// rextester编译器通信
async function rextesterCompiler(code, input) {
    try {
        const result = await OJB_promiseRetryWrapper(rextesterCompilerRequest, {
            maxRetries: 5,
            retryInterval: 500,
            errorHandler: (err) => ({ Errors: err.message, Result: '', Stats: '' })
        }, code, input);
        return result;
    } catch (error) {
        return { Errors: error.message, Result: '', Stats: '' };
    }
}

// rextester编译器请求方法
async function rextesterCompilerRequest(code, input) {
    const data = new FormData();
    data.append('LanguageChoiceWrapper', rextesterLanguage);
    data.append('EditorChoiceWrapper', '1');
    data.append('LayoutChoiceWrapper', '1');
    data.append('Program', code);
    data.append('CompilerArgs', $('#CompilerArgsInput').val());
    data.append('Input', input);
    data.append('ShowWarnings', 'false');
    data.append('IsInEditMode', 'false');
    data.append('IsLive', 'false');

    const responseDetails = await OJB_GMRequest({
        method: 'POST',
        url: 'https://rextester.com/rundotnet/Run',
        data: data
    });

    if (responseDetails.status !== 200 || !responseDetails.response) {
        throw new Error(`${i18next.t('compiler.common.error', { ns: 'codeEditor' })}`);
    }

    const response = JSON.parse(responseDetails.response);
    return {
        Errors: response.Errors || '',
        Result: response.Result || '',
        Stats: response.Stats || ''
    };
}

// wandbox编译器参数列表
var wandboxlist = JSON.parse(GM_getResourceText("wandboxlist"));
function wandboxCompilerArgsChange(nowSelect) {
    // let LanguageChoiceList = {
    //     "6": "PHP", "7": "Python", "9": "C#", "12": "Haskell", "13": "Perl", "19": "OCaml",
    //     "20": "Scala", "28": "D", "31": "Python", "32": "Go", "34": "JavaScript", "36": "Java", "40": "Python", "41": "Python",
    //     "43": "C++", "50": "C++", "51": "Pascal", "52": "C++", "54": "C++", "60": "Java", "61": "C++", "65": "C#", "67": "Ruby",
    //     "70": "Python", "73": "C++", "74": "Java", "75": "Rust", "79": "C#", "80": "C++", "87": "Java"
    // }
    let LanguageChoiceList = {
        "5001": "C++", "5002": "Go", "5003": "C#", "5005": "Java", "5009": "JavaScript", "5010": "JavaScript",
        "5012": "D", "5013": "D", "5016": "PHP", "5017": "C++", "5018": "Ruby", "5025": "Haskell",
        "5027": "Lua", "5028": "C++", "5031": "C++", "5037": "Perl", "5041": "Pascal", "5042": "C#",
        "5043": "Lua", "5047": "Scala", "5055": "Python", "5056": "Scala", "5059": "OCaml", "5062": "Lisp",
        "5063": "Python", "5077": "D", "5078": "Python", "5081": "OCaml", "5082": "Python"
    }

    // 移除旧的
    $('#CompilerChange').remove();

    if (nowSelect in LanguageChoiceList) {
        $('#RunTestButton').prop("disabled", false);
        const Languagefiltered = wandboxlist.filter(obj => obj.language === LanguageChoiceList[nowSelect]);

        // 创建编译器下拉框
        let CompilerChange = $('<select id="CompilerChange" style="width: 100%;"></select>');

        $('#CompilerSetting').show().append(CompilerChange);
        for (let i = 0; i < Languagefiltered.length; i++) {
            let Compiler = Languagefiltered[i];
            let op = $("<option></option>")
                .val(Compiler.name)
                .text(Compiler["display-name"] + " " + Compiler.version);
            $("#CompilerChange").append(op);
        }

        // 编译器参数刷新
        function refreshCompilerArgs() {
            var flags = '';
            $("#CompilerBox").find("*").each(function () {
                if ($(this).is("input[type='checkbox']")) {
                    let flag = $(this).prop("checked") ? $(this).val() : '';
                    flags += flag + (flag ? ' ' : '');
                } else if ($(this).is("select") || $(this).is("input") || $(this).is("textarea")) {
                    let flag = $(this).val();
                    flags += flag + (flag ? ' ' : '');
                }
            });
            $("#CompilerArgsInput").val(flags);
            $("#CompilerArgsInput").prop("readonly", true); // 只读
        }

        // 编译器切换监听
        CompilerChange.change(function () {
            let selectedName = CompilerChange.val();
            let Compiler = Languagefiltered.find(
                (obj) => obj.name === selectedName
            );

            $("#CompilerArgsInput").val(); // 初始化编译器输入框

            $("#CompilerBox").remove();
            let div = $("<div id='CompilerBox'></div>");

            let display_compile_command = OJB_safeCreateJQElement(`<input id='${Compiler.name}' value='${Compiler['display-compile-command']}' style="display:none;"}></input>`);
            div.append(display_compile_command);

            let switches = Compiler.switches;
            for (let i = 0; i < switches.length; i++) {
                let switche = switches[i];

                if (switche.type == "single") {
                    let single = OJB_safeCreateJQElement(`
                    <div>
                        <input type='checkbox' id='${switche.name}' value='${switche['display-flags']}' ${switche.default ? 'checked' : ''}></input>
                        <label for='${switche.name}'>${switche['display-name']}</label>
                    </div>
                    `);
                    div.append(single);
                    single.find("input").change(function () {
                        refreshCompilerArgs();
                    });
                } else if (switche.type == "select") {
                    let select = OJB_safeCreateJQElement(`<select id='${switche.name}'></select>`);
                    select.data('previousValue', switche.options[0]['display-flags']);
                    div.append(select);
                    for (let i = 0; i < switche.options.length; i++) {
                        let option = switche.options[i];
                        let op = $("<option></option>")
                            .val(option['display-flags'])
                            .text(option['display-name']);
                        select.append(op);
                    }
                    select.change(function () {
                        refreshCompilerArgs();
                    });
                }
            }

            if (Compiler['compiler-option-raw'] == true) {
                let textarea = OJB_safeCreateJQElement(`<textarea id="compiler_option_raw" placeholder="Raw compiler options" style="resize: vertical;"></textarea>`);
                div.append(textarea);
                textarea.on('input', function () {
                    refreshCompilerArgs();
                });
            }
            if (Compiler['runtime-option-raw'] == true) {
                let textarea = OJB_safeCreateJQElement(`<textarea id="runtime_option_raw" placeholder="Raw runtime options" style="resize: vertical;"></textarea>`);
                div.append(textarea);
                textarea.on('input', function () {
                    refreshCompilerArgs();
                });
            }
            $("#CompilerSetting").append(div);

            refreshCompilerArgs();  // 初始化
        });

        CompilerChange.trigger("change"); // 初始化
    } else {
        $('#RunTestButton').prop("disabled", true);
    }
}

// wandbox编译器通信
async function wandboxCompiler(code, input) {
    try {
        const result = await OJB_promiseRetryWrapper(wandboxCompilerRequest, {
            maxRetries: 5,
            retryInterval: 500,
            errorHandler: (err) => ({ Errors: err.message, Result: '', Stats: '' })
        }, code, input);
        return result;
    } catch (error) {
        return { Errors: error.message, Result: '', Stats: '' };
    }
}

// wandbox编译器请求方法
async function wandboxCompilerRequest(code, input) {
    const data = {
        code: code,
        codes: [],
        compiler: $('#CompilerChange').val().replace($('#compiler_option_raw').val(), '').replace($('#runtime_option_raw').val(), ''),
        'compiler-option-raw': $('#compiler_option_raw').val(),
        'runtime-option-raw': $('#runtime_option_raw').val(),
        options: $("#CompilerArgsInput").val(),
        description: '',
        stdin: input,
        title: ''
    };

    const responseDetails = await OJB_GMRequest({
        method: 'POST',
        url: 'https://wandbox.org/api/compile.json',
        data: JSON.stringify(data),
        headers: {
            'Content-Type': 'application/json'
        }
    });

    if (responseDetails.status !== 200 || !responseDetails.response) {
        throw new Error(`${i18next.t('compiler.common.error', { ns: 'codeEditor' })}`);
    }

    const response = JSON.parse(responseDetails.response);
    return {
        Errors: response.compiler_error === "" ? response.signal : response.compiler_error,
        Result: response.program_output || '',
        Stats: response.status === "0" ? "Finish" : "Error"
    };
}

// 更改编译器参数
function changeCompilerArgs(nowSelect) {
    if (OJBetter.monaco.onlineCompilerChoice == "official") {
        officialCompilerArgsChange(nowSelect);
    } else if (OJBetter.monaco.onlineCompilerChoice == "rextester") {
        rextesterCompilerArgsChange(nowSelect);
    } else if (OJBetter.monaco.onlineCompilerChoice == "wandbox") {
        wandboxCompilerArgsChange(nowSelect);
    }
}

// 在线编译器通信
async function onlineCompilerConnect(code, input) {
    if (OJBetter.monaco.onlineCompilerChoice == "official") {
        return await officialCompiler(code, input);
    } else if (OJBetter.monaco.onlineCompilerChoice == "rextester") {
        return await rextesterCompiler(code, input);
    } else if (OJBetter.monaco.onlineCompilerChoice == "wandbox") {
        return await wandboxCompiler(code, input);
    }
}

// 差异对比
function codeDiff(expectedText, actualText) {
    // 将文本按行拆分
    const expectedLines = expectedText ? expectedText.split('\n') : [];
    const actualLines = actualText ? actualText.split('\n') : [];

    const output = document.createElement('div');

    const createLineElement = (lineNo, contentElement) => {
        const lineDiv = document.createElement('div');
        lineDiv.className = 'diffLine';

        const lineNoDiv = document.createElement('div');
        lineNoDiv.className = 'lineNo';
        lineNoDiv.textContent = lineNo;

        lineDiv.appendChild(lineNoDiv);
        lineDiv.appendChild(contentElement);

        return lineDiv;
    };

    const createContentElement = (isEquals = true, expected = null, removed = null) => {
        const contentDiv = document.createElement('div');
        contentDiv.className = 'lineContent';

        if (isEquals) {
            const span = document.createElement('span');
            span.textContent = expected;
            contentDiv.appendChild(span);
        } else {
            if (removed != null) {
                const removedSpan = document.createElement('span');
                removedSpan.className = 'removed';
                removedSpan.textContent = removed;
                contentDiv.appendChild(removedSpan);
            }
            if (expected != null) {
                const addedSpan = document.createElement('span');
                addedSpan.className = 'added';
                addedSpan.textContent = expected;
                contentDiv.appendChild(addedSpan);
            }
        }

        return contentDiv;
    };

    let index = 1;

    expectedLines.forEach((expectedLine, i) => {
        const actualLine = actualLines[i];
        if (actualLine === undefined) {
            output.appendChild(createLineElement(index++, createContentElement(false, expectedLine)));
        } else if (expectedLine === actualLine) {
            output.appendChild(createLineElement(index++, createContentElement(true, expectedLine)));
        } else {
            output.appendChild(createLineElement(index++, createContentElement(false, expectedLine, actualLine)));
        }
    });

    // 处理多余的 actualLines
    for (let i = expectedLines.length; i < actualLines.length; i++) {
        output.appendChild(createLineElement(index++, createContentElement(false, null, actualLines[i])));
    }

    return output.innerHTML;
}

// 内容类型常量
const TestCaseContentType = {
    TERMINAL: 'terminal',
    DIFF: 'diff',
    NO_DIFF: 'no_diff',
    SUCCESS: 'success'
};

// 样例测试状态类
class TestCaseStatus {
    constructor(item, prefix) {
        this.testCase = OJB_safeCreateJQElement(`<div class="test-case"></div>`);
        this.item = item;
        this.prefix = prefix;
        this.titleElement = OJB_safeCreateJQElement(`<div class="test-case-title">${this.prefix} ${this.item}</div>`);
        this.statusElement = OJB_safeCreateJQElement(`<div class="test-case-status"></div>`);
        this.contentElement = OJB_safeCreateJQElement(`<div class="test-case-content"></div>`);
        this.judgeElement = OJB_safeCreateJQElement(`<div class="test-case-judge"></div>`);
        this.testCase.append(this.titleElement, this.statusElement, this.contentElement, this.judgeElement);
        $('#statePanel').append(this.testCase);
        this.setStatus('Queued', 'queued');
    }

    setTitle(title) {
        this.titleElement.text(title);
    }

    setStatus(text, status) {
        this.statusElement.text(text).removeClass('queued running success error').addClass(status);
    }

    setContent(content, type) {
        // 如果内容类型为SUCCESS,隐藏内容元素并提前返回
        if (type === TestCaseContentType.SUCCESS) {
            this.contentElement.hide();
            return;
        }

        // 根据内容类型创建内容元素
        const createContentElementByType = (content, type) => {
            let contentElement;
            switch (type) {
                case TestCaseContentType.TERMINAL:
                    // 为TERMINAL类型创建一个新的终端容器
                    contentElement = OJB_safeCreateJQElement(`<div class="terminal-container" style="overflow: auto;"></div>`);
                    break;
                case TestCaseContentType.DIFF:
                case TestCaseContentType.NO_DIFF:
                    // 为DIFF和NO_DIFF类型创建相应的内容元素,并添加差异说明
                    const className = type === TestCaseContentType.DIFF ? "output_diff" : "output_no_diff";
                    contentElement = OJB_safeCreateJQElement(`<pre class="${className}">${content}</pre>`);
                    appendDiffNote();
                    break;
                default:
                    throw new Error("Unsupported content type.");
            }
            return contentElement;
        };

        // 初始化终端
        const initializeTerminal = (content, contentElement) => {
            const term = new Terminal({ rows: 10, cols: 150 });
            term.setOption('theme', { background: '#2d2e2c' });
            term.setOption('convertEol', true); // 将换行符\n转换为\r\n
            term.write(content);
            term.open(contentElement.get(0));
        };

        // 添加差异说明
        const appendDiffNote = () => {
            const diffNote = OJB_safeCreateJQElement(`<div class="diff_note">${i18next.t('resultBlock.diffNote', { ns: 'codeEditor' })}</div>`);
            this.testCase.append(diffNote);
        };

        // 创建并追加内容元素
        const contentElement = createContentElementByType(content, type);
        this.contentElement.append(contentElement);

        // 如果内容类型为TERMINAL,初始化并打开终端
        if (type === TestCaseContentType.TERMINAL) {
            initializeTerminal(content, contentElement);
        }
    }

    setJudge(judge) {
        this.judgeElement.text(judge);
    }
}

// 样例测试函数
async function runCode(event, runButton, sourceDiv) {
    event.preventDefault();
    const statePanel = $('#statePanel').show().empty();
    const testData = getTestData();
    const customTestData = await getCustomTestData();
    const totalTests = Object.keys(customTestData).length + Object.keys(testData).length;

    let passedTests = 0;
    let failedTests = 0;
    let hasError = false;

    // 定义一个对象队列,包括创建的样例块实例和对应的样例数据
    const queue = [];

    // 先生成各个样例的块,并显示排队中,将创建的各个对象存到队列中,以便后面更新
    for (const [item, data] of Object.entries(customTestData)) {
        const testCase = new TestCaseStatus(item, i18next.t('resultBlock.title.custom', { ns: 'codeEditor' }));
        queue.push({ testCase, data });
    }

    if (!$('#onlyCustomTest').prop('checked')) {
        for (const [item, data] of Object.entries(testData)) {
            const testCase = new TestCaseStatus(item, i18next.t('resultBlock.title.sample', { ns: 'codeEditor' }));
            queue.push({ testCase, data });
        }
    }

    // 测试函数
    const runTest = async (testCase, data, index) => {
        runButton.setButtonState('running', `${index}/${totalTests}`);

        testCase.setStatus('Running', 'running');
        const result = await onlineCompilerConnect(sourceDiv.val(), data.input);

        if (result.Errors) {
            testCase.setStatus('Compilation error or Time limit', 'error');
            testCase.setContent(result.Errors, TestCaseContentType.TERMINAL);
            hasError = true;
        } else if (result.Result.trim() === data.output.trim()) {
            testCase.setStatus('Accepted', 'success');
            testCase.setContent('The output is correct.', TestCaseContentType.SUCCESS);
            passedTests++;
        } else {
            testCase.setStatus('Wrong Answer', 'error');
            const diffContent = $('#DontShowDiff').prop('checked') ? result.Result.trim() : codeDiff(data.output.trim(), result.Result.trim());
            const contentType = $('#DontShowDiff').prop('checked') ? TestCaseContentType.NO_DIFF : TestCaseContentType.DIFF;
            testCase.setContent(diffContent, contentType);
            failedTests++;
        }

        const judgeStats = `${i18next.t('resultBlock.state', { ns: 'codeEditor' })}${result.Stats}`;
        testCase.setJudge(judgeStats);

        await OJB_delay(500); // 等待500毫秒
    };

    // 对队列中的对象进行测试
    for (let i = 0; i < queue.length; i++) {
        const { testCase, data } = queue[i];
        await runTest(testCase, data, i + 1);
    }

    // 测试完成后更新按钮状态
    if (hasError) {
        runButton.setButtonState('error', i18next.t('runTestButton.error', { ns: 'codeEditor' }));
    } else if (failedTests > 0) {
        runButton.setButtonState('error', `${passedTests}/${totalTests} ` + i18next.t('runTestButton.partial', { ns: 'codeEditor' }));
    } else {
        runButton.setButtonState('success', i18next.t('runTestButton.success', { ns: 'codeEditor' }));
        if (OJBetter.monaco.setting.autoSubmitAfterPass) {
            $('#OJBetter_SubmitForm').submit(); // 自动提交
        }
    }
}

/**
 * 添加题目页代码编辑器
 * @returns 
 */
async function addProblemPageCodeEditor() {
    // if (typeof ace === 'undefined') {
    //     const loadingMessage = new LoadingMessage();
    //     loadingMessage.updateStatus(`${OJBetter.state.name} —— ${i18next.t('error.codeEditor.load', { ns: 'alert' })}`, 'error');
    //     return; // 因为Codeforces设定的是未登录时不能访问提交页,也不会加载ace库
    // }

    // 获取提交页链接
    const href = window.location.href;
    let submitUrl = OJBetter.common.hostAddress + $('.form-code-submit').attr('action');
    // if (/\/problemset\//.test(href)) {
    //     // problemset
    //     submitUrl = OJBetter.common.hostAddress + '/problemset/submit';
    // } else if (/\/gym\//.test(href)) {
    //     // gym 题目
    //     submitUrl = OJBetter.common.hostAddress + '/gym/' + ((href) => {
    //         const regex = /\/gym\/(?<num>[0-9a-zA-Z]*?)\/problem\//;
    //         const match = href.match(regex);
    //         return match && match.groups.num;
    //     })(href) + '/submit';
    // } else if (OJBetter.typeOfPage.is_acmsguru) {
    //     // acmsguru 题目
    //     submitUrl = href.replace(/\/problemsets[A-Za-z0-9\/#]*/, "/problemsets/acmsguru/submit");
    // } else {
    //     submitUrl = href.replace(/\/problem[A-Za-z0-9\/#]*/, "/submit");
    // }

    // // 获取提交页HTML
    // let cloneHTML = await getSubmitHTML(submitUrl);

    // 创建
    // let form = await createCodeEditorForm(submitUrl, cloneHTML);
    let form = await createCodeEditorForm(submitUrl);
    let selectLang = form.selectLang;
    let submitButton = form.submitButton;
    let runButton = form.runButton;

    // 初始化
    CustomTestInit(); // 自定义测试数据面板
    selectLang.val(OJBetter.monaco.compilerSelection); // 恢复上一次的语言选择

    // 设置语言选择change事件监听器
    selectLang.on('change', () => {
        changeMonacoLanguage(form); // 编辑器语言切换监听
    });
    changeMonacoLanguage(form);

    // 样例测试
    runButton.on('click', (event) => runCode(event, runButton, form.sourceDiv, form.submitDiv))
        .setHoverRedo();

    // 提交
    submitButton.on('click', async function (event) {
        event.preventDefault();
        if (OJBetter.monaco.setting.isCodeSubmitDoubleConfirm) {
            // 获取题目名
            const questionTitle = (() => {
                const element = document.querySelector('.h2');
                return Array.from(element.childNodes)
                    .filter(node => node.nodeType === Node.TEXT_NODE)
                    .map(textNode => textNode.textContent.trim())
                    .join(' ');
            })();
            const submit = await OJB_createDialog(
                i18next.t('submitCode.title', { ns: 'dialog' }),
                i18next.t('submitCode.content', { ns: 'dialog', questionTitle: questionTitle }),
                [
                    i18next.t('submitCode.buttons.0', { ns: 'dialog' }),
                    i18next.t('submitCode.buttons.1', { ns: 'dialog' })
                ],
                true
            ); //提交确认
            if (submit) {
                submitButton.after(`<img class="OJBetter_loding" src="//codeforces.org/s/84141/images/ajax-loading-24x24.gif">`);
                $('#OJBetter_SubmitForm').submit();
            } else {
                submitButton.addClass('disabled');
                setTimeout(function () {
                    submitButton.removeClass('disabled');
                }, 300);
            }
        } else {
            $('#OJBetter_SubmitForm').submit();
        }
    });
}

/**
 * 获取翻译服务目标语言的对应代码
 * @param {string} serverName 服务名称
 * @returns {string} 目标语言,如果没有对应代码则返回中文
 */
function getTargetLanguage(serverName) {
    let targetLanguage = OJBetter.supportList.translationSupport[serverName][OJBetter.translation.targetLang];
    if (targetLanguage) return targetLanguage;
    else return OJBetter.supportList.translationSupport[serverName]['zh'];
}

/**
 * 将文本中Markdown格式的加粗**转换成HTML格式。
 * @param {string} text 文本
 * @returns {string} 替换后的字符串
 */
function convertBoldMarkdownToHTML(text) {
    return text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
}

/**
* 将文本中Markdown格式的链接文本转换成HTML格式。
* @param {string} text 文本
* @returns {string} 替换后的字符串
*/
function convertLinksMarkdownToHTML(text) {
    return text.replace(/(?<!!)\[(.*?)\]\(([^"]*?)("(.*?)")*\)/g, '<a href="$2" title="$4">$1</a>');
}

/**
 * 将HTML格式的加粗文本转换回Markdown格式。
 * @param {string} text 文本
 * @returns {string} 替换后的字符串
 */
function convertBoldHTMLToMarkdown(text) {
    return text.replace(/<strong>(.*?)<\/strong>/g, '**$1**');
}

/**
 * 将HTML格式的链接文本转换回Markdown格式。
 * @param {string} html - 包含HTML链接标签<a>的字符串。
 * @returns {string} 转换后的字符串,其中HTML链接标签被替换为Markdown的链接语法。
 */
function convertLinksHTMLToMarkdown(html) {
    return html.replace(/<a href="([^"]*)"( title="([^"]*)")*>([^<]+)<\/a>/g, '[$4]($1 "$3")');
}

/**
 * DeepL翻译
 * @param {string} raw 原文
 * @returns {Promise<TransRawData>} 翻译结果对象
 */
async function translate_deepl(raw) {
    const id = (Math.floor(Math.random() * 99999) + 100000) * 1000;
    const data = {
        jsonrpc: '2.0',
        method: 'LMT_handle_texts',
        id,
        params: {
            splitting: 'newlines',
            lang: {
                source_lang_user_selected: 'auto',
                target_lang: getTargetLanguage('deepl'),
            },
            texts: [{
                text: raw,
                requestAlternatives: 3
            }],
            timestamp: getTimeStamp(raw.split('i').length - 1)
        }
    }
    let postData = JSON.stringify(data);
    if ((id + 5) % 29 === 0 || (id + 3) % 13 === 0) {
        postData = postData.replace('"method":"', '"method" : "');
    } else {
        postData = postData.replace('"method":"', '"method": "');
    }
    const options = {
        method: 'POST',
        url: 'https://www2.deepl.com/jsonrpc',
        data: postData,
        headers: {
            'Content-Type': 'application/json',
            'Host': 'www2.deepl.com',
            'Origin': 'https://www.deepl.com',
            'Referer': 'https://www.deepl.com/',
        },
        anonymous: true,
        nocache: true,
    }

    return await BaseTranslate(options, res => JSON.parse(res)?.result?.texts?.[0]?.text || res, res => {
        const resObj = {
            status: true,
            message: 'ok'
        };
        if (res.includes('"message":"Too many requests"')) {
            resObj.status = false;
            resObj.message = i18next.t('error.deepl429', { ns: 'translator' }); // Too many requests 提示
            return resObj;
        };
        return resObj;
    });
}

/**
 * 使用 DeepL Free API 进行翻译
 * @param {string} raw 原文
 * @returns {Promise<TransRawData>} 翻译结果对象
 */
async function translate_deepl_api_free(raw) {
    const data = JSON.stringify({
        text: [raw],
        target_lang: getTargetLanguage('deepl'),
        split_sentences: '1',
        ...(OJBetter.deepl.enableEmphasisProtection || OJBetter.deepl.enableLinkProtection ? { tag_handling: 'html' } : {}),
        ...Object.assign({}, ...OJBetter.deepl.config.data)
    });

    const options = {
        method: "POST",
        url: OJBetter.deepl.config.proxy || "https://api-free.deepl.com/v2/translate",
        headers: {
            "Authorization": `DeepL-Auth-Key ${OJBetter.deepl.config.key}`,
            "Content-Type": "application/json",
            ...Object.assign({}, ...OJBetter.deepl.config.header)
        },
        data: data,
        onload: response => response.responseText,
        onerror: error => console.error(error)
    };

    return await BaseTranslate(options, res => JSON.parse(res).translations[0].text);
}

/**
 * 使用 DeepL Pro API 进行翻译
 * @param {string} raw 原文
 * @returns {Promise<TransRawData>} 翻译结果对象
 */
async function translate_deepl_api_pro(raw) {
    const data = JSON.stringify({
        text: [raw],
        target_lang: getTargetLanguage('deepl'),
        split_sentences: '1',
        ...(OJBetter.deepl.enableEmphasisProtection || OJBetter.deepl.enableLinkProtection ? { tag_handling: 'html' } : {}),
        ...Object.assign({}, ...OJBetter.deepl.config.data)
    });

    const options = {
        method: "POST",
        url: OJBetter.deepl.config.proxy || "https://api.deepl.com/v2/translate",
        headers: {
            "Authorization": `DeepL-Auth-Key ${OJBetter.deepl.config.key}`,
            "Content-Type": "application/json",
            ...Object.assign({}, ...OJBetter.deepl.config.header)
        },
        data: data,
        onload: response => response.responseText,
        onerror: error => console.error(error)
    };

    return await BaseTranslate(options, res => JSON.parse(res).translations[0].text);
}

/**
 * 使用 DeepLX 进行翻译
 * @param {String} text 原文
 * @returns {Promise<TransRawData>} 翻译结果对象
 */
async function translate_deeplx(text) {
    const options = {
        method: "POST",
        url: OJBetter.deepl.config.proxy || 'https://api.deeplx.org/translate',
        data: JSON.stringify({
            "text": text,
            "source_lang": "EN",
            "target_lang": getTargetLanguage('deepl'),
        }),
        headers: {
            'Content-Type': 'application/json',
            ...(OJBetter.deepl.config.key ? { Authorization: `Bearer ${OJBetter.deepl.config.key}` } : {})
        },
        responseType: "json",
    };

    return await BaseTranslate(options, res => {
        const parsedResponse = JSON.parse(res);
        if (parsedResponse.code === 200 && parsedResponse.data) {
            return parsedResponse.data;
        } else {
            throw new Error('Translation failed or invalid response format.');
        }
    });
}

function getTimeStamp(iCount) {
    const ts = Date.now();
    if (iCount !== 0) {
        iCount = iCount + 1;
        return ts - (ts % iCount) + iCount;
    } else {
        return ts;
    }
}

/**
 * 讯飞听见翻译
 * @param {String} text 要翻译的文本
 * @returns {Promise<TransRawData>} 翻译结果对象
 */
async function translate_iflyrec(text) {
    const options = {
        method: "POST",
        url: 'https://www.iflyrec.com/TranslationService/v1/textTranslation',
        data: JSON.stringify({
            "from": "2",
            "to": getTargetLanguage('iflyrec'),
            "contents": [{
                "text": text,
                "frontBlankLine": 0
            }]
        }),
        anonymous: true,
        headers: {
            'Content-Type': 'application/json',
            'Origin': 'https://www.iflyrec.com',
        },
        responseType: "json",
    };
    return await BaseTranslate(options, res => JSON.parse(res).biz[0].translateResult.replace(/\\n/g, "\n\n"));
}

/**
 * 有道翻译
 * @param {string} raw 原文
 * @returns {Promise<TransRawData>} 翻译结果对象
 */
async function translate_youdao_mobile(raw) {
    /**
     * 生成cookie
     */
    const getcookie = (() => {
        // 生成IP地址
        const generateIP = () => {
            return `${OJB_getRandomNumberInRange(1, 255)}.${OJB_getRandomNumberInRange(1, 255)}.${OJB_getRandomNumberInRange(1, 255)}.${OJB_getRandomNumberInRange(1, 255)}`;
        }
        // 生成OUTFOX_SEARCH_USER_ID_NCOO的值
        const OUTFOX_SEARCH_USER_ID_NCOO = `${OJB_getRandomNumberInRange(100000000, 999999999)}.${OJB_getRandomNumberInRange(100000000, 999999999)}`;
        // 生成OUTFOX_SEARCH_USER_ID的值
        const OUTFOX_SEARCH_USER_ID = `${OJB_getRandomNumberInRange(100000000, 999999999)}@${generateIP()}`;
        return `OUTFOX_SEARCH_USER_ID_NCOO=${OUTFOX_SEARCH_USER_ID_NCOO}; OUTFOX_SEARCH_USER_ID=${OUTFOX_SEARCH_USER_ID}`;
    })();

    /**
     * 生成随机时间戳
     */
    const gettime = (new Date()).getTime();

    /**
     * 生成sign
     */
    const getsign = (() => {
        const d = "fanyideskweb";
        const u = "webfanyi";
        const t = "fsdsogkndfokasodnaso";
        function A(e) {
            return CryptoJS.MD5(e.toString()).toString(CryptoJS.enc.Hex);
        }
        function w(e) {
            return A(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`);
        }
        return w(gettime);
    })();

    /**
     * 解码方法
     * @param {string} src 待解码的字符串
     * @returns {Object} 解码后的数据
     */
    const decode = function (src) {
        // 解码URL安全的Base64
        const decodeUrlSafeBase64 = (str) => {
            let base64 = str.replace(/-/g, '+').replace(/_/g, '/');
            return base64;
        }

        // 使用MD5生成key和iv,取前16字节
        const key = "ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl";
        const iv = "ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4";
        const keyHash = CryptoJS.MD5(key).toString();
        const ivHash = CryptoJS.MD5(iv).toString();

        // 使用AES-128-CBC模式进行解密
        const keyForAES = CryptoJS.enc.Hex.parse(keyHash).toString().substring(0, 32);
        const ivForAES = CryptoJS.enc.Hex.parse(ivHash).toString().substring(0, 32);

        // 解码URL安全的Base64
        const decodedBase64 = decodeUrlSafeBase64(src);

        // 解密
        const decrypted = CryptoJS.AES.decrypt({
            ciphertext: CryptoJS.enc.Base64.parse(decodedBase64)
        }, CryptoJS.enc.Hex.parse(keyForAES), {
            iv: CryptoJS.enc.Hex.parse(ivForAES),
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });

        // 将解密结果转换为Utf8字符串
        const decryptedStr = decrypted.toString(CryptoJS.enc.Utf8);

        // 处理JSON字符串并解析
        const jsonStr = decryptedStr.substring(0, decryptedStr.lastIndexOf("}") + 1);
        return JSON.parse(jsonStr);
    }
    // 整理数据
    const organizeTranslation = (data) => {
        // 提取translateResult数组
        const { translateResult } = data;

        // 整理tgt字段
        return translateResult
            .flat()
            .map(item => item.tgt)
            .join('');
    };
    // 表单数据
    const data = {
        "i": raw,
        "from": "auto",
        "to": getTargetLanguage('youdao'),
        "dictResult": "true",
        "keyid": "webfanyi",
        "sign": getsign,
        "client": "fanyideskweb",
        "product": "webfanyi",
        "appVersion": "1.0.0",
        "vendor": "web",
        "pointParam": "client,mysticTime,product",
        "mysticTime": gettime,
        "keyfrom": "fanyi.web",
        "mid": "1",
        "screen": "1",
        "model": "1",
        "network": "wifi",
        "abtest": "0",
        "yduuid": "abcdefg"
    };
    const options = {
        method: "POST",
        url: 'https://dict.youdao.com/webtranslate',
        data: new URLSearchParams(data),
        anonymous: true,
        cookie: getcookie,
        headers: {
            "Content-Type": "application/x-www-form-urlencoded",
            'Referer': 'https://fanyi.youdao.com/',
        }
    }
    return await BaseTranslate(options,
        res => {
            const decodeData = decode(res)
            const result = organizeTranslation(decodeData);
            return result;
        }
    );
}

/**
 * google翻译
 * @param {string} raw 原文
 * @returns {Promise<TransRawData>} 翻译结果对象
 */
async function translate_gg(raw) {
    const params = `tl=${getTargetLanguage('google')}&q=${encodeURIComponent(raw)}`;
    const options = {
        method: "GET",
        url: `https://translate.google.com/m?${params}`,
    }
    return await BaseTranslate(options,
        res => $(res).filter('.result-container').text() || $(res).find('.result-container').text());
}

/**
 * 彩云翻译
 * @param {string} raw 原文
 * @returns {Promise<TransRawData>} 翻译结果对象
 */
async function translate_caiyun(raw) {
    const source = "NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm";
    const dic = [..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"].reduce((dic, current, index) => { dic[current] = source[index]; return dic }, {});
    const browser_id = CryptoJS.MD5(Math.random().toString()).toString();
    const caiyun_jwt = await (async () => {
        const options = {
            method: "POST",
            url: 'https://api.interpreter.caiyunai.com/v1/user/jwt/generate',
            headers: {
                "content-type": "application/json",
                "x-authorization": "token:qgemv4jr1y38jyq6vhvi",
                "origin": "https://fanyi.caiyunapp.com",
            },
            data: JSON.stringify({ browser_id }),
        }
        const res = await OJB_GMRequest(options);
        return JSON.parse(res.responseText).jwt;
    })();

    // 解码
    const decodeUnicode = str => {
        const decoder = new TextDecoder();
        const data = Uint8Array.from(atob(str), c => c.charCodeAt(0));
        return decoder.decode(data);
    };
    const decoder = line => decodeUnicode([...line].map(i => dic[i] || i).join(""));

    const options = {
        method: "POST",
        url: 'https://api.interpreter.caiyunai.com/v1/translator',
        data: JSON.stringify({
            "source": raw.split('\n'),
            "browser_id": browser_id,
            "trans_type": getTargetLanguage('caiyun'),
            "request_id": "web_fanyi",
            "media": "text",
            "os_type": "web",
            "dict": true,
            "cached": true,
            "replaced": true,
            "style": "formal",
            "model": "",
            "detect": true,
        }),
        headers: {
            "content-type": "application/json;charset=UTF-8",
            "x-authorization": "token:qgemv4jr1y38jyq6vhvi",
            "t-authorization": caiyun_jwt
        }
    }
    return await BaseTranslate(options, res => JSON.parse(res).target.map(decoder).join('\n'))
}

/**
 * ChatGPT
 * @param {string} raw 原文
 * @returns {Promise<TransRawData>} 翻译结果对象
 */
async function translate_openai(raw) {
    const modelDefault = 'gpt-3.5-turbo';
    const lang = getTargetLanguage('openai');
    let prompt = "";
    if (OJBetter.chatgpt.customPrompt) {
        prompt = `\n${OJBetter.chatgpt.customPrompt}`;
        if (!OJBetter.chatgpt.asSystemPrompt) {
            prompt += `\n${raw}`;
        };
    } else {
        prompt = `
As a professional English translator, your task is to accurately translate a segment of an algorithm programming competition question into ${lang}.
The translation should use professional terms and maintain the text format, including ${OJBetter.typeOfPage.is_oldLatex || OJBetter.typeOfPage.is_acmsguru
                ? "keeping the LaTeX equations unchanged."
                : "keeping the brackets【】, HTML tags, and their content unchanged."
            }
After translation, please ensure that the ${lang} version conforms to normal expression habits.
What I need is a carefully polished ${lang} translation of my question segment. ${OJBetter.chatgpt.asSystemPrompt ? '' :
                `The segment to be translated is as follows: "
${raw}
"`}`;
    };
    const data = {
        model: OJBetter.chatgpt.config.model || modelDefault,
        messages: OJBetter.chatgpt.asSystemPrompt ?
            [
                {
                    role: "system",
                    content: prompt
                },
                {
                    role: "user",
                    content: raw
                }
            ] :
            [
                {
                    role: "user",
                    content: prompt
                }
            ],
        temperature: 0.7,
        ...Object.assign({}, ...OJBetter.chatgpt.config.data)
    };
    const options = {
        method: "POST",
        url: OJBetter.chatgpt.config.proxy || 'https://api.openai.com/v1/chat/completions',
        data: JSON.stringify(data),
        responseType: 'json',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer ' + OJBetter.chatgpt.config.key,
            ...Object.assign({}, ...OJBetter.chatgpt.config.header)
        }
    }
    return await BaseTranslate(options,
        res => res,
        undefined,
        response => response.response.choices[0].message.content);
}

/**
 * ChatGPT 流式传输
 * @param {string} raw 原文
 * @param {TranslateDiv} translateDiv 翻译结果面板
 * @returns {Promise<TransRawData>} 翻译结果对象
 */
async function translate_openai_stream(raw, translateDiv) {
    const result = {
        done: true,
        checkPassed: null,
        response: null,
        responseText: null,
        text: "",
        error: null,
        message: null
    };
    const helpText = i18next.t('error.basic', { ns: 'translator' }); // 基本帮助提示信息
    try {
        for await (const delta of openai_stream(raw)) {
            result.text += delta;
            // 翻译结果面板更新
            translateDiv.updateTranslateDiv(result.text, !(OJBetter.typeOfPage.is_oldLatex || OJBetter.typeOfPage.is_acmsguru), false);
        }
        return result;
    } catch (err) {
        console.warn(err);
        result.error = {
            message: err.message || null,
            stack: err.stack ? err.stack.replace(/\n/g, '<br>').replace(/\s/g, '&nbsp;') : null,
            enumerable: err,
            source: 'openai_stream'
        };
        result.message = `${i18next.t('error.GMRequest', { ns: 'translator' })}${helpText}`;
    }

    return result;
}

/**
 * 流式传输
 * @param {string} raw 原文
 * @returns {AsyncGenerator<string>} 返回 AsyncGenerator
 */
async function* openai_stream(raw) {
    const modelDefault = 'gpt-3.5-turbo';
    const lang = getTargetLanguage('openai');
    let prompt = "";
    if (OJBetter.chatgpt.customPrompt) {
        prompt = `\n${OJBetter.chatgpt.customPrompt}`;
        if (!OJBetter.chatgpt.asSystemPrompt) {
            prompt += `\n${raw}`;
        };
    } else {
        prompt = `
As a professional English translator, your task is to accurately translate a segment of an algorithm programming competition question into ${lang}.
The translation should use professional terms and maintain the text format, including ${OJBetter.typeOfPage.is_oldLatex || OJBetter.typeOfPage.is_acmsguru
                ? "keeping the LaTeX equations unchanged."
                : "keeping the brackets【】, HTML tags, and their content unchanged."
            }
After translation, please ensure that the ${lang} version conforms to normal expression habits.
What I need is a carefully polished ${lang} translation of my question segment. ${OJBetter.chatgpt.asSystemPrompt ? '' :
                `The segment to be translated is as follows: "
${raw}
"`}`;
    };
    const data = {
        model: OJBetter.chatgpt.config.model || modelDefault,
        messages: OJBetter.chatgpt.asSystemPrompt ?
            [
                {
                    role: "system",
                    content: prompt
                },
                {
                    role: "user",
                    content: raw
                }
            ] :
            [
                {
                    role: "user",
                    content: prompt
                }
            ],
        temperature: 0.7,
        stream: true,
        ...Object.assign({}, ...OJBetter.chatgpt.config.data)
    };
    const options = {
        method: "POST",
        url: OJBetter.chatgpt.config.proxy || 'https://api.openai.com/v1/chat/completions',
        data: JSON.stringify(data),
        responseType: 'stream',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer ' + OJBetter.chatgpt.config.key,
            ...Object.assign({}, ...OJBetter.chatgpt.config.header)
        }
    }
    const response = await OJB_GMRequest(options, true);
    const reader = response.response.getReader();
    const decoder = new TextDecoder();
    let buffer = ''; // 用于累积数据片段的缓冲区

    while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        buffer += decoder.decode(value, { stream: true }); // 将新的数据片段追加到缓冲区
        let lines = buffer.split("\n\n"); // 处理累积的数据

        // 缓冲区的最后一行可能还未完整接收,保留在缓冲区中,-1
        for (let i = 0; i < lines.length - 1; i++) {
            let line = lines[i];
            line = line.substring(5); // 移除 'data:' 前缀
            if (line.includes('[DONE]')) {
                return; // End
            }
            try {
                let data = JSON.parse(line);
                let delta = data['choices'][0]['delta'];
                let content = delta['content'] ? delta['content'] : "";
                yield content; // 传递数据给调用者
            } catch (error) {
                console.warn(`Error parsing JSON: ${error}\n\nError data: ${line}`);
            }
        }

        // 保留最后一行在缓冲区中
        buffer = lines.slice(-1);
    }

    return buffer;
}

/**
 * @typedef {Object} CheckResponseResult
 * @property {boolean} status 检查是否通过
 * @property {string} message 检查失败时的消息
 */

/**
 * @typedef {Object} ErrorResponse
 * @property {Object} message 错误消息
 * @property {Object} stack 错误堆栈
 * @property {Object} enumerable 可枚举的错误属性
 * @property {string} source 错误来源
 */

/**
 * @typedef {Object} TransRawData
 * @property {boolean} done 操作是否完成
 * @property {CheckResponseResult|null} checkPassed 检查是否通过的结果
 * @property {Object|null} response 响应对象
 * @property {string|null} text 处理后的文本
 * @property {ErrorResponse} error 错误列表
 * @property {string|null} message 可能的消息
 */

/**
 * 通用翻译函数
 * @param {Object} options GM_xmlhttpRequest 的参数
 * @param {Function} processer 响应再处理函数,它接收响应文本,并应返回处理后的文本。
 * @param {Function} checkResponse 检查文本是否符合预期的函数,它接收文本,并返回一个Object,包含状态和信息。默认为返回 { status: true, message: 'ok' }
 * @param {Function} getResponseText 重写响应文本获取函数,它接收response,并返回响应文本。 默认为 response.responseText
 * @returns {Promise<TransRawData>} 返回 Promise,其解析值为翻译结果对象
 */
async function BaseTranslate(options, processer, checkResponse = () => { return { status: true, message: 'ok' } }, getResponseText = (response) => response.responseText) {
    const result = {
        done: false,
        checkPassed: null,
        response: null,
        responseText: null,
        text: "",
        error: null,
        message: null
    };
    const helpText = i18next.t('error.basic', { ns: 'translator' }); // 基本帮助提示信息
    const toDo = async () => {
        try {
            result.response = await OJB_GMRequest(options);
            result.responseText = result.response.responseText;
            result.text = getResponseText(result.response);
        } catch (err) {
            console.warn(err);
            result.error = {
                message: err.message || null,
                stack: err.stack ? err.stack.replace(/\n/g, '<br>').replace(/\s/g, '&nbsp;') : null,
                enumerable: err,
                source: 'GMRequest'
            };
            result.message = `${i18next.t('error.GMRequest', { ns: 'translator' })}${helpText}`;
            throw result;
        }
        try {
            result.text = processer(result.text);
        } catch (err) {
            console.warn(err);
            result.error = {
                message: err.message || null,
                stack: err.stack ? err.stack.replace(/\n/g, '<br>').replace(/\s/g, '&nbsp;') : null,
                enumerable: err,
                source: 'processer'
            };
            result.message = `${i18next.t('error.processer', { ns: 'translator' })}${helpText}`;
            throw result;
        }
        try {
            result.checkPassed = checkResponse(result.text);
            if (result.checkPassed.status) result.done = true;
            else result.message = result.checkPassed.message;
            return result;
        } catch (err) {
            console.warn(err);
            result.error = {
                message: err.message || null,
                stack: err.stack ? err.stack.replace(/\n/g, '<br>').replace(/\s/g, '&nbsp;') : null,
                enumerable: err,
                source: 'checkResponse'
            };
            result.message = `${i18next.t('error.checkResponse', { ns: 'translator' })}${helpText}`;
            throw result;
        }
    };

    return await OJB_promiseRetryWrapper(toDo, {
        maxRetries: 3,
        errorHandler: (err, maxRetries, attemptsLeft) => {
            const detailedError = {
                maxRetries: maxRetries,
                attemptsLeft: attemptsLeft,
                ...err
            };
            return detailedError;
        }
    });
}

/**
 * 查询服务余额
 * @param {Object} quotaConfig - 配额配置对象
 * @returns {Promise} 返回包含余额信息的 Promise
 */
async function queryServerBalance(quotaConfig) {
    // 确保传入了有效的配置对象
    if (!quotaConfig || !quotaConfig.url) {
        return Promise.reject(new Error('Quota configuration is missing.'));
    }

    // 准备请求选项
    const requestOptions = {
        method: quotaConfig.method || 'GET',
        url: quotaConfig.url,
        headers: {
            ...Object.assign({}, ...quotaConfig.header)
        },
        data: JSON.stringify({ ...Object.assign({}, ...quotaConfig.data) })
    };

    // 发送请求并返回 Promise
    return OJB_GMRequest(requestOptions).then(response => {
        try {
            const responseData = JSON.parse(response.responseText);
            // 从响应数据中提取余额
            const surplusPath = quotaConfig.surplus;
            const surplusValue = OJB_evaluatePathOrExpression(responseData, surplusPath);
            return surplusValue;
        } catch (error) {
            return Promise.reject(new Error('Failed to parse balance response.'));
        }
    }).catch(error => {
        console.warn('Error querying balance:', error);
        return Promise.reject(error);
    });
}

/**
 * 确认 jQuery 已加载
 * @param {number} retryDelay 重试延迟(毫秒)
 * @returns {Promise<void>}
 */
async function ensureJQueryIsLoaded(retryDelay = 50) {
    while (typeof jQuery === 'undefined') {
        console.warn(`JQuery is not loaded. Retry after ${retryDelay} ms.`);
        await OJB_delay(retryDelay);
        retryDelay = Math.min(retryDelay * 2, 2000);
    }
}

/**
 * 加载必须的函数
 * @returns {Promise} 加载提示信息
 */
async function loadRequiredFunctions() {
    await initVar();// 初始化全局变量
    return Promise.allSettled([
        initDB(), // 连接数据库
        initI18next(), // i18next初始化
        initButtonFunc(), // 加载按钮相关函数
        initHTML2MarkDown(), // 初始化html2markdown转换器
        checkScriptVersion(), // 更新检查
        // ...(OJBetter.typeOfPage.is_acmsguru ? [acmsguruReblock()] : []) // 为acmsguru题面重新划分div
    ]);
}

/**
 * DOM加载后即可执行
 */
function initOnDOMReady() {
    showAnnounce(); // 显示公告
    showWarnMessage(); // 显示警告消息
    initSettingsPanel(); // 加载设置按钮面板
    initMonacoEditor(); // 初始化monaco编辑器资源
    localizeWebsite(); // 网站本地化替换
    addDependencyStyles(); // 添加一些依赖库的样式
    addI18nStyles(); // 添加包含i18n内容的样式
    // if (OJBetter.basic.expandFoldingblocks) ExpandFoldingblocks(); // 折叠块展开
    // if (OJBetter.basic.renderPerfOpt) RenderPerfOpt(); // 折叠块渲染优化
    // if (OJBetter.basic.selectElementPerfOpt) SelectElementPerfOpt(); // 下拉选择框性能优化
    if (OJBetter.typeOfPage.is_problem) {
        const problemPageLinkbar = new ProblemPageLinkbar(); // 创建题目页相关链接栏
        if (OJBetter.basic.showCF2vjudge) CF2vjudge(problemPageLinkbar); // 跳转到Vjudge按钮
        if (OJBetter.basic.showJumpToLuogu) CF2luogu(problemPageLinkbar); // 跳转到洛谷按钮
        if (OJBetter.clist.enabled.problem) showRatingByClist_problem(problemPageLinkbar); // problem页显示Rating
    }
    if (OJBetter.typeOfPage.is_contest) {
        if (OJBetter.clist.enabled.contest) showRatingByClist_contest(); // contest页显示Rating
    }
    // if (OJBetter.typeOfPage.is_problemset) {
    //     if (OJBetter.clist.enabled.problemset) showRatingByClist_problemset(); // problemset页显示Rating
    // }
    if (OJBetter.typeOfPage.is_problem && OJBetter.monaco.enableOnProblemPage) {
        addProblemPageCodeEditor(); // 添加题目页代码编辑器
    }
}

/**
 * 需要在页面资源完全加载后执行的函数
 */
function onResourcesReady(loadingMessage) {
    if (OJBetter.preference.showLoading) loadingMessage.updateStatus(`${OJBetter.state.name} —— ${i18next.t('loadFunc', { ns: 'alert' })}`);
    initializeInParallel(loadingMessage);
    initializeSequentially(loadingMessage);
}

/**
 * 可以异步并行的函数
 */
function initializeInParallel(loadingMessage) {
    if (OJBetter.basic.darkMode == "dark") darkModeStyleAdjustment(); // 黑暗模式额外的处理事件
    // if (OJBetter.basic.commentPaging) CommentPagination(); // 评论区分页
    if (OJBetter.translation.comment.transMode == "2") multiChoiceTranslation(); // 选段翻译支持
    if (OJBetter.monaco.beautifyPreBlocks) beautifyPreBlocksWithMonaco(); // 美化Pre代码块
}

/**
 * 必须按序执行的函数
 */
async function initializeSequentially(loadingMessage) {
    await addConversionButton(); // 添加MD/复制/翻译按钮
    if ((OJBetter.typeOfPage.is_problem || OJBetter.typeOfPage.is_completeProblemset) && OJBetter.translation.memory.enabled) {
        await initTransResultsRecover(); // 翻译结果恢复功能初始化
    }
    if (OJBetter.translation.auto.enabled) {
        await initTransWhenViewable(); // 自动翻译
    }
    // if (OJBetter.basic.standingsRecolor && OJBetter.typeOfPage.is_cfStandings) {
    //     await recolorStandings(); // cf赛制榜单重新着色
    // }
    if (OJBetter.preference.showLoading) loadingMessage.updateStatus(`${OJBetter.state.name} —— ${i18next.t('loadSuccess', { ns: 'alert' })}`, 'success', 3000);
}

/**
 * 主方法
 */
async function main() {
    await ensureJQueryIsLoaded(); // 等待jQuery加载
    const loadingMessage = new LoadingMessage();
    await loadRequiredFunctions(); // 加载必须的函数
    initOnDOMReady(); // DOM加载后即可执行的函数
    if (OJBetter.preference.showLoading) loadingMessage.updateStatus(`${OJBetter.state.name} —— ${i18next.t('onload', { ns: 'alert' })}`);

    // 检查页面资源是否已经完全加载
    if (OJBetter.state.notWaiteLoaded) {
        onResourcesReady(loadingMessage);
    } else {
        if (document.readyState === 'complete') {
            onResourcesReady(loadingMessage);
        } else {
            window.addEventListener('load', () => onResourcesReady(loadingMessage));
        }
    }
};

// ------------------------------
// 脚本加载入口
if (document.readyState === 'loading') {
    document.addEventListener("DOMContentLoaded", main);
} else {
    main(); // 如果DOMContentLoaded已经触发,立即执行
}
// ------------------------------

// ------------------------------
// 配置自动迁移代码(将在10个小版本后移除-1.19)
// ------------------------------

if (GM_getValue("openai_key") || GM_getValue("api2d_key")) {
    const newConfig = { "choice": -1, "configurations": [] };
    if (GM_getValue("openai_key")) {
        let config1 = {
            "note": "我的配置1",
            "model": GM_getValue("openai_model"),
            "key": GM_getValue("openai_key"),
            "proxy": GM_getValue("openai_proxy"),
            "_header": "",
            "_data": ""
        }
        if (GM_getValue("translation") === "openai") newConfig.choice = 0;
        newConfig.configurations.push(config1);
    }
    if (GM_getValue("api2d_key")) {
        let config2 = {
            "note": "api2d",
            "model": GM_getValue("api2d_model"),
            "key": GM_getValue("api2d_key"),
            "proxy": GM_getValue("api2d_request_entry") + '/v1/chat/completions',
            "_header": GM_getValue("x_api2d_no_cache") ? "" : " x-api2d-no-cache : 1",
            "_data": ""
        }
        if (GM_getValue("translation") === "api2d") {
            if (GM_getValue("openai_key")) newConfig.choice = 1;
            else newConfig.choice = 0;
        }
        newConfig.configurations.push(config2);
    }
    GM_setValue("chatgpt-config", newConfig);
    const keysToDelete = ["openai_key", "openai_model", "openai_proxy", "api2d_key", "api2d_model", "api2d_request_entry", "x_api2d_no_cache", "showOpneAiAdvanced"];
    keysToDelete.forEach(key => {
        if (GM_getValue(key) != undefined) GM_deleteValue(key);
    });
    if (GM_getValue("translation") === "api2d") GM_setValue("translation", "openai");
    location.reload();
}


// ------------------------------
// 配置自动迁移代码(将在10个小版本后移除-1.23)
// ------------------------------

{
    let bottomZh_CN = GM_getValue("bottomZh_CN");
    if (bottomZh_CN !== undefined) {
        if (bottomZh_CN == true) {
            GM_setValue("localizationLanguage", "zh");
        } else {
            GM_setValue("localizationLanguage", "initial");
        }
        GM_deleteValue("bottomZh_CN");
        location.reload();
    }
}
{
    let config = GM_getValue("chatgpt-config");
    if (config && config !== undefined) {
        let index = parseInt(config.choice, 10);
        if (index == -1) config.choice = "";
        else config.choice = config.configurations[index].note;
        config.configurations.forEach(function (item) {
            item.name = item.note;
            delete item.note;
        });
        GM_deleteValue("chatgpt-config");
        GM_setValue("chatgpt_config", config);
        location.reload();
    }
}

// ------------------------------
// 配置自动迁移代码(将在10个小版本后移除-1.24)
// ------------------------------

{
    let config = GM_getValue("compilerSelection");
    if (config !== undefined) {
        if (config === "61") {
            GM_setValue("compilerSelection", "5001");
            location.reload();
        }
    }
}

Codeforces 脚本

1. Codeforces Better

// ==UserScript==
// @name         Codeforces Better!
// @namespace    https://greasyfork.org/users/747162
// @version      1.76.0
// @author       北极小狐
// @match        *://*.codeforces.com/*
// @match        *://*.codeforc.es/*
// @run-at       document-start
// @connect      www2.deepl.com
// @connect      api-free.deepl.com
// @connect      api.deepl.com
// @connect      api.deeplx.org
// @connect      www.iflyrec.com
// @connect      dict.youdao.com
// @connect      api.interpreter.caiyunai.com
// @connect      translate.google.com
// @connect      openai.api2d.net
// @connect      api.openai.com
// @connect      www.luogu.com.cn
// @connect      vjudge.net
// @connect      clist.by
// @connect      greasyfork.org
// @connect      rextester.com
// @connect      wandbox.org
// @connect      sustech.edu.cn
// @connect      aowuucdn.oss-cn-beijing.aliyuncs.com
// @connect      aowuucdn.oss-accelerate.aliyuncs.com
// @connect      127.0.0.1
// @connect      *
// @grant        GM_xmlhttpRequest
// @grant        GM_info
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_listValues
// @grant        GM_deleteValue
// @grant        GM_addStyle
// @grant        GM_setClipboard
// @grant        GM_getResourceText
// @icon         https://aowuucdn.oss-accelerate.aliyuncs.com/codeforces.png
// @require      https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/turndown/7.2.0/turndown.min.js#sha512-sJzEecN5Nk8cq81zKtGq6/z9Z/r3q38zV9enY75IVxiG7ybtlNUt864sL4L1Kf36bYIwxTMVKQOtU4VhD7hGrw==
// @require      https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/markdown-it/13.0.2/markdown-it.js#sha512-2LtYcLGnCbAWz9nDIrfG2pHFiFu9n+3oGecQlzLuYsLgen/oxiYscGWnDST9J9EZanlsQkDD0ZP2n/6peDuALQ==
// @require      https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/crypto-js/4.2.0/crypto-js.min.js#sha512-a+SUDuwNzXDvz4XrIcXHuCf089/iJAoN4lmrXJg18XnduKK6YlDHNRalv4yd1N40OKI80tFidF+rqTFKGPoWFQ==
// @require      https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/chroma-js/2.4.2/chroma.min.js#sha512-zInFF17qBFVvvvFpIfeBzo7Tj7+rQxLeTJDmbxjBz5/zIr89YVbTNelNhdTT+/DCrxoVzBeUPVFJsczKbB7sew==
// @require      https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/xterm/5.5.0/xterm.js#sha512-Gujw5GajF5is3nMoGv9X+tCMqePLL/60qvAv1LofUZTV9jK8ENbM9L+maGmOsNzuZaiuyc/fpph1KT9uR5w3CQ==
// @require      https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/dexie/4.0.7/dexie.min.js#sha512-882VotT07mOQRzqIxsyxHzJX0XUaoeee3qXp4THg1A0KI0XFnWFAaLFQm0x6OW3pHSIipVZW+gzQ1w9b6uvkVw==
// @require      https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/i18next/23.11.5/i18next.min.js#sha512-3RSGkmT48HnO+hlmzGYDx5/w2LIBX0O5hSuYX6KWAxmvVlSjFgoxIaWa2tlMExheGvt3lLyxeTsXfpC47yb8CQ==
// @require      https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/i18next-http-backend/2.5.2/i18nextHttpBackend.min.js#sha512-bBb+wrGRTx4MvHpksYb1Iv5oJ1o8ineCqpc0cnTgdJQhuAFJJ93SEVXxUOCptvt0vAqYdjzWO5emorYUBt6Ceg==
// @require      https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/jquery-i18next/1.2.1/jquery-i18next.min.js#sha512-79RgNpOyaf8AvNEUdanuk1x6g53UPoB6Fh2uogMkOMGADBG6B0DCzxc+dDktXkVPg2rlxGvPeAFKoZxTycVooQ==
// @require      https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/highlight.js/11.9.0/highlight.min.js#sha512-D9gUyxqja7hBtkWpPWGt9wfbfaMGVt9gnyCvYa+jojwwPHLCzUm5i8rpk7vD7wNee9bA35eYIjobYPaQuKS1MQ==
// @require      https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/dialog-polyfill/0.5.6/dialog-polyfill.min.js#sha512-qUIG93zKzcLBVD5RGRbx2PBmbVRu+tJIl+EPLTus0z8I1AMru9sQYdlf6cBacSzYmZVncB9rcc8rYBnazqgrxA==
// @require      https://update.greasyfork.org/scripts/484742/1311040/i18nextChainedBackendjs.js#sha512-JYm2AqU8EvoEOnCucDItAsNtmGcjbxccOXjnwNFp87zdlyclpEephXrgR2sMlWj/gL4DCJUN3X0JhI1omaRO0A==
// @require      https://update.greasyfork.org/scripts/484743/1311041/i18next-localstorage-backendjs.js#sha512-kY1lU3DCvgzkWkOl47sIlmLKdgDcO4T3NYN6p/ET4oi3fnKO74sHUt1xYGtksIHXciKF8Jt+N4RDqG3CRoeYww==
// @resource     acwing_cpp_code_completer https://aowuucdn.oss-accelerate.aliyuncs.com/acwing_cpp_code_completer-0.0.11.json#sha512-DQVpao4qMMExToRdid0g/S0nbO/C9hwCECjI5aW8A0g7nvi8hEcD2Lw3QIqdJBV7haP15oJOocfwuiw7ryTO9w==
// @resource     wandboxlist https://wandbox.org/api/list.json
// @resource     xtermcss https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/xterm/5.5.0/xterm.min.css#sha512-XpXUuzg5afNt1bsgnrOesXP70TLH8tXYYK5sK+Y0UV+YBvJn9EfRFYWy4HT3TVDfH0nl1CO0lwOxIrt2gk9qjg==
// @resource     selectpagecss https://aowuucdn.oss-accelerate.aliyuncs.com/css/selectpage.css#sha512-cRXJfA2tEcAxHEKylJfxteY17N7j9fia3waahHOVnvl63uVZT9OQ7jjjpofZMVZ4JSX3BRET+mI8UvKnsXd3NA==
// @resource     dialogpolyfillcss https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/dialog-polyfill/0.5.6/dialog-polyfill.min.css#sha512-J2+1q+RsZuJXabBfH1q/fgRr6jMy9By5SwVLk7bScEW7NFJkMUXxfeOyyxtDe6fsaJ4jsciexSlGrPYn9YbBIg==
// @license      GPL3
// @compatible	 Chrome
// @compatible	 Firefox
// @compatible	 Edge
// @incompatible safari
// @supportURL   https://github.com/beijixiaohu/OJBetter/issues
// @name:zh-TW   Codeforces Better!
// @name:en      Codeforces Better!
// @name:de      Codeforces Better!
// @name:fr      Codeforces Better!
// @name:ko      Codeforces Better!
// @name:pt      Codeforces Better!
// @name:ja      Codeforces Better!
// @name:es      Codeforces Better!
// @name:it      Codeforces Better!
// @name:hi      Codeforces Better!
// @description         一个适用于 Codeforces 的 Tampermonkey 脚本,增强功能与界面。
// @description:zh-TW   一個適用於 Codeforces 的 Tampermonkey 腳本,增強功能與界面。
// @description:en      A Tampermonkey script for Codeforces that enhances functionality and interface.
// @description:de      Ein Tampermonkey-Skript für Codeforces, das Funktionalität und Benutzeroberfläche verbessert.
// @description:fr      Un script Tampermonkey pour Codeforces qui améliore les fonctionnalités et l'interface.
// @description:ko      Codeforces를 위한 Tampermonkey 스크립트로 기능과 인터페이스를 개선합니다.
// @description:pt      Um script Tampermonkey para Codeforces que aprimora a funcionalidade e a interface.
// @description:ja      Codeforces用のTampermonkeyスクリプトで機能とインターフェースを強化します。
// @description:es      Un script Tampermonkey para Codeforces que mejora la funcionalidad y la interfaz.
// @description:it      Uno script Tampermonkey per Codeforces che migliora la funzionalità e l'interfaccia.
// @description:hi      Codeforces के लिए एक Tampermonkey स्क्रिप्ट जो कार्यक्षमता और इंटरफ़ेस को बेहतर बनाता है।
// @downloadURL https://update.greasyfork.org/scripts/465777/Codeforces%20Better%21.user.js
// @updateURL https://update.greasyfork.org/scripts/465777/Codeforces%20Better%21.meta.js
// ==/UserScript==

/**
 * @namespace OJBetter
 * @desc 主命名空间
 */
const OJBetter = {};

/**
 * @namespace state
 * @desc 描述脚本的当前状态。
 * @memberof OJBetter
 */
OJBetter.state = {
    /** @type {string} 脚本名*/
    name: GM_info.script.name,
    /** @type {string} 格式化后的脚本名*/
    formatName: undefined,
    /** @type {string} 版本号*/
    version: GM_info.script.version,
    /** @type {boolean?} 是否跳过页面加载等待 */
    notWaiteLoaded: undefined,
    /** @type {string} 最后公告版本,用于标识版本更新完成提示 */
    lastAnnounceVer: undefined,
    /** @type {string} 最后读取的有效公告版本 */
    lastReadAnnounceVer: undefined,
    /** @type {number} 当前已打开的模态对话框数量*/
    openDialogCount: 0
};

/**
 * @namespace common
 * @desc 通用设置和属性。
 * @memberof OJBetter
 */
OJBetter.common = {
    /** @type {string} 网站的主机地址 */
    hostAddress: location.origin,
    /** @type {string?} Codeforces的CSRF令牌 */
    cf_csrf_token: undefined,
    /** @type {Array?} 任务队列 */
    taskQueue: undefined,
    /** @type {object} OJBetter数据库连接实例*/
    database: undefined,
    /** @type {object} turndownService实例*/
    turndownService: undefined,
};

/**
 * @namespace basic
 * @desc 基本的用户界面设置。
 * @memberof OJBetter
 */
OJBetter.basic = {
    /** @type {string} 黑暗模式设置 */
    darkMode: undefined,
    /** @type {boolean?} 是否展开折叠块 */
    expandFoldingblocks: undefined,
    /** @type {boolean?} 是否开启折叠块渲染性能优化 */
    renderPerfOpt: undefined,
    /** @type {boolean?} 是否开启下拉选择框性能优化 */
    selectElementPerfOpt: undefined,
    /** @type {boolean?} 评论区分页 */
    commentPaging: undefined,
    /** @type {boolean?} 显示跳转到Luogu按钮 */
    showJumpToLuogu: undefined,
    /** @type {boolean?} 显示跳转到Virtual Judge按钮 */
    showCF2vjudge: undefined,
    /** @type {boolean?} 比赛排行榜重新着色 */
    standingsRecolor: undefined,
    /** @type {boolean?} 隐藏题目问题标签 */
    hiddenProblemTag: undefined
};

/**
 * @namespace typeOfPage
 * @desc 页面类型判断。
 * @memberof OJBetter
 */
OJBetter.typeOfPage = {
    /** @type {boolean?} 是否是轻量站 */
    is_mSite: undefined,
    /** @type {boolean?} 是否是训练营页面 */
    is_gym: undefined,
    /** @type {boolean?} 是否是acmsguru页面 */
    is_acmsguru: undefined,
    /** @type {boolean?} 是否是旧版LaTeX页面 */
    is_oldLatex: undefined,
    /** @type {boolean?} 是否是题目集页面 */
    is_contest: undefined,
    /** @type {boolean?} 是否是题目页面 */
    is_problem: undefined,
    /** @type {boolean?} 是否是完整的问题集页面 */
    is_completeProblemset: undefined,
    /** @type {boolean?} 是否是问题集中的问题页面 */
    is_problemset_problem: undefined,
    /** @type {boolean?} 是否是问题集页面 */
    is_problemset: undefined,
    /** @type {boolean?} 是否是Codeforces排名页面 */
    is_cfStandings: undefined,
    /** @type {boolean?} 是否是提交页面 */
    is_submitPage: undefined,
    /** @type {boolean?} 是否是代码状态页面 */
    is_statePage: undefined,
    /** @type {boolean?} 是否是提交记录页面 */
    is_submissions: undefined,
    /** @type {boolean?} 是否是提交记录详情页面 */
    is_submission: undefined,
};

/**
 * @namespace localization
 * @desc 本地化设置。
 * @memberof OJBetter
 */
OJBetter.localization = {
    /** @type {string?} 网站语言 */
    websiteLang: undefined,
    /** @type {string?} 脚本语言 */
    scriptLang: undefined
};

/**
 * @namespace translation
 * @desc 翻译设置。
 * @memberof OJBetter
 */
OJBetter.translation = {
    /** @type {string?} 翻译服务选择 */
    choice: undefined,
    /** @type {string?} 目标语言 */
    targetLang: undefined,
    comment: {
        /** @type {string?} 评论翻译服务选择 */
        choice: undefined,
        /** @type {string?} 评论翻译模式 */
        transMode: undefined
    },
    auto: {
        /** @type {boolean?} 自动翻译开关 */
        enabled: undefined,
        /** @type {number?} 短文本长度限制 */
        shortTextLength: undefined,
        mixTrans: {
            /** @type {boolean?} 混合翻译开关 */
            enabled: undefined,
            /** @type {Array?} 混合翻译服务列表 */
            servers: undefined
        }
    },
    memory: {
        /** @type {boolean?} 翻译记忆开关 */
        enabled: undefined,
        /** @type {Object?} 翻译记忆树 */
        ttTree: undefined
    },
    /** @type {string?} 重翻译时的行为 */
    retransAction: undefined,
    /** @type {number?} 等待时间 */
    waitTime: undefined,
    /** @type {boolean?} 替换符 */
    replaceSymbol: undefined,
    /** @type {boolean?} 过滤文本中的*号 */
    filterTextWithoutEmphasis: undefined,
    /** @type {boolean?} 强制使用turndown转换 */
    forceTurndownConversion: undefined
};

/**
 * @namespace clist
 * @desc Clist相关设置。
 * @memberof OJBetter
 */
OJBetter.clist = {
    enabled: {
        /** @type {boolean?} 比赛页面开关 */
        contest: undefined,
        /** @type {boolean?} 问题页面开关 */
        problem: undefined,
        /** @type {boolean?} 问题集页面开关 */
        problemset: undefined
    },
    /** @type {boolean?} Rating数据防剧透 */
    ratingHidden: undefined,
    /** @type {string?} Clist key */
    authorization: undefined
};

/**
 * @namespace monaco
 * @desc Monaco编辑器配置。
 * @memberof OJBetter
 */
OJBetter.monaco = {
    /** @type {boolean?} 在问题页面上启用Monaco编辑器 */
    enableOnProblemPage: undefined,
    /** @type {boolean?} 美化pre代码块 */
    beautifyPreBlocks: undefined,
    /** @type {boolean} Monaco编辑器加载完成标志 */
    loaderOnload: false,
    lsp: {
        /** @type {Array?} LSP套接字数组 */
        socket: [],
        /** @type {boolean?} 是否启用LSP */
        enabled: undefined,
        /** @type {string?} 工作路径 */
        workUri: undefined,
        /** @type {string?} 套接字URL */
        socketUrl: undefined
    },
    complet: {
        /** @type {boolean?} 是否启用C++代码补全模板 */
        cppCodeTemplate: undefined,
        /** @type {Object?} 自定义配置 */
        customConfig: undefined
    },
    /** @type {Object?} Monaco编辑器实例 */
    editor: null,
    /** @type {string?} 在线编译器选择 */
    onlineCompilerChoice: undefined,
    /** @type {string?} 记忆编译器语言选择 */
    compilerSelection: undefined,
    /** @type {string?}  当前选择的语言 */
    nowLangSelect: undefined,
    setting: {
        /** @type {Array?} 语言设置数组 */
        language: [],
        /** @type {string?} 位置 */
        position: undefined,
        /** @type {boolean} 位置初始化标志 */
        position_initialized: false,
        /** @type {number?} 字体大小 */
        fontsize: undefined,
        /** @type {boolean?} 鼠标滚动锁定 */
        alwaysConsumeMouseWheel: undefined,
        /** @type {boolean?} 提交代码二次确认 */
        isCodeSubmitDoubleConfirm: undefined,
        /** @type {boolean?} 测试通过后自动提交 */
        autoSubmitAfterPass: undefined,
        /** @type {string?} 提交按钮位置 */
        submitButtonPosition: undefined,
        /** @type {boolean?} 自动保存代码 */
        autoMemoryCode: undefined
    }
};

/**
 * @namespace deepl
 * @desc DeepL翻译服务配置。
 * @memberof OJBetter
 */
OJBetter.deepl = {
    /** @type {Object?} DeepL配置对象 */
    configs: undefined,
    config: {
        /** @type {string?} 类型 */
        type: undefined,
        /** @type {string?} 名称 */
        name: undefined,
        /** @type {string?} API类型 */
        apiGenre: undefined,
        /** @type {string?} API密钥 */
        key: undefined,
        /** @type {string?} 代理 */
        proxy: undefined,
        /** @type {Object?} 额外请求头 */
        header: undefined,
        /** @type {Object?} 额外请求数据 */
        data: undefined,
        quota: {
            /** @type {string?} 余额URL */
            url: undefined,
            /** @type {string?} 余额请求方法 */
            method: undefined,
            /** @type {Object?} 余额请求头 */
            header: undefined,
            /** @type {Object?} 余额请求数据 */
            data: undefined,
            /** @type {number?} 剩余配额 */
            surplus: undefined
        }
    },
    /** @type {boolean?} 启用重点保护 */
    enableEmphasisProtection: undefined,
    /** @type {boolean?} 启用链接保护 */
    enableLinkProtection: undefined
};

/**
 * @namespace chatgpt
 * @desc ChatGPT服务配置。
 * @memberof OJBetter
 */
OJBetter.chatgpt = {
    /** @type {Object?} ChatGPT配置对象 */
    configs: undefined,
    config: {
        /** @type {string?} 名称 */
        name: undefined,
        /** @type {string?} 模型 */
        model: undefined,
        /** @type {string?} API密钥 */
        key: undefined,
        /** @type {string?} 代理 */
        proxy: undefined,
        /** @type {Object?} 额外请求头 */
        header: undefined,
        /** @type {Object?} 额外请求数据 */
        data: undefined,
        quota: {
            /** @type {string?} 余额URL */
            url: undefined,
            /** @type {string?} 余额请求方法 */
            method: undefined,
            /** @type {Object?} 余额请求头 */
            header: undefined,
            /** @type {Object?} 余额请求数据 */
            data: undefined,
            /** @type {number?} 剩余配额 */
            surplus: undefined
        }
    },
    /** @type {boolean?} 是否为流式传输 */
    isStream: undefined,
    /** @type {string?} 是否使用自定义Prompt */
    customPrompt: undefined,
    /** @type {boolean?} 是否作为系统Prompt */
    asSystemPrompt: undefined
};

/**
 * @namespace preference
 * @desc 偏好设置
 * @memberof OJBetter
 */
OJBetter.preference = {
    /** @type {boolean?} 是否显示加载动画 */
    showLoading: undefined,
    /** @type {boolean?} 是否显示悬停目标区域 */
    hoverTargetAreaDisplay: undefined,
    /** @type {string?} 按钮图标大小 */
    iconButtonSize: undefined,
    /** @type {string?} 评测状态文本替换规则 */
    judgeStatusReplaceText: undefined,

};

/**
 * @namespace dev
 * @desc 维护
 * @memberof OJBetter
 */
OJBetter.dev = {
    /** @type {boolean?} 是否显示规则标记 */
    isRuleMarkingEnabled: undefined,
};

/**
 * @namespace about
 * @desc 关于页信息
 * @memberof OJBetter
 */
OJBetter.about = {
    /** @type {string?} 更新通道 */
    updateChannel: undefined,
    /** @type {string?} 更新源 */
    updateSource: undefined
};

/**
 * @namespace supportList
 * @desc 支持列表
 * @memberof OJBetter
 */
OJBetter.supportList = {
    /** @type {object} 翻译支持列表和对应语言代码*/
    translationSupport: {
        'deepl': { 'zh': 'ZH', 'de': 'DE', 'fr': 'FR', 'ko': 'KO', 'pt': 'PT', 'ja': 'JA', 'es': 'ES', 'it': 'IT' },
        'iflyrec': { 'zh': '1' },
        'youdao': { 'zh': 'zh-CHS', 'zh-Hant': 'zh-CHT', 'de': 'de', 'fr': 'fr', 'ko': 'ko', 'pt': 'pt', 'ja': 'ja', 'es': 'es', 'it': 'it', 'hi': 'hi' },
        'google': { 'zh': 'zh-CN', 'zh-Hant': 'zh-TW', 'de': 'de', 'fr': 'fr', 'ko': 'ko', 'pt': 'pt', 'ja': 'ja', 'es': 'es', 'it': 'it', 'hi': 'hi' },
        'caiyun': { 'zh': 'auto2zh', 'ja': 'auto2ja', 'ko': 'auto2ko', 'es': 'auto2es', 'fr': 'auto2fr' },
        'openai': { 'zh': 'Chinese', 'zh-Hant': 'Traditional Chinese', 'de': 'German', 'fr': 'French', 'ko': 'Korean', 'pt': 'Portuguese', 'ja': 'Japanese', 'es': 'Spanish', 'it': 'Italian', 'hi': 'Hindi' }
    },
    /** @type {object} 更新源支持列表*/
    updateSourceSupportList: {
        'greasyfork': {
            'release': true,
            'dev': false
        },
        'github': {
            'release': true,
            'dev': true
        },
        'aliyunoss': {
            'release': true,
            'dev': true
        }
    }
}

// ------------------------------
// 一些工具函数
// ------------------------------

/**
 * 延迟函数
 * @param {number} ms 延迟时间(毫秒)
 * @returns {Promise<void>}
 */
function OJB_delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

/**
 * 等待直到指定的条件函数返回true。
 *
 * @param {() => boolean} conditionCheck 一个无参数的函数,用于检查条件是否满足。当函数返回true时,表示条件已满足。
 * @param {number} [interval=100] 检查条件的间隔时间,单位为毫秒。默认为100毫秒。
 * @returns {Promise<void>} 返回一个Promise,在条件满足时解决。
 */
async function OJB_waitUntilTrue(conditionCheck, interval = 100) {
    return new Promise((resolve) => {
        const checkCondition = async () => {
            if (conditionCheck()) {
                resolve();
            } else {
                await OJB_delay(interval);
                checkCondition();
            }
        };
        checkCondition();
    });
}

/**
 * 动态加载JavaScript库并返回一个Promise,该Promise在脚本加载完成后解决。
 *
 * @param {string} url - 要加载的JavaScript库的URL地址。
 * @param {string} [expectedHash] - 可选的Base64编码的SHA-512哈希值,用于校验脚本内容。格式为 "sha512-<Base64编码的哈希值>"。
 * @returns {Promise<void>} 一个Promise,它在脚本加载并执行完成后解决。
 */
async function OJB_LoadJS(url, expectedHash) {
    /**
     * 计算给定数据的SHA-512哈希值,并将其转换为十六进制字符串。
     *
     * @param {string} data - 要计算哈希值的数据。
     * @returns {Promise<string>} 一个Promise,它解析为数据的SHA-512哈希值的十六进制字符串。
     */
    const calculateHash = async (data) => {
        const encoder = new TextEncoder();
        const dataBuffer = encoder.encode(data);
        const hashBuffer = await crypto.subtle.digest('SHA-512', dataBuffer);
        const hashArray = Array.from(new Uint8Array(hashBuffer));
        return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
    };

    /**
     * 将Base64编码的字符串转换为十六进制字符串。
     *
     * @param {string} base64 - Base64编码的字符串。
     * @returns {string} 转换后的十六进制字符串。
     */
    const base64ToHex = (base64) => {
        const binaryString = atob(base64);
        const byteArray = new Uint8Array(binaryString.length);
        for (let i = 0; i < binaryString.length; i++) {
            byteArray[i] = binaryString.charCodeAt(i);
        }
        return Array.from(byteArray).map(b => b.toString(16).padStart(2, '0')).join('');
    };

    try {
        const response = await fetch(url);
        if (!response.ok) throw new Error(`Failed to fetch script: ${response.statusText}`);
        const scriptContent = await response.text();

        if (expectedHash) {
            // 去掉前缀 "sha512-"
            const base64Hash = expectedHash.replace(/^sha512-/, '');
            const actualHash = await calculateHash(scriptContent);
            const expectedHashHex = base64ToHex(base64Hash);
            if (actualHash !== expectedHashHex) throw new Error('SHA-512 hash mismatch');
        }

        const scriptElement = document.createElement("script");
        scriptElement.textContent = scriptContent;
        document.head.prepend(scriptElement);

        return Promise.resolve();
    } catch (error) {
        return Promise.reject(error);
    }
}

/**
 * 安全地创建JQuery对象
 * @description 通过字符串创建JQuery对象时,如果字符串以空格开头,在某些Jquery版本中会发生错误,过滤空格以安全的创建元素。
 * @param {string} string - 字符串。
 * @returns {JQuery} JQuery对象
 */
const OJB_safeCreateJQElement = function (string) {
    return $(string.replace(/^\s+/, ""));
}

/**
 * 将数字或者字符串解析为数字。
 * @memberof OJBetter.common
 * @param {string} val 要解析的字符串
 * @param {boolean} [strict=false] 是否进行严格类型检查
 * @returns {number} 解析结果
 * @throws {Error} 如果解析失败,则抛出错误
 */
const OJB_parseNumber = (val, strict = false) => {
    const num = Number(val);
    if (isNaN(num) || (strict && val.toString() !== num.toString())) {
        throw new Error('Invalid number');
    }
    return num;
};

/**
 * 将字符串解析为布尔值
 * @param {string} val - 要解析的字符串
 * @param {boolean} strict - 是否进行严格类型检查
 * @returns {boolean} - 解析结果
 * @throws {Error} - 如果解析失败,则抛出错误
 */
const OJB_parseBoolean = (val, strict) => {
    if (strict) {
        if (val === true || val === false) return val;
        throw new Error('Invalid boolean');
    }
    return val === 'true' ? true : val === 'false' ? false : val;
};

/**
 * 将字符串解析为对象
 * @param {string} val - 要解析的字符串
 * @returns {Object} - 解析结果
 * @throws {Error} - 如果解析失败,则抛出错误
 */
const OJB_parseObject = val => {
    try {
        return JSON.parse(val);
    } catch {
        throw new Error('Invalid JSON');
    }
};

/**
 * 将字符串解析为键值对数组
 * @param {string} val - 要解析的字符串
 * @returns {Object[]} - 解析结果
 * @throws {Error} - 如果解析失败,则抛出错误
 */
const OJB_parseLinePairArray = val => {
    if (typeof val !== 'string' || val.trim() === '') return [];
    return val.split("\n").filter(line => line.trim() !== '').map(line => {
        const indexOfFirstColon = line.indexOf(":");
        if (indexOfFirstColon === -1) throw new Error('Invalid LinePairArray format: ":" is missing');
        const key = line.substring(0, indexOfFirstColon).trim();
        const value = line.substring(indexOfFirstColon + 1).trim();
        return { [key]: value };
    });
};

/**
 * 移除文本中的HTML标签
 * @param {string} text - 包含HTML标签的文本
 * @returns {string} - 移除HTML标签后的文本
 */
const OJB_removeHTMLTags = function (text) {
    return text.replace(/<\/?[a-zA-Z]+("[^"]*"|'[^']*'|[^'">])*>/g, '');
}

/**
 * 获取对象中指定路径表达式的值
 * @param {Object} obj - 要计算的对象
 * @param {string} pathOrExpression - 要计算的路径表达式
 * @returns {any} - 计算结果
 * @example
 * const obj = {
 *   "a": {
 *     "b": 1
 *   },
 *   "c": 2
 * };
 * OJB_evaluatePathOrExpression(obj, "a.b"); // 1
 * OJB_evaluatePathOrExpression(obj, "a.b + c"); // 3
 * OJB_evaluatePathOrExpression(obj, "a.b + a.c"); // 1
 */
function OJB_evaluatePathOrExpression(obj, pathOrExpression) {
    const hasOperator = /[\+\-\*\/]/.test(pathOrExpression);
    const getPathValue = (obj, path) => {
        return path.split('.').reduce((acc, part) => {
            return acc !== undefined && acc !== null && acc.hasOwnProperty(part) ? acc[part] : undefined;
        }, obj);
    };
    const evaluateExpression = (obj, expression) => {
        const tokens = expression.split(/([\+\-\*\/])/).map(token => token.trim());
        const values = tokens.map(token => {
            if (/[\+\-\*\/]/.test(token)) {
                return token;
            } else {
                const value = getPathValue(obj, token);
                return value !== undefined ? value : 0;
            }
        });
        const evaluatedExpression = values.join(' ');
        try {
            return Function(`'use strict'; return (${evaluatedExpression});`)();
        } catch (e) {
            console.error('Expression evaluation error:', e);
            return undefined;
        }
    };
    return hasOperator ? evaluateExpression(obj, pathOrExpression) : getPathValue(obj, pathOrExpression);
}

/**
 * 获取 GM 存储的值并根据类型进行处理
 * @param {string} key - 要检索的值的键。
 * @param {any} defaultValue - 如果值未找到,则返回的默认值。
 * @param {Object} [options={}] - 配置选项对象。
 * @param {string} [options.type='string'] - 期望的值的类型。可选值:'string', 'number', 'boolean', 'object', 'array', 'linePairArray'。
 * @param {boolean} [options.strict=false] - 用于数字和布尔类型,表示是否进行严格类型检查。
 * @param {string} [options.pathOrExpression=''] - 用于对象或数组类型,表示路径表达式或获取元素的索引。
 * @returns {any} - 检索到的值。
 */
const OJB_getGMValue = (key, defaultValue, { type = 'string', strict = false, pathOrExpression = '' } = {}) => {
    let value = GM_getValue(key);
    if (value === undefined || value === null || value === "") {
        GM_setValue?.(key, defaultValue);
        return defaultValue;
    }

    const parsers = {
        string: val => val,
        number: (val) => OJB_parseNumber(val, strict),
        boolean: (val) => OJB_parseBoolean(val, strict),
        object: OJB_parseObject,
        array: OJB_parseObject,
        linePairArray: OJB_parseLinePairArray
    };

    if (!(type in parsers)) {
        console.error(`Unsupported type: ${type}`);
        return defaultValue;
    }

    try {
        value = parsers[type](value);
    } catch (e) {
        console.error('Error:', e.message);
        return defaultValue;
    }

    // The pathOrExpression processing is not applicable to linePairArray type
    if ((type === 'object' || type === 'array') && pathOrExpression) {
        const evaluated = OJB_evaluatePathOrExpression(value, pathOrExpression);
        if (evaluated === undefined) {
            console.error('Path or expression evaluation returned undefined');
            return defaultValue;
        }
        value = evaluated;
    }

    return value;
};

/**
 * 版本号比较方法
 * @param {string} version1 版本号1
 * @param {string} version2 版本号2
 * @returns {number} -1: version1 < version2, 0: version1 = version2, 1: version1 > version2
 */
const OJB_compareVersions = function (version1 = "0", version2 = "0") {
    const v1Array = version1.split(".").map(Number);
    const v2Array = version2.split(".").map(Number);
    const length = Math.max(v1Array.length, v2Array.length);
    for (let i = 0; i < length; i++) {
        const diff = (v1Array[i] || 0) - (v2Array[i] || 0);
        if (diff) return Math.sign(diff);
    }
    return 0;
}

/**
 * 获取上一个主版本号
 * @param {string} currentVersion 当前版本号
 * @returns {string} 上一个主版本号
 */
const OJB_getPreviousVersion = function (currentVersion) {
    const versionArray = currentVersion.split(".").map(Number);
    let lastNonZeroIndex = versionArray.length - 1;
    while (lastNonZeroIndex >= 0 && versionArray[lastNonZeroIndex] === 0) {
        lastNonZeroIndex--;
    }
    if (lastNonZeroIndex >= 0) {
        versionArray[lastNonZeroIndex]--;
        for (let i = lastNonZeroIndex + 1; i < versionArray.length; i++) {
            versionArray[i] = 0;
        }
    }
    return versionArray.join(".");
};

/**
 * 在指定根节点下观察指定选择器的元素,当元素存在时,执行回调函数
 * @param {Object} options - 配置对象
 * @param {string} options.selector - CSS选择器文本
 * @param {Function} options.callback - 回调函数,接收变动的节点作为参数
 * @param {Boolean} [options.triggerOnExist=true] - 如果为true,元素已存在时立即触发一次回调
 * @param {Element} [options.root=document.body] - 在哪个根节点下监听变化
 * @param {Boolean} [options.subtree=false] - 是否监听子树变化(即非直接子元素)
 */
function OJB_observeElement({
    selector,
    callback,
    triggerOnExist = true,
    root = document.body,
    subtree = false
}) {
    // 尝试获取选择器指定的元素
    const targetNode = root.querySelector(selector);

    if (targetNode) {
        // 如果元素已存在,直接开始观察
        observeAndReport(targetNode, callback);
        // 如果triggerOnExist为true,则立即触发一次回调
        if (triggerOnExist) {
            callback(targetNode);
        }
    } else {
        // 如果元素不存在,监听DOM变化直到该元素被添加
        const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                mutation.addedNodes.forEach((node) => {
                    if (node.nodeType === Node.ELEMENT_NODE && node.matches(selector)) {
                        observeAndReport(node, callback);
                        if (triggerOnExist) {
                            callback(node);
                        }
                        observer.disconnect(); // 停止监听
                    }
                });
            });
        });

        observer.observe(root, { childList: true, subtree, attributes: false });
    }

    function observeAndReport(node, callback) {
        const childObserver = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                mutation.addedNodes.forEach((addedNode) => {
                    if (addedNode.nodeType === Node.ELEMENT_NODE) {
                        callback(addedNode); // 执行回调函数
                    }
                });
            });
        });

        childObserver.observe(node, { childList: true, subtree: true, attributes: false });
    }
}

/**
 * 初始化全局变量
 */
async function initVar() {
    const { hostname, href } = window.location;
    OJBetter.state.formatName = (() => OJBetter.state.name
        .toLowerCase()
        .replace(/\s+/g, '-')
        .replace(/[^a-z0-9-]/g, ''))();
    OJBetter.state.lastAnnounceVer = OJB_getGMValue("lastAnnounceVer", "0");
    OJBetter.state.lastReadAnnounceVer = OJB_getGMValue("lastReadAnnounceVer", "0");
    OJBetter.typeOfPage.is_mSite = /^m[0-9]/.test(hostname);
    OJBetter.typeOfPage.is_oldLatex = $('.tex-span').length;
    OJBetter.typeOfPage.is_gym = href.includes("gym") && href.includes('/problem/');
    OJBetter.typeOfPage.is_acmsguru = href.includes("acmsguru") && href.includes('/problem/');
    OJBetter.typeOfPage.is_contest = /\/contest\/[\d\/\s]+$/.test(href) && !href.includes('/problem/');
    OJBetter.typeOfPage.is_problem = href.includes('/problem/');
    OJBetter.typeOfPage.is_completeProblemset = /problems\/?$/.test(href);
    OJBetter.typeOfPage.is_problemset_problem = href.includes('/problemset/') && href.includes('/problem/');
    OJBetter.typeOfPage.is_problemset = href.includes('/problemset') && !href.includes('/problem/');
    OJBetter.typeOfPage.is_submitPage = href.includes('/submit');
    OJBetter.typeOfPage.is_statePage = href.includes('/status');
    OJBetter.typeOfPage.is_submissions = href.includes('/submissions');
    OJBetter.typeOfPage.is_submission = href.includes('/submission');
    OJBetter.typeOfPage.is_cfStandings = href.includes('/standings') &&
        $('.standings tr:first th:nth-child(n+5)')
            .map(function () {
                return $(this).find('span').text();
            })
            .get()
            .every(score => /^[0-9]+$/.test(score));
    OJBetter.localization.websiteLang = OJB_getGMValue("localizationLanguage", "zh");
    OJBetter.localization.scriptLang = OJB_getGMValue("scriptL10nLanguage", "zh");
    OJBetter.basic.renderPerfOpt = OJB_getGMValue("renderPerfOpt", false);
    OJBetter.basic.selectElementPerfOpt = OJB_getGMValue("selectElementPerfOpt", false);
    OJBetter.basic.commentPaging = OJB_getGMValue("commentPaging", true);
    OJBetter.basic.showJumpToLuogu = OJB_getGMValue("showJumpToLuogu", true);
    OJBetter.basic.showCF2vjudge = OJB_getGMValue("showCF2vjudge", true);
    OJBetter.basic.standingsRecolor = OJB_getGMValue("standingsRecolor", true);
    OJBetter.basic.hiddenProblemTag = OJB_getGMValue("hiddenProblemTag", false);
    OJBetter.state.notWaiteLoaded = OJB_getGMValue("notWaiteLoaded", false);
    OJBetter.translation.targetLang = OJB_getGMValue("transTargetLang", "zh");
    OJBetter.translation.choice = OJB_getGMValue("translation", "deepl");
    OJBetter.translation.comment.transMode = OJB_getGMValue("commentTranslationMode", "0");
    OJBetter.translation.comment.choice = OJB_getGMValue("commentTranslationChoice", "0");
    OJBetter.translation.memory.enabled = OJB_getGMValue("memoryTranslateHistory", true);
    OJBetter.translation.auto.enabled = OJB_getGMValue("autoTranslation", false);
    OJBetter.translation.auto.shortTextLength = OJB_getGMValue("shortTextLength", "2000");
    OJBetter.translation.retransAction = OJB_getGMValue("retransAction", "0");
    OJBetter.translation.waitTime = OJB_getGMValue("transWaitTime", "200");
    OJBetter.translation.auto.mixTrans.enabled = OJB_getGMValue("allowMixTrans", true);
    OJBetter.translation.auto.mixTrans.servers = OJB_getGMValue("mixedTranslation", ['deepl', 'iflyrec', 'youdao', 'caiyun']);
    OJBetter.common.taskQueue = new TaskQueue();
    OJBetter.translation.replaceSymbol = OJB_getGMValue("replaceSymbol", "2");
    OJBetter.translation.filterTextWithoutEmphasis = OJB_getGMValue("filterTextWithoutEmphasis", false);
    OJBetter.translation.forceTurndownConversion = OJB_getGMValue("forceTurndownConversion", false);
    OJBetter.clist.enabled.contest = OJB_getGMValue("showClistRating_contest", false);
    OJBetter.clist.enabled.problem = OJB_getGMValue("showClistRating_problem", false);
    OJBetter.clist.enabled.problemset = OJB_getGMValue("showClistRating_problemset", false);
    OJBetter.clist.ratingHidden = OJB_getGMValue("RatingHidden", false);
    OJBetter.clist.authorization = OJB_getGMValue("clist_Authorization", "");
    //deepl
    OJBetter.deepl.config.type = OJB_getGMValue("deepl_type", "free");
    OJBetter.deepl.configs = OJB_getGMValue("deepl_config", {
        "choice": "",
        "configurations": []
    });
    if (OJBetter.deepl.configs.choice !== "" && OJBetter.deepl.configs.configurations.length !== 0) {
        const choice = OJBetter.deepl.configs.choice;
        const configuration = OJBetter.deepl.configs.configurations.find(obj => obj.name === choice);;
        if (configuration == undefined) {
            let existingConfig = GM_getValue('deepl_config');
            existingConfig.choice = "";
            GM_setValue('deepl_config', existingConfig);
            location.reload();
        }
        OJBetter.deepl.config.name = configuration.name;
        OJBetter.deepl.config.apiGenre = configuration.apiGenre;
        OJBetter.deepl.config.key = configuration.key;
        OJBetter.deepl.config.proxy = configuration.proxy;
        OJBetter.deepl.config.header = OJB_parseLinePairArray(configuration._header);
        OJBetter.deepl.config.data = OJB_parseLinePairArray(configuration._data);
        OJBetter.deepl.config.quota.url = configuration.quota_url;
        OJBetter.deepl.config.quota.method = configuration.quota_method;
        OJBetter.deepl.config.quota.header = OJB_parseLinePairArray(configuration.quota_header);
        OJBetter.deepl.config.quota.data = OJB_parseLinePairArray(configuration.quota_data);
        OJBetter.deepl.config.quota.surplus = configuration.quota_surplus;
    }
    OJBetter.deepl.enableEmphasisProtection = OJB_getGMValue("enableEmphasisProtection", true);
    OJBetter.deepl.enableLinkProtection = OJB_getGMValue("enableLinkProtection", true);
    //openai
    OJBetter.chatgpt.isStream = OJB_getGMValue("openai_isStream", true);
    OJBetter.chatgpt.customPrompt = OJB_getGMValue("openai_customPrompt", '');
    OJBetter.chatgpt.asSystemPrompt = OJB_getGMValue("openai_asSystemPrompt", false);
    OJBetter.chatgpt.configs = OJB_getGMValue("chatgpt_config", {
        "choice": "",
        "configurations": []
    });
    if (OJBetter.chatgpt.configs.choice !== "" && OJBetter.chatgpt.configs.configurations.length !== 0) {
        const choice = OJBetter.chatgpt.configs.choice;
        const configuration = OJBetter.chatgpt.configs.configurations.find(obj => obj.name === choice);;
        if (configuration == undefined) {
            let existingConfig = GM_getValue('chatgpt_config');
            existingConfig.choice = "";
            GM_setValue('chatgpt_config', existingConfig);
            location.reload();
        }
        OJBetter.chatgpt.config.name = configuration.name;
        OJBetter.chatgpt.config.model = configuration.model;
        OJBetter.chatgpt.config.key = configuration.key;
        OJBetter.chatgpt.config.proxy = configuration.proxy;
        OJBetter.chatgpt.config.header = OJB_parseLinePairArray(configuration._header);
        OJBetter.chatgpt.config.data = OJB_parseLinePairArray(configuration._data);
        OJBetter.chatgpt.config.quota.url = configuration.quota_url;
        OJBetter.chatgpt.config.quota.method = configuration.quota_method;
        OJBetter.chatgpt.config.quota.header = OJB_parseLinePairArray(configuration.quota_header);
        OJBetter.chatgpt.config.quota.data = OJB_parseLinePairArray(configuration.quota_data);
        OJBetter.chatgpt.config.quota.surplus = configuration.quota_surplus;
    }
    // 编辑器
    if (!OJBetter.typeOfPage.is_mSite) OJBetter.common.cf_csrf_token = Codeforces.getCsrfToken();
    else OJBetter.common.cf_csrf_token = "";
    OJBetter.monaco.compilerSelection = OJB_getGMValue("compilerSelection", "61");
    OJBetter.monaco.setting.fontsize = OJB_getGMValue("editorFontSize", "15");
    OJBetter.monaco.enableOnProblemPage = OJB_getGMValue("problemPageCodeEditor", true);
    OJBetter.monaco.beautifyPreBlocks = OJB_getGMValue("beautifyPreBlocks", true);
    OJBetter.monaco.complet.cppCodeTemplate = OJB_getGMValue("cppCodeTemplateComplete", true);
    OJBetter.monaco.onlineCompilerChoice = OJB_getGMValue("onlineCompilerChoice", "official");
    OJBetter.monaco.setting.isCodeSubmitDoubleConfirm = OJB_getGMValue("isCodeSubmitConfirm", true);
    OJBetter.monaco.setting.autoSubmitAfterPass = OJB_getGMValue("autoSubmitAfterPass", false);
    OJBetter.monaco.setting.alwaysConsumeMouseWheel = OJB_getGMValue("alwaysConsumeMouseWheel", true);
    OJBetter.monaco.setting.submitButtonPosition = OJB_getGMValue("submitButtonPosition", "bottom");
    OJBetter.monaco.setting.autoMemoryCode = OJB_getGMValue("autoMemoryCode", true);
    //自定义补全
    OJBetter.monaco.complet.customConfig = OJB_getGMValue("Complet_config", {
        "choice": -1,
        "configurations": []
    });
    // monaco
    OJBetter.monaco.lsp.enabled = OJB_getGMValue("useLSP", false);
    OJBetter.monaco.setting.position = OJB_getGMValue("monacoEditor_position", "initial");
    OJBetter.monaco.lsp.workUri = OJB_getGMValue("OJBetter_Bridge_WorkUri", "C:/OJBetter_Bridge");
    OJBetter.monaco.lsp.socketUrl = OJB_getGMValue("OJBetter_Bridge_SocketUrl", "ws://127.0.0.1:2323/");
    OJBetter.preference.showLoading = OJB_getGMValue("showLoading", true);
    OJBetter.preference.hoverTargetAreaDisplay = OJB_getGMValue("hoverTargetAreaDisplay", false);
    OJBetter.basic.expandFoldingblocks = OJB_getGMValue("expandFoldingblocks", true);
    OJBetter.preference.iconButtonSize = OJB_getGMValue("iconButtonSize", "16");
    OJBetter.preference.judgeStatusReplaceText = OJB_getGMValue("judgeStatusReplaceText", "");
    OJBetter.dev.isRuleMarkingEnabled = OJB_getGMValue("isRuleMarkingEnabled", false);
    OJBetter.about.updateChannel = OJB_getGMValue("updateChannel", "release");
    OJBetter.about.updateSource = OJB_getGMValue("updateSource", "greasyfork");
}

/**
 * 显示警告消息
 */
function showWarnMessage() {
    if (OJBetter.typeOfPage.is_oldLatex) {
        const loadingMessage = new LoadingMessage();
        loadingMessage.updateStatus(`${OJBetter.state.name} —— ${i18next.t('warning.is_oldLatex', { ns: 'alert' })}`, 'warning');
    }
    if (OJBetter.typeOfPage.is_acmsguru) {
        const loadingMessage = new LoadingMessage();
        loadingMessage.updateStatus(`${OJBetter.state.name} —— ${i18next.t('warning.is_acmsguru', { ns: 'alert' })}`, 'warning');
    }
    if (OJBetter.translation.comment.transMode == "1") {
        const loadingMessage = new LoadingMessage();
        loadingMessage.updateStatus(`${OJBetter.state.name} —— ${i18next.t('warning.trans_segment', { ns: 'alert' })}`, 'warning');
    }
    if (OJBetter.translation.comment.transMode == "2") {
        const loadingMessage = new LoadingMessage();
        loadingMessage.updateStatus(`${OJBetter.state.name} —— ${i18next.t('warning.trans_select', { ns: 'alert' })}`, 'warning');
    }
    if (OJBetter.typeOfPage.is_submitPage && OJBetter.monaco.enableOnProblemPage) {
        const loadingMessage = new LoadingMessage();
        loadingMessage.updateStatus(`${OJBetter.state.name} —— ${i18next.t('warning.is_submitPage', { ns: 'alert' })}`, 'warning');
    }
    if (OJBetter.translation.forceTurndownConversion) {
        const loadingMessage = new LoadingMessage();
        loadingMessage.updateStatus(`${OJBetter.state.name} —— ${i18next.t('warning.forceTurndownConversion', { ns: 'alert' })}`, 'warning');
    }
}

// 常量
const helpCircleHTML = '<div class="help-icon"><svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M512 64a448 448 0 1 1 0 896 448 448 0 0 1 0-896zm23.744 191.488c-52.096 0-92.928 14.784-123.2 44.352-30.976 29.568-45.76 70.4-45.76 122.496h80.256c0-29.568 5.632-52.8 17.6-68.992 13.376-19.712 35.2-28.864 66.176-28.864 23.936 0 42.944 6.336 56.32 19.712 12.672 13.376 19.712 31.68 19.712 54.912 0 17.6-6.336 34.496-19.008 49.984l-8.448 9.856c-45.76 40.832-73.216 70.4-82.368 89.408-9.856 19.008-14.08 42.24-14.08 68.992v9.856h80.96v-9.856c0-16.896 3.52-31.68 10.56-45.76 6.336-12.672 15.488-24.64 28.16-35.2 33.792-29.568 54.208-48.576 60.544-55.616 16.896-22.528 26.048-51.392 26.048-86.592 0-42.944-14.08-76.736-42.24-101.376-28.16-25.344-65.472-37.312-111.232-37.312zm-12.672 406.208a54.272 54.272 0 0 0-38.72 14.784 49.408 49.408 0 0 0-15.488 38.016c0 15.488 4.928 28.16 15.488 38.016A54.848 54.848 0 0 0 523.072 768c15.488 0 28.16-4.928 38.72-14.784a51.52 51.52 0 0 0 16.192-38.72 51.968 51.968 0 0 0-15.488-38.016 55.936 55.936 0 0 0-39.424-14.784z"></path></svg></div>';
const closeIcon = `<svg t="1696693011050" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4322" width="14" height="14"><path d="M0 0h1024v1024H0z" fill-opacity="0" p-id="4323"></path><path d="M240.448 168l2.346667 2.154667 289.92 289.941333 279.253333-279.253333a42.666667 42.666667 0 0 1 62.506667 58.026666l-2.133334 2.346667-279.296 279.210667 279.274667 279.253333a42.666667 42.666667 0 0 1-58.005333 62.528l-2.346667-2.176-279.253333-279.253333-289.92 289.962666a42.666667 42.666667 0 0 1-62.506667-58.005333l2.154667-2.346667 289.941333-289.962666-289.92-289.92a42.666667 42.666667 0 0 1 57.984-62.506667z" p-id="4324"></path></svg>`;
const translateIcon = `<svg t="1696837407077" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6325" width="22" height="22"><path d="M536.380952 121.904762a73.142857 73.142857 0 0 1 73.142858 73.142857v219.428571h219.428571a73.142857 73.142857 0 0 1 73.142857 73.142858v341.333333a73.142857 73.142857 0 0 1-73.142857 73.142857H487.619048a73.142857 73.142857 0 0 1-73.142858-73.142857v-219.428571H195.047619a73.142857 73.142857 0 0 1-73.142857-73.142858V195.047619a73.142857 73.142857 0 0 1 73.142857-73.142857h341.333333zM243.809524 682.666667v97.523809h97.523809v73.142857h-97.523809a73.142857 73.142857 0 0 1-73.142857-73.142857v-97.523809h73.142857z m585.142857-195.047619h-219.428571v48.761904a73.142857 73.142857 0 0 1-73.142858 73.142858h-48.761904v219.428571h341.333333V487.619048z m-115.760762 89.526857L787.21219 780.190476h-62.025142l-14.043429-42.715428h-76.068571L620.739048 780.190476h-60.854858l74.605715-203.044571h78.701714z m-38.034286 50.029714h-3.510857l-21.065143 63.488h45.348572l-20.772572-63.488zM536.380952 195.047619H195.047619v341.333333h341.333333V195.047619z
m-195.072 49.883429l44.78781 1.072762v37.278476h87.698286v145.359238h-87.698286v65.974857h-44.78781v-65.974857h-87.698285v-145.359238h87.698285v-38.351238z m0 83.139047h-44.787809v56.05181h44.787809v-56.05181z m89.307429 0h-44.519619v56.05181h44.519619v-56.05181zM780.190476 170.666667a73.142857 73.142857 0 0 1 73.142857 73.142857v97.523809h-73.142857v-97.523809h-97.523809V170.666667h97.523809z" p-id="6326"></path></svg>`;
const clistIcon = `<svg width="37.7pt" height="10pt" viewBox="0 0 181 48" version="1.1" xmlns="http://www.w3.org/2000/svg"><g id="#0057b8ff"><path fill="#0057b8" opacity="1.00" d=" M 17.36 0.00 L 18.59 0.00 C 23.84 6.49 30.28 11.92 36.01 17.98 C 34.01 19.99 32.01 21.99 30.00 23.99 C 26.02 19.97 22.02 15.98 18.02 11.99 C 14.01 15.98 10.01 19.99 6.00 23.99 C 4.16 22.04 2.30 20.05 0.00 18.61 L 0.00 17.37 C 3.44 15.11 6.00 11.84 8.96 9.03 C 11.79 6.05 15.09 3.47 17.36 0.00 Z" /></g><g id="#a0a0a0ff"><path fill="#a0a0a0" opacity="1.00" d=" M 56.76 13.74 C 61.48 4.80 76.07 3.90 81.77 12.27 C 83.09 13.94 83.44 16.10 83.91 18.12 C 81.53 18.23 79.16 18.24 76.78 18.23 C 75.81 15.72 73.99 13.31 71.14 12.95 C 67.14 12.02 63.45 15.29 62.48 18.99 C 61.30 23.27 61.71 28.68 65.34 31.70 C 67.82 34.05 72.19 33.93 74.61 31.55 C 75.97 30.18 76.35 28.23 76.96 26.48 C 79.36 26.43 81.77 26.44 84.17 26.56 C 83.79 30.09 82.43 33.49 79.89 36.02 C 74.14 41.35 64.17 40.80 58.77 35.25 C 53.52 29.56 53.18 20.38 56.76 13.74 Z" />
<path fill="#a0a0a0" opacity="1.00" d=" M 89.01 7.20 C 91.37 7.21 93.74 7.21 96.11 7.22 C 96.22 15.71 96.10 24.20 96.18 32.69 C 101.25 32.76 106.32 32.63 111.39 32.79 C 111.40 34.86 111.41 36.93 111.41 39.00 C 103.94 39.00 96.47 39.00 89.00 39.00 C 89.00 28.40 88.99 17.80 89.01 7.20 Z" /><path fill="#a0a0a0" opacity="1.00" d=" M 115.00 7.21 C 117.33 7.21 119.66 7.21 121.99 7.21 C 122.01 17.81 122.00 28.40 122.00 39.00 C 119.67 39.00 117.33 39.00 115.00 39.00 C 115.00 28.40 114.99 17.80 115.00 7.21 Z" /><path fill="#a0a0a0" opacity="1.00" d=" M 133.35 7.47 C 139.11 5.56 146.93 6.28 150.42 11.87 C 151.42 13.39 151.35 15.31 151.72 17.04 C 149.33 17.05 146.95 17.05 144.56 17.03 C 144.13 12.66 138.66 11.12 135.34 13.30 C 133.90 14.24 133.54 16.87 135.35 17.61 C 139.99 20.02 145.90 19.54 149.92 23.19 C 154.43 26.97 153.16 35.36 147.78 37.72 C 143.39 40.03 137.99 40.11 133.30 38.69 C 128.80 37.34 125.34 32.90 125.91 28.10 C 128.22 28.10 130.53 28.11 132.84 28.16 C 132.98 34.19 142.68 36.07 145.18 30.97 C 146.11 27.99 142.17 27.05 140.05 26.35 C 135.54 25.04 129.83 24.33 127.50 19.63 C 125.30 14.78 128.42 9.00 133.35 7.47 Z" />
<path fill="#a0a0a0" opacity="1.00" d=" M 153.31 7.21 C 161.99 7.21 170.67 7.21 179.34 7.21 C 179.41 9.30 179.45 11.40 179.48 13.50 C 176.35 13.50 173.22 13.50 170.09 13.50 C 170.05 21.99 170.12 30.48 170.05 38.98 C 167.61 39.00 165.18 39.00 162.74 39.00 C 162.64 30.52 162.73 22.04 162.69 13.55 C 159.57 13.49 156.44 13.49 153.32 13.50 C 153.32 11.40 153.31 9.31 153.31 7.21 Z" /></g><g id="#ffd700ff"><path fill="#ffd700" opacity="1.00" d=" M 12.02 29.98 C 14.02 27.98 16.02 25.98 18.02 23.98 C 22.01 27.99 26.03 31.97 30.00 35.99 C 34.01 31.99 38.01 27.98 42.02 23.99 C 44.02 25.98 46.02 27.98 48.01 29.98 C 42.29 36.06 35.80 41.46 30.59 48.00 L 29.39 48.00 C 24.26 41.42 17.71 36.08 12.02 29.98 Z" /></g></svg>`;

/**
 * 连接数据库
 */
async function initDB() {
    OJBetter.common.database = new Dexie('CFBetterDB');
    OJBetter.common.database.version(3).stores({
        samplesData: '&url',
        editorCode: '&url',
        translateData: '&url',
        localizeSubsData: '&lang'
    });

    // 等待数据库打开
    await OJBetter.common.database.open();
}

/**
 * 清空数据库
 */
async function clearDatabase() {
    const isConfirmed = await OJB_createDialog(
        i18next.t('isClearDatabase.title', { ns: 'dialog' }),
        i18next.t('isClearDatabase.content', { ns: 'dialog' }),
        [
            i18next.t('isClearDatabase.buttons.0', { ns: 'dialog' }),
            i18next.t('isClearDatabase.buttons.1', { ns: 'dialog' })
        ]
    );
    if (!isConfirmed) {
        try {
            // 开启一个读写事务,包含数据库中的所有表
            await OJBetter.common.database.transaction('rw', OJBetter.common.database.tables, async () => {
                // 遍历所有表
                for (const table of OJBetter.common.database.tables) {
                    // 清空当前表
                    await table.clear();
                }
            });
            console.log("All tables in the database have been cleared.");
            alert("All tables in the database have been cleared.");
        } catch (error) {
            console.error("Error clearing the database:", error);
        }
    }
}

/**
 * 导出数据库
 * @returns {Promise<string>} 数据库的JSON字符串
 */
async function exportDatabase() {
    try {
        // 创建一个存储数据的对象
        const exportData = {};
        // 获取数据库中所有表的名称
        const tableNames = OJBetter.common.database.tables.map(table => table.name);

        // 遍历每一个表,获取数据
        for (const tableName of tableNames) {
            const tableData = await OJBetter.common.database.table(tableName).toArray();
            exportData[tableName] = tableData;
        }

        // 将数据对象转换为JSON字符串
        const jsonData = JSON.stringify(exportData, null, 4);
        return jsonData;
    } catch (error) {
        console.error("Error exporting database:", error);
    }
}

/**
 * 导入数据库
 * @param {string} jsonData 数据库的JSON字符串
 */
async function importDatabase(jsonData) {
    const isConfirmed = await OJB_createDialog(
        i18next.t('isImportDatabase.title', { ns: 'dialog' }),
        i18next.t('isImportDatabase.content', { ns: 'dialog' }),
        [
            i18next.t('isImportDatabase.buttons.0', { ns: 'dialog' }),
            i18next.t('isImportDatabase.buttons.1', { ns: 'dialog' })
        ]
    );
    if (!isConfirmed) {
        try {
            // 将JSON字符串解析为对象
            const importData = JSON.parse(jsonData);

            // 开启一个事务,并清空现有数据
            await OJBetter.common.database.transaction('rw', OJBetter.common.database.tables, async () => {
                // 清空所有表的数据
                for (const tableName of OJBetter.common.database.tables.map(table => table.name)) {
                    await OJBetter.common.database.table(tableName).clear();
                }

                // 插入新数据
                for (const [tableName, rows] of Object.entries(importData)) {
                    await OJBetter.common.database.table(tableName).bulkAdd(rows);
                }
            });
            alert("Data imported successfully");
        } catch (error) {
            console.error("Error importing database:", error);
        }
    }
}

/**
 * 将数据下载为文件
 * @param {string} data 数据
 * @param {string} filename 文件名,默认为'export.json'
 * @param {string} fileType 文件MIME类型,默认为'application/json'
 * @returns {void}
 */
function downloadDataAsFile(data, filename = 'export.json', fileType = 'application/json') {
    // 创建一个blob对象,指定文件类型
    const blob = new Blob([data], { type: fileType });
    const url = URL.createObjectURL(blob);

    // 创建一个隐藏的a标签,模拟点击进行下载
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    document.body.appendChild(a);
    a.click();

    // 清理
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
}


/**
 * 从文件中读取数据
 * @param {Function} callback 回调函数
 * @returns {void}
 */
function readFileInput(callback) {
    const fileInput = document.createElement('input');
    fileInput.type = 'file';
    fileInput.accept = '.json';
    fileInput.style.display = 'none'; // 隐藏input元素

    fileInput.onchange = (e) => {
        const file = e.target.files[0];
        if (file) {
            const reader = new FileReader();
            reader.onload = (e) => {
                const fileContent = e.target.result;
                if (callback && typeof callback === 'function') {
                    callback(fileContent); // 调用回调函数,传入文件内容
                }
            };
            reader.readAsText(file);
        }
    };

    document.body.appendChild(fileInput);
    fileInput.click();
    document.body.removeChild(fileInput);
}

/**
 * 清除所有设置
 */
async function deleteAllConfigSettings() {
    const isConfirmed = await OJB_createDialog(
        i18next.t('isDeleteAllConfigSettings.title', { ns: 'dialog' }),
        i18next.t('isDeleteAllConfigSettings.content', { ns: 'dialog' }),
        [
            i18next.t('isDeleteAllConfigSettings.buttons.0', { ns: 'dialog' }),
            i18next.t('isDeleteAllConfigSettings.buttons.1', { ns: 'dialog' })
        ]
    );
    if (!isConfirmed) {
        const keys = GM_listValues();

        keys.forEach(key => {
            GM_deleteValue(key);
        });

        alert('All settings have been deleted.');
        window.location.reload();
    }
}

/**
 * 导出设置到JSON
 * @returns {string} JSON字符串
 */
function exportSettingsToJSON() {
    const keys = GM_listValues();
    let settings = {};

    keys.forEach(key => {
        settings[key] = GM_getValue(key);
    });

    return JSON.stringify(settings, null, 4);
}

/**
 * 从JSON导入设置
 * @param {string} jsonData JSON字符串
 * @returns {void}
 */
async function importSettingsFromJSON(jsonData) {
    const isConfirmed = await OJB_createDialog(
        i18next.t('isImportSettings.title', { ns: 'dialog' }),
        i18next.t('isImportSettings.content', { ns: 'dialog' }),
        [
            i18next.t('isImportSettings.buttons.0', { ns: 'dialog' }),
            i18next.t('isImportSettings.buttons.1', { ns: 'dialog' })
        ]
    );
    if (!isConfirmed) {
        let settings;
        try {
            settings = JSON.parse(jsonData);
        } catch (e) {
            console.error('JSON parsing error:', e);
            return;
        }

        Object.keys(settings).forEach(key => {
            GM_setValue(key, settings[key]);
        });

        alert('Settings imported successfully!');
        window.location.reload();
    }
}

/**
 * 加载元素本地化语言数据
 * @param {JQuery} element jQuery元素
 * @param {number} [retries=10] 重试次数
 * @param {number} [interval=50] 重试间隔
 */
function elementLocalize(element, retries = 10, interval = 50) {
    if ($.isFunction(element.localize)) {
        element.localize();
    } else if (retries > 0) {
        setTimeout(elementLocalize, interval, element, retries - 1, interval);
    } else {
        console.error('Unable to localize', element);
    }
}

// 切换系统黑暗监听
const mediaQueryList = window.matchMedia('(prefers-color-scheme: dark)');
const changeEventListeners = [];
function handleColorSchemeChange(event) {
    event.matches ? $('html').attr('data-theme', 'dark') : $('html').attr('data-theme', 'light');
    if (!event.matches) {
        var originalColor = $(this).data("original-color");
        $(this).css("background-color", originalColor);
        const intervalId = setinterval(() => {
            if (OJBetter.monaco && OJBetter.monaco.editor) {
                monaco.editor.setTheme('vs');
                clearInterval(intervalId);
            }
        }, 100);
    } else {
        const intervalId = setInterval(() => {
            if (OJBetter.monaco && OJBetter.monaco.editor) {
                monaco.editor.setTheme('vs-dark');
                clearInterval(intervalId);
            }
        }, 100);
    }
}

/**
 * 黑暗模式
 */
(function setDark() {
    /**
     * 初始化
     */
    function setDarkTheme() {
        const htmlElement = document.querySelector('html');
        if (htmlElement) {
            htmlElement.setAttribute('data-theme', 'dark');
            const intervalId = setInterval(() => {
                if (OJBetter.monaco && OJBetter.monaco.editor) {
                    monaco.editor.setTheme('vs-dark');
                    clearInterval(intervalId);
                }
            }, 100);
        } else {
            setTimeout(setDarkTheme, 100);
        }
    }
    OJBetter.basic.darkMode = OJB_getGMValue("darkMode", "follow")
    if (OJBetter.basic.darkMode == "dark") {
        setDarkTheme();
    } else if (OJBetter.basic.darkMode == "follow") {
        // 添加事件监听器
        changeEventListeners.push(handleColorSchemeChange);
        mediaQueryList.addEventListener('change', handleColorSchemeChange);

        if (window.matchMedia('(prefers-color-scheme: dark)').matches) setDarkTheme();
    }

    // 定义全局变量
    GM_addStyle(`
        /* 黑暗支持 */
        html[data-theme=dark]:root {
            color-scheme: light dark;
        }
        /* 颜色 */
        :root {
            /* 文字颜色 */
            --ojb-color-text-primary: #a0adb9; /* 主要文字颜色 */
            --ojb-color-text-secondary: #9AA4B1; /* 次要文字颜色 */
            --ojb-color-text-tertiary: #9BA5B2; /* 第三级文字颜色 */
            --ojb-color-text-success: #43A047; /* 成功状态文字颜色 */
            --ojb-color-text-highlight: #cbd6e2; /* 高亮文字颜色 */
            --ojb-color-text-disabled: #506778; /* 禁用状态文字颜色 */
            --ojb-color-text-icon-success: #2e7d32; /* 成功状态图标颜色 */
            --ojb-color-text-link: #4b8eda; /* 链接颜色 */

            /* 背景颜色 */
            --ojb-color-bg-primary: #22272e; /* 主背景颜色 */
            --ojb-color-bg-secondary: #2d333b; /* 次级背景颜色 */
            --ojb-color-bg-disabled: #24292e; /* 禁用元素背景颜色 */

            /* 边框颜色 */
            --ojb-color-border-primary: #48535F; /* 主要边框颜色 */
            --ojb-color-border-disabled: #404950; /* 禁用状态边框颜色 */
            --ojb-color-border-dashed-hover: #03A9F4; /* 虚线边框悬浮颜色 */
            --ojb-color-border-radio-checked: #326154; /* 选中的单选框边框颜色 */

            /* 阴影颜色 */
            --ojb-shadow-standard: 0px 0px 0.5px 0.5px #3A4048; /* 标准阴影 */
            --ojb-shadow-menu-modal: 0px 0px 0px 4px #2d333b; /* 菜单和模态框阴影 */

            /* 区域遮罩颜色 */
            --ojb-overlay-background: repeating-linear-gradient(135deg, #49525f6e, #49525f6e 30px, #49525f29 0px, #49525f29 55px); /* 区域遮罩背景 */

            /* 文字阴影 */
            --ojb-text-shadow-icon: 1px 1px 0px #2d333b, 1px -1px 0px #2d333b, -1px -1px 0px #2d333b, -1px 1px 0px #2d333b; /* 图标文字阴影 */
        }
        /* 边框样式 */
        :root {
            /* 边框样式 */
            --ojb-border-width: 1px; /* 边框宽度 */
            --ojb-border-style-solid: solid; /* 实线样式 */
            --ojb-border-style-dashed: dashed; /* 虚线样式 */
            --ojb-border-radius-small: 4px; /* 小圆角 */
            --ojb-border-radius-medium: 8px; /* 中圆角 */
            --ojb-border-radius-large: 12px; /* 大圆角 */

            /* 组合边框样式 */
            --ojb-border-solid-primary: var(--ojb-border-width) var(--ojb-border-style-solid) var(--ojb-color-border-primary); /* 主要实线边框 */
            --ojb-border-dashed: var(--ojb-border-width) var(--ojb-border-style-dashed) var(--ojb-color-border-primary); /* 主要虚线边框 */
            --ojb-border-dashed-hover: var(--ojb-border-width) var(--ojb-border-style-dashed) var(--ojb-color-border-dashed-hover); /* 悬浮虚线边框 */
            --ojb-border-solid-disabled: var(--ojb-border-width) var(--ojb-border-style-solid) var(--ojb-color-border-disabled); /* 禁用状态实线边框 */
        }
    `);

    // OJBetter界面样式
    GM_addStyle(`
        /* 主要文字颜色 */
        html[data-theme=dark] .alert-success, html[data-theme=dark] .alert-info, html[data-theme=dark] .alert-error,
        html[data-theme=dark] .alert-warning, html[data-theme=dark] .markItUpEditor,
        html[data-theme=dark] .translate-problem-statement, html[data-theme=dark] .OJBetter_setting_menu,
        html[data-theme=dark] .help_tip .tip_text,
        html[data-theme=dark] .OJBetter_setting_menu input, html[data-theme=dark] .OJBetter_setting_menu textarea,
        html[data-theme=dark] #OJBetter_SubmitForm input, html[data-theme=dark] #OJBetter_SubmitForm textarea, html[data-theme=dark] #OJBetter_SubmitForm select,
        html[data-theme=dark] #items-per-page, html[data-theme=dark] #pagBar,
        html[data-theme=dark] .OJBetter_setting_sidebar li a:link,
        html[data-theme=dark] .popup .content{
            color: var(--ojb-color-text-primary);
        }
        /* 次要文字颜色 */
        html[data-theme=dark] .ojb_btn:hover, html[data-theme=dark] .OJBetter_modal button, html[data-theme=dark] #OJBetter_statusBar,
        html[data-theme=dark] #RunTestButton, html[data-theme=dark] #programTypeId, html[data-theme=dark] #addCustomTest,
        html[data-theme=dark] #customTestBlock, html[data-theme=dark] .OJBetter_setting_list.alert_info{
            color: var(--ojb-color-text-secondary);
        }
        /* 文字颜色3 */
        html[data-theme=dark] .ojb_btn{
            color: var(--ojb-color-text-tertiary);
        }
        /* 文字颜色 浅绿 */
        html[data-theme=dark] #SubmitButton{
            color: var(--ojb-color-text-success);
        }
        /* 禁止文字颜色 */
        html[data-theme=dark] .ojb_btn[disabled]{
            color: var(--ojb-color-text-disabled);
        }
        /* 主要背景层次 */
        html[data-theme=dark] .OJBetter_setting_menu, html[data-theme=dark] .help_tip .tip_text, html[data-theme=dark] li#add_button:hover,
        html[data-theme=dark] .ojb_btn:hover,
        html[data-theme=dark] .OJBetter_setting_menu input, html[data-theme=dark] .OJBetter_setting_menu textarea,
        html[data-theme=dark] #OJBetter_SubmitForm input,
        html[data-theme=dark] .OJBetter_setting_menu input[type="checkbox"], html[data-theme=dark] .OJBetter_setting_menu input[type="checkbox"]:checked,
        html[data-theme=dark] #OJBetter_SubmitForm textarea, html[data-theme=dark] #OJBetter_SubmitForm select,
        html[data-theme=dark] .OJBetter_setting_sidebar li a.active, html[data-theme=dark] .OJBetter_setting_sidebar li,
        html[data-theme=dark] .OJBetter_setting_menu::-webkit-scrollbar-track, html[data-theme=dark] .OJBetter_setting_content::-webkit-scrollbar-track,
        html[data-theme=dark] .OJBetter_modal, html[data-theme=dark] .OJBetter_modal button:hover,
        html[data-theme=dark] .popup .content,
        html[data-theme=dark] .config_bar_list, html[data-theme=dark] #LSPLog,
        html[data-theme=dark] .OJBetter_setting_menu .OJBetter_checkboxs,
        html[data-theme=dark] .OJBetter_setting_menu .OJBetter_checkboxs input[type="checkbox"]::before,
        html[data-theme=dark] .OJBetter_setting_menu a, html[data-theme=dark] .OJBetter_setting_menu .OJBetter_setting_list button:hover,
        html[data-theme=dark] .OJBetter_setting_menu select{
            background-color: var(--ojb-color-bg-primary);
            background-image: none;
        }
        /* 次要背景层次 */
        html[data-theme=dark] .ojb_btn,
        html[data-theme=dark] .alert-success, html[data-theme=dark] .alert-info, html[data-theme=dark] .alert-error,
        html[data-theme=dark] .alert-warning, html[data-theme=dark] .SumoSelect>.optWrapper>.options li.opt:hover,
        html[data-theme=dark] .translate-problem-statement-panel,
        html[data-theme=dark] .translate-problem-statement,
        html[data-theme=dark] .OJBetter_setting_list,
        html[data-theme=dark] .OJBetter_setting_menu hr,
        html[data-theme=dark] .OJBetter_setting_sidebar li a,
        html[data-theme=dark] .OJBetter_setting_menu::-webkit-scrollbar-thumb, html[data-theme=dark] .OJBetter_setting_content::-webkit-scrollbar-thumb,
        html[data-theme=dark] .OJBetter_modal button, html[data-theme=dark] .test-for-popup pre,
        html[data-theme=dark] .popup .content pre, html[data-theme=dark] .popup .content pre code,
        html[data-theme=dark] ul.config_bar_ul::-webkit-scrollbar-thumb,  html[data-theme=dark] #OJBetter_statusBar,
        html[data-theme=dark] #RunTestButton, html[data-theme=dark] #programTypeId, html[data-theme=dark] .sampleDiv,
        html[data-theme=dark] #addCustomTest, html[data-theme=dark] #LSPLog li:nth-child(odd),
        html[data-theme=dark] .OJBetter_setting_menu .OJBetter_checkboxs input[type="checkbox"]:checked::before,
        html[data-theme=dark] .config::before, html[data-theme=dark] .config li.tempConfig_add_button:hover,
        html[data-theme=dark] .OJBetter_setting_menu details, html[data-theme=dark] #config_bar_menu,
        html[data-theme=dark] .OJBetter_setting_menu .OJBetter_setting_list button,
        html[data-theme=dark] .OJBetter_setting_menu .badge, html[data-theme=dark] #OJBetter_SubmitForm #SubmitButton{
            background-color: var(--ojb-color-bg-secondary);
        }
        /* 禁止背景层次 */
        html[data-theme=dark] .ojb_btn[disabled]{
            background-color: var(--ojb-color-bg-disabled);
        }
        /* 实线边框颜色-圆角 */
        html[data-theme=dark] .alert-success, html[data-theme=dark] .alert-info, html[data-theme=dark] .alert-error,
        html[data-theme=dark] .alert-warning, html[data-theme=dark] .translate-problem-statement{
            border: var(--ojb-border-solid-primary);
            border-radius: 2px;
        }
        /* 实线边框颜色-无圆角 */
        html[data-theme=dark] .ojb_btn,
        html[data-theme=dark] .OJBetter_setting_list, html[data-theme=dark] .config_bar_list,
        html[data-theme=dark] label.config_bar_ul_li_text,
        html[data-theme=dark] .OJBetter_setting_sidebar li, html[data-theme=dark] .OJBetter_setting_menu select,
        html[data-theme=dark] .translate-problem-statement-panel, html[data-theme=dark] .OJBetter_modal button, html[data-theme=dark] #OJBetter_SubmitForm select,
        html[data-theme=dark] #OJBetter_editor, html[data-theme=dark] #OJBetter_statusBar,
        html[data-theme=dark] #OJBetter_SubmitForm #RunTestButton, html[data-theme=dark] #programTypeId, html[data-theme=dark] #customTestBlock,
        html[data-theme=dark] #OJBetter_SubmitForm #addCustomTest, html[data-theme=dark] #OJBetter_SubmitForm #SubmitButton,
        html[data-theme=dark] .OJBetter_setting_menu input,
        html[data-theme=dark] .OJBetter_setting_menu input[type="checkbox"], html[data-theme=dark] .OJBetter_setting_menu input[type="checkbox"]:checked,
        html[data-theme=dark] .OJBetter_setting_menu textarea,
        html[data-theme=dark] #OJBetter_SubmitForm input, html[data-theme=dark] #OJBetter_SubmitForm textarea,
        html[data-theme=dark] #CompilerSetting select, html[data-theme=dark] #CompilerSetting textarea, html[data-theme=dark] #CompilerBox,
        html[data-theme=dark] .OJBetter_setting_menu .OJBetter_checkboxs,
        html[data-theme=dark] .help_tip .tip_text, html[data-theme=dark] .config::before,
        html[data-theme=dark] #statePanel, html[data-theme=dark] .test-case, html[data-theme=dark] .OJBetter_setting_menu .badge{
            border: var(--ojb-border-solid-primary);
        }
        html[data-theme=dark] #customTestBlock #customTests{
            border-top: var(--ojb-border-solid-primary);
        }
        html[data-theme=dark] .OJBetter_setting_sidebar {
            border-right: var(--ojb-border-solid-primary);
        }
        /* 实线边框-禁止 */
        html[data-theme=dark] .ojb_btn[disabled]{
            border: var(--ojb-border-solid-disabled);
        }
        /* 虚线边框 */
        html[data-theme=dark] li#add_button,
        html[data-theme=dark] .OJBetter_setting_menu_label_text{
            border: var(--ojb-border-dashed);
        }
        /* 虚线边框-悬浮 */
        html[data-theme=dark] li#add_button:hover{
            border: var(--ojb-border-dashed-hover);
            background-color: var(--ojb-color-bg-secondary);
            color: var(--ojb-color-border-dashed-hover);
        }
        /* 无边框 */
        html[data-theme=dark] .translate-problem-statement-panel .ojb_btn{
            border: none;
        }
        /* 区域遮罩 */
        html[data-theme=dark] .overlay::before {
            background: var(--ojb-overlay-background);
            color: var(--ojb-color-text-secondary);
            text-shadow: 0px 0px 2px #000000;
        }
        /* 阴影 */
        html[data-theme=dark] .translate-problem-statement-panel, html[data-theme=dark] .translate-problem-statement{
            box-shadow: var(--ojb-shadow-standard);
        }
        /* 图标按钮状态样式 */
        html[data-theme=dark] .ojb_btn_popover.success i:before, html[data-theme=dark] .ojb_btn_popover.success i {
            color: var(--ojb-color-text-icon-success);
        }
        html[data-theme=dark] .ojb_btn_popover i:before {
            text-shadow: var(--ojb-text-shadow-icon);
        }
        /* 其他样式 */
        html[data-theme=dark] .OJBetter_setting_menu, html[data-theme=dark] .OJBetter_modal{
            box-shadow: var(--ojb-shadow-menu-modal);
            border: 1px solid var(--ojb-color-bg-secondary);
        }
        html[data-theme=dark] input[type="radio"]:checked+.OJBetter_setting_menu_label_text {
            color: var(--ojb-color-text-primary);
            border: 1px solid var(--ojb-color-border-radio-checked);
        }
        html[data-theme=dark] .alert{
            text-shadow: none;
        }
    `);

    // 网站界面样式
    GM_addStyle(`
        /* 文字颜色1 */
        html[data-theme=dark] body, html[data-theme=dark] .title,
        html[data-theme=dark] .problem-statement, html[data-theme=dark] #pageContent,
        html[data-theme=dark] .ttypography, html[data-theme=dark] .roundbox, html[data-theme=dark] .info,
        html[data-theme=dark] .ttypography .bordertable, html[data-theme=dark] .ttypography .bordertable thead th,
        html[data-theme=dark] .ttypography h1, html[data-theme=dark] .ttypography h2, html[data-theme=dark] .ttypography h3,
        html[data-theme=dark] .ttypography h4, html[data-theme=dark] .ttypography h5, html[data-theme=dark] .ttypography h6,
        html[data-theme=dark] .datatable table, html[data-theme=dark] .problem-statement .sample-tests pre,
        html[data-theme=dark] .ace-chrome .ace_gutter,
        html[data-theme=dark] .setting-name,
        html[data-theme=dark] .user-black, html[data-theme=dark] .comments label.show-archived,
        html[data-theme=dark] .comments label.show-archived *, html[data-theme=dark] table{
            color: var(--ojb-color-text-primary) !important;
        }
        html[data-theme=dark] h1 a, html[data-theme=dark] h2 a, html[data-theme=dark] h3 a, html[data-theme=dark] h4 a{
            color: var(--ojb-color-text-secondary);
        }
        /* 文字颜色2 */
        html[data-theme=dark] .contest-state-phase, html[data-theme=dark] .legendary-user-first-letter,
        html[data-theme=dark] .lang-chooser,
        html[data-theme=dark] .second-level-menu-list li a, html[data-theme=dark] #footer,
        html[data-theme=dark] .ttypography .tt, html[data-theme=dark] select,
        html[data-theme=dark] .roundbox .caption, html[data-theme=dark] .topic .title *,
        html[data-theme=dark] .user-admin{
            color: var(--ojb-color-text-secondary) !important;
        }
        /* 文字颜色3 */
        html[data-theme=dark] #program-source-text-copy{
            color: var(--ojb-color-text-secondary);
        }
        html[data-theme=dark] input{
            color: var(--ojb-color-text-secondary) !important;
        }
        /* 文字颜色4 */
        html[data-theme=dark] .ttypography .MathJax, html[data-theme=dark] .MathJax span{
            color: var(--ojb-color-text-highlight) !important;
        }
        /* 链接颜色 */
        html[data-theme=dark] a:link {
            color: var(--ojb-color-text-link);
        }
        html[data-theme=dark] a:visited {
            color: var(--ojb-color-text-secondary);
        }
        html[data-theme=dark] .menu-box a, html[data-theme=dark] .sidebox th a{
            color: var(--ojb-color-text-secondary) !important;
        }
        /* 按钮 */
        html[data-theme=dark] .second-level-menu-list li.backLava {
            border-radius: 6px;
            overflow: hidden;
            filter: invert(1) hue-rotate(.5turn);
        }
        html[data-theme=dark] input:hover{
            background-color: var(--ojb-color-bg-primary) !important;
        }
        /* 背景层次1 */
        html[data-theme=dark] body, html[data-theme=dark] .ttypography .bordertable thead th,
        html[data-theme=dark] .datatable table, html[data-theme=dark] .datatable .dark, html[data-theme=dark] li#add_button,
        html[data-theme=dark] .problem-statement .sample-tests pre, html[data-theme=dark] .markItUpEditor,
        html[data-theme=dark] .SumoSelect>.CaptionCont, html[data-theme=dark] .SumoSelect>.optWrapper,
        html[data-theme=dark] .SumoSelect>.optWrapper.multiple>.options li.opt span i, html[data-theme=dark] .ace_scroller,
        html[data-theme=dark] textarea, html[data-theme=dark] .state, html[data-theme=dark] .ace-chrome .ace_gutter-active-line,
        html[data-theme=dark] .sidebar-menu ul li:hover, html[data-theme=dark] .sidebar-menu ul li.active,
        html[data-theme=dark] .popup .content, html[data-theme=dark] .file.input-view .text, html[data-theme=dark] .file.output-view .text,
        html[data-theme=dark] .file.answer-view .text, html[data-theme=dark] .file.checker-comment-view .text{
            background-color: var(--ojb-color-bg-primary) !important;
            background-image: none;
        }
        /* 背景层次2 */
        html[data-theme=dark] .roundbox, html[data-theme=dark] .roundbox .dark, html[data-theme=dark] .bottom-links,
        html[data-theme=dark] .spoiler-content, html[data-theme=dark] input,
        html[data-theme=dark] .problem-statement .test-example-line-even, html[data-theme=dark] .highlight-blue,
        html[data-theme=dark] .ttypography .tt, html[data-theme=dark] select,
        html[data-theme=dark] .SumoSelect>.optWrapper>.options li.opt:hover,
        html[data-theme=dark] .aceEditorTd, html[data-theme=dark] .ace-chrome .ace_gutter,
        html[data-theme=dark] .datatable,
        html[data-theme=dark] .highlighted-row td, html[data-theme=dark] .highlighted-row th,
        html[data-theme=dark] .pagination span.active, html[data-theme=dark] .test-for-popup pre,
        html[data-theme=dark] .source-popup pre{
            background-color: var(--ojb-color-bg-secondary) !important;
        }
        /* 实线边框颜色-圆角 */
        html[data-theme=dark] .roundbox, html[data-theme=dark] .roundbox .rtable td,
        html[data-theme=dark] .sidebar-menu ul li,
        html[data-theme=dark] input, html[data-theme=dark] .ttypography .tt, html[data-theme=dark] #items-per-page,
        html[data-theme=dark] .datatable td, html[data-theme=dark] .datatable th,
        html[data-theme=dark] textarea, html[data-theme=dark] .input-output-copier{
            border: var(--ojb-border-solid-primary) !important;
            border-radius: 2px;
        }
        /* 实线边框颜色-无圆角 */
         html[data-theme=dark] .problem-statement .sample-tests .input,
        html[data-theme=dark] .problem-statement .sample-tests .output, html[data-theme=dark] .pagination span.active,
        html[data-theme=dark] .test-for-popup pre{
            border: var(--ojb-border-solid-primary) !important;
        }
        html[data-theme=dark] .roundbox .titled, html[data-theme=dark] .roundbox .rtable th {
            border-bottom: var(--ojb-border-solid-primary) !important;
        }
        html[data-theme=dark] .roundbox .bottom-links, html[data-theme=dark] #footer{
            border-top: var(--ojb-border-solid-primary) !important;
        }
        html[data-theme=dark] .topic .content {
            border-left: 4px solid var(--ojb-color-border-primary) !important;
        }
        html[data-theme=dark] hr {
            border-color: var(--ojb-color-border-primary) !important;
        }
        /* 虚线边框 */
        html[data-theme=dark] .comment-table{
            border: var(--ojb-border-dashed);
        }
        /* input-output-copier特殊处理 */
        html[data-theme=dark] .html2md-panel.input-output-copier,
        html[data-theme=dark] .translateDiv.input-output-copier,
        html[data-theme=dark] #OJBetter_SubmitForm.input-output-copier{
            border: none !important;
        }
        html[data-theme=dark] .html2md-panel.input-output-copier:hover,
        html[data-theme=dark] #OJBetter_SubmitForm.input-output-copier:hover{
            background-color: #ffffff00 !important;
        }
        /* focus-visible */
        html[data-theme=dark] input:focus-visible, html[data-theme=dark] textarea, html[data-theme=dark] select{
            border-width: 1.5px !important;
            outline: none;
        }
        /* 图片-亮度 */
        html[data-theme=dark] img, html[data-theme=dark] #facebox .popup a{
            opacity: .75;
        }
        /* 反转 */
        html[data-theme=dark] .SumoSelect>.CaptionCont>label>i, html[data-theme=dark] .delete-resource-link,
        html[data-theme=dark] #program-source-text, html[data-theme=dark] .spoiler-content pre,
        html[data-theme=dark] .popup .content pre code{
            filter: invert(1) hue-rotate(.5turn);
        }
        /* 阴影 */
        html[data-theme=dark] .translate-problem-statement-panel, html[data-theme=dark] .translate-problem-statement{
            box-shadow: var(--ojb-shadow-standard);
        }
        /* 图标按钮状态样式 */
        html[data-theme=dark] .ojb_btn_popover.success i:before, html[data-theme=dark] .ojb_btn_popover.success i {
            color: var(--ojb-color-text-icon-success);
        }
        html[data-theme=dark] .ojb_btn_popover i:before {
            text-shadow: var(--ojb-text-shadow-icon);
        }
        /* 评测状态文字颜色 */
        html[data-theme=dark] .verdict-accepted, html[data-theme=dark] .verdict-accepted-challenged,
        html[data-theme=dark] .verdict-successful-challenge{
            color: #0a0 !important;
        }
        html[data-theme=dark] .verdict-failed, html[data-theme=dark] .verdict-challenged{
            color: red !important;
        }
        html[data-theme=dark] .verdict-rejected, html[data-theme=dark] .verdict-unsuccessful-challenge{
            color: #673ab7 !important;
        }
        html[data-theme=dark] .verdict-waiting {
            color: gray !important;
        }
        /* 样例hover样式 */
        html[data-theme=dark] .problem-statement .darkhighlight {
          background-color: #455a64 !important;
        }
        /* 其他样式 */
        html[data-theme=dark] .rated-user{
            display: initial;
        }
        html[data-theme=dark] .datatable .ilt, html[data-theme=dark] .datatable .irt,
        html[data-theme=dark] .datatable .ilb, html[data-theme=dark] .datatable .irb,
        html[data-theme=dark] .datatable .lt, html[data-theme=dark] .datatable .rt,
        html[data-theme=dark] .datatable .lb, html[data-theme=dark] .datatable .rb{
            background: none;
        }
        html[data-theme=dark] .problems .accepted-problem td.id{
            border-left: 6px solid #47837d !important;
        }
        html[data-theme=dark] .problems .rejected-problem td.id{
            border-left: 6px solid #ef9a9a !important;
        }
        html[data-theme=dark] .problems .accepted-problem td.act {
            background-color: #47837d !important;
            border-radius: 0px;
        }
        html[data-theme=dark] .problems .rejected-problem td.act{
            background-color: #ef9a9a !important;
            border-radius: 0px;
        }
        html[data-theme=dark] .collapsible-topic.collapsed .content .collapsible-topic-options:before{
            background-image: linear-gradient(#22272e00, #22272e);
        }
    `);
})()

/**
 * 黑暗模式额外的处理事件
 */
function darkModeStyleAdjustment() {
    $(".test-example-line").off("mouseenter mouseleave"); // 移除上面原本的事件
    // 为奇数行添加悬停效果
    $('.test-example-line-odd').hover(
        function () {
            $(this).addClass('darkhighlight');
            $(this).prevUntil(':not(.test-example-line-odd)').addClass('darkhighlight');
            $(this).nextUntil(':not(.test-example-line-odd)').addClass('darkhighlight');
        },
        function () {
            $(this).removeClass('darkhighlight');
            $(this).prevUntil(':not(.test-example-line-odd)').removeClass('darkhighlight');
            $(this).nextUntil(':not(.test-example-line-odd)').removeClass('darkhighlight');
        }
    );

    // 为偶数行添加悬停效果
    $('.test-example-line-even').hover(
        function () {
            $(this).addClass('darkhighlight');
            $(this).prevUntil(':not(.test-example-line-even)').addClass('darkhighlight');
            $(this).nextUntil(':not(.test-example-line-even)').addClass('darkhighlight');
        },
        function () {
            $(this).removeClass('darkhighlight');
            $(this).prevUntil(':not(.test-example-line-even)').removeClass('darkhighlight');
            $(this).nextUntil(':not(.test-example-line-even)').removeClass('darkhighlight');
        }
    );
}

/**
 * 初始化monaco编辑器资源
 */
async function initMonacoEditor() {
    if (OJBetter.monaco.enableOnProblemPage || OJBetter.monaco.beautifyPreBlocks) {
        try {
            // 等待Monaco Editor加载器脚本加载完成
            await OJB_LoadJS("https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/monaco-editor/0.49.0/min/vs/loader.min.js", "sha512-ZG31AN9z/CQD1YDDAK4RUAvogwbJHv6bHrumrnMLzdCrVu4HeAqrUX7Jsal/cbUwXGfaMUNmQU04tQ8XXl5Znw==");

            // 配置Monaco Editor
            require.config({
                paths: { vs: "https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/monaco-editor/0.49.0/min/vs" },
                "vs/nls": { availableLanguages: { "*": "zh-cn" } },
            });

            // 加载Monaco Editor主脚本
            require(["vs/editor/editor.main"], () => {
                OJBetter.monaco.loaderOnload = true;
            });
        } catch (error) {
            console.error("Failed to load Monaco Editor: ", error);
        }
    }
}

/**
 * 美化代码块
 */
async function beautifyPreBlocksWithMonaco() {
    // 判断monacoLoader是否加载完毕
    await OJB_waitUntilTrue(() => OJBetter.monaco.loaderOnload);

    // 用于替换 <pre> 标签为 Monaco 编辑器的函数
    function replacePreWithMonaco(preElement) {
        const pre = $(preElement);
        if (pre.hasClass('source-code-for-copy')) return; // 跳过复制块
        const code = OJB_getCodeFromPre(pre.get(0));
        if (!code) return;
        const language = OJB_codeLangDetect(code);

        // 创建一个用于 Monaco 编辑器的容器
        const container = $('<div></div>');
        const lineCount = code.split('\n').length; // 代码的行数

        // 计算容器的高度
        const calculateContainerHeight = (lineCount) => {
            const lineHeight = 20; // 每行代码的高度
            const minHeight = 100; // 最小高度
            const maxHeight = 1000; // 最大高度
            const dynamicHeight = lineCount * lineHeight;
            return Math.min(Math.max(dynamicHeight, minHeight), maxHeight) + 'px';
        };

        // 应用样式
        container.css({
            height: calculateContainerHeight(lineCount),
            width: '100%'
        });
        pre.hide();
        pre.after(container);

        // 初始化 Monaco 编辑器
        const editor = monaco.editor.create(container[0], {
            value: code,
            language: language,
            readOnly: true,
            tabSize: 4,
            theme: OJBetter.basic.darkMode == "dark" ? "vs-dark" : "vs",
            scrollbar: {
                verticalScrollbarSize: 10,
                horizontalScrollbarSize: 10,
                alwaysConsumeMouseWheel: false
            },
            automaticLayout: true,
            scrollBeyondLastLine: false
        });

        // 替换复制按钮事件
        if (OJBetter.typeOfPage.is_submission) {
            const button = $('div[title=Copy]');
            console.log(button);
            button.off('click');
            button.on('click', (event) => {
                event.stopPropagation(); // 阻止事件冒泡到 clipboard.min.js
                const text = editor.getValue(); // 获取编辑器内的内容
                GM_setClipboard(text);
                Codeforces.showMessage("The source code has been copied into the clipboard");
            });
        }
    }
    // 全局替换页面上所有的 <pre> 元素
    $('pre').each(function () {
        replacePreWithMonaco(this);
    });
    // 监听页面上的提交状态页面窗口的 <pre> 元素
    if (OJBetter.typeOfPage.is_statePage || OJBetter.typeOfPage.is_submissions) {
        OJB_observeElement({
            selector: '#facebox',
            callback: (node) => {
                // 如果 facebox 中存在 pre 元素,则替换它们
                const preElements = $(node).find('pre');
                preElements.each(function () {
                    replacePreWithMonaco(this);
                });
            }
        });
    }
}

/**
 * 隐藏题目问题标签
 */
function hiddenProblemTag() {
    document.querySelectorAll('.roundbox.sidebox.borderTopRound .tag-box').forEach(element => {
        if (!element.textContent.includes('*')) {
            element.classList.add('hover-reveal');
        }
    });
}

// 样式
GM_addStyle(`
/*动画*/
@keyframes shake {
    0% { transform: translateX(-5px); }
    100% { transform: translateX(5px); }
}
@keyframes rotate {
    from {
        transform: rotate(0deg);
    }

    to {
        transform: rotate(360deg);
    }
}
@keyframes rippleout {
    0% {
        box-shadow: 0 0 0 0 rgba(96, 98, 102, 0.2);
    }

    100% {
        box-shadow: 0 0 0 6px rgba(0, 0, 0, 0);
    }
}
@keyframes bounce-in {
	20%,40%,60%,80%,from,to {
		animation-timing-function: cubic-bezier(.215,.61,.355,1);
	}

	0% {
		opacity: 0;
		transform: scale3d(.995,.995,.995);
	}

	20% {
        opacity: 1;
		transform: scale3d(1.005,1.005,1.005);
	}

	40% {
		transform: scale3d(.998,.998,.998);
	}

	60% {
		transform: scale3d(1.002,1.002,1.002);
	}

	80% {
		transform: scale3d(.995,.995,.995);
	}

	to {
		opacity: 1;
		transform: scale3d(1,1,1);
	}
}
/*iconfont图标*/
.iconfont {
    font-family: "iconfont" !important;
    font-size: 16px;
    font-style: normal !important;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}
@font-face {
  font-family: 'iconfont';  /* Project id 4284341 */
  src: url('//aowuucdn.oss-accelerate.aliyuncs.com/iconfont/iconfont.woff2') format('woff2'),
       url('//aowuucdn.oss-accelerate.aliyuncs.com/iconfont/iconfont.woff2.ttf') format('truetype');
}
html {
    scroll-behavior: smooth;
}
:root {
    --vp-font-family-base: "Chinese Quotes", "Inter var", "Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}
span.mdViewContent {
    white-space: pre-wrap;
}

/* 特殊处理,加上input-output-copier类, 让convertStatementToText方法忽略该元素 */
.translateDiv.input-output-copier, .html2md-panel.input-output-copier, #OJBetter_SubmitForm.input-output-copier {
  font-size: initial;
  float: initial;
  color: initial;
  cursor: initial;
  border: none;
  padding: 0px;
  margin: 0px;
  line-height: initial;
  text-transform: none;
}
.translateDiv.input-output-copier:hover, .html2md-panel.input-output-copier:hover, #OJBetter_SubmitForm.input-output-copier:hover {
  background-color: #ffffff00;
}

/* dialog */
dialog {
    margin: 0px !important;
}
dialog::backdrop {
    background-color: rgba(0, 0, 0, 0.4);
}

/*题目页链接栏样式*/
#problemToolbar {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    overflow: auto;
    height: 100%;
    margin: 0.5em;
}

/*html2md面板*/
.html2md-panel {
    display: flex;
    justify-content: flex-end;
    align-items: center;
}
.html2md-panel a {
    text-decoration: none;
}
.html2md-panel > button {
    margin: 5px;
}
.html2md-panel.is_simple {
    position: absolute;
    right: 2%;
}

/*通用按钮*/
.ojb_btn {
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    background-color: #ffffff;
    color: #606266;
    width: auto;
    font-size: 13px;
    border-radius: 0.3rem;
    padding: 2px 5px;
    margin: 0px 5px;
    border: 1px solid #dcdfe6;
}
.ojb_btn[disabled] {
    cursor: not-allowed !important;
    color: rgb(168, 171, 178) !important;
    border: 1px solid #e4e7ed;
    background-color: #ffffff;
}
.ojb_btn:hover {
    color: #409eff;
    border-color: #409eff;
    background-color: #f1f8ff;
    z-index: 150;
}
.ojb_btn.primary {
    color: #ffffff;
    border: 1px solid #409eff;
    background-color: #409eff;
}
.ojb_btn.primary:hover {
    color: #ffffff;
    border: 1px solid #79bbff;
    background-color: #79bbff;
}
.ojb_btn.success {
    color: #4caf50;
    border: 1px solid #C8E6C9;
    background-color: #f0f9eb;
}
.ojb_btn.warning {
    color: #e6a23c;
    border: 1px solid #f3d19e;
    background-color: #fdf6ec;
}
.ojb_btn.error {
    color: #f56c6c;
    border: 1px solid #fab6b6;
    background-color: #fef0f0;
}
.ojb_btn.enabled {
    color: #42A5F5;
    border: 1px solid #90CAF9;
    background-color: #fafbff;
}
.ojb_btn.active {
    animation: rippleout 0.5s ease-in-out;
}
a.ojb_btn {
    text-decoration: none;
}
a.ojb_btn:link {
    color: #606266;
}
a.ojb_btn span {
    margin-left: 2px;
}
/*按钮图标和popover*/
.ojb_btn_popover {
    display: flex;
    justify-content: center;
    position: relative;
    outline: none;
    appearance: none;
}
.ojb_btn_popover:hover span {
    opacity: 1;
    visibility: visible;
}
.ojb_btn_popover i:before {
    position: absolute;
    text-shadow: 1px 1px 0px #ffffff, 1px -1px 0px #ffffff, -1px -1px 0px #ffffff, -1px 1px 0px #ffffff;
}
.ojb_btn_popover span {
    cursor: initial;
    position: absolute;
    left: 50%;
    opacity: 0;
    visibility: hidden;
    padding: 4px 8px;
    background-color: rgba(33, 33, 33, 0.8);
    color: rgba(255, 255, 255, 0.9019607843);
    font-size: 12px;
    border-radius: 6px;
    line-height: 1.6;
    text-align: left;
    white-space: nowrap;
    transition: all 0.15s ease-in-out;
    z-index: 999;
}
.ojb_btn_popover span:hover {
    opacity: 0;
    visibility: hidden;
}
.ojb_btn_popover.top:hover span {
    transform: translate(-50%, 0);
}
.ojb_btn_popover.top span {
    bottom: 100%;
    transform: translate(-50%, -20%);
    margin-bottom: 4px;
}
.ojb_btn_popover.top span:hover {
    transform: translate(-50%, -20%);
}
.ojb_btn_popover.bottom:hover span {
    transform: translate(-50%, 105%);
}
.ojb_btn_popover.bottom span {
    bottom: -2%;
    transform: translate(-50%, 100%);
    margin-top: 4px;
}
.ojb_btn_popover.bottom span:hover {
    transform: translate(-50%, 50%);
}
.ojb_btn_popover.loading i {
    color: rgba(33, 33, 33, 0.1);
}
.ojb_btn_popover.loading i:before {
    content: "\\e640";
    color: rgb(168, 171, 178);
    animation: rotate 2s cubic-bezier(0.65, 0.05, 0.36, 1) infinite;
}
.ojb_btn_popover.running i {
    color: rgba(33, 33, 33, 0.1);
}
.ojb_btn_popover.running i:before {
    content: "\\e600";
    color: rgb(168, 171, 178);
    animation: rotate 1s linear infinite;
}
.ojb_btn_popover.warning i {
    color: rgba(230, 162, 61, 0.8);
}
.ojb_btn_popover.warning i:before {
    content: "\\e68b";
    font-size: 15px;
    left: 10px;
    bottom: 0%;
    color: #ff9800;
}
.ojb_btn_popover.error i {
    color: rgba(245, 108, 108, 0.8);
}
.ojb_btn_popover.error i:before {
    content: "\\e651";
    font-size: 15px;
    left: 10px;
    bottom: 0%;
    color: #F44336;
}
.ojb_btn_popover.success i {
    color: rgba(76, 175, 80, 0.9);
}
.ojb_btn_popover.success i:before {
    content: "\\e61e";
    font-size: 15px;
    left: 10px;
    bottom: 0%;
    color: #4caf50;
}
.ojb_btn_popover.enabled i {
    color: rgba(33, 150, 243, 0.6);
}
.ojb_btn_popover.enabled i:before {
    content: "\\e6f4";
    font-size: 15px;
    left: 10px;
    bottom: 0%;
    color: #2196F3;
}
.ojb_btn_popover.redo i {
    color: rgba(33, 33, 33, 0.1);
}
.ojb_btn_popover.redo i:before {
    content: "\\e831";
    color: #616161;
}
.ojb_btn_popover.reverse i {
    transform: rotate(180deg);
}

/*translateDiv样式*/
.translateDiv .topText {
    display: flex;
    margin-left: 5px;
    color: #9e9e9e;
    font-size: 13px;
    align-items: center;
}
.translateDiv .borderlessButton{
    display: flex;
    align-items: center;
    margin: 2.5px 7px;
    fill: #9E9E9E;
}
.translateDiv .borderlessButton:hover{
    cursor: pointer;
    fill: #059669;
}
.translateDiv.bounce-in {
    animation: bounce-in 1s forwards;
}
html:not([data-theme='dark']) .translateDiv {
    box-shadow: 0px 0px 0.5px 0.5px #defdf378;
}
.translate-problem-statement {
    justify-items: start;
    letter-spacing: 1.8px;
    color: #059669;
    background-color: #f9f9fa;
    border: 1px solid #c5ebdf;
    border-radius: 0rem 0rem 0.3rem 0.3rem;
    padding: 5px;
    margin: -5px 0px 6px 0px;
    width: 100%;
    box-sizing: border-box;
    font-size: 13px;
}
.translate-problem-statement h2 {
    font-size: 1.6em;
    font-weight: 700;
}
.translate-problem-statement h3 {
    font-size: 1.3em;
    font-weight: 700;
}
.translate-problem-statement-panel{
    display: flex;
    justify-content: space-between;
    background-color: #f9f9fa;
    border: 1px solid #c5ebdf;
    border-radius: 0.3rem;
    margin: 4px 0px;
}
.translate-problem-statement-panel .ojb_btn {
    background: none;
    border: none;
    color: #9e9e9e;
}
.translate-problem-statement-panel.error, .translate-problem-statement.error {
    color: red;
    border-color: red;
}
.translate-problem-statement a, .translate-problem-statement a:link {
    color: #10b981;
    font-weight: 600;
    background: 0 0;
    text-decoration: none;
}
.translate-problem-statement ol, .translate-problem-statement ul {
    display: grid;
    margin-inline-start: 0.8em;
    margin-block-start: 0em;
    margin: 0.5em 0 0 3em;
    padding-inline-start: 0px;
}
.translate-problem-statement li {
    display: list-item;
    height: auto;
    word-wrap: break-word;
}
.translate-problem-statement ol li {
    list-style-type: auto;
}
.translate-problem-statement ul li {
    list-style-type: disc;
}
.translate-problem-statement img {
    max-width: 100.0%;
    max-height: 100.0%;
}
.ttypography .translate-problem-statement .MathJax {
    color: #059669!important;
}
.translate-problem-statement span.math {
    margin: 0px 2.5px !important;
}
.translate-problem-statement a:hover {
    background-color: #800;
    color: #fff;
    text-decoration: none;
}
.translate-problem-statement table {
    border: 1px #ccc solid !important;
    margin: 1.5em 0 !important;
    color: #059669 !important;
}
.translate-problem-statement table thead th {
    border: 1px #ccc solid !important;
    color: #059669 !important;
}
.translate-problem-statement table td {
    border-right: 1px solid #ccc;
    border-top: 1px solid #ccc;
    padding: 0.7143em 0.5em;
}
.translate-problem-statement table th {
    padding: 0.7143em 0.5em;
}
.translate-problem-statement p:not(:first-child) {
    margin: 1.5em 0 0;
}
.translate-problem-statement p {
    line-height: 20px !important;
    word-wrap: break-word;
    font-size: 13px !important
}
.problem-statement p:last-child {
    margin-bottom: 0px !important;
}

/*设置按钮*/
header .enter-or-register-box, header .languages {
    position: absolute;
    right: 170px;
}
.ojb_btn.OJBetter_setting {
    float: right;
    height: 30px;
    background: #60a5fa;
    color: white;
    margin: 10px;
    border: 1px solid #60a5fa;
}
.ojb_btn.OJBetter_setting.open {
    background-color: #e6e6e6;
    color: #727378;
    cursor: not-allowed;
}

/*设置面板*/
.OJBetter_setting_menu {
    box-shadow: 0px 0px 0px 4px #ffffff;
    position: fixed;
    top: 50%;
    left: 50%;
    width: 600px;
    min-height: 600px;
    transform: translate(-50%, -50%);
    border-radius: 6px;
    background-color: #f0f4f9;
    border-collapse: collapse;
    border: 1px solid #ffffff;
    color: #697e91;
    font-family: var(--vp-font-family-base);
    padding: 10px 20px 20px 10px;
    box-sizing: content-box;
}
.OJBetter_setting_menu h3 {
    margin-top: 10px;
    font-size: 1.4em;
    font-weight: 700;
}
.OJBetter_setting_menu h4 {
    margin: 15px 0px 10px 0px;
}
.OJBetter_setting_menu h4,.OJBetter_setting_menu h5 {
    font-weight: 600;
}
.OJBetter_setting_menu hr {
    border: none;
    height: 1px;
    background-color: #ccc;
    margin: 10px 0;
}
.OJBetter_setting_menu details {
    padding: 10px;
    margin-bottom: 5px;
    background-color: #ffffff;
    border-bottom: 1px solid #c9c6c696;
    border-radius: 8px;
}
.OJBetter_setting_menu .badge {
    border-radius: 4px;
    border: 1px solid #009688;
    color: #009688;
    background-color: #fff;
    padding: 0.5px 4px;
    margin-left: 5px;
    margin-right: auto;
    line-height: initial;
    font-weight: initial;
}
.OJBetter_setting_menu .missing {
    box-shadow: inset 0px 0px 1px 1px red;
}
/* 页面切换 */
.OJBetter_setting_menu .settings-page {
    display: none;
}
.OJBetter_setting_menu .settings-page.active {
    display: block;
}
.OJBetter_setting_container {
    display: flex;
}
.OJBetter_setting_sidebar {
    flex: 0 0 auto;
    min-width: 110px;
    padding: 6px 10px 6px 6px;
    margin: 20px 0px;
    border-right: 1px solid #d4d8e9;
}
.OJBetter_setting_content {
    flex-grow: 1;
    margin: 20px 0px 0px 12px;
    padding-right: 10px;
    max-height: 580px;
    overflow-y: auto;
    box-sizing: border-box;
}
.OJBetter_setting_sidebar h3 {
    margin-top: 0;
}
.OJBetter_setting_sidebar hr {
    margin-top: 10px;
    margin-bottom: 10px;
    border: none;
    border-top: 1px solid #DADCE0;
}
.OJBetter_setting_sidebar ul {
    list-style-type: none;
    margin: 0;
    padding: 0;
}
.OJBetter_setting_sidebar li {
    margin: 5px 0px;
    background-color: #ffffff;
    border: 1px solid #d4d8e9;
    border-radius: 4px;
    font-size: 16px;
}
.OJBetter_setting_sidebar li a {
    text-decoration: none;
    display: flex;
    width: 100%;
    font-size: 16px;
    color: gray;
    background-color: #ffffff;
    border: none;
    letter-spacing: 2px;
    padding: 7px;
    margin: 0px;
    border-radius: 4px;
    align-items: center;
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}
.OJBetter_setting_sidebar li a.active {
    background-color: #eceff1c7;
}
/* 链接样式 */
.OJBetter_setting_menu a {
    font-size: 13px;
    color: #009688 !important;
    background-color: #E0F2F1;
    border: 1px solid #009688;
    border-radius: 4px;
    padding: 0px 5px;
    margin: 0px 5px;
    text-decoration: none;
}
/* 下拉选择框 */
.OJBetter_setting_menu select {
    appearance: none;
    padding: 5px 10px;
    margin: -5px 0px;
    border-radius: 6px;
    border-style: solid;
    border: 1px solid #ced4da;
    color: #009688;
    background: #ffffff;
    font-size: 15px;
}
.OJBetter_setting_menu select:focus-visible {
    outline: none;
}
.OJBetter_setting_menu select option:disabled {
    color: #EEEEEE;
}
/* 数值输入框 */
.OJBetter_setting_menu input[type="number"] {
    width: 40px;
    color: #009688;
    font-size: 15px;
    appearance: none;
    padding: 5px 10px;
    margin: -5px 3px;
    border-radius: 6px;
    border-style: solid;
    border: 1px solid #ced4da;
}
.OJBetter_setting_menu input[type="number"]:focus-visible {
    outline: none;
}
.OJBetter_setting_menu input[type="number"]::-webkit-inner-spin-button,
.OJBetter_setting_menu input[type="number"]::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
/*设置面板-滚动条*/
.OJBetter_setting_menu::-webkit-scrollbar, .OJBetter_setting_content::-webkit-scrollbar,
.OJBetter_modal .content::-webkit-scrollbar {
    width: 5px;
    height: 7px;
    background-color: #aaa;
}
.OJBetter_setting_menu::-webkit-scrollbar-thumb, .OJBetter_setting_content::-webkit-scrollbar-thumb,
.OJBetter_modal .content::-webkit-scrollbar-thumb {
    background-clip: padding-box;
    background-color: #d7d9e4;
}
.OJBetter_setting_menu::-webkit-scrollbar-track, .OJBetter_setting_content::-webkit-scrollbar-track,
.OJBetter_modal .content::-webkit-scrollbar-track {
    background-color: #f1f1f1;
}
/*设置面板-关闭按钮*/
.OJBetter_setting_menu .tool-box {
    position: absolute;
    width: 20px;
    height: 20px;
    top: 3px;
    right: 3px;
}
.OJBetter_setting_menu .btn-close {
    width: 20px;
    height: 20px;
    border-radius: 50%;
    border: none;
    margin: 0px;
    padding: 0px;
    background-color: #ff000080;
    transition: .15s ease all;
    box-sizing: border-box;
    text-align: center;
    color: transparent;
}
.OJBetter_setting_menu .iconfont {
    font-size: 10px;
    font-weight: bolder;
}
.OJBetter_setting_menu .btn-close:hover {
    color: #ffffff;
    background-color: #ff0000cc;
    box-shadow: 0 5px 5px 0 #00000026;
}
.OJBetter_setting_menu .btn-close:active {
    color: #ffffffde;
    background-color: #ff000080;
}
/*设置面板-checkbox*/
.OJBetter_setting_menu input[type=checkbox]:focus {
    outline: 0px;
}
.OJBetter_setting_menu .OJBetter_setting_list input[type="checkbox"] {
    margin: 0px;
	appearance: none;
    -webkit-appearance: none;
	width: 40px;
	height: 20px;
	border: 1.5px solid #D7CCC8;
    padding: 0px !important;
	border-radius: 20px;
	background: #efebe978;
	position: relative;
	box-sizing: border-box;
}
.OJBetter_setting_menu .OJBetter_setting_list input[type="checkbox"]::before {
	content: "";
    width: 17px;
    height: 17px;
    background: #D7CCC8;
    border: 1.5px solid #BCAAA4;
    border-radius: 50%;
    position: absolute;
    top: 0;
    left: 0;
    transform: translate(2%, 2%);
    transition: all 0.3s ease-in-out;
    box-sizing: border-box;
}
.OJBetter_setting_menu .OJBetter_setting_list input[type="checkbox"]::after {
	content: url("data:image/svg+xml,%3Csvg xmlns='://www.w3.org/2000/svg' width='23' height='23' viewBox='0 0 23 23' fill='none'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M6.55021 5.84315L17.1568 16.4498L16.4497 17.1569L5.84311 6.55026L6.55021 5.84315Z' fill='%23EA0707' fill-opacity='0.89'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M17.1567 6.55021L6.55012 17.1568L5.84302 16.4497L16.4496 5.84311L17.1567 6.55021Z' fill='%23EA0707' fill-opacity='0.89'/%3E%3C/svg%3E");
	position: absolute;
	top: 0;
	left: 24px;
}
.OJBetter_setting_menu .OJBetter_setting_list input[type="checkbox"]:checked {
	border: 1.5px solid #C5CAE9;
	background: #E8EAF6;
}
.OJBetter_setting_menu .OJBetter_setting_list input[type="checkbox"]:checked::before {
    background: #C5CAE9;
    border: 1.5px solid #7986CB;
    transform: translate(122%, 2%);
    transition: all 0.3s ease-in-out;
}
.OJBetter_setting_menu .OJBetter_setting_list input[type="checkbox"]:checked::after {
    content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 15 13' fill='none'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M14.8185 0.114533C15.0314 0.290403 15.0614 0.605559 14.8855 0.818454L5.00187 12.5L0.113036 6.81663C-0.0618274 6.60291 -0.0303263 6.2879 0.183396 6.11304C0.397119 5.93817 0.71213 5.96967 0.886994 6.18339L5.00187 11L14.1145 0.181573C14.2904 -0.0313222 14.6056 -0.0613371 14.8185 0.114533Z' fill='%2303A9F4' fill-opacity='0.9'/%3E%3C/svg%3E");
    position: absolute;
    top: 1.5px;
    left: 4.5px;
}
.OJBetter_setting_menu .OJBetter_setting_list button {
    cursor: pointer;
    color: #7986cb;
    background-color: #e8eaf6;
    border: 1px solid #7986cb;
    border-radius: 6px;
    width: 100px;
    margin: -5px 2px;
    padding: 5px 10px;
}
.OJBetter_setting_menu .OJBetter_setting_list button:hover {
    color: #e8eaf6;
    background-color: #7986cb;
    border: 1px solid #7986cb;
}
.OJBetter_setting_menu label, #darkMode_span, #loaded_span {
    font-size: 16px;
}
.OJBetter_setting_list {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    padding: 10px;
    margin: 5px 0px;
    background-color: #ffffff;
    border: 1px solid #c9c6c642;
    border-bottom-color: #c9c6c696;
    border-radius: 8px;
    justify-content: space-between;
}
.OJBetter_setting_list.alert_danger {
    color: #F44336;
    background-color: #FFEBEE;
    border: 1px solid #F44336;
    margin: 10px 0px;
}
.OJBetter_setting_list.alert_warn {
    color: #E65100;
    background-color: #FFF3E0;
    border: 1px solid #FF9800;
    margin: 10px 0px;
}
.OJBetter_setting_list.alert_tip {
    color: #009688;
    background-color: #E0F2F1;
    border: 1px solid #009688;
    margin: 10px 0px;
}
.OJBetter_setting_list.alert_info {
    color: #ffffff;
    background-color: #009688;
    margin: 10px 0px;
    box-shadow: rgba(0, 0, 0, 0.06) 0px 2px 4px 0px inset;
}
.OJBetter_setting_list p:not(:last-child) {
    margin-bottom: 10px;
}
.OJBetter_setting_list p:not(:first-child) {
    margin-top: 10px;
}
/*设置面板-checkboxs*/
.OJBetter_setting_menu .OJBetter_checkboxs {
    flex-basis: 100%;
    display: flex;
    padding: 8px;
    margin: 10px 0px 0px 0px;
    border-bottom: 1px solid #c9c6c696;
    border-radius: 8px;
    border: 1px solid #c5cae9;
    background-color: #f0f8ff;
}
.OJBetter_setting_menu .OJBetter_checkboxs label {
    font-size: 13px;
    margin: 0px 6px 0px 3px;
}
.OJBetter_setting_menu .OJBetter_checkboxs input[type=checkbox]:checked+label{
    color: #7986cb;
}
.OJBetter_setting_menu .OJBetter_checkboxs input[type="checkbox"] {
    border: none;
    width: 16px;
    height: 16px;
}
.OJBetter_setting_menu .OJBetter_checkboxs input[type="checkbox"]::before{
    background: #ffffff;
    transform: none;
    width: 16px;
    height: 16px;
}
.OJBetter_setting_menu .OJBetter_checkboxs input[type="checkbox"]:checked {
	background: none;
    border: none;
}
.OJBetter_setting_menu .OJBetter_checkboxs input[type="checkbox"]:checked::before {
    border: 1.5px solid #95a2de;
    background: #e8eaf6;
	transform: none;
}
.OJBetter_setting_menu .OJBetter_checkboxs input[type="checkbox"]:checked::after {
    content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='9' height='9' viewBox='0 0 15 13' fill='none'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M14.8185 0.114533C15.0314 0.290403 15.0614 0.605559 14.8855 0.818454L5.00187 12.5L0.113036 6.81663C-0.0618274 6.60291 -0.0303263 6.2879 0.183396 6.11304C0.397119 5.93817 0.71213 5.96967 0.886994 6.18339L5.00187 11L14.1145 0.181573C14.2904 -0.0313222 14.6056 -0.0613371 14.8185 0.114533Z' fill='%2303A9F4' fill-opacity='0.9'/%3E%3C/svg%3E");
    top: 0px;
    left: 3.5px;
}
.OJBetter_setting_menu .OJBetter_checkboxs input[type="checkbox"]:disabled+label {
    color: #BDBDBD;
}
/*设置面板-radio*/
.OJBetter_setting_menu label {
    display: block;
    font-weight: initial;
    list-style-type: none;
    padding-inline-start: 0px;
    overflow-x: auto;
    max-width: 100%;
    margin: 3px 0px;
    overflow-x: visible;
}
.OJBetter_setting_menu_label_text {
    display: flex;
    border: 1px dashed #00aeeccc;
    height: 35px;
    width: 100%;
    color: #6e6e6e;
    font-weight: 300;
    font-size: 14px;
    letter-spacing: 2px;
    padding: 7px;
    margin-bottom: 4px;
    align-items: center;
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}
input[type="radio"]:checked+.OJBetter_setting_menu_label_text {
    background: #41e49930;
    border: 1px solid green;
    color: green;
    text-shadow: 0px 0px 0.5px green;
}
input[type="radio"]:disabled+.OJBetter_setting_menu_label_text {
    background: #fafafa00;
    border: 1px solid #e0e0e07a;
    color: #e0e0e0;
}
.OJBetter_setting_menu label input[type="radio"], .OJBetter_contextmenu label input[type="radio"]{
    appearance: none;
    list-style: none;
    padding: 0px !important;
    margin: 0px;
    clip: rect(0 0 0 0);
    -webkit-clip-path: inset(100%);
    clip-path: inset(100%);
    height: 1px;
    overflow: hidden;
    position: absolute;
    white-space: nowrap;
    width: 1px;
}
/*设置面板-文本输入框*/
.OJBetter_setting_menu input[type="text"] {
    display: block;
    height: 25px !important;
    width: 100%;
    background-color: #ffffff;
    color: #727378;
    font-size: 12px;
    border-radius: 0.3rem;
    padding: 1px 5px !important;
    box-sizing: border-box;
    margin: 5px 0px 5px 0px;
    border: 1px solid #00aeeccc;
    box-shadow: 0 0 1px #0000004d;
}
.OJBetter_setting_menu .OJBetter_setting_list input[type="text"] {
    margin-left: 5px;
}
.OJBetter_setting_menu input[type="text"]:focus-visible{
    border-style: solid;
    border-color: #3f51b5;
    outline: none;
}
.OJBetter_setting_menu_config_box {
    width: 100%;
    display: grid;
    margin-top: 5px;
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}
.OJBetter_setting_menu input::placeholder {
    color: #727378;
}
.OJBetter_setting_menu input.no_default::placeholder{
    color: #BDBDBD;
}
.OJBetter_setting_menu input.is_null::placeholder{
    color: red;
    border-width: 1.5px;
}
.OJBetter_setting_menu input.is_null{
    border-color: red;
}
.OJBetter_setting_menu textarea {
    resize: vertical;
    display: block;
    width: 100%;
    height: 60px;
    background-color: #ffffff;
    color: #727378;
    font-size: 12px;
    padding: 1px 5px !important;
    box-sizing: border-box;
    margin: 5px 0px 5px 0px;
    border: 1px solid #00aeeccc;
    box-shadow: 0 0 1px #0000004d;
}
.OJBetter_setting_menu textarea:focus-visible{
    border-style: solid;
    border-color: #3f51b5;
    outline: none;
}
.OJBetter_setting_menu textarea::placeholder{
    color: #BDBDBD;
    font-size: 14px;
}
.OJBetter_setting_menu #tempConfig_save {
    cursor: pointer;
	display: inline-flex;
    padding: 5px;
	background-color: #1aa06d;
	color: #ffffff;
	font-size: 14px;
	line-height: 1.5rem;
	font-weight: 500;
	justify-content: center;
	width: 100%;
	border-radius: 0.375rem;
	border: none;
	box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
    margin-top: 20px
}
.OJBetter_setting_menu button#debug_button.debug_button {
    width: 18%;
}
.OJBetter_setting_menu span.tip {
    color: #999;
    font-size: 12px;
    font-weight: 500;
    padding: 5px 0px;
}
/*设置面板-tip*/
.help_tip {
    margin-right: auto;
}
span.input_label {
    font-size: 14px;
}
.help_tip .tip_text {
    display: none;
    position: absolute;
    color: #697e91;
    font-weight: 400;
    font-size: 14px;
    letter-spacing: 0px;
    background-color: #ffffff;
    padding: 10px;
    margin: 5px 0px;
    border-radius: 4px;
    border: 1px solid #e4e7ed;
    box-shadow: 0px 0px 12px rgba(0, 0, 0, .12);
    z-index: 100;
}
.help_tip .tip_text p {
    margin-bottom: 5px;
}
.help_tip .tip_text:before {
    content: "";
    position: absolute;
    top: -20px;
    right: -10px;
    bottom: -10px;
    left: -10px;
    z-index: -1;
}
.help-icon {
    cursor: help;
    width: 15px;
    color: #b4b9d4;
    margin-left: 5px;
    margin-top: 3px;
}
.OJBetter_setting_menu .OJBetter_setting_menu_label_text .help_tip .help-icon {
    color: #7fbeb2;
}
.help_tip .help-icon:hover + .tip_text, .help_tip .tip_text:hover {
    display: block;
    cursor: help;
    width: 250px;
}
/* 版本信息 */
.OJBetter_setting_menu .versionInfo{
    display: grid;
    justify-items: center;
    font-size: 16px;
    padding: 10px;
}
.OJBetter_setting_menu .versionInfo>* {
    margin: 10px 0px;
}

/* 配置管理面板 */
.config{
    width: 100%;
    margin: 10px 0px;
}
.config li.tempConfig_add_button {
    cursor: pointer;
    height: 40px;
    border: 1px dashed #BDBDBD;
    border-radius: 8px;
    background-color: #fcfbfb36;
    color: #bdbdbd;
    font-size: 14px;
    align-items: center;
    justify-content: center;
}
.config li.tempConfig_add_button:hover {
    border: 1px dashed #03A9F4;
    background-color: #d7f0fb8c;
    color: #03A9F4;
}
.config .config_bar_list {
    display: flex;
    width: 100%;
    padding-bottom: 2px;
    border: 1px solid #c5cae9;
    background-color: #f0f8ff;
    box-sizing: border-box;
    border-radius: 0px 0px 8px 8px;
}
.config .config_bar_list input[type="radio"] {
    appearance: none;
    width: 0;
    height: 0;
    overflow: hidden;
}
.config .config_bar_list input[type="radio"] {
    margin: 0px;
}
.config .config_bar_list input[type=radio]:focus {
    outline: 0px;
}
.config .config_bar_ul_li_text {
    display: flex;
    align-items: center;
    justify-content: center;
    max-width: 100%;
    height: 40px;
    overflow-x: auto;
    font-size: 14px;
    font-weight: 400;
    margin: 0px 4px;
    padding: 3px;
    border: 1px solid #dedede;
    border-radius: 10px;
    box-shadow: 0px 2px 4px 0px rgba(0,0,0,.05);
    box-sizing: border-box;
}
.config .config_bar_ul li button {
    background-color: #e6e6e6;
    color: #727378;
    height: 23px;
    font-size: 14px;
    border-radius: 0.3rem;
    padding: 1px 5px;
    margin: 5px;
    border: none;
    box-shadow: 0 0 1px #0000004d;
}
.config .config_bar_ul {
    display: flex;
    align-items: center;
    list-style-type: none;
    padding-inline-start: 0px;
    overflow-x: auto;
    max-width: 100%;
    margin: 0px;
    padding: 5px;
}
.config .config_bar_ul li {
    width: 80px;
    display: grid;
    margin: 4px 4px;
    min-width: 100px;
    box-sizing: border-box;
}
.config .config_bar_ul_li_text:hover {
    background-color: #eae4dc24;
}
input[type="radio"]:checked + .config_bar_ul_li_text {
    background: #41b3e430;
    border: 1px solid #5e7ce0;
    color: #5e7ce0;
}
.config .config_bar_ul::-webkit-scrollbar {
    width: 5px;
    height: 4px;
}
.config .config_bar_ul::-webkit-scrollbar-thumb {
    background-clip: padding-box;
    background-color: #d7d9e4;
    border-radius: 8px;
}
.config .config_bar_ul::-webkit-scrollbar-button:start:decrement {
    width: 4px;
    background-color: transparent;
}
.config .config_bar_ul::-webkit-scrollbar-button:end:increment {
    width: 4px;
    background-color: transparent;
}
.config .config_bar_ul::-webkit-scrollbar-track {
    border-radius: 5px;
}
.config .config_bar_ul_li_text::-webkit-scrollbar {
    width: 5px;
    height: 7px;
    background-color: #aaa;
}
.config .config_bar_ul_li_text::-webkit-scrollbar-thumb {
    background-clip: padding-box;
    background-color: #d7d9e4;
}
.config .config_bar_ul_li_text::-webkit-scrollbar-track {
    background-color: #f1f1f1;
}
.config .config_bar_list_add_div {
    display: flex;
    height: 40px;
    margin: 4px 2px;
}

/* 修改菜单 */
#config_bar_menu {
    z-index: 400;
    position: fixed;
    width: 60px;
    background: #ffffff;
    box-shadow: 1px 1px 4px 0px #0000004d;
    border: 0px solid rgba(0,0,0,0.04);
    border-radius: 4px;
    padding: 8px 0;
}
.config_bar_menu_item {
    cursor: pointer;
    padding: 2px 6px;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 32px;
    font-size: 14px;
    font-weight: 500;
    box-shadow: inset 0px 0px 0px 0px #8bb2d9;
}
#config_bar_menu_edit:hover {
    background-color: #00aeec;
    color: white;
}
#config_bar_menu_delete:hover {
    background-color: #FF5722;
    color: white;
}

/* 配置编辑页面 */
#config_edit_menu {
    z-index: 300;
    width: 450px;
}

/* 黑暗模式选项按钮 */
.dark-mode-selection {
    display: flex;
    justify-content: center;
    align-items: center;
    max-width: 350px;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}
.dark-mode-selection label {
    margin: 8px 0px 8px 8px;
}
.dark-mode-selection > * {
    margin: 6px;
}
.dark-mode-selection .OJBetter_setting_menu_label_text {
    border-radius: 8px;
    margin-bottom: 0px;
}

/*确认弹窗*/
.OJBetter_modal {
    z-index: 600;
    display: grid;
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-size: 12px;
    font-family: var(--vp-font-family-base);
    width: max-content;
    padding: 10px 20px;
    box-shadow: 0px 0px 0px 4px #ffffff;
    border-radius: 6px;
    background-color: #f0f4f9;
    border-collapse: collapse;
    border: 1px solid #ffffff;
    color: #697e91;
}
.OJBetter_modal h2 {
    font-size: 1.6em;
    font-weight: 700;
}
.OJBetter_modal .content{
    white-space: nowrap;
    max-height: 500px;
    overflow-y: auto;
}
.OJBetter_modal .buttons{
    display: flex;
    padding-top: 15px;
}
.OJBetter_modal button {
    display: inline-flex;
    justify-content: center;
    align-items: center;
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
    text-align: center;
    box-sizing: border-box;
    outline: none;
    transition: .1s;
    user-select: none;
    vertical-align: middle;
    -webkit-appearance: none;
    height: 24px;
    padding: 5px 11px;
    margin-right: 15px;
    font-size: 12px;
    border-radius: 4px;
    color: #ffffff;
    background: #009688;
    border-color: #009688;
    border: none;
}
.OJBetter_modal button.secondary{
    background-color:#4DB6AC;
}
.OJBetter_modal button:hover{
    background-color:#4DB6AC;
}
.OJBetter_modal button.secondary:hover {
    background-color: #80CBC4;
}
.OJBetter_modal .help-icon {
    margin: 0px 8px 0px 0px;
    height: 1em;
    width: 1em;
    line-height: 1em;
    display: inline-flex;
    justify-content: center;
    align-items: center;
    position: relative;
    fill: currentColor;
    font-size: inherit;
}
.OJBetter_modal p {
    margin: 5px 0px;
}

/* 右键菜单 */
.OJBetter_contextmenu {
    z-index: 500;
    display: grid;
    position: absolute;
    background-color: #f0f4f9;
    border-collapse: collapse;
    color: #697e91;
    font-family: var(--vp-font-family-base);
    overflow: hidden;
    box-sizing: content-box;
    box-shadow: 0px 0px 0px 2px #eddbdb4d;
}
.OJBetter_contextmenu label {
    margin: 0px;
}
input[type="radio"]:checked+.OJBetter_contextmenu_label_text {
    background: #41e49930;
    border: 1px solid green;
    color: green;
    font-weight: 500;
}
.OJBetter_contextmenu_label_text {
    display: flex;
    border: 1px dashed #80cbc4;
    height: 26px;
    width: 100%;
    color: gray;
    font-size: 13px;
    font-weight: initial;
    padding: 4px;
    align-items: center;
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}
.OJBetter_contextmenu_label_text:hover {
    color: #F44336;
    border: 1px dashed #009688;
    background-color: #ffebcd;
}

/* RatingByClist */
.ratingBadge, html[data-theme=dark] button.ratingBadge{
    display: block;
    font-weight: 700;
    font-size: 11px;
    margin-top: 5px;
    padding: 2px;
    border-radius: 4px;
    color: #B0BEC5;
    border: 1px solid #cccccc66;
}

/* 多选翻译 */
.block_selected{
    box-shadow: 0px 0px 0px 1px #FF9800;
    outline: none;
}

/* 悬浮菜单 */
.OJBetter_MiniTranslateButton {
    z-index: 100;
    display: grid;
    position: absolute;
    border-collapse: collapse;
    fill: #F57C00;
    background-color: #FFF3E0;
    overflow: hidden;
    box-sizing: content-box;
    box-shadow: 0px 0px 0px 2px #FFE0B2;
    border-radius: 100%;
}
.OJBetter_MiniTranslateButton:hover {
    cursor: pointer;
    box-shadow: 0px 0px 0px 2px #FFB74D;
}

/* acmsguru划分块 */
.OJBetter_acmsguru {
    margin: 0 0 1em!important;
}

/* 代码提交表单 */
#OJBetter_SubmitForm.input-output-copier:hover {
    background-color: #ffffff00;
}
#OJBetter_SubmitForm input[type="number"] {
    width: 40px;
    color: #009688;
    appearance: none;
    border-radius: 6px;
    border-style: solid;
    border: none;
    background-color: #ffffff00;
}
#OJBetter_SubmitForm :focus-visible {
    outline: none;
}
#OJBetter_SubmitForm .topDiv {
    height: 50px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 0px;
    box-sizing: border-box;
}
#OJBetter_SubmitForm .topDiv .topRightDiv {
    height: 100%;
    display: flex;
    flex-wrap: wrap;
    gap: 0px;
}
#OJBetter_SubmitForm input[type="checkbox"], #OJBetter_SubmitForm label {
    margin: 0px;
    font-weight: initial;
}
#OJBetter_SubmitForm #fontSizeInput {
    border: none;
    background-color: #ffffff00;
}

/* 顶部区域 */
#OJBetter_SubmitForm .topRightDiv>* {
    height: 100%;
    box-sizing: border-box;
}
#OJBetter_SubmitForm .topRightDiv>button{
    padding: 0px 8px;
}
#OJBetter_SubmitForm .topRightDiv {
    display: flex;
    flex-wrap: wrap;
    gap: 0px;
    align-items: center;
}
#OJBetter_SubmitForm input[type="checkbox"], #OJBetter_SubmitForm label {
    margin: 0px;
    font-weight: initial;
}

/* LSP连接Log */
#LSPLog{
    width: 500px;
    height: 500px;
    position: fixed;
    top: 50%;
    left: 50%;
    padding: 10px;
    transform: translate(-50%, -50%);
    border: 1px solid;
    z-index: 200;
    background-color: #ffffff;
}
#LSPLog button{
    position: fixed;
    top: 10px;
    right: 10px;
    z-index: 200;
}
#LSPLog #LSPLogList{
    width: 500px;
    height: 500px;
    overflow: auto;
    color: #424242;
}
#LSPLog li:nth-child(odd){
    background-color: #f5f5f5;
}
#LSPLog details{
    padding: 2px;
}

/* 代码编辑器 */
#OJBetter_editor{
    box-sizing: border-box;
    height: 600px;
    border: 1px solid #d3d3d3;
    width: 100%;
    resize: vertical;
    display: flex;
    flex-direction: column;
}
#OJBetter_editor.fullscreen{
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100vh;
    z-index: 2000;
}
#OJBetter_editor.bottom{
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 50vh;
    z-index: 2000;
}
.ojb_btn.exit_button_bottom {
    position: fixed;
    bottom: 30px;
    right: 15px;
    z-index: 2000;
    height: 28px;
}

/* monaco */
#OJBetter_monaco {
    flex: 1;
    min-height: 0;
    width: 100%;
}
#OJBetter_monaco .highlight {
    border: 1px solid #ffffff00;
    background-color: #ffffff00!important
}
.monaco-hover hr {
    margin: 4px -8px 4px !important;
}

/* 状态底栏 */
#OJBetter_statusBar{
    height: 22px;
    font-size: 12px;
    color: #757575;
    border: 1px solid #d3d3d3;
    background-color: #f8f8f8;
    padding: 3px;
    box-sizing: border-box;
}

/* 提交 */
#OJBetter_submitDiv{
    display: flex;
    padding-top: 15px;
    height: 50px;
    box-sizing: border-box;
}
#OJBetter_submitDiv >* {
    border-radius: 6px;
}
#OJBetter_submitDiv > button {
    height: 100%;
    aspect-ratio: 1 / 1;
}
#SubmitButton {
    color: #fff;
    background-color: #209978;
    border-color: #17795E;
}
#SubmitButton:hover {
    background-color: #17795e;
}
#SubmitButton.disabled {
    background-color: red;
    animation: shake 0.07s infinite alternate;
}
#programTypeId{
    height: 100%;
    padding: 5px 10px;
    border-radius: 6px;
    border-style: solid;
    border: 1px solid #ced4da;
    color: #212529;
}

/* 调试 */
.OJBetter_loding{
    padding: 6px 0px 0px 5px;
    height: 22px;
}
#CompilerArgsInput{
    flex-grow: 1;
    width: 100%;
    height: 100%;
    margin-bottom: 10px;
    padding: 5px 10px;
    border-radius: 6px;
    box-sizing: border-box;
    border: 1px solid #ccc;
    box-shadow: inset 0px 1px 1px rgba(0,0,0,.075);
}
#CompilerArgsInput[disabled] {
    cursor: not-allowed;
}
#CompilerSetting{
    font-size: 14px;
    margin-top: 10px;
    display: none;
}
#CompilerSetting select, #CompilerSetting textarea{
    padding: 4px 10px;
    border-radius: 6px;
    border-style: solid;
    border: 1px solid #ced4da;
    color: #212529;
}
#CompilerBox{
    display: grid;
    margin-top: 10px;
    border: #d0d7de solid 1px;
    border-radius: 6px;
}
#CompilerBox > * {
    margin: 5px;
}

/* 自定义样例 */
#customTestBlock {
    margin-top: 10px;
    font-size: 14px;
    color: #616161;
    border: 1px solid #d3d3d3;
    box-sizing: border-box;
    position: relative;
}
#customTestBlock #customTests{
    border-top: 1px solid #d3d3d3;
    margin: 0px 0px 40px 0px;
}
#customTestBlock summary {
    cursor: pointer;
    padding: 10px;
}
#customTestBlock textarea {
    resize: vertical;
}
.sampleDiv {
    color: #727378;
    background-color: #FAFAFA;
    padding: 5px;
    margin-bottom: 10px;
    box-shadow: inset 0 0 1px #0000004d;
    position: relative;
}
.dynamicTextarea {
    width: 98%;
    height: 120px;
    margin: 10px 5px;
    border: 1px solid #E0E0E0;
}
.deleteCustomTest {
    cursor: pointer;
    position: absolute;
    top: 5px;
    right: 5px;
    display: flex;
    fill: #9E9E9E;
    padding: 2px 2px;
    border-radius: 4px;
    border: 1px solid #ffffff00;
    background-color: #ffffff00;
    align-items: center;
}
.deleteCustomTest:hover {
    fill: #EF5350;
    border: 1px solid #ef9a9a;
    background-color: #FFEBEE;
}
#addCustomTest {
    cursor: pointer;
    position: absolute;
    bottom: 5px;
    right: 5px;
    padding: 3px 10px;
    color: #795548;
    border: 1px solid #ccc;
    border-radius: 4px;
    background-color: #FAFAFA;
}
#addCustomTest:hover {
    background-color: #f5f5f5;
}

/* 调试结果 */
#statePanel{
    display: none;
    padding: 5px;
    margin-top: 10px;
    border: 1px solid #ddd;
    border-radius: 4px;
}
.test-case {
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 4px;
}
.test-case:not(:first-child){
    margin-top: 5px;
}
.test-case > * {
    margin: 5px 0px;
}
.test-case > :first-child {
    margin-top: 0px;
}
.test-case > :last-child {
    margin-bottom: 0px;
}
.test-case-title, .test-case-status {
    font-size: 16px;
    display: inline;
}
.test-case-status{
    margin-left: 5px;
}
.test-case-status.error{
    color: red;
}
.test-case-status.success{
    color: #449d44;
}
.test-case-judge {
    font-size: 13px;
}

/* 差异对比 */
.output_diff {
    color: #5d4037;
    margin: 5px 0px;
    display: grid;
    border: 1px solid #bcaaa4;
    font-size: 13px;
    font-family: Consolas, "Lucida Console", "Andale Mono", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace;
    overflow: auto;
}
.output_diff .added {
    background-color: #c8f7c5;
    user-select: none;
}
.output_diff .removed {
    background-color: #f7c5c5;
}
.output_diff .diffLine {
    display: flex;
}
.output_diff .diffLine:nth-child(odd) {
    background-color: #f5f5f5;
}
.lineNo {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 17px;
    color: #BDBDBD;
    font-size: 10px;
    border-right: 1px solid;
    user-select: none;
}
.lineContent {
    display: grid;
    width: 100%;
}
.lineContent>span {
    height: 16px;
    padding-left: 3px;
}
.output_no_diff {
    padding: 5px;
    border: 1px solid #ddd;
}
.diff_note {
    font-size: 10px;
}

/* 网站本地化替换规则标记 */
.markingTextReplaceRule{
    color: #FFF3E0;
    background-color: #FF9800;
}

/* SelectPage样式 */
.sp_input {
    padding: 4px 6px px !important;
    height: 20px !important;
    min-height: 20px !important;
    line-height: 20px !important;
}
div.sp_clear_btn {
    padding: 0px !important;
}

/* 题目问题标签hover隐藏 */
.hover-reveal {
    opacity: 0;
    transition: opacity 0.5s;
}
.hover-reveal:hover {
    opacity: 1;
}


/* 移动设备 */
@media (max-device-width: 450px) {
    .ojb_btn{
        height: 2em;
        font-size: 1.2em;
    }
    .ojb_btn.OJBetter_setting{
        height: 2.5em;
        font-size: 1em;
    }
    .OJBetter_setting_menu{
        width: 90%;
    }
    .OJBetter_setting_menu label, #darkMode_span, #loaded_span, .OJBetter_setting_menu_label_text,
    .OJBetter_setting_sidebar li{
        font-size: 1em;
    }
    .translate-problem-statement{
        font-size: 1.2em;
    }
    .OJBetter_modal{
        font-size: 1.5em;
    }
    .OJBetter_setting_list, .translate-problem-statement{
        padding: 0.5em;
    }
    .OJBetter_setting_menu_label_text{
        height: 2.5em;
        padding: 0.5em;
    }
    #pagBar #jump-input, #pagBar #items-per-page, .OJBetter_modal button{
        height: 2.5em;
        font-size: 1em;
    }
    .translate-problem-statement p, .translate-problem-statement ul li{
        line-height: 1.5em !important;
    }
    .OJBetter_contextmenu_label_text{
        height: 3em;
        font-size: 1em;
    }
}

/* 覆盖网站原本的样式 */
#footer > div:nth-child(7) {
    left: 0px !important;
}
`);

/**
 * 添加一些依赖库和条件加载的css样式
 */
function addDependencyStyles() {
    GM_addStyle(GM_getResourceText("xtermcss"));
    GM_addStyle(GM_getResourceText("selectpagecss"));
    GM_addStyle(GM_getResourceText("dialogpolyfillcss"));
    // 自定义图标大小
    GM_addStyle(`
        .iconfont {
            font-size: ${OJBetter.preference.iconButtonSize}px;
        }
    `);
}

/**
 * 添加包含i18n内容的css样式
 */
function addI18nStyles() {
    GM_addStyle(`
    /* 加载鼠标悬浮覆盖层css */
    .overlay::before {
        content: '';
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: repeating-linear-gradient(135deg, rgb(77 208 225 / 30%), rgb(77 208 225 / 30%) 30px, rgb(77 208 225 / 10%) 0px, rgb(77 208 225 / 10%) 55px);
        z-index: 100;
    }
    .overlay::after {
        content: '${i18next.t('targetArea', { ns: 'common' })}';
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        color: #00695C;
        font-size: 16px;
        font-weight: bold;
        z-index: 100;
    }

    .config::before {
        content: "${i18next.t('common.configManageTitle', { ns: 'settings' })}";
        display: block;
        height: 20px;
        background-color: #f0f8ff;
        border: 1px solid #c5cae9;
        border-bottom: 0px;
        line-height: 20px;
        padding: 2px 10px;
        border-radius: 8px 8px 0px 0px;
        box-sizing: content-box;
    }
    .config.missing::before {
        content: "${i18next.t('common.missing.radio', { ns: 'settings' })}";
        background-color: #fef0f0;
        color: #f56c6c;
        border: 1px solid #fab6b6;
    }
    `);
}

// ------------------------------
// 一些工具类
// ------------------------------

/**
 * 自定义错误类,以区分不同的错误类型
 */
class OJB_GMError extends Error {
    constructor(type, message, originalError) {
        super(message);
        this.name = 'GMError';
        this.type = type;
        this.stack = originalError.stack;
        Object.assign(this, originalError);
    }
}

/**
 * 文本块替换/恢复类
 */
class TextBlockReplacer {
    constructor() {
        /** @type {string[]} 匹配项 */
        this.matches = [];
        /** @type {Map<string, string>} 待还原项 */
        this.replacements = new Map();
        /** @type {Map<string, string>} 暂时未找到的待还原项 */
        this.tempReplacements = new Map();
        /** @type {string} 替换符号 */
        this.replaceSymbol = OJBetter.translation.replaceSymbol;
    }

    /**
     * 替换文本
     * @param {string} text 原文本
     * @param {RegExp} regex 匹配规则
     * @returns {string} 替换后的文本
     */
    replace(text, regex) {
        this.matches = text.match(regex) || [];
        try {
            for (let i = 0; i < this.matches.length; i++) {
                const match = this.matches[i];
                const id = OJB_getRandomNumber(8);
                let replacement = '';
                switch (this.replaceSymbol) {
                    case "1":
                        replacement = `【${id}】`;
                        break;
                    case "2":
                        replacement = `{${id}}`;
                        break;
                    case "3":
                        replacement = `[${id}]`;
                        break;
                    default:
                        replacement = `【${id}】`;
                        break;
                }
                text = text.replace(match, replacement);
                this.replacements.set(id, match);
            }
        } catch (e) { }
        return text;
    }


    /**
     * 恢复替换的文本
     * @param {string} text 还原前的文本
     * @returns {string} 还原后的文本
     */
    recover(text) {
        let textCopy = text;

        /**
         * 替换回文本
         * @param {string} replacement 替换的文本
         * @param {string} regexPattern 匹配规则
         * @returns {void}
         */
        const replaceText = (replacement, regexPattern) => {
            const latexMatch = '(?<latex_block>\\$\\$(\\\\\\$|[^\\$])*?\\$\\$)|(?<latex_inline>\\$(\\\\\\$|[^\\$])*?\\$)|';
            const regex = new RegExp(latexMatch + regexPattern, 'g');
            textCopy = textCopy.replace(regex, (match, ...args) => {
                // LaTeX中的不替换
                const groups = args[args.length - 1]; // groups是replace方法的最后一个参数
                if (groups.latex_block || groups.latex_inline) return match;
                // 没有空格则加一个
                const offset = args[args.length - 3]; // offset是replace方法的倒数第三个参数
                let leftSpace = "", rightSpace = "";
                if (!/\s/.test(textCopy[offset - 1])) leftSpace = " ";
                if (!/\s/.test(textCopy[offset + match.length])) rightSpace = " ";
                return leftSpace + replacement + rightSpace;
            });
        };

        /**
         * 尝试还原
         * @param {string} replacement 替换的文本
         * @param {string} id 替换的 id
         * @returns {boolean} 是否替换成功
         */
        const tryRecover = (replacement, id) => {
            // 尝试还原,如果还原成功,则从 replacements 中删除
            const originalText = textCopy;
            replaceText(replacement, `【\\s*${id}\\s*】|\\[\\s*${id}\\s*\\]|{\\s*${id}\\s*}`); // 替换符完整匹配(考虑了多出空格的情况)
            replaceText(replacement, `【\\s*${id}(?![】\\d])|(?<![【\\d])${id}\\s*】|\\[\\s*${id}(?![\\]\\d])|(?<![\\[\\d])${id}\\s*\\]|{\\s*${id}(?![}\\d])|(?<![{\\d])${id}\\s*}`); // 替换符部分匹配

            if (textCopy === originalText) {
                // 如果文本没有变化,说明没有找到,加入到 tempReplacements
                this.tempReplacements.set(id, replacement);
                return false;
            } else {
                // 如果文本变化了,说明找到并成功替换,则删除
                this.replacements.delete(id);
                this.tempReplacements.delete(id);
                return true;
            }
        }

        // 处理 replacements 中的项
        this.replacements.forEach((replacement, id) => {
            tryRecover(replacement, id);
        });

        // 处理 tempReplacements 中的项
        while (this.tempReplacements.size > 0) {
            let found = false;
            this.tempReplacements.forEach((replacement, id) => {
                found = tryRecover(replacement, id) || found;
            });
            if (!found) break; // 如果这一轮没有找到任何项,终止循环
        }

        // 如果 tempReplacements 还有未找到的项
        if (this.tempReplacements.size > 0) {
            console.warn("There are still some replacements not found:", this.tempReplacements);
        }

        return textCopy;
    }
}

// ------------------------------
// 一些工具函数
// ------------------------------

/**
 * 格式化链接格式
 * @param {string} url 链接字符串
 * @returns {string} 清理后的链接字符串
 */
function OJB_cleanLink(url) {
    if (url === null || url === undefined) return "";

    // 替换'http://'为'https://'
    let cleanUrl = url.replace(/^http:\/\//i, 'https://');

    // 移除末尾的斜杠
    cleanUrl = cleanUrl.replace(/\/$/, '');

    return cleanUrl;
}

/**
 * 深度比较两个对象或数组是否完全相等。
 * @param {any} a - 第一个比较对象。
 * @param {any} b - 第二个比较对象。
 * @returns {boolean} - 如果两个对象或数组深度相等,则返回true,否则返回false。
 */
function OJB_deepEquals(a, b) {
    if (a === b) return true;
    if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) return false;
    const keysA = Object.keys(a);
    const keysB = Object.keys(b);
    if (keysA.length !== keysB.length) return false;
    for (let key of keysA) {
        if (!b.hasOwnProperty(key)) return false;
        if (!OJB_deepEquals(a[key], b[key])) return false;
    }
    return true;
}

/**
 * 用于封装需要重试的异步函数
 * @param {Function} task 需要封装的异步函数
 * @param {Object} options 配置项
 * @param {Number} options.maxRetries 重试次数,默认为 5
 * @param {Number} options.retryInterval 重试时间间隔,默认为 0 毫秒
 * @param {Function} options.errorHandler 错误处理函数,默认为抛出错误
 * @param {...any} args task 函数的参数
 * @returns {Promise} 返回 Promise
 */
async function OJB_promiseRetryWrapper(task, {
    maxRetries = 5,
    retryInterval = 0,
    errorHandler = (err) => { throw err }
} = {}, ...args) {
    let attemptsLeft = maxRetries;
    while (attemptsLeft--) {
        try {
            return await task(...args);
        } catch (err) {
            if (attemptsLeft <= 0) {
                return errorHandler(err, maxRetries, attemptsLeft);
            }
            if (retryInterval > 0) {
                await OJB_delay(retryInterval);
            }
        }
    }
}

/**
 * GM_xmlhttpRequest 的 Promise 封装
 * @param {Object} options GM_xmlhttpRequest 的参数
 * @param {Boolean} isStream 是否为流式请求
 * @returns {Promise<OJB_GMError>} 返回 Promise
 */
function OJB_GMRequest(options, isStream = false) {
    return new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
            ...options,
            ...(isStream ? {
                onloadstart: resolve
            } : {
                onload: resolve
            }),
            onerror: (error) => reject(new OJB_GMError('error', 'An error occurred during the request.', error)),
            ontimeout: (error) => reject(new OJB_GMError('timeout', 'The request timed out.', error)),
            onabort: (error) => reject(new OJB_GMError('abort', 'The request was aborted.', error)),
        });
    });
}

/**
 * 获取cookie
 * @param {string} name cookie名称
 * @returns {string} cookie值
 */
function OJB_getCookie(name) {
    const cookies = document.cookie.split(";");
    for (let i = 0; i < cookies.length; i++) {
        const cookie = cookies[i].trim();
        const [cookieName, cookieValue] = cookie.split("=");

        if (cookieName === name) {
            return decodeURIComponent(cookieValue);
        }
    }
    return "";
}

/**
 * 检查是否仍在同一浏览器会话中
 * @param {string} sessionKey - 会话键名,用于标识会话
 * @returns {boolean} - 如果在当前会话中之前已经设置过这个键,则返回true,否则返回false
 */
function OJB_isSameBrowserSession(sessionKey) {
    const fullCookieName = `OJB_Session_${sessionKey}`;
    const sessionValue = OJB_getCookie(fullCookieName);
    if (sessionValue === "") {
        document.cookie = `${fullCookieName}=true; path=/`;
        return false;
    }
    return true;
}

/**
 * 随机数生成
 * @param {number} numDigits 位数
 * @returns {number} 一个随机数
 */
function OJB_getRandomNumber(numDigits) {
    let min = Math.pow(10, numDigits - 1);
    let max = Math.pow(10, numDigits) - 1;
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

/**
 * 随机数生成-范围内
 * @param {number} min 最小值
 * @param {number} max 最大值
 * @returns {number} 一个随机数
 */
function OJB_getRandomNumberInRange(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

/**
 * 防抖函数
 * @param {Function} callback 回调函数
 * @returns {Function}
 */
function OJB_debounce(callback) {
    let timer;
    let immediateExecuted = false;
    const delay = 500;
    return function () {
        clearTimeout(timer);
        if (!immediateExecuted) { callback.call(this); immediateExecuted = true; }
        timer = setTimeout(() => { immediateExecuted = false; }, delay);
    };
}

/**
 * 为元素添加鼠标拖拽支持
 * @param {JQuery<HTMLElement>} element 要添加拖拽支持的元素
 * @returns {void}
 */
function OJB_addDraggable(element) {
    let isDragging = false;
    let x, y, l, t, nl, nt;
    let isSpecialMouseDown = false; // 选取某些元素时不拖动

    element.on('mousedown', function (e) {
        isSpecialMouseDown = $(e.target).is('label, p, input, textarea, span, select, details, summary');
        if (isSpecialMouseDown) return;

        isDragging = true;
        x = e.clientX;
        y = e.clientY;
        l = element.offset().left - $(window).scrollLeft();
        t = element.offset().top - $(window).scrollTop();

        element.css({ left: l + 'px', top: t + 'px', transform: 'none' });

        $(document).on("mousemove", drag);
        $(document).on("mouseup", stopDrag);
        element.css('cursor', 'all-scroll');
    });

    const drag = (e) => {
        if (!isDragging) return;
        // 不执行拖动操作
        if ($(e.target).is('label, p, input, textarea, span') || isSpecialMouseDown && !$(e.target).is('input, textarea')) return;
        e.preventDefault();

        const nx = e.clientX;
        const ny = e.clientY;
        nl = nx - (x - l);
        nt = ny - (y - t);
        element.css({ transform: `translate(${nx - x}px, ${ny - y}px)` });
    };

    const stopDrag = () => {
        isDragging = false;
        isSpecialMouseDown = false;
        element.css('cursor', 'default');

        // 在停止拖拽后,设置元素的left和top,并还原transform
        element.css({ left: nl + 'px', top: nt + 'px', transform: 'none' });
        $(document).off("mousemove", drag);
        $(document).off("mouseup", stopDrag);
    };
}

/**
 * 切换元素的折叠/展开过渡动画
 * @param {HTMLElement} element
 */
function OJB_toggleCollapseExpand(element) {
    // 设置transitionend事件监听器的函数
    const setTransitionListener = (listener) => {
        const listenerName = `transitionEndListener${Date.now()}`;
        window[listenerName] = listener;
        element.addEventListener('transitionend', listener);
        element.setAttribute('data-transition-end-listener', listenerName);
    };

    // 移除事件监听器的函数
    const removeTransitionListener = () => {
        const transitionEndListenerName = element.getAttribute('data-transition-end-listener');
        if (transitionEndListenerName) {
            element.removeEventListener('transitionend', window[transitionEndListenerName]);
            element.removeAttribute('data-transition-end-listener');
        }
    };

    const collapsed = element.getAttribute('data-collapsed') === 'true';
    const sectionHeight = element.scrollHeight;

    // 移除事件监听器
    removeTransitionListener();

    // 设置初始样式
    element.style.overflow = 'hidden';
    element.style.transition = 'height 0.3s ease-out 0s';
    element.style.height = collapsed ? `0px` : `${sectionHeight}px`;
    element.style.opacity = collapsed ? '' : '1';

    // 需要立即开始动画
    requestAnimationFrame(() => {
        // 设置结束样式
        element.style.height = collapsed ? `${sectionHeight}px` : `0px`;
    });

    const transitionEndListener = (event) => {
        if (event.propertyName === 'height') {
            if (collapsed) {
                // 展开后的设置
                element.style.height = '';
                element.style.overflow = '';
            } else {
                // 折叠后的设置
                element.style.opacity = '0';
            }
            removeTransitionListener();
        }
    };

    setTransitionListener(transitionEndListener);

    // 更新data-collapsed属性
    element.setAttribute('data-collapsed', collapsed ? 'false' : 'true');
}

/**
 * 获取外部JSON并转换为Object
 * @param {string} url JSON Url
 * @param {boolean} [nacache=true] 是否不使用缓存
 * @returns {Promise<Object>} JSON Object
 */
async function OJB_getExternalJSON(url, nacache = true) {
    const response = await OJB_GMRequest({
        method: "GET",
        url: url,
        nocache: nacache
    });
    try {
        return JSON.parse(response.responseText);
    } catch (e) {
        throw new Error(`JSON parse error\n${e}`);
    }
}

/**
 * 创建确认对话框dialog
 * @param {string} title 标题
 * @param {string} content 内容
 * @param {string[]} buttons 按钮 (取消 确定) 可以为null
 * @param {boolean} renderMarkdown 是否使用markdown渲染文本
 * @returns {Promise<boolean>} 用户点击了确定按钮返回true, 否则返回false
 */
function OJB_createDialog(title, content, buttons, renderMarkdown = false) {
    return new Promise(resolve => {
        let contentHtml = content;

        if (renderMarkdown) {
            const md = window.markdownit();
            contentHtml = md.render(content);
        }

        const dialog = OJB_safeCreateJQElement(`
        <dialog class="OJBetter_modal">
            <h2>${title}</h2>
            <div class="content">${contentHtml}</div>
        </dialog>
        `);
        const buttonbox = OJB_safeCreateJQElement(`<div class="buttons"></div>`);
        const cancelButton = OJB_safeCreateJQElement(`<button class="cancelButton">${buttons[0]}</button>`)
            .addClass("secondary");
        const continueButton = OJB_safeCreateJQElement(`<button class="continueButton">${buttons[1]}</button>`);
        if (buttons[0] !== null) buttonbox.append(cancelButton);
        if (buttons[1] !== null) buttonbox.append(continueButton);
        dialog.append(buttonbox);
        $('body').before(dialog);

        OJB_showModal(dialog);
        OJB_addDraggable(dialog);

        continueButton.click(function () {
            OJB_closeAndRemoveModal(dialog);
            resolve(true);
        });

        cancelButton.click(function () {
            OJB_closeAndRemoveModal(dialog);
            resolve(false);
        });
    });
}

/**
 * 显示模态对话框并阻止页面滚动,同时考虑滚动条宽度变化和原始marginRight
 * @param {JQuery<HTMLElement>} element
 */
function OJB_showModal(element) {
    const dialog = element.get(0);
    dialogPolyfill.registerDialog(dialog);
    dialog.showModal();
    OJBetter.state.openDialogCount++;

    if (OJBetter.state.openDialogCount === 1) {
        const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
        // 获取原始的html marginRight,考虑到可能的非数字值,比如auto
        const originalMarginRight = window.getComputedStyle(document.documentElement).marginRight;
        const marginRightValue = parseFloat(originalMarginRight) || 0; // 将非数字值转换为0

        if (scrollbarWidth > 0) {
            // 保存原始的marginRight,并设置新的值以补偿滚动条宽度
            document.documentElement.style.setProperty('--original-margin-right', originalMarginRight);
            document.documentElement.style.marginRight = `${marginRightValue + scrollbarWidth}px`;
        }

        // 保存原始的overflow样式
        document.documentElement.setAttribute('data-original-overflow', document.documentElement.style.overflow);
        document.documentElement.style.overflow = 'hidden';
    }

    const allowScrollIfNeeded = () => {
        OJBetter.state.openDialogCount--;
        if (OJBetter.state.openDialogCount === 0) {
            // 恢复原始的html marginRight和overflow样式
            const originalMarginRight = document.documentElement.style.getPropertyValue('--original-margin-right');
            document.documentElement.style.marginRight = originalMarginRight;
            document.documentElement.style.removeProperty('--original-margin-right');

            const originalOverflow = document.documentElement.getAttribute('data-original-overflow');
            document.documentElement.style.overflow = originalOverflow;
            document.documentElement.removeAttribute('data-original-overflow');
        }
    };

    dialog.addEventListener('close', allowScrollIfNeeded);
}

/**
 * 关闭并移除模态对话框
 * @param {JQuery<HTMLElement>} element
 */
function OJB_closeAndRemoveModal(element) {
    const dialog = element.get(0);
    dialog.close();
    dialog.remove();
}

/**
 * 关闭并移除模态对话框
 * @param {JQuery<HTMLElement>} element
 */
function OJB_closeModal(element) {
    const dialog = element.get(0);
    dialog.close();
}

/**
 * 清除i18next的缓存数据并刷新
 */
function clearI18nextCache() {
    Object.keys(localStorage)
        .filter(key => key.startsWith('i18next_res_'))
        .forEach(key => localStorage.removeItem(key));
    window.location.reload();
}

/**
 * 清除网站本地化数据
 */
async function clearWebsiteL10nData() {
    OJBetter.common.database.localizeSubsData.clear().then(() => {
        console.log('localizeSubsData table has been cleared');
        window.location.reload();
    }).catch((error) => {
        console.error('Failed to clear localizeSubsData table:', error);
    });
}

/**
 * 从Pre代码块中获取原始代码
 * @param {HTMLElement} element pre代码块元素
 * @returns {string|null} 代码文本
 */
function OJB_getCodeFromPre(element) {
    /**
     * 从Ace格式化的代码块中获取原始代码
     * @param {HTMLElement} element pre代码块元素
     * @returns {string} 代码文本
     */
    const getCodeFromAcePre = function (element) {
        const editor = ace.edit(element);
        return editor.getValue();
    }

    /**
     * 从Pretty格式化的代码块中获取原始代码-1
     * 代码直接存放在 pre 元素中
     * @param {HTMLElement} element pre代码块元素
     * @returns {string} 代码文本
     */
    const getCodeFromPrettyPre = function (element) {
        return Array.from(element.querySelectorAll('li')).map(function (li) {
            return li.textContent;
        }).join('\n');
    }

    /**
     * 从Pretty格式化的代码块中获取原始代码-2
     * 代码存放在子元素 code 中
     * @param {HTMLElement} element pre代码块元素
     * @returns {string} 代码文本
     */
    const getCodeFromPreChild = function (element) {
        const code = element.querySelector("code.prettyprint");
        if (code.classList.contains("linenums")) {
            return getCodeFromPrettyPre(element);
        } else {
            return element.querySelector("code.prettyprint").textContent;
        }
    }

    let result;
    if (element.id === "submission-code") {
        result = getCodeFromAcePre(element);
    } else if (element.classList.contains("prettyprint")) {
        result = getCodeFromPrettyPre(element);
    } else if (element.querySelector("code.prettyprint")) {
        result = getCodeFromPreChild(element);
    } else {
        result = "";
    }
    result = result.replace(/\u00A0/g, ''); // 过滤文本中的U+00a0字符(由&nbsp;造成的)
    return result;
}

/**
 * 判断代码的语言
 * @param {string} code 代码文本
 * @returns {string} 可能的语言
 */
function OJB_codeLangDetect(code) {
    result = hljs.highlightAuto(code);
    return result.language;
}

/**
 * 获取指定命名空间下的所有i18n翻译键值对。
 *
 * @param {string} namespace - 要获取键值对的i18next命名空间。
 * @returns {Map<string, string>} 一个包含命名空间下所有键值对的Map对象。
 */
function OJB_getAllI18nKeysForNamespace(namespace) {
    const language = i18next.language; // 获取当前语言
    const resources = i18next.store.data[language]; // 获取当前语言的所有资源
    const nsResources = resources[namespace]; // 获取特定命名空间的资源
    const resultMap = new Map();

    if (nsResources) {
        // 遍历命名空间下的所有键值对,并添加到Map中
        Object.keys(nsResources).forEach(key => {
            resultMap.set(key, nsResources[key]);
        });
    } else {
        console.log(`No resources found for namespace "${namespace}"`);
    }

    return resultMap;
}

/**
 * 更新检查
 */
async function checkScriptVersion() {
    try {
        const versionResponse = await OJB_GMRequest({
            method: "GET",
            url: "https://aowuucdn.oss-accelerate.aliyuncs.com/script/versions.json",
            timeout: 10 * 1e3,
            nocache: true
        });
        const versionData = JSON.parse(versionResponse.responseText);
        const { [OJBetter.state.formatName]: { dev: version_dev, release: version_release } } = versionData;
        const baseUrls = {
            greasyfork: 'https://update.greasyfork.org/scripts/465777/Codeforces%20Better%21.user.js',
            github: `https://github.com/beijixiaohu/OJBetter/raw/main/script/${OJBetter.about.updateChannel}/${OJBetter.state.formatName}.user.js`,
            aliyunoss: `https://aowuucdn.oss-accelerate.aliyuncs.com/script/${OJBetter.about.updateChannel}/${OJBetter.state.formatName}.user.js`
        };
        /** @type {string} 更新跳转url */
        const updateUrl = baseUrls[OJBetter.about.updateSource];
        /** @type {string} 是否暂时跳过cookie */
        const skipUpdate = OJB_getCookie("skipUpdate");
        /** @type {string} 当前更新频道的最新版本 */
        const version = OJBetter.about.updateChannel == "release" ? version_release : version_dev;
        if (OJB_compareVersions(version, OJBetter.state.version) === 1 && skipUpdate !== "true") {
            const updateConfirmed = await OJB_createDialog(
                i18next.t('update.title', { ns: 'dialog', scriptName: OJBetter.state.name }),
                i18next.t('update.content', { ns: 'dialog', oldVersion: OJBetter.state.version, newVersion: version }),
                [
                    i18next.t('update.buttons.0', { ns: 'dialog' }),
                    i18next.t('update.buttons.1', { ns: 'dialog' })
                ],
                true
            );

            if (updateConfirmed) {
                window.location.href = updateUrl;
            } else {
                document.cookie = "skipUpdate=true; path=/";
            }
        }
    } catch (error) {
        console.error("Update check failed: ", error);
    }
}

/**
 * 公告
 */
async function showAnnounce() {
    /** @type {string} 最新公告版本*/
    const lastAnnounceVer = i18next.t('lastVersion', { ns: 'announce' });
    if (OJB_compareVersions(OJBetter.state.version, OJBetter.state.lastAnnounceVer) === 1) {
        const title = `🎉${i18next.t('announce.title', { ns: 'dialog' })} ${OJBetter.state.version}`;
        /** @type {Boolean} 是否是新的公告 */
        const isNewAnnounceVer = OJB_compareVersions(lastAnnounceVer, OJBetter.state.lastReadAnnounceVer) === 1;
        /** @type {Boolean} 是否展示新的公告(高于当前版本的测试公告不展示) */
        const showNewAnnounceVer = OJB_compareVersions(lastAnnounceVer, OJBetter.state.version) !== 1;
        /**
         * 获取公告的内容
         * @returns {string} 公告内容
         */
        const getAnnounceContent = function () {
            // 获取公告
            const announceMap = OJB_getAllI18nKeysForNamespace('announce');
            // 移除 'lastVersion' 键
            announceMap.delete('lastVersion');
            // 将 Map 转换为数组并根据版本号排序
            const sortedVersions = [...announceMap.keys()].sort(OJB_compareVersions).reverse();
            let content = "";
            sortedVersions.forEach(version => {
                content += `### ${version}\n\n`; // 使用版本号作为标题
                content += announceMap.get(version); // 添加对应版本的公告内容
                content += "\n\n";
            });

            return content;
        };

        const content = (() => {
            if (isNewAnnounceVer && showNewAnnounceVer) {
                return `${i18next.t('announce.prefix', { ns: 'dialog' })}\n\n${getAnnounceContent()}`;
            } else {
                return i18next.t('announce.divContent', { ns: 'dialog' });
            }
        })();
        const ok = await OJB_createDialog(
            title,
            content,
            [
                null,
                i18next.t('announce.buttons.0', { ns: 'dialog' })
            ],
            true
        ); //跳过折叠块确认
        if (ok) {
            if (isNewAnnounceVer && showNewAnnounceVer) {
                GM_setValue('lastReadAnnounceVer', lastAnnounceVer);
            }
            GM_setValue('lastAnnounceVer', OJBetter.state.version);
        }
    }
};

/**
 * 页面顶部提示信息alert类
 */
class LoadingMessage {
    constructor() {
        this._statusElement = null;
        this._isDisplayed = false;
        this.init();
    }

    /**
     * 初始化加载提示信息
     */
    init() {
        this._statusElement = this.createStatusElement();
        this.insertStatusElement();
    }

    /**
     * 创建提示信息元素
     */
    createStatusElement() {
        const statusElement = $("<div></div>").addClass("alert OJBetter_alert")
            .css({
                "margin": "1em",
                "text-align": "center",
                "position": "relative"
            }).hide();
        return statusElement;
    }

    /**
     * 插入提示信息
     * @returns {void}
     */
    insertStatusElement() {
        (OJBetter.typeOfPage.is_mSite ? $("header") : $(".menu-box:first").next()).after(this._statusElement);
    }

    /**
     * 显示提示信息
     */
    showStatus() {
        this._statusElement.show();
        this._isDisplayed = true;
    }

    /**
     * 隐藏提示信息
     */
    hideStatus() {
        this._statusElement.fadeOut(500);
        this._isDisplayed = false;
    }

    /**
     * 移除提示信息
     */
    removeStatus() {
        this._statusElement.remove();
        this._isDisplayed = false;
    }

    /**
     * 更新提示信息
     * @param {string} text 提示信息文本
     * @param {string} type 提示信息类型,可选值:info, success, warning, error
     * @param {number} timeout 提示信息显示的持续时间(毫秒), 默认为无限长
     */
    updateStatus(text, type = 'info', timeout = Infinity, isMarkdown = false) {
        if (isMarkdown) {
            let md = window.markdownit({
                html: !is_escapeHTML,
            });
            text = md.render(text);
        }
        this._statusElement.html(text).removeClass("alert-info alert-success alert-warning alert-error").addClass(`alert-${type}`);
        if (!this._isDisplayed) {
            this.showStatus();
        }
        if (timeout !== Infinity) {
            setTimeout(() => {
                this.hideStatus();
            }, timeout);
        }
    }
}

/**
 * 获取网站本地化的数据
 * @param {*} localizationLanguage 本地化语言
 * @returns {Promise<Object>} 本地化数据
 */
async function getLocalizeWebsiteJson(localizationLanguage) {
    let data = await OJBetter.common.database.localizeSubsData.get(localizationLanguage);
    let url = localizationLanguage === "zh" ?
        `https://aowuucdn.oss-accelerate.aliyuncs.com/resources/subs/${OJBetter.state.formatName}.json` :
        `https://aowuucdn.oss-accelerate.aliyuncs.com/i18n/${localizationLanguage}/resources/subs/${OJBetter.state.formatName}.json`;
    if (data) data = data.data;
    if (!data) {
        // 如果本地没有数据,从远端获取并保存
        data = await OJB_getExternalJSON(url);
        await OJBetter.common.database.localizeSubsData.put({ lang: localizationLanguage, data: data });
    } else {
        // 如果本地有数据,检查是否已经在当前会话中尝试过更新
        const sessionKey = `ojb_updateL10nWebsiteJson_${localizationLanguage}`;
        if (!OJB_isSameBrowserSession(sessionKey)) {
            // 如果尚未更新,则在后台更新
            (async () => {
                try {
                    const newData = await OJB_getExternalJSON(url);
                    await OJBetter.common.database.localizeSubsData.put({ lang: localizationLanguage, data: newData });
                    console.log("Website local data has been refreshed!");
                } catch (error) {
                    console.error('Failed to update localization data:', error);
                }
            })();
        }
    }
    return data;
}

/**
 * 网站本地化替换
 * @returns
 */
async function localizeWebsite() {
    if (OJBetter.localization.websiteLang === "initial") return;

    // 设置网页语言
    var htmlTag = document.getElementsByTagName("html")[0];
    htmlTag.setAttribute("lang", OJBetter.localization.websiteLang);

    // 获取网站本地化的数据
    var subs = await getLocalizeWebsiteJson(OJBetter.localization.websiteLang);

    /**
     * 文本节点遍历替换
     * @param {JQuery} $nodes jQuery对象
     * @param {Object} textReplaceRules 文本替换规则对象
     * @param {string} key 应用的规则集的名字
     */
    const traverseTextNodes = ($nodes, textReplaceRules, key) => {
        if (!$nodes) return;

        $nodes.each((_, node) => {
            if (node.nodeType === Node.TEXT_NODE) {
                Object.entries(textReplaceRules).forEach(([match, replace]) => {
                    try {
                        const regex = new RegExp(match, 'g');
                        const beforeText = node.textContent;
                        node.textContent = node.textContent.replace(regex, replace);
                        if (node.textContent !== beforeText && OJBetter.dev.isRuleMarkingEnabled) {
                            $(node).after(`<span class="markingTextReplaceRule">${key}</span>`);
                        }
                    } catch (error) {
                        console.error(`Error processing text replacement for match: ${match}`, error);
                    }
                });
            } else if (node.nodeType === Node.ELEMENT_NODE && node.tagName.toLowerCase() !== 'iframe') {
                $(node).contents().each((_, childNode) => {
                    traverseTextNodes($(childNode), textReplaceRules, key);
                });
            }
        });
    };

    /**
     * value替换
     * @param {JQuery} $nodes jQuery对象
     * @param {Object} valueReplaceRules 值替换规则对象
     * @param {string} key 应用的规则集的名字
     */
    function traverseValueNodes($nodes, valueReplaceRules, key) {
        if (!$nodes) return;

        $nodes.each(function () {
            let $node = $(this);
            if ($node.is('[value]')) {
                Object.keys(valueReplaceRules).forEach(match => {
                    const replace = valueReplaceRules[match];
                    const regex = new RegExp(match, 'g');
                    let currentValue = $node.val();
                    let newValue = currentValue.replace(regex, replace);
                    $node.val(newValue);
                    if (OJBetter.dev.isRuleMarkingEnabled) {
                        if (newValue !== currentValue) $($node).after(`<span class="markingTextReplaceRule">${key}</span>`);
                    }
                });
            } else {
                $node.children().each(function () {
                    traverseValueNodes($(this), valueReplaceRules, key);
                });
            }
        });
    }

    /**
     * 严格的文本节点遍历替换
     * 要求被替换文本严格与规则文本一致
     * @param {JQuery} $nodes jQuery对象
     * @param {Object} textReplaceRules 文本替换规则对象
     * @param {string} key 应用的规则集的名字
     */
    const strictTraverseTextNodes = ($nodes, textReplaceRules, key) => {
        if (!$nodes) return;

        $nodes.each((_, node) => {
            if (node.nodeType === Node.TEXT_NODE) {
                const trimmedNodeText = node.textContent.trim();
                for (const [match, replacement] of Object.entries(textReplaceRules)) {
                    if (trimmedNodeText === match) {
                        const beforeText = node.textContent;
                        node.textContent = replacement;
                        if (node.textContent !== beforeText && OJBetter.dev.isRuleMarkingEnabled) {
                            $(node).after(`<span class="markingTextReplaceRule">${key}</span>`);
                        }
                    }
                }
            } else if (node.nodeType === Node.ELEMENT_NODE && node.tagName.toLowerCase() !== 'iframe') {
                $(node).contents().each((_, childNode) => {
                    strictTraverseTextNodes($(childNode), textReplaceRules, key);
                });
            }
        });
    };

    /**
     * 应用文本替换
     */
    let commonReplacements = subs.commonReplacements;
    Object.entries(commonReplacements).forEach(([key, value]) => {
        const classSelectors = Array.isArray(value.class) ? value.class : [value.class]; // 兼容,class的值可以为数组或者字符串
        classSelectors.forEach(classSelector => {
            if (value.isStrict) {
                strictTraverseTextNodes(OJB_safeCreateJQElement(`${classSelector}`), value.rules, key);
            } else {
                traverseTextNodes(OJB_safeCreateJQElement(`${classSelector}`), value.rules, key);
            }
        });
    });

    /**
     * 应用value替换
     */
    let InputValueReplacements = subs.InputValueReplacements;
    Object.entries(InputValueReplacements).forEach(([key, value]) => {
        const classSelectors = Array.isArray(value.class) ? value.class : [value.class];
        classSelectors.forEach(classSelector => {
            traverseValueNodes(OJB_safeCreateJQElement(`${classSelector}`), value.rules, key);
        });
    });

    /**
     * 动态添加的文本的替换
     */
    let dynamicReplacements = subs.dynamicReplacements;
    Object.entries(dynamicReplacements).forEach(([key, value]) => {
        const classSelectors = Array.isArray(value.class) ? value.class : [value.class]; // 兼容,class的值可以为数组或者字符串
        classSelectors.forEach(classSelector => {
            OJB_observeElement({
                selector: classSelector,
                callback: (node) => {
                    // let popupContent = node.textContent.replace(/^×/, ''); // 去除开头多余的 '×' 字符
                    if (value.isStrict) {
                        strictTraverseTextNodes(OJB_safeCreateJQElement(`${classSelector}`), value.rules, key);
                    } else {
                        traverseTextNodes(OJB_safeCreateJQElement(`${classSelector}`), value.rules, key);
                    }
                }
            });
        });
    });

    // 杂项
    (function () {
        // 选项汉化input[type="radio"]
        var translations = {
            "as individual participant": "个人",
            "as a team member": "作为一个团队成员",
        };
        $('input[type="radio"]').each(function () {
            var tag = $(this).parent().contents().filter(function () {
                return this.nodeType === Node.TEXT_NODE;
            });
            for (var i = 0; i < tag.length; i++) {
                var text = tag[i].textContent.trim();
                if (translations.hasOwnProperty(text)) {
                    $(this).addClass(text);
                    tag[i].replaceWith(translations[text]);
                    break;
                }
            }
        });
    })();
    (function () {
        var translations = {
            "(standard input\/output)": "标准输入/输出",
        };
        $("div.notice").each(function () {
            var tag = $(this).children().eq(0).text();
            for (var property in translations) {
                if (tag.match(property)) {
                    $(this).children().eq(0).text(translations[property]);
                    break;
                }
            }
        });
    })();

    // 轻量站特殊
    if (OJBetter.typeOfPage.is_mSite) {
        traverseTextNodes($('nav'), commonReplacements['.second-level-menu']['rules']);
    }
    if (OJBetter.typeOfPage.is_mSite) {
        (function () {
            var translations = {
                "Announcements": "公告",
                "Submissions": "提交记录",
                "Contests": "比赛",
            };
            $(".caption").each(function () {
                var optionValue = $(this).text();
                if (translations[optionValue]) {
                    $(this).text(translations[optionValue]);
                }
            });
        })();
    }
};

/**
 * i18next初始化
 */
async function initI18next() {
    return new Promise((resolve, reject) => {
        i18next
            .use(i18nextChainedBackend)
            .init({
                lng: OJBetter.localization.scriptLang,
                ns: ['common', 'settings', 'config', 'dialog', 'alert', 'translator',
                    'button', 'codeEditor', 'comments', 'announce', 'logMessage'], // 命名空间列表
                defaultNS: 'settings',
                fallbackLng: ['zh', OJBetter.translation.targetLang],
                load: 'currentOnly',
                debug: false,
                backend: {
                    backends: [
                        i18nextLocalStorageBackend,
                        i18nextHttpBackend
                    ],
                    backendOptions: [{
                        prefix: 'i18next_res_',
                        expirationTime: 7 * 24 * 60 * 60 * 1000,
                        defaultVersion: `v${OJBetter.state.version}`,
                        store: typeof window !== 'undefined' ? window.localStorage : null
                    }, {
                        /* options for secondary backend */
                        loadPath: (lng, ns) => {
                            if (lng[0] === 'zh' || lng[0] === 'zh-Hans') {
                                return `https://aowuucdn.oss-accelerate.aliyuncs.com/resources/locales/${OJBetter.state.formatName}/${ns}.json`;
                            }
                            return `https://aowuucdn.oss-accelerate.aliyuncs.com/i18n/${lng}/resources/locales/${OJBetter.state.formatName}/${ns}.json`;
                        }
                    }]
                }
            }, (err, t) => {
                if (err) {
                    reject(err);
                } else {
                    jqueryI18next.init(i18next, $, {
                        useOptionsAttr: true
                    });
                    resolve(t);
                }
            });
    });
};

/**
 * 抽象命令类
 */
class Command {
    execute() { }
    undo() { }
}

/**
 * 命令调用者
 */
class CommandInvoker {
    constructor() {
        this.history = [];
    }

    /**
     * 执行命令
     * @param {Command} command 命令对象
     */
    execute(command) {
        this.history.push(command);
        command.execute();
    }

    /**
     * 撤销命令
     */
    undo() {
        const command = this.history.pop();
        if (command) {
            command.undo();
        }
    }
}

/**
 * 接收者
 */
class DOMContainer {
    /**
     * @param {JQueryObject} element 容器对象
     */
    constructor(element) {
        this.containerElement = element;
    }

    /**
     * 添加元素
     * @param {JQueryObject} element 元素对象
     * @returns {JQueryObject} 添加的元素对象
     */
    add(element) {
        this.containerElement.append(element);
        return this.containerElement.children().last();
    }

    /**
     * 删除元素
     * @param {JQueryObject} element 元素对象
     */
    remove(element) {
        $(element).remove();
    }
}

/**
 * 具体命令类:添加元素
 */
class AddElementCommand extends Command {
    /**
     * @param {DOMContainer} receiver 接收者
     * @param {JQueryObject} element 元素对象
     */
    constructor(receiver, element) {
        super();
        this.receiver = receiver;
        this.element = element;
        this.addedElement = null;
    }

    execute() {
        this.addedElement = this.receiver.add(this.element);
    }

    undo() {
        if (this.addedElement) {
            this.receiver.remove(this.addedElement);
        }
    }
}

/**
 * 具体命令类:删除元素
 */
class RemoveElementCommand extends Command {
    /**
     * @param {DOMContainer} receiver 接收者
     * @param {JQueryObject} element 元素对象
     */
    constructor(receiver, element) {
        super();
        this.receiver = receiver;
        this.element = element;
        this.parent = $(element).parent();
        this.nextSibling = $(element).next();
    }

    execute() {
        this.receiver.remove(this.element);
    }

    undo() {
        if (this.nextSibling.length > 0) {
            $(this.element).insertBefore(this.nextSibling);
        } else {
            this.parent.append(this.element);
        }
    }
}

/**
 * 验证器
 */
class Validator {
    /**
     * 表单必填项空值校验
     */
    static required(structure) {
        let config = {};
        let allFieldsValid = true;
        for (const key in structure) {
            let value = key.type == 'checkbox' ?
                $(key).prop("checked") : $(key).val();

            config[structure[key].value] = value;

            if (value || structure[key].require === false) {
                $(key).removeClass('is_null');
            } else {
                $(key).addClass('is_null');
                allFieldsValid = false;
            }
        }
        return {
            valid: allFieldsValid,
            config: config
        };
    }

    /**
     * 表单合法性校验
     */
    static checkKeyValuePairs(structure, config) {
        let errorKeys = [];
        let allFieldsValid = true;

        for (const key in structure) {
            const { check, value } = structure[key];
            const fieldValue = config[value];

            // 如果字段没有值或校验类型不匹配,则跳过当前迭代
            if (!fieldValue) continue;

            let isValid = true;
            switch (check) {
                case 'keyValuePairs':
                    isValid = Validator.keyValuePairs(fieldValue);
                    break;
                case 'dotSeparatedPath':
                    isValid = Validator.validateDotSeparatedPath(fieldValue);
                    break;
                default:
                    // 没有匹配的校验类型
                    continue;
            }

            Validator.toggleErrorDisplay(key, isValid);
            if (!isValid) {
                allFieldsValid = false;
                errorKeys.push(key);
            }
        }

        return {
            valid: allFieldsValid,
            errorKeys: errorKeys
        };
    }

    /**
     * 切换错误信息的显示和隐藏
     * @param {string} key - 字段的键
     * @param {boolean} isValid - 字段值是否有效
     */
    static toggleErrorDisplay(key, isValid) {
        const errorMessage = i18next.t('common.unValid', { ns: 'settings' });
        const $errorSpan = $(key).prev('span.text-error');
        if (!isValid) {
            if (!$errorSpan.length) {
                $(key).before(`<span class="text-error" style="color: red;">${errorMessage}</span>`);
            }
        } else {
            $errorSpan.remove();
        }
    }

    /**
     * 键值对合法性校验
     * @param {string} value
     * @returns {boolean}
     */
    static keyValuePairs(value) {
        const keyValuePairs = value.split('\n');
        // 允许值中包含空格和冒号
        const regex = /^[a-zA-Z0-9_-]+\s*:\s*.+$/;
        return keyValuePairs.every(pair => regex.test(pair));
    }


    /**
     * 点分隔符路径格式校验,允许加减运算
     * @param {string} path
     * @returns {boolean}
     */
    static validateDotSeparatedPath(path) {
        // 正则表达式允许标识符之间有点号,标识符可以包含加减运算
        const regex = /^([a-zA-Z0-9_-]+(\s*[\+\-]\s*[a-zA-Z0-9_-]+)*\.)*([a-zA-Z0-9_-]+(\s*[\+\-]\s*[a-zA-Z0-9_-]+)*)$/;
        return regex.test(path);
    }
}

/**
 * 配置管理
 */
class ConfigManager {
    /**
     * @param {HTMLElement} element - 挂载容器
     * @param {string} prefix - 前缀
     * @param {object} tempConfig - 配置内容
     * @param {object} structure - 配置结构
     * @param {object} configHTML - 配置编辑页面HTML
     * @param {boolean} allowChoice - 是否允许选择列表项
     */
    constructor(element, prefix, tempConfig, structure, configHTML, allowChoice = true) {
        /** @param 设置面板DIV */
        this.settingMenuDiv = $('#OJBetter_setting_menu');
        this.element = $(element);
        this.prefix = prefix;
        this.tempConfig = tempConfig;
        this.structure = structure;
        this.configHTML = configHTML;
        this.allowChoice = allowChoice;

        this.controlTip = null;
        this.config_bar_list = null;
        this.config_bar_ul = null;
        this.config_add_button = null;
        this.menu = null;
        this.editItem = null;
        this.deleteItem = null;

        // 绑定方法
        this.onAdd = this.onAdd.bind(this);
        this.onEdit = this.onEdit.bind(this);
        this.onDelete = this.onDelete.bind(this);
        this.createListItemElement = this.createListItemElement.bind(this);

        this.lastItemId = 0; // 列表中当前最后一个元素的id号
        this.init();
    }

    init() {
        this.createControlBar();
        this.createContextMenu();
        this.renderList();
    }

    /**
     * 创建控制栏
     */
    createControlBar() {
        this.controlTip = OJB_safeCreateJQElement(`<div id='${this.prefix}configControlTip' style='color:red;'></div>`);
        this.config_bar_list = OJB_safeCreateJQElement(`<div class='config_bar_list' id='${this.prefix}config_bar_list'></div>`);
        this.config_bar_ul = OJB_safeCreateJQElement(`<ul class='config_bar_ul' id='${this.prefix}config_bar_ul'></ul>`);
        this.element.append(this.controlTip);
        this.element.append(this.config_bar_list);
        this.config_bar_list.append(this.config_bar_ul);
    }

    /**
     * 创建右键菜单
     */
    createContextMenu() {
        const menu = OJB_safeCreateJQElement(`<div id='config_bar_menu' style='display: none;'></div>`);
        const editItem = OJB_safeCreateJQElement(`
        <div class='config_bar_menu_item' id='config_bar_menu_edit'>
            ${i18next.t('contextMenu.edit', { ns: 'translator' })}
        </div>`);
        const deleteItem = OJB_safeCreateJQElement(`
        <div class='config_bar_menu_item' id='config_bar_menu_delete'>
            ${i18next.t('contextMenu.delete', { ns: 'translator' })}
        </div>`);
        menu.append(editItem);
        menu.append(deleteItem);
        this.editItem = editItem;
        this.deleteItem = deleteItem;
        this.menu = menu;
        this.settingMenuDiv.append(menu);
    }

    /**
     * 关闭右键菜单
     */
    closeContextMenu() {
        this.menu.css({ display: "none" });
    }

    /**
     * 创建列表项
     * @param {string} text - 列表项文本
     * @returns {HTMLElement} - 列表项
     */
    createListItemElement(text) {
        const id = OJB_getRandomNumber(4);
        const li = $("<li></li>");
        const radio = OJB_safeCreateJQElement(`<input type='radio' name='${this.prefix}config_item'></input>`)
            .attr("value", text)
            .attr("id", id)
            .attr("prev_id", this.lastItemId)
            .appendTo(li);
        if (!this.allowChoice) {
            radio.prop("disabled", true);
        }
        const label = OJB_safeCreateJQElement(`<label for='${id}' class='config_bar_ul_li_text'>${text}</label>`).appendTo(li);


        this.lastItemId = id;

        // 添加右键菜单
        li.on("contextmenu", (event) => {
            event.preventDefault();
            this.menu.css({
                display: "block",
                left: event.pageX, top: event.pageY
            });

            const deleteItem = this.deleteItem;
            const editItem = this.editItem;

            // 移除旧事件
            deleteItem.off("click");
            editItem.off("click");

            // 获取 li 在 ul 中的索引
            const index = li.index();

            deleteItem.on("click", () => this.onDelete(index, li));
            editItem.on("click", () => this.onEdit(index, li));

            $(document).one("click", (event) => {
                if (!this.menu.get(0).contains(event.target)) {
                    this.closeContextMenu();
                    deleteItem.off("click", () => this.onDelete);
                    editItem.off("click", () => this.onEdit);
                }
            });
        });

        return li;
    }

    /**
     * 渲染配置列表
     */
    renderList() {
        const list = this.config_bar_ul;
        list.empty(); // 清空
        this.tempConfig.configurations.forEach((item) => {
            list.append(this.createListItemElement(item['name']));
        });

        // 添加按钮
        let addButton = OJB_safeCreateJQElement(`<li id='${this.prefix}add_button' class="tempConfig_add_button">
            <span>+ ${i18next.t('add', { ns: 'common' })}</span>
        </li>`);
        this.config_add_button = addButton;
        list.append(addButton);
        addButton.on("click", this.onAdd);
    }

    /**
     * 添加配置项
     */
    onAdd() {
        const configMenu = this.createConfigHTML();
        const structure = this.structure;

        configMenu.on("click", "#tempConfig_save", () => {

            // 检查必填字段
            const { valid, config } = Validator.required(structure);
            if (!valid) return;

            // 检查键值对
            const { valid: checkOk, errorKey } = Validator.checkKeyValuePairs(structure, config);
            if (!checkOk) return;

            this.tempConfig.configurations.push(config);

            this.createListItemElement(config.name).insertBefore(this.config_add_button);

            configMenu.remove();
        });

        configMenu.on("click", ".btn-close", () => {
            configMenu.remove();
        });
    }

    /**
     * 修改配置项
     * @param {number} index - 配置项索引
     * @param {HTMLElement} li - 配置项
     * @returns {void}
     */
    onEdit(index, li) {
        const configMenu = this.createConfigHTML();
        const structure = this.structure;

        this.closeContextMenu();

        // 填充表单
        for (const [key, { value, type }] of Object.entries(this.structure)) {
            const configValue = this.tempConfig.configurations[index][value];
            const $element = $(key);
            if (type === 'checkbox') {
                $element.prop("checked", configValue);
            } else {
                $element.val(configValue);
            }
        }

        configMenu.on("click", "#tempConfig_save", () => {
            // 检查必填字段
            const { valid, config } = Validator.required(structure);
            if (!valid) return;

            // 检查键值对
            const { valid: checkOk, errorKey } = Validator.checkKeyValuePairs(structure, config);
            if (!checkOk) return;

            // 更新配置
            this.tempConfig.configurations[index] = config;
            li.find('label').text(config.name);

            OJB_closeAndRemoveModal(configMenu);
        });

        configMenu.on("click", ".btn-close", () => {
            OJB_closeAndRemoveModal(configMenu);
        });
    }

    /**
     * 删除配置项
     * @param {number} index - 配置项索引
     * @param {HTMLElement} li - 配置项
     * @returns {void}
     */
    onDelete(index, li) {
        this.closeContextMenu();
        this.tempConfig.configurations.splice(index, 1);
        li.remove();
    }

    /**
     * 创建配置编辑页面
     * @returns {JQuery<HTMLElement>} 返回配置编辑页面
     */
    createConfigHTML() {
        const configMenu = OJB_safeCreateJQElement(this.configHTML);
        this.settingMenuDiv.after(configMenu);
        OJB_showModal(configMenu);
        OJB_addDraggable(configMenu);
        elementLocalize(configMenu);
        return configMenu;
    }

    /**
     * 获取配置内容
     * @returns {object} - 配置内容
     */
    getTempConfig() {
        return this.tempConfig;
    }

    /**
     * 注册列表项选中改变监听
     */
    registerChoiceChange() {
        this.config_bar_ul.on("change", "input[type='radio']", (event) => {
            const value = event.target.value;
            this.tempConfig.choice = value;
        });
    }
}

const OJBetter_setting_sidebar_HTML = `
<div class="OJBetter_setting_sidebar">
    <ul>
        <li><a href="#basic-settings" id="sidebar-basic-settings" class="active" data-i18n="settings:sidebar.basic"></a></li>
        <li><a href="#preference-settings" id="sidebar-preference-settings" data-i18n="settings:sidebar.preference"></a></li>
        <li><a href="#translation-settings" id="sidebar-translation-settings" data-i18n="settings:sidebar.translation"></a></li>
        <li><a href="#clist_rating-settings" id="sidebar-clist_rating-settings" data-i18n="settings:sidebar.clist"></a></li>
        <li><a href="#code_editor-settings" id="sidebar-code_editor-settings" data-i18n="settings:sidebar.monaco"></a></li>
        <li><a href="#dev-settings" id="sidebar-dev-settings" data-i18n="settings:sidebar.dev"></a></li>
        <li><a href="#about-settings" id="sidebar-about-settings" data-i18n="settings:sidebar.about"></a></li>
    </ul>
</div>
`;

const basic_settings_HTML = `
<div id="basic-settings" class="settings-page active">
    <h3 data-i18n="settings:basic.title"></h3>
    <hr>
    <div class='OJBetter_setting_list' style="padding: 0px 10px;">
        <span id="darkMode_span" data-i18n="settings:basic.darkMode.name"></span>
        <div class="dark-mode-selection">
            <label>
                <input class="radio-input" type="radio" name="darkMode" value="dark" />
                <span class="OJBetter_setting_menu_label_text"
                    data-i18n="settings:basic.darkMode.options.dark"></span>
                <span class="radio-icon"> </span>
            </label>
            <label>
                <input checked="" class="radio-input" type="radio" name="darkMode" value="light" />
                <span class="OJBetter_setting_menu_label_text"
                    data-i18n="settings:basic.darkMode.options.light"></span>
                <span class="radio-icon"> </span>
            </label>
            <label>
                <input class="radio-input" type="radio" name="darkMode" value="follow" />
                <span class="OJBetter_setting_menu_label_text"
                    data-i18n="settings:basic.darkMode.options.system"></span>
                <span class="radio-icon"> </span>
            </label>
        </div>
    </div>
    <div class='OJBetter_setting_list'>
        <label for="scriptL10nLanguage" style="display: flex;" data-i18n="settings:localization.scriptLanguageLabel"></label>
        <select id="scriptL10nLanguage" name="scriptL10nLanguage">
            <option value="zh">简体中文</option>
            <option value="zh-Hant">繁體中文</option>
            <option value="en">English</option>
            <option value="de">Deutsch</option>
            <option value="fr">Français</option>
            <option value="ko">한국어</option>
            <option value="pt">Português</option>
            <option value="ja">日本語</option>
            <option value="es">Español</option>
            <option value="it">Italiano</option>
            <option value="hi">हिन्दी</option>
        </select>
    </div>
    <div class='OJBetter_setting_list'>
        <label for="localizationLanguage" style="display: flex;" data-i18n="settings:localization.websiteLanguageLabel"></label>
        <select id="localizationLanguage" name="localizationLanguage">
            <option value="initial">——</option>
            <option value="zh">简体中文</option>
            <option value="zh-Hant">繁體中文</option>
            <option value="de">Deutsch</option>
            <option value="fr">Français</option>
            <option value="ko">한국어</option>
            <option value="pt">Português</option>
            <option value="ja">日本語</option>
            <option value="es">Español</option>
            <option value="it">Italiano</option>
            <option value="hi">हिन्दी</option>
        </select>
    </div>
    <div class='OJBetter_setting_list alert_tip'>
        <div data-i18n="[html]settings:localization.notice.1"></div>
    </div>
    <div class='OJBetter_setting_list alert_tip'>
        <div data-i18n="[html]settings:localization.notice.2"></div>
    </div>
</div>
`;

const translation_settings_HTML = `
<div id="translation-settings" class="settings-page">
    <h3 data-i18n="settings:translation.title"></h3>
    <hr>
    <h4 data-i18n="settings:translation.options.title"></h4>
    <div class='OJBetter_setting_list'>
        <label for="transTargetLang" style="display: flex;" data-i18n="settings:translation.preference.target.title"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.preference.target.helpText"></div>
        </div>
        <select id="transTargetLang" name="transTargetLang">
            <option value="zh">简体中文</option>
            <option value="zh-Hant">繁體中文</option>
            <option value="de">Deutsch</option>
            <option value="fr">Français</option>
            <option value="ko">한국어</option>
            <option value="pt">Português</option>
            <option value="ja">日本語</option>
            <option value="es">Español</option>
            <option value="it">Italiano</option>
            <option value="hi">हिन्दी</option>
        </select>
    </div>
    <div id="translationServices">
        <label>
            <input type='radio' name='translation' value='deepl'>
            <span class='OJBetter_setting_menu_label_text'
                data-i18n="settings:translation.options.services.deepl"></span>
        </label>
        <label>
            <input type='radio' name='translation' value='iflyrec'>
            <span class='OJBetter_setting_menu_label_text'
                data-i18n="settings:translation.options.services.iflyrec"></span>
        </label>
        <label>
            <input type='radio' name='translation' value='youdao'>
            <span class='OJBetter_setting_menu_label_text'
                data-i18n="settings:translation.options.services.youdao"></span>
        </label>
        <label>
            <input type='radio' name='translation' value='google'>
            <span class='OJBetter_setting_menu_label_text'
                data-i18n="settings:translation.options.services.google"></span>
        </label>
        <label>
            <input type='radio' name='translation' value='caiyun'>
            <span class='OJBetter_setting_menu_label_text'
                data-i18n="settings:translation.options.services.caiyun"></span>
        </label>
        <label>
            <input type='radio' name='translation' value='openai'>
            <span class='OJBetter_setting_menu_label_text'
                data-i18n="settings:translation.options.services.openai.name">
                <div class="help_tip">
                    ${helpCircleHTML}
                    <div class="tip_text"
                        data-i18n="[html]settings:translation.options.services.openai.helpText"></div>
                </div>
            </span>
        </label>
    </div>
    <hr>
    <h4>DeepL</h4>
    <div class='OJBetter_setting_list'>
        <label for="deepl_type" style="display: flex;" data-i18n="settings:translation.deepl.mode.title"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.deepl.mode.helpText"></div>
        </div>
        <select id="deepl_type" name="deepl_type">
            <option value="free" data-i18n="settings:translation.deepl.mode.select.free"></option>
            <option value="api" data-i18n="settings:translation.deepl.mode.select.api"></option>
        </select>
    </div>
    <div id="deepl_config" class="config"></div>
    <div class='OJBetter_setting_list'>
        <label for="enableEmphasisProtection" data-i18n="settings:translation.deepl.enableEmphasisProtection.title"></label>
        <div class="help_tip" style="margin-right: initial;">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.deepl.enableEmphasisProtection.helpText"></div>
        </div>
        <div class="badge">Official API Only</div>
        <input type="checkbox" id="enableEmphasisProtection" name="enableEmphasisProtection">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="enableLinkProtection" data-i18n="settings:translation.deepl.enableLinkProtection.title"></label>
        <div class="help_tip" style="margin-right: initial;">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.deepl.enableLinkProtection.helpText"></div>
        </div>
        <div class="badge">Official API Only</div>
        <input type="checkbox" id="enableLinkProtection" name="enableLinkProtection">
    </div>
    <hr>
    <h4>ChatGPT</h4>
    <div id="chatgpt_config" class="config"></div>
    <div class='OJBetter_setting_list'>
        <label for="openai_isStream" data-i18n="settings:translation.chatgpt.isStream.name"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.chatgpt.isStream.helpText"></div>
        </div>
        <input type="checkbox" id="openai_isStream" name="openai_isStream">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="openai_asSystemPrompt" data-i18n="settings:translation.chatgpt.asSystemPrompt.name"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.chatgpt.asSystemPrompt.helpText"></div>
        </div>
        <input type="checkbox" id="openai_asSystemPrompt" name="openai_asSystemPrompt">
    </div>
    <div class="OJBetter_setting_list">
        <label for='openai_customPrompt'>
            <div style="display: flex;align-items: center;">
                <span class="input_label" data-i18n="settings:translation.chatgpt.customPrompt.name"></span>
                <div class="help_tip">
                    ${helpCircleHTML}
                    <div class="tip_text" data-i18n="[html]settings:translation.chatgpt.customPrompt.helpText"></div>
                </div>
            </div>
        </label>
        <textarea id="openai_customPrompt" placeholder='' require = false data-i18n="[placeholder]settings:translation.chatgpt.customPrompt.placeholder"></textarea>
    </div>
    <hr>
    <h4 data-i18n="settings:translation.preference.title"></h4>
    <div class='OJBetter_setting_list'>
        <label for="comment_translation_choice" style="display: flex;"
            data-i18n="settings:translation.preference.comment_translation_choice.title">
        </label>
        <select id="comment_translation_choice" name="comment_translation_choice">
            <option value="0" data-i18n="settings:translation.preference.comment_translation_choice.services.follow"></option>
            <option value="deepl" data-i18n="settings:translation.preference.comment_translation_choice.services.deepl"></option>
            <option value="iflyrec" data-i18n="settings:translation.preference.comment_translation_choice.services.iflyrec"></option>
            <option value="youdao" data-i18n="settings:translation.preference.comment_translation_choice.services.youdao"></option>
            <option value="google" data-i18n="settings:translation.preference.comment_translation_choice.services.google"></option>
            <option value="caiyun" data-i18n="settings:translation.preference.comment_translation_choice.services.caiyun"></option>
            <option value="openai" data-i18n="settings:translation.preference.comment_translation_choice.services.openai"></option>
        </select>
    </div>
    <hr>
    <h4 data-i18n="settings:translation.autoTranslation.title"></h4>
    <div class='OJBetter_setting_list'>
        <label for="autoTranslation" data-i18n="settings:translation.autoTranslation.enable"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.autoTranslation.helpText"></div>
        </div>
        <input type="checkbox" id="autoTranslation" name="autoTranslation">
    </div>
    <div class='OJBetter_setting_list'>
        <label for='shortTextLength'>
            <div style="display: flex;align-items: center;"
                data-i18n="settings:translation.autoTranslation.shortTextLength.name"></div>
        </label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.autoTranslation.shortTextLength.helpText">
            </div>
        </div>
        <input type='number' id='shortTextLength' class='no_default' require=true data-i18n="[placeholder]settings:translation.autoTranslation.shortTextLength.placeholder">
        <span data-i18n="settings:translation.autoTranslation.shortTextLength.end"></span>
    </div>
    <div class='OJBetter_setting_list'>
        <label for="allowMixTrans" data-i18n="settings:translation.autoTranslation.allowMixTrans.name"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.autoTranslation.allowMixTrans.helpText">
            </div>
        </div>
        <input type="checkbox" id="allowMixTrans" name="allowMixTrans">
        <div class='OJBetter_checkboxs'>
            <input type="checkbox" id="deepl" name="mixedTranslation" value="deepl">
            <label for="deepl" data-i18n="settings:translation.autoTranslation.allowMixTrans.checkboxs.deepl"></label>
            <input type="checkbox" id="iflyrec" name="mixedTranslation" value="iflyrec">
            <label for="iflyrec" data-i18n="settings:translation.autoTranslation.allowMixTrans.checkboxs.iflyrec"></label>
            <input type="checkbox" id="youdao" name="mixedTranslation" value="youdao">
            <label for="youdao" data-i18n="settings:translation.autoTranslation.allowMixTrans.checkboxs.youdao"></label>
            <input type="checkbox" id="google" name="mixedTranslation" value="google">
            <label for="google" data-i18n="settings:translation.autoTranslation.allowMixTrans.checkboxs.google">Google</label>
            <input type="checkbox" id="caiyun" name="mixedTranslation" value="caiyun">
            <label for="caiyun" data-i18n="settings:translation.autoTranslation.allowMixTrans.checkboxs.caiyun"></label>
        </div>
    </div>
    <hr>
    <h4 data-i18n="settings:translation.advanced.name"></h4>
    <div class='OJBetter_setting_list'>
        <label for="comment_translation_mode" style="display: flex;" data-i18n="settings:translation.advanced.mode.name"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.advanced.mode.helpText"></div>
        </div>
        <select id="comment_translation_mode" name="comment_translation_mode">
            <option value="0" data-i18n="settings:translation.advanced.mode.options.0"></option>
            <option value="1" data-i18n="settings:translation.advanced.mode.options.1"></option>
            <option value="2" data-i18n="settings:translation.advanced.mode.options.2"></option>
        </select>
    </div>
    <div class='OJBetter_setting_list'>
        <label for="memoryTranslateHistory" data-i18n="settings:translation.advanced.memory.name"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.advanced.memory.helpText"></div>
        </div>
        <input type="checkbox" id="memoryTranslateHistory" name="memoryTranslateHistory">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="translation_retransAction" style="display: flex;" data-i18n="settings:translation.advanced.retrans.name"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.advanced.retrans.helpText"></div>
        </div>
        <select id="translation_retransAction" name="translation_retransAction">
            <option value=0 data-i18n="settings:translation.advanced.retrans.options.0"></option>
            <option value=1 data-i18n="settings:translation.advanced.retrans.options.1"></option>
        </select>
    </div>
    <div class='OJBetter_setting_list'>
        <label for='transWaitTime'>
            <div style="display: flex;align-items: center;" data-i18n="settings:translation.advanced.transWaitTime.name"></div>
        </label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.advanced.transWaitTime.helpText"></div>
        </div>
        <input type='number' id='transWaitTime' class='no_default' require=true data-i18n="[placeholder]settings:translation.advanced.transWaitTime.placeholder">
        <span data-i18n="settings:translation.advanced.transWaitTime.end"></span>
    </div>
    <div class='OJBetter_setting_list'>
        <label for="translation_replaceSymbol" style="display: flex;" data-i18n="settings:translation.advanced.replaceSymbol.name"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.advanced.replaceSymbol.helpText"></div>
        </div>
        <select id="translation_replaceSymbol" name="translation_replaceSymbol">
            <option value=2 data-i18n="settings:translation.advanced.replaceSymbol.options.2"></option>
            <option value=1 data-i18n="settings:translation.advanced.replaceSymbol.options.1"></option>
            <option value=3 data-i18n="settings:translation.advanced.replaceSymbol.options.3"></option>
        </select>
    </div>
    <div class='OJBetter_setting_list'>
        <label for="filterTextWithoutEmphasis" data-i18n="settings:translation.advanced.filterTextWithoutEmphasis.name"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.advanced.filterTextWithoutEmphasis.helpText"></div>
        </div>
        <input type="checkbox" id="filterTextWithoutEmphasis" name="filterTextWithoutEmphasis">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="forceTurndownConversion" data-i18n="settings:translation.advanced.forceTurndownConversion.name"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:translation.advanced.forceTurndownConversion.helpText"></div>
        </div>
        <input type="checkbox" id="forceTurndownConversion" name="forceTurndownConversion">
    </div>
</div>
`;

const clist_rating_settings_HTML = `
<div id="clist_rating-settings" class="settings-page">
    <h3 data-i18n="settings:clist.title"></h3>
    <hr>
    <h4 data-i18n="settings:clist.basics.name"></h4>
    <div class='OJBetter_setting_list alert_tip'>
        <div>
            <p data-i18n="[html]settings:clist.basics.notice"></p>
        </div>
    </div>
    <div class='OJBetter_setting_list'>
        <label for='clist_Authorization'>
            <div style="display: flex;align-items: center;">
                <span class="input_label" data-i18n="settings:clist.basics.key.title"></span>
            </div>
        </label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:clist.basics.key.helpText"></div>
        </div>
        <input type='text' id='clist_Authorization' class='no_default' required="true"
            data-i18n="[placeholder]settings:clist.basics.key.keyPlaceholder">
    </div>
    <hr>
    <h4 data-i18n="settings:clist.displayRating.title"></h4>
    <div class='OJBetter_setting_list'>
        <label for="showClistRating_contest"><span data-i18n="settings:clist.displayRating.contest.name"></span></label>
        <input type="checkbox" id="showClistRating_contest" name="showClistRating_contest">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="showClistRating_problem"><span data-i18n="settings:clist.displayRating.problem.name"></span></label>
        <input type="checkbox" id="showClistRating_problem" name="showClistRating_problem">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="showClistRating_problemset"><span data-i18n="settings:clist.displayRating.problemset.name"></span></label>
        <input type="checkbox" id="showClistRating_problemset" name="showClistRating_problemset">
    </div>
    <hr>
    <div class='OJBetter_setting_list'>
        <label for="RatingHidden"><span data-i18n="settings:clist.spoilerProtection.title"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:clist.spoilerProtection.helpText"></div>
        </div>
        <input type="checkbox" id="RatingHidden" name="RatingHidden">
    </div>
</div>
`;

const code_editor_settings_HTML = `
<div id="code_editor-settings" class="settings-page">
    <h3 data-i18n="settings:codeEditor.title"></h3>
    <hr>
    <h4 data-i18n="settings:codeEditor.basics"></h4>
    <div class='OJBetter_setting_list'>
        <label for="problemPageCodeEditor"><span
                data-i18n="settings:codeEditor.problemPageCodeEditor.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="settings:codeEditor.problemPageCodeEditor.helpText"></div>
        </div>
        <input type="checkbox" id="problemPageCodeEditor" name="problemPageCodeEditor">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="beautifyPreBlocks"><span
                data-i18n="settings:codeEditor.beautifyPreBlocks.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="settings:codeEditor.beautifyPreBlocks.helpText"></div>
        </div>
        <input type="checkbox" id="beautifyPreBlocks" name="beautifyPreBlocks">
    </div>
    <hr>
    <h4 data-i18n="settings:codeEditor.preferences.title"></h4>
    <div class='OJBetter_setting_list'>
        <label for="isCodeSubmitConfirm"><span
                data-i18n="settings:codeEditor.preferences.isCodeSubmitConfirm.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="settings:codeEditor.preferences.isCodeSubmitConfirm.helpText"></div>
        </div>
        <input type="checkbox" id="isCodeSubmitConfirm" name="isCodeSubmitConfirm">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="autoSubmitAfterPass"><span
                data-i18n="settings:codeEditor.preferences.autoSubmitAfterPass.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="settings:codeEditor.preferences.autoSubmitAfterPass.helpText"></div>
        </div>
        <input type="checkbox" id="autoSubmitAfterPass" name="autoSubmitAfterPass">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="alwaysConsumeMouseWheel"><span
                data-i18n="settings:codeEditor.preferences.alwaysConsumeMouseWheel.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="settings:codeEditor.preferences.alwaysConsumeMouseWheel.helpText"></div>
        </div>
        <input type="checkbox" id="alwaysConsumeMouseWheel" name="alwaysConsumeMouseWheel">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="autoMemoryCode"><span
                data-i18n="settings:codeEditor.preferences.autoMemoryCode.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="settings:codeEditor.preferences.autoMemoryCode.helpText"></div>
        </div>
        <input type="checkbox" id="autoMemoryCode" name="autoMemoryCode">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="submitButtonPosition"><span
                data-i18n="settings:codeEditor.preferences.submitButtonPosition.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="settings:codeEditor.preferences.submitButtonPosition.helpText"></div>
        </div>
        <select id="submitButtonPosition" name="submitButtonPosition">
            <option value="bottom" data-i18n="settings:codeEditor.preferences.submitButtonPosition.options.bottom"></option>
            <option value="top" data-i18n="settings:codeEditor.preferences.submitButtonPosition.options.top"></option>
        </select>
    </div>
    <hr>
    <h4 data-i18n="settings:codeEditor.onlineCodeExecution.title"></h4>
    <label>
        <input type='radio' name='compiler' value='official'>
        <span class='OJBetter_setting_menu_label_text'
            data-i18n="settings:codeEditor.onlineCodeExecution.compilerOptions.codeforces"></span>
    </label>
    <label>
        <input type='radio' name='compiler' value='wandbox'>
        <span class='OJBetter_setting_menu_label_text'
            data-i18n="settings:codeEditor.onlineCodeExecution.compilerOptions.wandbox"></span>
    </label>
    <label>
        <input type='radio' name='compiler' value='rextester'>
        <span class='OJBetter_setting_menu_label_text'
            data-i18n="settings:codeEditor.onlineCodeExecution.compilerOptions.rextester"></span>
    </label>
    <hr>
    <h4 data-i18n="settings:codeEditor.lsp.title"></h4>
    <div class='OJBetter_setting_list'>
        <label for="useLSP"><span data-i18n="settings:codeEditor.lsp.useLSP.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:codeEditor.lsp.useLSP.helpText"></div>
        </div>
        <input type="checkbox" id="useLSP" name="useLSP">
    </div>
    <div class='OJBetter_setting_list'>
        <label for='OJBetter_Bridge_WorkUri'>
            <div style="display: flex;align-items: center;">
                <span class="input_label" data-i18n="settings:codeEditor.lsp.OJBetter_Bridge_WorkUri.label"></span>
            </div>
        </label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:codeEditor.lsp.OJBetter_Bridge_WorkUri.helpText"></div>
        </div>
        <input type='text' id='OJBetter_Bridge_WorkUri' class='no_default'
            require=true data-i18n="[placeholder]settings:codeEditor.lsp.OJBetter_Bridge_WorkUri.placeholder">
    </div>
    <div class='OJBetter_setting_list'>
        <label for='OJBetter_Bridge_SocketUrl'>
            <div style="display: flex;align-items: center;">
                <span class="input_label"
                    data-i18n="settings:codeEditor.lsp.OJBetter_Bridge_SocketUrl.label"></span>
            </div>
        </label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:codeEditor.lsp.OJBetter_Bridge_SocketUrl.helpText"></div>
        </div>
        <input type='text' id='OJBetter_Bridge_SocketUrl' class='no_default'
            require=true data-i18n="[placeholder]settings:codeEditor.lsp.OJBetter_Bridge_SocketUrl.placeholder">
    </div>
    <hr>
    <h4 data-i18n="settings:codeEditor.staticCompletionEnhancement.title"></h4>
    <div class='OJBetter_setting_list'>
        <label for="cppCodeTemplateComplete"><span
                data-i18n="settings:codeEditor.staticCompletionEnhancement.cppCodeTemplateComplete.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:codeEditor.staticCompletionEnhancement.cppCodeTemplateComplete.helpText"></div>
        </div>
        <input type="checkbox" id="cppCodeTemplateComplete" name="cppCodeTemplateComplete">
    </div>
    <hr>
    <h5 data-i18n="settings:codeEditor.staticCompletionEnhancement.customization"></h5>
    <div class='OJBetter_setting_list alert_warn'>
        <div>
            <p data-i18n="settings:codeEditor.staticCompletionEnhancement.performanceWarning"></p>
        </div>
    </div>
    <div id="Complet_config" class="config"></div>
</div>
`;

const preference_settings_HTML = `
<div id="preference-settings" class="settings-page">
    <h3 data-i18n="settings:preference.title"></h3>
    <hr>
    <div class='OJBetter_setting_list'>
        <label for="expandFoldingblocks" data-i18n="settings:basic.expandBlocks"></label>
        <input type="checkbox" id="expandFoldingblocks" name="expandFoldingblocks">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="renderPerfOpt" data-i18n="settings:basic.renderOptimization.label"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:basic.renderOptimization.helpText"></div>
        </div>
        <input type="checkbox" id="renderPerfOpt" name="renderPerfOpt">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="selectElementPerfOpt" data-i18n="settings:basic.selectElementOptimization.label"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:basic.selectElementOptimization.helpText"></div>
        </div>
        <input type="checkbox" id="selectElementPerfOpt" name="selectElementPerfOpt">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="commentPaging" data-i18n="settings:basic.paging.label"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:basic.paging.helpText"></div>
        </div>
        <input type="checkbox" id="commentPaging" name="commentPaging">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="showJumpToLuogu" data-i18n="settings:basic.luoguJump.label"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:basic.luoguJump.helpText"></div>
        </div>
        <input type="checkbox" id="showJumpToLuogu" name="showJumpToLuogu">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="showCF2vjudge" data-i18n="settings:basic.vjudgeJump.label"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:basic.vjudgeJump.helpText"></div>
        </div>
        <input type="checkbox" id="showCF2vjudge" name="showCF2vjudge">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="standingsRecolor" data-i18n="settings:basic.recolor.label"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:basic.recolor.helpText"></div>
        </div>
        <input type="checkbox" id="standingsRecolor" name="standingsRecolor">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="hiddenProblemTag" data-i18n="settings:basic.hiddenProblemTag.label"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:basic.hiddenProblemTag.helpText"></div>
        </div>
        <input type="checkbox" id="hiddenProblemTag" name="hiddenProblemTag">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="showLoading" data-i18n="settings:preference.loadingInfo.label"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:preference.loadingInfo.helpText" data-i18n-options='{ "scriptName": "${OJBetter.state.name}" }'></div>
        </div>
        <input type="checkbox" id="showLoading" name="showLoading">
    </div>
    <div class='OJBetter_setting_list'>
        <label for="hoverTargetAreaDisplay" data-i18n="settings:preference.targetArea.label"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:preference.targetArea.helpText"></div>
        </div>
        <input type="checkbox" id="hoverTargetAreaDisplay" name="hoverTargetAreaDisplay">
    </div>
    <div class='OJBetter_setting_list'>
        <label for='iconButtonSize'>
            <div style="display: flex;align-items: center;" data-i18n="settings:preference.iconButtonSize.title"></div>
        </label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:preference.iconButtonSize.helpText"></div>
        </div>
        <input type='number' id='iconButtonSize' class='no_default' require=true data-i18n="[placeholder]settings:preference.iconButtonSize.placeholder">
        <span>px</span>
    </div>
    <div class='OJBetter_setting_list'>
        <label for='judgeStatusReplaceText'>
            <div style="display: flex;align-items: center;" data-i18n="settings:preference.judgeStatusReplaceText.title"></div>
        </label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:preference.judgeStatusReplaceText.helpText"></div>
        </div>
        <input type="text" id='judgeStatusReplaceText' class='no_default' data-i18n="[placeholder]settings:preference.judgeStatusReplaceText.placeholder">
    </div>
</div>
`;

const dev_settings_HTML = `
<div id="dev-settings" class="settings-page">
    <h3 data-i18n="settings:dev.title"></h3>
    <hr>
    <div class='OJBetter_setting_list alert_danger'>
        <div>
            <p data-i18n="[html]settings:dev.notice"></p>
        </div>
    </div>
    <hr>
    <h5 data-i18n="settings:dev.load.title"></h5>
    <div class='OJBetter_setting_list'>
        <label for="notWaiteLoaded"><span data-i18n="settings:dev.load.notWaiteLoaded.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:dev.load.notWaiteLoaded.helpText"></div>
        </div>
        <input type="checkbox" id="notWaiteLoaded" name="notWaiteLoaded">
    </div>
    <hr>
    <h5 data-i18n="settings:dev.l10n.title"></h5>
    <div class='OJBetter_setting_list'>
        <label><span data-i18n="settings:dev.l10n.refreshScrpitCache.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:dev.l10n.refreshScrpitCache.helpText"></div>
        </div>
        <button type="button" id="l10n_refreshScrpitCacheButton" name="l10n_refreshScrpitCacheButton" data-i18n="settings:dev.l10n.refreshScrpitCache.button"></button>
    </div>
    <hr>
    <h5 data-i18n="settings:dev.l10n_web.title"></h5>
    <div class='OJBetter_setting_list'>
        <label><span data-i18n="settings:dev.l10n_web.refreshScrpitCache.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:dev.l10n_web.refreshScrpitCache.helpText"></div>
        </div>
        <button type="button" id="l10n_web_refreshScrpitCacheButton" name="l10n_web_refreshScrpitCacheButton" data-i18n="settings:dev.l10n_web.refreshScrpitCache.button"></button>
    </div>
    <div class='OJBetter_setting_list'>
        <label for="isRuleMarkingEnabled"><span data-i18n="settings:dev.l10n_web.isRuleMarkingEnabled.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:dev.l10n_web.isRuleMarkingEnabled.helpText"></div>
        </div>
        <input type="checkbox" id="isRuleMarkingEnabled" name="isRuleMarkingEnabled">
    </div>
    <hr>
    <h5 data-i18n="settings:dev.indexedDB.title"></h5>
    <div class='OJBetter_setting_list'>
        <label><span data-i18n="settings:dev.indexedDB.clear.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:dev.indexedDB.clear.helpText"></div>
        </div>
        <button type="button" id="indexedDB_clearButton" name="indexedDB_clearButton" data-i18n="settings:dev.indexedDB.clear.button"></button>
    </div>
    <div class='OJBetter_setting_list'>
        <label><span data-i18n="settings:dev.indexedDB.inputOrExport.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:dev.indexedDB.inputOrExport.helpText"></div>
        </div>
        <button type="button" id="indexedDB_exportButton" name="indexedDB_exportButton" data-i18n="settings:dev.indexedDB.inputOrExport.export"></button>
        <button type="button" id="indexedDB_importButton" name="indexedDB_importButton" data-i18n="settings:dev.indexedDB.inputOrExport.import"></button>
    </div>
    <hr>
    <h5 data-i18n="settings:dev.configuration.title"></h5>
    <div class='OJBetter_setting_list'>
        <label><span data-i18n="settings:dev.configuration.clear.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:dev.configuration.clear.helpText"></div>
        </div>
        <button type="button" id="configuration_clearButton" name="configuration_clearButton" data-i18n="settings:dev.configuration.clear.button"></button>
    </div>
    <div class='OJBetter_setting_list'>
        <label><span data-i18n="settings:dev.configuration.inputOrExport.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:dev.configuration.inputOrExport.helpText"></div>
        </div>
        <button type="button" id="configuration_exportButton" name="configuration_exportButton" data-i18n="settings:dev.configuration.inputOrExport.export"></button>
        <button type="button" id="configuration_importButton" name="configuration_importButton" data-i18n="settings:dev.configuration.inputOrExport.import"></button>
    </div>
</div>
`;

const about_settings_HTML = `
<div id="about-settings" class="settings-page">
    <h3 data-i18n="settings:about.title"></h3>
    <hr>
    <div class='versionInfo'>
        <p>${OJBetter.state.name}</p>
        <p><span data-i18n="settings:about.version"></span><span id="nowVersion">${OJBetter.state.version}</span></p>
        <p> @北极小狐 <a target="_blank" href="https://github.com/beijixiaohu/OJBetter">Github</a>
        <a target="_blank" href="https://greasyfork.org/zh-CN/scripts/465777">GreasyFork</a></p>
    </div>
    <hr>
    <h5 data-i18n="settings:about.update.title"></h5>
    <div id="thanksforDevChannelNotice" class='OJBetter_setting_list alert_info'>
        <div data-i18n="[html]settings:about.update.thanksforDevChannelNotice"} data-i18n-options='{ "scriptName": "${OJBetter.state.name}" }' ></div>
    </div>
    <div class='OJBetter_setting_list'>
        <label for="updateChannel"><span data-i18n="settings:about.update.channel.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:about.update.channel.helpText"></div>
        </div>
        <select id="updateChannel" name="updateChannel">
            <option value="release" data-i18n="settings:about.update.channel.options.release"></option>
            <option value="dev" data-i18n="settings:about.update.channel.options.dev"></option>
        </select>
    </div>
    <div class='OJBetter_setting_list'>
        <label for="updateSource"><span data-i18n="settings:about.update.source.label"></span></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]settings:about.update.source.helpText"></div>
        </div>
        <select id="updateSource" name="updateSource">
            <option value="greasyfork" data-i18n="settings:about.update.source.options.greasyfork"></option>
            <option value="github" data-i18n="settings:about.update.source.options.github"></option>
            <option value="aliyunoss" data-i18n="settings:about.update.source.options.aliyunoss"></option>
        </select>
    </div>
</div>
`;

const OJBetter_setting_content_HTML = `
<div class="OJBetter_setting_content">
    ${basic_settings_HTML}
    ${translation_settings_HTML}
    ${clist_rating_settings_HTML}
    ${code_editor_settings_HTML}
    ${preference_settings_HTML}
    ${dev_settings_HTML}
    ${about_settings_HTML}
</div>
`;

// 设置界面HTML
const OJBetterSettingMenu_HTML = `
    <dialog class='OJBetter_setting_menu' id='OJBetter_setting_menu'>
        <div class="tool-box">
            <button class='ojb_btn ojb_btn_popover top btn-close'>
                <i class="iconfont">&#xe614;</i>
            </button>
        </div>
        <div class="OJBetter_setting_container">
            ${OJBetter_setting_sidebar_HTML}
            ${OJBetter_setting_content_HTML}
        </div>
    </dialog>
`;

const apiCustomConfigHTML = (prefix) => {
    return `
    <div class="OJBetter_setting_list">
        <label for='${prefix}_header'>
            <div style="display: flex;align-items: center;">
                <span class="input_label" data-i18n="config:common.advanced.header.label"></span>
                <div class="help_tip">
                    ${helpCircleHTML}
                    <div class="tip_text" data-i18n="[html]config:common.advanced.header.tipText"></div>
                </div>
            </div>
        </label>
        <textarea id="${prefix}_header" placeholder='' require = false data-i18n="[placeholder]config:common.advanced.header.placeholder"></textarea>
    </div>
    <div class="OJBetter_setting_list">
        <label for='${prefix}_data'>
            <div style="display: flex;align-items: center;">
                <span class="input_label" data-i18n="config:common.advanced.data.label"></span>
                <div class="help_tip">
                    ${helpCircleHTML}
                    <div class="tip_text" data-i18n="[html]config:common.advanced.data.tipText"></div>
                </div>
            </div>
        </label>
        <textarea id="${prefix}_data" placeholder='' require = false data-i18n="[placeholder]config:common.advanced.data.placeholder"></textarea>
    </div>
    `;
};

const apiQuotaConfigHTML = (prefix) => {
    return `
    <div class="OJBetter_setting_list">
        <label for='${prefix}_quota_url'>
        <div style="display: flex;align-items: center;">
                <span class="input_label" data-i18n="config:common.quota.url.label"></span>
                <div class="help_tip">
                    ${helpCircleHTML}
                    <div class="tip_text" data-i18n="[html]config:common.quota.url.tipText"></div>
                </div>
            </div>
        </label>
        <input type='text' id='${prefix}_quota_url' class='no_default' placeholder='' require = true data-i18n="[placeholder]config:common.quota.url.placeholder">
    </div>
    <div class="OJBetter_setting_list">
        <label for="${prefix}_quota_method" style="display: flex;" data-i18n="config:common.quota.method.label"></label>
        <div class="help_tip">
            ${helpCircleHTML}
            <div class="tip_text" data-i18n="[html]config:common.quota.method.tipText"></div>
        </div>
        <select id="${prefix}_quota_method" name="${prefix}_quota_method">
            <option value="get">GET</option>
            <option value="post">POST</option>
        </select>
    </div>
    <div class="OJBetter_setting_list">
        <label for='${prefix}_quota_header'>
            <div style="display: flex;align-items: center;">
                <span class="input_label" data-i18n="config:common.quota.header.label"></span>
                <div class="help_tip">
                    ${helpCircleHTML}
                    <div class="tip_text" data-i18n="[html]config:common.quota.header.tipText"></div>
                </div>
            </div>
        </label>
        <textarea id="${prefix}_quota_header" placeholder='' require = false data-i18n="[placeholder]config:common.quota.header.placeholder"></textarea>
    </div>
    <div class="OJBetter_setting_list">
        <label for='${prefix}_quota_data'>
            <div style="display: flex;align-items: center;">
                <span class="input_label" data-i18n="config:common.quota.data.label"></span>
                <div class="help_tip">
                    ${helpCircleHTML}
                    <div class="tip_text" data-i18n="[html]config:common.quota.data.tipText"></div>
                </div>
            </div>
        </label>
        <textarea id="${prefix}_quota_data" placeholder='' require = false data-i18n="[placeholder]config:common.quota.data.placeholder"></textarea>
    </div>
    <div class="OJBetter_setting_list">
        <div style="display: flex;align-items: center;">
                <span class="input_label" data-i18n="config:common.quota.surplus.label"></span>
                <div class="help_tip">
                    ${helpCircleHTML}
                    <div class="tip_text" data-i18n="[html]config:common.quota.surplus.tipText"></div>
                </div>
            </div>
        </label>
        <input type='text' id='${prefix}_quota_surplus' class='no_default' placeholder='' require = true data-i18n="[placeholder]config:common.quota.surplus.placeholder">
    </div>
    `;
}

const deeplConfigEditHTML = `
    <dialog class='OJBetter_setting_menu' id='config_edit_menu'>
    <div class='OJBetter_setting_content'>
        <div class="tool-box">
            <button class='ojb_btn ojb_btn_popover top btn-close'>
                <i class="iconfont">&#xe614;</i>
            </button>
        </div>
        <h4 data-i18n="config:deepl.title"></h4>
        <h5 data-i18n="config:deepl.basic.title"></h5>
        <hr>
        <div class="OJBetter_setting_list">
            <label for='name'>
                <span class="input_label" data-i18n="config:deepl.basic.name.label"></span>
            </label>
            <input type='text' id='name' class='no_default' placeholder='' require = true data-i18n="[placeholder]config:deepl.basic.name.placeholder">
        </div>
        <div class='OJBetter_setting_list'>
            <label for="deepl_apiGenre" style="display: flex;" data-i18n="config:deepl.genre.label"></label>
            <div class="help_tip">
                ${helpCircleHTML}
                <div class="tip_text" data-i18n="[html]config:deepl.genre.tipText"></div>
            </div>
            <select id="deepl_apiGenre" name="deepl_apiGenre">
                <option value="api-free">api-free</option>
                <option value="api-pro">api-pro</option>
                <option value="deeplx">deeplx</option>
            </select>
        </div>
        <div class="OJBetter_setting_list">
            <label for='deepl_key'>
                <div style="display: flex;align-items: center;">
                    <span class="input_label" data-i18n="config:deepl.basic.key.label"></span>
                    <div class="help_tip">
                        ${helpCircleHTML}
                        <div class="tip_text" data-i18n="[html]config:deepl.basic.key.tipText"></div>
                    </div>
                </div>
            </label>
            <input type='text' id='deepl_key' class='no_default' placeholder='' require = true data-i18n="[placeholder]config:deepl.basic.key.placeholder">
        </div>
        <div class="OJBetter_setting_list">
            <label for='deepl_proxy'>
                <div style="display: flex;align-items: center;">
                    <span class="input_label" data-i18n="config:deepl.basic.proxy.label">Proxy API:</span>
                    <div class="help_tip">
                        ${helpCircleHTML}
                        <div class="tip_text" data-i18n="[html]config:deepl.basic.proxy.tipText"></div>
                    </div>
                </div>
            </label>
            <input type='text' id='deepl_proxy' placeholder='' require = false>
        </div>
        <hr>
        <details>
            <summary data-i18n="config:common.advanced.title"></summary>
            ${apiCustomConfigHTML('deepl')}
        </details>
        <details>
            <summary data-i18n="config:common.quota.title"></summary>
            ${apiQuotaConfigHTML('deepl')}
        </details>
        <button id='tempConfig_save' data-i18n="common:save"></button>
    </div>
    </dialog>
`;

const chatgptConfigEditHTML = `
    <dialog class='OJBetter_setting_menu' id='config_edit_menu'>
    <div class='OJBetter_setting_content'>
        <div class="tool-box">
            <button class='ojb_btn ojb_btn_popover top btn-close'>
                <i class="iconfont">&#xe614;</i>
            </button>
        </div>
        <h4 data-i18n="config:chatgpt.title"></h4>
        <h5 data-i18n="config:chatgpt.basic.title"></h5>
        <hr>
        <div class="OJBetter_setting_list">
            <label for='name'>
                <span class="input_label" data-i18n="config:chatgpt.basic.name.label"></span>
            </label>
            <input type='text' id='name' class='no_default' placeholder='' require = true data-i18n="[placeholder]config:chatgpt.basic.name.placeholder">
        </div>
        <div class="OJBetter_setting_list">
            <label for='chatgpt_model'>
                <div style="display: flex;align-items: center;">
                    <span class="input_label" data-i18n="[html]config:chatgpt.basic.model.label"></span>
                    <div class="help_tip">
                        ${helpCircleHTML}
                        <div class="tip_text" data-i18n="[html]config:chatgpt.basic.model.tipText"></div>
                    </div>
                </div>
            </label>
            <input type='text' id='chatgpt_model' placeholder='gpt-3.5-turbo' require = false>
        </div>
        <div class="OJBetter_setting_list">
            <label for='chatgpt_key'>
                <div style="display: flex;align-items: center;">
                    <span class="input_label" data-i18n="config:chatgpt.basic.key.label"></span>
                    <div class="help_tip">
                        ${helpCircleHTML}
                        <div class="tip_text" data-i18n="[html]config:chatgpt.basic.key.tipText"></div>
                    </div>
                </div>
            </label>
            <input type='text' id='chatgpt_key' class='no_default' placeholder='' require = true data-i18n="[placeholder]config:chatgpt.basic.key.placeholder">
        </div>
        <div class="OJBetter_setting_list">
            <label for='chatgpt_proxy'>
                <div style="display: flex;align-items: center;">
                    <span class="input_label" data-i18n="config:chatgpt.basic.proxy.label">Proxy API:</span>
                    <div class="help_tip">
                        ${helpCircleHTML}
                        <div class="tip_text" data-i18n="[html]config:chatgpt.basic.proxy.tipText"></div>
                    </div>
                </div>
            </label>
            <input type='text' id='chatgpt_proxy' placeholder='https://api.openai.com/v1/chat/completions' require = false>
        </div>
        <hr>
        <details>
            <summary data-i18n="config:common.advanced.title"></summary>
            ${apiCustomConfigHTML('chatgpt')}
        </details>
        <details>
            <summary data-i18n="config:common.quota.title"></summary>
            ${apiQuotaConfigHTML('chatgpt')}
        </details>
        <button id='tempConfig_save' data-i18n="common:save"></button>
    </div>
    </dialog>
`;

const CompletConfigEditHTML = `
    <dialog class='OJBetter_setting_menu' id='config_edit_menu'>
    <div class='OJBetter_setting_content'>
        <div class="tool-box">
            <button class='ojb_btn ojb_btn_popover top btn-close'>
                <i class="iconfont">&#xe614;</i>
            </button>
        </div>
        <h4 data-i18n="config:complet.title"></h4>
        <hr>
        <div class="OJBetter_setting_list">
            <label for='name'>
                <span class="input_label" data-i18n="config:complet.name.label"></span>
            </label>
            <input type='text' id='name' class='no_default' placeholder='' require = true  data-i18n="[placeholder]config:complet.name.placeholder">
        </div>
        <div class='OJBetter_setting_list'>
            <label for="complet_isChoose"><span id="loaded_span" data-i18n="config:complet.choose.label"></span></label>
            <input type="checkbox" id="complet_isChoose" name="complet_isChoose" require = false>
        </div>
        <div class='OJBetter_setting_list'>
            <label for="complet_genre" style="display: flex;" data-i18n="config:complet.genre.label"></label>
            <div class="help_tip">
                ${helpCircleHTML}
                <div class="tip_text" data-i18n="[html]config:complet.genre.tipText"></div>
            </div>
            <select id="complet_genre" name="complet_genre">
                <option value="monaco">monaco</option>
                <option value="ace">ace</option>
            </select>
        </div>
        <div class='OJBetter_setting_list'>
            <label for="complet_language" style="display: flex;" data-i18n="config:complet.language.label"></label>
            <select id="complet_language" name="complet_language">
                <option value="cpp">cpp</option>
                <option value="python">python</option>
                <option value="java">java</option>
                <option value="c">c</option>
            </select>
        </div>
        <div class="OJBetter_setting_list">
            <label for='complet_jsonUrl'>
                <div style="display: flex;align-items: center;">
                    <span class="input_label">JSON URL:</span>
                    <div class="help_tip">
                        ${helpCircleHTML}
                        <div class="tip_text" data-i18n="[html]config:complet.jsonurl.tipText"></div>
                    </div>
                </div>
            </label>
            <div class='OJBetter_setting_list alert_warn' data-i18n="[html]config:complet.jsonurl.alert"></div>
            <input type='text' id='complet_jsonUrl' class='no_default' placeholder='' require = true data-i18n="[placeholder]config:complet.jsonurl.placeholder">
        </div>
        <button id='tempConfig_save' data-i18n="common:save"></button>
    </div>
    </dialog>
`;

/**
 * 加载设置按钮面板
 */
async function initSettingsPanel() {
    /**
     * 添加右上角设置按钮
     * @param {string} location 位置选择器
     * @param {string} method 插入方法
     */
    function insertOJBetterSettingButton(location, method) {
        $(location)[method](`<button class='ojb_btn OJBetter_setting'>
        ${OJBetter.state.name} ${i18next.t('settings', { ns: 'common' })}</button>`);
    }

    /**
     * ============================================
     * 该网站插入设置按钮的位置和方式
     */
    insertOJBetterSettingButton(".lang-chooser", "before");
    insertOJBetterSettingButton(".enter-or-register-box", "after");
    if (OJBetter.typeOfPage.is_completeProblemset) insertOJBetterSettingButton(".lang", "before");
    /**
     * ============================================
     */

    const $settingBtns = $(".OJBetter_setting");
    $settingBtns.click(() => {
        $settingBtns.prop("disabled", true).addClass("open");

        // 设置面板div
        const settingMenu = OJB_safeCreateJQElement(OJBetterSettingMenu_HTML);
        $("body").append(settingMenu);

        elementLocalize(settingMenu); // 加载i18n
        OJB_showModal(settingMenu);
        OJB_addDraggable($('#OJBetter_setting_menu')); // 窗口支持拖拽

        // help帮助悬浮窗位置更新
        $(document).on('mouseenter', '.help-icon', function (event) {
            var menuOffset = $('.OJBetter_setting_menu:last').offset();
            var mouseX = event.pageX - menuOffset.left;
            var mouseY = event.pageY - menuOffset.top;

            $('.tip_text').css({
                'top': mouseY + 'px',
                'left': mouseX + 'px'
            });
        });

        // 选项卡切换
        $('.OJBetter_setting_sidebar a').click(function (event) {
            event.preventDefault();
            $('.OJBetter_setting_sidebar a').removeClass('active');
            $('.settings-page').removeClass('active');
            $(this).addClass('active');
            const targetPageId = $(this).attr('href').substring(1);
            $('#' + targetPageId).addClass('active');
        });

        /**
         * 更新单选按钮组的可用状态
         * @param {string} selector 单选按钮组的选择器
         * @param {string} targetLanguage 目标语言
         * @param {Object} translationSupport 翻译支持的语言对应表
         */
        const updateRadioButtonsAvailability = (selector, targetLanguage) => {
            Object.entries(OJBetter.supportList.translationSupport).forEach(([service, languages]) => {
                const radioButton = $(selector).find(`input[value="${service}"]`);
                const isEnabled = languages[targetLanguage];
                $(radioButton).prop('disabled', !isEnabled);
                if (!isEnabled) {
                    $(radioButton).prop('checked', false);
                }
            });
        };

        /**
         * 检查下拉框选中项是否有效,若无效则清空
         * @param {string} selector 下拉框的选择器
         */
        const validateSelectOption = (selector) => {
            const selectedValue = $(selector).val();
            if (!selectedValue) {
                $(selector).val('');
            }
        };

        /**
         * 更新翻译目标语言下拉框的可用状态
         * @param {string} selector 下拉框的选择器
         * @param {string} targetLanguage 目标语言
         * @param {Object} translationSupport 翻译支持的语言对应表
         */
        const updateSelectOptionsAvailability = (selector, targetLanguage) => {
            $(selector).children('option').each(function () {
                const optionValue = $(this).val();
                const isEnabled = OJBetter.supportList.translationSupport[optionValue] ? OJBetter.supportList.translationSupport[optionValue][targetLanguage] : true;
                $(this).prop('disabled', !isEnabled);
            });
            validateSelectOption(selector);
        };

        /**
         * 更新翻译服务复选框的可用状态
         * @param {string} selector 复选框的选择器
         * @param {string} targetLanguage 目标语言
         * @param {Object} translationSupport 翻译支持的语言对应表
         */
        const updateCheckboxesAvailability = (selector, targetLanguage) => {
            $(selector).children('input').each(function () {
                const checkboxValue = $(this).val();
                const isEnabled = OJBetter.supportList.translationSupport[checkboxValue][targetLanguage];
                $(this).prop('disabled', !isEnabled);
                if (!isEnabled) {
                    $(this).prop('checked', false);
                }
            });
        };

        /**
         * 更新更新源下拉框的可用状态
         * @param {string} selector 下拉框的选择器
         * @param {string} targetLanguage 目标语言
         * @param {Object} translationSupport 翻译支持的语言对应表
         */
        const updateUpdateSourceSelectOptionsAvailability = (selector, updateChannel) => {
            $(selector).children('option').each(function () {
                const optionValue = $(this).val();
                const isEnabled = OJBetter.supportList.updateSourceSupportList[optionValue][updateChannel];
                $(this).prop('disabled', !isEnabled);
            });
            validateSelectOption(selector);
        };

        /**
         * 创建配置结构
         * @param {string} type - 该字段的在表单中的类型
         * @param {string} value - 在配置中的键值
         * @param {boolean} require - 是否是表单的必填项
         * @param {string} [check=""] check - 调用的合法性检查
         */
        function createStructure(type, value, require, check = "") {
            return { type, value, require, check };
        }

        // deepl配置
        const deeplStructure = {
            '#name': createStructure('text', 'name', true),
            '#deepl_apiGenre': createStructure('text', 'apiGenre', true),
            '#deepl_key': createStructure('text', 'key', false),
            '#deepl_proxy': createStructure('text', 'proxy', false),
            '#deepl_header': createStructure('text', '_header', false, 'keyValuePairs'),
            '#deepl_data': createStructure('text', '_data', false, 'keyValuePairs'),
            '#deepl_quota_url': createStructure('text', 'quota_url', false),
            '#deepl_quota_method': createStructure('text', 'quota_method', false),
            '#deepl_quota_header': createStructure('text', 'quota_header', false, 'keyValuePairs'),
            '#deepl_quota_data': createStructure('text', 'quota_data', false, 'keyValuePairs'),
            '#deepl_quota_surplus': createStructure('text', 'quota_surplus', false, 'dotSeparatedPath'),
        };
        let tempConfig_deepl = GM_getValue('deepl_config'); // 获取配置信息
        const configManager_deepl = new ConfigManager('#deepl_config', 'deepl_config_', tempConfig_deepl, deeplStructure, deeplConfigEditHTML);
        configManager_deepl.registerChoiceChange();

        // chatgpt配置
        const chatgptStructure = {
            '#name': createStructure('text', 'name', true),
            '#chatgpt_model': createStructure('text', 'model', false),
            '#chatgpt_key': createStructure('text', 'key', true),
            '#chatgpt_proxy': createStructure('text', 'proxy', false),
            '#chatgpt_header': createStructure('text', '_header', false, 'keyValuePairs'),
            '#chatgpt_data': createStructure('text', '_data', false, 'keyValuePairs'),
            '#chatgpt_quota_url': createStructure('text', 'quota_url', false),
            '#chatgpt_quota_header': createStructure('text', 'quota_header', false, 'keyValuePairs'),
            '#chatgpt_quota_data': createStructure('text', 'quota_data', false, 'keyValuePairs'),
            '#chatgpt_quota_surplus': createStructure('text', 'quota_surplus', false, 'dotSeparatedPath'),
            '#chatgpt_quota_method': createStructure('text', 'quota_method', false),
        };
        let tempConfig_chatgpt = GM_getValue('chatgpt_config'); // 获取配置信息
        const configManager_chatgpt = new ConfigManager('#chatgpt_config', 'chatgpt_config_', tempConfig_chatgpt, chatgptStructure, chatgptConfigEditHTML);
        configManager_chatgpt.registerChoiceChange();

        // Complet配置
        const CompletStructure = {
            '#name': createStructure('text', 'name', true),
            '#complet_isChoose': createStructure('checkbox', 'isChoose', true),
            '#complet_genre': createStructure('text', 'genre', true),
            '#complet_language': createStructure('text', 'language', true),
            '#complet_jsonUrl': createStructure('text', 'jsonUrl', true)
        };
        let tempConfig_Complet = GM_getValue('Complet_config'); // 获取配置信息
        const configManager_complet = new ConfigManager('#Complet_config', 'complet_config_', tempConfig_Complet, CompletStructure, CompletConfigEditHTML, false);

        // 状态更新
        $("input[name='darkMode'][value='" + OJBetter.basic.darkMode + "']").prop("checked", true);
        $("#showLoading").prop("checked", GM_getValue("showLoading") === true);
        $("#expandFoldingblocks").prop("checked", GM_getValue("expandFoldingblocks") === true);
        $("#renderPerfOpt").prop("checked", GM_getValue("renderPerfOpt") === true);
        $("#selectElementPerfOpt").prop("checked", GM_getValue("selectElementPerfOpt") === true);
        $("#commentPaging").prop("checked", GM_getValue("commentPaging") === true);
        $("#standingsRecolor").prop("checked", GM_getValue("standingsRecolor") === true);
        $("#hiddenProblemTag").prop("checked", GM_getValue("hiddenProblemTag") === true);
        $("#showJumpToLuogu").prop("checked", GM_getValue("showJumpToLuogu") === true);
        $("#showCF2vjudge").prop("checked", GM_getValue("showCF2vjudge") === true);
        $("#hoverTargetAreaDisplay").prop("checked", GM_getValue("hoverTargetAreaDisplay") === true);
        $("#showClistRating_contest").prop("checked", GM_getValue("showClistRating_contest") === true);
        $("#showClistRating_problemset").prop("checked", GM_getValue("showClistRating_problemset") === true);
        $("#showClistRating_problem").prop("checked", GM_getValue("showClistRating_problem") === true);
        $("#RatingHidden").prop("checked", GM_getValue("RatingHidden") === true);
        $('#scriptL10nLanguage').val(GM_getValue("scriptL10nLanguage"));
        $('#localizationLanguage').val(GM_getValue("localizationLanguage"));
        $("input[name='translation'][value='" + OJBetter.translation.choice + "']").prop("checked", true);
        $("input[name='translation']").css("color", "gray");
        $('#deepl_type').val(GM_getValue("deepl_type"));
        $("#deepl_config_config_bar_ul").find(`input[name='deepl_config_config_item'][value='${tempConfig_deepl.choice}']`).prop("checked", true);
        $('#enableEmphasisProtection').prop("checked", GM_getValue("enableEmphasisProtection") === true);
        $('#enableLinkProtection').prop("checked", GM_getValue("enableLinkProtection") === true);
        $("#chatgpt_config_config_bar_ul").find(`input[name='chatgpt_config_config_item'][value='${tempConfig_chatgpt.choice}']`).prop("checked", true);
        $("#openai_isStream").prop("checked", GM_getValue("openai_isStream") === true);
        $("#openai_asSystemPrompt").prop("checked", GM_getValue("openai_asSystemPrompt") === true);
        $('#openai_customPrompt').val(GM_getValue("openai_customPrompt"));
        $('#comment_translation_choice').val(GM_getValue("commentTranslationChoice"));
        $('#iconButtonSize').val(GM_getValue("iconButtonSize"));
        $('#judgeStatusReplaceText').val(GM_getValue("judgeStatusReplaceText"));
        $("#autoTranslation").prop("checked", GM_getValue("autoTranslation") === true);
        $('#shortTextLength').val(GM_getValue("shortTextLength"));
        $("#allowMixTrans").prop("checked", GM_getValue("allowMixTrans") === true);
        $('.OJBetter_checkboxs').find('input[type="checkbox"][name="mixedTranslation"]').each(function () {
            if (OJBetter.translation.auto.mixTrans.servers.indexOf($(this).val()) > -1) {
                $(this).prop('checked', true);
            }
        });
        // 翻译目标语言下拉框
        $('#transTargetLang').change(function () {
            var selectedLang = $(this).val();
            updateRadioButtonsAvailability('#translationServices', selectedLang);
            updateSelectOptionsAvailability('#comment_translation_choice', selectedLang);
            updateCheckboxesAvailability('.OJBetter_checkboxs', selectedLang);
        });
        $('#transTargetLang').val(GM_getValue("transTargetLang"));
        $('#transTargetLang').change();
        //
        $('#comment_translation_mode').val(GM_getValue("commentTranslationMode"));
        $("#memoryTranslateHistory").prop("checked", GM_getValue("memoryTranslateHistory") === true);
        $('#transWaitTime').val(GM_getValue("transWaitTime"));
        $('#translation_replaceSymbol').val(GM_getValue("replaceSymbol"));
        $("#filterTextWithoutEmphasis").prop("checked", GM_getValue("filterTextWithoutEmphasis") === true);
        $("#forceTurndownConversion").prop("checked", GM_getValue("forceTurndownConversion") === true);
        $('#translation_retransAction').val(GM_getValue("retransAction"));
        $("#clist_Authorization").val(GM_getValue("clist_Authorization"));
        $("#problemPageCodeEditor").prop("checked", GM_getValue("problemPageCodeEditor") === true);
        $("#beautifyPreBlocks").prop("checked", GM_getValue("beautifyPreBlocks") === true);
        $("#isCodeSubmitConfirm").prop("checked", GM_getValue("isCodeSubmitConfirm") === true);
        $("#autoSubmitAfterPass").prop("checked", GM_getValue("autoSubmitAfterPass") === true);
        $("#alwaysConsumeMouseWheel").prop("checked", GM_getValue("alwaysConsumeMouseWheel") === true);
        $("#autoMemoryCode").prop("checked", GM_getValue("autoMemoryCode") === true);
        $("#submitButtonPosition").val(GM_getValue("submitButtonPosition"));
        $("#cppCodeTemplateComplete").prop("checked", GM_getValue("cppCodeTemplateComplete") === true);
        $("#useLSP").prop("checked", GM_getValue("useLSP") === true);
        $("#OJBetter_Bridge_WorkUri").val(GM_getValue("OJBetter_Bridge_WorkUri"));
        $("#OJBetter_Bridge_SocketUrl").val(GM_getValue("OJBetter_Bridge_SocketUrl"));
        $("input[name='compiler'][value='" + OJBetter.monaco.onlineCompilerChoice + "']").prop("checked", true);
        $("input[name='compiler']").css("color", "gray");
        // 调试
        $("#notWaiteLoaded").prop("checked", GM_getValue("notWaiteLoaded") === true);
        $('#l10n_refreshScrpitCacheButton').click(clearI18nextCache);
        $('#l10n_web_refreshScrpitCacheButton').click(clearWebsiteL10nData);
        $("#isRuleMarkingEnabled").prop("checked", GM_getValue("isRuleMarkingEnabled") === true);
        $('#indexedDB_clearButton').click(async () => { await clearDatabase(); });
        $('#indexedDB_exportButton').click(async () => { downloadDataAsFile(await exportDatabase(), 'database_export.json') });
        $('#indexedDB_importButton').click(() => { readFileInput(async (fileContent) => { await importDatabase(fileContent); }); });
        $('#configuration_clearButton').click(deleteAllConfigSettings);
        $('#configuration_exportButton').click(() => { downloadDataAsFile(exportSettingsToJSON(), 'configuration_export.json') });
        $('#configuration_importButton').click(() => { readFileInput((fileContent) => { importSettingsFromJSON(fileContent); }) });
        // 关于
        $('#updateChannel').val(GM_getValue("updateChannel"));
        $('#updateSource').val(GM_getValue("updateSource"));
        $('#updateChannel').change(function () {
            var selectedLang = $(this).val();
            updateUpdateSourceSelectOptionsAvailability('#updateSource', selectedLang);
            if (selectedLang == "dev") $('#thanksforDevChannelNotice').show();
            else $('#thanksforDevChannelNotice').hide();
        });
        $('#updateChannel').change();

        // 关闭
        const $settingMenu = $(".OJBetter_setting_menu");
        $settingMenu.on("click", ".btn-close", async () => {
            // 设置的数据
            const settings = {
                darkMode: $("input[name='darkMode']:checked").val(),
                showLoading: $("#showLoading").prop("checked"),
                hoverTargetAreaDisplay: $("#hoverTargetAreaDisplay").prop("checked"),
                expandFoldingblocks: $("#expandFoldingblocks").prop("checked"),
                renderPerfOpt: $("#renderPerfOpt").prop("checked"),
                selectElementPerfOpt: $("#selectElementPerfOpt").prop("checked"),
                commentPaging: $("#commentPaging").prop("checked"),
                standingsRecolor: $("#standingsRecolor").prop("checked"),
                hiddenProblemTag: $("#hiddenProblemTag").prop("checked"),
                showJumpToLuogu: $("#showJumpToLuogu").prop("checked"),
                showCF2vjudge: $("#showCF2vjudge").prop("checked"),
                scriptL10nLanguage: $('#scriptL10nLanguage').val(),
                localizationLanguage: $('#localizationLanguage').val(),
                transTargetLang: $('#transTargetLang').val(),
                translation: $("input[name='translation']:checked").val(),
                deepl_type: $('#deepl_type').val(),
                enableEmphasisProtection: $("#enableEmphasisProtection").prop("checked"),
                enableLinkProtection: $("#enableLinkProtection").prop("checked"),
                openai_isStream: $("#openai_isStream").prop("checked"),
                openai_asSystemPrompt: $("#openai_asSystemPrompt").prop("checked"),
                openai_customPrompt: $('#openai_customPrompt').val(),
                commentTranslationChoice: $('#comment_translation_choice').val(),
                iconButtonSize: $('#iconButtonSize').val(),
                judgeStatusReplaceText: $('#judgeStatusReplaceText').val(),
                autoTranslation: $("#autoTranslation").prop("checked"),
                shortTextLength: $('#shortTextLength').val(),
                allowMixTrans: $("#allowMixTrans").prop("checked"),
                mixedTranslation: (() => {
                    let mixedTranslation = [];
                    $('.OJBetter_checkboxs').find('input[type="checkbox"][name="mixedTranslation"]').each(function () {
                        if ($(this).is(":checked")) {
                            mixedTranslation.push($(this).val());
                        }
                    });
                    return mixedTranslation;
                })(),
                commentTranslationMode: $('#comment_translation_mode').val(),
                memoryTranslateHistory: $('#memoryTranslateHistory').prop("checked"),
                transWaitTime: $('#transWaitTime').val(),
                replaceSymbol: $('#translation_replaceSymbol').val(),
                filterTextWithoutEmphasis: $('#filterTextWithoutEmphasis').prop("checked"),
                forceTurndownConversion: $('#forceTurndownConversion').prop("checked"),
                retransAction: $('#translation_retransAction').val(),
                showClistRating_contest: $('#showClistRating_contest').prop("checked"),
                showClistRating_problemset: $('#showClistRating_problemset').prop("checked"),
                showClistRating_problem: $('#showClistRating_problem').prop("checked"),
                RatingHidden: $('#RatingHidden').prop("checked"),
                clist_Authorization: $('#clist_Authorization').val(),
                problemPageCodeEditor: $("#problemPageCodeEditor").prop("checked"),
                beautifyPreBlocks: $("#beautifyPreBlocks").prop("checked"),
                isCodeSubmitConfirm: $("#isCodeSubmitConfirm").prop("checked"),
                autoSubmitAfterPass: $("#autoSubmitAfterPass").prop("checked"),
                alwaysConsumeMouseWheel: $("#alwaysConsumeMouseWheel").prop("checked"),
                autoMemoryCode: $("#autoMemoryCode").prop("checked"),
                submitButtonPosition: $('#submitButtonPosition').val(),
                cppCodeTemplateComplete: $("#cppCodeTemplateComplete").prop("checked"),
                useLSP: $("#useLSP").prop("checked"),
                OJBetter_Bridge_WorkUri: $('#OJBetter_Bridge_WorkUri').val().replace(/\\/g, '/').replace(/\/$/, ''),
                OJBetter_Bridge_SocketUrl: $('#OJBetter_Bridge_SocketUrl').val(),
                onlineCompilerChoice: $("input[name='compiler']:checked").val(),
                notWaiteLoaded: $("#notWaiteLoaded").prop("checked"),
                isRuleMarkingEnabled: $("#isRuleMarkingEnabled").prop("checked"),
                updateChannel: $('#updateChannel').val(),
                updateSource: $('#updateSource').val()
            };
            // tempConfigs的数据
            const tempConfigs = {
                'deepl_config': configManager_deepl.getTempConfig(),
                'chatgpt_config': configManager_chatgpt.getTempConfig(),
                'Complet_config': configManager_complet.getTempConfig()
            }

            // 判断是否改变
            let changes = {};
            const combinedConfigs = Object.assign({}, settings, tempConfigs); // 合并settings和tempConfigs对象
            for (const [key, value] of Object.entries(combinedConfigs)) {
                const storedValue = GM_getValue(key);
                if (!OJB_deepEquals(value, storedValue)) {
                    changes[key] = { oldValue: storedValue, newValue: value };
                }
            }

            // 如果changes对象不为空,则有变化
            if (Object.keys(changes).length > 0) {
                console.log("Changes detected:", changes);
                const shouldSave = await OJB_createDialog(
                    i18next.t('saveSetting.title', { ns: 'dialog' }),
                    i18next.t('saveSetting.content', { ns: 'dialog' }),
                    [
                        i18next.t('saveSetting.buttons.0', { ns: 'dialog' }),
                        i18next.t('saveSetting.buttons.1', { ns: 'dialog' })
                    ]
                ); // 配置改变保存确认
                if (shouldSave) {
                    // 数据校验
                    // TODO
                    if (settings.deepl_type !== 'free') {
                        let selectedIndex = $('input[name="deepl_config_config_item"]:checked').length > 0;
                        if (!selectedIndex) {
                            $('.deepl_config a').removeClass('active');
                            $('.settings-page').removeClass('active');
                            $('#sidebar-translation-settings').addClass('active');
                            $('#translation-settings').addClass('active');

                            $('#deepl_config').addClass('missing');
                            return;
                        } else {
                            $('#deepl_config').removeClass('missing');
                        }
                    }
                    if (settings.translation === "openai") {
                        let selectedIndex = $('input[name="chatgpt_config_config_item"]:checked').length > 0;
                        if (!selectedIndex) {
                            $('.chatgpt_config a').removeClass('active');
                            $('.settings-page').removeClass('active');
                            $('#sidebar-translation-settings').addClass('active');
                            $('#translation-settings').addClass('active');

                            $('#chatgpt_config').addClass('missing');
                            return;
                        } else {
                            $('#chatgpt_config').removeClass('missing');
                        }
                    }
                    {
                        let selectedIndex = $('input[name="translation"]:checked').length > 0;
                        if (!selectedIndex) {
                            $('.OJBetter_setting_sidebar a').removeClass('active');
                            $('.settings-page').removeClass('active');
                            $('#sidebar-translation-settings').addClass('active');
                            $('#translation-settings').addClass('active');

                            $('#translationServices').addClass('missing');
                            return;
                        } else {
                            $('#translationServices').removeClass('missing');
                        }
                    }

                    // 保存数据
                    let refreshPage = false; // 是否需要刷新页面
                    for (const [key, value] of Object.entries(settings)) {
                        if (!refreshPage && !(key == 'translation' || key == 'darkMode' || key == 'commentTranslationChoice')) {
                            if (GM_getValue(key) != value) refreshPage = true;
                        }
                        GM_setValue(key, value);
                    }
                    for (const [key, value] of Object.entries(tempConfigs)) {
                        if (!refreshPage && (JSON.stringify(GM_getValue(key)) != JSON.stringify(value))) refreshPage = true;
                        GM_setValue(key, value);
                    }

                    if (refreshPage) location.reload();
                    else {
                        // 切换黑暗模式
                        if (OJBetter.basic.darkMode != settings.darkMode) {
                            OJBetter.basic.darkMode = settings.darkMode;
                            // 移除旧的事件监听器
                            changeEventListeners.forEach(listener => {
                                mediaQueryList.removeEventListener('change', listener);
                            });

                            if (OJBetter.basic.darkMode == "follow") {
                                changeEventListeners.push(handleColorSchemeChange);
                                mediaQueryList.addEventListener('change', handleColorSchemeChange);
                                $('html').removeAttr('data-theme');
                            } else if (OJBetter.basic.darkMode == "dark") {
                                $('html').attr('data-theme', 'dark');
                                if (OJBetter.monaco.editor) {
                                    monaco.editor.setTheme('vs-dark');
                                }
                            } else {
                                $('html').attr('data-theme', 'light');
                                if (OJBetter.monaco.editor) {
                                    monaco.editor.setTheme('vs');
                                }
                                // 移除旧的事件监听器
                                const mediaQueryList = window.matchMedia('(prefers-color-scheme: dark)');
                                window.matchMedia('(prefers-color-scheme: dark)');
                            }
                        }
                        // 更新配置信息
                        OJBetter.translation.choice = settings.translation;
                        OJBetter.translation.comment.choice = settings.commentTranslationChoice;
                    }
                }
            }
            OJB_closeAndRemoveModal(settingMenu);
            $settingBtns.prop("disabled", false).removeClass("open");
        });
    });
};

/**
 * 初始化html2markdown转换器
 */
async function initHTML2MarkDown() {
    OJBetter.common.turndownService = new TurndownService({ bulletListMarker: '-' });

    // 保留原始
    OJBetter.common.turndownService.keep(['del']);

    // 丢弃
    OJBetter.common.turndownService.addRule('remove-by-class', {
        filter: function (node) {
            return node.classList.contains('sample-tests') ||
                node.classList.contains('header') ||
                node.classList.contains('overlay') ||
                node.classList.contains('html2md-panel') ||
                node.classList.contains('likeForm') ||
                node.classList.contains('monaco-editor') ||
                node.nodeName === 'SCRIPT';
        },
        replacement: function (content, node) {
            return "";
        }
    });
    OJBetter.common.turndownService.addRule('remove-script', {
        filter: function (node, options) {
            return node.tagName.toLowerCase() == "script" && node.type.startsWith("math/tex");
        },
        replacement: function (content, node) {
            return "";
        }
    });

    // inline math
    OJBetter.common.turndownService.addRule('inline-math', {
        filter: function (node, options) {
            return node.tagName.toLowerCase() == "span" && node.className == "MathJax";
        },
        replacement: function (content, node) {
            var latex = $(node).next().text();
            latex = latex.replace(/</g, "&lt;").replace(/>/g, "&gt;");
            return "$" + latex + "$";
        }
    });

    // block math
    OJBetter.common.turndownService.addRule('block-math', {
        filter: function (node, options) {
            return node.tagName.toLowerCase() == "div" && node.className == "MathJax_Display";
        },
        replacement: function (content, node) {
            var latex = $(node).next().text();
            latex = latex.replace(/</g, "&lt;").replace(/>/g, "&gt;");
            return "\n$$\n" + latex + "\n$$\n";
        }
    });

    // texFontStyle
    OJBetter.common.turndownService.addRule('texFontStyle', {
        filter: function (node) {
            return (
                node.nodeName === 'SPAN' &&
                node.classList.contains('tex-font-style-bf')
            )
        },
        replacement: function (content) {
            return '**' + content + '**'
        }
    })

    // sectionTitle
    OJBetter.common.turndownService.addRule('sectionTitle', {
        filter: function (node) {
            return (
                node.nodeName === 'DIV' &&
                node.classList.contains('section-title')
            )
        },
        replacement: function (content) {
            return '**' + content + '**'
        }
    })

    // property-title
    OJBetter.common.turndownService.addRule('property-title', {
        filter: function (node) {
            return (
                node.nodeName === 'DIV' &&
                node.classList.contains('property-title')
            )
        },
        replacement: function (content) {
            return content + ': '
        }
    })

    // pre
    OJBetter.common.turndownService.addRule('pre', {
        filter: function (node, options) {
            return node.tagName.toLowerCase() == "pre";
        },
        replacement: function (content, node) {
            if (!!node.querySelector('code.prettyprint')) {
                return "";
            } else {
                return "```\n" + content + "```\n";
            }
        }
    });

    // bordertable
    OJBetter.common.turndownService.addRule('bordertable', {
        filter: 'table',
        replacement: function (content, node) {
            if (node.classList.contains('bordertable')) {
                var output = [],
                    thead = '',
                    trs = node.querySelectorAll('tr');
                if (trs.length > 0) {
                    var ths = trs[0].querySelectorAll('td,th');
                    if (ths.length > 0) {
                        thead = '| ' + Array.from(ths).map(th => OJBetter.common.turndownService.turndown(th.innerHTML.trim())).join(' | ') + ' |\n'
                            + '| ' + Array.from(ths).map(() => ' --- ').join('|') + ' |\n';
                    }
                }
                var rows = node.querySelectorAll('tr');
                Array.from(rows).forEach(function (row, i) {
                    if (i > 0) {
                        var cells = row.querySelectorAll('td,th');
                        var trow = '| ' + Array.from(cells).map(cell => OJBetter.common.turndownService.turndown(cell.innerHTML.trim())).join(' | ') + ' |';
                        output.push(trow);
                    }
                });
                return thead + output.join('\n');
            } else {
                return content;
            }
        }
    });
};

/**
 * 任务队列
 */
class TaskQueue {
    constructor() {
        this.taskQueues = {};
        this.isProcessing = {}; // 处理状态
        this.delays = {}; // 等待时间(毫秒)
    }

    getDelay(type) {
        if (type === 'openai') {
            return 0;
        } else {
            return OJBetter.translation.waitTime;
        }
    }

    /**
     * 添加任务
     * @param {string} type 任务类型
     * @param {function} fn 任务函数
     * @param {boolean} isNonQueueTask 是否为非队列任务
     */
    addTask(type, fn, isNonQueueTask = false) {
        if (!this.taskQueues[type]) {
            this.taskQueues[type] = [];
        }

        if (isNonQueueTask) {
            fn();
        } else {
            this.taskQueues[type].push(fn);

            if (!this.isProcessing[type]) {
                this.processQueue(type);
            }
        }
    }

    async processQueue(type) {
        this.isProcessing[type] = true;

        while (this.taskQueues[type].length > 0) {
            const task = this.taskQueues[type].shift();
            await task();

            if (this.taskQueues[type].length > 0) {
                await this.wait(this.getDelay(type));
            }
        }

        this.isProcessing[type] = false;
    }

    wait(delay) {
        return new Promise(resolve => {
            setTimeout(resolve, delay);
        });
    }
}

/**
 * 检测为空文本
 * @param {string} text 待检测的文本
 * @returns {boolean} 是否为空文本
 */
const isEmptyText = text => text.trim() === '';

/**
 * 加载按钮相关函数
 */
async function initButtonFunc() {
    // 鼠标悬浮时为目标元素区域添加一个覆盖层
    $.fn.addHoverOverlay = function (target) {
        let position = $(target).css('position');
        let display = $(target).css('display');

        this.hover(() => {
            $(target)
                .addClass('overlay')
                .css('position', 'relative');
            if (display == "inline" || display == "contents") {
                $(target).css('display', 'block');
            }
        }, () => {
            $(target)
                .removeClass('overlay')
                .css('position', position);
            if (display == "inline" || display == "contents") {
                $(target).css('display', display);
            }
        })
    }

    /**
     * 为按钮设置图标
     * @param {string} icon 图标
     * @returns {JQuery<HTMLElement>} 按钮
     */
    $.fn.setButtonIcon = function (icon) {
        let i = this.find("i");
        if (i.length != 0 && i.hasClass("iconfont")) {
            i.html(icon);
        } else {
            i = OJB_safeCreateJQElement(`<i>${icon}</i>`);
            this.prepend(i);
        }
        return this;
    }

    /**
     * 设置按钮为加载等待状态
     */
    $.fn.setButtonLoading = function () {
        this.addClass("loading");
        this.prop("disabled", true);
        return this;
    }

    /**
     * 解除按钮的加载等待状态
     */
    $.fn.setButtonLoaded = function () {
        this.removeClass("loading");
        this.prop("disabled", false);
        return this;
    }

    /**
     * 为按钮设置popover提示文本
     * @param {string} text 文本
     * @returns {JQuery<HTMLElement>} 按钮
     */
    $.fn.setButtonPopover = function (text) {
        // find if has popover_content class element
        let popover_content = this.find(".popover_content");
        if (popover_content.length != 0) {
            popover_content.text(text);
        } else {
            popover_content = OJB_safeCreateJQElement(`<span class="popover_content">${text}</span>`);
            this.append(popover_content);
        }
        return this;
    }

    /**
     * 获取MarkDown
     * @returns {string} MarkDown
     */
    $.fn.getMarkdown = function () {
        const markdown = this.data('markdown');
        if (markdown === undefined) {
            const htmlContent = this.html();
            const newMarkdown = OJBetter.common.turndownService.turndown(htmlContent);
            this.data('markdown', newMarkdown);
            return newMarkdown;
        }
        return markdown;
    }

    // 设置按钮状态
    $.fn.setButtonState = function (state, popoverText = null, disabled = false) {
        this.data('buttonState', state)
            .prop('disabled', disabled)
            .css('cursor', disabled ? 'not-allowed' : 'pointer')
            .removeClass('running success enabled error loading redo');
        if (popoverText) this.setButtonPopover(popoverText);

        if (state !== 'initial') this.addClass(state);
        return this;
    };

    // 为按钮添加鼠标悬浮重试
    $.fn.setHoverRedo = function () {
        this.hover(() => {
            prevState = this.getButtonState();
            if (prevState !== "normal" && prevState !== "running") {
                this.setButtonState('redo');
            }
        }, () => {
            const currentState = this.getButtonState();
            if (prevState !== "normal" && ["normal", "redo"].includes(currentState)) {
                this.setButtonState(prevState);
                prevState = null;
            }
        });
    };

    // 获取按钮状态
    $.fn.getButtonState = function () {
        return this.data('buttonState') || 'normal';
    };

    // 设置翻译按钮状态
    $.fn.setTransButtonState = function (state, text = null) {
        const popoverText = text || i18next.t(`trans.${state}`, { ns: 'button' });
        const disabled = state === 'running' || state === 'loading';
        this.setButtonState(state, popoverText, disabled);
        return this;
    };

    // 存翻译结果
    $.fn.pushResultToTransButton = function (result) {
        let resultStack = this.data('resultStack');
        if (!resultStack) resultStack = [];
        resultStack.push(result);
        this.data('resultStack', resultStack);
    }

    // 获取翻译结果
    $.fn.getResultFromTransButton = function () {
        return this.data('resultStack');
    }

    // 标记为不自动翻译
    $.fn.setNotAutoTranslate = function () {
        this.data('notAutoTranslate', true);
    }

    // 获取是否为不自动翻译
    $.fn.getNotAutoTranslate = function () {
        return this.data('notAutoTranslate');
    }

    // 判断是否已经翻译
    $.fn.IsTranslated = function () {
        if (this.hasAttr('translated')) {
            return true;
        } else {
            return false;
        }
    }

    // 判断是否为评论区按钮
    $.fn.IsCommentButton = function () {
        let isCommentButton = this.data('isCommentButton');
        if (isCommentButton == undefined) {
            this.parents('.comments').length > 0 ? isCommentButton = true : isCommentButton = false;
            this.data('isCommentButton', isCommentButton);
        }
        return isCommentButton;
    }

    // 按钮点击效果
    $(document).on('mousedown', '.ojb_btn', function () {
        $(this).addClass('active').on('animationend', () => $(this).removeClass('active'));
    });
}

/**
 * 添加题目markdown转换/复制/翻译按钮面板
 * @param {HTMLElement} element 需要添加按钮面板的元素
 * @param {string} suffix 按钮面板id后缀
 * @param {string} type 按钮面板添加位置
 * @param {boolean} is_simple 是否是简单模式
 * @returns {object} 返回按钮面板元素
 */
function addButtonPanel(element, suffix, type, is_simple = false) {
    let text;
    if (OJBetter.translation.comment.transMode == "1") text = i18next.t('trans.segment', { ns: 'button' });
    else if (OJBetter.translation.comment.transMode == "2") text = i18next.t('trans.select', { ns: 'button' });
    else text = i18next.t('trans.normal', { ns: 'button' });

    let panel = OJB_safeCreateJQElement(`<div class='html2md-panel input-output-copier ${is_simple ? 'is_simple' : ''}'></div>`);
    let viewButton = OJB_safeCreateJQElement(`
        <button class='ojb_btn ojb_btn_popover top' id='html2md-view${suffix}'>
            <i class="iconfont">&#xe7e5;</i>
            <span class="popover_content">${i18next.t('md.normal', { ns: 'button' })}</span>
        </button>`);
    let copyButton = OJB_safeCreateJQElement(`
        <button class='ojb_btn ojb_btn_popover top' id='html2md-cb${suffix}'>
            <i class="iconfont">&#xe608;</i>
            <span class="popover_content">${i18next.t('copy.normal', { ns: 'button' })}</span>
        </button>`);
    let translateButton = OJB_safeCreateJQElement(`
        <button class='ojb_btn translateButton ojb_btn_popover top' id='translateButton${suffix}'>
            <i class="iconfont">&#xe6be;</i>
            <span class="popover_content">${text}</span>
        </button>`);
    if (!is_simple) panel.append(viewButton);
    if (!is_simple) panel.append(copyButton);
    panel.append(translateButton);
    if (type === "this_level") {
        $(element).before(panel);
    } else if (type === "child_level") {
        $(element).prepend(panel);
    }

    return {
        panel: panel,
        viewButton: viewButton,
        copyButton: copyButton,
        translateButton: translateButton
    }
}

/**
 * 添加MD视图按钮
 * @param {JQuery<HTMLElement>} button 按钮
 * @param {JQuery<HTMLElement>} element 目标元素
 * @param {string} suffix id后缀
 * @param {string} type 类型
 * @returns {void}
 */
async function addButtonWithHTML2MD(button, element, suffix, type) {
    /**
     * 改变按钮状态
     * @param {string} state 状态
     */
    const changeButtonState = (state) => {
        if (state == "loading") {
            button.setButtonLoading();
            button.setButtonPopover(i18next.t('state.waitMathJax', { ns: 'button' }));
        } else if (state == "loaded") {
            button.setButtonLoaded();
            button.setButtonPopover(i18next.t('md.normal', { ns: 'button' }));
        } else if (state == "normal") {
            button.removeClass("enabled");
            button.setButtonPopover(i18next.t('md.normal', { ns: 'button' }));
        } else if (state == "mdView") {
            button.addClass("enabled");
            button.setButtonPopover(i18next.t('md.reduction', { ns: 'button' }));
        } else if (state == "disabled") {
            button.prop("disabled", true);
            button.setButtonPopover(i18next.t('md.disabled', { ns: 'button' }));
        }
    }

    /**
     * 存放目标元素的 JQueryObject
     */
    const target = (() => {
        if (type = "child_level") {
            return $(element).children(':not(.html2md-panel)');
        } else {
            return $(element);
        }
    })();

    if (OJBetter.typeOfPage.is_oldLatex || OJBetter.typeOfPage.is_acmsguru) {
        changeButtonState("disabled");
        return;
    } else {
        changeButtonState("loading");
        await waitForMathJaxIdle();
        changeButtonState("loaded");
    }

    button.click(OJB_debounce(function () {
        /**
         * 检查是否是MarkDown视图
         * @returns {boolean} 是否是MarkDown视图
         */
        function checkViewmd() {
            if ($(element).attr("viewmd") === "true") {
                return true;
            } else {
                return false;
            }
        }

        /**
         * 设置是否是MarkDown视图
         * @param {boolean} value 是否是MarkDown视图
         * @returns {void}
         */
        function setViewmd(value) {
            $(element).attr("viewmd", value);
            if (value) {
                changeButtonState("mdView");
            } else {
                changeButtonState("normal");
            }
        }

        if (checkViewmd()) {
            setViewmd(false);
            target.last().next(".mdViewContent").remove();
            target.show();
        } else {
            setViewmd(true);
            const markdown = $(element).getMarkdown();
            const mdViewContent = OJB_safeCreateJQElement(`<span class="mdViewContent" style="width:auto; height:auto;">${markdown}</span>`);
            target.last().after(mdViewContent);
            target.hide();
        }
    }));

    if (OJBetter.preference.hoverTargetAreaDisplay && !OJBetter.typeOfPage.is_oldLatex && !OJBetter.typeOfPage.is_acmsguru) {
        button.addHoverOverlay($(element));
    }
}

/**
 * 添加复制按钮
 * @param {JQuery<HTMLElement>} button 按钮
 * @param {JQuery<HTMLElement>} element 目标元素
 * @param {string} suffix 后缀
 * @param {string} type 类型
 */
async function addButtonWithCopy(button, element, suffix, type) {
    /**
     * 改变按钮状态
     * @param {string} state 状态
     */
    function changeButtonState(state) {
        if (state == "loading") {
            button.setButtonLoading();
            button.setButtonPopover(i18next.t('state.waitMathJax', { ns: 'button' }));
        } else if (state == "loaded") {
            button.setButtonLoaded();
            button.setButtonPopover(i18next.t('copy.normal', { ns: 'button' }));
        } else if (state == "normal") {
            button.setButtonPopover(i18next.t('copy.normal', { ns: 'button' }));
        } else if (state == "copied") {
            button.setButtonPopover(i18next.t('copy.copied', { ns: 'button' }));
        } else if (state == "disabled") {
            button.prop("disabled", true);
            button.setButtonPopover(i18next.t('copy.disabled', { ns: 'button' }));
        }
    }

    // 等待MathJax队列完成
    if (OJBetter.typeOfPage.is_oldLatex || OJBetter.typeOfPage.is_acmsguru) {
        changeButtonState("disabled");
        return;
    } else {
        changeButtonState("loading");
        await waitForMathJaxIdle();
        changeButtonState("loaded");
    }

    button.click(OJB_debounce(function () {
        var target = $(element).get(0);

        var markdown = $(element).getMarkdown();

        GM_setClipboard(markdown);

        $(this).addClass("success");
        changeButtonState("copied");


        // 更新复制按钮文本
        setTimeout(() => {
            $(this).removeClass("success");
            changeButtonState("normal")
        }, 2000);
    }));

    if (OJBetter.preference.hoverTargetAreaDisplay && !OJBetter.typeOfPage.is_oldLatex && !OJBetter.typeOfPage.is_acmsguru) {
        button.addHoverOverlay($(element));
    }
}

/**
 * 添加翻译按钮
 * @param {JQuery<HTMLElement>} button 按钮
 * @param {JQuery<HTMLElement>} element 目标元素
 * @param {string} suffix 后缀
 * @param {string} type 类型
 * @param {boolean} is_comment 是否是评论
 */
async function addButtonWithTranslation(button, element, suffix, type, is_comment = false) {
    /**
     * 添加可指定翻译服务的方法调用
     * @param {string} translation 翻译服务
     */
    button.data("translatedItBy", function (translation) {
        button.setTransButtonState('running', i18next.t('trans.wait', { ns: 'button' }));
        OJBetter.common.taskQueue.addTask(translation, () => transTask(button, element, type, is_comment, translation), translation == 'openai');
    });

    // 等待MathJax队列完成
    button.setButtonLoading();
    await waitForMathJaxIdle();
    button.setButtonLoaded();

    // 标记目标文本区域不自动翻译
    {
        let text;
        if ((OJBetter.typeOfPage.is_oldLatex || OJBetter.typeOfPage.is_acmsguru) && (!OJBetter.translation.forceTurndownConversion)) {
            text = $(element).html();
        } else {
            text = $(element).getMarkdown();
        }
        let length = text.length;
        if (length > OJBetter.translation.auto.shortTextLength || isEmptyText(text) || $(element).find('.spoiler').length > 0) {
            button.setNotAutoTranslate();
        }
        // button.after(`<span>${length}</span>`); // 显示字符数
    }

    button.click(OJB_debounce(async function () {
        // 重新翻译
        let resultStack = $(this).getResultFromTransButton();
        if (resultStack) {
            let pElements = $(element).find("p.block_selected:not(li p), li.block_selected");
            for (let item of resultStack) {
                if (OJBetter.translation.retransAction == "0") {
                    // 选段翻译不直接移除旧结果
                    if (OJBetter.translation.comment.transMode == "2") {
                        // 只移除即将要翻译的段的结果
                        if (pElements.is(item.translateDiv.getDiv().prev())) {
                            item.translateDiv.close();
                        }
                    } else {
                        item.translateDiv.close();
                        $($(element)).find(".translate-problem-statement, .translate-problem-statement-panel").remove();
                    }
                } else {
                    item.translateDiv.fold();
                }
            }
        }

        // 翻译
        button.setTransButtonState('running', i18next.t('trans.wait', { ns: 'button' }));
        OJBetter.common.taskQueue.addTask(OJBetter.translation.choice, () => transTask(button, element, type, is_comment), OJBetter.translation.choice == 'openai');
    }));

    // 重新翻译提示
    let prevState;
    button.hover(() => {
        prevState = button.getButtonState();
        if (prevState !== "normal" && prevState !== "running") {
            button.setTransButtonState('redo');
        }
    }, () => {
        const currentState = button.getButtonState();
        if (prevState !== "normal" && ["normal", "redo"].includes(currentState)) {
            button.setTransButtonState(prevState);
            prevState = null;
        }
    });

    // 目标区域指示
    if (OJBetter.preference.hoverTargetAreaDisplay) {
        button.addHoverOverlay($(element));
    }

    // 翻译右键切换菜单
    $(document).on('contextmenu', '#translateButton' + suffix, function (e) {
        e.preventDefault();

        // 是否为评论的翻译
        let is_comment = button.IsCommentButton();

        // 移除旧的
        if (!$(e.target).closest('.OJBetter_contextmenu').length) {
            $('.OJBetter_contextmenu').remove();
        }

        var menu = $('<div class="OJBetter_contextmenu"></div>');
        var translations = [
            { value: 'deepl', name: i18next.t('translation.options.services.deepl', { ns: 'settings' }) },
            { value: 'iflyrec', name: i18next.t('translation.options.services.iflyrec', { ns: 'settings' }) },
            { value: 'youdao', name: i18next.t('translation.options.services.youdao', { ns: 'settings' }) },
            { value: 'google', name: i18next.t('translation.options.services.google', { ns: 'settings' }) },
            { value: 'caiyun', name: i18next.t('translation.options.services.caiyun', { ns: 'settings' }) },
            { value: 'openai', name: i18next.t('translation.options.services.openai.name', { ns: 'settings' }) }
        ];

        // Function to check if the service supports the target language
        function supportsTargetLanguage(service, targetLang) {
            return OJBetter.supportList.translationSupport[service] && OJBetter.supportList.translationSupport[service][targetLang] !== undefined;
        }

        if (is_comment) {
            var label = OJB_safeCreateJQElement(`<label><input type="radio" name="translation" value="0">
            <span class="OJBetter_contextmenu_label_text">
            ${i18next.t('translation.preference.comment_translation_choice.services.follow', { ns: 'settings' })}
            </span></label>`);
            menu.append(label);
        }
        translations.forEach(function (translation) {
            if (supportsTargetLanguage(translation.value, OJBetter.translation.targetLang)) {
                var label = OJB_safeCreateJQElement(`<label><input type="radio" name="translation" value="${translation.value}">
                <span class="OJBetter_contextmenu_label_text">${translation.name}</span></label>`);
                menu.append(label);
            }
        });

        // 初始化
        if (is_comment) {
            menu.find(`input[name="translation"][value="${OJBetter.translation.comment.choice}"]`).prop('checked', true);
        } else {
            menu.find(`input[name="translation"][value="${OJBetter.translation.choice}"]`).prop('checked', true);
        }
        menu.css({
            top: e.pageY + 'px',
            left: e.pageX + 'px'
        }).appendTo('body');

        $(document).one('change', 'input[name="translation"]', function () {
            if (is_comment) {
                OJBetter.translation.comment.choice = $('input[name="translation"]:checked').val();
                GM_setValue("commentTranslationChoice", OJBetter.translation.comment.choice);
            } else {
                OJBetter.translation.choice = $('input[name="translation"]:checked').val();
                GM_setValue("translation", OJBetter.translation.choice);
            }
            $('.OJBetter_contextmenu').remove();
        });

        // 点击区域外关闭菜单
        function handleClick(event) {
            if (!$(event.target).closest('.OJBetter_contextmenu').length) {
                $('.OJBetter_contextmenu').remove();
                $(document).off('change', 'input[name="translation"]');
            } else {
                $(document).one('click', handleClick);
            }
        }
        $(document).one('click', handleClick);
    });
}

/**
 * 创建翻译任务
 * @param {JQuery<HTMLElement>} button 按钮
 * @param {HTMLElement} element 目标元素
 * @param {string} type 类型
 * @param {boolean} is_comment 是否是评论
 * @param {string} overrideTrans 覆盖全局翻译服务设定
 */
async function transTask(button, element, type, is_comment, overrideTrans) {
    /** @type {HTMLElement} 目标元素 */
    let target;
    /**
     * 错误计数数据结构
     * @typedef {Object} count
     * @property {number} errerNum 错误数量
     * @property {number} skipNum 跳过数量
     */
    const count = {
        errerNum: 0,
        skipNum: 0
    };
    if (OJBetter.translation.comment.transMode == "1") {
        // 分段翻译
        let pElements = $(element).find("p:not(li p), li, .OJBetter_acmsguru");
        for (let i = 0; i < pElements.length; i++) {
            target = $(pElements[i]).eq(0).clone();
            element_node = pElements[i];
            await process(button, target, element_node, type, is_comment, count, overrideTrans);
        }
    } else if (OJBetter.translation.comment.transMode == "2") {
        // 选段翻译
        let pElements = $(element).find("p.block_selected:not(li p), li.block_selected, .OJBetter_acmsguru");
        for (let i = 0; i < pElements.length; i++) {
            target = $(pElements[i]).eq(0).clone();
            element_node = pElements[i];
            await process(button, target, element_node, type, is_comment, count, overrideTrans);
        }
        $(element).find("p.block_selected:not(li p), li.block_selected").removeClass('block_selected');
    } else {
        // 普通翻译
        target = $(element).eq(0).clone();
        if (type === "child_level") $(target).children(':first').remove();
        element_node = $($(element)).get(0);
        await process(button, target, element_node, type, is_comment, count, overrideTrans);
    }

    // 翻译完成
    if (!count.errerNum && !count.skipNum) {
        button.setTransButtonState('success');
    }
}

/**
 * 翻译处理
 * @param {JQuery<HTMLElement>} button 按钮
 * @param {HTMLElement} target 目标元素
 * @param {HTMLElement} element_node 目标节点
 * @param {string} type 类型
 * @param {boolean} is_comment 是否是评论
 * @param {string} overrideTrans 覆盖全局翻译服务设定
 */
async function process(button, target, element_node, type, is_comment, count, overrideTrans) {
    if (type === "child_level") {
        let div = $("<div>");
        $(element_node).append(div);
        element_node = div.get(0);
    }

    //是否跳过折叠块
    if ($(target).find('.spoiler').length > 0) {
        const shouldSkip = await OJB_createDialog(
            i18next.t('skipFold.title', { ns: 'dialog' }),
            i18next.t('skipFold.content', { ns: 'dialog' }),
            [
                i18next.t('skipFold.buttons.0', { ns: 'dialog' }),
                i18next.t('skipFold.buttons.1', { ns: 'dialog' })
            ],
            true
        ); //跳过折叠块确认
        if (shouldSkip) {
            $(target).find('.spoiler').remove();
        } else {
            $(target).find('.html2md-panel').remove();
        }
    }

    // 等待并获取结果
    button.setTransButtonState('running');
    const result = await blockProcessing(button, target, element_node, is_comment, overrideTrans);
    button.pushResultToTransButton(result);

    if (result.status == "error") count.errerNum += 1;
    else if (result.status == "skip") count.skipNum += 1;
    $(target).remove();
}

/**
 * 块处理
 * @param {JQuery<HTMLElement>} button
 * @param {HTMLElement} target 目标元素
 * @param {HTMLElement} element_node 目标节点
 * @param {boolean} is_comment 是否是评论
 * @param {string} overrideTrans 覆盖全局翻译服务设定
 * @returns {TranslateResult} 翻译结果对象
 */
async function blockProcessing(button, target, element_node, is_comment, overrideTrans) {
    if ((OJBetter.typeOfPage.is_oldLatex || OJBetter.typeOfPage.is_acmsguru) && (!OJBetter.translation.forceTurndownConversion)) {
        target.markdown = $(target).html();
    } else if (!target.markdown) {
        target.markdown = OJBetter.common.turndownService.turndown($(target).html());
    }

    const result = await translateProblemStatement(target.markdown, element_node, is_comment, overrideTrans);
    if (result.status == "skip") {
        button.setTransButtonState('error', i18next.t('trans.tooLong', { ns: 'button' }));
        result.translateDiv.close();
    } else if (result.status == "error" || !result.rawData.done) {
        result.translateDiv.setError();
        result.translateDiv.setRawData(result.rawData);
        result.translateDiv.showDebugButton();
        button.setTransButtonState('error', i18next.t('trans.error', { ns: 'button' }));
        $(target).remove();
    }
    return result;
}

/**
 * 选段翻译支持
 */
async function multiChoiceTranslation() {
    GM_addStyle(`
        .topic .content .ttypography {
            overflow: initial;
        }
    `);

    $(document).on('click', 'p, li:not(:has(.comment)), .OJBetter_acmsguru', function (e) {
        let $this = $(this);
        e.stopPropagation();
        if ($this.hasClass('block_selected')) {
            $this.removeClass('block_selected');
            // 移除对应的按钮
            $('.OJBetter_MiniTranslateButton').remove("#translateButton_selected_" + $this.attr('OJBetter_p_id'));
        } else {
            let id = OJB_getRandomNumber(8);
            $this.attr('OJBetter_p_id', id);
            $this.addClass('block_selected');
            // 添加按钮
            let menu = OJB_safeCreateJQElement(`<div class="OJBetter_MiniTranslateButton" id='translateButton_selected_${id}'>${translateIcon}</div>`)
                .css({
                    left: $($this).outerWidth(true) + $($this).position().left + 10 + 'px',
                });
            $this.before(menu);

            $("#translateButton_selected_" + id).click(async function () {
                // 处理旧的结果
                if ($this.attr('translated')) {
                    let result = $this.data("resultData");
                    if (OJBetter.translation.retransAction == "0") {
                        result.translateDiv.close();
                    } else {
                        result.translateDiv.fold();
                    }
                }
                // 翻译
                let target = $this.eq(0).clone();
                let result = await blockProcessing(OJBetter.translation.choice, target, $this.eq(0), $("#translateButton_selected_" + id), false);
                $this.data("resultData", result);
                $this.removeClass('block_selected');
                // 移除对应的按钮
                $('.OJBetter_MiniTranslateButton').remove("#translateButton_selected_" + id);
                $this.attr('translated', '1'); // 标记已翻译
            });
        }
    });
}

/**
 * 为acmsguru题面重新划分div
 */
async function acmsguruReblock() {
    if (OJBetter.translation.comment.transMode == '0') {
        // 普通模式下的划分方式
        var html = $('.ttypography').children().html();
        var separator = /(<div align="left" style="margin-top: 1\.0em;"><b>.*?<\/b><\/div>)/g;
        var result = html.split(separator); // 分割代码
        var outputHtml = '';
        var header = '';
        for (var i = 0; i < result.length; i++) {
            if (separator.test(result[i])) {
                header = result[i];
                continue;
            }
            outputHtml += '<div class="ttypography">' + header + result[i] + '</div>';
            header = '';
        }
        $('.ttypography').html(outputHtml);
    }
    else {
        // 分段/选段模式下的划分方式
        $('.ttypography').children().each(function () {
            var html = $(this).html();
            var replacedHtml = html.replace(/(?:<\/div>|<br><br>)(?<text>[\s\S]+?)(?=<br><br>)/g,
                '<div align="left" class="OJBetter_acmsguru" >$<text></div>');
            $(this).html(replacedHtml);
        });
    }
}

/**
 * 添加MD/复制/翻译按钮
 */
async function addConversionButton() {
    let promises = []; // 用于收集所有的 Promise

    // 题目页添加按钮
    if (OJBetter.typeOfPage.is_problem) {
        let exContentsPageClasses = ["sample-tests"];
        $('.problem-statement').children('div').each((i, e) => {
            var className = $(e).attr('class');
            if (!exContentsPageClasses.includes(className)) {
                var id = "_problem_" + OJB_getRandomNumber(8);
                let panel = addButtonPanel(e, id, "this_level");
                promises.push(addButtonWithHTML2MD(panel.viewButton, e, id, "this_level"));
                promises.push(addButtonWithCopy(panel.copyButton, e, id, "this_level"));
                promises.push(addButtonWithTranslation(panel.translateButton, e, id, "this_level"));
                if (i == 0) panel.translateButton.setNotAutoTranslate(); // 题目标题块跳过,不自动翻译
            }
        });
    }
    // 添加按钮到ttypography部分
    $(".ttypography").each((i, e) => {
        // 是否为评论
        let is_comment = false;
        if ($(e).parents('.comments').length > 0) is_comment = true;
        // 题目页不添加
        if (!OJBetter.typeOfPage.is_problem || OJBetter.typeOfPage.is_acmsguru) {
            let id = "_ttypography_" + OJB_getRandomNumber(8);
            let panel = addButtonPanel(e, id, "this_level");
            promises.push(addButtonWithHTML2MD(panel.viewButton, e, id, "this_level"));
            promises.push(addButtonWithCopy(panel.copyButton, e, id, "this_level"));
            promises.push(addButtonWithTranslation(panel.translateButton, e, id, "this_level", is_comment));
        }
    });

    // 完整题目集页特殊处理
    if (OJBetter.typeOfPage.is_completeProblemset) {
        let exContentsPageClasses = ["sample-tests"];
        $('.problem-statement').each(function () {
            $(this).children('div').each((i, e) => {
                var className = $(e).attr('class');
                if (!exContentsPageClasses.includes(className)) {
                    var id = "_problem_" + OJB_getRandomNumber(8);
                    let panel = addButtonPanel(e, id, "this_level");
                    promises.push(addButtonWithHTML2MD(panel.viewButton, e, id, "this_level"));
                    promises.push(addButtonWithCopy(panel.copyButton, e, id, "this_level"));
                    promises.push(addButtonWithTranslation(panel.translateButton, e, id, "this_level"));
                    if (i == 0) panel.translateButton.setNotAutoTranslate(); // 题目标题块跳过,不自动翻译
                }
            });
        });
    }

    // 添加按钮到spoiler部分
    $('.spoiler-content').each((i, e) => {
        if ($(e).find('.html2md-panel').length === 0) {
            const id = "_spoiler_" + OJB_getRandomNumber(8);
            const panel = addButtonPanel(e, id, "child_level");
            promises.push(addButtonWithHTML2MD(panel.viewButton, e, id, "child_level"));
            promises.push(addButtonWithCopy(panel.copyButton, e, id, "child_level"));
            promises.push(addButtonWithTranslation(panel.translateButton, e, id, "child_level"));
        }
    });

    // 添加按钮到比赛QA部分
    $(".question-response").each((i, e) => {
        let id = "_question_" + OJB_getRandomNumber(8);
        let panel = addButtonPanel(e, id, "this_level", true);
        promises.push(addButtonWithTranslation(panel.translateButton, e, id, "this_level"));
    });
    if (OJBetter.typeOfPage.is_mSite) {
        $("div._ProblemsPage_announcements table tbody tr:gt(0)").each((i, e) => {
            var $nextDiv = $(e).find("td:first");
            let id = "_question_" + OJB_getRandomNumber(8);
            let panel = addButtonPanel($nextDiv, id, "this_level", true);
            promises.push(addButtonWithTranslation(panel.translateButton, $nextDiv, id, "this_level"));
        });
    }

    // 添加按钮到弹窗confirm-proto部分
    $(".confirm-proto").each((i, e) => {
        let id = "_titled_" + OJB_getRandomNumber(8);
        var $nextDiv = $(e).children().get(0);
        let panel = addButtonPanel($nextDiv, id, "this_level", true);
        promises.push(addButtonWithTranslation(panel.translateButton, $nextDiv, id, "this_level"));
    });

    // 添加按钮到_CatalogHistorySidebarFrame_item部分
    $("._CatalogHistorySidebarFrame_item").each((i, e) => {
        let id = "_history_sidebar_" + OJB_getRandomNumber(8);
        let panel = addButtonPanel(e, id, "this_level", true);
        promises.push(addButtonWithTranslation(panel.translateButton, e, id, "this_level"));
    });

    $(".problem-lock-link").on("click", function () {
        $(".popup .content div").each((i, e) => {
            let id = "_popup_" + OJB_getRandomNumber(8);
            let panel = addButtonPanel(e, id, "this_level", true);
            promises.push(addButtonWithTranslation(panel.translateButton, e, id, "this_level"));
        });
    });

    // 添加按钮到弹窗alert部分
    $(".alert:not(.OJBetter_alert)").each((i, e) => {
        let id = "_alert_" + OJB_getRandomNumber(8);
        let panel = addButtonPanel(e, id, "child_level", true);
        promises.push(addButtonWithTranslation(panel.translateButton, e, id, "child_level"));
    });

    // 添加按钮到talk-text部分
    $(".talk-text").each((i, e) => {
        let id = "_talk-text_" + OJB_getRandomNumber(8);
        let panel = addButtonPanel(e, id, "child_level", true);
        promises.push(addButtonWithTranslation(panel.translateButton, e, id, "child_level"));
    });

    return Promise.all(promises).catch(error => {
        console.error("One or more of the Add Button operations failed: ", error);
    });
};

/**
 * 等待LaTeX渲染队列全部完成
 * @returns {Promise} 完成渲染
 */
function waitForMathJaxIdle() {
    return new Promise((resolve, reject) => {
        // 检查MathJax对象是否存在
        const checkMathJaxExists = () => {
            if (typeof MathJax === 'undefined') {
                // 如果MathJax不存在,稍后再次检查
                OJB_delay(100).then(checkMathJaxExists);
            } else {
                // MathJax存在,开始监视渲染队列
                startMonitoringQueue();
            }
        };

        // 开始监视MathJax渲染队列
        const startMonitoringQueue = () => {
            const intervalId = setInterval(() => {
                const queue = MathJax.Hub.queue;
                if (queue.pending === 0 && queue.running === 0) {
                    clearInterval(intervalId);
                    resolve();
                }
            }, 100);
        };

        // 开始检查MathJax对象
        checkMathJaxExists();
    });
}

/**
 * 翻译结果面板
 */
class TranslateDiv {
    /**
     * 构造函数
     * @param {string} id 指定翻译框的id
     */
    constructor(id) {
        this.id = id;
        this.div = $('<div>').attr('id', id).addClass('translateDiv bounce-in');
        if (!OJBetter.typeOfPage.is_completeProblemset) {
            this.div.addClass('input-output-copier');
        }
        this.panelDiv = $('<div>').addClass('translate-problem-statement-panel');
        this.div.append(this.panelDiv);

        // 主要信息
        this.mainDiv = $('<div>').addClass('translate-problem-statement');
        this.span = $('<span>');
        this.mainDiv.append(this.span);
        this.div.append(this.mainDiv);
        this.mainDivState = {
            current: 'transHTML',
            transHTML: '',
            rawDataHTML: ''
        };

        // 顶栏信息
        this.topText = $('<div>').addClass('topText');
        this.panelDiv.append(this.topText);

        // 右侧
        this.rightDiv = $('<div>').css('display', 'flex');
        this.panelDiv.append(this.rightDiv);
        this.debugButton = OJB_safeCreateJQElement(`
        <button class='ojb_btn ojb_btn_popover top'>
            <i class="iconfont">&#xe641;</i>
            <span class="popover_content">${i18next.t('rawData.normal', { ns: 'button' })}</span>
        </button>`).hide();
        this.rightDiv.append(this.debugButton);
        this.queryBalanceButton = OJB_safeCreateJQElement(`
        <button class='ojb_btn ojb_btn_popover top'>
            <i class="iconfont">&#xe6ae;</i>
            <span class="popover_content">${i18next.t('queryBalance.normal', { ns: 'button' })}</span>
        </button>`).hide();
        this.rightDiv.append(this.queryBalanceButton);
        this.copyButton = OJB_safeCreateJQElement(`
        <button class='ojb_btn ojb_btn_popover top'>
            <i class="iconfont">&#xe608;</i>
            <span class="popover_content">${i18next.t('copy.normal', { ns: 'button' })}</span>
        </button>`);
        this.rightDiv.append(this.copyButton);
        this.upButton = OJB_safeCreateJQElement(`
        <button class='ojb_btn ojb_btn_popover top'>
            <i class="iconfont">&#xe601;</i>
            <span class="popover_content">${i18next.t('fold.normal', { ns: 'button' })}</span>
        </button>`);
        this.rightDiv.append(this.upButton);
        this.closeButton = OJB_safeCreateJQElement(`
        <button class='ojb_btn ojb_btn_popover top'>
            <i class="iconfont">&#xe614;</i>
            <span class="popover_content">${i18next.t('close.normal', { ns: 'button' })}</span>
        </button>`);
        this.rightDiv.append(this.closeButton);
    }

    /**
     * 获取翻译框
     * @returns {JQuery<HTMLElement>} 返回翻译框
     */
    getDiv() {
        return this.div;
    }

    /**
     * 设置翻译框顶部的文本
     * @param {string} text 翻译框顶部的文本
     */
    setTopText(text) {
        this.div.attr("data-topText", text);
        this.topText.text(text);
    }

    /**
     * 获取翻译框顶部的文本
     * @returns {string} 返回翻译框顶部的文本
     */
    getTopText() {
        return this.topText.text();
    }

    /**
     * 渲染一个元素内的LaTeX公式
     * @param {HTMLElement} element 元素
     */
    renderLaTeX(element) {
        MathJax.Hub.Queue(["Typeset", MathJax.Hub, element]);
    }

    /**
     * 更新翻译框内容
     * @param {string} text 文本内容
     * @param {boolean} is_escapeHTML 是否转义HTML标签,为true则HTML标签将作为普通文本处理,默认为true
     * @param {boolean} is_renderLaTeX 是否渲染LaTeX,为true则会渲染LaTeX,默认为true
     */
    updateTranslateDiv(text, is_escapeHTML = true, is_renderLaTeX = true,) {
        // 渲染MarkDown
        let md = window.markdownit({
            html: !is_escapeHTML,
        });
        if (!text) text = "";
        let html = md.render(text);
        this.mainDiv.html(html);
        // 渲染Latex
        if (is_renderLaTeX) {
            // MathJax.Hub.Queue(["Typeset", MathJax.Hub, this.mainDiv.get(0)]);
            this.renderLaTeX(this.mainDiv.get(0));
        }
        // // 渲染代码块中的公式 (AtCoder)
        // this.mainDiv.find('pre code').each((index, element) => {
        //     const codeText = $(element).text();
        //     const latexPattern = /\$\$(\\.|[^\$])*?\$\$|\$(\\.|[^\$])*?\$/;
        //     if (latexPattern.test(codeText)) {
        //         this.renderLaTeX(element);
        //     }
        // });
    }

    /**
     * 关闭元素
     */
    close() {
        this.closeButton.click();
    }

    /**
     * 收起元素
     */
    fold() {
        if (!this.upButton.hasClass("reverse")) {
            this.upButton.click();
        }
    }

    /**
     * 注册收起按钮事件
     */
    registerUpButtonEvent() {
        this.upButton.on("click", () => {
            // 如果没有reverse类,说明是展开状态
            if (!this.upButton.hasClass("reverse")) {
                // 执行收起操作
                this.upButton.addClass("reverse");
                this.upButton.setButtonState('initial', i18next.t('fold.unfold', { ns: 'button' }));
                OJB_toggleCollapseExpand(this.mainDiv.get(0));
            } else {
                // 执行展开操作
                this.upButton.removeClass("reverse");
                this.upButton.setButtonState('initial', i18next.t('fold.normal', { ns: 'button' }));
                OJB_toggleCollapseExpand(this.mainDiv.get(0));
            }
        });
    }

    /**
     * 注册关闭按钮事件
     */
    registerCloseButtonEvent() {
        this.closeButton.on("click", () => {
            $(this.div).remove();
            $(this.panelDiv).remove();
            if (OJBetter.typeOfPage.is_problem && OJBetter.translation.memory.enabled) {
                OJBetter.translation.memory.ttTree.rmTransResultMap(this.id); // 移除ttTree中的数据
                OJBetter.translation.memory.ttTree.refreshNode(".ttypography");
                updateTransDBData(OJBetter.translation.memory.ttTree.getNodeData(), OJBetter.translation.memory.ttTree.getTransResultMap()); // 更新DB中的数据
            }
        });
    }

    /**
     * 注册复制按钮事件
     * @param {string} text 复制的文本
     */
    registerCopyButtonEvent(text) {
        this.copyButton.on("click", () => {
            GM_setClipboard(text);
            this.copyButton.setButtonState('success', i18next.t('copy.copied', { ns: 'button' }));
            // 复制提示
            setTimeout(() => {
                this.copyButton.setButtonState('initial', i18next.t('copy.normal', { ns: 'button' }));
            }, 2000);
        });
    }

    /**
     * 禁用复制按钮
     */
    disableCopyButton() {
        this.copyButton.css({ 'fill': '#ccc' });
        this.copyButton.off("click");
    }

    /**
     * 设置面板为error状态
     */
    setError() {
        this.div.addClass('error');
        this.panelDiv.addClass('error');
        this.mainDiv.addClass('error');
    }

    /**
     * 设置原始数据数据
     * @param {Object} Object 原始数据
     */
    setRawData(Object) {
        this.mainDivState.rawDataHTML = $("<pre>").text(JSON.stringify(Object, null, 4)).get(0);
        if (this.mainDivState.current === 'rawDataHTML') {
            this.renderMainDiv();
        }
    }

    /**
     * 切换结果面板与原始数据面板
     */
    switchMainDiv() {
        // 在切换之前,保存当前内容的状态
        this.mainDivState[this.mainDivState.current] = this.mainDiv.html();
        // 切换当前状态
        this.debugButton.setButtonState(this.mainDivState.current === 'transHTML' ? 'enabled' : 'initial');
        this.mainDivState.current = this.mainDivState.current === 'transHTML' ? 'rawDataHTML' : 'transHTML';
        // 渲染新的当前状态
        this.renderMainDiv();
    }

    // 渲染当前内容到 mainDiv
    renderMainDiv() {
        requestAnimationFrame(() => {
            this.mainDiv.html(this.mainDivState[this.mainDivState.current]);
        });
    }

    /**
     * 注册debug按钮事件
     */
    registerDebugButtonEvent() {
        this.debugButton.on("click", () => {
            this.switchMainDiv();
        });
    }

    /**
     * 显示debug按钮
     */
    showDebugButton() {
        this.debugButton.show();
        this.registerDebugButtonEvent();
    }

    /**
     * 注册查询余额按钮事件
     * @param {function} callback 查询回调函数
     */
    registerQueryBalanceButtonEvent(callback) {
        this.queryBalanceButton.on("click", async () => {
            this.queryBalanceButton.setButtonState('loading', i18next.t('queryBalance.loading', { ns: 'button' }));
            try {
                const balance = await callback();
                this.queryBalanceButton.setButtonState('success', `${i18next.t('queryBalance.success', { ns: 'button' })} ${balance}`);
            } catch (error) {
                this.queryBalanceButton.setButtonState('error', `${i18next.t('queryBalance.error', { ns: 'button' })} ${error.message}`);
            }
        });
    }

    /**
     * 显示余额查询按钮
     * @param {string} server 服务名称
     */
    showQueryBalanceButton(server) {
        if (server == 'deepl') {
            const quotaConfig = OJBetter.deepl.config.quota;
            if (quotaConfig.url && quotaConfig.surplus && quotaConfig.header) {
                this.queryBalanceButton.show();
                this.registerQueryBalanceButtonEvent(() => {
                    return queryServerBalance(OJBetter.deepl.config.quota);
                });
            }
        } else if (server == 'openai') {
            const quotaConfig = OJBetter.chatgpt.config.quota;
            if (quotaConfig.url && quotaConfig.surplus && quotaConfig.header) {
                this.queryBalanceButton.show();
                this.registerQueryBalanceButtonEvent(() => {
                    return queryServerBalance(OJBetter.chatgpt.config.quota);
                });
            }
        }
    }
}

// 元素关系树
class ElementsTree {
    constructor(elements) {
        this.node = [];
        this.transResultMap = {};
        this.index = 0;
        this.tagNames = ["DIV", "P", "UL", "LI"]
        this.init($(elements));
    }

    // Iterate through all elements, because there may be multiple ttypography
    init(elements) {
        elements.each((i, e) => {
            this.node.push({}); // add one element
            this.index = 0; // reset index
            this.create(i, $(e));
        });
    }

    // 刷新关系树
    refreshNode(elements) {
        this.node = [];
        this.index = 0;
        this.init($(elements));
    }

    // 创建节点间的关系树
    create(i_, element) {
        var prev = null;
        var node = this.node[i_];
        element.children().each((i, e) => {
            // only add element with tagNames
            if (this.tagNames.includes($(e).prop("tagName"))) {
                prev = this.addNode(i_, prev, e);
            }
            // recursively child element
            if ($(e).children().length > 0 && prev !== null) {
                node[prev].firstChild = this.index;
                this.create(i_, $(e));
            }
        });
    }

    // 向树中添加一个节点
    addNode(i_, prev, e) {
        let node = this.node[i_];
        node[this.index] = {
            prev: prev,
            next: null,
            firstChild: null,
            type: $(e).prop("tagName"),
            isTranslateDiv: $(e).hasClass("translateDiv"),
            topText: $(e).attr("data-topText"),
            id: $(e).attr("id"),
        };

        if (prev !== null) {
            node[prev].next = this.index;
        }

        prev = this.index;

        this.index++;
        return prev;
    }

    getNodeData() {
        return this.node;
    }

    setNodeData(node) {
        this.node = node;
    }

    getTransResultMap() {
        return this.transResultMap;
    }

    setTransResultMap(transResultMap) {
        this.transResultMap = transResultMap;
    }

    rmTransResultMap(id) {
        delete this.transResultMap[id];
    }

    addTransResultMap(id, text) {
        this.transResultMap[id] = text;
    }

    getTranslateDivNum(ttTree) {
        var num = 0;
        for (var i in ttTree) {
            if (ttTree[i].isTranslateDiv) {
                num++;
            }
        }
        return num;
    }

    // 恢复目标元素中的translateDiv
    recover(elements) {
        elements.each((i, e) => {
            var ttTreeNode = this.node[i];
            var missingTranslateDivs = this.getTranslateDivNum(ttTreeNode);
            if (missingTranslateDivs > 0) {
                this.recoverOneElement($(e), ttTreeNode);
            }
        });
    }

    recoverOneElement(element, ttTreeNode) {
        this.recoverOneFork(element.children().eq(0), ttTreeNode, 0);
    }

    // 恢复一个分支
    recoverOneFork(pElement, ttTreeNode, index) {
        do {
            // only recover element with tagNames
            if (!this.tagNames.includes(pElement.prop("tagName"))) {
                if (pElement.next().length > 0) {
                    pElement = pElement.next();
                } else {
                    return;
                }
            }
            if (!ttTreeNode[index] || pElement.prop("tagName") !== ttTreeNode[index].type) {
                // console.warn(`元素不存在或类型不同, 元素结构可能已经发生了变化: \nindex: ${index}`, pElement);
                return;
            } else {
                // recursively child element
                var node = ttTreeNode[index];
                if (node.firstChild !== null) {
                    this.recoverOneFork(
                        pElement.children().eq(0),
                        ttTreeNode,
                        node.firstChild
                    );
                }
                // check if next node is translateDiv
                if (node.next !== null) {
                    index = node.next;

                    var ne_node = ttTreeNode[index];
                    if (ne_node.isTranslateDiv) {
                        var id = ne_node.id;
                        var topText = ne_node.topText;
                        var text = this.transResultMap[id];
                        // create element after pElement
                        this.reCreateTransDiv(pElement, id, text, topText, node.isTranslateDiv); // 如果前面一个也是翻译结果,则该结果折叠
                    }
                    pElement = pElement.next(); // go to next element
                }
            }
        } while (node.next !== null);
    }

    /**
     * 重新创建translateDiv
     * @param {*} pElement
     * @param {*} id
     * @param {*} translatedText
     * @param {*} topText
     * @param {Boolean} isFold 是否折叠
     */
    reCreateTransDiv(pElement, id, translatedText, topText, isFold) {
        const translateDiv = new TranslateDiv(id);
        pElement.after(translateDiv.getDiv());
        translateDiv.setTopText(topText);
        translateDiv.registerUpButtonEvent();
        translateDiv.registerCloseButtonEvent();
        if (!OJBetter.typeOfPage.is_oldLatex && !OJBetter.typeOfPage.is_acmsguru) {
            translateDiv.registerCopyButtonEvent(translatedText);
        } else {
            translateDiv.disableCopyButton();
        }
        translateDiv.updateTranslateDiv(translatedText, !((OJBetter.typeOfPage.is_oldLatex || OJBetter.typeOfPage.is_acmsguru) && (!OJBetter.translation.forceTurndownConversion)));
        // 标记已翻译并添加到翻译按钮的结果栈中
        let transButton = pElement.prev('.html2md-panel').find('.translateButton');
        if (transButton.length == 0) {
            // 如果没有找到,则应该是得在父元素中找到
            transButton = pElement.parent().prev('.html2md-panel').find('.translateButton');
        }
        if (isFold) translateDiv.fold(); // 是否折叠该翻译
        transButton.pushResultToTransButton({
            translateDiv: translateDiv,
            status: 0
        });
        transButton.setTransButtonState('success');
    }
}

// 更新TransDB中的翻译数据
async function updateTransDBData(nodeDate, transResultMap) {
    var url = window.location.href.replace(/#/, "");
    try {
        await OJBetter.common.database.translateData.put({ url, transResultMap, nodeDate });
        return 'translateData saved successfully';
    } catch (error) {
        throw new Error(`Failed to save translateData: ${error}`);
    }
}

// 获取TransDB中保存的翻译数据
async function getTransDBData() {
    var url = window.location.href.replace(/#/, "");
    try {
        const result = await OJBetter.common.database.translateData.get(url);
        return result;
    } catch (error) {
        throw new Error(`Failed to get translateData: ${error}`);
    }
}

/**
 * 翻译结果恢复功能初始化
 * @returns
 */
async function initTransResultsRecover() {
    OJBetter.translation.memory.ttTree = new ElementsTree(".ttypography"); // 初始化当前页面.ttypography元素的结构树
    let result = await getTransDBData();
    if (!result) return;
    OJBetter.translation.memory.ttTree.setNodeData(result.nodeDate);
    OJBetter.translation.memory.ttTree.setTransResultMap(result.transResultMap);
    OJBetter.translation.memory.ttTree.recover($(".ttypography"));
}

/**
 * 自动翻译
 */
async function initTransWhenViewable() {
    await waitForMathJaxIdle();

    const elements = $('.ttypography, .comments').find('.translateButton');
    const observers = [];

    // Use a single Intersection Observer for all elements
    const observer = new IntersectionObserver((entries, obs) => {
        entries.forEach((entry) => {
            if (entry.isIntersecting) {
                const button = $(entry.target);
                const state = button.getButtonState();
                const notAutoTranslate = button.getNotAutoTranslate();
                // Check if the button meets the criteria
                if (state === 'normal' && !notAutoTranslate) {
                    let trans = OJBetter.translation.choice;

                    if (OJBetter.translation.auto.mixTrans.enabled && button.IsCommentButton() && OJBetter.translation.auto.mixTrans.servers.length > 0) {
                        const randomIndex = Math.floor(Math.random() * OJBetter.translation.auto.mixTrans.servers.length);
                        trans = OJBetter.translation.auto.mixTrans.servers[randomIndex];
                    }
                    button.data("translatedItBy")(trans);
                }

                // Stop observing the element
                obs.unobserve(entry.target);
            }
        });
    });

    // Observe each element
    elements.each((i, e) => {
        observer.observe(e);
    });

    // Store the observer in case you need to disconnect it later
    observers.push(observer);
}

/**
 * 翻译返回结果结构体
 * @typedef {Object} TranslateResult
 * @property {string} status 翻译状态
 * @property {TranslateDiv} translateDiv 翻译结果面板
 * @property {TransRawData} rawData 原始翻译数据
 */

/**
 * 翻译主方法
 * @param {string} text 待翻译文本
 * @param {HTMLElement} element_node 元素节点
 * @param {Boolean} is_comment 是否为评论区文本
 * @param {string} overrideTrans 覆盖全局翻译服务设定
 * @returns {TranslateResult} 翻译结果对象
 */
async function translateProblemStatement(text, element_node, is_comment, overrideTrans) {
    /** @type {number} 翻译结果的ID*/
    const id = OJB_getRandomNumber(8);
    /** @type {TextBlockReplacer} 文本块替换/恢复实例*/
    const textBlockReplacer = new TextBlockReplacer();
    /** @type {string} 翻译结果文本*/
    let translatedText = "";

    /** @type {string} 当前实际应用的翻译服务 */
    const realTransServer = overrideTrans ||
        (is_comment && OJBetter.translation.comment.choice != "0" ?
            OJBetter.translation.comment.choice :
            OJBetter.translation.choice);

    /** @type {TranslateResult} 翻译结果对象 */
    const translateResult = {
        status: "ok",
        rawData: {
            done: false
        }
    }

    /**
     * LaTeX替换
     * @param {string} text 待翻译文本
     * @returns {string} 处理后的文本
     */
    const replaceLatex = function (text) {
        if (OJBetter.typeOfPage.is_oldLatex) {
            const regex = /<span\s+class="tex-span">.*?<\/span>/gi;
            text = textBlockReplacer.replace(text, regex);
            text = text.replace(/<p>(.*?)<\/p>/g, "$1\n\n"); // <p/>标签换为换行
        } else if (OJBetter.typeOfPage.is_acmsguru) {
            const regex = /<i>.*?<\/i>|<sub>.*?<\/sub>|<sup>.*?<\/sup>|<pre>.*?<\/pre>/gi; // TODO 111
            text = textBlockReplacer.replace(text, regex);
        } else if (realTransServer != "openai") {
            // 使用GPT翻译时不必替换latex公式
            const regex = /\$\$([^]*?)\$\$|\$(\\\$|[^\$])*?\$/g;
            text = textBlockReplacer.replace(text, regex);

            // 替换行间代码块```
            const regex2 = /```[\s\S]*?```/g;
            text = textBlockReplacer.replace(text, regex2);
        }
        return text;
    }

    /**
     * LaTeX恢复
     * @param {string} text 已翻译的文本
     * @returns {string} 恢复后的文本
     */
    const recoverLatex = function (text) {
        // 两个公式之间加个空格,防止有些LaTeX解析器解析错误
        let resultText = text
            .replace(/】【/g, '】 【')
            .replace(/\]\[/g, '] [')
            .replace(/\}\{/g, '} {');

        if (OJBetter.typeOfPage.is_oldLatex) {
            resultText = resultText.replace(/(.+?)(\n\n|$)/g, "<p>$1</p>"); // 换行符还原为<p/>标签
            resultText = textBlockReplacer.recover(resultText);
        } else if (OJBetter.typeOfPage.is_acmsguru) {
            resultText = textBlockReplacer.recover(resultText);
        } else if (realTransServer != "openai") {
            resultText = textBlockReplacer.recover(resultText);
        }
        return resultText;
    }

    /**
     * 格式化翻译结果
     * @param {string} text
     * @returns {string} 处理后的翻译结果
     */
    const formatText = function (text) {
        // 转义LaTex中的特殊符号
        if (!OJBetter.typeOfPage.is_oldLatex && !OJBetter.typeOfPage.is_acmsguru) {
            // 先替换掉行间代码块
            const replacer = new TextBlockReplacer();
            text = replacer.replace(text, /```[\s\S]*?```/g);

            // 处理LaTeX公式
            const escapeRules = [
                { pattern: /(?<!\\)>(?!\s)/g, replacement: " &gt; " }, // >符号
                { pattern: /(?<!\\)</g, replacement: " &lt; " }, // <符号
                { pattern: /(?<!\\)\*/g, replacement: " &#42; " }, // *符号
                { pattern: /(?<!\\)_/g, replacement: " &#95; " }, // _符号
                { pattern: /(?<!\\)\\\\(?=\s)/g, replacement: "\\\\\\\\" }, // \\符号
                { pattern: /(?<!\\)\\(?![\\a-zA-Z0-9])/g, replacement: "\\\\" }, // \符号
            ];

            let latexMatches = [...text.matchAll(/\$\$([\s\S]*?)\$\$|\$(.*?)\$|\$([\s\S]*?)\$/g)];
            for (const match of latexMatches) {
                const matchedText = match[0];
                let escapedText = matchedText;

                for (const rule of escapeRules) {
                    escapedText = escapedText.replaceAll(rule.pattern, rule.replacement);
                }
                escapedText = escapedText.replace(/\$\$/g, "$$$$$$$$");// $$符号(因为后面需要作为replacement,双倍消耗)
                text = text.replace(matchedText, escapedText);
            }

            // 恢复行间代码块
            text = replacer.recover(text);
        }

        // 使符合mathjx的转换语法
        const mathjaxRuleMap = [
            { pattern: /\$/g, replacement: "$$$$$$" }, // $$ 行间
        ];
        mathjaxRuleMap.forEach(({ pattern, replacement }) => {
            text = text.replace(pattern, replacement);
        });

        // markdown修正
        const mdRuleMap = [
            { pattern: /(\s_[\u4e00-\u9fa5]+_)([\u4e00-\u9fa5]+)/g, replacement: "$1 $2" }, // 斜体
            { pattern: /(_[\u4e00-\u9fa5]+_\s)([\u4e00-\u9fa5]+)/g, replacement: " $1$2" },
            { pattern: /(_[\u4e00-\u9fa5]+_)([\u4e00-\u9fa5]+)/g, replacement: " $1 $2" },
            { pattern: /(([\s\S]*?))/g, replacement: "($1)" }, // 中文()
            // { pattern: /:/g, replacement: ":" }, // 中文:
            { pattern: /\*\* (.*?) \*\*/g, replacement: "\*\*$1\*\*" } // 加粗
        ];
        mdRuleMap.forEach(({ pattern, replacement }) => {
            text = text.replace(pattern, replacement);
        });

        return text;
    }

    // 创建翻译结果元素并放在element_node的后面
    translateResult.translateDiv = new TranslateDiv(id);
    $(element_node).after(translateResult.translateDiv.getDiv());

    // 顶栏左侧信息
    translateResult.translateDiv.setTopText(i18next.t('servers.' + realTransServer, { ns: 'translator' }) +
        i18next.t('translateDiv.topTextSuffix', { ns: 'translator' }));

    // 注册按钮
    translateResult.translateDiv.registerUpButtonEvent();
    translateResult.translateDiv.registerCloseButtonEvent();
    if (OJBetter.translation.choice == 'openai' || OJBetter.translation.choice == 'deepl') {
        translateResult.translateDiv.showQueryBalanceButton(OJBetter.translation.choice); // 显示额度查询
    }

    // 翻译内容是否为空文本
    if (isEmptyText(text)) {
        const shouldContinue = await OJB_createDialog(
            i18next.t('isEmptyText.title', { ns: 'dialog' }),
            i18next.t('isEmptyText.content', { ns: 'dialog' }),
            [
                i18next.t('isEmptyText.buttons.0', { ns: 'dialog' }),
                i18next.t('isEmptyText.buttons.1', { ns: 'dialog' })
            ],
            true
        );
        if (shouldContinue) {
            translateResult.status = "skip";
            return translateResult;
        }
    }

    // 替换latex公式
    text = replaceLatex(text);

    // 过滤**号
    if (OJBetter.translation.filterTextWithoutEmphasis && GM_getValue("translation") !== "openai") { // TODO
        text = text.replace(/\*\*/g, "");
    }

    // 字符数上限
    const translationLimits = {
        deepl: 5000,
        iflyrec: 2000,
        youdao: 5000,
        google: 5000,
        caiyun: 5000
    };
    if (translationLimits.hasOwnProperty(realTransServer) && text.length > translationLimits[realTransServer]) {
        let textLength = translationLimits[realTransServer];
        let realTextLength = text.length;
        const shouldContinue = await OJB_createDialog(
            i18next.t('transTextLimits.title', { ns: 'dialog' }),
            i18next.t('transTextLimits.content', { ns: 'dialog', textLength: textLength, realTextLength: realTextLength }),
            [
                i18next.t('transTextLimits.buttons.0', { ns: 'dialog' }),
                i18next.t('transTextLimits.buttons.1', { ns: 'dialog' })
            ],
            true
        ); // 字数超限确认
        if (shouldContinue) {
            translateResult.status = "skip";
            return translateResult;
        }
    }

    /**
     * 调用各个翻译服务
     * @param {string} transServer 翻译服务
     * @returns {TransRawData} 原始翻译数据
     */
    async function translate(transServer) {
        const is_renderLaTeX = !((OJBetter.typeOfPage.is_oldLatex || OJBetter.typeOfPage.is_acmsguru) && (!OJBetter.translation.forceTurndownConversion));
        const servername = i18next.t('servers.' + realTransServer, { ns: 'translator' });
        /** @type {TransRawData} 原始翻译数据*/
        let rawData = {};
        try {
            if (transServer == "deepl") {
                if (OJBetter.deepl.config.type == 'free') {
                    translateResult.translateDiv.updateTranslateDiv(`${i18next.t('transingTip.basic', { ns: 'translator', server: servername })}`, is_renderLaTeX);
                    rawData = await translate_deepl(text);
                } else if (OJBetter.deepl.config.type == 'api') {
                    translateResult.translateDiv.updateTranslateDiv(`${i18next.t('transingTip.deeplApi', { ns: 'translator', deepl_configName: OJBetter.deepl.config.name })}`, is_renderLaTeX);
                    if (OJBetter.deepl.config.apiGenre == 'deeplx') {
                        rawData = await translate_deeplx(text);
                    } else {
                        if (OJBetter.deepl.enableEmphasisProtection) text = convertBoldMarkdownToHTML(text);
                        if (OJBetter.deepl.enableLinkProtection) text = convertLinksMarkdownToHTML(text);
                        if (OJBetter.deepl.config.apiGenre == 'api-free') {
                            rawData = await translate_deepl_api_free(text);
                        } else if (OJBetter.deepl.config.apiGenre == 'api-pro') {
                            rawData = await translate_deepl_api_pro(text);
                        }
                        if (OJBetter.deepl.enableEmphasisProtection) rawData.text = convertBoldHTMLToMarkdown(rawData.text);
                        if (OJBetter.deepl.enableLinkProtection) rawData.text = convertLinksHTMLToMarkdown(rawData.text);
                    }
                }
            } else if (transServer == "iflyrec") {
                translateResult.translateDiv.updateTranslateDiv(`${i18next.t('transingTip.basic', { ns: 'translator', server: servername })}`, is_renderLaTeX);
                rawData = await translate_iflyrec(text);
            } else if (transServer == "youdao") {
                translateResult.translateDiv.updateTranslateDiv(`${i18next.t('transingTip.basic', { ns: 'translator', server: servername })}`, is_renderLaTeX);
                rawData = await translate_youdao_mobile(text);
            } else if (transServer == "google") {
                translateResult.translateDiv.updateTranslateDiv(`${i18next.t('transingTip.basic', { ns: 'translator', server: servername })}`, is_renderLaTeX);
                rawData = await translate_gg(text);
            } else if (transServer == "caiyun") {
                translateResult.translateDiv.updateTranslateDiv(`${i18next.t('transingTip.basic', { ns: 'translator', server: servername })}`, is_renderLaTeX);
                rawData = await translate_caiyun(text);
            } else if (transServer == "openai") {
                translateResult.translateDiv.updateTranslateDiv(`${i18next.t('transingTip.openai', { ns: 'translator', openai_name: OJBetter.chatgpt.config.name })}${!OJBetter.chatgpt.isStream
                    ? i18next.t('transingTip.openai_isStream', { ns: 'translator' }) : ""}`,
                    is_renderLaTeX);
                if (OJBetter.chatgpt.isStream) {
                    // 流式传输
                    rawData = await translate_openai_stream(text, translateResult.translateDiv);
                } else {
                    // 普通模式
                    rawData = await translate_openai(text);
                }
            }
            translatedText = rawData.text;
            if (!rawData.done) {
                translateResult.status = "error";
            }
        } catch (e) {
            translateResult.status = "error";
            rawData.message = i18next.t('error.unexpected', { ns: 'translator' });
            console.warn(e);
        }
        return rawData;
    }
    translateResult.rawData = await translate(realTransServer);

    if (translateResult.status == "error") {
        translateResult.translateDiv.updateTranslateDiv(translateResult.rawData.message);
        return translateResult;
    }

    // 还原latex公式
    translatedText = recoverLatex(translatedText);

    // 注册结果复制按钮
    if (!OJBetter.typeOfPage.is_oldLatex && !OJBetter.typeOfPage.is_acmsguru) {
        translateResult.translateDiv.registerCopyButtonEvent(translatedText);
    } else {
        translateResult.translateDiv.disableCopyButton();
    }

    // 翻译结果格式化
    translatedText = formatText(translatedText);

    // 保存翻译结果
    if ((OJBetter.typeOfPage.is_problem || OJBetter.typeOfPage.is_completeProblemset) && OJBetter.translation.memory.enabled) {
        OJBetter.translation.memory.ttTree.refreshNode(".ttypography"); // 刷新当前页面.ttypography元素的结构树实例
        OJBetter.translation.memory.ttTree.addTransResultMap(id, translatedText);
        updateTransDBData(OJBetter.translation.memory.ttTree.getNodeData(), OJBetter.translation.memory.ttTree.getTransResultMap()); // 更新翻译结果到transDB
    }

    // 翻译结果面板更新
    translateResult.translateDiv.updateTranslateDiv(translatedText, !((OJBetter.typeOfPage.is_oldLatex || OJBetter.typeOfPage.is_acmsguru) && (!OJBetter.translation.forceTurndownConversion)));

    return translateResult;
}

//弹窗翻译
function alertZh() {
    // var _alert = window.alert;
    // window.alert = async function (msg) {
    //     _alert(msg + "\n=========翻译=========\n" + await translate_deepl(msg));
    //     return true;
    // }
};

/**
 * 折叠块展开
 */
function ExpandFoldingblocks() {
    $('.spoiler').addClass('spoiler-open');
    $('.spoiler-content').attr('style', '');
};

/**
 * 折叠块渲染优化
 */
function RenderPerfOpt() {
    GM_addStyle(`
        .spoiler-content {
            contain: layout style;
        }
    `);
}

/**
 * 下拉选择框性能优化
 */
async function SelectElementPerfOpt() {
    // TODO 10
    // 加载库资源
    await OJB_LoadJS("https://aowuucdn.oss-accelerate.aliyuncs.com/js/selectpage.min.js","sha512-HhBheWc9nbTuTG0oVYtY9c3nkJAAiuk899lycOtB8NALvp20CNOjlYdTAYbRy9/0zXnLl0LZpiwhfLZurvK1XQ==");
    /**
     * 将一个<select>元素转换为SelectPage控件
     * @param {HTMLElement|string} selector - 要转换的<select>元素或其选择器
     */
    const OJB_transformSelectToSelectPage = (selector) => {
        const $select = $(selector);
        if ($select.length === 0 || !$select.is('select')) {
            console.error('Invalid select element provided.');
            return;
        }

        // 隐藏原生的<select>元素
        $select.hide();

        // 创建一个新的<input>元素用于SelectPage控件
        const $inputForSelectPage = $('<input>', {
            type: 'text',
            class: 'selectpage-input',
            autocomplete: 'off'
        });
        $select.after($inputForSelectPage);

        // 准备SelectPage所需的数据格式
        const data = $select.find('option').map((_, option) => ({
            id: option.value,
            text: option.text
        })).get();

        // 初始化SelectPage
        $inputForSelectPage.selectPage({
            showField: 'text',
            keyField: 'id',
            data,
            lang: 'en',
            // 当选中一个选项时,更新隐藏的<select>元素的值
            eSelect: (data) => {
                $select.val(data.id).trigger('change');
            },
            // 初始化时根据<select>的当前值设置SelectPage
            initRecord: $select.val()
        });
    };

    // 遍历页面上的所有select
    $('select').each((_, select) => {
        // 选项大于500才优化
        if ($(select).find('option').length > 500) {
            OJB_transformSelectToSelectPage(select);
        }
    });
}

/**
 * 评论区分页
 */
function CommentPagination() {
    GM_addStyle(`
        .comments > .comment {
            display: none;
        }
        #next-page-btn, #prev-page-btn {
            display: none;
        }
        #jump-input, #items-per-page{
            height: 22px;
            border: 1px solid #dcdfe6;
            border-radius: 0.3rem;
        }
        #jump-input:focus-visible{
            border-style: solid;
            border-color: #3f51b5;
            outline: none;
        }
    `);
    $('.comments').after(`
            <div id="pagBar" style="display: flex; align-items: center; justify-content: center; color: #606266;">
                <label for="items-per-page">${i18next.t('perpage', { ns: 'comments' })}</label>
                <select id="items-per-page" style="margin-right: 15px;">
                    <option value="5">5</option>
                    <option value="10">10</option>
                    <option value="20">20</option>
                </select>
                <div class="paging" style="margin-right: 15px;">
                    <span id="current-page">1</span> / <span id="total-pages"></span>
                </div>
                <input type="text" id="jump-input" placeholder="${i18next.t('jumpTo', { ns: 'comments' })}">
                <button type="button" id="jump-btn" class="ojb_btn">${i18next.t('jump', { ns: 'comments' })}</button>
                <button id="prev-page-btn" class="ojb_btn">${i18next.t('prev', { ns: 'comments' })}</button>
                <button id="next-page-btn" class="ojb_btn">${i18next.t('next', { ns: 'comments' })}</button>
            </div>
        `);

    let batchSize = 5;
    let elements = $(".comments > .comment").slice(0, -1);
    let start = 0;
    let end = batchSize;
    let currentPage = 1;
    let displayedIndexes = []; // 存储已显示元素的索引

    function showBatch(start, end) {
        // 隐藏上一页
        for (var i = 0; i < displayedIndexes.length; i++) {
            elements.eq(displayedIndexes[i]).hide();
        }

        displayedIndexes = [];

        // 显示当前页
        elements.slice(start, end).each(function (index) {
            $(this).show();
            displayedIndexes.push(start + index);
        });

        // 更新页码和翻页按钮
        $("#current-page").text(currentPage);
        $("#total-pages").text(Math.ceil(elements.length / batchSize));

        if (currentPage === 1) $("#prev-page-btn").hide();
        else $("#prev-page-btn").show();

        if (end >= elements.length) $("#next-page-btn").hide();
        else $("#next-page-btn").show();
    }

    // 初始化
    var commentID = null;
    var pageURL = window.location.href;
    if (pageURL.includes("#comment-")) {
        // 带评论区锚点的链接
        var startIndex = pageURL.lastIndexOf("#comment-") + 9;
        commentID = pageURL.substring(startIndex);
        var indexInComments = null;
        $(".comments > .comment").each(function (index) {
            $(this).find(".comment-table").each(function () {
                var tableCommentID = $(this).attr("commentid");
                if (tableCommentID === commentID) {
                    indexInComments = index;
                    return false;
                }
            });
        });
        let page = Math.ceil((indexInComments + 1) / batchSize);
        currentPage = !page ? 1 : page;
        showBatch((currentPage - 1) * batchSize, currentPage * batchSize);
        setTimeout(function () {
            window.location.href = pageURL;
        }, 1000);
    } else {
        showBatch(0, batchSize);
    }

    $("#prev-page-btn").on("click", function () {
        var itemsPerPage = parseInt($("#items-per-page").val());
        start = (currentPage - 2) * itemsPerPage;
        end = (currentPage - 1) * itemsPerPage;
        currentPage--;
        showBatch(start, end);
    });

    $("#next-page-btn").on("click", function () {
        var itemsPerPage = parseInt($("#items-per-page").val());
        start = currentPage * itemsPerPage;
        end = (currentPage + 1) * itemsPerPage;
        currentPage++;
        showBatch(start, end);
    });

    $("#jump-btn").on("click", function () {
        var inputPage = parseInt($("#jump-input").val());

        if (inputPage >= 1 && inputPage <= Math.ceil(elements.length / parseInt($("#items-per-page").val()))) {
            var itemsPerPage = parseInt($("#items-per-page").val());
            start = (inputPage - 1) * itemsPerPage;
            end = inputPage * itemsPerPage;
            currentPage = inputPage; // 更新当前页码
            showBatch(start, end);
        }
    });

    $("#items-per-page").on("change", function () {
        batchSize = parseInt($(this).val());
        let page = Math.ceil(end / batchSize);
        currentPage = !page ? 1 : page;
        let maxPage = Math.ceil(elements.length / batchSize);
        if (currentPage > maxPage) currentPage = maxPage;
        showBatch((currentPage - 1) * batchSize, currentPage * batchSize);
    });
}

/**
 * 题目页相关链接栏
 */
class ProblemPageLinkbar {
    constructor() {
        this.containerElement = this.createToolbar();
        this.commandInvoker = new CommandInvoker();
    }

    /**
     * 创建工具栏
     */
    createToolbar() {
        const toolbarElement = $("<div>").attr("id", "problemToolbar").insertBefore($(".problemindexholder"));
        return new DOMContainer(toolbarElement);
    }

    /**
     * 添加按钮
     * @param {string} id 按钮id
     * @param {string} url 按钮链接
     * @param {string} text 按钮文字
     * @param {JQueryObject} icon 按钮图标
     * @param {string} iconHeight 图标高度
     * @returns {object} 按钮对象
     */
    addLinkButton(id, url, text, icon = $('<div>'), iconHeight = "22px") {
        const linkElement = $("<a>")
            .attr("href", url)
            .attr("target", "_blank")
            .addClass("ojb_btn")
            .attr("id", id);

        linkElement.append(icon);
        icon.css("height", iconHeight);

        const textSpan = $("<span>").html(text);
        linkElement.append(textSpan);

        this.commandInvoker.execute(new AddElementCommand(this.containerElement, linkElement));
        return {
            element: linkElement,
            text: textSpan,
            icon: icon
        };
    }

    /**
     * 更新链接
     * @param {object} button 按钮对象
     * @param {string} url 按钮链接
     */
    updateUrl(button, url) {
        button.element.attr("href", url);
    }

    /**
     * 更新文字
     * @param {object} button 按钮对象
     * @param {string} text 按钮文字
     */
    updateText(button, text) {
        button.text.html(text);
    }

    /**
     * 设置文字为粗体
     * @param {object} button 按钮对象
     */
    setBold(button) {
        button.text.css("font-weight", "bold");
    }

    /**
     * 更新图标
     * @param {object} button 按钮对象
     * @param {JQueryObject} icon 按钮图标
     * @param {string} iconHeight 图标高度
     */
    updateIcon(button, icon, iconHeight = "16px") {
        button.icon.remove();
        button.text.prepend(icon);
        icon.css("height", iconHeight);
        button.icon = icon;
    }

    /**
     * 添加类
     * @param {object} button 按钮对象
     * @param {string} className 类名
     */
    addClass(button, className) {
        button.element.addClass(className);
    }

    /**
     * 禁用链接按钮
     * @param {object} button 按钮对象
     */
    disableButton(button) {
        button.element.addClass("disabled");
    }

    /**
     * 启用链接按钮
     * @param {object} button 按钮对象
     */
    enableButton(button) {
        button.element.removeClass("disabled");
    }
}

/**
 * 获取题目的id
 * @param {String} url 题目的链接
 * @returns 题目的id,形如2000A
 */
function getProblemId(url) {
    const regexMap = new Map([
        ['/contest/', /\/contest\/(\d+)\/problem\/([A-Za-z\d]+)/],
        ['/problemset/', /\/problemset\/problem\/(\d+)\/([A-Za-z\d]+)/],
        ['/gym/', /\/gym\/(\d+)\/problem\/([A-Za-z\d]+)/],
    ]);

    let regex = null;
    for (let [key, value] of regexMap) {
        if (url.includes(key)) {
            regex = value;
            break;
        }
    }

    if (!regex) return '';

    const matchResult = url.match(regex);
    return matchResult && matchResult.length >= 3 ? `${matchResult[1]}${matchResult[2]}` : '';
};

/**
 * 跳转到洛谷
 * @param {ProblemPageLinkbar} problemToolbar
 */
async function CF2luogu(problemToolbar) {
    const url = window.location.href;
    const problemId = getProblemId(url);
    const luoguButton = problemToolbar.addLinkButton(
        "luoguButton",
        "https://www.luogu.com.cn/",
        i18next.t('state.loading', { ns: 'button' }),
        $("<img>").attr("src", "https://cdn.luogu.com.cn/fe/logo.png")
    );

    const checkLinkExistence = async (url) => {
        return OJB_promiseRetryWrapper(async () => {
            const response = await OJB_GMRequest({
                method: "GET",
                url
            });
            return !response.responseText.match(/出错了/g);
        }, {
            maxRetries: 3,
            retryInterval: 1000
        });
    };

    const LuoguUrl = `https://www.luogu.com.cn/problem/CF${problemId}`;
    try {
        const result = await checkLinkExistence(LuoguUrl);
        if (problemId && result) {
            problemToolbar.updateText(luoguButton, "");
            problemToolbar.updateUrl(luoguButton, LuoguUrl);
        } else {
            problemToolbar.updateText(luoguButton, i18next.t('state.404', { ns: 'button' }));
            problemToolbar.disableButton(luoguButton);
        }
    } catch (error) {
        if (error instanceof OJB_GMError && error.type == "error") {
            problemToolbar.updateText(luoguButton, i18next.t('state.netError', { ns: 'button' }));
            problemToolbar.disableButton(luoguButton);
        }
    }
}

/**
 * 跳转到 Virtual Judge
 * @param {ProblemPageLinkbar} problemToolbar
 */
async function CF2vjudge(problemToolbar) {
    const url = window.location.href;
    const problemId = getProblemId(url);
    const vjudgeButton = problemToolbar.addLinkButton(
        "vjudgeButton",
        "https://vjudge.net/",
        i18next.t('state.loading', { ns: 'button' }),
        $("<img>").attr("src", "https://aowuucdn.oss-accelerate.aliyuncs.com/vjudge.ico")
    );

    const checkLinkExistence = async (url) => {
        return OJB_promiseRetryWrapper(async () => {
            const response = await OJB_GMRequest({
                method: "HEAD",
                url: url,
            });
            if (response.status >= 200 && response.status < 300) return true;
            else if (response.status == 404) return false;
            else throw new OJB_GMError('network', 'An unknown network error occurred!', response);
        }, {
            maxRetries: 3,
            retryInterval: 1000
        });
    };

    const VjudgeUrl = OJBetter.typeOfPage.is_gym ?
        `https://vjudge.net/problem/Gym-${problemId}` :
        `https://vjudge.net/problem/CodeForces-${problemId}`;
    try {
        const result = await checkLinkExistence(VjudgeUrl);
        if (problemId && result) {
            problemToolbar.updateText(vjudgeButton, "VJudge");
            problemToolbar.updateUrl(vjudgeButton, VjudgeUrl);
        } else {
            problemToolbar.updateText(vjudgeButton, i18next.t('state.404', { ns: 'button' }));
            problemToolbar.disableButton(vjudgeButton);
        }
    } catch (error) {
        if (error instanceof OJB_GMError && error.type == "error") {
            problemToolbar.updateText(vjudgeButton, i18next.t('state.netError', { ns: 'button' }));
            problemToolbar.disableButton(vjudgeButton);
        }
    }
}

// RatingClass
const ratingClassMap = {
    NaN: "rating_by_clist_colorNaN",
    0: "rating_by_clist_color0",
    1200: "rating_by_clist_color1",
    1400: "rating_by_clist_color2",
    1600: "rating_by_clist_color3",
    1900: "rating_by_clist_color4",
    2100: "rating_by_clist_color5",
    2300: "rating_by_clist_color6",
    2400: "rating_by_clist_color7",
    2600: "rating_by_clist_color8",
    3000: "rating_by_clist_color9"
};
const cssMap = {
    "rating_by_clist_colorNaN": "#cccccc",
    "rating_by_clist_color0": "#808080",
    "rating_by_clist_color1": "#73e473",
    "rating_by_clist_color2": "#77ddbb",
    "rating_by_clist_color3": "#aaaaff",
    "rating_by_clist_color4": "#ff88ff",
    "rating_by_clist_color5": "#ffcc88",
    "rating_by_clist_color6": "#ffbb55",
    "rating_by_clist_color7": "#ff7777",
    "rating_by_clist_color8": "#ff3333",
    "rating_by_clist_color9": "#aa0000"
};
// TODO 7
/**
 * clist 访问有效性检查
 * @param {boolean} onlyCookie 是否只检查Cookie
 * @returns {Promise<boolean>} 是否有效
 */
async function validateClistConnection(onlyCookie = false) {
    const clistApiUrl = "https://clist.by:443/api/v4/contest/?limit=1&resource_id=1";
    const requestOptions = {
        method: "GET",
        url: clistApiUrl,
        timeout: 5000,
    };

    // 尝试发送请求
    async function tryRequest(options) {
        try {
            const response = await OJB_GMRequest(options);
            if (response.status === 200) {
                return { ok: true };
            } else if (response.status === 401) {
                throw new Error('unauthorized');
            } else if (response.status === 404) {
                throw new Error('not_found');
            } else {
                throw new Error('other_error');
            }
        } catch (error) {
            console.warn(`Error accessing clist.by: ${error.message}`);
            return { ok: false, error: error.message };
        }
    }

    // 尝试携带Key发送请求
    let result = await tryRequest(requestOptions);
    if (!onlyCookie && !result.ok) {
        requestOptions.headers = { "Authorization": OJBetter.clist.authorization };
        result = await tryRequest(requestOptions);
    }

    // 根据结果显示错误信息
    if (!result.ok) {
        let errorType = result.error;
        const loadingMessage = new LoadingMessage();
        let state;
        if (errorType === 'not_found') {
            state = i18next.t('error.clist.404', { ns: 'alert' });
        } else if (errorType === 'unauthorized') {
            state = i18next.t('error.clist.cookie', { ns: 'alert' });
        } else {
            state = i18next.t('error.clist.other', { ns: 'alert' });
        }
        loadingMessage.updateStatus(`${OJBetter.state.name} —— ${state}`, 'error');
    }
    return result.ok;
}

/**
 * 创建Rating相关css
 * @param {boolean} [hasBorder=true] 是否有边框
 */
function creatRatingCss(hasBorder = true) {
    const defaultBorderColor = '#dcdfe6';
    let dynamicCss = "";
    let hoverSelector = OJBetter.clist.ratingHidden ? ":hover" : "";
    for (let cssClass in cssMap) {
        dynamicCss += `a.${cssClass}${hoverSelector}, a.${cssClass}${hoverSelector}:link {\n`;
        let borderColor = hasBorder ? cssMap[cssClass] : defaultBorderColor;
        dynamicCss += `    color: ${cssMap[cssClass]} ${OJBetter.clist.ratingHidden ? "!important" : ""};\n`;
        dynamicCss += `}\n`;
    }
    GM_addStyle(dynamicCss);
    if (OJBetter.clist.ratingHidden) {
        GM_addStyle(`
        #clistButton {
            color: #ffffff00;
        }
    `);
    }
}

/**
 * 模拟clist网页访问获取rating
 * @param {string} problem 题目名称
 * @param {string} problem_url 题目链接
 * @param {string} contest 比赛名称
 * @returns {Promise<{rating: number, problem: string}>} 题目难度
 */
async function getRatingFromHTML(problem, problem_url, contest = null) {
    // 去除题目名称中的括号,以及首尾的空白符
    problem = problem.replace(/\([\s\S]*?\)/g, '').trim();

    return OJB_promiseRetryWrapper(async () => {
        const queryString = `search=${encodeURIComponent(problem)}&resource=1`;
        const response = await OJB_GMRequest({
            method: 'GET',
            url: `https://clist.by/problems/?${queryString}`,
        });

        if (!response.responseText) throw new OJB_GMError('network', 'An unknown network error occurred!', response);
        const html = response.responseText;
        const cleanedHtml = html.replace(/src=(.|\s)*?"/g, '');
        const parser = new DOMParser();
        const doc = parser.parseFromString(cleanedHtml, 'text/html');
        const trs = doc.querySelectorAll('table tbody tr');

        for (let tr of trs) {
            const rating = tr.querySelector('.problem-rating-column').textContent.trim();
            const link = OJB_cleanLink(tr.querySelector('.problem-name-column a:nth-of-type(2)')?.href);

            if (link === problem_url || link === problem_url + '/') {
                return {
                    rating: parseInt(rating),
                    problem: problem
                };
            } else if (contest !== null) {
                const contestTitles = [...tr.querySelectorAll('.problem-name-column .pull-right a[title], .problem-name-column .pull-right span[title]')].map(el => el.title);
                if (contestTitles.includes(contest)) {
                    return {
                        rating: parseInt(rating),
                        problem: problem
                    };
                }
            }
        }
        console.warn(`No data found for the question: ${problem}`);
    }, {
        maxRetries: 3,
        retryInterval: 500
    });
}

/**
 * 从clist API获取题目的rating
 * @param {string} problem_name 题目名
 * @param {string} problem_url 题目链接
 * @returns {Promise<number>} 题目rating
 *
 * 使用两个Map对象来存储和快速访问题目信息:
 * - problemsMap: 通过题目的URL作为键来存储题目信息。
 * - nameMap: 通过题目的名称作为键来存储题目信息。
 *
 * 每个题目信息是一个对象,包含以下属性:
 * @typedef {Object} ProblemInfo
 * @property {string} name 题目名称
 * @property {string} url 题目URL
 * @property {number} rating 题目评分,如果没有评分信息则为NaN
 */
async function getRatingFromApi_problem(problem_name, problem_url) {
    return OJB_promiseRetryWrapper(async () => {
        const response = await OJB_GMRequest({
            method: "GET",
            url: `https://clist.by:443/api/v4/problem/?name=${encodeURIComponent(problem_name)}&resource__regex=codeforces`,
            headers: { "Authorization": OJBetter.clist.authorization }
        });

        if (!response.responseText) throw new OJB_GMError('network', 'An unknown network error occurred!', response);
        let data = JSON.parse(response.responseText);
        /**
         * 使用题目的URL作为键来存储题目信息。
         * @type {Map<string, ProblemInfo>}
         */
        let problemsMap = new Map();

        /**
         * 使用题目的名称作为键来存储题目信息。
         * @type {Map<string, ProblemInfo>}
         */
        let nameMap = new Map();

        data.objects.forEach(problem => {
            /** @type {ProblemInfo} 题目信息*/
            let problemInfo = {
                name: problem.name,
                url: problem.url,
                rating: problem.rating ? problem.rating : NaN
            };
            problemsMap.set(OJB_cleanLink(problem.url), problemInfo);
            nameMap.set(problem.name, problemInfo);
        });

        if (problemsMap.has(problem_url)) {
            return problemsMap.get(problem_url).rating;
        } else if (nameMap.has(problem_name)) {
            return nameMap.get(problem_name).rating;
        } else {
            console.warn('Problem not found in the response');
        }
    }, {
        maxRetries: 5,
        retryInterval: 1000
    });
}

/**
 * 获取字符串中的关键词列表
 * @param {string} text 字符串文本
 * @returns {array<string>} 返回关键词列表
 */
function getKeywords(text) {
    // 定义要过滤掉的高频词
    const highFrequencyWords = ['Educational', 'Codeforces', 'Round', 'Div'];

    // 使用正则表达式替换掉特殊符号(保留空格以便分词)
    const sanitizedText = text.replace(/[^\w\s]|_/g, '').replace(/\s+/g, ' ');

    // 将字符串拆分为单词数组
    const words = sanitizedText.split(' ');

    // 过滤掉高频词和空字符串
    const filteredWords = words.filter(word => {
        return word && highFrequencyWords.indexOf(word) === -1;
    });

    // 返回关键词列表
    return filteredWords;
}

/**
 * 根据关键词从 Clist API 中获取实际比赛名称
 * @param {string} contestName 比赛名
 * @param {string} contestUrl 比赛链接
 * @returns {string|null} 该比赛在Clist中的实际名字
 */
async function getContestNameFromApi(contestName, contestUrl) {
    return OJB_promiseRetryWrapper(async () => {
        const options = {
            method: "GET",
            url: `https://clist.by:443/api/v4/contest/?resource_id=1&event__regex=${encodeURIComponent(contestName)}`,
            headers: {
                "Authorization": OJBetter.clist.authorization
            }
        };

        let response = await OJB_GMRequest(options);

        if (!response.responseText) throw new OJB_GMError('network', 'An unknown network error occurred!', response);

        let data = JSON.parse(response.responseText);
        let objects = data.objects;

        if (objects.length > 0) {
            for (const contest of objects) {
                const href = contest.href.replace(/\/contests\//i, '/contest/'); // 链接可能是contests而不是contest,换回来
                if (OJB_cleanLink(href) == contestUrl) {
                    return contest.event;
                }
            }
        }
        return null;
    }, {
        maxRetries: 5,
        retryInterval: 1000
    });
}

/**
 * 获取在clist中的实际比赛名称
 * @param {string} contestName 待搜索的比赛名称
 * @param {string} contestUrl 比赛的url
 * @returns {Promise<string|null>} 在clist中的实际比赛名称,如果没有找到,则返回null
 */
async function getActualContestName(contestName, contestUrl) {
    // 首先尝试使用完整的比赛名称进行搜索
    let actualContestName = await getContestNameFromApi(contestName, contestUrl);
    if (actualContestName) {
        return actualContestName;
    }

    // 如果使用完整名称没有找到,则尝试使用关键词进行搜索
    const keywords = getKeywords(contestName);
    const maxKeywordAttempts = 1; // 最多尝试到第几个关键词(因为Clist API有频率限制)
    for (let i = 0; i < Math.min(keywords.length, maxKeywordAttempts); i++) {
        actualContestName = await getContestNameFromApi(keywords[i], contestUrl);
        if (actualContestName) {
            return actualContestName;
        }
    }

    // 如果全部尝试后仍没有找到,返回null
    return null;
}

/**
 * 从clist API获取比赛题目集的rating
 * @param {string} contestName 比赛名
 * @returns {Promise<Map<string, number>>} 题目rating
 */
async function getRatingFromApi_contest(contestName, contestUrl) {
    const actualContestName = await getActualContestName(contestName, contestUrl);
    return OJB_promiseRetryWrapper(async () => {
        const options = {
            method: "GET",
            url: `https://clist.by:443/api/v4/contest/?resource_id=1&with_problems=true&event=${encodeURIComponent(actualContestName)}`,
            headers: {
                "Authorization": OJBetter.clist.authorization
            }
        };

        let response = await OJB_GMRequest(options);

        if (!response.responseText) throw new OJB_GMError('network', 'An unknown network error occurred!', response);

        let data = JSON.parse(response.responseText);
        let objects = data.objects;
        let problemsMap = new Map();

        if (objects.length > 0 && objects[0].problems) {
            objects[0].problems.forEach(problem => {
                problemsMap.set(OJB_cleanLink(problem.url), problem.rating ? problem.rating : NaN);
            });
        }

        return problemsMap;
    }, {
        maxRetries: 5,
        retryInterval: 1000
    });
}

/**
 * 根据rating获取对应的颜色class名
 * @param {number} rating 题目rating
 * @returns {string} 颜色class名
 */
function getClassNameByRating(rating) {
    let className = "rating_by_clist_color9";
    if (Number.isNaN(rating)) {
        className = "rating_by_clist_colorNaN";
    } else {
        let keys = Object.keys(ratingClassMap);
        for (let i = 0; i < keys.length; i++) {
            if (rating < keys[i]) {
                className = ratingClassMap[keys[i - 1]];
                break;
            }
        }
    }
    return className;
}

/**
 * problem题目页显示Rating
 * @param {ProblemPageLinkbar} problemToolbar
 * @returns {Promise<void>}
 */
async function showRatingByClist_problem(problemToolbar) {
    // 题目名
    let problem = $('.header .title').eq(0).text().replace(/[\s\S]*?. /, '');
    if (OJBetter.typeOfPage.is_acmsguru) problem = $('h4').eq(0).text().replace(/[\s\S]*?. /, '');

    // 创建Rating按钮元素
    creatRatingCss(false);
    // TODO
    const clistButton = problemToolbar.addLinkButton(
        'clistButton',
        `https://clist.by/problems/?search=${problem}&resource=1&resource=64`,
        i18next.t('state.wait', { ns: 'button' }),
        $("<img>").attr("src", "https://clist.by/static/img/logo-48.png"),
        "15px"
    );

    // 检测clist连接
    if (!await validateClistConnection()) {
        problemToolbar.updateText(clistButton, i18next.t('state.netError', { ns: 'button' }));
        return;
    }

    // 题目链接
    let problem_url = window.location.href;
    if (problem_url.includes('/contest/')) {
        problem_url = problem_url.replace(/\/contest\/(\d+)\/problem\/(\w+)[^\w]*/, '/contest/$1/problem/$2');
    } else {
        problem_url = problem_url.replace(/\/problemset\/problem\/(\d+)\/(\w+)/, '/contest/$1/problem/$2');
    }
    if (OJBetter.typeOfPage.is_mSite) problem_url = problem_url.replace(/\/\/(\w+).codeforces.com/, '//codeforces.com'); // 轻量站

    // 比赛名
    // let contest = $('#sidebar').children().first().find('.rtable th').first().text();

    // rating
    problemToolbar.updateText(clistButton, i18next.t('state.loading', { ns: 'button' }));
    let rating = await getRatingFromApi_problem(problem, problem_url);
    if (rating) {
        let className = getClassNameByRating(rating);
        problemToolbar.updateText(clistButton, rating);
        problemToolbar.setBold(clistButton);
        problemToolbar.addClass(clistButton, className);
    } else {
        problemToolbar.updateText(clistButton, i18next.t('state.404', { ns: 'button' }));
        problemToolbar.disableButton(clistButton);
    }
}

/**
 * contest页显示Rating
 * @returns {Promise<void>}
 */
async function showRatingByClist_contest() {
    // 创建Rating显示框
    creatRatingCss();
    let ratingBadges = {};
    $('.datatable .id.left').each(function () {
        let href = 'https://codeforces.com' + $(this).find('a').attr('href');
        let badge = OJB_safeCreateJQElement(`<a id="clistButton" class="ratingBadge">${i18next.t('state.wait', { ns: 'button' })}</a>`);
        $(this).find('a').after(badge);
        ratingBadges[href] = badge;
    });

    // 检测clist连接
    if (!await validateClistConnection()) {
        for (let href in ratingBadges) {
            ratingBadges[href].text('error').addClass('ratingBadge_error');
        }
        return;
    }

    // 显示loading
    for (let href in ratingBadges) {
        ratingBadges[href].text(i18next.t('state.loading', { ns: 'button' })).addClass('ratingBadge_loading');
    }

    // 获取Rating
    let contestName = $('#sidebar').children().first().find('.rtable th').first().text();
    let contestUrl = OJB_cleanLink(window.location.href);
    try {
        let problemsMap = await getRatingFromApi_contest(contestName, contestUrl);

        // 填充数据
        for (let href in ratingBadges) {
            if (problemsMap.has(href)) {
                let rating = problemsMap.get(href);
                let className = getClassNameByRating(rating);
                ratingBadges[href].text(rating).addClass(className);
            } else {
                ratingBadges[href].text(i18next.t('state.404', { ns: 'button' })).addClass('ratingBadge_no');
            }
        }
    } catch (error) {
        // 填充数据
        for (let href in ratingBadges) {
            ratingBadges[href].text(i18next.t('state.netError', { ns: 'button' })).addClass('ratingBadge_no');
        }
        console.warn(error);
    }
}

/**
 * problemset页显示Rating
 * @returns {Promise<void>}
 */
async function showRatingByClist_problemset() {
    creatRatingCss();
    let ratingBadges = [];
    const $problems = $('.problems');
    const $trs = $problems.find('tbody tr:gt(0)');

    // 先创建Rating显示框,并将关系存进数组ratingBadges
    for (let i = 0; i < $trs.length; i++) {
        const $tds = $($trs[i]).find('td');
        const $firstDiv = $($tds[1]).find('div:first');
        let problem = $firstDiv.text();
        let problem_url = $firstDiv.find('a').attr('href');
        problem_url = problem_url.replace(/^\/problemset\/problem\/(\d+)\/(\w+)/, 'https://codeforces.com/contest/$1/problem/$2');

        const ratingBadge = OJB_safeCreateJQElement(`<a id="clistButton" class="ratingBadge"></a>`);
        const rating = OJB_safeCreateJQElement(`<span class="rating">${i18next.t('state.wait', { ns: 'button' })}</span>`);
        ratingBadge.append(rating);
        $($tds[0]).find('a').after(ratingBadge);
        ratingBadges.push({ ratingBadge, rating, problem, problem_url });
    }

    // 检测clist连接
    if (!await validateClistConnection()) {
        for (let i = 0; i < ratingBadges.length; i++) {
            ratingBadges[i].rating.text(i18next.t('state.netError', { ns: 'button' }));
        }
        return;
    }

    // 每次只获取3个rating
    for (let i = 0; i < ratingBadges.length; i += 3) {
        const promises = [];
        const endIndex = Math.min(i + 3, ratingBadges.length);

        for (let j = i; j < endIndex; j++) {
            const ratingBadge = ratingBadges[j];
            // 显示请求中
            ratingBadge.rating.text(i18next.t('state.loading', { ns: 'button' }));
            promises.push(getRatingFromHTML(ratingBadge.problem, ratingBadge.problem_url).catch(error => console.warn(error)));
        }

        const results = await Promise.all(promises);

        for (let j = i; j < endIndex; j++) {
            const result = results[j - i];
            const ratingBadge = ratingBadges[j];
            if (result) {
                let className = getClassNameByRating(result.rating);
                ratingBadge.ratingBadge.addClass(className);
                ratingBadge.rating.text(result.rating);
            } else {
                ratingBadge.rating.text(i18next.t('state.404', { ns: 'button' }));
            }
        }
    }
}

/**
 * cf赛制榜单重新着色
 */
async function recolorStandings() {
    function getColorValue(value) {
        value = Math.max(0, Math.min(1, value));

        const scale = chroma.scale(['#b71c1c', '#ff9800', '#ffc107', '#00aa00']).mode('lch').domain([0, 0.45, 0.7, 1]);
        return scale(value).hex();
    }
    var maxScores = $('.standings tr:first th:nth-child(n+5)')
        .map(function () {
            return $(this).find('span').text();
        })
        .get();
    $('.standings tr:not(:first):not(:last)').each(function () {
        var thElements = $(this).find('td:nth-child(n+5)');
        thElements.each(function (index) {
            var spanElement = $(this).find('span:first');
            var value = parseInt(spanElement.text());
            if (value <= 0 || /[a-zA-Z]/.test(maxScores[index])) return;
            var colorValue = getColorValue(value / maxScores[index]);
            spanElement.css('color', colorValue);
        });
    });
}

/**
 * 评测结果的简写
 */
const StatusAcronyms = {
    "Accepted": "AC",
    "Wrong answer": "WA",
    "Time limit exceeded": "TLE",
    "Memory limit exceeded": "MLE",
    "Runtime error": "RE",
    "Compilation error": "CE",
    "Hacked": "Hacked",
    "Skipped": "Skipped",
    "Idleness limit exceeded": "ILE",
    "Perfect result:": "AC",
    "Partial result:": "PC",
    "Running": "PENDING"
};

/** 
 * 评测结果的颜色
 */
const StatusColors = {
    "AC": "#52c41a",
    "WA": "#e74c3c",
    "PENDING": "#808080",
};

/**
 * 替换评测结果
 */
async function judgeStatusReplace() {

    /**
     * 获取评测状态的名称和编号
     * 
     * @param {string} text 评测状态
     * @returns {object} 评测状态的名称和编号
     */
    const getStatusName = (text) => {
        const words = text.split(' ');
        const number_of_words = words.length;
        let status_name = "", number = "";
        let status_name_is_over = false;
        for (let i = 0; i < number_of_words; i++) {
            if (words[i] === "on") {
                status_name_is_over = true;
            }
            if (!status_name_is_over) {
                status_name += words[i] + " ";
            } else if (parseInt(words[i]).toString() !== "NaN") {
                number = words[i];
            }
            if (words[i] === "result:") {
                status_name_is_over = true;
            }
        }
        status_name = status_name.trim();
        return {
            status_name: status_name,
            number: number
        }
    };

    /** 
     * 获取当前评测状态的缩写
     * 
     * @param {string} status_name 评测状态
     * @returns {string} 评测状态的缩写
     */
    const getStatusAcronym = (status_name) => {
        return StatusAcronyms[status_name];
    };

    /** 
     * 根据评测状态的缩写获取颜色
     * 
     * @param {string} statusAcronym 评测状态的缩写
     * @returns {string} 颜色值
     */
    const getStatusColor = (statusAcronym) => {
        let color = StatusColors[statusAcronym];
        return color ? color : StatusColors["WA"];
    };

    /**
     * 解析模板
     * 
     * @param {string} template 模板
     * @param {object} display 各个前置标识符的bool值,决定了是否显示这一部分
     * @returns {string} 解析后的文本
     * 
     * @example
     * parseTemplate("{ac:!}{wa:呜呜}{pending:别急}", {ac: false, wa: true, pending: false}) => "呜呜"
     */
    const parseTemplate = (template, display) => {
        const regex = /{(\w+):([^}]*)}/g;
        let result = '';
        let lastIndex = 0;

        template.replace(regex, (match, key, text, offset) => {
            // 添加模板前的普通文本部分
            result += template.slice(lastIndex, offset);

            // 根据布尔值决定是否添加模板部分
            if (display[key]) {
                result += text;
            }
            lastIndex = offset + match.length;
        });

        // 添加最后一段普通文本部分
        result += template.slice(lastIndex);

        return result;
    }

    /**
     * 根据替换规则获取替换后的文本
     * 
     * @param {string} status_name 评测状态
     * @param {string} number 评测编号
     * @returns {string} 替换后的文本
     */
    const getReplaceText = (status_name, number) => {
        const statusAcronym = getStatusAcronym(status_name);

        let result = OJBetter.preference.judgeStatusReplaceText;
        result = result.replace("{Status}", status_name);
        result = result.replace("{Stat}", statusAcronym);
        result = result.replace("{Number}", number);

        const statusMap = {
            ac: statusAcronym === "AC",
            wa: statusAcronym === "WA",
            tle: statusAcronym === "TLE",
            mle: statusAcronym === "MLE",
            re: statusAcronym === "RE",
            ce: statusAcronym === "CE",
            hacked: statusAcronym === "Hacked",
            skipped: statusAcronym === "Skipped",
            ile: statusAcronym === "ILE",
            pc: statusAcronym === "PC",
            pending: statusAcronym === "PENDING"
        }
        const final_result = parseTemplate(result, statusMap);
        return final_result;
    };

    /**
     * 处理评测结果
     * @param {string} text 代替换的文本
     * @returns {object} 处理后的文本和颜色
     */
    const process = (text) => {
        /**
         * 当前评测状态
         */
        const { status_name, number } = getStatusName(text);

        /**
         * 当前评测状态的缩写
         */
        const statusAcronym = getStatusAcronym(status_name);

        return {
            text: statusAcronym ? getReplaceText(status_name, number) : text,
            color: getStatusColor(statusAcronym)
        }
    };

    OJB_observeElement({
        selector: '.datatable',
        callback: (node) => {
            const updateElement = (element) => {
                const { text, color } = process($(element).text());
                $(element).text(text);
                $(element).css({
                    "color": color,
                    "font-weight": "700"
                });
            };

            // 选择器
            const selectorCondition = "[class^='verdict-'], [submissionverdict='COMPILATION_ERROR']";

            // 检查[node]本身是否符合选择器条件
            if ($(node).is(selectorCondition)) {
                updateElement(node);
            }

            // 更新[node]内部符合选择器条件的所有元素
            $(node).find(selectorCondition).each(function () {
                updateElement(this);
            });
        }
    });
}

/**
 * 存放编辑器语言select的值与Monaco语言对应关系的map.
 * @type {Object.<string, string>}
 */
const value_monacoLanguageMap = {
    "4": "pascal", "6": "php", "7": "python", "9": "csharp", "13": "perl", "20": "scala", "31": "python",
    "32": "go", "34": "javascript", "36": "java", "40": "python", "41": "python", "43": "cpp",
    "50": "cpp", "51": "pascal", "52": "cpp", "54": "cpp", "55": "javascript", "59": "cpp", "60": "java",
    "61": "cpp", "65": "csharp", "67": "ruby", "70": "python", "73": "cpp", "74": "java", "75": "rust",
    "77": "kotlin", "79": "csharp", "80": "cpp", "83": "kotlin", "87": "java"
};

/**
 * 更新代码提交页的HTML
 * @param {string} submitUrl 提交页面的URL
 * @param {string} cacheKey 本地缓存的键名
 * @returns {Promise<jQuery<HTMLElement>>} 返回 jQuery 包装的 HTML 元素
 */
async function CloneOriginalHTML(submitUrl, cacheKey) {
    return OJB_promiseRetryWrapper(async () => {
        const response = await OJB_GMRequest({
            method: 'GET',
            url: submitUrl
        });
        const html = response.responseText;
        const parser = new DOMParser();
        const doc = parser.parseFromString(html, 'text/html');
        const cloneHTML = $(doc.body).html();
        localStorage.setItem(cacheKey, html);
        return $(cloneHTML);
    }, {
        maxRetries: 5,
        retryInterval: 1000,
        errorHandler: (err) => {
            console.error('A network error occurred while retrieving the HTML for the code submission page.', submitUrl);
        }
    });
}

/**
 * 获取代码提交页的HTML元素
 * @param {string} submitUrl
 * @returns {Promise<jQuery>}
 */
async function getSubmitHTML(submitUrl) {
    const cacheKey = 'OJBetter_CloneOriginalHTML';
    const cookieKey = 'OJBetter_CloneOriginalHTML_time';
    if (OJB_getCookie(cookieKey) === '1') {
        // 存在缓存
        CloneOriginalHTML(submitUrl, cacheKey);
        // 校验
        let cloneHTML = $(localStorage.getItem(cacheKey));
        if (cloneHTML.find('form.submit-form').length > 0) {
            return cloneHTML;
        } else {
            // 存在错误,更新缓存
            console.warn(`Cache error detected!\nattempting to update, cache destination submitUrl:\n${submitUrl}`);
            return await CloneOriginalHTML(submitUrl, cacheKey);
        }

    } else {
        // 没有缓存,更新
        document.cookie = `${cookieKey}=1; path=/`;
        return await CloneOriginalHTML(submitUrl, cacheKey);
    }
}

// 代码自动保存
async function saveCode(url, code) {
    try {
        await OJBetter.common.database.editorCode.put({ url, code });
        return 'Code saved successfully';
    } catch (error) {
        throw new Error('Failed to save code');
    }
}

async function getCode(url) {
    try {
        const result = await OJBetter.common.database.editorCode.get(url);
        return result ? result.code : null;
    } catch (error) {
        throw new Error('Failed to get code');
    }
}

// 创建代码编辑调试表单元素
async function createCodeEditorForm(submitUrl, cloneHTML) {
    // 表单
    let formDiv = $('<form method="post" id="OJBetter_SubmitForm" class="input-output-copier"></form>');
    $('.ttypography').after(formDiv);
    formDiv.attr('action', submitUrl + "?csrf_token=" + OJBetter.common.cf_csrf_token);

    // 顶部区域
    let topDiv = OJB_safeCreateJQElement(`<div class="topDiv"></div>`);
    let selectLang = cloneHTML.find('select[name="programTypeId"]'); // 语言选择
    selectLang.css({ 'margin': '10px 0px' }).attr('id', 'programTypeId');
    topDiv.append(selectLang);
    let topRightDiv = OJB_safeCreateJQElement(`<div class="topRightDiv"></div>`);
    topDiv.append(topRightDiv);
    formDiv.append(topDiv);

    // 问题选择/编号
    let selectProblem = $('<input name="submittedProblemIndex" style="display:none;"></input>');
    let problemCode;
    if (OJBetter.typeOfPage.is_acmsguru) {
        problemCode = $('h4').eq(0).text();
        let matchResult = problemCode.match(/([A-Z0-9]+)/);
        problemCode = matchResult[0];
    } else if (OJBetter.typeOfPage.is_problemset_problem) {
        let match = window.location.href.match(/\/problem\/([0-9]+?)\/([A-Za-z0-9]+)/);
        problemCode = match[1] + match[2];
        selectProblem.attr('name', 'submittedProblemCode');
    } else {
        problemCode = $('.header .title').eq(0).text();
        let matchResult = problemCode.match(/([A-Z0-9]+)/);
        problemCode = matchResult[0];
    }
    selectProblem.val(problemCode);
    formDiv.append(selectProblem);

    // 隐藏的代码记录
    let sourceDiv = $('<textarea id="sourceCodeTextarea" name="source" style="display: none;"></textarea>');
    formDiv.append(sourceDiv);

    // 代码编辑器
    let editorDiv = $('<div id="OJBetter_editor"></div>');
    formDiv.append(editorDiv);

    // monaco
    let monaco = $('<div id="OJBetter_monaco"></div>');
    editorDiv.append(monaco);

    // 自定义调试
    let customTestDiv = OJB_safeCreateJQElement(`
        <details id="customTestBlock">
            <summary >${i18next.t('customTestBlock.title', { ns: 'codeEditor' })}</summary>
            <div id="customTests" style="min-height: 30px;"></div>
            <div id="control" style="display:flex;">
                <div style="display: flex;margin: 5px;">
                    <input type="checkbox" id="onlyCustomTest"}><label for="onlyCustomTest">
                    ${i18next.t('customTestBlock.onlyCustom', { ns: 'codeEditor' })}
                    </label>
                </div>
                <div style="display: flex;margin: 5px;">
                    <input type="checkbox" id="DontShowDiff"}>
                    <label for="DontShowDiff">
                        ${i18next.t('customTestBlock.DontShowDiff', { ns: 'codeEditor' })}
                    </label>
                </div>
                <button type="button" id="addCustomTest">${i18next.t('customTestBlock.add', { ns: 'codeEditor' })}</button>
            </div>
        </details>
    `)
    formDiv.append(customTestDiv);

    // 调试/提交
    let submitDiv = $('<div id="OJBetter_submitDiv"></div>');
    let CompilerArgsInput = $('<input type="text" id="CompilerArgsInput">');
    submitDiv.append(CompilerArgsInput);

    let runButton = OJB_safeCreateJQElement(`
        <button type="button" id="RunTestButton" class="ojb_btn ojb_btn_popover top">
            <i class="iconfont">&#xe6c1;</i>
            <span class="popover_content">${i18next.t('runTestButton.initial', { ns: 'codeEditor' })}</span>
        </button>
    `);
    let submitButton = OJB_safeCreateJQElement(`
        <button id="SubmitButton" class="ojb_btn ojb_btn_popover top" type="submit">
            <i class="iconfont">&#xe633;</i>
            <span class="popover_content">${i18next.t('submitButton', { ns: 'codeEditor' })}</span>
        </button>
    `);
    if (OJBetter.monaco.setting.submitButtonPosition == "bottom") {
        // 添加测试/提交按钮到底部
        submitDiv.append(runButton);
        submitDiv.append(submitButton);
    }

    formDiv.append(submitDiv);
    let CompilerSetting = OJB_safeCreateJQElement(`
        <div id="CompilerSetting"></div>
    `);
    formDiv.append(CompilerSetting);
    let statePanel = OJB_safeCreateJQElement(`
        <div id="statePanel"></div>
    `);
    formDiv.append(statePanel);

    let from = {
        formDiv: formDiv,
        selectLang: selectLang,
        topRightDiv: topRightDiv,
        sourceDiv: sourceDiv,
        editorDiv: editorDiv,
        monaco: monaco,
        runButton: runButton,
        submitButton: submitButton,
        submitDiv: submitDiv,
        CompilerSetting: CompilerSetting,
        statePanel: statePanel
    };
    return from;
}

// 解析ace格式的补全规则(acwing)
function parseAceCompleter(rules, range) {
    const suggestions = [];
    if (rules && rules.templates && rules.templates.items) {
        const items = rules.templates.items;
        for (let i = 0; i < items.length; i++) {
            const item = items[i];
            const parts = item.caption.split(' ');
            for (let i = 0; i < parts.length; i++) {
                if (item.value.startsWith(parts[i])) {
                    item.value = item.value.replace(parts[i], parts.slice(0, i + 1).join(' '));
                    break;
                }
            }
            const completionItem = {
                label: item.caption,
                kind: monaco.languages.CompletionItemKind.Function,
                insertText: item.value,
                range: range
            };
            suggestions.push(completionItem);
        }
    }
    return { suggestions };
}

// 解析monaco格式的补全规则
function parseMonacoCompleter(rules, range) {
    const suggestions_ = [];
    if (rules && rules.suggestions) {
        const suggestion = rules.suggestions;
        for (let i = 0; i < rules.suggestions.length; i++) {
            const item = suggestion[i];
            const completionItem = {
                ...item,
                range: range
            };
            suggestions_.push(completionItem);
        }
    }
    return { suggestions: suggestions_ };
}

/**
 * 创建monaco编辑器的一个实例
 */
async function createMonacoEditor(language, form, support) {
    // 判断monacoLoader是否加载完毕
    await OJB_waitUntilTrue(() => OJBetter.monaco.loaderOnload);

    /**
     * 通用参数
     */
    var id = 0; // 协议中的id标识
    var workspace = language + "_workspace";
    var rootUri = OJBetter.monaco.lsp.workUri + "/" + workspace;
    // 文件名
    var InstanceID = OJB_getRandomNumber(8).toString();
    var filename = language == "java" ? "hello/src/" + InstanceID : InstanceID;
    // 后缀名
    var fileExtension =
        language === "cpp"
            ? ".cpp"
            : language === "python"
                ? ".py"
                : language === "java"
                    ? ".java"
                    : "";
    var uri = rootUri + "/" + filename + fileExtension;
    var initialized = false; // 是否已初始化
    var serverInfo; // 服务器返回的支持信息
    var model; // model
    var OJBetter_monaco = {};
    window.OJBetter_monaco = OJBetter_monaco; // 全局方法

    /**
     * 一些工具函数
     */
    // 将lsp格式的rang转换为Monaco格式
    OJBetter_monaco.lspRangeToMonacoRange = function (range) {
        const { start, end } = range;
        return new monaco.Range(
            start.line + 1,
            start.character + 1,
            end.line + 1,
            end.character + 1
        );
    };
    // 将Monaco格式的rang转为lsp格式
    OJBetter_monaco.MonacoRangeTolspRange = function (range) {
        return {
            start: {
                line: range.startLineNumber - 1,
                character: range.startColumn - 1,
            },
            end: {
                line: range.endLineNumber - 1,
                character: range.endColumn - 1,
            },
        };
    };
    // 将Monaco格式的position转为lsp格式的
    OJBetter_monaco.MonacoPositionTolspPosition = function (position) {
        return {
            line: position.lineNumber - 1,
            character: position.column - 1,
        };
    };
    // 将Monaco格式的severity转为lsp格式的
    OJBetter_monaco.MonacoSeverityTolspSeverity = function (severity) {
        switch (severity) {
            case 8:
                return 1;
            case 1:
                return 4;
            case 2:
                return 3;
            case 4:
                return 2;
            default:
                return severity;
        }
    };
    // 将lsp格式的severity转为Monaco格式的
    OJBetter_monaco.lspSeverityToMonacoSeverity = function (severity) {
        switch (severity) {
            case 1:
                return 8;
            case 4:
                return 1;
            case 3:
                return 2;
            case 2:
                return 4;
            default:
                return severity;
        }
    };
    // 收集Monaco数据中的rang数据
    OJBetter_monaco.CollectRange = function (item) {
        return {
            startLineNumber: item.startLineNumber,
            startColumn: item.startColumn,
            endLineNumber: item.endLineNumber,
            endColumn: item.endColumn,
        };
    };
    // 收集Monaco position数据中的rang数据
    OJBetter_monaco.CollectRangeByPosition = function (item) {
        var word = model.getWordUntilPosition(item);
        return {
            startLineNumber: item.lineNumber,
            endLineNumber: item.lineNumber,
            startColumn: word.startColumn,
            endColumn: word.endColumn,
        };
    };
    // 将lsp格式的Edit转换为Monaco格式
    OJBetter_monaco.lspEditToMonacoEdit = function (edit) {
        const edits = [];

        if (language == "python") {
            for (const item1 of edit.documentChanges) {
                for (const item2 of item1.edits) {
                    const newElement = {
                        textEdit: {
                            range: OJBetter_monaco.lspRangeToMonacoRange(item2.range),
                            text: item2.newText,
                        },
                        resource: monaco.Uri.parse(item1.textDocument.uri),
                        versionId: model.getVersionId(),
                    };
                    edits.push(newElement);
                }
            }
        } else if (language == "java") {
            for (const item1 in edit.changes) {
                edit.changes[item1].forEach((item2) => {
                    const newElement = {
                        textEdit: {
                            range: OJBetter_monaco.lspRangeToMonacoRange(item2.range),
                            text: item2.newText,
                        },
                        resource: uri,
                        versionId: model.getVersionId(),
                    };
                    edits.push(newElement);
                });
            }
        } else {
            for (const key in edit.changes) {
                const arr = edit.changes[key];
                for (const item of arr) {
                    const newElement = {
                        textEdit: {
                            range: OJBetter_monaco.lspRangeToMonacoRange(item.range),
                            text: item.newText,
                        },
                        resource: monaco.Uri.parse(key),
                        versionId: model.getVersionId(),
                    };
                    edits.push(newElement);
                }
            }
        }
        return { edits: edits };
    };

    /**
     * 实例化一个editor
     */
    uri = monaco.Uri.file(uri);
    model = monaco.editor.createModel('', language, uri);
    OJBetter.monaco.editor = monaco.editor.create(document.getElementById("OJBetter_monaco"), {
        model: model,
        rootUri: rootUri,
        fontSize: 15,
        tabSize: 4,
        theme: OJBetter.basic.darkMode == "dark" ? "vs-dark" : "vs",
        bracketPairColorization: {
            enabled: true,
            independentColorPoolPerBracketType: true,
        },
        automaticLayout: true,
        lineNumbersMinChars: 3,
        matchOnWordStartOnly: false,
        wordWrap: "on",
        wrappingIndent: "same",
        glyphMargin: true,
        formatOnType: true,
        scrollbar: {
            verticalScrollbarSize: 10,
            horizontalScrollbarSize: 10,
            alwaysConsumeMouseWheel: OJBetter.monaco.setting.alwaysConsumeMouseWheel
        },
        suggest: {
            selectionMode: 'never' // 代码建议不自动选择
        }
    });

    /**
     * 添加快捷功能
     */
    (OJBetter_monaco.addShortCuts = async () => {
        // 从配置信息更新字体大小
        OJBetter.monaco.editor.updateOptions({ fontSize: parseInt(OJBetter.monaco.setting.fontsize) });

        // 调整字体大小
        let changeSize = OJB_safeCreateJQElement(`
        <div class="ojb_btn ojb_btn_popover top">
            <input type="number" id="fontSizeInput" value="${OJBetter.monaco.setting.fontsize}">
            <span class="popover_content">${i18next.t('fontSizeInput', { ns: 'codeEditor' })}</span>
        </div>`)
        form.topRightDiv.append(changeSize);
        changeSize.find('input#fontSizeInput').on('input', function () {
            var size = $(this).val();
            OJBetter.monaco.editor.updateOptions({ fontSize: parseInt(size) });
            GM_setValue('editorFontSize', size);
        });

        // 全屏按钮
        let fullscreenButton = OJB_safeCreateJQElement(`
        <button type="button" class="ojb_btn ojb_btn_popover top">
            <i class="iconfont">&#xe606;</i>
            <span class="popover_content">${i18next.t('fullscreenButton', { ns: 'codeEditor' })}</span>
        </button>
        `);
        form.topRightDiv.append(fullscreenButton);
        fullscreenButton.on('click', enterFullscreen);

        // 固定到底部按钮
        let fixToBottomButton = OJB_safeCreateJQElement(`
        <button type="button" class="ojb_btn ojb_btn_popover top">
            <i class="iconfont">&#xe607;</i>
            <span class="popover_content">${i18next.t('fixToBottomButton', { ns: 'codeEditor' })}</span>
        </button>
        `);
        form.topRightDiv.append(fixToBottomButton);
        fixToBottomButton.on('click', fixToBottom);

        // 固定到右侧按钮
        let fixToRightButton = OJB_safeCreateJQElement(`
        <button type="button" class="ojb_btn ojb_btn_popover top">
            <i class="iconfont">&#xe605;</i>
            <span class="popover_content">${i18next.t('fixToRightButton', { ns: 'codeEditor' })}</span>
        </button>
        `);
        form.topRightDiv.append(fixToRightButton);
        fixToRightButton.on('click', fixToRight);

        // 添加测试/提交按钮到顶部
        if (OJBetter.monaco.setting.submitButtonPosition == "top") {
            form.topRightDiv.append(form.runButton);
            form.topRightDiv.append(form.submitButton);
        }

        // 选择记忆
        if (!OJBetter.monaco.setting.position_initialized) {
            OJBetter.monaco.setting.position_initialized = true; // 标记是否已经初始化过
            if (OJBetter.monaco.setting.position == "full") {
                fullscreenButton.click();
            } else if (OJBetter.monaco.setting.position == "bottom") {
                fixToBottomButton.click();
            } else if (OJBetter.monaco.setting.position == "right") {
                fixToRightButton.click();
            }
        }

        // 禁用按钮
        function disableButtons() {
            fullscreenButton.prop("disabled", true);
            fixToBottomButton.prop("disabled", true);
            fixToRightButton.prop("disabled", true);
        }

        // 启用按钮
        function enableButtons() {
            fullscreenButton.prop("disabled", false);
            fixToBottomButton.prop("disabled", false);
            fixToRightButton.prop("disabled", false);
        }

        // 进入全屏
        function enterFullscreen() {
            let editor = $('#OJBetter_editor');
            editor.addClass('fullscreen');

            // 取消按钮
            let cancelButton = OJB_safeCreateJQElement(`
                <button type="button" class="ojb_btn ojb_btn_popover top primary exit_button_bottom">
                    <i class="iconfont">&#xe60b;</i>
                    <span class="popover_content">${i18next.t('exitFullscreenButton', { ns: 'codeEditor' })}</span>
                </button>
            `).on('click', () => exitFullscreen(cancelButton));
            $('body').append(cancelButton);

            disableButtons();
            GM_setValue("monacoEditor_position", "full");
        }

        // 退出全屏
        const exitFullscreen = (cancelButton) => {
            let editor = $('#OJBetter_editor');
            editor.removeClass('fullscreen');
            cancelButton.remove();
            enableButtons();
            GM_setValue("monacoEditor_position", "initial");
        };

        // 固定到底部
        function fixToBottom() {
            let editor = $('#OJBetter_editor');
            editor.addClass('bottom');

            let halfHeight = $(window).height() * 0.5;
            let blankSpace = $('<div>', {
                'class': 'blank-space',
                'style': 'height: ' + (halfHeight + 30) + 'px;'
            });
            $('body').append(blankSpace);

            let cancelButton = OJB_safeCreateJQElement(`
                <button type="button" class="ojb_btn ojb_btn_popover top enabled exit_button_bottom">
                    <i class="iconfont">&#xe625;</i>
                    <span class="popover_content">${i18next.t('cancelFixButton', { ns: 'codeEditor' })}</span>
                </button>
            `).on('click', () => cancelFixingToBottom(cancelButton, blankSpace));
            $('body').append(cancelButton);

            disableButtons();
            GM_setValue("monacoEditor_position", "bottom");
        }

        // 取消固定到底部
        const cancelFixingToBottom = (cancelButton, blankSpace) => {
            let editor = $('#OJBetter_editor');
            editor.removeClass('bottom');
            cancelButton.remove();
            blankSpace.remove();
            enableButtons();
            GM_setValue("monacoEditor_position", "initial");
        };

        // 固定到右侧边栏
        function fixToRight() {
            const sidebar = $('#sidebar').hide();

            // 添加样式
            const styleElement = GM_addStyle(`
                #body {
                    min-width: 50vw;
                    max-width: 50vw;
                    max-height: 100vh;
                    overflow-x: hidden;
                    overflow-y: auto;
                    padding: 1rem;
                    box-sizing: border-box;
                }
                body {
                    margin: 0px;
                }
                .content-with-sidebar {
                    margin-right: 0px !important;
                }
                .menu-list li {
                    margin-right: 0.5em;
                }
                .menu-list li a {
                    font-size: 1.4rem;
                }
                #OJBetter_editor{
                    height: 100vh;
                    width: 50vw;
                }
            `);

            // 包装一层div
            $('#body').wrap('<div id="right-side-wrapper" style="display:flex; max-width: 100vw; overflow: hidden;"></div>');
            const blankSpace = $('<div>').appendTo('#right-side-wrapper');

            // 移动编辑器
            const editor = $('#OJBetter_editor').prependTo(blankSpace).addClass('right-side');

            // 取消按钮
            const cancelButton = OJB_safeCreateJQElement(`
                <button type="button" class="ojb_btn ojb_btn_popover top enabled exit_button_bottom">
                    <i class="iconfont">&#xe625;</i>
                    <span class="popover_content">${i18next.t('cancelFixButton', { ns: 'codeEditor' })}</span>
                </button>
            `).on('click', () => cancelFixingToRight(sidebar, styleElement, editor, cancelButton)).appendTo('body');

            disableButtons();
            GM_setValue("monacoEditor_position", "right");

            // 补丁:修复固定到右侧导致的样例元素.sample-test相关代码重复执行的问题(具体原因未查)
            $('.sample-test').find('.title').each((i, e) => {
                if ($(e).find('.input-output-copier').length > 1) {
                    $(e).find('.input-output-copier').first().remove();
                }
            });
            darkModeStyleAdjustment();
        }

        const cancelFixingToRight = (sidebar, styleElement, editor, cancelButton) => {
            sidebar.show();
            // 移回来
            editor.insertAfter(form.sourceDiv).removeClass('right-side');

            // 移除包装
            $('#body').unwrap();
            cancelButton.remove();
            styleElement.remove(); // 移除添加的样式

            enableButtons();
            GM_setValue("monacoEditor_position", "initial");
        }

        // 代码同步与保存
        if (OJBetter.monaco.setting.autoMemoryCode) {
            let nowUrl = window.location.href;
            nowUrl = nowUrl.replace(/#/, ""); // 当页面存在更改时url会多出一个#,去掉
            const code = await getCode(nowUrl);
            if (code) {
                OJBetter.monaco.editor.setValue(code); // 恢复代码
                form.sourceDiv.val(code);
            }
            OJBetter.monaco.editor.onDidChangeModelContent(async () => {
                // 将monaco editor的内容同步到sourceDiv
                const code = OJBetter.monaco.editor.getValue();
                form.sourceDiv.val(code);
                await saveCode(nowUrl, code);
            });
        }
    })();

    /**
     * 注册本地自动补全
     */
    (OJBetter_monaco.RegisterLocalComplet = async () => {
        // 补全器注册函数
        function registMyCompletionItemProvider(language, genre, rule) {
            if (genre == "monaco") {
                monaco.languages.registerCompletionItemProvider(language, {
                    provideCompletionItems: function (model, position) {
                        return parseMonacoCompleter(rule, OJBetter_monaco.CollectRangeByPosition(position));
                    }
                })
            } else if (genre == "ace") {
                monaco.languages.registerCompletionItemProvider(language, {
                    provideCompletionItems: function (model, position) {
                        return parseAceCompleter(rule, OJBetter_monaco.CollectRangeByPosition(position));
                    }
                })
            }
        }

        // 注册acwing cpp 模板
        if (language == "cpp" && OJBetter.monaco.complet.cppCodeTemplate) {
            try {
                var acwing_cpp_code_completer = JSON.parse(GM_getResourceText("acwing_cpp_code_completer"));
                registMyCompletionItemProvider('cpp', 'ace', acwing_cpp_code_completer);
            } catch (error) {
                console.error("Error registering acwing cpp template:", error);
            }
        }

        // 注册自定义的补全
        let complet_length = OJBetter.monaco.complet.customConfig.configurations.length;
        if (complet_length > 0) {
            for (let i = 0; i < complet_length; i++) {
                let item = OJBetter.monaco.complet.customConfig.configurations[i];
                if (item.isChoose && item.language == language) {
                    try {
                        let rule = await OJB_getExternalJSON(item.jsonUrl);
                        registMyCompletionItemProvider(item.language, item.genre, rule);
                    } catch (error) {
                        console.error(`Error registering custom completer for ${item.language}:`, error);
                    }
                }
            }
        }
    })();

    if (!support || !OJBetter.monaco.lsp.enabled) { return; } // 如果不支持lsp,则到此为止

    /**
     * LSP连接状态指示
     */
    const lspStateButton = OJB_safeCreateJQElement(`
    <div id="lspStateDiv" class="ojb_btn ojb_btn_popover top loading">
        <i class="iconfont">&#xe658;</i>
        <span class="popover_content">${i18next.t('lsp.connect', { ns: 'codeEditor' })}</span>
    </div>
    `).on('click', () => {
        OJB_showModal(LSPLogDiv);
        LSPLogDiv.show();
    });
    form.topRightDiv.prepend(lspStateButton);

    const LSPLogDiv = OJB_safeCreateJQElement(`
    <dialog id="LSPLog" style="display: none;">
        <button class="ojb_btn">${i18next.t('close', { ns: 'common' })}</button>
        <div id="LSPLogList" style="overflow: auto;"></div>
    <dialog>`);
    $('body').append(LSPLogDiv);

    const LSPLogList = $('<ul></ul>');
    $('#LSPLogList').append(LSPLogList);

    const closeButton = LSPLogDiv.find('button');
    closeButton.on('click', function () {
        OJB_closeModal(LSPLogDiv);
    });

    /**
     * 推送新的消息到LSP日志中
     * @param {'error' | 'warn' | 'info'} status
     * @param {string} msg
     * @param {boolean} data
     */
    function pushLSPLogMessage(status, msg, data) {
        var li = $('<li>').text('[' + new Date().toLocaleString() + '] ' + msg);
        if (status === 'error') {
            li.attr('style', 'color: #f44336;');
        } else if (status === 'warn') {
            li.attr('style', 'color: #ff9800;');
        } else if (status === 'info') {
            li.attr('style', 'color: #616161;');
        }
        if (data) {
            var jsonText = JSON.stringify(data, null, 2);
            var details = $('<details>');
            var summary = $('<summary>').text('Data');
            var pre = $('<pre>').text(jsonText);
            details.append(summary, pre);
            li.append(details);
        }
        LSPLogList.append(li);
    }

    /**
     * 添加状态底栏
     */
    var statusBar = $('<div id="OJBetter_statusBar">');
    form.editorDiv.append(statusBar);

    /**
     * languageSocket
     */
    var url = OJBetter.monaco.lsp.socketUrl;
    var languageSocket = new WebSocket(url + language);
    OJBetter.monaco.lsp.socket.push(languageSocket);
    var languageSocketState = false;
    var responseHandlers = new Map(); // 映射表,需要等待返回数据的请求 -> 对应的事件触发函数

    languageSocket.onopen = () => {
        languageSocketState = true;
        lspStateButton.setButtonPopover(i18next.t('lsp.waitingAnswer', { ns: 'codeEditor' }));
        pushLSPLogMessage("info", `languageSocket ${i18next.t('lsp.socket.open', { ns: 'logMessage' })}`);
    };
    languageSocket.onmessage = (event) => {
        const message = JSON.parse(event.data);
        if (message.id === 0 && message.result) {
            // 初始化完成
            lspStateButton.setButtonState('success', i18next.t('lsp.connected', { ns: 'codeEditor' }));
            pushLSPLogMessage("info", `Initialization ${i18next.t('lsp.state.finished', { ns: 'logMessage' })}`);
            serverInfo = message.result; // 存下服务器支持信息
            OJBetter_monaco.openDocRequest(); // 打开文档
            if (!OJBetter.monaco.setting.language.includes(language)) {
                OJBetter.monaco.setting.language.push(language);
                OJBetter_monaco.RegistrationAfterInit(); // 注册语言及功能
            } else {
                location.reload(); // 这里有问题,先贴个补丁
            }
            OJBetter_monaco.PassiveReceiveHandler(); // 注册被动接收函数
        } else if (message.id === 0 && message.error) {
            pushLSPLogMessage("warn", `Initialization ${i18next.t('lsp.state.error', { ns: 'logMessage' })}`);
        } else if (message.id !== undefined && responseHandlers.has(message.id)) {
            // 如果收到带有id字段的消息,则回传给对应的事件触发函数
            const handler = responseHandlers.get(message.id);
            if (handler) {
                handler(message);
                responseHandlers.delete(message.id); // 删除已处理的事件触发函数
            }
        } else if (message.method == "textDocument/publishDiagnostics") {
            // 接收代码诊断推送
            OJBetter_monaco.updateMarkers(message);
        } else if (message.method == "workspace/applyEdit") {
            // 应用服务器推送的更改
            OJBetter_monaco.applyEdit(message);
        }
    };
    languageSocket.onerror = (error) => {
        pushLSPLogMessage("error", `languageSocket ${i18next.t('lsp.state.error', { ns: 'logMessage' })}`, error);
        console.warn(`Error connecting to languageSocket: ${error}`)
    };
    languageSocket.onclose = (event) => {
        languageSocketState = false;
        lspStateButton.setButtonState('error', i18next.t('lsp.error', { ns: 'codeEditor' }));
        pushLSPLogMessage("warn", `languageSocket ${i18next.t('lsp.socket.close', { ns: 'logMessage' })}`);
    };

    /**
     * 等待LanguageSocketState
     */
    async function waitForLanguageSocketState() {
        return new Promise((resolve) => {
            const checkInitialized = () => {
                if (languageSocketState) {
                    resolve();
                } else {
                    setTimeout(checkInitialized, 100); // 每100毫秒检查一次initialized的值
                }
            };
            checkInitialized();
        });
    }

    // 等待lsp响应初始化结果
    async function waitForInitialized() {
        return new Promise((resolve) => {
            const checkInitialized = () => {
                if (initialized) {
                    resolve();
                } else {
                    setTimeout(checkInitialized, 100); // 每100毫秒检查一次initialized的值
                }
            };
            checkInitialized();
        });
    }

    /**
     * 与languageSocket通信的包装方法
     */
    async function sendMessage(data, requiresResponse, callback) {
        if (!initialized) {
            await waitForInitialized(); // 等待initialized为真
        }
        if (requiresResponse) {
            responseHandlers.set(data.id, callback) // 将事件触发函数与id关联起来
        }
        if (!languageSocketState) await waitForLanguageSocketState();
        languageSocket.send(JSON.stringify(data));
    }
    // 发送消息并等待返回结果
    function fetchData(params, callback) {
        sendMessage(params, true, callback);
    }
    // 发送消息,不需要等待返回结果
    function sendData(data) {
        sendMessage(data, false);
    }

    /**
     * 代码文件更新fileWebSocket
     */
    var fileWebSocket = new WebSocket(url + "file");
    var fileWebSocketState = false;
    OJBetter.monaco.lsp.socket.push(fileWebSocket);
    fileWebSocket.onopen = () => {
        fileWebSocketState = true;
        pushLSPLogMessage("info", `fileWebSocket ${i18next.t('lsp.socket.open', { ns: 'logMessage' })}`);
    };
    fileWebSocket.onclose = (ev) => {
        fileWebSocketState = false;
        pushLSPLogMessage("warn", `fileWebSocket ${i18next.t('lsp.socket.close', { ns: 'logMessage' })}`, ev);
    };
    fileWebSocket.onmessage = (ev) => {
        let message = JSON.parse(ev.data);
        if (message.result !== "ok")
            pushLSPLogMessage("error", `update file failed: ${ev}`);
    };
    fileWebSocket.onerror = (error) => {
        console.warn(`Error connecting to fileWebSocket: ${error}`);
    };
    async function updateFile(workspace, filename, fileExtension, code) {
        async function waitForfileWebSocketState() {
            return new Promise((resolve) => {
                const checkInitialized = () => {
                    if (fileWebSocketState) {
                        resolve();
                    } else {
                        setTimeout(checkInitialized, 100); // 每100毫秒检查一次initialized的值
                    }
                };
                checkInitialized();
            });
        }
        if (!fileWebSocketState) await waitForfileWebSocketState();
        fileWebSocket.send(
            JSON.stringify({
                type: "update",
                workspace,
                filename,
                fileExtension,
                code,
            })
        );
    }

    /**
     * 发送初始化请求
     */
    OJBetter_monaco.Initialize = () => {
        //初始化initialize
        const capabilities = {
            workspace: {
                applyEdit: true,
            },
            textDocument: {
                publishDiagnostics: {
                    relatedInformation: true,
                    versionSupport: false,
                    tagSupport: {
                        valueSet: [1, 2],
                    },
                    codeDescriptionSupport: true,
                },
                completion: {
                    contextSupport: true,
                    completionItem: {
                        snippetSupport: true,
                        commitCharactersSupport: true,
                        documentationFormat: ["markdown", "plaintext"],
                        deprecatedSupport: true,
                        preselectSupport: true,
                        tagSupport: {
                            valueSet: [1],
                        },
                        insertReplaceSupport: true,
                        resolveSupport: {
                            properties: [
                                "documentation",
                                "detail",
                                "additionalTextEdits",
                            ],
                        },
                        insertTextModeSupport: {
                            valueSet: [1, 2],
                        },
                    },
                },
                hover: {
                    dynamicRegistration: true,
                    contentFormat: ["markdown", "plaintext"],
                },
                signatureHelp: {
                    signatureInformation: {
                        documentationFormat: ["markdown", "plaintext"],
                        parameterInformation: {
                            labelOffsetSupport: true,
                        },
                        activeParameterSupport: true,
                    },
                    contextSupport: true,
                },
                definition: {
                    dynamicRegistration: true,
                    linkSupport: true,
                },
                references: {
                    dynamicRegistration: true,
                },
                documentHighlight: {
                    dynamicRegistration: true,
                },
                codeAction: {
                    codeActionLiteralSupport: {
                        codeActionKind: {
                            valueSet:
                                language == "java"
                                    ? []
                                    : [
                                        "",
                                        "quickfix",
                                        "refactor",
                                        "refactor.extract",
                                        "refactor.inline",
                                        "refactor.rewrite",
                                        "source",
                                        "source.organizeImports",
                                    ],
                        },
                    },
                },
                rename: {
                    dynamicRegistration: true,
                    prepareSupport: true,
                    prepareSupportDefaultBehavior: 1,
                    honorsChangeAnnotations: true,
                },
                documentLink: {
                    tooltipSupport: true,
                },
                typeDefinition: {
                    dynamicRegistration: true,
                    linkSupport: true,
                },
                implementation: {
                    dynamicRegistration: true,
                    linkSupport: true,
                },
                colorProvider: {
                    dynamicRegistration: true,
                },
                foldingRange: {
                    dynamicRegistration: true,
                    rangeLimit: 5000,
                    lineFoldingOnly: true,
                },
                declaration: {
                    dynamicRegistration: true,
                    linkSupport: true,
                },
                semanticTokens: {
                    dynamicRegistration: true,
                    tokenTypes: [
                        "namespace",
                        "type",
                        "class",
                        "enum",
                        "interface",
                        "struct",
                        "typeParameter",
                        "parameter",
                        "variable",
                        "property",
                        "enumMember",
                        "event",
                        "function",
                        "method",
                        "macro",
                        "keyword",
                        "modifier",
                        "comment",
                        "string",
                        "number",
                        "regexp",
                        "operator",
                    ],
                    tokenModifiers: [
                        "declaration",
                        "definition",
                        "readonly",
                        "static",
                        "deprecated",
                        "abstract",
                        "async",
                        "modification",
                        "documentation",
                        "defaultLibrary",
                    ],
                    formats: ["relative"],
                    requests: {
                        range: true,
                        full: {
                            delta: true,
                        },
                    },
                    multilineTokenSupport: false,
                    overlappingTokenSupport: false,
                },
                callHierarchy: {
                    dynamicRegistration: true,
                },
            },
            window: {
                showMessage: {
                    messageActionItem: {
                        additionalPropertiesSupport: true,
                    },
                },
                showDocument: {
                    support: true,
                },
                workDoneProgress: true,
            },
            general: {
                regularExpressions: {
                    engine: "ECMAScript",
                    version: "ES2020",
                },
                markdown: {
                    parser: "marked",
                    version: "1.1.0",
                },
            },
        };

        const initializeRequest = {
            id: id++,
            jsonrpc: "2.0",
            method: "initialize",
            params: {
                processId: null,
                clientInfo: {
                    name: "CFMonaco" + InstanceID,
                },
                locale: "zh-CN",
                rootPath: null,
                rootUri: null,
                capabilities: capabilities,
                trace: "off",
                workspaceFolders: [
                    {
                        uri:
                            "file:///" + OJBetter.monaco.lsp.workUri + workspace,
                        name:
                            "file:///" + OJBetter.monaco.lsp.workUri + workspace,
                    },
                ],
            },
        };
        languageSocket.send(JSON.stringify(initializeRequest));

        // 打开文档函数
        OJBetter_monaco.openDocRequest = function () {
            const initializ = {
                jsonrpc: "2.0",
                method: "initialized",
                params: {},
            };
            languageSocket.send(JSON.stringify(initializ));
            const openDocRequest = {
                jsonrpc: "2.0",
                method: "textDocument/didOpen",
                params: {
                    textDocument: {
                        uri: model.uri.toString(),
                        languageId: language,
                        version: model.getVersionId(),
                        text: model.getValue(),
                    },
                },
            };
            languageSocket.send(JSON.stringify(openDocRequest));
            initialized = true; // 初始化完成,这里确认逻辑待完善
        };

        // 初始化更新文件
        updateFile(workspace, filename, fileExtension, model.getValue());
    }

    /**
     * 注册语言及功能
     */
    OJBetter_monaco.RegistrationAfterInit = () => {
        // 注册语言
        monaco.languages.register({ id: language });

        // 注册"Command"
        (function registerCommand() {
            serverInfo.capabilities.executeCommandProvider.commands.forEach(
                (item) => {
                    pushLSPLogMessage("info", `${i18next.t('lsp.server.regist', { ns: 'logMessage' })}`, item);
                    monaco.editor.registerCommand(item, (accessor, ...args) => {
                        sendData({
                            jsonrpc: "2.0",
                            id: id++,
                            method: "workspace/executeCommand",
                            params: {
                                command: item,
                                arguments: args,
                            },
                        });
                    });
                }
            );
        })();

        // 注册"增量更新"
        model.onDidChangeContent((event) => {
            updateFile(workspace, filename, fileExtension, model.getValue()); // 更新文件
            const changeDocRequest = {
                jsonrpc: "2.0",
                method: "textDocument/didChange",
                params: {
                    textDocument: {
                        uri: model.uri.toString(),
                        version: model.getVersionId(),
                    },
                    contentChanges: event.changes.map((change) => ({
                        range: OJBetter_monaco.MonacoRangeTolspRange(change.range),
                        rangeLength: change.rangeLength,
                        text: change.text,
                    })),
                },
            };
            sendData(changeDocRequest);
        });

        //注册"自动补全"
        monaco.languages.registerCompletionItemProvider(language, {
            provideCompletionItems: (model, position, context) => {
                const request = {
                    jsonrpc: "2.0",
                    id: id++,
                    method: "textDocument/completion",
                    params: {
                        textDocument: {
                            uri: model.uri.toString(),
                        },
                        position: OJBetter_monaco.MonacoPositionTolspPosition(position),
                        context: {
                            triggerKind: context.triggerKind + 1, // 这里要+1,两边的定义不一样。。。
                            triggerCharacter: context.triggerCharacter,
                        },
                    },
                };
                return new Promise((resolve, reject) => {
                    fetchData(request, (response) => {
                        const result = response.result;
                        pushLSPLogMessage("info", `completion ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);
                        if (!result) return resolve(null);
                        const CompletionItems = {
                            suggestions: result.items.map(
                                ({
                                    label,
                                    kind,
                                    filterText,
                                    insertText,
                                    insertTextFormat,
                                    sortText,
                                    textEdit,
                                    documentation,
                                    additionalTextEdits,
                                }) => ({
                                    additionalTextEdits: additionalTextEdits
                                        ? additionalTextEdits.map(({ newText, range }) => ({
                                            text: newText,
                                            range: OJBetter_monaco.lspRangeToMonacoRange(range),
                                        }))
                                        : [],
                                    documentation: documentation ? documentation.value : "",
                                    filterText,
                                    insertText: insertText ? insertText : textEdit.newText,
                                    insertTextRules:
                                        insertTextFormat === 2
                                            ? monaco.languages.CompletionItemInsertTextRule
                                                .InsertAsSnippet
                                            : monaco.languages.CompletionItemInsertTextRule
                                                .KeepWhitespace,
                                    kind,
                                    label,
                                    sortText,
                                    range: textEdit
                                        ? textEdit.range
                                            ? OJBetter_monaco.lspRangeToMonacoRange(textEdit.range)
                                            : OJBetter_monaco.lspRangeToMonacoRange(textEdit.insert)
                                        : null,
                                })
                            ),
                        };
                        pushLSPLogMessage("info", `completion ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, CompletionItems);
                        resolve(CompletionItems);
                    });
                });
            },
        });

        // 注册"代码修复"
        monaco.languages.registerCodeActionProvider(language, {
            provideCodeActions: (model, range, context) => {
                const request = {
                    id: id++,
                    jsonrpc: "2.0",
                    method: "textDocument/codeAction",
                    params: {
                        textDocument: {
                            uri: model.uri.toString(),
                        },
                        range: OJBetter_monaco.MonacoRangeTolspRange(range),
                        context: {
                            diagnostics: context.markers.map((item) => ({
                                range: OJBetter_monaco.MonacoRangeTolspRange({
                                    startLineNumber: item.startLineNumber,
                                    startColumn: item.startColumn,
                                    endLineNumber: item.endLineNumber,
                                    endColumn: item.endColumn,
                                }),
                                severity: OJBetter_monaco.MonacoSeverityTolspSeverity(
                                    item.severity
                                ),
                                code: item.code,
                                source: item.source,
                                message: item.message,
                                tags: item.tags,
                                relatedInformation: item.relatedInformation
                                    ? item.relatedInformation.map((item) => ({
                                        location: {
                                            uri: item.resource.toString(),
                                            range: OJBetter_monaco.MonacoRangeTolspRange({
                                                startLineNumber: item.startLineNumber,
                                                startColumn: item.startColumn,
                                                endLineNumber: item.endLineNumber,
                                                endColumn: item.endColumn,
                                            }),
                                        },
                                        message: item.message,
                                    }))
                                    : null,
                            })),
                            only: context.only ? [context.only] : [],
                            triggerKind: context.trigger,
                        },
                    },
                };

                return new Promise((resolve, reject) => {
                    fetchData(request, (response) => {
                        const result = response.result;
                        pushLSPLogMessage("info", `codeAction ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);
                        if (!result) return resolve(null);
                        const codeAction = {
                            actions: result.map((item) => ({
                                title: item.title,
                                kind: item.kind ? item.kind : "quickfix",
                                command: item.command
                                    ? item.command.command
                                        ? {
                                            id: item.command.command,
                                            arguments: item.command.arguments,
                                            title: item.command.title,
                                        }
                                        : null
                                    : null,
                                diagnostics: item.diagnostics
                                    ? item.diagnostics.map((item) => ({
                                        code: item.code,
                                        message: item.message,
                                        range: OJBetter_monaco.lspRangeToMonacoRange(item.range),
                                        severity: OJBetter_monaco.lspSeverityToMonacoSeverity(
                                            item.severity
                                        ),
                                        source: item.source,
                                    }))
                                    : null,
                                edit: item.edit
                                    ? OJBetter_monaco.lspEditToMonacoEdit(item.edit)
                                    : item.arguments
                                        ? {
                                            edits: item.arguments.flatMap(
                                                (item1) => OJBetter_monaco.lspEditToMonacoEdit(item1).edits
                                            ),
                                        }
                                        : null,
                            })),
                            dispose: () => { },
                        };
                        pushLSPLogMessage("info", `codeAction ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, codeAction);

                        resolve(codeAction);
                    });
                });
            },
        });

        // 注册"hover提示"
        monaco.languages.registerHoverProvider(language, {
            provideHover: (model, position) => {
                const request = {
                    jsonrpc: "2.0",
                    id: id++,
                    method: "textDocument/hover",
                    params: {
                        textDocument: {
                            uri: model.uri.toString(),
                        },
                        position: OJBetter_monaco.MonacoPositionTolspPosition(position),
                    },
                };

                return new Promise((resolve, reject) => {
                    fetchData(request, (response) => {
                        pushLSPLogMessage("info", `Hover ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);
                        const result = response.result;

                        if (!result) return resolve(null);
                        const Hover = {
                            range: result.range
                                ? OJBetter_monaco.lspRangeToMonacoRange(result.range)
                                : new monaco.Range(
                                    position.lineNumber,
                                    position.column,
                                    position.lineNumber,
                                    position.column
                                ),
                            contents: Array.isArray(result.contents)
                                ? result.contents.map((item) => ({
                                    value: item.value ? item.value : item,
                                }))
                                : [
                                    {
                                        value: result.contents.value,
                                    },
                                ],
                        };
                        pushLSPLogMessage("info", `Hover ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, Hover);
                        resolve(Hover);
                    });
                });
            },
        });

        // 注册"inlay提示"
        if (language == "cpp" || language == "java")
            monaco.languages.registerInlayHintsProvider(language, {
                provideInlayHints: (model, range, token) => {
                    return new Promise((resolve, reject) => {
                        const request = {
                            jsonrpc: "2.0",
                            id: id++,
                            method: "textDocument/inlayHint",
                            params: {
                                textDocument: {
                                    uri: model.uri.toString(),
                                },
                                range: OJBetter_monaco.MonacoRangeTolspRange(range),
                            },
                        };

                        fetchData(request, (response) => {
                            const result = response.result;
                            pushLSPLogMessage("info", `Inlay Hints ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);

                            if (!result) return resolve(null);

                            const inlayHints = {
                                hints: result.map((item) => {
                                    return {
                                        kind: item.kind,
                                        label: item.label,
                                        paddingLeft: item.paddingLeft,
                                        paddingRight: item.paddingRight,
                                        position: {
                                            lineNumber: item.position.line + 1,
                                            column: item.position.character + 1,
                                        },
                                    };
                                }),
                            };
                            pushLSPLogMessage("info", `Inlay Hints ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, inlayHints);

                            resolve(inlayHints);
                        });
                    });
                },
            });

        // 注册"转到定义"
        monaco.languages.registerDefinitionProvider(language, {
            provideDefinition: (model, position) => {
                const request = {
                    jsonrpc: "2.0",
                    id: id++,
                    method: "textDocument/definition",
                    params: {
                        textDocument: {
                            uri: model.uri.toString(),
                        },
                        position: OJBetter_monaco.MonacoPositionTolspPosition(position),
                    },
                };

                return new Promise((resolve, reject) => {
                    fetchData(request, (response) => {
                        const result = response.result;
                        pushLSPLogMessage("info", `definition ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);

                        if (result.length == 0) return resolve(null);
                        const definition = result.map((item) => ({
                            range: OJBetter_monaco.lspRangeToMonacoRange(item.range),
                            uri: monaco.Uri.parse(item.uri), //
                        }));
                        pushLSPLogMessage("info", `definition ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, definition);

                        resolve(definition);
                    });
                });

            },
        });

        // 注册"转到引用"
        monaco.languages.registerReferenceProvider(language, {
            provideReferences: (model, position, context) => {
                const request = {
                    jsonrpc: "2.0",
                    id: id++,
                    method: "textDocument/references",
                    params: {
                        context: context,
                        textDocument: {
                            uri: model.uri.toString(),
                        },
                        position: OJBetter_monaco.MonacoPositionTolspPosition(position),
                    },
                };

                return new Promise((resolve, reject) => {
                    fetchData(request, (response) => {
                        const result = response.result;
                        pushLSPLogMessage("info", `references ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);

                        if (result.length == 0) return resolve([]);

                        const references = result.map((item) => ({
                            range: OJBetter_monaco.lspRangeToMonacoRange(item.range),
                            uri: monaco.Uri.parse(item.uri), //
                        }));
                        pushLSPLogMessage("info", `references ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, references);
                        resolve(references);
                    });
                });
            },
        });

        // 注册"符号引用点击高亮"
        monaco.languages.registerDocumentHighlightProvider(language, {
            provideDocumentHighlights: (model, position) => {
                const request = {
                    jsonrpc: "2.0",
                    id: id++,
                    method: "textDocument/documentHighlight",
                    params: {
                        textDocument: {
                            uri: model.uri.toString(),
                        },
                        position: OJBetter_monaco.MonacoPositionTolspPosition(position),
                    },
                };

                return new Promise((resolve, reject) => {
                    fetchData(request, (response) => {
                        const result = response.result;
                        pushLSPLogMessage("info", `documentHighlight ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);

                        if (!result || result.length == 0) return resolve([]);
                        const highlights = result.map((item) => ({
                            range: OJBetter_monaco.lspRangeToMonacoRange(item.range),
                            kind: item.kind,
                        }));
                        pushLSPLogMessage("info",
                            `documentHighlight ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`,
                            highlights
                        );

                        resolve(highlights);
                    });
                });
            },
        });

        // 注册"文件链接"
        if (language == "cpp" || language == "java")
            monaco.languages.registerLinkProvider(language, {
                provideLinks: (model) => {
                    const request = {
                        jsonrpc: "2.0",
                        id: id++,
                        method: "textDocument/documentLink",
                        params: {
                            textDocument: {
                                uri: model.uri.toString(),
                            },
                        },
                    };

                    return new Promise((resolve, reject) => {
                        fetchData(request, (response) => {
                            const result = response.result;
                            pushLSPLogMessage("info", `DocumentLink ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);

                            if (!result) return resolve(null);
                            const links = {
                                links: result.map((item) => ({
                                    range: OJBetter_monaco.lspRangeToMonacoRange(item.range),
                                    url: item.target.toString(),
                                    tooltip: item.tooltip ? item.tooltip : null,
                                })),
                            };
                            pushLSPLogMessage("info", `DocumentLink ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, links);
                            resolve(links);
                        });
                    });
                },
            });

        // 注册"格式化"
        monaco.languages.registerDocumentFormattingEditProvider(language, {
            provideDocumentFormattingEdits: (model, options, token) => {
                const request = {
                    jsonrpc: "2.0",
                    id: id++,
                    method: "textDocument/formatting",
                    params: {
                        textDocument: {
                            uri: model.uri.toString(),
                        },
                        options: options,
                    },
                };

                return new Promise((resolve, reject) => {
                    fetchData(request, (response) => {
                        const result = response.result;
                        pushLSPLogMessage("info", `formatting ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);

                        const TextEdit = result.map((edit) => ({
                            range: OJBetter_monaco.lspRangeToMonacoRange(edit.range),
                            text: edit.newText,
                        }));
                        pushLSPLogMessage("info", `formatting ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, TextEdit);
                        resolve(TextEdit);
                    });
                });
            },
        });

        // 注册"部分格式化"
        monaco.languages.registerDocumentRangeFormattingEditProvider(language, {
            provideDocumentRangeFormattingEdits: (model, range, options) => {
                const request = {
                    jsonrpc: "2.0",
                    id: id++,
                    method: "textDocument/rangeFormatting",
                    params: {
                        textDocument: {
                            uri: model.uri.toString(),
                        },
                        range: OJBetter_monaco.MonacoRangeTolspRange(range),
                        options,
                    },
                };

                return new Promise((resolve, reject) => {
                    fetchData(request, (response) => {
                        const result = response.result;
                        pushLSPLogMessage("info", `rangeFormatting ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);

                        if (!result || result.length == 0) return resolve([]);
                        const edits = result.map((item) => ({
                            range: OJBetter_monaco.lspRangeToMonacoRange(item.range),
                            text: item.newText,
                        }));
                        pushLSPLogMessage("info", `rangeFormatting ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, edits);
                        resolve(edits);
                    });
                });
            },
        });

        // 注册"重命名"
        monaco.languages.registerRenameProvider(language, {
            provideRenameEdits: (model, position, newName, token) => {
                const request = {
                    jsonrpc: "2.0",
                    id: id++,
                    method: "textDocument/rename",
                    params: {
                        textDocument: {
                            uri: model.uri.toString(),
                        },
                        position: OJBetter_monaco.MonacoPositionTolspPosition(position),
                        newName: newName,
                    },
                };

                return new Promise((resolve, reject) => {
                    fetchData(request, (response) => {
                        const result = response.result;
                        pushLSPLogMessage("info", `rename ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);

                        const rename = OJBetter_monaco.lspEditToMonacoEdit(result);
                        pushLSPLogMessage("info", `rename ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, rename);
                        resolve(rename);
                    });
                });
            },
        });

        // 注册"折叠范围分析"
        monaco.languages.registerFoldingRangeProvider(language, {
            provideFoldingRanges: (model) => {
                const request = {
                    jsonrpc: "2.0",
                    id: id++,
                    method: "textDocument/foldingRange",
                    params: {
                        textDocument: {
                            uri: model.uri.toString(),
                        },
                    },
                };

                return new Promise((resolve, reject) => {
                    fetchData(request, (response) => {
                        const result = response.result;
                        pushLSPLogMessage("info", `FoldingRange ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);

                        if (!result) return resolve([]);
                        const foldingRanges = result.map((item) => ({
                            start: item.startLine + 1,
                            end: item.endLine + 1,
                            kind: monaco.languages.FoldingRangeKind.fromValue(item.kind),
                        }));
                        pushLSPLogMessage("info", `FoldingRange ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, foldingRanges);
                        resolve(foldingRanges);
                    });
                });
            },
        });

        // 注册"方法签名提示"
        monaco.languages.registerSignatureHelpProvider(language, {
            signatureHelpTriggerCharacters:
                serverInfo.capabilities.signatureHelpProvider.triggerCharacters,
            provideSignatureHelp: (model, position, token, context) => {
                const request = {
                    jsonrpc: "2.0",
                    id: id++,
                    method: "textDocument/signatureHelp",
                    params: {
                        textDocument: {
                            uri: model.uri.toString(),
                        },
                        position: {
                            line: position.lineNumber - 1,
                            character: position.column - 1,
                        },
                        context: {
                            triggerKind: context.triggerKind,
                            triggerCharacter: context.triggerCharacter,
                            isRetrigger: context.isRetrigger,
                            activeSignatureHelp: context.activeSignatureHelp,
                        },
                    },
                };

                return new Promise((resolve, reject) => {
                    fetchData(request, (response) => {
                        const result = response.result;

                        pushLSPLogMessage("info", `signatureHelp ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);

                        if (!result) return resolve(null);
                        const SignatureHelpResult = {
                            value: {
                                activeParameter: result.activeParameter,
                                activeSignature: result.activeSignature,
                                signatures: result.signatures,
                            },
                            dispose: () => { },
                        };

                        pushLSPLogMessage("info",
                            `signatureHelp ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`,
                            SignatureHelpResult
                        );
                        resolve(SignatureHelpResult);
                    });
                });
            },
        });

        // 注册"渐进式自动格式化" 如果server有这个
        if (serverInfo.capabilities.documentOnTypeFormattingProvider)
            monaco.languages.registerOnTypeFormattingEditProvider(language, {
                autoFormatTriggerCharacters: [
                    serverInfo.capabilities.documentOnTypeFormattingProvider
                        .firstTriggerCharacter,
                ],
                provideOnTypeFormattingEdits: (model, position, ch, options) => {
                    const request = {
                        jsonrpc: "2.0",
                        id: id++,
                        method: "textDocument/onTypeFormatting",
                        params: {
                            textDocument: {
                                uri: model.uri.toString(),
                            },
                            position: OJBetter_monaco.MonacoPositionTolspPosition(position),
                            ch,
                            options,
                        },
                    };

                    return new Promise((resolve, reject) => {
                        fetchData(request, (response) => {
                            const result = response.result;
                            pushLSPLogMessage("info", `onTypeFormatting ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, response);

                            if (!result || result.length == 0) return resolve([]);

                            const edits = result.map((item) => ({
                                range: OJBetter_monaco.lspRangeToMonacoRange(item.range),
                                text: item.newText,
                            }));
                            pushLSPLogMessage("info", `onTypeFormatting ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, edits);
                            resolve(edits);
                        });
                    });
                },
            });
    };

    /**
     * 被动式接收处理
     */
    OJBetter_monaco.PassiveReceiveHandler = () => {

        // "实时代码诊断"
        OJBetter_monaco.updateMarkers = function (message) {
            const params = message.params;
            pushLSPLogMessage("info", `Markers ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, message);

            if (!params) return;
            const markers = params.diagnostics.map((item1) => ({
                code: item1.code,
                message: item1.message,
                ...OJBetter_monaco.lspRangeToMonacoRange(item1.range),
                relatedInformation: item1.relatedInformation
                    ? item1.relatedInformation.map((item2) => ({
                        ...(item2.location.range
                            ? OJBetter_monaco.lspRangeToMonacoRange(item2.location.range)
                            : OJBetter_monaco.lspRangeToMonacoRange(item2.location)),
                        message: item2.message,
                        resource: monaco.Uri.parse(item2.location.uri),
                    }))
                    : null,
                severity: OJBetter_monaco.lspSeverityToMonacoSeverity(item1.severity),
                source: item1.source,
            }));

            pushLSPLogMessage("info", `Markers ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, markers);
            monaco.editor.setModelMarkers(model, "eslint", markers);

            // 更新状态底栏信息
            const nowMarks = monaco.editor.getModelMarkers();
            warningCount = 0;
            errorCount = 0;
            for (const marker of nowMarks) {
                if (marker.severity === monaco.MarkerSeverity.Warning) {
                    warningCount++;
                } else if (marker.severity === monaco.MarkerSeverity.Error) {
                    errorCount++;
                }
            }
            $('#OJBetter_statusBar').text(`Warnings: ${warningCount}, Errors: ${errorCount}`);
        };

        // "应用服务器推送的更改"(代码修复)
        OJBetter_monaco.applyEdit = function (message) {
            const params = message.params;
            pushLSPLogMessage("info", `applyEdit ${i18next.t('lsp.server.receive', { ns: 'logMessage' })}`, message);

            if (!params) return;
            const operations = Object.values(params.edit.changes)
                .flat()
                .map((item) => ({
                    range: OJBetter_monaco.lspRangeToMonacoRange(item.range),
                    text: item.newText,
                }));

            pushLSPLogMessage("info", `applyEdit ${i18next.t('lsp.server.transmit', { ns: 'logMessage' })}`, operations);
            model.pushEditOperations([], operations, () => null); // 入栈编辑操作
        };
    }

    if (!languageSocketState) await waitForLanguageSocketState();
    OJBetter_monaco.Initialize();
}

// 语言更改
function changeMonacoLanguage(form) {
    let nowSelect = form.selectLang.val();
    // 记忆更改
    GM_setValue('compilerSelection', nowSelect);
    // 销毁旧的编辑器
    try {
        if (OJBetter.monaco.editor) OJBetter.monaco.editor.dispose();
    } catch (error) {
        console.warn("Encountered an error while attempting to delete the old editor, but it should not affect your regular usage.", error)
    }
    // 关闭旧的socket
    OJBetter.monaco.lsp.socket.forEach(socket => {
        socket.close();
    });
    // 移除相关元素
    form.topRightDiv.empty();
    $('#LSPLog').remove();
    $('#OJBetter_statusBar').remove();
    // 创建新的编辑器
    if (nowSelect in value_monacoLanguageMap) {
        let language = value_monacoLanguageMap[nowSelect];
        if (language == "python" || language == "cpp") {
            createMonacoEditor(language, form, true);
        } else {
            createMonacoEditor(language, form, false);
        }
    } else {
        createMonacoEditor(null, form, false);
    }
    // 更新在线编译器参数
    changeCompilerArgs(nowSelect);
}

// 收集样例数据
function getTestData() {
    let testData = {};

    /**
     * 从pre中获取文本信息
     * @param {JQuery<HTMLElement>} node 元素
     * @returns {string} 文本信息
     */
    function getTextFromPre(node) {
        let text;
        if (node.find("br").length > 0) {
            text = node.html().replace(/<br>/g, "\n"); // <br>作换行符的情况
        } else {
            text = node.text();
        }
        return text;
    }

    $('.input').each(function (index) {
        var inputText = '';
        if ($(this).find('pre').find('div').length > 0) {
            $(this).find('pre').find('div').each(function () {
                inputText += getTextFromPre($(this)) + '\n';
            });
        } else {
            inputText = getTextFromPre($(this).find('pre'));
        }
        var outputText = '';
        if ($('.output').eq(index).find('pre').find('div').length > 0) {
            $('.output').eq(index).find('pre').find('div').each(function () {
                inputText += getTextFromPre($(this)) + '\n';
            });
        } else {
            outputText = getTextFromPre($('.output').eq(index).find('pre'));
        }

        testData[index + 1] = {
            input: inputText.trim(),
            output: outputText.trim()
        };
    });
    return testData;
}

// 初始化自定义测试数据面板
function CustomTestInit() {
    const url = window.location.href;

    restoreText();

    // 添加
    $('#addCustomTest').click(function () {
        var sampleDiv = $('<div class="sampleDiv">');
        var inputTextarea = $('<p style="padding: 0px 5px;">input</p><textarea class="dynamicTextarea inputTextarea"></textarea>');
        var outputTextarea = $('<p style="padding: 0px 5px;">output</p><textarea class="dynamicTextarea outputTextarea"></textarea>');
        var deleteCustomTest = OJB_safeCreateJQElement(`<button type="button" class="deleteCustomTest">${closeIcon}</button>`);
        sampleDiv.append(deleteCustomTest);
        sampleDiv.append(inputTextarea);
        sampleDiv.append(outputTextarea);
        $('#customTests').append(sampleDiv);
    });

    // 实时保存文本内容到 IndexedDB 中
    $(document).on('input', '.inputTextarea, .outputTextarea', function () {
        OJBetter.common.database.transaction('rw', OJBetter.common.database.samplesData, function () {
            var objectStore = OJBetter.common.database.samplesData;
            var samples = {
                url: url,
                samples: []
            };
            var index = 0;
            $('.sampleDiv').each(function () {
                var $sampleDiv = $(this);
                var inputTextarea = $sampleDiv.find('.inputTextarea');
                var outputTextarea = $sampleDiv.find('.outputTextarea');
                $sampleDiv.attr('data-index', index);
                inputTextarea.attr('id', 'input' + index);
                outputTextarea.attr('id', 'output' + index);
                var sample = {
                    id: index,
                    input: inputTextarea.val(),
                    output: outputTextarea.val()
                };
                samples.samples.push(sample);
                index++;
            });
            objectStore.put(samples);
        });
    });

    // 删除
    $(document).on('click', '.deleteCustomTest', function () {
        var $sampleDiv = $(this).closest('.sampleDiv');
        OJBetter.common.database.transaction('rw', OJBetter.common.database.samplesData, function () {
            var objectStore = OJBetter.common.database.samplesData;
            var index = parseInt($sampleDiv.attr('data-index'));
            if (!isNaN(index)) {
                objectStore.get(url).then(row => {
                    let samples = row.samples;
                    samples.splice(index, 1); // 移除第index个元素
                    objectStore.put({
                        url: url,
                        samples: samples
                    });
                })
            }
            $sampleDiv.remove();
        });
    });

    // 恢复保存的内容
    function restoreText() {
        OJBetter.common.database.transaction('r', OJBetter.common.database.samplesData, function () {
            return OJBetter.common.database.samplesData.get(url);
        }).then(function (data) {
            if (data.samples && data.samples.length > 0) {
                data.samples.forEach(function (item, index) {
                    var sampleDiv = $('<div class="sampleDiv">');
                    var inputTextarea = OJB_safeCreateJQElement(`<p style="padding: 0px 5px;">input</p><textarea id="input${index}" class="dynamicTextarea inputTextarea"></textarea>`);
                    var outputTextarea = OJB_safeCreateJQElement(`<p style="padding: 0px 5px;">output</p><textarea id="output${index}" class="dynamicTextarea outputTextarea"></textarea>`);
                    var deleteCustomTest = OJB_safeCreateJQElement(`<button type="button" class="deleteCustomTest">${closeIcon}</button>`);

                    inputTextarea.val(item.input);
                    outputTextarea.val(item.output);

                    sampleDiv.append(deleteCustomTest);
                    sampleDiv.append(inputTextarea);
                    sampleDiv.append(outputTextarea);
                    sampleDiv.attr('data-index', index)
                    $('#customTests').append(sampleDiv);
                });
            }
        });
    }
}

// 获取自定义测试数据
function getCustomTestData() {
    const url = window.location.href;

    return new Promise(function (resolve) {
        var customTestData = {};
        OJBetter.common.database.transaction('r', OJBetter.common.database.samplesData, function () {
            return OJBetter.common.database.samplesData.get(url);
        }).then(function (data) {
            if (!data) resolve(customTestData);
            if (data.samples && data.samples.length > 0) {
                data.samples.forEach(function (item, index) {
                    customTestData[index + 1] = {
                        input: item.input,
                        output: item.output
                    };
                });
            }
            resolve(customTestData);
        });
    });
}

// codeforces编译器参数列表
let officialLanguage = "";
function officialCompilerArgsChange(nowSelect) {
    officialLanguage = nowSelect;
    $('#CompilerArgsInput').prop("disabled", true);
}

// codeforces编译器通信
async function officialCompiler(code, input) {
    const data = new FormData();
    data.append('csrf_token', OJBetter.common.cf_csrf_token);
    data.append('source', code);
    data.append('tabSize', '4');
    data.append('programTypeId', officialLanguage);
    data.append('input', input);
    data.append('output', '');
    data.append('communityCode', '');
    data.append('action', 'submitSourceCode');
    data.append('programTypeId', officialLanguage);
    data.append('sourceCode', code);

    const requestOptions = {
        method: 'POST',
        url: `${OJBetter.common.hostAddress}/data/customtest`,
        data: data,
        headers: {
            'X-Csrf-Token': OJBetter.common.cf_csrf_token
        }
    };

    const result = {
        Errors: '',
        Result: '',
        Stats: ''
    };

    try {
        const submitResponse = await OJB_GMRequest(requestOptions);
        if (submitResponse.status !== 200 || !submitResponse.response) {
            result.Errors = `${i18next.t('compiler.official.pushError', { ns: 'codeEditor' })}`;
            return result;
        }

        const submitResult = JSON.parse(submitResponse.response);
        const customTestSubmitId = submitResult.customTestSubmitId;

        const verdictResponse = await OJB_promiseRetryWrapper(
            getOfficialCompilerVerdict,
            {
                maxRetries: 10,
                retryInterval: 500
            },
            customTestSubmitId
        );
        return verdictResponse;
    } catch (error) {
        result.Errors = error.message;
        return result;
    }
}

// 获取codeforces编译器的执行结果
async function getOfficialCompilerVerdict(customTestSubmitId) {
    const newdata = new FormData();
    newdata.append('csrf_token', OJBetter.common.cf_csrf_token);
    newdata.append('action', 'getVerdict');
    newdata.append('customTestSubmitId', customTestSubmitId);

    const requestOptions = {
        method: 'POST',
        url: `${OJBetter.common.hostAddress}/data/customtest`,
        data: newdata,
        headers: {
            'X-Csrf-Token': OJBetter.common.cf_csrf_token
        }
    };

    const responseDetails = await OJB_GMRequest(requestOptions);
    if (responseDetails.status !== 200 || !responseDetails.response) {
        throw new Error(`${i18next.t('compiler.official.getResultError', { ns: 'codeEditor' })}`);
    }

    const response = JSON.parse(responseDetails.response);
    if (!response.stat) {
        throw new Error('Verdict not ready, retrying...');
    }

    return {
        Errors: response.verdict === "OK" ? null : response.verdict + '<br>' + response.output,
        Result: response.output.replace(/\r\n/g, "\n"),
        Stats: `Status: ${response.stat}`
    };
}

// rextester编译器参数列表
let rextesterLanguage = "";
function rextesterCompilerArgsChange(nowSelect) {
    let LanguageChoiceList = {
        "4": "9", "6": "8", "7": "5", "9": "1", "13": "13", "19": "42", "20": "21", "28": "30", "31": "24", "32": "20",
        "34": "17", "36": "4", "43": "6", "45": "7", "46": "4", "50": "7", "51": "9", "52": "27", "54": "7", "55": "23", "60": "4",
        "61": "7", "65": "1", "67": "12", "70": "5", "73": "7", "74": "4", "75": "46", "77": "43", "79": "1", "80": "27", "83": "43", "87": "4"
    }
    let CompilerArgsList = {
        "6": "-Wall -std=gnu99 -O2 -o a.out source_file.c",
        "7": "-Wall -std=c++14 -O2 -o a.out source_file.cpp",
        "20": "-o a.out source_file.go",
        "27": "-Wall -std=c++14 -stdlib=libc++ -O2 -o a.out source_file.cpp",
        "30": "source_file.d -ofa.out"
    }
    if (nowSelect in LanguageChoiceList) {
        $('#RunTestButton').prop("disabled", false);
        rextesterLanguage = LanguageChoiceList[nowSelect];
    } else {
        $('#RunTestButton').prop("disabled", true);
    }
    if (rextesterLanguage in CompilerArgsList) {
        const rextesterCompilerArgs = CompilerArgsList[rextesterLanguage];
        $('#CompilerArgsInput').val(rextesterCompilerArgs);
    } else {
        $('#CompilerArgsInput').val("");
    }
}

// rextester编译器通信
async function rextesterCompiler(code, input) {
    try {
        const result = await OJB_promiseRetryWrapper(rextesterCompilerRequest, {
            maxRetries: 5,
            retryInterval: 500,
            errorHandler: (err) => ({ Errors: err.message, Result: '', Stats: '' })
        }, code, input);
        return result;
    } catch (error) {
        return { Errors: error.message, Result: '', Stats: '' };
    }
}

// rextester编译器请求方法
async function rextesterCompilerRequest(code, input) {
    const data = new FormData();
    data.append('LanguageChoiceWrapper', rextesterLanguage);
    data.append('EditorChoiceWrapper', '1');
    data.append('LayoutChoiceWrapper', '1');
    data.append('Program', code);
    data.append('CompilerArgs', $('#CompilerArgsInput').val());
    data.append('Input', input);
    data.append('ShowWarnings', 'false');
    data.append('IsInEditMode', 'false');
    data.append('IsLive', 'false');

    const responseDetails = await OJB_GMRequest({
        method: 'POST',
        url: 'https://rextester.com/rundotnet/Run',
        data: data
    });

    if (responseDetails.status !== 200 || !responseDetails.response) {
        throw new Error(`${i18next.t('compiler.common.error', { ns: 'codeEditor' })}`);
    }

    const response = JSON.parse(responseDetails.response);
    return {
        Errors: response.Errors || '',
        Result: response.Result || '',
        Stats: response.Stats || ''
    };
}

// wandbox编译器参数列表
var wandboxlist = JSON.parse(GM_getResourceText("wandboxlist"));
function wandboxCompilerArgsChange(nowSelect) {
    let LanguageChoiceList = {
        "6": "PHP", "7": "Python", "9": "C#", "12": "Haskell", "13": "Perl", "19": "OCaml",
        "20": "Scala", "28": "D", "31": "Python", "32": "Go", "34": "JavaScript", "36": "Java", "40": "Python", "41": "Python",
        "43": "C++", "50": "C++", "51": "Pascal", "52": "C++", "54": "C++", "60": "Java", "61": "C++", "65": "C#", "67": "Ruby",
        "70": "Python", "73": "C++", "74": "Java", "75": "Rust", "79": "C#", "80": "C++", "87": "Java"
    }

    // 移除旧的
    $('#CompilerChange').remove();

    if (nowSelect in LanguageChoiceList) {
        $('#RunTestButton').prop("disabled", false);
        const Languagefiltered = wandboxlist.filter(obj => obj.language === LanguageChoiceList[nowSelect]);

        // 创建编译器下拉框
        let CompilerChange = $('<select id="CompilerChange" style="width: 100%;"></select>');

        $('#CompilerSetting').show().append(CompilerChange);
        for (let i = 0; i < Languagefiltered.length; i++) {
            let Compiler = Languagefiltered[i];
            let op = $("<option></option>")
                .val(Compiler.name)
                .text(Compiler["display-name"] + " " + Compiler.version);
            $("#CompilerChange").append(op);
        }

        // 编译器参数刷新
        function refreshCompilerArgs() {
            var flags = '';
            $("#CompilerBox").find("*").each(function () {
                if ($(this).is("input[type='checkbox']")) {
                    let flag = $(this).prop("checked") ? $(this).val() : '';
                    flags += flag + (flag ? ' ' : '');
                } else if ($(this).is("select") || $(this).is("input") || $(this).is("textarea")) {
                    let flag = $(this).val();
                    flags += flag + (flag ? ' ' : '');
                }
            });
            $("#CompilerArgsInput").val(flags);
            $("#CompilerArgsInput").prop("readonly", true); // 只读
        }

        // 编译器切换监听
        CompilerChange.change(function () {
            let selectedName = CompilerChange.val();
            let Compiler = Languagefiltered.find(
                (obj) => obj.name === selectedName
            );

            $("#CompilerArgsInput").val(); // 初始化编译器输入框

            $("#CompilerBox").remove();
            let div = $("<div id='CompilerBox'></div>");

            let display_compile_command = OJB_safeCreateJQElement(`<input id='${Compiler.name}' value='${Compiler['display-compile-command']}' style="display:none;"}></input>`);
            div.append(display_compile_command);

            let switches = Compiler.switches;
            for (let i = 0; i < switches.length; i++) {
                let switche = switches[i];

                if (switche.type == "single") {
                    let single = OJB_safeCreateJQElement(`
                    <div>
                        <input type='checkbox' id='${switche.name}' value='${switche['display-flags']}' ${switche.default ? 'checked' : ''}></input>
                        <label for='${switche.name}'>${switche['display-name']}</label>
                    </div>
                    `);
                    div.append(single);
                    single.find("input").change(function () {
                        refreshCompilerArgs();
                    });
                } else if (switche.type == "select") {
                    let select = OJB_safeCreateJQElement(`<select id='${switche.name}'></select>`);
                    select.data('previousValue', switche.options[0]['display-flags']);
                    div.append(select);
                    for (let i = 0; i < switche.options.length; i++) {
                        let option = switche.options[i];
                        let op = $("<option></option>")
                            .val(option['display-flags'])
                            .text(option['display-name']);
                        select.append(op);
                    }
                    select.change(function () {
                        refreshCompilerArgs();
                    });
                }
            }

            if (Compiler['compiler-option-raw'] == true) {
                let textarea = OJB_safeCreateJQElement(`<textarea id="compiler_option_raw" placeholder="Raw compiler options" style="resize: vertical;"></textarea>`);
                div.append(textarea);
                textarea.on('input', function () {
                    refreshCompilerArgs();
                });
            }
            if (Compiler['runtime-option-raw'] == true) {
                let textarea = OJB_safeCreateJQElement(`<textarea id="runtime_option_raw" placeholder="Raw runtime options" style="resize: vertical;"></textarea>`);
                div.append(textarea);
                textarea.on('input', function () {
                    refreshCompilerArgs();
                });
            }
            $("#CompilerSetting").append(div);

            refreshCompilerArgs();  // 初始化
        });

        CompilerChange.trigger("change"); // 初始化
    } else {
        $('#RunTestButton').prop("disabled", true);
    }
}

// wandbox编译器通信
async function wandboxCompiler(code, input) {
    try {
        const result = await OJB_promiseRetryWrapper(wandboxCompilerRequest, {
            maxRetries: 5,
            retryInterval: 500,
            errorHandler: (err) => ({ Errors: err.message, Result: '', Stats: '' })
        }, code, input);
        return result;
    } catch (error) {
        return { Errors: error.message, Result: '', Stats: '' };
    }
}

// wandbox编译器请求方法
async function wandboxCompilerRequest(code, input) {
    const data = {
        code: code,
        codes: [],
        compiler: $('#CompilerChange').val().replace($('#compiler_option_raw').val(), '').replace($('#runtime_option_raw').val(), ''),
        'compiler-option-raw': $('#compiler_option_raw').val(),
        'runtime-option-raw': $('#runtime_option_raw').val(),
        options: $("#CompilerArgsInput").val(),
        description: '',
        stdin: input,
        title: ''
    };

    const responseDetails = await OJB_GMRequest({
        method: 'POST',
        url: 'https://wandbox.org/api/compile.json',
        data: JSON.stringify(data),
        headers: {
            'Content-Type': 'application/json'
        }
    });

    if (responseDetails.status !== 200 || !responseDetails.response) {
        throw new Error(`${i18next.t('compiler.common.error', { ns: 'codeEditor' })}`);
    }

    const response = JSON.parse(responseDetails.response);
    return {
        Errors: response.compiler_error === "" ? response.signal : response.compiler_error,
        Result: response.program_output || '',
        Stats: response.status === "0" ? "Finish" : "Error"
    };
}

// 更改编译器参数
function changeCompilerArgs(nowSelect) {
    if (OJBetter.monaco.onlineCompilerChoice == "official") {
        officialCompilerArgsChange(nowSelect);
    } else if (OJBetter.monaco.onlineCompilerChoice == "rextester") {
        rextesterCompilerArgsChange(nowSelect);
    } else if (OJBetter.monaco.onlineCompilerChoice == "wandbox") {
        wandboxCompilerArgsChange(nowSelect);
    }
}

// 在线编译器通信
async function onlineCompilerConnect(code, input) {
    if (OJBetter.monaco.onlineCompilerChoice == "official") {
        return await officialCompiler(code, input);
    } else if (OJBetter.monaco.onlineCompilerChoice == "rextester") {
        return await rextesterCompiler(code, input);
    } else if (OJBetter.monaco.onlineCompilerChoice == "wandbox") {
        return await wandboxCompiler(code, input);
    }
}

// 差异对比
function codeDiff(expectedText, actualText) {
    // 将文本按行拆分
    const expectedLines = expectedText ? expectedText.split('\n') : [];
    const actualLines = actualText ? actualText.split('\n') : [];

    const output = document.createElement('div');

    const createLineElement = (lineNo, contentElement) => {
        const lineDiv = document.createElement('div');
        lineDiv.className = 'diffLine';

        const lineNoDiv = document.createElement('div');
        lineNoDiv.className = 'lineNo';
        lineNoDiv.textContent = lineNo;

        lineDiv.appendChild(lineNoDiv);
        lineDiv.appendChild(contentElement);

        return lineDiv;
    };

    const createContentElement = (isEquals = true, expected = null, removed = null) => {
        const contentDiv = document.createElement('div');
        contentDiv.className = 'lineContent';

        if (isEquals) {
            const span = document.createElement('span');
            span.textContent = expected;
            contentDiv.appendChild(span);
        } else {
            if (removed != null) {
                const removedSpan = document.createElement('span');
                removedSpan.className = 'removed';
                removedSpan.textContent = removed;
                contentDiv.appendChild(removedSpan);
            }
            if (expected != null) {
                const addedSpan = document.createElement('span');
                addedSpan.className = 'added';
                addedSpan.textContent = expected;
                contentDiv.appendChild(addedSpan);
            }
        }

        return contentDiv;
    };

    let index = 1;

    expectedLines.forEach((expectedLine, i) => {
        const actualLine = actualLines[i];
        if (actualLine === undefined) {
            output.appendChild(createLineElement(index++, createContentElement(false, expectedLine)));
        } else if (expectedLine === actualLine) {
            output.appendChild(createLineElement(index++, createContentElement(true, expectedLine)));
        } else {
            output.appendChild(createLineElement(index++, createContentElement(false, expectedLine, actualLine)));
        }
    });

    // 处理多余的 actualLines
    for (let i = expectedLines.length; i < actualLines.length; i++) {
        output.appendChild(createLineElement(index++, createContentElement(false, null, actualLines[i])));
    }

    return output.innerHTML;
}

// 内容类型常量
const TestCaseContentType = {
    TERMINAL: 'terminal',
    DIFF: 'diff',
    NO_DIFF: 'no_diff',
    SUCCESS: 'success'
};

// 样例测试状态类
class TestCaseStatus {
    constructor(item, prefix) {
        this.testCase = OJB_safeCreateJQElement(`<div class="test-case"></div>`);
        this.item = item;
        this.prefix = prefix;
        this.titleElement = OJB_safeCreateJQElement(`<div class="test-case-title">${this.prefix} ${this.item}</div>`);
        this.statusElement = OJB_safeCreateJQElement(`<div class="test-case-status"></div>`);
        this.contentElement = OJB_safeCreateJQElement(`<div class="test-case-content"></div>`);
        this.judgeElement = OJB_safeCreateJQElement(`<div class="test-case-judge"></div>`);
        this.testCase.append(this.titleElement, this.statusElement, this.contentElement, this.judgeElement);
        $('#statePanel').append(this.testCase);
        this.setStatus('Queued', 'queued');
    }

    setTitle(title) {
        this.titleElement.text(title);
    }

    setStatus(text, status) {
        this.statusElement.text(text).removeClass('queued running success error').addClass(status);
    }

    setContent(content, type) {
        // 如果内容类型为SUCCESS,隐藏内容元素并提前返回
        if (type === TestCaseContentType.SUCCESS) {
            this.contentElement.hide();
            return;
        }

        // 根据内容类型创建内容元素
        const createContentElementByType = (content, type) => {
            let contentElement;
            switch (type) {
                case TestCaseContentType.TERMINAL:
                    // 为TERMINAL类型创建一个新的终端容器
                    contentElement = OJB_safeCreateJQElement(`<div class="terminal-container" style="overflow: auto;"></div>`);
                    break;
                case TestCaseContentType.DIFF:
                case TestCaseContentType.NO_DIFF:
                    // 为DIFF和NO_DIFF类型创建相应的内容元素,并添加差异说明
                    const className = type === TestCaseContentType.DIFF ? "output_diff" : "output_no_diff";
                    contentElement = OJB_safeCreateJQElement(`<pre class="${className}">${content}</pre>`);
                    appendDiffNote();
                    break;
                default:
                    throw new Error("Unsupported content type.");
            }
            return contentElement;
        };

        // 初始化终端
        const initializeTerminal = (content, contentElement) => {
            const term = new Terminal({ rows: 10, cols: 150 });
            term.setOption('theme', { background: '#2d2e2c' });
            term.setOption('convertEol', true); // 将换行符\n转换为\r\n
            term.write(content);
            term.open(contentElement.get(0));
        };

        // 添加差异说明
        const appendDiffNote = () => {
            const diffNote = OJB_safeCreateJQElement(`<div class="diff_note">${i18next.t('resultBlock.diffNote', { ns: 'codeEditor' })}</div>`);
            this.testCase.append(diffNote);
        };

        // 创建并追加内容元素
        const contentElement = createContentElementByType(content, type);
        this.contentElement.append(contentElement);

        // 如果内容类型为TERMINAL,初始化并打开终端
        if (type === TestCaseContentType.TERMINAL) {
            initializeTerminal(content, contentElement);
        }
    }

    setJudge(judge) {
        this.judgeElement.text(judge);
    }
}

// 样例测试函数
async function runCode(event, runButton, sourceDiv) {
    event.preventDefault();
    const statePanel = $('#statePanel').show().empty();
    const testData = getTestData();
    const customTestData = await getCustomTestData();
    const totalTests = Object.keys(customTestData).length + Object.keys(testData).length;

    let passedTests = 0;
    let failedTests = 0;
    let hasError = false;

    // 定义一个对象队列,包括创建的样例块实例和对应的样例数据
    const queue = [];

    // 先生成各个样例的块,并显示排队中,将创建的各个对象存到队列中,以便后面更新
    for (const [item, data] of Object.entries(customTestData)) {
        const testCase = new TestCaseStatus(item, i18next.t('resultBlock.title.custom', { ns: 'codeEditor' }));
        queue.push({ testCase, data });
    }

    if (!$('#onlyCustomTest').prop('checked')) {
        for (const [item, data] of Object.entries(testData)) {
            const testCase = new TestCaseStatus(item, i18next.t('resultBlock.title.sample', { ns: 'codeEditor' }));
            queue.push({ testCase, data });
        }
    }

    // 测试函数
    const runTest = async (testCase, data, index) => {
        runButton.setButtonState('running', `${index}/${totalTests}`);

        testCase.setStatus('Running', 'running');
        const result = await onlineCompilerConnect(sourceDiv.val(), data.input);

        if (result.Errors) {
            testCase.setStatus('Compilation error or Time limit', 'error');
            testCase.setContent(result.Errors, TestCaseContentType.TERMINAL);
            hasError = true;
        } else if (result.Result.trim() === data.output.trim()) {
            testCase.setStatus('Accepted', 'success');
            testCase.setContent('The output is correct.', TestCaseContentType.SUCCESS);
            passedTests++;
        } else {
            testCase.setStatus('Wrong Answer', 'error');
            const diffContent = $('#DontShowDiff').prop('checked') ? result.Result.trim() : codeDiff(data.output.trim(), result.Result.trim());
            const contentType = $('#DontShowDiff').prop('checked') ? TestCaseContentType.NO_DIFF : TestCaseContentType.DIFF;
            testCase.setContent(diffContent, contentType);
            failedTests++;
        }

        const judgeStats = `${i18next.t('resultBlock.state', { ns: 'codeEditor' })}${result.Stats}`;
        testCase.setJudge(judgeStats);

        await OJB_delay(500); // 等待500毫秒
    };

    // 对队列中的对象进行测试
    for (let i = 0; i < queue.length; i++) {
        const { testCase, data } = queue[i];
        await runTest(testCase, data, i + 1);
    }

    // 测试完成后更新按钮状态
    if (hasError) {
        runButton.setButtonState('error', i18next.t('runTestButton.error', { ns: 'codeEditor' }));
    } else if (failedTests > 0) {
        runButton.setButtonState('error', `${passedTests}/${totalTests} ` + i18next.t('runTestButton.partial', { ns: 'codeEditor' }));
    } else {
        runButton.setButtonState('success', i18next.t('runTestButton.success', { ns: 'codeEditor' }));
        if (OJBetter.monaco.setting.autoSubmitAfterPass) {
            $('#OJBetter_SubmitForm').submit(); // 自动提交
        }
    }
}

/**
 * 添加题目页代码编辑器
 * @returns
 */
async function addProblemPageCodeEditor() {
    if (typeof ace === 'undefined') {
        const loadingMessage = new LoadingMessage();
        loadingMessage.updateStatus(`${OJBetter.state.name} —— ${i18next.t('error.codeEditor.load', { ns: 'alert' })}`, 'error');
        return; // 因为Codeforces设定的是未登录时不能访问提交页,也不会加载ace库
    }

    // 获取提交页链接
    const href = window.location.href;
    let submitUrl;
    if (/\/problemset\//.test(href)) {
        // problemset
        submitUrl = OJBetter.common.hostAddress + '/problemset/submit';
    } else if (/\/gym\//.test(href)) {
        // gym 题目
        submitUrl = OJBetter.common.hostAddress + '/gym/' + ((href) => {
            const regex = /\/gym\/(?<num>[0-9a-zA-Z]*?)\/problem\//;
            const match = href.match(regex);
            return match && match.groups.num;
        })(href) + '/submit';
    } else if (OJBetter.typeOfPage.is_acmsguru) {
        // acmsguru 题目
        submitUrl = href.replace(/\/problemsets[A-Za-z0-9\/#]*/, "/problemsets/acmsguru/submit");
    } else {
        submitUrl = href.replace(/\/problem[A-Za-z0-9\/#]*/, "/submit");
    }

    // 获取提交页HTML
    let cloneHTML = await getSubmitHTML(submitUrl);

    // 创建
    let form = await createCodeEditorForm(submitUrl, cloneHTML);
    let selectLang = form.selectLang;
    let submitButton = form.submitButton;
    let runButton = form.runButton;

    // 初始化
    CustomTestInit(); // 自定义测试数据面板
    selectLang.val(OJBetter.monaco.compilerSelection);

    // 设置语言选择change事件监听器
    selectLang.on('change', () => {
        changeMonacoLanguage(form); // 编辑器语言切换监听
    });
    changeMonacoLanguage(form);

    // 样例测试
    runButton.on('click', (event) => runCode(event, runButton, form.sourceDiv, form.submitDiv))
        .setHoverRedo();

    // 提交
    submitButton.on('click', async function (event) {
        event.preventDefault();
        if (OJBetter.monaco.setting.isCodeSubmitDoubleConfirm) {
            // 获取题目名
            const questionTitle = (() => {
                if (OJBetter.typeOfPage.is_acmsguru) {
                    return $('h4').eq(0).text();
                } else {
                    return $('.header .title').eq(0).text();
                }
            })();
            const submit = await OJB_createDialog(
                i18next.t('submitCode.title', { ns: 'dialog' }),
                i18next.t('submitCode.content', { ns: 'dialog', questionTitle: questionTitle }),
                [
                    i18next.t('submitCode.buttons.0', { ns: 'dialog' }),
                    i18next.t('submitCode.buttons.1', { ns: 'dialog' })
                ],
                true
            ); //提交确认
            if (submit) {
                submitButton.after(`<img class="OJBetter_loding" src="//codeforces.org/s/84141/images/ajax-loading-24x24.gif">`);
                $('#OJBetter_SubmitForm').submit();
            } else {
                submitButton.addClass('disabled');
                setTimeout(function () {
                    submitButton.removeClass('disabled');
                }, 300);
            }
        } else {
            $('#OJBetter_SubmitForm').submit();
        }
    });
}

/**
 * 获取翻译服务目标语言的对应代码
 * @param {string} serverName 服务名称
 * @returns {string} 目标语言,如果没有对应代码则返回中文
 */
function getTargetLanguage(serverName) {
    let targetLanguage = OJBetter.supportList.translationSupport[serverName][OJBetter.translation.targetLang];
    if (targetLanguage) return targetLanguage;
    else return OJBetter.supportList.translationSupport[serverName]['zh'];
}

/**
 * 将文本中Markdown格式的加粗**转换成HTML格式。
 * @param {string} text 文本
 * @returns {string} 替换后的字符串
 */
function convertBoldMarkdownToHTML(text) {
    return text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
}

/**
* 将文本中Markdown格式的链接文本转换成HTML格式。
* @param {string} text 文本
* @returns {string} 替换后的字符串
*/
function convertLinksMarkdownToHTML(text) {
    return text.replace(/(?<!!)\[(.*?)\]\(([^"]*?)("(.*?)")*\)/g, '<a href="$2" title="$4">$1</a>');
}

/**
 * 将HTML格式的加粗文本转换回Markdown格式。
 * @param {string} text 文本
 * @returns {string} 替换后的字符串
 */
function convertBoldHTMLToMarkdown(text) {
    return text.replace(/<strong>(.*?)<\/strong>/g, '**$1**');
}

/**
 * 将HTML格式的链接文本转换回Markdown格式。
 * @param {string} html - 包含HTML链接标签<a>的字符串。
 * @returns {string} 转换后的字符串,其中HTML链接标签被替换为Markdown的链接语法。
 */
function convertLinksHTMLToMarkdown(html) {
    return html.replace(/<a href="([^"]*)"( title="([^"]*)")*>([^<]+)<\/a>/g, '[$4]($1 "$3")');
}

/**
 * DeepL翻译
 * @param {string} raw 原文
 * @returns {Promise<TransRawData>} 翻译结果对象
 */
async function translate_deepl(raw) {
    const id = (Math.floor(Math.random() * 99999) + 100000) * 1000;
    const data = {
        jsonrpc: '2.0',
        method: 'LMT_handle_texts',
        id,
        params: {
            splitting: 'newlines',
            lang: {
                source_lang_user_selected: 'auto',
                target_lang: getTargetLanguage('deepl'),
            },
            texts: [{
                text: raw,
                requestAlternatives: 3
            }],
            timestamp: getTimeStamp(raw.split('i').length - 1)
        }
    }
    let postData = JSON.stringify(data);
    if ((id + 5) % 29 === 0 || (id + 3) % 13 === 0) {
        postData = postData.replace('"method":"', '"method" : "');
    } else {
        postData = postData.replace('"method":"', '"method": "');
    }
    const options = {
        method: 'POST',
        url: 'https://www2.deepl.com/jsonrpc',
        data: postData,
        headers: {
            'Content-Type': 'application/json',
            'Host': 'www2.deepl.com',
            'Origin': 'https://www.deepl.com',
            'Referer': 'https://www.deepl.com/',
        },
        anonymous: true,
        nocache: true,
    }

    return await BaseTranslate(options, res => JSON.parse(res)?.result?.texts?.[0]?.text || res, res => {
        const resObj = {
            status: true,
            message: 'ok'
        };
        if (res.includes('"message":"Too many requests"')) {
            resObj.status = false;
            resObj.message = i18next.t('error.deepl429', { ns: 'translator' }); // Too many requests 提示
            return resObj;
        };
        return resObj;
    });
}

/**
 * 使用 DeepL Free API 进行翻译
 * @param {string} raw 原文
 * @returns {Promise<TransRawData>} 翻译结果对象
 */
async function translate_deepl_api_free(raw) {
    const data = JSON.stringify({
        text: [raw],
        target_lang: getTargetLanguage('deepl'),
        split_sentences: '1',
        ...(OJBetter.deepl.enableEmphasisProtection || OJBetter.deepl.enableLinkProtection ? { tag_handling: 'html' } : {}),
        ...Object.assign({}, ...OJBetter.deepl.config.data)
    });

    const options = {
        method: "POST",
        url: OJBetter.deepl.config.proxy || "https://api-free.deepl.com/v2/translate",
        headers: {
            "Authorization": `DeepL-Auth-Key ${OJBetter.deepl.config.key}`,
            "Content-Type": "application/json",
            ...Object.assign({}, ...OJBetter.deepl.config.header)
        },
        data: data,
        onload: response => response.responseText,
        onerror: error => console.error(error)
    };

    return await BaseTranslate(options, res => JSON.parse(res).translations[0].text);
}

/**
 * 使用 DeepL Pro API 进行翻译
 * @param {string} raw 原文
 * @returns {Promise<TransRawData>} 翻译结果对象
 */
async function translate_deepl_api_pro(raw) {
    const data = JSON.stringify({
        text: [raw],
        target_lang: getTargetLanguage('deepl'),
        split_sentences: '1',
        ...(OJBetter.deepl.enableEmphasisProtection || OJBetter.deepl.enableLinkProtection ? { tag_handling: 'html' } : {}),
        ...Object.assign({}, ...OJBetter.deepl.config.data)
    });

    const options = {
        method: "POST",
        url: OJBetter.deepl.config.proxy || "https://api.deepl.com/v2/translate",
        headers: {
            "Authorization": `DeepL-Auth-Key ${OJBetter.deepl.config.key}`,
            "Content-Type": "application/json",
            ...Object.assign({}, ...OJBetter.deepl.config.header)
        },
        data: data,
        onload: response => response.responseText,
        onerror: error => console.error(error)
    };

    return await BaseTranslate(options, res => JSON.parse(res).translations[0].text);
}

/**
 * 使用 DeepLX 进行翻译
 * @param {String} text 原文
 * @returns {Promise<TransRawData>} 翻译结果对象
 */
async function translate_deeplx(text) {
    const options = {
        method: "POST",
        url: OJBetter.deepl.config.proxy || 'https://api.deeplx.org/translate',
        data: JSON.stringify({
            "text": text,
            "source_lang": "EN",
            "target_lang": getTargetLanguage('deepl'),
        }),
        headers: {
            'Content-Type': 'application/json',
            ...(OJBetter.deepl.config.key ? { Authorization: `Bearer ${OJBetter.deepl.config.key}` } : {})
        },
        responseType: "json",
    };

    return await BaseTranslate(options, res => {
        const parsedResponse = JSON.parse(res);
        if (parsedResponse.code === 200 && parsedResponse.data) {
            return parsedResponse.data;
        } else {
            throw new Error('Translation failed or invalid response format.');
        }
    });
}

function getTimeStamp(iCount) {
    const ts = Date.now();
    if (iCount !== 0) {
        iCount = iCount + 1;
        return ts - (ts % iCount) + iCount;
    } else {
        return ts;
    }
}

/**
 * 讯飞听见翻译
 * @param {String} text 要翻译的文本
 * @returns {Promise<TransRawData>} 翻译结果对象
 */
async function translate_iflyrec(text) {
    const options = {
        method: "POST",
        url: 'https://www.iflyrec.com/TranslationService/v1/textTranslation',
        data: JSON.stringify({
            "from": "2",
            "to": getTargetLanguage('iflyrec'),
            "contents": [{
                "text": text,
                "frontBlankLine": 0
            }]
        }),
        anonymous: true,
        headers: {
            'Content-Type': 'application/json',
            'Origin': 'https://www.iflyrec.com',
        },
        responseType: "json",
    };
    return await BaseTranslate(options, res => JSON.parse(res).biz[0].translateResult.replace(/\\n/g, "\n\n"));
}

/**
 * 有道翻译
 * @param {string} raw 原文
 * @returns {Promise<TransRawData>} 翻译结果对象
 */
async function translate_youdao_mobile(raw) {
    /**
     * 生成cookie
     */
    const getcookie = (() => {
        // 生成IP地址
        const generateIP = () => {
            return `${OJB_getRandomNumberInRange(1, 255)}.${OJB_getRandomNumberInRange(1, 255)}.${OJB_getRandomNumberInRange(1, 255)}.${OJB_getRandomNumberInRange(1, 255)}`;
        }
        // 生成OUTFOX_SEARCH_USER_ID_NCOO的值
        const OUTFOX_SEARCH_USER_ID_NCOO = `${OJB_getRandomNumberInRange(100000000, 999999999)}.${OJB_getRandomNumberInRange(100000000, 999999999)}`;
        // 生成OUTFOX_SEARCH_USER_ID的值
        const OUTFOX_SEARCH_USER_ID = `${OJB_getRandomNumberInRange(100000000, 999999999)}@${generateIP()}`;
        return `OUTFOX_SEARCH_USER_ID_NCOO=${OUTFOX_SEARCH_USER_ID_NCOO}; OUTFOX_SEARCH_USER_ID=${OUTFOX_SEARCH_USER_ID}`;
    })();

    /**
     * 生成随机时间戳
     */
    const gettime = (new Date()).getTime();

    /**
     * 生成sign
     */
    const getsign = (() => {
        const d = "fanyideskweb";
        const u = "webfanyi";
        const t = "fsdsogkndfokasodnaso";
        function A(e) {
            return CryptoJS.MD5(e.toString()).toString(CryptoJS.enc.Hex);
        }
        function w(e) {
            return A(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`);
        }
        return w(gettime);
    })();

    /**
     * 解码方法
     * @param {string} src 待解码的字符串
     * @returns {Object} 解码后的数据
     */
    const decode = function (src) {
        // 解码URL安全的Base64
        const decodeUrlSafeBase64 = (str) => {
            let base64 = str.replace(/-/g, '+').replace(/_/g, '/');
            return base64;
        }

        // 使用MD5生成key和iv,取前16字节
        const key = "ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl";
        const iv = "ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4";
        const keyHash = CryptoJS.MD5(key).toString();
        const ivHash = CryptoJS.MD5(iv).toString();

        // 使用AES-128-CBC模式进行解密
        const keyForAES = CryptoJS.enc.Hex.parse(keyHash).toString().substring(0, 32);
        const ivForAES = CryptoJS.enc.Hex.parse(ivHash).toString().substring(0, 32);

        // 解码URL安全的Base64
        const decodedBase64 = decodeUrlSafeBase64(src);

        // 解密
        const decrypted = CryptoJS.AES.decrypt({
            ciphertext: CryptoJS.enc.Base64.parse(decodedBase64)
        }, CryptoJS.enc.Hex.parse(keyForAES), {
            iv: CryptoJS.enc.Hex.parse(ivForAES),
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });

        // 将解密结果转换为Utf8字符串
        const decryptedStr = decrypted.toString(CryptoJS.enc.Utf8);

        // 处理JSON字符串并解析
        const jsonStr = decryptedStr.substring(0, decryptedStr.lastIndexOf("}") + 1);
        return JSON.parse(jsonStr);
    }
    // 整理数据
    const organizeTranslation = (data) => {
        // 提取translateResult数组
        const { translateResult } = data;

        // 整理tgt字段
        return translateResult
            .flat()
            .map(item => item.tgt)
            .join('');
    };
    // 表单数据
    const data = {
        "i": raw,
        "from": "auto",
        "to": getTargetLanguage('youdao'),
        "dictResult": "true",
        "keyid": "webfanyi",
        "sign": getsign,
        "client": "fanyideskweb",
        "product": "webfanyi",
        "appVersion": "1.0.0",
        "vendor": "web",
        "pointParam": "client,mysticTime,product",
        "mysticTime": gettime,
        "keyfrom": "fanyi.web",
        "mid": "1",
        "screen": "1",
        "model": "1",
        "network": "wifi",
        "abtest": "0",
        "yduuid": "abcdefg"
    };
    const options = {
        method: "POST",
        url: 'https://dict.youdao.com/webtranslate',
        data: new URLSearchParams(data),
        anonymous: true,
        cookie: getcookie,
        headers: {
            "Content-Type": "application/x-www-form-urlencoded",
            'Referer': 'https://fanyi.youdao.com/',
        }
    }
    return await BaseTranslate(options,
        res => {
            const decodeData = decode(res)
            const result = organizeTranslation(decodeData);
            return result;
        }
    );
}

/**
 * google翻译
 * @param {string} raw 原文
 * @returns {Promise<TransRawData>} 翻译结果对象
 */
async function translate_gg(raw) {
    const params = `tl=${getTargetLanguage('google')}&q=${encodeURIComponent(raw)}`;
    const options = {
        method: "GET",
        url: `https://translate.google.com/m?${params}`,
    }
    return await BaseTranslate(options,
        res => $(res).filter('.result-container').text() || $(res).find('.result-container').text());
}

/**
 * 彩云翻译
 * @param {string} raw 原文
 * @returns {Promise<TransRawData>} 翻译结果对象
 */
async function translate_caiyun(raw) {
    const source = "NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm";
    const dic = [..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"].reduce((dic, current, index) => { dic[current] = source[index]; return dic }, {});
    const browser_id = CryptoJS.MD5(Math.random().toString()).toString();
    const caiyun_jwt = await (async () => {
        const options = {
            method: "POST",
            url: 'https://api.interpreter.caiyunai.com/v1/user/jwt/generate',
            headers: {
                "content-type": "application/json",
                "x-authorization": "token:qgemv4jr1y38jyq6vhvi",
                "origin": "https://fanyi.caiyunapp.com",
            },
            data: JSON.stringify({ browser_id }),
        }
        const res = await OJB_GMRequest(options);
        return JSON.parse(res.responseText).jwt;
    })();

    // 解码
    const decodeUnicode = str => {
        const decoder = new TextDecoder();
        const data = Uint8Array.from(atob(str), c => c.charCodeAt(0));
        return decoder.decode(data);
    };
    const decoder = line => decodeUnicode([...line].map(i => dic[i] || i).join(""));

    const options = {
        method: "POST",
        url: 'https://api.interpreter.caiyunai.com/v1/translator',
        data: JSON.stringify({
            "source": raw.split('\n'),
            "browser_id": browser_id,
            "trans_type": getTargetLanguage('caiyun'),
            "request_id": "web_fanyi",
            "media": "text",
            "os_type": "web",
            "dict": true,
            "cached": true,
            "replaced": true,
            "style": "formal",
            "model": "",
            "detect": true,
        }),
        headers: {
            "content-type": "application/json;charset=UTF-8",
            "x-authorization": "token:qgemv4jr1y38jyq6vhvi",
            "t-authorization": caiyun_jwt
        }
    }
    return await BaseTranslate(options, res => JSON.parse(res).target.map(decoder).join('\n'))
}

/**
 * ChatGPT
 * @param {string} raw 原文
 * @returns {Promise<TransRawData>} 翻译结果对象
 */
async function translate_openai(raw) {
    const modelDefault = 'gpt-3.5-turbo';
    const lang = getTargetLanguage('openai');
    let prompt = "";
    if (OJBetter.chatgpt.customPrompt) {
        prompt = `\n${OJBetter.chatgpt.customPrompt}`;
        if (!OJBetter.chatgpt.asSystemPrompt) {
            prompt += `\n${raw}`;
        };
    } else {
        prompt = `
As a professional English translator, your task is to accurately translate a segment of an algorithm programming competition question into ${lang}.
The translation should use professional terms and maintain the text format, including ${OJBetter.typeOfPage.is_oldLatex || OJBetter.typeOfPage.is_acmsguru
                ? "keeping the LaTeX equations unchanged."
                : "keeping the brackets【】, HTML tags, and their content unchanged."
            }
After translation, please ensure that the ${lang} version conforms to normal expression habits.
What I need is a carefully polished ${lang} translation of my question segment. ${OJBetter.chatgpt.asSystemPrompt ? '' :
                `The segment to be translated is as follows: "
${raw}
"`}`;
    };
    const data = {
        model: OJBetter.chatgpt.config.model || modelDefault,
        messages: OJBetter.chatgpt.asSystemPrompt ?
            [
                {
                    role: "system",
                    content: prompt
                },
                {
                    role: "user",
                    content: raw
                }
            ] :
            [
                {
                    role: "user",
                    content: prompt
                }
            ],
        temperature: 0.7,
        ...Object.assign({}, ...OJBetter.chatgpt.config.data)
    };
    const options = {
        method: "POST",
        url: OJBetter.chatgpt.config.proxy || 'https://api.openai.com/v1/chat/completions',
        data: JSON.stringify(data),
        responseType: 'json',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer ' + OJBetter.chatgpt.config.key,
            ...Object.assign({}, ...OJBetter.chatgpt.config.header)
        }
    }
    return await BaseTranslate(options,
        res => res,
        undefined,
        response => response.response.choices[0].message.content);
}

/**
 * ChatGPT 流式传输
 * @param {string} raw 原文
 * @param {TranslateDiv} translateDiv 翻译结果面板
 * @returns {Promise<TransRawData>} 翻译结果对象
 */
async function translate_openai_stream(raw, translateDiv) {
    const result = {
        done: true,
        checkPassed: null,
        response: null,
        responseText: null,
        text: "",
        error: null,
        message: null
    };
    const helpText = i18next.t('error.basic', { ns: 'translator' }); // 基本帮助提示信息
    try {
        for await (const delta of openai_stream(raw)) {
            result.text += delta;
            // 翻译结果面板更新
            translateDiv.updateTranslateDiv(result.text, !((OJBetter.typeOfPage.is_oldLatex || OJBetter.typeOfPage.is_acmsguru) && (!OJBetter.translation.forceTurndownConversion)), false);
        }
        return result;
    } catch (err) {
        console.warn(err);
        result.error = {
            message: err.message || null,
            stack: err.stack ? err.stack.replace(/\n/g, '<br>').replace(/\s/g, '&nbsp;') : null,
            enumerable: err,
            source: 'openai_stream'
        };
        result.message = `${i18next.t('error.GMRequest', { ns: 'translator' })}${helpText}`;
    }

    return result;
}

/**
 * 流式传输
 * @param {string} raw 原文
 * @returns {AsyncGenerator<string>} 返回 AsyncGenerator
 */
async function* openai_stream(raw) {
    const modelDefault = 'gpt-3.5-turbo';
    const lang = getTargetLanguage('openai');
    let prompt = "";
    if (OJBetter.chatgpt.customPrompt) {
        prompt = `\n${OJBetter.chatgpt.customPrompt}`;
        if (!OJBetter.chatgpt.asSystemPrompt) {
            prompt += `\n${raw}`;
        };
    } else {
        prompt = `
As a professional English translator, your task is to accurately translate a segment of an algorithm programming competition question into ${lang}.
The translation should use professional terms and maintain the text format, including ${OJBetter.typeOfPage.is_oldLatex || OJBetter.typeOfPage.is_acmsguru
                ? "keeping the LaTeX equations unchanged."
                : "keeping the brackets【】, HTML tags, and their content unchanged."
            }
After translation, please ensure that the ${lang} version conforms to normal expression habits.
What I need is a carefully polished ${lang} translation of my question segment. ${OJBetter.chatgpt.asSystemPrompt ? '' :
                `The segment to be translated is as follows: "
${raw}
"`}`;
    };
    const data = {
        model: OJBetter.chatgpt.config.model || modelDefault,
        messages: OJBetter.chatgpt.asSystemPrompt ?
            [
                {
                    role: "system",
                    content: prompt
                },
                {
                    role: "user",
                    content: raw
                }
            ] :
            [
                {
                    role: "user",
                    content: prompt
                }
            ],
        temperature: 0.7,
        stream: true,
        ...Object.assign({}, ...OJBetter.chatgpt.config.data)
    };
    const options = {
        method: "POST",
        url: OJBetter.chatgpt.config.proxy || 'https://api.openai.com/v1/chat/completions',
        data: JSON.stringify(data),
        responseType: 'stream',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer ' + OJBetter.chatgpt.config.key,
            ...Object.assign({}, ...OJBetter.chatgpt.config.header)
        }
    }
    const response = await OJB_GMRequest(options, true);
    const reader = response.response.getReader();
    const decoder = new TextDecoder();
    let buffer = ''; // 用于累积数据片段的缓冲区

    while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        buffer += decoder.decode(value, { stream: true }); // 将新的数据片段追加到缓冲区
        let lines = buffer.split("\n\n"); // 处理累积的数据

        // 缓冲区的最后一行可能还未完整接收,保留在缓冲区中,-1
        for (let i = 0; i < lines.length - 1; i++) {
            let line = lines[i];
            line = line.substring(5); // 移除 'data:' 前缀
            if (line.includes('[DONE]')) {
                return; // End
            }
            try {
                let data = JSON.parse(line);
                let delta = data['choices'][0]['delta'];
                let content = delta['content'] ? delta['content'] : "";
                yield content; // 传递数据给调用者
            } catch (error) {
                console.warn(`Error parsing JSON: ${error}\n\nError data: ${line}`);
            }
        }

        // 保留最后一行在缓冲区中
        buffer = lines.slice(-1);
    }

    return buffer;
}

/**
 * @typedef {Object} CheckResponseResult
 * @property {boolean} status 检查是否通过
 * @property {string} message 检查失败时的消息
 */

/**
 * @typedef {Object} ErrorResponse
 * @property {Object} message 错误消息
 * @property {Object} stack 错误堆栈
 * @property {Object} enumerable 可枚举的错误属性
 * @property {string} source 错误来源
 */

/**
 * @typedef {Object} TransRawData
 * @property {boolean} done 操作是否完成
 * @property {CheckResponseResult|null} checkPassed 检查是否通过的结果
 * @property {Object|null} response 响应对象
 * @property {string|null} text 处理后的文本
 * @property {ErrorResponse} error 错误列表
 * @property {string|null} message 可能的消息
 */

/**
 * 通用翻译函数
 * @param {Object} options GM_xmlhttpRequest 的参数
 * @param {Function} processer 响应再处理函数,它接收响应文本,并应返回处理后的文本。
 * @param {Function} checkResponse 检查文本是否符合预期的函数,它接收文本,并返回一个Object,包含状态和信息。默认为返回 { status: true, message: 'ok' }
 * @param {Function} getResponseText 重写响应文本获取函数,它接收response,并返回响应文本。 默认为 response.responseText
 * @returns {Promise<TransRawData>} 返回 Promise,其解析值为翻译结果对象
 */
async function BaseTranslate(options, processer, checkResponse = () => { return { status: true, message: 'ok' } }, getResponseText = (response) => response.responseText) {
    const result = {
        done: false,
        checkPassed: null,
        response: null,
        responseText: null,
        text: "",
        error: null,
        message: null
    };
    const helpText = i18next.t('error.basic', { ns: 'translator' }); // 基本帮助提示信息
    const toDo = async () => {
        try {
            result.response = await OJB_GMRequest(options);
            result.responseText = result.response.responseText;
            result.text = getResponseText(result.response);
        } catch (err) {
            console.warn(err);
            result.error = {
                message: err.message || null,
                stack: err.stack ? err.stack.replace(/\n/g, '<br>').replace(/\s/g, '&nbsp;') : null,
                enumerable: err,
                source: 'GMRequest'
            };
            result.message = `${i18next.t('error.GMRequest', { ns: 'translator' })}${helpText}`;
            throw result;
        }
        try {
            result.text = processer(result.text);
        } catch (err) {
            console.warn(err);
            result.error = {
                message: err.message || null,
                stack: err.stack ? err.stack.replace(/\n/g, '<br>').replace(/\s/g, '&nbsp;') : null,
                enumerable: err,
                source: 'processer'
            };
            result.message = `${i18next.t('error.processer', { ns: 'translator' })}${helpText}`;
            throw result;
        }
        try {
            result.checkPassed = checkResponse(result.text);
            if (result.checkPassed.status) result.done = true;
            else result.message = result.checkPassed.message;
            return result;
        } catch (err) {
            console.warn(err);
            result.error = {
                message: err.message || null,
                stack: err.stack ? err.stack.replace(/\n/g, '<br>').replace(/\s/g, '&nbsp;') : null,
                enumerable: err,
                source: 'checkResponse'
            };
            result.message = `${i18next.t('error.checkResponse', { ns: 'translator' })}${helpText}`;
            throw result;
        }
    };

    return await OJB_promiseRetryWrapper(toDo, {
        maxRetries: 3,
        errorHandler: (err, maxRetries, attemptsLeft) => {
            const detailedError = {
                maxRetries: maxRetries,
                attemptsLeft: attemptsLeft,
                ...err
            };
            return detailedError;
        }
    });
}

/**
 * 查询服务余额
 * @param {Object} quotaConfig - 配额配置对象
 * @returns {Promise} 返回包含余额信息的 Promise
 */
async function queryServerBalance(quotaConfig) {
    // 确保传入了有效的配置对象
    if (!quotaConfig || !quotaConfig.url) {
        return Promise.reject(new Error('Quota configuration is missing.'));
    }

    // 准备请求选项
    const requestOptions = {
        method: quotaConfig.method || 'GET',
        url: quotaConfig.url,
        headers: {
            ...Object.assign({}, ...quotaConfig.header)
        },
        data: JSON.stringify({ ...Object.assign({}, ...quotaConfig.data) })
    };

    // 发送请求并返回 Promise
    return OJB_GMRequest(requestOptions).then(response => {
        try {
            const responseData = JSON.parse(response.responseText);
            // 从响应数据中提取余额
            const surplusPath = quotaConfig.surplus;
            const surplusValue = OJB_evaluatePathOrExpression(responseData, surplusPath);
            return surplusValue;
        } catch (error) {
            return Promise.reject(new Error('Failed to parse balance response.'));
        }
    }).catch(error => {
        console.warn('Error querying balance:', error);
        return Promise.reject(error);
    });
}

/**
 * 确认 jQuery 已加载
 * @param {number} retryDelay 重试延迟(毫秒)
 * @returns {Promise<void>}
 */
async function ensureJQueryIsLoaded(retryDelay = 50) {
    while (typeof jQuery === 'undefined') {
        console.warn(`JQuery is not loaded. Retry after ${retryDelay} ms.`);
        await OJB_delay(retryDelay);
        retryDelay = Math.min(retryDelay * 2, 2000);
    }
}

/**
 * 加载必须的函数
 * @returns {Promise} 加载提示信息
 */
async function loadRequiredFunctions() {
    await initVar();// 初始化全局变量
    return Promise.allSettled([
        initDB(), // 连接数据库
        initI18next(), // i18next初始化
        initButtonFunc(), // 加载按钮相关函数
        initHTML2MarkDown(), // 初始化html2markdown转换器
        checkScriptVersion(), // 更新检查
        ...(OJBetter.typeOfPage.is_acmsguru ? [acmsguruReblock()] : []) // 为acmsguru题面重新划分div
    ]);
}

/**
 * DOM加载后即可执行
 */
function initOnDOMReady() {
    showAnnounce(); // 显示公告
    showWarnMessage(); // 显示警告消息
    initSettingsPanel(); // 加载设置按钮面板
    initMonacoEditor(); // 初始化monaco编辑器资源
    localizeWebsite(); // 网站本地化替换
    addDependencyStyles(); // 添加一些依赖库的样式
    addI18nStyles(); // 添加包含i18n内容的样式
    if (OJBetter.basic.expandFoldingblocks) ExpandFoldingblocks(); // 折叠块展开
    if (OJBetter.basic.renderPerfOpt) RenderPerfOpt(); // 折叠块渲染优化
    if (OJBetter.basic.selectElementPerfOpt) SelectElementPerfOpt(); // 下拉选择框性能优化
    if (OJBetter.typeOfPage.is_problem) {
        const problemPageLinkbar = new ProblemPageLinkbar(); // 创建题目页相关链接栏
        if (OJBetter.basic.showCF2vjudge) CF2vjudge(problemPageLinkbar); // 跳转到Vjudge按钮
        if (OJBetter.basic.showJumpToLuogu) CF2luogu(problemPageLinkbar); // 跳转到洛谷按钮
        if (OJBetter.clist.enabled.problem) showRatingByClist_problem(problemPageLinkbar); // problem页显示Rating
    }
    if (OJBetter.typeOfPage.is_contest) {
        if (OJBetter.clist.enabled.contest) showRatingByClist_contest(); // contest页显示Rating
    }
    if (OJBetter.typeOfPage.is_problemset) {
        if (OJBetter.clist.enabled.problemset) showRatingByClist_problemset(); // problemset页显示Rating
    }
    if (OJBetter.typeOfPage.is_problem && OJBetter.monaco.enableOnProblemPage) {
        addProblemPageCodeEditor(); // 添加题目页代码编辑器
    }
    if (OJBetter.preference.judgeStatusReplaceText && (OJBetter.typeOfPage.is_submissions || OJBetter.typeOfPage.is_statePage)) {
        judgeStatusReplace(); // 评测结果替换
    }
}

/**
 * 需要在页面资源完全加载后执行的函数
 */
function onResourcesReady(loadingMessage) {
    if (OJBetter.preference.showLoading) loadingMessage.updateStatus(`${OJBetter.state.name} —— ${i18next.t('loadFunc', { ns: 'alert' })}`);
    initializeInParallel(loadingMessage);
    initializeSequentially(loadingMessage);
}

/**
 * 可以异步并行的函数
 */
function initializeInParallel(loadingMessage) {
    if (OJBetter.basic.darkMode == "dark") darkModeStyleAdjustment(); // 黑暗模式额外的处理事件
    if (OJBetter.basic.commentPaging) CommentPagination(); // 评论区分页
    if (OJBetter.translation.comment.transMode == "2") multiChoiceTranslation(); // 选段翻译支持
    if (OJBetter.monaco.beautifyPreBlocks) beautifyPreBlocksWithMonaco(); // 美化Pre代码块
    if (OJBetter.basic.hiddenProblemTag) hiddenProblemTag()// 隐藏题目问题标签
}

/**
 * 必须按序执行的函数
 */
async function initializeSequentially(loadingMessage) {
    await addConversionButton(); // 添加MD/复制/翻译按钮
    if ((OJBetter.typeOfPage.is_problem || OJBetter.typeOfPage.is_completeProblemset) && OJBetter.translation.memory.enabled) {
        await initTransResultsRecover(); // 翻译结果恢复功能初始化
    }
    if (OJBetter.translation.auto.enabled) {
        await initTransWhenViewable(); // 自动翻译
    }
    if (OJBetter.basic.standingsRecolor && OJBetter.typeOfPage.is_cfStandings) {
        await recolorStandings(); // cf赛制榜单重新着色
    }
    if (OJBetter.preference.showLoading) {
        loadingMessage.updateStatus(`${OJBetter.state.name} —— ${i18next.t('loadSuccess', { ns: 'alert' })}`, 'success', 3000);
    }
}

/**
 * 主方法
 */
async function main() {
    await ensureJQueryIsLoaded(); // 等待jQuery加载
    const loadingMessage = new LoadingMessage();
    await loadRequiredFunctions(); // 加载必须的函数
    initOnDOMReady(); // DOM加载后即可执行的函数
    if (OJBetter.preference.showLoading) loadingMessage.updateStatus(`${OJBetter.state.name} —— ${i18next.t('onload', { ns: 'alert' })}`);

    // 检查页面资源是否已经完全加载
    if (OJBetter.state.notWaiteLoaded) {
        onResourcesReady(loadingMessage);
    } else {
        if (document.readyState === 'complete') {
            onResourcesReady(loadingMessage);
        } else {
            window.addEventListener('load', () => onResourcesReady(loadingMessage));
        }
    }
};

// ------------------------------
// 脚本加载入口
if (document.readyState === 'loading') {
    document.addEventListener("DOMContentLoaded", main);
} else {
    main(); // 如果DOMContentLoaded已经触发,立即执行
}
// ------------------------------

// ------------------------------
// 配置自动迁移代码(将在10个小版本后移除-1.83)
// ------------------------------

{
    let bottomZh_CN = GM_getValue("bottomZh_CN");
    if (bottomZh_CN !== undefined) {
        if (bottomZh_CN == true) {
            GM_setValue("localizationLanguage", "zh");
        } else {
            GM_setValue("localizationLanguage", "initial");
        }
        GM_deleteValue("bottomZh_CN");
        location.reload();
    }
}
{
    let config = GM_getValue("chatgpt-config");
    if (config && config !== undefined) {
        let index = parseInt(config.choice, 10);
        if (index == -1) config.choice = "";
        else config.choice = config.configurations[index].note;
        config.configurations.forEach(function (item) {
            item.name = item.note;
            delete item.note;
        });
        GM_deleteValue("chatgpt-config");
        GM_setValue("chatgpt_config", config);
        location.reload();
    }
}
{
    let config = GM_getValue("Complet_config");
    if (config && config.changed === undefined) {
        config.changed = true; // 设置一个迁移标志
        config.configurations.forEach(function (item) {
            if (item.note !== undefined) {
                item.name = item.note;
                delete item.note;
            }
        });
        GM_setValue("Complet_config", config);
        location.reload();
    }
}