// ==UserScript== // @name DDDD OCR WEB - 验证码自动识别 // @namespace https://github.com/MakotoArai-CN/ddddocr-webjs // @version 1.1.4-2026/3/15 21:11:58 // @author MakotoArai-CN // @description 自动检测并识别页面验证码,自动填充到输入框。首次使用需设置白名单,会自动下载约50MB模型文件以及20MB左右的ONNX推理运行时文件。 // @license MIT // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAADsOAAA7DgBcSvKOAAAC9BJREFUaN7tmVuMVdd5gL912Xufy5zLMMNgYMYQAyFAiUceHNtNgxTTVJYfUtNUeemF8VtvlhXJUvPQRn2oVKV5qCo/uKkqxaiWqrZq7RIlkl2rkWIpRBgBBlHIYOxyGRgGmJkz57r3Xpc+nDPMnDlzAZPGrdRfWtLRWWuvtb7/tv+1Nvy/fLoiHnaC8fFxAAX0AcUlc3rvfUUIUfPeu6NHj/7vAThy5AhCiCywDzgIPAk8BgwBsjPMAbeAS8AJ4MfARSB+/fXXf/EA4+PjCCHw3g8Ch4EXgKedc/3OOeGcY6EJIRBCIKVESolSygsh7gA/Af4V+L6UcjZJEt54443/eYCOm2SAQ8A3vfdPG2N0mqYYY3DO4b1feYEOiNaaMAxRSiVCiPeAv+hYJX0Yi6wL0Nn8EPCn3vvxNE374jjGWrvqplddTAiUUkRRRBAEc0KIvwG+Dcx9UohVATobB9gN/LUx5tdarZZI03T12ZYDiZWnF0IQBAGZTMYqpd4CvgFc+yQQarWO0dHRhc3/bZIkhxqNhrDWrrBpB3iEDlHZEipTQEZ5RBCBs+AM4HtgrLUYY6SUcq9Saifw49HR0eqZM2ce3gJL3OZoHMfPNZvNXnfxDqQmGnyM3KNjZDZ9Dl3chFBBu9smmOptWlMXaFw5SXz34zaMkN0bEIJcLkcYhv8ghPh9733lQSzRA9DJNpH3/jtxHP9Rs9kUvZv3RJs+S2nf82QfHUNmilgPzoHrDJUCpAQlwDUrNK+dpnL+B8TTEz3LdiBsGIZ/CXwLMPcL0eVCL7744sKEz6Vp+ufNZjPq3rxHSE1xz1cY/NLvEWzeS0JEK4XEgHFgO804SG27eZ0hs3E7+UfHcEmDZOZKx/UWQay1Umu9Ryl1HLgyMjLCxMTEgwGMjo4ipRy01r7aaDR2Oee6NSU15dHfoPzkb2F0kWbS2eAaycj7RRgZ5ugb+TwSiG/9rOtB7z3OuZzWekgp9f1sNpvcTzzo7sU8zrnDcRz/ck/Aek9h9yEKo1+jRYY0hQ05eGYEBnKwGoMA7jTgp9dgpgFWZyiMfg3brDB/4e0uKxhjSJLkkJTyV4E378eF7gGMj48ThmGmXq9/NUmS7uzkHdGm3RSf+DotMhgLoYLf3AdPj9zPMjBchNdPQWIBlaH4xNeJ71wmnr7UFdhxHGe01r9eKpWOjY+P2/VioSslJEmyL03Tp1dyneK+57GZQawFPBQi2Dlwf5uH9thC1H7WWLCZQQp7nwPZ5QQ450jT9Nlms7njgSwQxzFKqYPGmMHl2g837kRveaKtvQUosWj8Wsvw1slbVJsG0fnX4ylkNS8c2ERfRiPofhUkFqKRLxANvt3OTEusYIzZmqbpU977daNYQtt9tm/frowxTy7XPkB2ZAwblVZ19Gbi+dHFKj88W+GH5+ba7WyFH12s0kxWeciDDUtkR57o6bLWSmPMF06fPr20IljbArdu3eozxuxYnvOFjtBDn7uX31fUgtb0b96BKXpkR8vOQ39eILVe9TnrQQ/tQegIbxdLFO89xpjdBw8ejJrNZrwugLUWa23ROTfUrSWPjPogP8TadZtAqACpF91EehCKNcV7EPkhZNSHrc90+Zi1drBer+e99+sDtFotAOGckz0jpMbJcM2yVUnoz3ZQxOLm+rPtvrXEybAnkDtW6DfG5IUQM+sCJEmyppa8X7vuLkbwB0+138DLwYrR+lZYybree+I4Zj3RC4MBL4ToiWDvUrxZeyIpoJxZd62VAUyMd70lurW2Uq1WG/cFUKlUEELUCoXCjJRy+71eIXCtGun8FKp/eNUs5JxntmFwyyJdSkF/TiPlKvYTkM5P4Vo1xLJy23t/t1KpNNc7NGmA69evE8dx/fHHH78ipezOazYmmTpP5tEDq04y2zB8+9hl7laTexvx3jNQCPnjr+5goC9YRf2QTJ0HG4Psjnjn3KWTJ082+/v71wSQAJcvX+a1115L0jQ93aMkIWhdPdHJEqvsQ0iawUbq4SNdrRlsxItVoliArc/QunpiJe3bVqv1/ksvveRnZ2fXBwA4evQotVrtJ865ue6FJG7uKo0r73e9SZcaVghFtjhArjxErryx04bIFgcQS3Lpcmeof3wcN3e155AD2Gw2y7Fjx+6V+OsCnDp1iitXrpw1xpxZPkhJqJ8/Rjo/DaKdKustw+RMC4DJmRa1lmkbyLeboF1iLB1Tb5m2EgSIxjTJz36AWsGqUsqwXC7/2bPPPvs8sObb+J56ZmdnqVarreHh4U2ZTObLS+GEkJjabeLaHLmRMYQOSFLLhY+u89HUPO+er1AVJeQyP+4a858VqpSQSiFMg6GPvsuIOcd8HJCa3vO/EKKotT4IXNJaT+zfv58PPvhgdQCAw4cP+4mJidkNGzY8o5Ta0jVQKVq3P8RaS2bzHmQQMVuNuXh9jjgoE+VLK2hSLY5RJaK+EsI0KF1+nUduvclnNmcp5QJuVRyp7YboXNuUtNZf9N5PbN269dKOHTtYfsjpApiYmGBmZqa6YcMGk8/nvyyECJdoBKUE9etnSWszRBt3kSlvIlMYIMoWVr1CCTN9ZIoDRH0lfO0WGyZeZePUmwwWFAJHuU9RzAVMz/seS3Qgylrrg7Va7aIQ4sPR0dEuiC4A7z3ee/PKK6/cHhoaGg7DcD9Lco8QEqUkzakLNK6dBiEJio8gw+xiHIrFJiSdd8k81YvvcuPdv6Jv5qfs2hISBqp9ArSG/j5FX05xe57VIEpa62eEECeFENeWAqxWIejh4eGx/fv3fycMwy8t73TOEbeapMYRDO4kt/0pMlt+iaC4GaHbtYM3Men8FK0b52h8fJz07kfgHVrB2Gdgz1Zxb3EhJGEmz/W5gBMfGppJr0GjKCKbzb4lpfwd731t4aS2VonTt3Pnzl/ZtWvXt8IwfGalAcYYkriFMQavQmRUQHQKM+9SXFxD2KR9Lxpl0FqTpoY0aTC23fdABJk8k3MBJy5bmrHvgchms61MJvPbwL8sAKxV8CYzMzMz1toPy+XyVq31tuXAUkqCMCQIQ7QUSBejFppPCQNNJpMljCKkbPuYUhKEYvKuIZCeweLiGa7HnWz3gs45rbU2xWLx3/bu3evPnDnDOhU7rbm5ublarXa+v78/CoLgsaWBvRgb7UtbrXVXU0r1vGW7IGYMgeqF2FDQZEPFzVnfVal67xFCSCHEP3vv6/cDANCs1WpzU1NTF6IomshmsxuVUptYdiHwoLIAcf1uL4S3hkyouHpXkiyzgve+JYT4e2tt5dy5c/cFABCnaTp/8+bNyUql8n4URZe11l4pVRZCZGDda3rrvU+FEF0nl+XutLEoOpcFnkrD8l93Ndb11EmzxpjvpWk6f/HiRTT3Lw3g2vT09Pz09PRkLpf7923btu0YHBx8PJvN7tNaD0spB4UQwYKmnHN30jS9Uq/Xz3rv08HBwT+UUm5dOmkQaCDHqSsNjPM8NiSIU8+Zq5Zm4ntKcefcdKVSqS2U2Z/0I58CcrQ/6uWVUvl8Pp8rFosFrdun+DRN0/n5+Wq9Xq875+qAGxsb+8qWLVv+REo5vHzCNDXErQaRdlgHToRks7meTNRsNl995513vlEqlWylUnn4r5S0zxRRp0kWi04BWCDuNIBHDhw48MLmzZu/uRKEtZY0NQgpCIOgJwFYaydv3Ljxu0eOHPmPl19++Z4mH1YckAJN2m7WXPK71enr1KjUb9y4caNQKNzq6+sbE0IUlk608C1Nr5y9bLVa/bvjx4//43vvvdda+FL08wB4EPFA/ebNmzcLhcLNfD7/eSll6T6eM/V6/Z/OnTv3aqPRmEzT9F5y/UUDdEFEUfRxLpcra623wMoJxTk3WalUvnf27Nnv3rlzZ4K2Re/JzyMGPqkoYFO5XN62e/fuL5bL5UNBEHxWCJEDvHNuutlsvj85Ofn2xMTEOe/9Ndpu2SWfJgC0g74PGAzDsH9gYGBDFEWR997Pz8/Pz83NzXjvZ4C7wIqXV582wFKQHO1MtiCOtsYTVv9+8n9f/hszVLRFCfSpmgAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOC0wMy0yM1QyMDo0MDo1NCswMTowMMg8IW4AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTgtMDMtMjNUMjA6NDA6NTQrMDE6MDC5YZnSAAAARnRFWHRzb2Z0d2FyZQBJbWFnZU1hZ2ljayA2LjcuOC05IDIwMTYtMDYtMTYgUTE2IGh0dHA6Ly93d3cuaW1hZ2VtYWdpY2sub3Jn5r80tgAAABh0RVh0VGh1bWI6OkRvY3VtZW50OjpQYWdlcwAxp/+7LwAAABh0RVh0VGh1bWI6OkltYWdlOjpoZWlnaHQANTEywNBQUQAAABd0RVh0VGh1bWI6OkltYWdlOjpXaWR0aAA1MTIcfAPcAAAAGXRFWHRUaHVtYjo6TWltZXR5cGUAaW1hZ2UvcG5nP7JWTgAAABd0RVh0VGh1bWI6Ok1UaW1lADE1MjE4MzQwNTSBWxDZAAAAE3RFWHRUaHVtYjo6U2l6ZQAzMC44S0JCwEbzfwAAAD90RVh0VGh1bWI6OlVSSQBmaWxlOi8vLi91cGxvYWRzLzU2L2F1czRHbDAvMTM4MS9vY3JmZWVkZXJfOTQ0MjMucG5nsES2aAAAAABJRU5ErkJggg== // @match *://*/* // @require https://cdnjs.cloudflare.com/ajax/libs/onnxruntime-web/1.17.0/ort.min.js // @connect cdn.jsdelivr.net // @connect unpkg.com // @connect cdnjs.cloudflare.com // @connect fastly.jsdelivr.net // @connect registry.npmmirror.com // @connect raw.githubusercontent.com // @connect ghproxy.com // @connect ghfast.top // @connect mirror.ghproxy.com // @connect raw.kkgithub.com // @connect github.moeyy.xyz // @connect ghps.cc // @connect cors.isteed.cc // @connect raw.githubusercontents.com // @grant GM_getValue // @grant GM_notification // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_xmlhttpRequest // @grant unsafeWindow // ==/UserScript== (function () { 'use strict'; class t{static extractImageFromElement(t){const e=document.createElement("canvas");e.width=t.naturalWidth||t.width,e.height=t.naturalHeight||t.height;const n=e.getContext("2d");n.fillStyle="#FFFFFF",n.fillRect(0,0,e.width,e.height),n.drawImage(t,0,0);const i=n.getImageData(0,0,e.width,e.height);return {data:this.toGrayscale(i.data),width:e.width,height:e.height}}static async loadImage(t){if("undefined"==typeof document)return this.loadImageInServiceWorker(t);if(t instanceof HTMLImageElement)return this.extractImageFromElement(t);const e=new Image;e.crossOrigin="anonymous",e.src="string"==typeof t?t:URL.createObjectURL(t),await new Promise((t,n)=>{e.onload=()=>t(),e.onerror=()=>n(new Error("图片加载失败")),setTimeout(()=>n(new Error("图片加载超时")),1e4);});const n=document.createElement("canvas");n.width=e.width,n.height=e.height;const i=n.getContext("2d");i.fillStyle="#FFFFFF",i.fillRect(0,0,e.width,e.height),i.drawImage(e,0,0);const o=i.getImageData(0,0,e.width,e.height),s=this.toGrayscale(o.data);return "string"!=typeof t&&URL.revokeObjectURL(e.src),{data:s,width:e.width,height:e.height}}static async loadImageInServiceWorker(t){let e;if("string"==typeof t)if(t.startsWith("data:")){const n=await fetch(t);e=await n.blob();}else {const n=await fetch(t);e=await n.blob();}else {if(!(t instanceof Blob))throw new Error("Service Worker 环境不支持 HTMLImageElement");e=t;}const n=await createImageBitmap(e),i=new OffscreenCanvas(n.width,n.height),o=i.getContext("2d");o.fillStyle="#FFFFFF",o.fillRect(0,0,i.width,i.height),o.drawImage(n,0,0);const s=o.getImageData(0,0,i.width,i.height),r=this.toGrayscale(s.data);return n.close(),{data:r,width:i.width,height:i.height}}static toGrayscale(t){const e=new Uint8ClampedArray(t.length/4);for(let n=0;n{if("undefined"!=typeof ort)return ort;if("undefined"!=typeof window&&window.ort)return window.ort;if("undefined"!=typeof globalThis&&globalThis.ort)return globalThis.ort;try{if("undefined"!=typeof unsafeWindow&&unsafeWindow.ort)return unsafeWindow.ort}catch(t){}return null};let e=t();if(e)return console.log("✅ ort 已存在"),e;console.log("⏳ 等待 ort 加载...");for(let n=0;n<100;n++)if(await new Promise(t=>setTimeout(t,100)),e=t(),e)return console.log("✅ ort 已就绪"),e;throw new Error("等待 ort 超时")}async recognize(e){this.initialized&&this.session||await this.init();const n=Date.now(),{data:i,width:o,height:s}=await t.loadImage(e),r=Math.floor(o*(64/s)),a=t.resize(i,o,s,r,64),d=t.normalize(a),c={input1:new this.ort.Tensor("float32",d,[1,1,64,r])},l=(await this.session.run(c)).output,h=this.decodeOutput(l);return console.log(`识别完成: ${h} (耗时: ${Date.now()-n}ms)`),{text:h}}decodeOutput(t){const e=this.convertToNumberArray(t.data),n=[];let i=-1;for(const o of e){if(o===i)continue;if(i=o,o<=0||o>=this.charsets.length)continue;const t=this.charsets[o];t&&n.push(t);}return n.join("")}convertToNumberArray(t){const e=[];for(let n=0;nt.startsWith("__reactProps$")||t.startsWith("__reactFiber$")||t.startsWith("__reactEventHandlers$"))){const e=t._valueTracker;e&&e.setValue("");}if((t.ngModel||t.getAttribute("ng-model")||t.getAttribute("[(ngModel)]"))&&(this.dispatchEvent(t,"input"),this.dispatchEvent(t,"blur")),Object.keys(t).find(t=>t.startsWith("__vue"))||t.hasAttribute("v-model")){const e=new CompositionEvent("compositionstart",{bubbles:true}),n=new CompositionEvent("compositionend",{bubbles:true,data:t.value});t.dispatchEvent(e),t.dispatchEvent(n);}}async simulateTyping(t,e){for(const n of e){this.dispatchKeyEvent(t,"keydown",n);const e=Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,"value")?.set;e?e.call(t,t.value+n):t.value+=n;const i=new InputEvent("input",{bubbles:true,cancelable:true,inputType:"insertText",data:n});t.dispatchEvent(i),this.dispatchKeyEvent(t,"keyup",n),await this.delay(50+100*Math.random());}this.dispatchEvent(t,"change"),this.dispatchEvent(t,"blur");}dispatchEvent(t,e){t.dispatchEvent(new Event(e,{bubbles:true,cancelable:true}));}dispatchKeyEvent(t,e,n){t.dispatchEvent(new KeyboardEvent(e,{key:n,code:`Key${n.toUpperCase()}`,charCode:n.charCodeAt(0),keyCode:n.charCodeAt(0),bubbles:true,cancelable:true}));}delay(t){return new Promise(e=>setTimeout(e,t))}highlightInput(t){const e=t.style.border,n=t.style.boxShadow;t.style.border="2px solid #4CAF50",t.style.boxShadow="0 0 8px rgba(76, 175, 80, 0.5)",setTimeout(()=>{t.style.border=e,t.style.boxShadow=n;},2e3);}async submitForm(t){const e=t.closest("form");if(e){const t=new Event("submit",{bubbles:true,cancelable:true});e.dispatchEvent(t)&&e.submit();}else {const e=(t.parentElement?.parentElement||document).querySelector('button[type="submit"], input[type="submit"], button:not([type])');e&&e.click();}}getLastFilledInput(){return this.lastFilledInput}}const i={MODEL_VERSION:"1.5.1",MODEL_REPO:"MakotoArai-CN/ddddocr-webjs",MODEL_BRANCH:"main",MODEL_PATH:"public/common.onnx",CHARSETS_PATH:"public/charsets.json",WASM_VERSION:"1.17.0",CACHE_DURATION:2592e6,CAPTCHA_KEYWORDS:["captcha","verify","code","vcode","authcode","验证码","checkcode","yzm","capimg","signCaptcha","imgcode","seccode","validcode","yanzhengma","validatecode","piccode","imgverify","codeimg","randcode","identify","kaptcha","verifycode","imgCaptcha","captchaImg","vcodeImg"],INPUT_KEYWORDS:["captcha","verify","code","vcode","authcode","验证码","checkcode","yzm","validatecode","validcode","seccode","imgcode","randcode","identify","kaptcha","answer","verifycode","captchaInput","vcodeInput"],AGREEMENT_KEYWORDS:["agree","agreement","accept","terms","policy","privacy","同意","协议","条款","隐私","用户协议","隐私政策","tos","consent","checkbox","check"],EXCLUDED_INPUT_TYPES:["password","email","tel","phone","mobile","hidden","submit","button","reset","file","image","checkbox","radio"],EXCLUDED_INPUT_NAMES:["username","user","account","email","phone","mobile","tel","password","pwd","pass","name","realname","nickname","search","query","q","keyword","address","city"],EXCLUDE_PATTERNS:["avatar","logo","icon","banner","ad","sponsor","background","bg","profile","user","photo","emoji","emoticon","sticker","gif","loading","spinner","placeholder","slider","slide","drag","puzzle","jigsaw"],MIN_CAPTCHA_WIDTH:50,MIN_CAPTCHA_HEIGHT:20,MAX_CAPTCHA_WIDTH:400,MAX_CAPTCHA_HEIGHT:150,AUTO_DETECT_INTERVAL:2e3,GITHUB_MIRRORS:["https://raw.githubusercontent.com","https://ghproxy.com/https://raw.githubusercontent.com","https://ghfast.top/https://raw.githubusercontent.com","https://mirror.ghproxy.com/https://raw.githubusercontent.com","https://raw.kkgithub.com","https://gh-proxy.org","https://hk.gh-proxy.org","https://cdn.gh-proxy.org","https://edgeone.gh-proxy.org","https://github.moeyy.xyz/https://raw.githubusercontent.com","https://ghps.cc/https://raw.githubusercontent.com","https://cors.isteed.cc/github.com/MakotoArai-CN/ddddocr-webjs/raw/main","https://raw.githubusercontents.com"],CDN_SOURCES:["https://cdn.jsdelivr.net","https://unpkg.com","https://cdnjs.cloudflare.com","https://fastly.jsdelivr.net","https://registry.npmmirror.com"]},o={debugMode:false,autoDetect:true,captchaSelector:"",inputSelector:"",submitSelector:"",agreementSelector:"",agreementSelectors:[],autoCheckAgreement:true,useLocalModel:false,localModelPath:"",localCharsetsPath:"",autoDownload:true,enableWhitelist:true,whitelist:[],useUploadedModel:false,useUploadedWasm:false,theme:"auto",typewriterEffect:true,autoCalculate:false,calculateOutputMode:"result",calculateRules:[],customIncludeKeywords:[],customExcludePatterns:[],enableNotification:true},s=class{static setDebugMode(t){this.debugMode=t;}static isDebugMode(){return this.debugMode}static debug(...t){this.debugMode&&console.log(`${this.prefix} [DEBUG]`,...t);}static info(...t){this.debugMode&&console.info(`${this.prefix} [INFO]`,...t);}static warn(...t){console.warn(`${this.prefix} [WARN]`,...t);}static error(...t){console.error(`${this.prefix} [ERROR]`,...t);}static group(t){this.debugMode&&console.group(`${this.prefix} ${t}`);}static groupEnd(){this.debugMode&&console.groupEnd();}static table(t){this.debugMode&&console.table(t);}static time(t){this.debugMode&&console.time(`${this.prefix} ${t}`);}static timeEnd(t){this.debugMode&&console.timeEnd(`${this.prefix} ${t}`);}};s.debugMode=false,s.prefix="[DDDD OCR]";let r=s;const a=class{static parseExpression(t){const e=t.trim();let n=e;for(const s of this.NOISE_CHARS)n=n.split(s).join("");const i=[/^(\d+(?:\.\d+)?)\s*([+\-×*÷/xX])\s*(\d+(?:\.\d+)?)\s*[==]?\s*\d*$/,/^(\d+(?:\.\d+)?)\s*([+\-×*÷/xX])\s*(\d+(?:\.\d+)?)\s*[==]$/,/^(\d+(?:\.\d+)?)\s*([+\-×*÷/xX])\s*(\d+(?:\.\d+)?)$/];for(const s of i){const t=n.match(s);if(t){const n=parseFloat(t[1]),i=t[2],o=parseFloat(t[3]);if(!isNaN(n)&&!isNaN(o))return {num1:n,operator:i,num2:o,originalText:e,cleanExpression:`${n}${this.normalizeOperator(i)}${o}`}}}const o=e.match(/^(\d+(?:\.\d+)?)\s*([+\-×*÷/xX])\s*(\d+(?:\.\d+)?)\s*[==]\s*(\d+)$/);if(o){const t=parseFloat(o[1]),n=o[2],i=parseFloat(o[3]),s=parseFloat(o[4]);if(!isNaN(t)&&!isNaN(i)){const o=this.compute(t,n,i);if(null!==o&&Math.abs(o-s)>.001)return {num1:t,operator:n,num2:i,originalText:e,cleanExpression:`${t}${this.normalizeOperator(n)}${i}`}}}return null}static normalizeOperator(t){switch(t){case "x":case "X":case "*":return "×";case "/":return "÷";default:return t}}static compute(t,e,n){switch(e){case "+":return t+n;case "-":return t-n;case "*":case "×":case "x":case "X":return t*n;case "/":case "÷":return 0===n?null:t/n;default:return null}}static calculate(t){return this.compute(t.num1,t.operator,t.num2)}static formatResult(t){return Number.isInteger(t)?String(t):t.toFixed(2).replace(/\.?0+$/,"")}static formatEquation(t,e){const n=this.normalizeOperator(t.operator);return `${t.num1}${n}${t.num2}=${this.formatResult(e)}`}static matchesPattern(t,e,n){try{if("regex"===n)return new RegExp(e,"i").test(t);{const n="^"+e.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*").replace(/\?/g,".")+"$";return new RegExp(n,"i").test(t)}}catch{return false}}static getOutputModeForHostname(t,e,n){for(const i of e)if(i.enabled&&this.matchesPattern(t,i.pattern,i.matchType))return i.outputMode;return n}static shouldCalculateForHostname(t,e){if(!e||0===e.length)return true;const n=e.filter(t=>t.enabled);if(0===n.length)return true;for(const i of n)if(this.matchesPattern(t,i.pattern,i.matchType))return true;return false}static processResult(t,e,n){if(!e.autoCalculate)return t;if(e.rules&&e.rules.length>0&&!this.shouldCalculateForHostname(n,e.rules))return t;const i=this.parseExpression(t);if(!i)return t;const o=this.calculate(i);return null===o?t:"equation"===this.getOutputModeForHostname(n,e.rules,e.outputMode)?this.formatEquation(i,o):this.formatResult(o)}static isExpression(t){return null!==this.parseExpression(t)}};a.OPERATORS=["+","-","×","*","÷","/","x","X"],a.EQUALS_CHARS=["=","="],a.QUESTION_CHARS=["?","?","〇","o","O","0"],a.NOISE_CHARS=["?","?","〇"," ","\t","\n","\r"];let d=a;class c{constructor(){this.detectedCaptchas=[],this.processedElements=new WeakMap,this.checkedAgreements=new WeakSet,this.customIncludeKeywords=[],this.customExcludePatterns=[];}setCustomPatterns(t,e){this.customIncludeKeywords=t.map(t=>t.toLowerCase().trim()).filter(Boolean),this.customExcludePatterns=e.map(t=>t.toLowerCase().trim()).filter(Boolean);}getCaptchaKeywords(){return 0===this.customIncludeKeywords.length?i.CAPTCHA_KEYWORDS:[...i.CAPTCHA_KEYWORDS,...this.customIncludeKeywords]}getExcludePatterns(){return 0===this.customExcludePatterns.length?i.EXCLUDE_PATTERNS:[...i.EXCLUDE_PATTERNS,...this.customExcludePatterns]}hasNearbyCaptchaInput(t){const e=this.findRelatedInput(t);return !!e&&this.isCaptchaInputByName(e)}isExcludedElement(t){const e=(t.className?.toString?.()||"").toLowerCase(),n=(t.id||"").toLowerCase(),i=this.getExcludePatterns(),o=`${e} ${n}`.trim();return i.some(t=>o.includes(t))}scan(){return this.detectedCaptchas=[],r.time("CaptchaDetector.scan"),this.scanImages(),this.scanCanvas(),this.scanSvg(),this.scanBackgroundImages(),r.timeEnd("CaptchaDetector.scan"),r.debug("扫描结果:",this.detectedCaptchas.length,"个验证码"),this.detectedCaptchas}scanImages(){document.querySelectorAll("img").forEach((t,e)=>{if(this.isLikelyCaptcha(t)){const n=t.getBoundingClientRect(),i={id:`captcha-${e}`,type:"image",element:t,src:t.src,rect:n,confidence:this.calculateConfidence(t),inputElement:this.findRelatedInput(t),elementInfo:this.extractCaptchaInfo(t)};this.detectedCaptchas.push(i),r.debug("检测到图片验证码:",i.elementInfo);}});}scanCanvas(){document.querySelectorAll("canvas").forEach((t,e)=>{if(this.isLikelyCanvasCaptcha(t)){const n=t.getBoundingClientRect(),i={id:`captcha-canvas-${e}`,type:"canvas",element:t,rect:n,confidence:this.calculateConfidence(t),inputElement:this.findRelatedInput(t),elementInfo:this.extractCaptchaInfo(t)};this.detectedCaptchas.push(i),r.debug("检测到Canvas验证码:",i.elementInfo);}});}scanSvg(){document.querySelectorAll("svg").forEach((t,e)=>{if(this.isLikelySvgCaptcha(t)){const n=t.getBoundingClientRect(),i={id:`captcha-svg-${e}`,type:"svg",element:t,rect:n,confidence:this.calculateConfidence(t),inputElement:this.findRelatedInput(t),elementInfo:this.extractCaptchaInfo(t)};this.detectedCaptchas.push(i),r.debug("检测到SVG验证码:",i.elementInfo);}});}scanBackgroundImages(){document.querySelectorAll('div[style*="background"], span[style*="background"], td[style*="background"]').forEach((t,e)=>{const n=t;if(this.isLikelyBackgroundCaptcha(n)){const t=n.getBoundingClientRect(),i={id:`captcha-bg-${e}`,type:"background",element:n,src:n.style.backgroundImage||"",rect:t,confidence:this.calculateConfidence(n),inputElement:this.findRelatedInput(n),elementInfo:this.extractCaptchaInfo(n)};this.detectedCaptchas.push(i),r.debug("检测到背景图验证码:",i.elementInfo);}});}extractCaptchaInfo(t){const e=t.getBoundingClientRect();return {tagName:t.tagName.toLowerCase(),id:t.id||null,className:t.className?.toString?.()||"",width:Math.round(e.width),height:Math.round(e.height),src:t.src}}extractInputInfo(t){return {tagName:t.tagName.toLowerCase(),id:t.id||null,name:t.name||null,className:t.className||"",placeholder:t.placeholder||null,type:t.type||"text"}}getEffectiveSize(t){const e=t.getBoundingClientRect();let n=e.width,i=e.height;return t instanceof HTMLImageElement&&(0===n&&t.naturalWidth>0&&(n=t.naturalWidth),0===i&&t.naturalHeight>0&&(i=t.naturalHeight),0===n&&(n=parseInt(t.getAttribute("width")||"0")||0),0===i&&(i=parseInt(t.getAttribute("height")||"0")||0)),{width:n,height:i}}isLikelyCaptcha(t){const{width:e,height:n}=this.getEffectiveSize(t);return !(!this.isCaptchaSize(e,n)||!this.isVisibleOrHasSize(t,e,n)||this.isExcludedImage(t)||!this.matchesKeywords(t)&&!this.srcContainsKeywords(t.src)&&!this.parentContainsKeywords(t)&&!this.hasNearbyCaptchaInput(t)&&(!this.isDataUrlImage(t)||!this.isCaptchaSize(e,n)||!this.hasNearbyCaptchaInput(t)&&!this.parentContainsKeywords(t)))}isDataUrlImage(t){return t.src&&(t.src.startsWith("data:image/")||t.src.startsWith("blob:"))?t.src:null}isVisibleOrHasSize(t,e,n){const i=window.getComputedStyle(t);if("none"===i.display||"hidden"===i.visibility||"0"===i.opacity)return false;const o=t.getBoundingClientRect();return !(o.width<=0&&o.height<=0)&&e>0&&n>0}getImageSrcForExclusionCheck(t){const e=(t.currentSrc||t.src||"").trim();if(!e)return "";if(e.startsWith("data:image/")||e.startsWith("blob:"))return "";try{const t=new URL(e,window.location.href);return (t.origin+t.pathname).toLowerCase()}catch{return e.slice(0,200).toLowerCase()}}isExcludedImage(t){const e=this.getImageSrcForExclusionCheck(t),n=(t.alt||"").toLowerCase(),i=(t.className?.toString?.()||"").toLowerCase(),o=(t.id||"").toLowerCase(),s=this.getExcludePatterns(),r=`${e} ${n} ${i} ${o}`.trim();return s.some(t=>r.includes(t))}isLikelyCanvasCaptcha(t){const e=t.getBoundingClientRect();return !(!this.isCaptchaSize(e.width,e.height)||!this.isVisible(t)||this.isExcludedElement(t)||!this.matchesKeywords(t)&&!this.parentContainsKeywords(t)&&!this.hasNearbyCaptchaInput(t))}isLikelySvgCaptcha(t){const e=t.clientWidth||parseInt(t.getAttribute("width")||"0"),n=t.clientHeight||parseInt(t.getAttribute("height")||"0");return !(!this.isCaptchaSize(e,n)||!this.isVisible(t)||this.isExcludedElement(t)||!this.matchesKeywords(t)&&!this.parentContainsKeywords(t)&&!this.hasNearbyCaptchaInput(t))}isLikelyBackgroundCaptcha(t){const e=t.style.backgroundImage||"";if(!e||"none"===e)return false;const n=t.getBoundingClientRect();return !!this.isCaptchaSize(n.width,n.height)&&!!this.isVisible(t)&&!this.isExcludedElement(t)&&(!!this.matchesKeywords(t)||!!this.parentContainsKeywords(t)||!!this.hasNearbyCaptchaInput(t)||!!e.includes("data:image/")&&(this.hasNearbyCaptchaInput(t)||this.parentContainsKeywords(t)))}isCaptchaSize(t,e){return t>=i.MIN_CAPTCHA_WIDTH&&t<=i.MAX_CAPTCHA_WIDTH&&e>=i.MIN_CAPTCHA_HEIGHT&&e<=i.MAX_CAPTCHA_HEIGHT}isVisible(t){const e=window.getComputedStyle(t),n=t.getBoundingClientRect();return "none"!==e.display&&"hidden"!==e.visibility&&"0"!==e.opacity&&n.width>0&&n.height>0}isFrameworkCheckbox(t){const e=["el-checkbox__original","ant-checkbox-input","ivu-checkbox-input","van-checkbox__input","weui-check","mdui-checkbox-input","mdc-checkbox__native-control"];for(const i of e)if(t.classList.contains(i))return true;const n=[".el-checkbox",".ant-checkbox",".ant-checkbox-wrapper",".ivu-checkbox",".ivu-checkbox-wrapper",".van-checkbox",".weui-check__label",".mdui-checkbox",".mdc-checkbox"];for(const i of n)if(t.closest(i))return true;return false}isCheckboxFunctional(t){if(t.disabled)return false;if(this.isFrameworkCheckbox(t)){const e=[t.closest(".el-checkbox"),t.closest(".ant-checkbox-wrapper"),t.closest(".ivu-checkbox-wrapper"),t.closest(".van-checkbox"),t.closest("label")];for(const t of e)if(t){const e=window.getComputedStyle(t);if("none"!==e.display&&"hidden"!==e.visibility)return true}let n=t.parentElement,i=0;for(;n&&i<5;){const t=window.getComputedStyle(n);if("none"===t.display)return false;if("hidden"===t.visibility)return false;n=n.parentElement,i++;}return true}if("none"===window.getComputedStyle(t).display)return false;let e=t.parentElement,n=0;for(;e&&n<5;){const t=window.getComputedStyle(e);if("none"===t.display||"hidden"===t.visibility)return false;e=e.parentElement,n++;}return true}findClickableTarget(t){const e=t.closest(".el-checkbox");if(e)return e.querySelector(".el-checkbox__inner")||e;const n=t.closest(".ant-checkbox-wrapper");if(n)return n.querySelector(".ant-checkbox-inner")||n;const i=t.closest(".ivu-checkbox-wrapper");if(i)return i.querySelector(".ivu-checkbox-inner")||i;const o=t.closest(".van-checkbox");return o?o.querySelector(".van-checkbox__icon")||o:t.closest("label")||null}matchesKeywords(t){const e=(t.className?.toString?.()||"").toLowerCase(),n=(t.id||"").toLowerCase();return this.getCaptchaKeywords().some(t=>e.includes(t)||n.includes(t))}srcContainsKeywords(t){if(!t)return false;const e=t.toLowerCase();return this.getCaptchaKeywords().some(t=>e.includes(t))}parentContainsKeywords(t){let e=t.parentElement,n=0;for(;e&&n<3;){if(this.matchesKeywords(e))return true;e=e.parentElement,n++;}return false}hasNearbyInput(t){return null!==this.findRelatedInput(t)}getInputLabelText(t){try{if(t.id){const e=document.querySelector(`label[for="${CSS.escape(t.id)}"]`);if(e)return (e.textContent||"").trim()}const e=t.closest("label");if(e)return (e.textContent||"").trim()}catch{}return ""}getInputSearchText(t){const e=[];return e.push(t.name||""),e.push(t.id||""),e.push(t.className||""),e.push(t.placeholder||""),e.push(t.getAttribute("aria-label")||""),e.push(t.getAttribute("data-label")||""),e.push(t.getAttribute("data-name")||""),e.push(this.getInputLabelText(t)),e.join(" ").toLowerCase()}isCaptchaInputByName(t){const e=this.getInputSearchText(t);return i.INPUT_KEYWORDS.some(t=>e.includes(t))}isExcludedInputByText(t){const e=this.getInputSearchText(t);return ["username","user","account","email","phone","mobile","tel","password","pwd","pass","search","query","keyword","用户名","账号","密码","手机","手机号","邮箱","搜索","查询","关键字"].some(t=>e.includes(t))}scoreInputCandidate(t,e,n){const i=this.calculateDistance(e,n);let o=0;const s=this.getInputSearchText(t);return this.isCaptchaInputByName(t)&&(o+=120),s.includes("验证码")&&(o+=140),s.includes("verify")&&(o+=80),s.includes("vcode")&&(o+=80),s.includes("authcode")&&(o+=80),s.includes("checkcode")&&(o+=80),s.includes("yzm")&&(o+=60),this.isExcludedInputByText(t)&&(o-=200),i-o}findClosestInputInContainer(t,e,n=Infinity){const i=t.querySelectorAll("input");let o=null,s=Infinity,r=Infinity;for(const a of i){const t=a;if(!this.isValidCaptchaInput(t))continue;const i=a.getBoundingClientRect(),d=this.calculateDistance(e,i);if(d>n)continue;const c=this.scoreInputCandidate(t,e,i);(cn.right&&e.left-n.right<220&&Math.abs(e.top-n.top)<90||e.top>n.bottom&&e.top-n.bottom<160&&Math.abs(e.left-n.left)<160||this.calculateDistance(n,e)<240))continue;const i=this.scoreInputCandidate(t,n,e);i{if(!this.isVisible(t))return;const{width:e,height:o}=this.getEffectiveSize(t);if(!this.isCaptchaSize(e,o))return;if(this.isExcludedImage(t))return;const s=t.getBoundingClientRect(),r=this.calculateDistance(n,s);i.push({element:t,distance:r,type:"image"});}),document.querySelectorAll("canvas").forEach(t=>{if(!this.isVisible(t))return;const e=t.getBoundingClientRect();if(!this.isCaptchaSize(e.width,e.height))return;const o=this.calculateDistance(n,e);i.push({element:t,distance:o,type:"canvas"});}),document.querySelectorAll("svg").forEach(t=>{if(!this.isVisible(t))return;const e=t.clientWidth||parseInt(t.getAttribute("width")||"0"),o=t.clientHeight||parseInt(t.getAttribute("height")||"0");if(!this.isCaptchaSize(e,o))return;const s=t.getBoundingClientRect(),r=this.calculateDistance(n,s);i.push({element:t,distance:r,type:"svg"});}),document.querySelectorAll('div[style*="background"], span[style*="background"]').forEach(t=>{const e=t;if(!this.isVisible(e))return;const o=e.style.backgroundImage||"";if(!o||"none"===o)return;const s=e.getBoundingClientRect();if(!this.isCaptchaSize(s.width,s.height))return;const r=this.calculateDistance(n,s);i.push({element:e,distance:r,type:"background"});}),i.sort((t,e)=>t.distance-e.distance);const o=i.slice(0,3);for(const s of o){const t=Math.max(0,100-Math.floor(s.distance/5));e.push({element:s.element,type:"captcha",confidence:t,selector:this.generateSelector(s.element)});}return r.debug("猜测的验证码元素:",e),e}guessRelatedInput(t){const e=[],n=t.getBoundingClientRect();r.debug("开始猜测关联的输入框, 验证码位置:",n);const i=[];document.querySelectorAll("input").forEach(t=>{const e=t;if(!this.isValidCaptchaInput(e))return;if(!this.isVisible(e))return;const o=e.getBoundingClientRect(),s=this.calculateDistance(n,o),r=this.isCaptchaInputByName(e),a=this.scoreInputCandidate(e,n,o);i.push({element:e,distance:s,hasKeyword:r,score:a});}),i.sort((t,e)=>t.score-e.score);const o=i.slice(0,3);for(const s of o){let t=Math.max(0,100-Math.floor(s.distance/5));s.hasKeyword&&(t=Math.min(100,t+20)),e.push({element:s.element,type:"input",confidence:t,selector:this.generateSelector(s.element)});}return r.debug("猜测的输入框元素:",e),e}guessAgreementCheckboxes(){const t=[];return document.querySelectorAll('input[type="checkbox"]').forEach(e=>{const n=e;if(!this.isCheckboxFunctional(n))return;if(this.checkedAgreements.has(n))return;const o=[];o.push(n.name||""),o.push(n.id||""),o.push(n.className||""),o.push(n.getAttribute("data-type")||""),o.push(n.getAttribute("data-name")||""),o.push(n.getAttribute("aria-label")||""),o.push(n.getAttribute("data-v-inspector")||"");const s=n.id?document.querySelector(`label[for="${n.id}"]`):null;s&&(o.push(s.textContent||""),o.push(s.className||""));const r=n.closest("label");r&&(o.push(r.textContent||""),o.push(r.className||""));const a=[n.closest(".el-checkbox"),n.closest(".ant-checkbox-wrapper"),n.closest(".ivu-checkbox-wrapper"),n.closest(".van-checkbox"),n.closest('[class*="checkbox"]')];for(const t of a)t&&(o.push(t.textContent||""),o.push(t.className||""));let d=n.parentElement,c=0;for(;d&&c<6;){const t=d.tagName.toLowerCase();if(o.push(d.className||""),o.push(d.id||""),["label","div","span","p","li","td"].includes(t)){const t=d.children;for(let e=0;eh.includes(t))){const e=this.findClickableTarget(n);t.push({element:n,type:"agreement",confidence:80,selector:this.generateSelector(n),clickTarget:e||void 0});}}),r.debug("猜测的协议复选框:",t),t}findAgreementsBySelectors(t){const e=[];for(const i of t)if(i.trim())try{document.querySelectorAll(i).forEach(t=>{if(t instanceof HTMLInputElement&&"checkbox"===t.type&&!this.checkedAgreements.has(t)){const n=this.findClickableTarget(t);e.push({element:t,type:"agreement",confidence:100,selector:i,clickTarget:n||void 0});}});}catch(n){r.warn("无效的协议选择器:",i,n);}return e}markAgreementChecked(t){this.checkedAgreements.add(t);}calculateDistance(t,e){const n=t.left+t.width/2,i=t.top+t.height/2,o=e.left+e.width/2,s=e.top+e.height/2;return Math.sqrt(Math.pow(o-n,2)+Math.pow(s-i,2))}generateSelector(t){if(t.id)return "#"+t.id;const e=t.className;if(e){const n=e.toString().trim().split(/\s+/).filter(t=>t&&!t.includes(":"));if(n.length>0){const e=t.tagName.toLowerCase()+"."+n.join(".");if(1===document.querySelectorAll(e).length)return e}}const n=[];let i=t;for(;i&&i!==document.body&&n.length<5;){let t=i.tagName.toLowerCase();if(i.id){n.unshift("#"+i.id);break}const e=i.parentElement;if(e){const n=Array.from(e.children).filter(t=>t.tagName===i.tagName);n.length>1&&(t+=":nth-of-type("+(n.indexOf(i)+1)+")");}n.unshift(t),i=i.parentElement;}return n.join(" > ")}async captureImage(t){switch(t.type){case "image":return this.captureImgElement(t.element);case "canvas":return this.captureCanvasElement(t.element);case "svg":return this.captureSvgElement(t.element);case "background":return this.captureBackgroundElement(t.element)}}async captureBuffer(t){const e=await this.captureBlob(t);return await e.arrayBuffer()}async captureBlob(t){switch(t.type){case "image":return this.captureImgAsBlob(t.element);case "canvas":return this.captureCanvasAsBlob(t.element);case "svg":return this.captureSvgAsBlob(t.element);case "background":return this.captureBackgroundAsBlob(t.element)}}async captureImgElement(t){await this.waitForImageLoad(t);const e=document.createElement("canvas"),n=e.getContext("2d"),i=t.naturalWidth||t.width,o=t.naturalHeight||t.height;e.width=i,e.height=o,n.drawImage(t,0,0,i,o);try{return e.toDataURL("image/png")}catch{if(t.src.startsWith("data:"))return t.src;throw new Error("无法捕获跨域图片")}}async captureImgAsBlob(t){if(await this.waitForImageLoad(t),t.src&&!t.src.startsWith("data:")&&!t.src.startsWith("blob:"))try{const e=await fetch(t.src,{credentials:"include"});if(e.ok&&(e.headers.get("content-type")||"").includes("image/"))return await e.blob()}catch{}const e=document.createElement("canvas"),n=e.getContext("2d"),i=t.naturalWidth||t.width,o=t.naturalHeight||t.height;return e.width=i,e.height=o,n.fillStyle="#FFFFFF",n.fillRect(0,0,i,o),n.drawImage(t,0,0,i,o),await new Promise((t,n)=>{e.toBlob(e=>{e?t(e):n(new Error("图片转换失败"));},"image/png");})}async waitForImageLoad(t){if(!(t.complete&&t.naturalWidth>0||t.src?.startsWith("data:")&&t.naturalWidth>0))return new Promise((e,n)=>{const i=setTimeout(()=>n(new Error("图片加载超时")),5e3),o=()=>{r(),e();},s=()=>{r(),n(new Error("图片加载失败"));},r=()=>{clearTimeout(i),t.removeEventListener("load",o),t.removeEventListener("error",s);};t.addEventListener("load",o),t.addEventListener("error",s);})}captureCanvasElement(t){return t.toDataURL("image/png")}captureCanvasAsBlob(t){return new Promise((e,n)=>{t.toBlob(t=>{t?e(t):n(new Error("Canvas转换失败"));},"image/png");})}async captureSvgElement(t){const e=await this.captureSvgAsBlob(t);return await this.blobToDataURL(e)}async captureSvgAsBlob(t){const e=t.cloneNode(true),n=t.getBoundingClientRect();e.setAttribute("width",String(n.width)),e.setAttribute("height",String(n.height)),e.getAttribute("xmlns")||e.setAttribute("xmlns","http://www.w3.org/2000/svg");const i=(new XMLSerializer).serializeToString(e),o=new Blob([i],{type:"image/svg+xml"}),s=URL.createObjectURL(o);try{const t=await new Promise((t,e)=>{const n=new Image;n.onload=()=>t(n),n.onerror=()=>e(new Error("SVG转换失败")),n.src=s;}),e=document.createElement("canvas");e.width=Math.max(1,Math.round(n.width)),e.height=Math.max(1,Math.round(n.height));const i=e.getContext("2d");return i.fillStyle="#FFFFFF",i.fillRect(0,0,e.width,e.height),i.drawImage(t,0,0),await new Promise((t,n)=>{e.toBlob(e=>{e?t(e):n(new Error("SVG转换失败"));},"image/png");})}finally{URL.revokeObjectURL(s);}}async captureBackgroundElement(t){const e=await this.captureBackgroundAsBlob(t);return await this.blobToDataURL(e)}async captureBackgroundAsBlob(t){const e=(t.style.backgroundImage||window.getComputedStyle(t).backgroundImage||"").match(/url\(['"]?(.+?)['"]?\)/);if(!e)throw new Error("无法提取背景图URL");const n=e[1];if(n.startsWith("data:")){const t=await fetch(n);return await t.blob()}const i=new Image;i.crossOrigin="anonymous",i.src=n,await new Promise((t,e)=>{i.onload=()=>t(),i.onerror=()=>e(new Error("背景图加载失败")),setTimeout(()=>e(new Error("背景图加载超时")),5e3);});const o=document.createElement("canvas"),s=t.getBoundingClientRect();o.width=Math.max(1,Math.round(s.width)),o.height=Math.max(1,Math.round(s.height));const r=o.getContext("2d");return r.fillStyle="#FFFFFF",r.fillRect(0,0,o.width,o.height),r.drawImage(i,0,0,o.width,o.height),new Promise((t,e)=>{o.toBlob(n=>{n?t(n):e(new Error("背景图转换失败"));},"image/png");})}blobToDataURL(t){return new Promise((e,n)=>{const i=new FileReader;i.onload=()=>e(String(i.result)),i.onerror=()=>n(new Error("读取失败")),i.readAsDataURL(t);})}highlight(t){t.element.setAttribute("data-captcha-highlight","true");}unhighlight(t){t.element.removeAttribute("data-captcha-highlight");}highlightGuessed(t){t.setAttribute("data-captcha-guessed","true");}unhighlightGuessed(t){t.removeAttribute("data-captcha-guessed");}unhighlightAllGuessed(){document.querySelectorAll("[data-captcha-guessed]").forEach(t=>{t.removeAttribute("data-captcha-guessed");});}getDetectedCaptchas(){return this.detectedCaptchas}getMostLikelyCaptcha(){return 0===this.detectedCaptchas.length?null:this.detectedCaptchas.reduce((t,e)=>e.confidence>t.confidence?e:t)}hasElementChanged(t){const e=this.getElementHash(t),n=this.processedElements.get(t);return !n||e!==n}markElementProcessed(t){const e=this.getElementHash(t);this.processedElements.set(t,e);}getElementHash(t){if(t instanceof HTMLImageElement)return t.src+"_"+t.naturalWidth+"_"+t.naturalHeight;if(t instanceof HTMLCanvasElement)try{return t.toDataURL()}catch{return "canvas_"+Date.now()}else {if(t instanceof SVGElement)return t.outerHTML;if(t instanceof HTMLElement&&t.style.backgroundImage)return t.style.backgroundImage}return ""}}class l{constructor(){this.listeners=new Map;}on(t,e){this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(e);}off(t,e){this.listeners.get(t)?.delete(e);}emit(t,e){this.listeners.get(t)?.forEach(n=>{try{n(e);}catch(i){console.error(`Event handler error [${String(t)}]:`,i);}});}clear(){this.listeners.clear();}}const h="ddddocr_model_cache",u="ddddocr_uploaded_model";class p{constructor(){this.dbName="DdddOCRDB",this.storeName="modelStore",this.db=null;}async init(){return new Promise((t,e)=>{const n=indexedDB.open(this.dbName,2);n.onerror=()=>e(n.error),n.onsuccess=()=>{this.db=n.result,t();},n.onupgradeneeded=t=>{const e=t.target.result;e.objectStoreNames.contains(this.storeName)||e.createObjectStore(this.storeName);};})}async get(){return this.db||await this.init(),new Promise((t,e)=>{const n=this.db.transaction([this.storeName],"readonly").objectStore(this.storeName).get(h);n.onerror=()=>e(n.error),n.onsuccess=()=>{const e=n.result;if(e)return Date.now()-e.timestamp>i.CACHE_DURATION||e.version!==i.MODEL_VERSION?(this.delete(),void t(null)):void t(e);t(null);};})}async set(t,e){this.db||await this.init();const n={model:t,charsets:e,timestamp:Date.now(),version:i.MODEL_VERSION};return new Promise((t,e)=>{const i=this.db.transaction([this.storeName],"readwrite").objectStore(this.storeName).put(n,h);i.onerror=()=>e(i.error),i.onsuccess=()=>t();})}async delete(){return this.db||await this.init(),new Promise((t,e)=>{const n=this.db.transaction([this.storeName],"readwrite").objectStore(this.storeName).delete(h);n.onerror=()=>e(n.error),n.onsuccess=()=>t();})}async getUploadedModel(){return this.db||await this.init(),new Promise((t,e)=>{const n=this.db.transaction([this.storeName],"readonly").objectStore(this.storeName).get(u);n.onerror=()=>e(n.error),n.onsuccess=()=>t(n.result||null);})}async setUploadedModel(t,e){this.db||await this.init();const n={model:t,charsets:e,timestamp:Date.now(),version:"uploaded"};return new Promise((t,e)=>{const i=this.db.transaction([this.storeName],"readwrite").objectStore(this.storeName).put(n,u);i.onerror=()=>e(i.error),i.onsuccess=()=>t();})}async deleteUploadedModel(){return this.db||await this.init(),new Promise((t,e)=>{const n=this.db.transaction([this.storeName],"readwrite").objectStore(this.storeName).delete(u);n.onerror=()=>e(n.error),n.onsuccess=()=>t();})}}function m(t,e=3e4){return new Promise((n,i)=>{GM_xmlhttpRequest({method:"GET",url:t,responseType:"arraybuffer",timeout:e,headers:{"Cache-Control":"max-age=2592000"},onload:t=>{200===t.status?n(t.response):i(new Error(`HTTP ${t.status}`));},onerror:t=>i(t),ontimeout:()=>i(new Error("下载超时"))});})}function g(t,e=3e4){return new Promise((n,i)=>{GM_xmlhttpRequest({method:"GET",url:t,responseType:"json",timeout:e,headers:{"Cache-Control":"max-age=2592000"},onload:t=>{200===t.status?n(t.response):i(new Error(`HTTP ${t.status}`));},onerror:t=>i(t),ontimeout:()=>i(new Error("下载超时"))});})}function b(t,e){const n=t.replace(/\/+$/,""),o=e.replace(/^\/+/,"");if(n.includes("jsdelivr"))return `${n}/${i.MODEL_REPO}@${i.MODEL_BRANCH}/${o}`;const s=`/${i.MODEL_REPO}/`;return n.includes(s)?`${n}/${o}`:`${n}/${i.MODEL_REPO}/${i.MODEL_BRANCH}/${o}`}async function f(){const t=GM_getValue("ddddocr_config")||{},e=new p;if(t.useUploadedModel){const t=await e.getUploadedModel();if(t)return console.log("✅ 使用上传的模型"),{model:t.model,charsets:t.charsets}}if(false===t.autoDownload)throw new Error("自动下载已禁用,请上传模型文件或启用自动下载");const n=await e.get();if(n)return console.log("✅ 使用缓存的模型"),{model:n.model,charsets:n.charsets};console.log("📥 开始下载模型");let o=null,s=null;for(let a=0;at.includes("jsdelivr")?`${t}/npm/onnxruntime-web@${i.WASM_VERSION}/dist/`:t.includes("unpkg")?`${t}/onnxruntime-web@${i.WASM_VERSION}/dist/`:t.includes("cdnjs")?`${t}/ajax/libs/onnxruntime-web/${i.WASM_VERSION}/`:t.includes("npmmirror")?`${t}/onnxruntime-web/${i.WASM_VERSION}/files/dist/`:`${t}/onnxruntime-web@${i.WASM_VERSION}/dist/`),v=["ort-wasm.wasm","ort-wasm-simd.wasm","ort-wasm-threaded.wasm","ort-wasm-simd-threaded.wasm"],y=new class{constructor(){this.dbName="WASMCacheDB",this.storeName="wasmStore",this.db=null,this.memoryCache=new Map;}async init(){if(!this.db)try{await this.openDatabase();}catch(t){if(!(t instanceof Error&&"VersionError"===t.name))throw t;await this.handleVersionError();}}openDatabase(){return new Promise((t,e)=>{const n=indexedDB.open(this.dbName,3);n.onerror=()=>{const t=n.error;e(t||new Error("数据库打开失败"));},n.onsuccess=()=>{this.db=n.result,this.db.onerror=t=>{console.warn("[WASMCache] 数据库错误:",t);},t();},n.onupgradeneeded=t=>{const e=t.target.result;e.objectStoreNames.contains(this.storeName)||e.createObjectStore(this.storeName);},n.onblocked=()=>{console.warn("[WASMCache] 数据库被阻塞");};})}async handleVersionError(){return console.warn("[WASMCache] 检测到数据库版本冲突,正在重建数据库..."),new Promise((t,e)=>{const n=indexedDB.deleteDatabase(this.dbName);n.onerror=()=>{e(new Error("无法删除旧数据库"));},n.onsuccess=()=>{console.log("[WASMCache] 旧数据库已删除,正在创建新数据库..."),this.openDatabase().then(t).catch(e);},n.onblocked=()=>{console.warn("[WASMCache] 删除数据库被阻塞"),setTimeout(()=>{this.openDatabase().then(t).catch(e);},100);};})}async get(t){return this.memoryCache.has(t)?this.memoryCache.get(t):(this.db||await this.init(),new Promise((e,n)=>{try{const o=this.db.transaction([this.storeName],"readonly").objectStore(this.storeName).get(t);o.onerror=()=>n(o.error),o.onsuccess=()=>{const n=o.result;if(n){if(Date.now()-n.timestamp>i.CACHE_DURATION||n.version!==i.WASM_VERSION)return this.delete(t).catch(()=>{}),void e(null);this.memoryCache.set(t,n.data),e(n.data);}else e(null);};}catch(o){n(o);}}))}async set(t,e){this.db||await this.init(),this.memoryCache.set(t,e);const n={data:e,timestamp:Date.now(),version:i.WASM_VERSION};return new Promise((e,i)=>{try{const o=this.db.transaction([this.storeName],"readwrite").objectStore(this.storeName).put(n,t);o.onerror=()=>i(o.error),o.onsuccess=()=>e();}catch(o){i(o);}})}async delete(t){return this.memoryCache.delete(t),this.db||await this.init(),new Promise((e,n)=>{try{const i=this.db.transaction([this.storeName],"readwrite").objectStore(this.storeName).delete(t);i.onerror=()=>n(i.error),i.onsuccess=()=>e();}catch(i){n(i);}})}async clear(){return this.memoryCache.clear(),this.db||await this.init(),new Promise((t,e)=>{try{const n=this.db.transaction([this.storeName],"readwrite").objectStore(this.storeName).clear();n.onerror=()=>e(n.error),n.onsuccess=()=>t();}catch(n){e(n);}})}};async function x(t){for(let n=0;n{GM_xmlhttpRequest({method:"GET",url:i,responseType:"arraybuffer",timeout:3e4,headers:{Accept:"application/wasm","Cache-Control":"max-age=2592000"},onload:n=>{200===n.status?t(n.response):e(new Error(`HTTP ${n.status}`));},onerror:e,ontimeout:()=>e(new Error("下载超时"))});})}catch(e){if(n===w.length-1)throw new Error(`所有 WASM CDN 均下载失败: ${t}`)}}throw new Error(`下载 WASM 失败: ${t}`)}const E=class{static isMobile(){return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)||window.innerWidth<=768}static injectStyles(){if(this.stylesInjected)return;const t=document.createElement("style");t.textContent="\n .ddddocr-dialog-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n z-index: 2147483647;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: ddddocr-fade-in 0.3s ease;\n padding: 16px;\n box-sizing: border-box;\n }\n\n .ddddocr-dialog {\n background: white;\n border-radius: 16px;\n box-shadow: 0 20px 60px rgba(74, 144, 226, 0.25);\n max-width: 500px;\n width: 100%;\n max-height: 80vh;\n overflow: hidden;\n animation: ddddocr-scale-in 0.3s ease;\n display: flex;\n flex-direction: column;\n }\n\n .ddddocr-dialog.mobile {\n max-width: 100%;\n border-radius: 12px;\n }\n\n .ddddocr-dialog-header {\n background: #4A90E2;\n color: white;\n padding: 20px 24px;\n font-size: 18px;\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: 10px;\n flex-shrink: 0;\n }\n\n .ddddocr-dialog.mobile .ddddocr-dialog-header {\n padding: 16px 20px;\n font-size: 16px;\n }\n\n .ddddocr-dialog-body {\n padding: 24px;\n max-height: calc(80vh - 140px);\n overflow-y: auto;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n flex: 1;\n -webkit-overflow-scrolling: touch;\n }\n\n .ddddocr-dialog.mobile .ddddocr-dialog-body {\n padding: 20px;\n }\n\n .ddddocr-dialog-body::-webkit-scrollbar {\n width: 6px;\n }\n\n .ddddocr-dialog-body::-webkit-scrollbar-track {\n background: #f1f5f9;\n border-radius: 3px;\n }\n\n .ddddocr-dialog-body::-webkit-scrollbar-thumb {\n background: #FFB6C1;\n border-radius: 3px;\n }\n\n .ddddocr-dialog-body::-webkit-scrollbar-thumb:hover {\n background: #FF69B4;\n }\n\n .ddddocr-dialog-content {\n color: #333;\n font-size: 14px;\n line-height: 1.6;\n white-space: pre-wrap;\n }\n\n .ddddocr-dialog.mobile .ddddocr-dialog-content {\n font-size: 15px;\n }\n\n .ddddocr-dialog-footer {\n padding: 16px 24px;\n border-top: 1px solid #f1f5f9;\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n flex-shrink: 0;\n }\n\n .ddddocr-dialog.mobile .ddddocr-dialog-footer {\n padding: 16px 20px;\n flex-direction: column-reverse;\n }\n\n .ddddocr-dialog-button {\n padding: 10px 24px;\n border: none;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.3s;\n -webkit-tap-highlight-color: transparent;\n }\n\n .ddddocr-dialog.mobile .ddddocr-dialog-button {\n padding: 14px 24px;\n font-size: 15px;\n width: 100%;\n }\n\n .ddddocr-dialog-button.primary {\n background: #4A90E2;\n color: white;\n }\n\n .ddddocr-dialog-button.primary:hover {\n transform: translateY(-2px);\n box-shadow: 0 6px 20px rgba(74, 144, 226, 0.35);\n }\n\n .ddddocr-dialog-button.primary:active {\n transform: translateY(0);\n }\n\n .ddddocr-dialog-button.secondary {\n background: #f1f5f9;\n color: #4A90E2;\n }\n\n .ddddocr-dialog-button.secondary:hover {\n background: #e2e8f0;\n }\n\n .ddddocr-dialog-button.secondary:active {\n background: #cbd5e1;\n }\n\n @keyframes ddddocr-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n\n @keyframes ddddocr-scale-in {\n from { opacity: 0; transform: scale(0.9); }\n to { opacity: 1; transform: scale(1); }\n }\n ",document.head.appendChild(t),this.stylesInjected=true;}static stopPropagation(t){t.stopPropagation();}static show(t){this.injectStyles(),this.close();const e=document.createElement("div");e.className="ddddocr-dialog-overlay";const n=document.createElement("div");n.className="ddddocr-dialog",this.isMobile()&&n.classList.add("mobile"),n.innerHTML=`\n
${t.icon||"ℹ️"} ${t.title}
\n
\n
${t.content}
\n
\n \n `,e.appendChild(n),document.body.appendChild(e),this.container=e,n.addEventListener("mousedown",this.stopPropagation),n.addEventListener("mouseup",this.stopPropagation),n.addEventListener("click",this.stopPropagation),n.addEventListener("dblclick",this.stopPropagation),n.addEventListener("wheel",this.stopPropagation),n.addEventListener("keydown",this.stopPropagation),n.addEventListener("keyup",this.stopPropagation),n.addEventListener("keypress",this.stopPropagation),n.addEventListener("contextmenu",this.stopPropagation),n.addEventListener("touchstart",this.stopPropagation,{passive:true}),n.addEventListener("touchmove",this.stopPropagation,{passive:true}),n.addEventListener("touchend",this.stopPropagation),n.querySelector(".ddddocr-dialog-button")?.addEventListener("click",()=>{t.onConfirm?.(),this.close();}),e.addEventListener("click",t=>{t.target===e&&this.close();});}static confirm(t){this.injectStyles(),this.close();const e=document.createElement("div");e.className="ddddocr-dialog-overlay";const n=document.createElement("div");n.className="ddddocr-dialog",this.isMobile()&&n.classList.add("mobile"),n.innerHTML=`\n
${t.icon||"❓"} ${t.title}
\n
\n
${t.content}
\n
\n \n `,e.appendChild(n),document.body.appendChild(e),this.container=e,n.addEventListener("mousedown",this.stopPropagation),n.addEventListener("mouseup",this.stopPropagation),n.addEventListener("click",this.stopPropagation),n.addEventListener("dblclick",this.stopPropagation),n.addEventListener("wheel",this.stopPropagation),n.addEventListener("keydown",this.stopPropagation),n.addEventListener("keyup",this.stopPropagation),n.addEventListener("keypress",this.stopPropagation),n.addEventListener("contextmenu",this.stopPropagation),n.addEventListener("touchstart",this.stopPropagation,{passive:true}),n.addEventListener("touchmove",this.stopPropagation,{passive:true}),n.addEventListener("touchend",this.stopPropagation),n.querySelector(".confirm-btn")?.addEventListener("click",()=>{t.onConfirm?.(),this.close();}),n.querySelector(".cancel-btn")?.addEventListener("click",()=>{t.onCancel?.(),this.close();}),e.addEventListener("click",n=>{n.target===e&&(t.onCancel?.(),this.close());});}static close(){this.container?.remove(),this.container=null;}};E.container=null,E.stylesInjected=false;let C=E;const S="ddddocr_config_version",k="ddddocr_stats",I="ddddocr_site_rules",A=function(t,e,n){const i=()=>t(S)||1,s=t=>e(S,t);return {getConfig:()=>function(t,e,n){if(!t)return n(2),{...o};const i=e();let s={...o};for(const r of Object.keys(t)){const e=t[r];void 0!==e&&r in o&&(s[r]=e);}if(i<2&&t.agreementSelector&&(!s.agreementSelectors||0===s.agreementSelectors.length)){const e=t.agreementSelector.trim();e&&(s.agreementSelectors=[e]);}for(const r of Object.keys(o)) void 0===s[r]&&(s[r]=o[r]);return 2!==i&&n(2),s}(t(n),i,s),saveConfig(t){const i={...this.getConfig(),...t};e(n,i);},resetConfig(){e(n,o),s(2);}}}(t=>GM_getValue(t),(t,e)=>GM_setValue(t,e),"ddddocr_config");function R(){return A.getConfig()}function L(t){A.saveConfig(t);}function M(){return GM_getValue(I)||{}}function T(t,e){const n=M(),i=e.fullUrl||e.urlPattern||t,o=n[i]||{};n[i]={...o,...e,hostname:t,createdAt:o.createdAt||Date.now(),updatedAt:Date.now(),enabled:false!==e.enabled},GM_setValue(I,n);}function D(t){const e=M();delete e[t],GM_setValue(I,e);}const N=new class{constructor(t){this.dirty=false,this.saveTimer=null,this.storage=t,this.data=this.load();}load(){const t=this.storage.get(k);return t&&"object"==typeof t&&t.sites?t:{sites:{},total:0,updated:Date.now()}}scheduleSave(){this.saveTimer||(this.saveTimer=window.setTimeout(()=>{this.flush(),this.saveTimer=null;},5e3));}flush(){this.dirty&&(this.data.updated=Date.now(),this.storage.set(k,this.data),this.dirty=false);}record(t,e){this.data.sites[t]||(Object.keys(this.data.sites).length>=100&&this.pruneOldest(),this.data.sites[t]={count:0,lastTime:0,totalTime:0});const n=this.data.sites[t];n.count++,n.lastTime=Date.now(),n.totalTime+=e,this.data.total++,this.dirty=true,this.scheduleSave();}pruneOldest(){const t=Object.entries(this.data.sites);t.sort((t,e)=>t[1].lastTime-e[1].lastTime);const e=t.slice(0,10);for(const[n]of e)delete this.data.sites[n];}getStats(){return {...this.data}}getSiteStats(t){return this.data.sites[t]||null}getTopSites(t=10){return Object.entries(this.data.sites).map(([t,e])=>({hostname:t,stats:e})).sort((t,e)=>e.stats.count-t.stats.count).slice(0,t)}getAverageTime(t){if(t){const e=this.data.sites[t];return e&&e.count>0?Math.round(e.totalTime/e.count):0}if(0===this.data.total)return 0;const e=Object.values(this.data.sites).reduce((t,e)=>t+e.totalTime,0);return Math.round(e/this.data.total)}clear(){this.data={sites:{},total:0,updated:Date.now()},this.dirty=true,this.flush();}}({get:t=>GM_getValue(t),set:(t,e)=>GM_setValue(t,e)});function _(){const t=R();if(!t.enableWhitelist)return true;if(!t.whitelist||0===t.whitelist.length)return false;const e=window.location.hostname;return t.whitelist.some(t=>new RegExp("^"+t.replace(/\*/g,".*")+"$","i").test(e))}function P(){const t=R();if(t.enableWhitelist){if(!t.whitelist||0===t.whitelist.length)return false;if(!_())return false}return true}function $(){return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)||window.innerWidth<=768}function H(t){const e=document.createElement("div");return e.textContent=t,e.innerHTML}function O(t){return t?new Date(t).toLocaleDateString():"-"}class z{constructor(){this.container=null,this.overlay=null,this.isVisible=false,this.onConfigChange=()=>{},this.activeTab="general",this.currentEditRuleKey=null,this.createStyles(),this.handleResize=this.handleResize.bind(this);}handleResize(){this.container&&this.container.classList.toggle("mobile",$());}createStyles(){const t=document.createElement("style");t.id="ddddocr-settings-styles",t.textContent="\n.ddddocr-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n z-index: 2147483646;\n display: none;\n animation: ddddocr-fade 0.2s ease;\n -webkit-overflow-scrolling: touch;\n}\n.ddddocr-overlay.visible { display: block; }\n\n.ddddocr-modal {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 640px;\n max-width: 95vw;\n max-height: 85vh;\n background: #4A90E2;\n border-radius: 20px;\n z-index: 2147483647;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n overflow: hidden;\n display: none;\n box-shadow: 0 25px 80px rgba(74, 144, 226, 0.35);\n}\n.ddddocr-modal.visible {\n display: block;\n animation: ddddocr-scale 0.3s ease;\n}\n.ddddocr-modal.mobile {\n width: 100%;\n max-width: 100%;\n height: 100%;\n max-height: 100%;\n top: 0;\n left: 0;\n transform: none;\n border-radius: 0;\n}\n\n@keyframes ddddocr-fade { from { opacity: 0; } to { opacity: 1; } }\n@keyframes ddddocr-scale {\n from { opacity: 0; transform: translate(-50%, -50%) scale(0.9); }\n to { opacity: 1; transform: translate(-50%, -50%) scale(1); }\n}\n.ddddocr-modal.mobile.visible {\n animation: ddddocr-slide-up 0.3s ease;\n}\n@keyframes ddddocr-slide-up {\n from { opacity: 0; transform: translateY(100%); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.ddddocr-header {\n background: rgba(255, 255, 255, 0.15);\n padding: 20px 24px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n border-bottom: 1px solid rgba(255, 255, 255, 0.15);\n}\n.ddddocr-modal.mobile .ddddocr-header {\n padding: 16px;\n position: sticky;\n top: 0;\n z-index: 10;\n}\n\n.ddddocr-title {\n color: white;\n font-size: 20px;\n font-weight: 700;\n display: flex;\n align-items: center;\n gap: 10px;\n text-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n.ddddocr-modal.mobile .ddddocr-title { font-size: 18px; }\n\n.ddddocr-close {\n width: 36px;\n height: 36px;\n min-width: 36px;\n min-height: 36px;\n border-radius: 50%;\n background: rgba(255, 255, 255, 0.25);\n border: none;\n color: white;\n font-size: 24px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s;\n -webkit-tap-highlight-color: transparent;\n}\n.ddddocr-close:hover { background: rgb(242, 0, 105); }\n.ddddocr-close:active { background: rgba(255, 255, 255, 0.5); }\n.ddddocr-modal.mobile .ddddocr-close { width: 44px; height: 44px; min-width: 44px; min-height: 44px; }\n\n.ddddocr-tabs {\n display: flex;\n background: rgba(255, 255, 255, 0.1);\n padding: 0 16px;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: none;\n -ms-overflow-style: none;\n}\n.ddddocr-tabs::-webkit-scrollbar { display: none; }\n.ddddocr-modal.mobile .ddddocr-tabs { padding: 0 8px; position: sticky; top: 76px; z-index: 10; }\n\n.ddddocr-tab {\n padding: 14px 16px;\n color: rgba(255, 255, 255, 0.8);\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n border: none;\n background: none;\n position: relative;\n transition: all 0.2s;\n white-space: nowrap;\n -webkit-tap-highlight-color: transparent;\n}\n.ddddocr-modal.mobile .ddddocr-tab { padding: 12px 14px; font-size: 14px; min-height: 48px; }\n.ddddocr-tab:hover { color: white; }\n.ddddocr-tab.active { color: white; }\n.ddddocr-tab.active::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 50%;\n transform: translateX(-50%);\n width: 30px;\n height: 3px;\n background: white;\n border-radius: 3px 3px 0 0;\n}\n\n.ddddocr-content {\n background: #f8fafc;\n padding: 24px;\n max-height: calc(85vh - 180px);\n overflow-y: auto;\n overflow-x: hidden;\n -webkit-overflow-scrolling: touch;\n}\n.ddddocr-modal.mobile .ddddocr-content {\n padding: 16px;\n max-height: none;\n height: calc(100% - 140px);\n padding-bottom: 100px;\n}\n.ddddocr-content::-webkit-scrollbar { width: 6px; }\n.ddddocr-content::-webkit-scrollbar-track { background: #f1f5f9; border-radius: 3px; }\n.ddddocr-content::-webkit-scrollbar-thumb { background: #FFB6C1; border-radius: 3px; }\n.ddddocr-content::-webkit-scrollbar-thumb:hover { background: #FF69B4; }\n\n.ddddocr-panel { display: none; }\n.ddddocr-panel.active { display: block; }\n\n.ddddocr-card {\n background: white;\n border-radius: 16px;\n padding: 20px;\n margin-bottom: 16px;\n box-shadow: 0 2px 12px rgba(74, 144, 226, 0.08);\n border: 1px solid rgba(74, 144, 226, 0.06);\n}\n.ddddocr-modal.mobile .ddddocr-card { padding: 16px; border-radius: 12px; }\n\n.ddddocr-card-title {\n font-size: 15px;\n font-weight: 600;\n color: #1e293b;\n margin-bottom: 16px;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.ddddocr-card-title::before {\n content: '';\n width: 4px;\n height: 16px;\n background: #4A90E2;\n border-radius: 2px;\n}\n\n.ddddocr-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 0;\n border-bottom: 1px solid #f1f5f9;\n gap: 12px;\n}\n.ddddocr-modal.mobile .ddddocr-row { padding: 16px 0; }\n.ddddocr-row:last-child { border-bottom: none; }\n\n.ddddocr-row-info { flex: 1; min-width: 0; }\n.ddddocr-row-label { font-size: 14px; color: #334155; font-weight: 500; }\n.ddddocr-modal.mobile .ddddocr-row-label { font-size: 15px; }\n.ddddocr-row-desc { font-size: 12px; color: #94a3b8; margin-top: 2px; }\n.ddddocr-modal.mobile .ddddocr-row-desc { font-size: 13px; }\n\n.ddddocr-switch {\n position: relative;\n width: 48px;\n height: 26px;\n min-width: 48px;\n background: #e2e8f0;\n border-radius: 13px;\n cursor: pointer;\n transition: background 0.3s;\n -webkit-tap-highlight-color: transparent;\n}\n.ddddocr-modal.mobile .ddddocr-switch { width: 56px; height: 32px; min-width: 56px; }\n.ddddocr-switch.on { background: #4A90E2; }\n.ddddocr-switch-knob {\n position: absolute;\n top: 3px;\n left: 3px;\n width: 20px;\n height: 20px;\n background: white;\n border-radius: 50%;\n transition: transform 0.3s;\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);\n}\n.ddddocr-modal.mobile .ddddocr-switch-knob { width: 26px; height: 26px; }\n.ddddocr-switch.on .ddddocr-switch-knob { transform: translateX(22px); }\n.ddddocr-modal.mobile .ddddocr-switch.on .ddddocr-switch-knob { transform: translateX(24px); }\n\n.ddddocr-input {\n width: 100%;\n padding: 12px 14px;\n border: 2px solid #e2e8f0;\n border-radius: 10px;\n font-size: 14px;\n transition: all 0.2s;\n margin-top: 8px;\n box-sizing: border-box;\n}\n.ddddocr-modal.mobile .ddddocr-input { padding: 14px 16px; font-size: 16px; border-radius: 12px; }\n.ddddocr-input:focus { outline: none; border-color: #4A90E2; box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.15); }\n\n.ddddocr-select {\n width: 100%;\n padding: 12px 14px;\n border: 2px solid #e2e8f0;\n border-radius: 10px;\n font-size: 14px;\n transition: all 0.2s;\n margin-top: 8px;\n box-sizing: border-box;\n background: white;\n cursor: pointer;\n}\n.ddddocr-modal.mobile .ddddocr-select { padding: 14px 16px; font-size: 16px; border-radius: 12px; }\n.ddddocr-select:focus { outline: none; border-color: #4A90E2; }\n\n.ddddocr-textarea {\n width: 100%;\n padding: 12px 14px;\n border: 2px solid #e2e8f0;\n border-radius: 10px;\n font-size: 13px;\n font-family: 'Monaco', 'Consolas', monospace;\n min-height: 100px;\n resize: vertical;\n transition: all 0.2s;\n margin-top: 8px;\n box-sizing: border-box;\n}\n.ddddocr-modal.mobile .ddddocr-textarea { padding: 14px 16px; font-size: 14px; min-height: 120px; border-radius: 12px; }\n.ddddocr-textarea:focus { outline: none; border-color: #4A90E2; box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.15); }\n\n.ddddocr-file-zone {\n border: 2px dashed #d1e3f6;\n border-radius: 12px;\n padding: 24px;\n text-align: center;\n cursor: pointer;\n transition: all 0.2s;\n margin-top: 12px;\n background: rgba(74, 144, 226, 0.02);\n -webkit-tap-highlight-color: transparent;\n}\n.ddddocr-file-zone:hover { border-color: #4A90E2; background: rgba(74, 144, 226, 0.05); }\n.ddddocr-file-zone:active { background: rgba(74, 144, 226, 0.1); }\n.ddddocr-file-zone input { display: none; }\n.ddddocr-file-icon { font-size: 32px; margin-bottom: 8px; color: #4A90E2; }\n.ddddocr-file-text { font-size: 13px; color: #64748b; }\n.ddddocr-file-name { font-size: 12px; color: #4A90E2; margin-top: 8px; font-weight: 500; }\n\n.ddddocr-btn-group { display: flex; gap: 12px; margin-top: 16px; flex-wrap: wrap; }\n.ddddocr-btn {\n flex: 1;\n min-width: 100px;\n padding: 12px 20px;\n border: none;\n border-radius: 10px;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s;\n -webkit-tap-highlight-color: transparent;\n}\n.ddddocr-modal.mobile .ddddocr-btn { padding: 14px 20px; font-size: 15px; min-height: 48px; }\n.ddddocr-btn-primary { background: #4A90E2; color: white; }\n.ddddocr-btn-primary:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(74, 144, 226, 0.35); }\n.ddddocr-btn-primary:active { transform: translateY(0); }\n.ddddocr-btn-secondary { background: #f1f5f9; color: #475569; }\n.ddddocr-btn-secondary:hover { background: #e2e8f0; }\n.ddddocr-btn-secondary:active { background: #cbd5e1; }\n.ddddocr-btn-danger { background: #fee2e2; color: #dc2626; }\n.ddddocr-btn-danger:hover { background: #fecaca; }\n.ddddocr-btn-danger:active { background: #fca5a5; }\n.ddddocr-btn-sm { padding: 8px 14px; font-size: 12px; flex: none;}\n.ddddocr-modal.mobile .ddddocr-btn-sm { padding: 10px 16px; font-size: 13px; min-height: 40px; }\n\n.ddddocr-hint {\n background: rgba(74, 144, 226, 0.08);\n border: 1px solid rgba(74, 144, 226, 0.15);\n border-radius: 10px;\n padding: 12px 14px;\n font-size: 12px;\n color: #64748b;\n margin-top: 12px;\n}\n.ddddocr-modal.mobile .ddddocr-hint { font-size: 13px; padding: 14px 16px; }\n\n.ddddocr-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: rgba(16, 185, 129, 0.15);\n color: #10b981;\n border-radius: 20px;\n font-size: 12px;\n font-weight: 500;\n margin-bottom: 12px;\n border: 1px solid rgba(16, 185, 129, 0.3);\n}\n\n.ddddocr-rule-item {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px;\n background: #f8fafc;\n border-radius: 10px;\n margin-bottom: 8px;\n border: 1px solid rgba(74, 144, 226, 0.08);\n}\n.ddddocr-modal.mobile .ddddocr-rule-item { flex-wrap: wrap; padding: 14px; }\n.ddddocr-rule-item:last-child { margin-bottom: 0; }\n.ddddocr-rule-pattern { flex: 1; font-family: monospace; font-size: 13px; color: #334155; word-break: break-all; min-width: 0; }\n.ddddocr-rule-type { font-size: 11px; padding: 4px 8px; background: #e2e8f0; border-radius: 4px; color: #64748b; white-space: nowrap; }\n.ddddocr-rule-output { font-size: 11px; padding: 4px 8px; background: #dbeafe; border-radius: 4px; color: #4A90E2; white-space: nowrap; }\n.ddddocr-rule-delete {\n background: none;\n border: none;\n color: #ef4444;\n cursor: pointer;\n font-size: 16px;\n padding: 8px;\n min-width: 32px;\n min-height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n -webkit-tap-highlight-color: transparent;\n}\n.ddddocr-rule-edit {\n background: none;\n border: none;\n color: #4A90E2;\n cursor: pointer;\n font-size: 12px;\n padding: 8px;\n min-width: 32px;\n min-height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n -webkit-tap-highlight-color: transparent;\n}\n\n.ddddocr-empty { text-align: center; padding: 24px; color: #94a3b8; font-size: 13px; }\n\n.ddddocr-add-rule { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }\n.ddddocr-add-rule input { flex: 2; min-width: 120px; }\n.ddddocr-add-rule select { flex: 1; min-width: 80px; }\n.ddddocr-modal.mobile .ddddocr-add-rule { flex-direction: column; }\n.ddddocr-modal.mobile .ddddocr-add-rule input,\n.ddddocr-modal.mobile .ddddocr-add-rule select { width: 100%; flex: none; }\n\n.ddddocr-save-float { display: none; }\n.ddddocr-modal.mobile .ddddocr-save-float {\n display: block;\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n padding: 16px;\n background: white;\n box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1);\n z-index: 100;\n}\n.ddddocr-modal.mobile .ddddocr-save-float .ddddocr-btn { width: 100%; }\n.ddddocr-modal.mobile .ddddocr-tabs .ddddocr-btn { display: none; }\n\n/* 统计样式 */\n.ddddocr-stats-grid {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 12px;\n margin-bottom: 16px;\n}\n.ddddocr-modal.mobile .ddddocr-stats-grid { grid-template-columns: 1fr; }\n\n.ddddocr-stat-card {\n background: linear-gradient(135deg, #4A90E2 0%, #357ABD 100%);\n border-radius: 12px;\n padding: 16px;\n color: white;\n position: relative;\n overflow: hidden;\n}\n.ddddocr-stat-card::before {\n content: '';\n position: absolute;\n top: -50%;\n right: -50%;\n width: 100%;\n height: 100%;\n background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);\n pointer-events: none;\n}\n.ddddocr-stat-card.accent { background: linear-gradient(135deg, #FF69B4 0%, #FF1493 100%); }\n.ddddocr-stat-card.success { background: linear-gradient(135deg, #10b981 0%, #059669 100%); }\n.ddddocr-stat-card.warning { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); }\n\n.ddddocr-stat-label { font-size: 12px; opacity: 0.9; margin-bottom: 6px; }\n.ddddocr-stat-value { font-size: 28px; font-weight: 700; line-height: 1; }\n.ddddocr-stat-unit { font-size: 14px; font-weight: 400; opacity: 0.8; margin-left: 4px; }\n\n.ddddocr-rank-list { max-height: 400px; overflow-y: auto; }\n.ddddocr-rank-item {\n display: flex;\n align-items: center;\n padding: 14px 12px;\n background: #f8fafc;\n border-radius: 10px;\n margin-bottom: 8px;\n border: 1px solid rgba(74, 144, 226, 0.08);\n transition: all 0.2s;\n}\n.ddddocr-rank-item:hover { background: #f1f5f9; }\n.ddddocr-rank-item:last-child { margin-bottom: 0; }\n\n.ddddocr-rank-num {\n width: 28px;\n height: 28px;\n border-radius: 50%;\n background: #e2e8f0;\n color: #64748b;\n font-size: 12px;\n font-weight: 600;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-right: 12px;\n flex-shrink: 0;\n}\n.ddddocr-rank-item:nth-child(1) .ddddocr-rank-num { background: linear-gradient(135deg, #fbbf24, #f59e0b); color: white; }\n.ddddocr-rank-item:nth-child(2) .ddddocr-rank-num { background: linear-gradient(135deg, #94a3b8, #64748b); color: white; }\n.ddddocr-rank-item:nth-child(3) .ddddocr-rank-num { background: linear-gradient(135deg, #cd7f32, #b8860b); color: white; }\n\n.ddddocr-rank-info { flex: 1; min-width: 0; }\n.ddddocr-rank-host { font-size: 14px; font-weight: 500; color: #334155; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.ddddocr-rank-meta { font-size: 12px; color: #94a3b8; margin-top: 4px; display: flex; gap: 12px; }\n.ddddocr-rank-count { font-size: 18px; font-weight: 700; color: #4A90E2; margin-left: 12px; flex-shrink: 0; }\n\n.ddddocr-progress-bar { height: 6px; background: #e2e8f0; border-radius: 3px; margin-top: 8px; overflow: hidden; }\n.ddddocr-progress-fill { height: 100%; background: linear-gradient(90deg, #4A90E2, #FF69B4); border-radius: 3px; transition: width 0.3s ease; }\n\n/* 站点规则样式 */\n.ddddocr-site-rule-item {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: 14px;\n background: #f8fafc;\n border-radius: 10px;\n margin-bottom: 10px;\n border: 1px solid rgba(74, 144, 226, 0.08);\n}\n.ddddocr-site-rule-item:last-child { margin-bottom: 0; }\n.ddddocr-site-rule-info { flex: 1; min-width: 0; }\n.ddddocr-site-rule-key { font-size: 14px; font-weight: 500; color: #334155; word-break: break-all; margin-bottom: 4px; }\n.ddddocr-site-rule-selector { font-size: 12px; color: #64748b; font-family: monospace; word-break: break-all; }\n.ddddocr-site-rule-badge { font-size: 10px; padding: 2px 6px; background: #dbeafe; color: #4A90E2; border-radius: 4px; margin-top: 6px; display: inline-block; }\n.ddddocr-site-rule-actions { display: flex; gap: 4px; flex-shrink: 0; }\n\n.ddddocr-card-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 16px;\n}\n.ddddocr-card-header .ddddocr-card-title { margin-bottom: 0; }\n",document.getElementById("ddddocr-settings-styles")||document.head.appendChild(t);}async createContainer(){this.overlay=document.createElement("div"),this.overlay.className="ddddocr-overlay",this.container=document.createElement("div"),this.container.className="ddddocr-modal",$()&&this.container.classList.add("mobile");const t=R();let e=false,n=0;try{const t=new p,i=await t.getUploadedModel();i&&(e=!0,n=i.model.byteLength);}catch(a){console.warn("Failed to load uploaded model info:",a);}const i=this.renderCalculateRules(t.calculateRules||[]),o=this.renderSiteRules(),s=this.renderStats(),r=this.renderAgreementSelectors(t.agreementSelectors||[]);this.container.innerHTML=`\n
\n
DDDD OCR 设置
\n \n
\n
\n \n \n \n \n \n \n \n
\n
\n \x3c!-- 基本设置 --\x3e\n
\n
\n
检测与填充
\n ${this.renderSwitchRow("autoDetect","自动检测并填充","自动识别页面验证码并填充结果",t.autoDetect)}\n ${this.renderSwitchRow("typewriterEffect","打字机效果","模拟人工逐字输入,关闭则一次性填充",t.typewriterEffect)}\n ${this.renderSwitchRow("autoCheckAgreement","自动勾选协议","自动勾选用户协议、隐私政策等复选框",t.autoCheckAgreement)}\n ${this.renderSwitchRow("enableNotification","系统通知","识别完成后显示桌面通知提醒",t.enableNotification)}\n ${this.renderSwitchRow("debugMode","调试模式","在控制台输出详细日志",t.debugMode)}\n
\n
\n
自定义选择器
\n
验证码选择器
\n \n
输入框选择器
\n \n
留空则自动检测页面中的验证码元素
\n
协议复选框选择器
\n
${r}
\n
\n \n \n
\n
支持添加多个协议复选框选择器
\n
\n
\n\n
\n
自定义检测规则
\n
自定义触发关键词
\n \n
内置关键词: captcha, verify, code, vcode, authcode, 验证码, checkcode, yzm 等
\n
自定义排除关键词
\n \n
内置排除词: avatar, logo, icon, banner, ad, sponsor, background, emoji, loading 等
\n
\n\n \x3c!-- 网站规则 --\x3e\n
\n
\n
\n
已保存的规则
\n
\n \n \n
\n
\n
${o}
\n
\n \n
\n
添加规则
\n
主机名/URL
\n \n
验证码选择器
\n \n
输入框选择器
\n \n \n
\n
\n\n \x3c!-- 识别统计 --\x3e\n
\n ${s}\n
\n\n \x3c!-- 四则运算 --\x3e\n
\n
\n
四则运算识别
\n ${this.renderSwitchRow("autoCalculate","自动计算结果",'识别到 "3+5=?" 自动计算并填充',t.autoCalculate)}\n
\n
默认输出格式
\n \n
可为不同站点配置不同的输出格式
\n
\n
\n
\n
站点规则
\n
${i}
\n
\n \n \n \n \n
\n
\n 通配符: * 匹配任意字符,? 匹配单个字符
\n 正则: 使用标准正则表达式语法\n
\n
\n
\n\n \x3c!-- 模型管理 --\x3e\n
\n
\n
模型来源
\n ${e?`
[已上传] 本地模型 (${(n/1024/1024).toFixed(1)} MB)
`:""}\n ${this.renderSwitchRow("useUploadedModel","使用上传的模型","优先使用本地上传的模型文件",t.useUploadedModel)}\n ${this.renderSwitchRow("autoDownload","自动下载模型","首次使用时自动从网络下载模型",t.autoDownload)}\n
\n
\n
上传模型文件
\n
\n \n
[ONNX]
\n
点击上传 common.onnx
\n
\n
\n
\n \n
[JSON]
\n
点击上传 charsets.json
\n
\n
\n
\n \n \n
\n
\n
\n\n \x3c!-- 站点白名单 --\x3e\n
\n
\n
白名单设置
\n ${this.renderSwitchRow("enableWhitelist","启用站点白名单","仅在白名单站点启用脚本功能",t.enableWhitelist)}\n
\n \n
当前站点: ${window.location.hostname}
\n
\n
\n
\n
配置导入导出
\n
\n \n \n \n
\n
\n
\n
\n
\n \n
\n `,document.body.appendChild(this.overlay),document.body.appendChild(this.container),this.bindEvents(),window.addEventListener("resize",this.handleResize);}renderSwitchRow(t,e,n,i){return `\n
\n
\n
${e}
\n
${n}
\n
\n
\n
\n
\n
\n `}renderCalculateRules(t){return t&&0!==t.length?t.map((t,e)=>`\n
\n ${H(t.pattern)}\n ${"regex"===t.matchType?"正则":"通配符"}\n ${"result"===t.outputMode?"仅结果":"完整等式"}\n \n
\n `).join(""):'
暂无规则,将使用默认输出格式
'}renderSiteRules(){const t=M(),e=Object.keys(t);return 0===e.length?'
暂无保存的规则
':e.map(e=>{const n=t[e],i=e.length>35?e.substring(0,35)+"...":e,o=n.selector.length>40?n.selector.substring(0,40)+"...":n.selector;return `\n
\n
\n
${H(i)}
\n
${H(o)}
\n ${n.fullUrl?'
完整URL匹配
':""}\n
\n
\n \n \n
\n
\n `}).join("")}renderStats(){const t=N.getStats(),e=Object.entries(t.sites||{}),n=e.reduce((t,[,e])=>t+e.totalTime,0),i=t.total>0?Math.round(n/t.total):0,o=t.updated?O(t.updated):"-";let s="";if(0===e.length)s='
暂无统计数据
';else {e.sort((t,e)=>e[1].count-t[1].count);const t=e.slice(0,15),n=t[0]?.[1].count||1;s=t.map(([t,e],i)=>{const o=e.count>0?Math.round(e.totalTime/e.count):0,s=O(e.lastTime),r=Math.round(e.count/n*100);return `\n
\n
${i+1}
\n
\n
${H(t)}
\n
\n 平均 ${o}ms\n 最后: ${s}\n
\n
\n
\n
\n
\n
${e.count}
\n
\n `}).join("");}return `\n
\n
\n
总识别次数
\n
${t.total}
\n
\n
\n
统计站点数
\n
${e.length}
\n
\n
\n
平均识别耗时
\n
${i}ms
\n
\n
\n
最后更新
\n
${o}
\n
\n
\n
\n
\n
站点排行榜
\n \n
\n
${s}
\n
\n `}renderAgreementSelectors(t){return t&&0!==t.length?t.map((t,e)=>`\n
\n ${H(t)}\n \n
\n `).join(""):'
暂无协议选择器
'}stopPropagation(t){t.stopPropagation();}bindEvents(){this.container&&this.overlay&&(["mousedown","mouseup","click","dblclick","wheel","keydown","keyup","keypress","contextmenu"].forEach(t=>this.container.addEventListener(t,this.stopPropagation)),this.container.addEventListener("touchstart",this.stopPropagation,{passive:true}),this.container.addEventListener("touchmove",this.stopPropagation,{passive:true}),this.container.addEventListener("touchend",this.stopPropagation),this.container.querySelector(".ddddocr-close")?.addEventListener("click",()=>this.hide()),this.overlay.addEventListener("click",()=>this.hide()),this.container.querySelectorAll(".ddddocr-tab").forEach(t=>{t.addEventListener("click",()=>{const e=t.dataset.tab;e&&this.switchTab(e);});}),this.container.querySelectorAll(".ddddocr-switch").forEach(t=>{t.addEventListener("click",()=>{t.classList.toggle("on");const e=t.dataset.key;if("enableWhitelist"===e){const e=this.container.querySelector("#whitelistArea");e&&(e.style.display=t.classList.contains("on")?"block":"none");}if("autoCalculate"===e){const e=this.container.querySelector("#calculateOptionsArea"),n=this.container.querySelector("#calculateRulesCard"),i=t.classList.contains("on");e&&(e.style.display=i?"block":"none"),n&&(n.style.display=i?"block":"none");}});}),this.container.querySelector("#addAgreementSelectorBtn")?.addEventListener("click",()=>this.addAgreementSelector()),this.bindAgreementSelectorDeleteEvents(),this.container.querySelector("#addCalcRuleBtn")?.addEventListener("click",()=>this.addCalculateRule()),this.bindCalcRuleDeleteEvents(),this.container.querySelector("#addSiteRuleBtn")?.addEventListener("click",()=>this.addSiteRule()),this.container.querySelector("#saveEditRuleBtn")?.addEventListener("click",()=>this.saveEditRule()),this.container.querySelector("#cancelEditRuleBtn")?.addEventListener("click",()=>this.cancelEditRule()),this.container.querySelector("#exportRulesBtn")?.addEventListener("click",()=>this.exportSiteRules()),this.container.querySelector("#importRulesBtn")?.addEventListener("click",()=>this.importSiteRules()),this.bindSiteRuleEvents(),this.container.querySelector("#clearStatsBtn")?.addEventListener("click",()=>this.clearStats()),this.bindModelEvents(),this.container.querySelector("#exportBtn")?.addEventListener("click",()=>this.exportConfig()),this.container.querySelector("#importBtn")?.addEventListener("click",()=>this.importConfig()),this.container.querySelector("#resetBtn")?.addEventListener("click",()=>this.resetConfig()),this.container.querySelector("#saveBtn")?.addEventListener("click",()=>this.saveSettings()),this.container.querySelector("#saveBtnFloat")?.addEventListener("click",()=>this.saveSettings()));}bindModelEvents(){const t=this.container?.querySelector("#modelZone"),e=this.container?.querySelector("#modelFile"),n=this.container?.querySelector("#modelName"),i=this.container?.querySelector("#charsetsZone"),o=this.container?.querySelector("#charsetsFile"),s=this.container?.querySelector("#charsetsName");t?.addEventListener("click",()=>e?.click()),e?.addEventListener("change",()=>{e.files?.[0]&&(n.textContent=`[OK] ${e.files[0].name}`);}),i?.addEventListener("click",()=>o?.click()),o?.addEventListener("change",()=>{o.files?.[0]&&(s.textContent=`[OK] ${o.files[0].name}`);}),this.container?.querySelector("#uploadBtn")?.addEventListener("click",async()=>{const t=e?.files?.[0],n=o?.files?.[0];if(t&&n)try{await async function(t,e){const n=await t.arrayBuffer(),i=await e.text(),o=JSON.parse(i),s=new p;await s.setUploadedModel(n,o),console.log("✅ 上传的模型已保存");}(t,n),L({useUploadedModel:!0}),C.show({title:"成功",content:"模型已保存,请刷新页面",icon:""});}catch(i){C.show({title:"错误",content:String(i),icon:""});}else C.show({title:"提示",content:"请选择模型文件和字符集文件",icon:""});}),this.container?.querySelector("#deleteModelBtn")?.addEventListener("click",()=>{C.confirm({title:"删除模型",content:"确定删除已上传的模型吗?",onConfirm:async()=>{await async function(){const t=new p;await t.deleteUploadedModel(),console.log("🗑️ 上传的模型已删除");}(),L({useUploadedModel:false}),C.show({title:"成功",content:"模型已删除",icon:""});}});});}bindAgreementSelectorDeleteEvents(){this.container?.querySelectorAll(".btn-delete-agreement").forEach(t=>{t.addEventListener("click",t=>{const e=parseInt(t.target.dataset.index||"0");this.deleteAgreementSelector(e);});});}bindCalcRuleDeleteEvents(){this.container?.querySelectorAll(".btn-delete-calc-rule").forEach(t=>{t.addEventListener("click",t=>{const e=parseInt(t.target.dataset.index||"0");this.deleteCalculateRule(e);});});}bindSiteRuleEvents(){this.container?.querySelectorAll(".btn-edit-site-rule").forEach(t=>{t.addEventListener("click",t=>{const e=t.target.dataset.key;e&&this.editSiteRule(e);});}),this.container?.querySelectorAll(".btn-delete-site-rule").forEach(t=>{t.addEventListener("click",t=>{const e=t.target.dataset.key;e&&this.deleteSiteRuleUI(e);});});}addAgreementSelector(){const t=this.container?.querySelector("#newAgreementSelector"),e=t?.value.trim();if(!e)return void C.show({title:"提示",content:"请输入选择器",icon:""});const n=R().agreementSelectors||[];if(n.includes(e))return void C.show({title:"提示",content:"选择器已存在",icon:""});n.push(e),L({agreementSelectors:n});const i=this.container?.querySelector("#agreementSelectorsList");i&&(i.innerHTML=this.renderAgreementSelectors(n)),this.bindAgreementSelectorDeleteEvents(),t.value="";}deleteAgreementSelector(t){const e=R().agreementSelectors||[];e.splice(t,1),L({agreementSelectors:e});const n=this.container?.querySelector("#agreementSelectorsList");n&&(n.innerHTML=this.renderAgreementSelectors(e)),this.bindAgreementSelectorDeleteEvents();}addCalculateRule(){const t=this.container?.querySelector("#newCalcRulePattern"),e=this.container?.querySelector("#newCalcRuleMatchType"),n=this.container?.querySelector("#newCalcRuleOutputMode"),i=t?.value.trim();if(!i)return void C.show({title:"提示",content:"请输入站点匹配规则",icon:""});const o=R().calculateRules||[];o.push({pattern:i,matchType:e.value,outputMode:n.value,enabled:true}),L({calculateRules:o});const s=this.container?.querySelector("#calculateRulesList");s&&(s.innerHTML=this.renderCalculateRules(o)),this.bindCalcRuleDeleteEvents(),t.value="";}deleteCalculateRule(t){const e=R().calculateRules||[];e.splice(t,1),L({calculateRules:e});const n=this.container?.querySelector("#calculateRulesList");n&&(n.innerHTML=this.renderCalculateRules(e)),this.bindCalcRuleDeleteEvents();}addSiteRule(){const t=this.container?.querySelector("#newRuleHostname"),e=this.container?.querySelector("#newRuleSelector"),n=this.container?.querySelector("#newRuleInputSelector"),i=t?.value.trim(),o=e?.value.trim(),s=n?.value.trim();i&&o?(T(i,{selector:o,inputSelector:s||void 0,enabled:true}),this.refreshSiteRulesList(),t.value="",e.value="",n.value=""):C.show({title:"提示",content:"请填写主机名和验证码选择器",icon:""});}editSiteRule(t){const e=M()[t];if(!e)return;this.currentEditRuleKey=t;const n=this.container?.querySelector("#editRuleCard"),i=this.container?.querySelector("#editRuleKey"),o=this.container?.querySelector("#editRuleOriginalKey"),s=this.container?.querySelector("#editRuleSelector"),r=this.container?.querySelector("#editRuleInput");n&&(n.style.display="block"),i&&(i.value=t),o&&(o.value=t),s&&(s.value=e.selector||""),r&&(r.value=e.inputSelector||"");}saveEditRule(){const t=this.container?.querySelector("#editRuleOriginalKey"),e=this.container?.querySelector("#editRuleSelector"),n=this.container?.querySelector("#editRuleInput"),i=t?.value,o=e?.value.trim(),s=n?.value.trim();if(o){if(i){const t=M()[i];t&&(D(i),T(t.hostname,{...t,selector:o,inputSelector:s||void 0}));}this.cancelEditRule(),this.refreshSiteRulesList();}else C.show({title:"提示",content:"验证码选择器不能为空",icon:""});}cancelEditRule(){this.currentEditRuleKey=null;const t=this.container?.querySelector("#editRuleCard");t&&(t.style.display="none");}deleteSiteRuleUI(t){C.confirm({title:"删除规则",content:`确定删除规则 "${t}" 吗?`,onConfirm:()=>{D(t),this.refreshSiteRulesList();}});}refreshSiteRulesList(){const t=this.container?.querySelector("#siteRulesList");t&&(t.innerHTML=this.renderSiteRules()),this.bindSiteRuleEvents();}exportSiteRules(){const t=M(),e=Object.entries(t).map(([t,e])=>({hostname:e.hostname||t,selector:e.selector,inputSelector:e.inputSelector,fullUrl:e.fullUrl}));this.downloadJson(e,"ddddocr-rules.json");}importSiteRules(){const t=document.createElement("input");t.type="file",t.accept=".json",t.onchange=async t=>{const e=t.target.files?.[0];if(e)try{const t=await e.text(),n=JSON.parse(t);if(!Array.isArray(n))throw new Error("格式错误");for(const e of n)e.hostname&&e.selector&&T(e.hostname,{selector:e.selector,inputSelector:e.inputSelector,fullUrl:e.fullUrl,enabled:!0});this.refreshSiteRulesList(),C.show({title:"成功",content:`已导入 ${n.length} 条规则`,icon:""});}catch{C.show({title:"错误",content:"规则文件格式错误",icon:""});}},t.click();}clearStats(){C.confirm({title:"清除统计",content:"确定要清除所有统计数据吗?",onConfirm:()=>{N.clear();const t=this.container?.querySelector('[data-panel="stats"]');t&&(t.innerHTML=this.renderStats()),this.container?.querySelector("#clearStatsBtn")?.addEventListener("click",()=>this.clearStats());}});}exportConfig(){const t=R();this.downloadJson(t,"ddddocr-config.json");}importConfig(){const t=document.createElement("input");t.type="file",t.accept=".json",t.onchange=async t=>{const e=t.target.files?.[0];if(e)try{const t=await e.text();L(JSON.parse(t)),C.show({title:"成功",content:"配置已导入,请刷新页面",icon:""});}catch{C.show({title:"错误",content:"配置文件格式错误",icon:""});}},t.click();}resetConfig(){C.confirm({title:"重置设置",content:"确定重置所有设置吗?",onConfirm:()=>{GM_setValue("ddddocr_config",o),C.show({title:"成功",content:"设置已重置,请刷新页面",icon:""});}});}downloadJson(t,e){const n=new Blob([JSON.stringify(t,null,2)],{type:"application/json"}),i=URL.createObjectURL(n),o=document.createElement("a");o.href=i,o.download=e,o.click(),URL.revokeObjectURL(i);}switchTab(t){if(this.container&&(this.activeTab=t,this.container.querySelectorAll(".ddddocr-tab").forEach(e=>{e.classList.toggle("active",e.dataset.tab===t);}),this.container.querySelectorAll(".ddddocr-panel").forEach(e=>{e.classList.toggle("active",e.dataset.panel===t);}),"stats"===t)){const t=this.container.querySelector('[data-panel="stats"]');t&&(t.innerHTML=this.renderStats()),this.container.querySelector("#clearStatsBtn")?.addEventListener("click",()=>this.clearStats());}}saveSettings(){if(!this.container)return;const t={};this.container.querySelectorAll(".ddddocr-switch").forEach(e=>{const n=e.dataset.key;n&&(t[n]=e.classList.contains("on"));}),this.container.querySelectorAll("input[data-key]").forEach(e=>{const n=e.dataset.key;n&&(t[n]=e.value.trim());}),this.container.querySelectorAll("select[data-key]").forEach(e=>{const n=e.dataset.key;n&&(t[n]=e.value);}),this.container.querySelectorAll("textarea[data-key]").forEach(e=>{const n=e.dataset.key;if(n){const i=e.value;t[n]=i.split("\n").filter(t=>t.trim());}}),L(t),this.onConfigChange(R()),"undefined"!=typeof GM_notification&&GM_notification({title:"设置已保存",text:"配置已成功保存",timeout:2e3}),this.hide();}async show(){this.container||await this.createContainer(),this.isVisible=true,this.overlay?.classList.add("visible"),this.container?.classList.add("visible"),$()&&(this.container?.classList.add("mobile"),document.body.style.overflow="hidden");}hide(){this.isVisible=false,this.overlay?.classList.remove("visible"),this.container?.classList.remove("visible"),document.body.style.overflow="",window.removeEventListener("resize",this.handleResize);}setOnConfigChange(t){this.onConfigChange=t;}}class B{constructor(){this.container=null,this.create();}create(){const t=document.createElement("style");t.textContent="\n .ddddocr-loading-indicator {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n height: 4px;\n z-index: 2147483647;\n overflow: hidden;\n background: linear-gradient(90deg, #4A90E2 0%, #FF69B4 25%, #87CEEB 50%, #FF69B4 75%, #4A90E2 100%);\n background-size: 200% 100%;\n animation: ddddocr-wave 3s linear infinite;\n opacity: 0;\n transition: opacity 0.3s;\n }\n .ddddocr-loading-indicator.visible { opacity: 1; }\n @keyframes ddddocr-wave {\n 0% { background-position: 0% 50%; }\n 100% { background-position: 200% 50%; }\n }\n .ddddocr-loading-text {\n position: fixed;\n top: 8px;\n left: 50%;\n transform: translateX(-50%);\n background: #4A90E2;\n color: white;\n padding: 6px 16px;\n border-radius: 20px;\n font-size: 12px;\n font-weight: 500;\n z-index: 2147483647;\n opacity: 0;\n transition: opacity 0.3s;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n }\n .ddddocr-loading-text.visible { opacity: 1; }\n ",document.head.appendChild(t),this.container=document.createElement("div"),this.container.className="ddddocr-loading-indicator",document.body.appendChild(this.container);const e=document.createElement("div");e.className="ddddocr-loading-text",e.id="ddddocr-loading-text",e.textContent="正在初始化 DDDD OCR...",document.body.appendChild(e);}show(t){if(!this.container)return;this.container.classList.add("visible");const e=document.getElementById("ddddocr-loading-text");e&&(t&&(e.textContent=t),e.classList.add("visible"));}updateText(t){const e=document.getElementById("ddddocr-loading-text");e&&(e.textContent=t);}hide(){if(!this.container)return;this.container.classList.remove("visible");const t=document.getElementById("ddddocr-loading-text");t&&t.classList.remove("visible"),setTimeout(()=>{this.container?.remove(),t?.remove(),this.container=null;},300);}}class q{constructor(){this.engine=new e({getModel:f,wasmPaths:"https://cdn.jsdelivr.net/npm/onnxruntime-web@1.17.0/dist/"});}async init(){await async function(){try{await y.init();}catch(e){console.warn("[WASMCache] 初始化失败:",e);}(async function(){console.log("📦 开始预下载 WASM 文件"),await Promise.allSettled(v.map(async t=>{try{if(await y.get(t))return;const e=await x(t);await y.set(t,e);}catch(e){console.warn(`⚠️ ${t} 下载失败`,e);}}));})().catch(()=>{});const t=window.fetch;window.fetch=async function(n,i){const o="string"==typeof n?n:n instanceof URL?n.href:n.url,s=v.find(t=>o.includes(t));if(!s)return t.call(this,n,i);try{let t=await y.get(s);return t||(t=await x(s),y.set(s,t).catch(()=>{})),new Response(t,{status:200,headers:{"Content-Type":"application/wasm","Content-Length":String(t.byteLength)}})}catch(e){return t.call(this,n,i)}},console.log("✅ WASM 缓存已启用");}(),await this.engine.init();}async recognize(t){return this.engine.recognize(t)}}class F{constructor(t,e){this.observer=null,this.enabled=false,this.checkInterval=null,this.eventEmitter=null,this.autoFill=new n,this.initialScanDone=false,this.initialScanTimer=null,this.processingElements=new WeakSet,this.processedElements=new WeakMap,this.customCaptchaElement=null,this.customInputElement=null,this.ocr=t,this.detector=new c,this.eventEmitter=e||null;}start(){this.enabled||(this.enabled=true,r.info("启动验证码自动检测"),this.applyStoredSiteRule(),this.scheduleInitialDetect(),this.observer=new MutationObserver(t=>{let e=false;for(const n of t){if(n.addedNodes.forEach(t=>{t instanceof HTMLElement&&(this.checkElement(t),(t.matches('input[type="checkbox"]')||t.querySelector('input[type="checkbox"]'))&&(e=true));}),"attributes"===n.type){const t=n.target;t instanceof HTMLElement&&(t instanceof HTMLImageElement?"src"!==n.attributeName&&"data-src"!==n.attributeName||this.recheckImage(t):t instanceof HTMLCanvasElement?this.recheckCanvas(t):"style"===n.attributeName&&t.style.backgroundImage&&this.recheckDiv(t));}"childList"===n.type&&n.target instanceof SVGElement&&this.recheckSVG(n.target);}e&&setTimeout(()=>this.checkAgreementBoxes(),300);}),this.observer.observe(document.body,{childList:true,subtree:true,attributes:true,attributeFilter:["src","data-src","srcset","style","href"],characterData:true}),this.startIntervalCheck(),this.checkAgreementBoxes());}stop(){this.enabled&&(this.enabled=false,r.info("停止验证码自动检测"),this.observer?.disconnect(),this.observer=null,this.checkInterval&&(clearInterval(this.checkInterval),this.checkInterval=null),this.initialScanTimer&&(clearTimeout(this.initialScanTimer),this.initialScanTimer=null));}isEnabled(){return this.enabled}applyStoredSiteRule(){const t=M(),e=window.location.href,n=window.location.hostname;let i=null;for(const o of Object.keys(t)){const s=t[o];if(s.enabled){if(s.fullUrl&&e===s.fullUrl){i=s;break}if(s.urlPattern&&e.startsWith(s.urlPattern)){i=s;break}s.hostname!==n||s.fullUrl||s.urlPattern||(i=s);}}if(i){const t=document.querySelector(i.selector);t&&(this.customCaptchaElement=t,i.inputSelector&&(this.customInputElement=document.querySelector(i.inputSelector)),r.info("应用站点规则:",i.selector));}}checkAgreementBoxes(){const t=R();if(!t.autoCheckAgreement)return;let e=[];t.agreementSelectors&&t.agreementSelectors.length>0&&(e=t.agreementSelectors);const n=this.detector.guessAgreementCheckboxes();for(const o of n){const t=o.element;if(t.checked)continue;const e=o.clickTarget;e?(e.click(),r.info("点击协议复选框容器")):(t.checked=true,t.dispatchEvent(new Event("change",{bubbles:true})),t.dispatchEvent(new Event("input",{bubbles:true})),r.info("直接勾选协议复选框")),this.detector.markAgreementChecked(t);}for(const o of e)if(o.trim())try{document.querySelectorAll(o).forEach(t=>{t instanceof HTMLInputElement&&"checkbox"===t.type&&!t.checked&&(t.checked=!0,t.dispatchEvent(new Event("change",{bubbles:!0})),t.dispatchEvent(new Event("input",{bubbles:!0})),r.info("通过选择器勾选协议:",o));});}catch(i){r.warn("无效的协议选择器:",o);}}scheduleInitialDetect(){this.initialScanDone||(this.detectExistingCaptchas(false),this.initialScanTimer=window.setTimeout(()=>{this.initialScanDone||this.detectExistingCaptchas(true);},3e3));}startIntervalCheck(){this.checkInterval=window.setInterval(()=>{const t=this.detector.scan();if(t&&0!==t.length)for(const e of t)if(this.hasElementChanged(e.element)){this.processDetectedCaptcha(e);break}},i.AUTO_DETECT_INTERVAL);}detectExistingCaptchas(t){r.debug("检测页面已存在的验证码"),document.querySelectorAll("img").forEach(e=>this.checkImage(e,t)),document.querySelectorAll("canvas").forEach(e=>this.checkCanvas(e,t)),document.querySelectorAll("svg").forEach(e=>this.checkSVG(e,t)),document.querySelectorAll('div[style*="background"]').forEach(e=>this.checkDiv(e,t)),t&&(this.initialScanDone=true);}checkElement(t){t instanceof HTMLImageElement&&this.checkImage(t,true),t instanceof HTMLCanvasElement&&this.checkCanvas(t,true),t instanceof SVGElement&&this.checkSVG(t,true),t.style.backgroundImage&&this.checkDiv(t,true),t.querySelectorAll("img").forEach(t=>this.checkImage(t,true)),t.querySelectorAll("canvas").forEach(t=>this.checkCanvas(t,true)),t.querySelectorAll("svg").forEach(t=>this.checkSVG(t,true));}async waitForImageLoad(t,e=5e3){return !!(t.complete&&t.naturalWidth>0)||new Promise(n=>{const i=setTimeout(()=>{r(),n(false);},e),o=()=>{r(),n(true);},s=()=>{r(),n(false);},r=()=>{clearTimeout(i),t.removeEventListener("load",o),t.removeEventListener("error",s);};t.addEventListener("load",o),t.addEventListener("error",s),t.complete&&t.naturalWidth>0&&(r(),n(true));})}async recheckImage(t){await this.waitForImageLoad(t)&&await this.checkImage(t,true);}async recheckCanvas(t){await new Promise(t=>requestAnimationFrame(t)),await this.checkCanvas(t,true);}async recheckSVG(t){await new Promise(t=>requestAnimationFrame(t)),await this.checkSVG(t,true);}async recheckDiv(t){await new Promise(t=>requestAnimationFrame(t)),await this.checkDiv(t,true);}async checkImage(t,e){const n=R();if(n.captchaSelector){if(!t.matches(n.captchaSelector))return}else if(!this.isCaptchaImage(t))return;if(!this.hasElementChanged(t))return;if(this.processingElements.has(t))return;if(this.eventEmitter?.emit("detect:found",{element:t,type:"img"}),!e)return;const i=this.findNearbyInput(t);i&&(i.value.trim()&&(i.value="",i.dispatchEvent(new Event("input",{bubbles:true}))),await this.waitForImageLoad(t)&&t.naturalWidth&&await this.recognizeAndFill(t,i));}async checkCanvas(t,e){const n=R();if(n.captchaSelector){if(!t.matches(n.captchaSelector))return}else if(!this.isCaptchaCanvas(t))return;if(!this.hasElementChanged(t))return;if(this.processingElements.has(t))return;if(this.eventEmitter?.emit("detect:found",{element:t,type:"canvas"}),!e)return;const i=this.findNearbyInput(t);if(!i)return;await new Promise(t=>requestAnimationFrame(t));const o=await new Promise((e,n)=>{t.toBlob(t=>{t?e(t):n(new Error("Canvas转换失败"));},"image/png");});await this.recognizeAndFillBlob(t,o,i);}async checkSVG(t,e){const n=R();if(n.captchaSelector){if(!t.matches(n.captchaSelector))return}else if(!this.isCaptchaSVG(t))return;if(!this.hasElementChanged(t))return;if(this.processingElements.has(t))return;if(this.eventEmitter?.emit("detect:found",{element:t,type:"svg"}),!e)return;const i=this.findNearbyInput(t);if(!i)return;const o=await this.svgToBlob(t);await this.recognizeAndFillBlob(t,o,i);}async checkDiv(t,e){const n=R();if(n.captchaSelector){if(!t.matches(n.captchaSelector))return}else if(!this.isCaptchaDiv(t))return;const i=t.style.backgroundImage;if(!i)return;if(!this.hasElementChanged(t))return;if(this.processingElements.has(t))return;if(this.eventEmitter?.emit("detect:found",{element:t,type:"div"}),!e)return;const o=this.findNearbyInput(t);if(!o)return;const s=i.match(/url\(['"]?(.+?)['"]?\)/);if(!s)return;const r=s[1],a=Date.now();let d="";if(r.startsWith("data:")){const t=await fetch(r),e=await t.blob();d=(await this.ocr.recognize(e)).text;}else d=(await this.ocr.recognize(r)).text;const c=Date.now()-a,l=this.processResult(d);await this.fillInput(o,l),this.markElementProcessed(t),N.record(window.location.hostname,c);}async svgToBlob(t){const e=(new XMLSerializer).serializeToString(t),n=new Blob([e],{type:"image/svg+xml;charset=utf-8"}),i=URL.createObjectURL(n),o=new Image;o.src=i,await new Promise((t,e)=>{o.onload=t,o.onerror=e,setTimeout(e,5e3);});const s=document.createElement("canvas");return s.width=t.clientWidth||150,s.height=t.clientHeight||50,s.getContext("2d").drawImage(o,0,0),URL.revokeObjectURL(i),new Promise((t,e)=>{s.toBlob(n=>{n?t(n):e(new Error("SVG转换失败"));},"image/png");})}async recognizeAndFill(t,e){if(this.processingElements.has(t))return;this.processingElements.add(t);const n=Date.now();r.time("recognizeAndFill");try{this.eventEmitter?.emit("recognize:start",{element:t});const i=await this.ocr.recognize(t),o=this.processResult(i.text);this.eventEmitter?.emit("recognize:complete",{element:t,result:{text:o}}),await this.fillInput(e,o),this.markElementProcessed(t);const s=Date.now()-n;N.record(window.location.hostname,s),r.timeEnd("recognizeAndFill"),r.info("识别完成:",o);}catch(i){r.error("识别失败:",i),this.eventEmitter?.emit("recognize:error",{element:t,error:i});}finally{this.processingElements.delete(t);}}async recognizeAndFillBlob(t,e,n){if(this.processingElements.has(t))return;this.processingElements.add(t);const i=Date.now();try{this.eventEmitter?.emit("recognize:start",{element:t});const o=await this.ocr.recognize(e),s=this.processResult(o.text);this.eventEmitter?.emit("recognize:complete",{element:t,result:{text:s}}),await this.fillInput(n,s),this.markElementProcessed(t);const a=Date.now()-i;N.record(window.location.hostname,a),r.info("识别完成:",s);}catch(o){r.error("识别失败:",o),this.eventEmitter?.emit("recognize:error",{element:t,error:o});}finally{this.processingElements.delete(t);}}processResult(t){const e=R();return e.autoCalculate?d.processResult(t,{autoCalculate:true,outputMode:e.calculateOutputMode,rules:e.calculateRules||[]},window.location.hostname):t}async fillInput(t,e){const n=R();await this.autoFill.fill(t,e,{simulate:true,autoSubmit:false,typewriterEffect:n.typewriterEffect}),n.enableNotification&&"undefined"!=typeof GM_notification&&GM_notification({title:"验证码已自动填充",text:`识别结果: ${e}`,timeout:3e3});}processDetectedCaptcha(t){const e=t.inputElement||this.detector.findRelatedInput(t.element);if(e)if("image"===t.type)this.recognizeAndFill(t.element,e);else if("canvas"===t.type){const n=t.element;n.toBlob(t=>{t&&this.recognizeAndFillBlob(n,t,e);},"image/png");}}isCaptchaImage(t){const e=t.naturalWidth||t.width,n=t.naturalHeight||t.height;if(ei.MAX_CAPTCHA_WIDTH||n>i.MAX_CAPTCHA_HEIGHT)return false;const o=(t.src+t.className+t.id+t.alt+(t.getAttribute("data-src")||"")).toLowerCase();return !i.EXCLUDE_PATTERNS.some(t=>o.includes(t))&&i.CAPTCHA_KEYWORDS.some(t=>o.includes(t))}isCaptchaCanvas(t){const e=t.width,n=t.height;if(ei.MAX_CAPTCHA_WIDTH||n>i.MAX_CAPTCHA_HEIGHT)return false;const o=(t.className+t.id+(t.getAttribute("data-type")||"")).toLowerCase();return !i.EXCLUDE_PATTERNS.some(t=>o.includes(t))&&i.CAPTCHA_KEYWORDS.some(t=>o.includes(t))}isCaptchaSVG(t){const e=t.clientWidth||parseInt(t.getAttribute("width")||"0"),n=t.clientHeight||parseInt(t.getAttribute("height")||"0");if(ei.MAX_CAPTCHA_WIDTH||n>i.MAX_CAPTCHA_HEIGHT)return false;const o=((t.className?.baseVal||"")+(t.id||"")).toLowerCase();if(i.CAPTCHA_KEYWORDS.some(t=>o.includes(t)))return true;let s=t.parentElement,r=0;for(;s&&r<3;){const t=((s.className?.toString?.()||"")+(s.id||"")).toLowerCase();if(i.CAPTCHA_KEYWORDS.some(e=>t.includes(e)))return true;s=s.parentElement,r++;}const a=this.findNearbyInput(t);if(!a)return false;const d=(a.name+a.id+a.className+a.placeholder).toLowerCase();return i.INPUT_KEYWORDS.some(t=>d.includes(t))}isCaptchaDiv(t){const e=t.clientWidth,n=t.clientHeight;if(ei.MAX_CAPTCHA_WIDTH||n>i.MAX_CAPTCHA_HEIGHT)return false;const o=(t.className+t.id).toLowerCase();return i.CAPTCHA_KEYWORDS.some(t=>o.includes(t))}findNearbyInput(t){const e=R();if(e.inputSelector){const t=document.querySelector(e.inputSelector);if(t instanceof HTMLInputElement)return t}return this.detector.findRelatedInput(t)}getElementHash(t){if(t instanceof HTMLImageElement)return t.src+"_"+t.naturalWidth+"_"+t.naturalHeight;if(t instanceof HTMLCanvasElement)try{return t.toDataURL()}catch{return "canvas_"+Date.now()}else {if(t instanceof SVGElement)return t.outerHTML;if(t instanceof HTMLElement&&t.style.backgroundImage)return t.style.backgroundImage}return ""}hasElementChanged(t){const e=this.getElementHash(t),n=this.processedElements.get(t);return !n||e!==n}markElementProcessed(t){const e=this.getElementHash(t);this.processedElements.set(t,e);}}class W{constructor(){this.loadingIndicator=null,this.initialized=false,this.menuCommandIds=new Map,this.eventEmitter=new l,this.ocr=new q,this.detector=new F(this.ocr,this.eventEmitter),this.settingsUI=new z,this.registerMenuCommands(),this.settingsUI.setOnConfigChange(t=>this.handleConfigChange(t));const t=R();r.setDebugMode(t.debugMode||false);}async init(){if(!P())return void r.debug("当前站点不满足执行条件");if(this.initialized)return;const t=R();this.initialized=true,this.loadingIndicator=new B,r.info("DDDD OCR 启动");try{this.loadingIndicator.show("正在初始化 DDDD OCR"),this.loadingIndicator.updateText("正在加载模型文件"),await this.ocr.init(),r.info("OCR 已就绪"),this.loadingIndicator.updateText("DDDD OCR 已就绪"),t.autoDetect&&(this.detector.start(),r.info("自动检测已启动")),setTimeout(()=>this.loadingIndicator?.hide(),2e3),this.showNotification("DDDD OCR 已就绪",t.autoDetect?"自动检测已启用":"点击菜单启用自动检测"),this.refreshMenuCommands();}catch(e){r.error("初始化失败:",e),this.loadingIndicator?.updateText("初始化失败: "+String(e)),setTimeout(()=>this.loadingIndicator?.hide(),3e3),this.showNotification("初始化失败",String(e),true);}}registerMenuCommands(){this.refreshMenuCommands();}refreshMenuCommands(){const t=GM_registerMenuCommand("打开设置",()=>this.settingsUI.show(),"s");this.menuCommandIds.set("settings",t);const e=GM_registerMenuCommand("清除缓存",async()=>{C.confirm({title:"清除缓存",content:"确定要清除所有缓存吗(包括模型和 WASM)?下次启动将重新下载。",confirmText:"确定清除",cancelText:"取消",onConfirm:async()=>{await async function(){const t=new p;await t.delete(),console.log("🗑️ 模型缓存已清除");}(),await async function(){await y.clear();}(),this.showNotification("缓存已清除","请刷新页面");}});},"d");this.menuCommandIds.set("cache",e);const n=GM_registerMenuCommand("查看状态",()=>this.showStatus(),"i");this.menuCommandIds.set("status",n);}showStatus(){const t=R(),e=_(),n=M(),i=N.getStats(),o=`\n脚本状态: ${this.initialized?"已初始化":"未初始化"}\n当前站点: ${window.location.hostname}\n白名单状态: ${t.enableWhitelist?"已启用":"已禁用"}\n白名单数量: ${t.whitelist?.length||0} 个站点\n当前站点匹配: ${e?"在白名单中":"不在白名单中"}\n自动检测: ${t.autoDetect?"已启用":"已禁用"}\n打字机效果: ${t.typewriterEffect?"已启用":"已禁用"}\n自动勾选协议: ${t.autoCheckAgreement?"已启用":"已禁用"}\n协议选择器数: ${t.agreementSelectors?.length||0} 个\n自动计算: ${t.autoCalculate?"已启用":"已禁用"}\n计算输出: ${"result"===t.calculateOutputMode?"仅结果":"完整等式"}\n计算规则数: ${t.calculateRules?.length||0} 条\n站点规则数: ${Object.keys(n).length} 条\n调试模式: ${t.debugMode?"已启用":"已禁用"}\n上传模型: ${t.useUploadedModel?"已启用":"未启用"}\n自动下载: ${t.autoDownload?"已启用":"已禁用"}\n总识别次数: ${i.total} 次`;C.show({title:"当前状态",content:o});}handleConfigChange(t){r.setDebugMode(t.debugMode||false),t.autoDetect&&!this.initialized&&this.init(),t.autoDetect?this.detector.start():this.detector.stop(),this.refreshMenuCommands();}showNotification(t,e,n=false){R().enableNotification&&"undefined"!=typeof GM_notification&&GM_notification({title:t,text:e,timeout:n?5e3:3e3});}}function U(){const t=new W;P()?setTimeout(()=>t.init(),500):r.debug("DDDD OCR 不满足执行条件,仅注册菜单命令");}"loading"===document.readyState?document.addEventListener("DOMContentLoaded",U):U(); })();