Repository: enncy/online-course-script Branch: 4.0 Commit: 038707cd3225 Files: 86 Total size: 703.8 KB Directory structure: gitextract_8zhb6tyv/ ├── .eslintignore ├── .eslintrc.json ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug.md │ │ └── feat.md │ └── workflows/ │ └── auto-release.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc.json ├── .vscode/ │ ├── settings.json │ └── tasks.json ├── CHANGELOG.md ├── CHANGELOG_CURRENT.md ├── CHANGELOG_SIMPLIFIED.md ├── LICENSE ├── README.md ├── package.json ├── packages/ │ ├── core/ │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── package.json │ │ ├── src/ │ │ │ ├── core/ │ │ │ │ ├── answer-wrapper/ │ │ │ │ │ ├── answer.wrapper.handler.ts │ │ │ │ │ ├── answer.wrapper.parser.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── interface.ts │ │ │ │ ├── utils/ │ │ │ │ │ ├── dom.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── request.ts │ │ │ │ │ └── string.ts │ │ │ │ └── worker/ │ │ │ │ ├── index.ts │ │ │ │ ├── interface.ts │ │ │ │ ├── question.resolver.ts │ │ │ │ ├── utils.ts │ │ │ │ └── worker.ts │ │ │ ├── index.ts │ │ │ └── utils/ │ │ │ ├── common.ts │ │ │ ├── const.ts │ │ │ ├── index.ts │ │ │ ├── playwright.ts │ │ │ └── string.ts │ │ ├── tsconfig.json │ │ ├── typedoc.json │ │ └── vite.config.ts │ ├── scripts/ │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── assets/ │ │ │ ├── css/ │ │ │ │ └── style.css │ │ │ └── less/ │ │ │ └── style.less │ │ ├── entry.common.js │ │ ├── entry.dev.js │ │ ├── entry.js │ │ ├── global.d.ts │ │ ├── global.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── elements/ │ │ │ │ └── search.infos.ts │ │ │ ├── index.ts │ │ │ ├── projects/ │ │ │ │ ├── background.ts │ │ │ │ ├── common.ts │ │ │ │ ├── cx.ts │ │ │ │ ├── icourse.ts │ │ │ │ ├── icve.ts │ │ │ │ ├── yuketang.ts │ │ │ │ ├── zhs.ts │ │ │ │ └── zjy.ts │ │ │ ├── render.ts │ │ │ └── utils/ │ │ │ ├── app.ts │ │ │ ├── configs.ts │ │ │ ├── index.ts │ │ │ ├── markdown.ts │ │ │ ├── render.ts │ │ │ ├── study.ts │ │ │ └── work.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ └── utils/ │ ├── .gitignore │ ├── package.json │ ├── src/ │ │ ├── common/ │ │ │ ├── index.ts │ │ │ └── script.builder.ts │ │ └── index.ts │ └── tsconfig.json ├── pnpm-workspace.yaml └── scripts/ ├── build-core.js ├── dev-core.js ├── release.sh ├── simplify_changelog.js ├── tsc.js └── utils.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .eslintignore ================================================ node_modules dist lib package-lock.json pnpm-lock.yaml public stats.html tests/* .md ================================================ FILE: .eslintrc.json ================================================ { "env": { "node": true, "commonjs": true, "browser": true, "es6": true, "vue/setup-compiler-macros": true }, "extends": ["standard", "plugin:vue/vue3-recommended", "prettier"], "parserOptions": { "ecmaVersion": "latest", "parser": "@typescript-eslint/parser", "sourceType": "module" }, "plugins": ["vue", "@typescript-eslint", "eslint-plugin-vue"], "rules": { "max-len": [ 2, { "code": 120, "tabWidth": 2, "ignoreComments": true, "ignoreStrings": true } ], "indent": "off", "camelcase": "off", "vue/multi-word-component-names": "off", "vue/valid-attribute-name": "off", "space-before-function-paren": "off", "func-call-spacing": "off", "no-redeclare": "off" }, "globals": { "core": "readonly", "scripts": "readonly" } } ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: ['enncy'] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry custom: ['https://afdian.net/@enncy'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] ================================================ FILE: .github/ISSUE_TEMPLATE/bug.md ================================================ --- name: bug about: 报告/提交漏洞 title: '[bug] : BUG xxx' labels: '' assignees: enncy --- **环境描述** 系统: win11/mac/linux/... 浏览器:谷歌/edge/fire fox/... OCS版本:xx.xx.xx 页面链接: https://example.com/xxxxxxxxxxxxxxx **BUG 描述** ... **重现方法** 1. 进入 '...' 2. 点击 '....' 3. 滚动到 '....' 4. 看到此 BUG **预期行为** 应该是 xxx 动作,而不是 xxx 动作。 **屏幕截图** 如果可以请附带截图。 **解决方案** 如果可以,请提供您的解决思路 ================================================ FILE: .github/ISSUE_TEMPLATE/feat.md ================================================ --- name: feat about: 建议或者新功能新想法 title: '[feat] : 新特性XXX' labels: '' assignees: enncy --- **特性说明** **实现方案** **备选方案** ================================================ FILE: .github/workflows/auto-release.yml ================================================ # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs name: Userscript Auto Releases on: push: tags: - "*" jobs: build: runs-on: ubuntu-latest permissions: contents: write strategy: matrix: node-version: [18.x] steps: - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - name: Setup env run: | echo 'BUILD_PATH="../release"' > ./scripts/.env echo 'VITE_BUILD_PATH="../../release"' > ./packages/core/.env echo 'VITE_BUILD_PATH="../../release"' > ./packages/scripts/.env echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV VERSION=$(jq -r .version package.json) echo "VERSION=$VERSION" >> $GITHUB_ENV echo "DOWNLOAD_URL=https://github.com/ocsjs/ocsjs/releases/download/$VERSION/" >> $GITHUB_ENV - name: Install Dependencies run: | npm install pnpm -g pnpm install -w cd packages/scripts pnpm install cd ../../ npm run tsc - name: Build ocs scripts run: npm run build - name: Build ChangeLogs run: | npm run changelog npm run changelog:simplify npm run changelog:current - name: Fetch Change logs if: runner.os == 'Linux' run: | echo "pwd: $(pwd)" echo "files:" ls -la echo "Displaying CHANGELOG_CURRENT.md content:" cat CHANGELOG_CURRENT.md if [ -f "CHANGELOG_CURRENT.md" ]; then CHANGE_LOGS=$(cat CHANGELOG_CURRENT.md) echo "CHANGE_LOGS<> $GITHUB_ENV echo "$CHANGE_LOGS" >> $GITHUB_ENV echo "EOF" >> $GITHUB_ENV else echo "CHANGELOG_CURRENT.md file not found" fi shell: bash - name: Generate release.txt if: runner.os == 'Linux' run: | if [ -z "$CHANGE_LOGS" ]; then echo "No change logs found, using default message" CHANGE_LOGS="More new features are now supported. Check for detailed changelog soon." else echo "Using found change logs" fi cat > release.txt << EOF $CHANGE_LOGS ## 资源下载列表 - [用户脚本](${{ env.DOWNLOAD_URL }}ocs.user.js) - [用户脚本(全域名通用版)](${{ env.DOWNLOAD_URL }}ocs.common.user.js) - [核心库](${{ env.DOWNLOAD_URL }}core.js) - [简洁更新日志](${{ env.DOWNLOAD_URL }}CHANGELOG_SIMPLIFIED.md) ### 快捷访问 - [官网](https://docs.ocsjs.com/) Created at ${{ env.BUILDTIME }}. EOF - name: Create release uses: ncipollo/release-action@v1 with: artifactContentType: "html/text" artifacts: "./release/ocs.user.js,./release/ocs.common.user.js,./release/core.js,./release/index.js,./CHANGELOG_SIMPLIFIED.md" bodyFile: "./release.txt" ================================================ FILE: .gitignore ================================================ # customize **/*.zip # tsc lib/ # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* .pnpm-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage *.lcov # nyc test coverage .nyc_output # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # Snowpack dependency directory (https://snowpack.dev/) web_modules/ # TypeScript cache *.tsbuildinfo # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional stylelint cache .stylelintcache # Microbundle cache .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variable files .env .env.development.local .env.test.local .env.production.local .env.local # parcel-bundler cache (https://parceljs.org/) .cache .parcel-cache # Next.js build output .next out # Nuxt.js build / generate output .nuxt dist/ # Gatsby files .cache/ # Comment in the public line in if your project uses Gatsby and not Next.js # https://nextjs.org/blog/next-9-1#public-directory-support # public # vuepress build output .vuepress/dist # vuepress v2.x temp and cache directory .temp .cache # Docusaurus cache and generated files .docusaurus # Serverless directories .serverless/ # FuseBox cache .fusebox/ # DynamoDB Local files .dynamodb/ # TernJS port file .tern-port # Stores VSCode versions used for testing VSCode extensions .vscode-test # yarn v2 .yarn/cache .yarn/unplugged .yarn/build-state.yml .yarn/install-state.gz .pnp.* ================================================ FILE: .npmrc ================================================ node-linker=hoisted ================================================ FILE: .prettierignore ================================================ node_modules dist lib package-lock.json pnpm-lock.yaml public stats.html .md ================================================ FILE: .prettierrc.json ================================================ { "$schema": "https://json.schemastore.org/prettierrc.json", "semi": true, "singleQuote": true, "bracketSpacing": true, "bracketSameLine": false, "jsxSingleQuote": false, "printWidth": 120, "tabWidth": 2, "useTabs": true, "vueIndentScriptAndStyle": false, "arrowParens": "always", "proseWrap": "preserve", "htmlWhitespaceSensitivity": "css", "endOfLine": "auto", "trailingComma": "none", "singleAttributePerLine": true } ================================================ FILE: .vscode/settings.json ================================================ { "cSpell.words": [ "axios", "historychange", "icve", "isclose", "isstop", "jsdom", "nomove", "ocsjs", "ondefault", "ondone", "onhistorychange", "onprevent", "onrender", "onrun", "scripterror", "sider", "Tesseract", "traineddata", "Userscript", "VITE", "webrtc", "xmlhttp", "zhihuishu" ], "eslint.validate": ["javascript", "javascriptreact", "vue"], "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, "editor.defaultFormatter": "esbenp.prettier-vscode", /** 样式自动生成 */ "less.compile": { "compress": false, "sourceMap": false, "out": "../css/" }, /** 鼠标中键代码缩进 */ "editor.mouseWheelZoom": true, /** 鼠标滚动速度 */ "editor.mouseWheelScrollSensitivity": 2, /** 不生成 @return 的 jsdoc */ "typescript.suggest.jsdoc.generateReturns": false, "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "editor.wordBasedSuggestions": "off" } ================================================ FILE: .vscode/tasks.json ================================================ { // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", "tasks": [ { "label": "echo", "type": "shell", "command": "echo Hello" } ] } ================================================ FILE: CHANGELOG.md ================================================ ## [4.13.4](https://github.com/ocsjs/ocsjs/compare/4.13.2...4.13.4) (2026-04-23) ### Features * **script:** 兼容超星长时阅读任务点(限制翻页时间) ([ac34ba2](https://github.com/ocsjs/ocsjs/commit/ac34ba2d543afe9dc09c1803b558f7b37dbce095)) * **script:** 添加超星域名支持:jnzyjsxy.cn ([a27cb49](https://github.com/ocsjs/ocsjs/commit/a27cb49b9111e3317120c33eff75981de4b19ed8)) ## [4.13.2](https://github.com/ocsjs/ocsjs/compare/4.12.39...4.13.2) (2026-04-22) ### Bug Fixes * **script:** 修复智慧树26年上半年studywisdomh5复习模式错误的问题 ([2e82a0a](https://github.com/ocsjs/ocsjs/commit/2e82a0a8ea4cca4265ba592336a2762bf156ec08)) ### Features * **script:** 兼容雨课堂AI学伴自动学习 ([c96f4db](https://github.com/ocsjs/ocsjs/commit/c96f4db7d949df6f3fc983a6ae4783ece9b19783)) ## [4.12.39](https://github.com/ocsjs/ocsjs/compare/4.12.38...4.12.39) (2026-04-20) ### Bug Fixes * 修复智慧职教题目识别丢失img的src问题 ([482ac64](https://github.com/ocsjs/ocsjs/commit/482ac64f6d9ee0dc54a8bef1fe210ccb4b02d25a)) * 修复智慧职教题目识别丢失img的src问题,使用与学习通页面相同的方法 ([488f467](https://github.com/ocsjs/ocsjs/commit/488f467a8c212e17a866f0746e86a6c0084f1e2b)) * 修复智慧职教题目识别丢失img的src问题,使用与学习通页面相同的方法,且考虑纯图片无法识别问题 ([3a23e04](https://github.com/ocsjs/ocsjs/commit/3a23e04d89bb7913638ec880e9817aaa013143ba)) * 修复智慧职教题目识别丢失img的src问题,使用与学习通页面相同的方法,且考虑纯图片无法识别问题 ([d020954](https://github.com/ocsjs/ocsjs/commit/d0209548c56f6205b7c9edf4fe7ef84f334d03ec)) ## [4.12.38](https://github.com/ocsjs/ocsjs/compare/4.12.37...4.12.38) (2026-04-20) ### Bug Fixes * **script:** 兼容26年上半年智慧树新版studywisdomh5网课更新 ([cf52bf0](https://github.com/ocsjs/ocsjs/commit/cf52bf019b3f9c36c82bc5b90e5e3125510ff3cb)) ## [4.12.37](https://github.com/ocsjs/ocsjs/compare/4.12.32...4.12.37) (2026-04-15) ### Bug Fixes * **script:** 修复超星某些章节测试无法自动答题的BUG ([62b0aaf](https://github.com/ocsjs/ocsjs/commit/62b0aaf86416a07051513e7fbebcce94f29c12cc)) ### Features * **script:** 添加智慧树2026上学期新版HIKE AI教学中心视频学习功能 ([392a1a5](https://github.com/ocsjs/ocsjs/commit/392a1a59d5c352e0ca893bf72799cce9333e7f08)) ### Performance Improvements * **script:** 适配超星积分课的阅读任务不会自动开始阅读的问题 ([9b8ff4f](https://github.com/ocsjs/ocsjs/commit/9b8ff4f4e34b3e9de6e4c863b10b24bc7d7cf607)) * **script:** 添加超星阅读界面的使用提示 ([a8aa869](https://github.com/ocsjs/ocsjs/commit/a8aa8695efe8fb1c74baaab106c4c2401f86de53)) * **script:** 优化超星跳转时间太长的问题,改成智能等待时间 ([6314616](https://github.com/ocsjs/ocsjs/commit/631461620821559ea1bf068ba2445308df4995f1)) ## [4.12.32](https://github.com/ocsjs/ocsjs/compare/4.12.30...4.12.32) (2026-04-09) ### Bug Fixes * **script:** 尝试修复智慧树AI学伴课程过一段时间才会播放的BUG ([7c56dc9](https://github.com/ocsjs/ocsjs/commit/7c56dc925284dd8ac5237da0a6fd9b7dae643264)) ### Performance Improvements * **script:** 优化超星闯关模式重复提示 ([fd83815](https://github.com/ocsjs/ocsjs/commit/fd83815150eced1e8caf8d11493ab9841187c49c)) ## [4.12.30](https://github.com/ocsjs/ocsjs/compare/4.12.29...4.12.30) (2026-03-20) ### Performance Improvements * **script:** 添加智慧树新智慧形态考试菜单进入菜单栏 ([d9d362b](https://github.com/ocsjs/ocsjs/commit/d9d362bdc5d9bbd3de04019dede94c856d68ac77)) ## [4.12.29](https://github.com/ocsjs/ocsjs/compare/4.12.26...4.12.29) (2026-03-20) ### Bug Fixes * **script:** 兼容职教云xls任务点自动学习 ([7a3a174](https://github.com/ocsjs/ocsjs/commit/7a3a1748d85d617c740ce631b5d2b52cf5cec80f)) * **script:** 修复智慧树-新形态课程链接任务点无法跳过的BUG ([51a639d](https://github.com/ocsjs/ocsjs/commit/51a639d4b80a8851e347e1190f94d1cdc5f7f4a5)) ### Features * **script:** 添加智慧树-新形态课程自动考试功能 ([587a045](https://github.com/ocsjs/ocsjs/commit/587a045ff454e20b5ecef7d509c0fb25f43af4db)) ## [4.12.26](https://github.com/ocsjs/ocsjs/compare/4.12.23...4.12.26) (2026-03-18) ### Bug Fixes * **script:** 修复职教云PPT仅有2页时无法完成的BUG ([018462f](https://github.com/ocsjs/ocsjs/commit/018462f2746816f70439270e669756aa87240b03)) * **script:** 修复智慧职教AI测验答题时填空功能失效的BUG ([d850bea](https://github.com/ocsjs/ocsjs/commit/d850bea91b70780d23706eea433756812c6c6016)) ### Features * **script:** 添加新版职教云自动跳转旧版功能 ([55a9f1e](https://github.com/ocsjs/ocsjs/commit/55a9f1e198a71d8e8dd163cda4fdadfa8106c925)) ## [4.12.23](https://github.com/ocsjs/ocsjs/compare/4.12.22...4.12.23) (2026-03-17) ### Bug Fixes * 修复上个版本无法打包的BUG ([97608d8](https://github.com/ocsjs/ocsjs/commit/97608d81991ce1e0f262e2e09f1252a4839996af)) ## [4.12.22](https://github.com/ocsjs/ocsjs/compare/4.12.16...4.12.22) (2026-03-17) ### Bug Fixes * **script:** 添加智慧树-AI教学中心-题目作业自动答题功能 ([2a630aa](https://github.com/ocsjs/ocsjs/commit/2a630aade1c71b78e2fd1275f1f9713b2bbf21f6)) * **script:** 修复图片题中的图片在解析时重复出现的BUG ([84c697e](https://github.com/ocsjs/ocsjs/commit/84c697e1b681aa11c397cf335cd2730d00e7160c)) * **script:** 修复职教云资源库无法跳过测验的问题 ([34e4f38](https://github.com/ocsjs/ocsjs/commit/34e4f38310fc887b27cb71c5b8d1b6132c3b9f03)) * **script:** 修复智慧职教AI测验答题时无法填空的BUG ([edb492a](https://github.com/ocsjs/ocsjs/commit/edb492af72d6d8cd09e2146b6e39ecfe68c135f7)) ### Performance Improvements * 更新API,添加新API $msg,可同时显示气泡和打印日志 ([099907a](https://github.com/ocsjs/ocsjs/commit/099907ae72be2cc50874162667528e1c6d643b8b)) * 添加答题日志输出开关功能 ([d041373](https://github.com/ocsjs/ocsjs/commit/d04137339f023270f390c9344be74df854008181)) ## [4.12.16](https://github.com/ocsjs/ocsjs/compare/4.12.15...4.12.16) (2026-03-06) ### Bug Fixes * **script:** 修复智慧课程AI伴学的视频无法检测,以及掌握度只有一题的情况无法答题的BUG ([8ff88f2](https://github.com/ocsjs/ocsjs/commit/8ff88f2ddf2a16852082b5ca7d84575d5215bb6a)) * **script:** 优化 eslint 报错 ([036644f](https://github.com/ocsjs/ocsjs/commit/036644fc351cbdba3cca55655200bf1cd421dc92)) ## [4.12.15](https://github.com/ocsjs/ocsjs/compare/4.12.14...4.12.15) (2026-03-04) ### Bug Fixes * **script:** 修复2026上学期智慧树-新智慧课程无法自动下一章的BUG ([437696d](https://github.com/ocsjs/ocsjs/commit/437696da2017549c40e10584d964c82be77bd16e)) ## [4.12.14](https://github.com/ocsjs/ocsjs/compare/4.12.13...4.12.14) (2026-02-28) ### Features * **script:** 添加ZE题库域名白名单授权 ([a5bdb91](https://github.com/ocsjs/ocsjs/commit/a5bdb919b28f87023e79230930f46245326aef6d)) ## [4.12.13](https://github.com/ocsjs/ocsjs/compare/4.12.12...4.12.13) (2026-02-25) ### Bug Fixes * **script:** 适配 2026 上学期智慧树-新智慧学习界面 ([6f449cc](https://github.com/ocsjs/ocsjs/commit/6f449ccfc25c6d99d287ae39a6434a8a28a07f58)) ## [4.12.12](https://github.com/ocsjs/ocsjs/compare/4.12.11...4.12.12) (2025-12-29) ### Bug Fixes * 修复 github workflow 运行后 release 不显示更新内容的BUG ([96065e7](https://github.com/ocsjs/ocsjs/commit/96065e70fcea1226ffc97f6759ec4b10e0e6783c)) ## [4.12.11](https://github.com/ocsjs/ocsjs/compare/4.12.10...4.12.11) (2025-12-29) ### Bug Fixes * 添加 git workflow 输出 ([467d53d](https://github.com/ocsjs/ocsjs/commit/467d53d5f96c44cb3629723ebf471a13b65e4b49)) ## [4.12.10](https://github.com/ocsjs/ocsjs/compare/4.12.9...4.12.10) (2025-12-29) ### Bug Fixes * 修复 github workflow 运行后 release 不显示更新内容的BUG ([addb25a](https://github.com/ocsjs/ocsjs/commit/addb25a5aceb6b0ae72ef1c8718797d60f595df0)) ## [4.12.9](https://github.com/ocsjs/ocsjs/compare/4.12.8...4.12.9) (2025-12-29) ### Bug Fixes * 修复 github workflow 运行后 release 不显示更新内容的BUG ([2bacbfd](https://github.com/ocsjs/ocsjs/commit/2bacbfd0dcfe4f35d8058aee92aec07a376b96c4)) ## [4.12.8](https://github.com/ocsjs/ocsjs/compare/4.12.7...4.12.8) (2025-12-29) ### Bug Fixes * 持续修复 github workflow 运行后 release 不显示更新内容的BUG ([ecfa203](https://github.com/ocsjs/ocsjs/commit/ecfa20390b3648522a0b5383fcc6cae6d8cc4223)) ## [4.12.7](https://github.com/ocsjs/ocsjs/compare/4.12.6...4.12.7) (2025-12-29) ### Bug Fixes * 修复 github workflow 运行后 release 不显示更新内容的BUG ([676c7ed](https://github.com/ocsjs/ocsjs/commit/676c7ed2dffc2f5a547a496b9fcf20ff3ab3dc14)) ## [4.12.6](https://github.com/ocsjs/ocsjs/compare/4.12.4...4.12.6) (2025-12-29) ### Bug Fixes * **script:** 适配2025-12月智慧树新智慧学习域名 ([acc2c79](https://github.com/ocsjs/ocsjs/commit/acc2c79ccb5c1542f92f1811d84e543caebd1fe8)) * **script:** 修复超星学习通没配置题库无法跳过章节测试的BUG ([e27d978](https://github.com/ocsjs/ocsjs/commit/e27d978f3620a90e961c79f523d6cce3b58f6380)) ## [4.12.4](https://github.com/ocsjs/ocsjs/compare/4.12.3...4.12.4) (2025-12-16) ### Bug Fixes * **core:** 修复答案为圆圈数字时无法答题的BUG ([676f437](https://github.com/ocsjs/ocsjs/commit/676f4374745effe0187460560e097bc5c2e84c14)) ## [4.12.3](https://github.com/ocsjs/ocsjs/compare/4.12.0...4.12.3) (2025-12-03) ### Bug Fixes * **script:** 添加职教云资源库修复功能按钮 ([898bb3b](https://github.com/ocsjs/ocsjs/commit/898bb3b31ca18740b4cf4072369adf45150f1b3d)) * **script:** 修复中国大学MOOC新版考试无法运行的BUG ([8f0a188](https://github.com/ocsjs/ocsjs/commit/8f0a18824ec7839a288620cf9c58cd79ec11ca57)) ### Performance Improvements * **script:** 加强智慧树作业考试自动保存警告 ([7a44eb7](https://github.com/ocsjs/ocsjs/commit/7a44eb7a70ca23547f55419fa3c7e69755c0c038)) # [4.12.0](https://github.com/ocsjs/ocsjs/compare/4.11.98...4.12.0) (2025-11-27) ### Bug Fixes * 修复超星答题时无法点击快速定位到题库配置界面问题 ([855e8e9](https://github.com/ocsjs/ocsjs/commit/855e8e92a647d886a578f658aa105f8686b26e49)) ### Features * **core:** 添加软件辅助鼠标在空闲状态下执行辅助点击等功能,防止与用户抢夺控制权 ([e5b3b1e](https://github.com/ocsjs/ocsjs/commit/e5b3b1e2527a1930452164d7cd1fda77eafaa8a8)) * **core:** 添加自定义答题器,可自定义题目类型、答题器等参数,自由度更高 ([f1f7813](https://github.com/ocsjs/ocsjs/commit/f1f7813849f7c7211d630f5dacbe629d74fc5b02)) * **script:** 兼容超星繁体字判断题适配 ([a81cd57](https://github.com/ocsjs/ocsjs/commit/a81cd577b1e4d9f3b42a8bed55fab4a28d151036)) ### Performance Improvements * **script:** 优化 waitForElement API ([4cf2191](https://github.com/ocsjs/ocsjs/commit/4cf2191be1d36a61d78c878cfd45e282ad7f095b)) ## [4.11.98](https://github.com/ocsjs/ocsjs/compare/4.11.97...4.11.98) (2025-11-24) ### Bug Fixes * **core:** 修复上个版本无法打包的BUG ([089aaaf](https://github.com/ocsjs/ocsjs/commit/089aaafcfefcdb1ae69fde9cdb0e1ad7aed376d3)) ## [4.11.97](https://github.com/ocsjs/ocsjs/compare/4.11.96...4.11.97) (2025-11-24) ### Bug Fixes * **script:** 修复高级设置无法打开的BUG ([81dbc12](https://github.com/ocsjs/ocsjs/commit/81dbc1254939a5996ff384c70a49169b7189d6b4)) ## [4.11.96](https://github.com/ocsjs/ocsjs/compare/4.11.95...4.11.96) (2025-11-24) ### Bug Fixes * **script:** 修复中国大学MOOC视频题目重复答题的BUG ([7c6fd88](https://github.com/ocsjs/ocsjs/commit/7c6fd8852af43022c18e21cb6582e8388eb2587e)) ## [4.11.95](https://github.com/ocsjs/ocsjs/compare/4.11.94...4.11.95) (2025-11-23) ### Performance Improvements * aPI优化 ([f988878](https://github.com/ocsjs/ocsjs/commit/f988878dfb65c5feb95aaa771efe05eaa39944c0)) ## [4.11.94](https://github.com/ocsjs/ocsjs/compare/4.11.86...4.11.94) (2025-11-23) ### Bug Fixes * **common:** 题库缓存清空后弹窗数量文字归零 ([7a1ae7d](https://github.com/ocsjs/ocsjs/commit/7a1ae7d7508ba4d352fc4374aec8463a284cadf2)) * **script:** 修复超星默认跳转模式无法自动滚动页面到当前章节列表问题 ([5600bdd](https://github.com/ocsjs/ocsjs/commit/5600bdd761d05c081c383e34b215abdfe909fcf0)) ### Features * **core:** 对移动端进行拖动面板适配 ([d253f8b](https://github.com/ocsjs/ocsjs/commit/d253f8b77bfb7006b3cb94338602476e65aa2961)) ### Performance Improvements * **core:** 优化题库搜索题目显示,可兼容长题目例如阅读理解上下文显示 ([2e62f4c](https://github.com/ocsjs/ocsjs/commit/2e62f4c35404ef8558456aa34c1309afb059d2fa)) ## [4.11.86](https://github.com/ocsjs/ocsjs/compare/4.11.85...4.11.86) (2025-11-20) ### Bug Fixes * **script:** 修复上个版本类型报错 ([f472a0e](https://github.com/ocsjs/ocsjs/commit/f472a0e8ffc63b67ab03d56e19857a38d58d57bf)) ## [4.11.85](https://github.com/ocsjs/ocsjs/compare/4.11.82...4.11.85) (2025-11-20) ### Bug Fixes * **script:** 将超星环境调整脚本整合到内部,防止全局污染 ([b8a2a20](https://github.com/ocsjs/ocsjs/commit/b8a2a207a679eefd6647a9b058ea4f1dd39509fa)) * **script:** 修复中国大学MOOC作业答题错乱问题 ([3dfcef1](https://github.com/ocsjs/ocsjs/commit/3dfcef1074d0e8258adee054af93ebb5d9373c35)) ## [4.11.82](https://github.com/ocsjs/ocsjs/compare/4.11.81...4.11.82) (2025-11-14) ## [4.11.81](https://github.com/ocsjs/ocsjs/compare/4.11.78...4.11.81) (2025-11-14) ### Bug Fixes * **script:** 修复职教云资源库附件视频任务无法完成的BUG ([e8635e5](https://github.com/ocsjs/ocsjs/commit/e8635e5e090243ba04aee82887c9f95190e32bcc)) * **script:** 修复职教云资源库总是无法学满进度的BUG ([b06dc44](https://github.com/ocsjs/ocsjs/commit/b06dc44b5be6847dbdc238fc69fb9a732f1e6178)) ## [4.11.78](https://github.com/ocsjs/ocsjs/compare/4.11.77...4.11.78) (2025-11-13) ### Bug Fixes * **script:** 修改职教云资源库视频PPT跳转间隔为5秒 ([32504fa](https://github.com/ocsjs/ocsjs/commit/32504fa2553925fae3439a769650aaf9b6734095)) ## [4.1.76](https://github.com/ocsjs/ocsjs/compare/4.11.75...4.1.76) (2025-11-12) ### Bug Fixes * **script:** 修复职教云资源库PPT总是会返回第一页才结束的BUG ([5aab29b](https://github.com/ocsjs/ocsjs/commit/5aab29b9f67f743cef71ab2e99f3eec31d87c47a)) ## [4.11.77](https://github.com/ocsjs/ocsjs/compare/4.1.76...4.11.77) (2025-11-12) ### Bug Fixes * **script:** 修复职教云资源库PPT总是会返回第一页才结束的BUG ([e665f55](https://github.com/ocsjs/ocsjs/commit/e665f554d6b261cc176d38acc788e80f492923f1)) ## [4.1.76](https://github.com/ocsjs/ocsjs/compare/4.11.75...4.1.76) (2025-11-12) ### Bug Fixes * **script:** 修复职教云资源库PPT总是会返回第一页才结束的BUG ([5aab29b](https://github.com/ocsjs/ocsjs/commit/5aab29b9f67f743cef71ab2e99f3eec31d87c47a)) ## [4.11.75](https://github.com/ocsjs/ocsjs/compare/4.11.74...4.11.75) (2025-11-12) ### Bug Fixes * **script:** 修复超星在章节列表页面提示已进入学习界面的BUG ([9e899e8](https://github.com/ocsjs/ocsjs/commit/9e899e82c70bfaf530d42e1bdaf489e0680723c5)) ## [4.11.74](https://github.com/ocsjs/ocsjs/compare/4.11.73...4.11.74) (2025-11-12) ### Bug Fixes * **script:** 修复超星视频加载失败无法重启的BUG ([385d8a3](https://github.com/ocsjs/ocsjs/commit/385d8a32e40cc91dcb156f8c48238bedb060152b)) ## [4.11.73](https://github.com/ocsjs/ocsjs/compare/4.11.69...4.11.73) (2025-11-12) ### Bug Fixes * **script:** 使用软件辅助模拟输入填空适配智慧树智慧课程掌握度填空题 ([a57be37](https://github.com/ocsjs/ocsjs/commit/a57be3706f408f68401b80a0672d6599959aaa05)) * **script:** 添加智慧树倍速最高风险说明 ([cffcbc9](https://github.com/ocsjs/ocsjs/commit/cffcbc981f28a91964ee2f7f17f170272c875ad0)) * **script:** 修复配置多个题库其中某个超时无法答题的BUG ([dd569dc](https://github.com/ocsjs/ocsjs/commit/dd569dce9cb1697d0ee084d28f76eeafca9e52c3)), closes [#270](https://github.com/ocsjs/ocsjs/issues/270) ## [4.11.69](https://github.com/ocsjs/ocsjs/compare/4.11.64...4.11.69) (2025-11-09) ### Bug Fixes * **core:** 修复隐藏窗口按钮无法使用的BUG ([c028a8f](https://github.com/ocsjs/ocsjs/commit/c028a8f9a921a4e1ce1ca01924c6d9f6dc0befcd)) * **script:** 添加中国大学MOOC空白页自动跳转的功能 ([c562d2c](https://github.com/ocsjs/ocsjs/commit/c562d2ce4ccb2a1e2fce15adec1f56a9b913e76c)) * **script:** 修复中国大学MOOC视频答题和章节测试冲突的问题 ([a61a8e0](https://github.com/ocsjs/ocsjs/commit/a61a8e02bb3c5c7054d99a90224d9bcfacec312e)) ### Features * **script:** 适配智慧树-AI教学中心-智慧课程作业答题功能 ([3d124af](https://github.com/ocsjs/ocsjs/commit/3d124af156fa640099a7fb7d94fe5c0a684a9685)) * **script:** 修复智慧树倍速失效的BUG ([26a9a6d](https://github.com/ocsjs/ocsjs/commit/26a9a6dd73106f956acc5f6a299651522a77b24a)) ## [4.11.64](https://github.com/ocsjs/ocsjs/compare/4.11.61...4.11.64) (2025-11-07) ### Bug Fixes * **core:** 答案匹配时移除标点符号保证匹配更加准确 ([3f02c30](https://github.com/ocsjs/ocsjs/commit/3f02c307de1baef626de8c2ad83830904fc519b7)) * **scirpt:** 修复上个版本中国大学MOCC考试部分题目答题错误问题 ([bcf21b3](https://github.com/ocsjs/ocsjs/commit/bcf21b362c122d9414589d658cafc9e2ff19e398)) ## [4.11.61](https://github.com/ocsjs/ocsjs/compare/4.11.51...4.11.61) (2025-11-06) ### Bug Fixes * **script:** 修复职教云资源库点击PPT过快的BUG ([454ee5d](https://github.com/ocsjs/ocsjs/commit/454ee5d564c1e3f45728e56f779da5ea313abdcd)) * **script:** 修复智慧树新形态课程(智慧课程)某些外链无法完成并跳过的BUG ([d9bbd50](https://github.com/ocsjs/ocsjs/commit/d9bbd501049a23605e94ea899618207734341a11)) * **script:** 优化超星非整卷预览考试流畅度,添加搜索结果对于部分答页面题过程可控的菜单 ([f1f9082](https://github.com/ocsjs/ocsjs/commit/f1f9082c557007b86e22312f450b7530012fef5d)) ### Features * **script:** 支持搜索结果界面可控制答题进程功能 ([98573b5](https://github.com/ocsjs/ocsjs/commit/98573b5d00817040f859dc20fb337ed5e590687d)) * **script:** 支持中国大学MOOC自动考试功能 ([40fbfdd](https://github.com/ocsjs/ocsjs/commit/40fbfdd763496176fc65b83280b351ac4a741052)) ### Performance Improvements * **script:** 添加超星凌晨刷课文案提示 ([f17992c](https://github.com/ocsjs/ocsjs/commit/f17992c778a14f67e652f2a9faefde6e55c42f87)) * **script:** 添加多个答题页面菜单自动注册功能 ([b101424](https://github.com/ocsjs/ocsjs/commit/b10142401d8bb3a8874d6322f017881be7035d0f)) ## [4.11.51](https://github.com/ocsjs/ocsjs/compare/4.11.47...4.11.51) (2025-11-05) ### Bug Fixes * **core:** 兼容搜题结果格式A#B#C#D解析为多选题 ([0d29813](https://github.com/ocsjs/ocsjs/commit/0d29813370b06580e37bce5bb68b659618e38747)) ### Features * **script:** 兼容职教云在线课程内容单独任务点连续学习功能,添加PPT翻阅速度调整选项 ([9883897](https://github.com/ocsjs/ocsjs/commit/9883897d39c8d10729c65084b13aff58711aee9e)) ### Performance Improvements * **script:** 添加智慧树共享课作业开启前阅读须知功能 ([7e7657b](https://github.com/ocsjs/ocsjs/commit/7e7657b4ec91f43eb4f91a2b1cae5df64e98d14d)) ## [4.11.47](https://github.com/ocsjs/ocsjs/compare/4.11.22...4.11.47) (2025-11-05) ### Bug Fixes * 优化智慧树卡巴斯基文案 ([99253d6](https://github.com/ocsjs/ocsjs/commit/99253d643be17f4e73f5718c410d1da1b8a21e1b)) * **core:** 修复窗口反复闪烁的BUG ([217b5e1](https://github.com/ocsjs/ocsjs/commit/217b5e103b0c01eb7f89e12a343847430ff687fc)) * **icourse:** 修复中国大学MOOC无法完成富文本任务的BUG ([5dc3c06](https://github.com/ocsjs/ocsjs/commit/5dc3c0674290b3dd40a2e6a339b6d1574dbe644a)) * **script:** 持续修复超星跳转未完成任务点模式无法使用的BUG ([66a74a2](https://github.com/ocsjs/ocsjs/commit/66a74a234ac4fd3810fed08eda99d10e7412d4f7)) * **script:** 兼容职教云资源库新版PPT ([4919cef](https://github.com/ocsjs/ocsjs/commit/4919ceff58eef18336c8cc8c18bbf900cdd6cc23)) * **script:** 兼容智慧树-AI助教课程PPT和文档功能 ([7c456ed](https://github.com/ocsjs/ocsjs/commit/7c456edb9149b2705d3e207740a80467225560f3)) * **script:** 修复超星视频暂停后长时间才播放的BUG ([a26c03b](https://github.com/ocsjs/ocsjs/commit/a26c03b9cf54c2eb3a495ec9eec7343c623095c4)) * **script:** 修复超星跳转未完成任务点模式无法使用的BUG ([a1ed883](https://github.com/ocsjs/ocsjs/commit/a1ed883152c406b2e041f96f1e6398ae37a8e87d)) * **script:** 修复智慧树-AI助教倍速无法点击的BUG ([8ffa983](https://github.com/ocsjs/ocsjs/commit/8ffa983d23b55a21b66636789cba91a6a557d7ce)) * **script:** 修复智慧树-AI助教课程无法自动跳转小节和PPT的BUG ([fe76e0e](https://github.com/ocsjs/ocsjs/commit/fe76e0e1a08d418cd532e5ba31f5bf3810413872)) * **script:** 优化多选题纯答案无法解析的BUG ([97d8655](https://github.com/ocsjs/ocsjs/commit/97d8655ec656c6838f45a13ed0000be988862264)) * **script:** 优化智慧树音量开始播放后没有立即调整的问题 ([eff3c71](https://github.com/ocsjs/ocsjs/commit/eff3c71b9a1e5b0f1ae4b831413ddd983145dc84)) ### Features * **script:** 软件配置同步后添加脚本双击配置页面强制取消功能 ([99948e9](https://github.com/ocsjs/ocsjs/commit/99948e9c180fd93b247af8de910cb1104986ee19)) * **script:** 添加超星下一章切换后自动滚动到该元素的功能 ([c5c0726](https://github.com/ocsjs/ocsjs/commit/c5c0726075d52be3e1a8f5273d1e50a97df16566)) * **script:** 添加超星页面加载后自动滚动到当前任务点的功能 ([16232df](https://github.com/ocsjs/ocsjs/commit/16232dfe8270224141068489bf5fe51c8a31d23c)) * **script:** 添加超星章节测试答题时自动显示搜索结果 ([967c495](https://github.com/ocsjs/ocsjs/commit/967c495023b7671eee1581fae89a3795fb820568)) * **script:** 添加全局快捷菜单自动注册功能 ([11403f6](https://github.com/ocsjs/ocsjs/commit/11403f67c80e9d033e868485cff37e1517f907cd)) * **script:** 添加最小化/切屏窗口警告功能,优化文案 ([22028e4](https://github.com/ocsjs/ocsjs/commit/22028e470448b62ce6d305117831ab2a04ca7ccf)) * **script:** 优化超星章节测试答题时自动打开搜索结果功能 ([7cc0046](https://github.com/ocsjs/ocsjs/commit/7cc0046a1a8d4f9caa7a03bee37f362cc42e52fa)) * **script:** 支持中国大学MOOC视频内弹窗答题 ([8af7ec2](https://github.com/ocsjs/ocsjs/commit/8af7ec24affd9ea2db3410ee7774d4308df675f7)) ### Performance Improvements * **script:** 降低超星倍速警告阈值为2倍速,优化倍速警告 ([5aababb](https://github.com/ocsjs/ocsjs/commit/5aababba9a73ac860fe23147cd1c5a6f45d6a62b)) * **script:** 添加智慧树作业禁止同时打开多个界面答题文案 ([f1cd8cb](https://github.com/ocsjs/ocsjs/commit/f1cd8cbbc32491d3d80ef386dadd109c19d67f70)) * **script:** 修改搜题超时默认时间为2分钟,最大值为3分钟 ([a2a662f](https://github.com/ocsjs/ocsjs/commit/a2a662f339352672fc2a825912f42d1c150b455b)) ## [4.11.22](https://github.com/ocsjs/ocsjs/compare/4.11.21...4.11.22) (2025-10-16) ### Bug Fixes * **script:** 兼容2025下半年超星PPT新版无法完成的问题 ([cbe876e](https://github.com/ocsjs/ocsjs/commit/cbe876ef653ab24108b6dc471c2a6cd3231c3537)) ## [4.11.21](https://github.com/ocsjs/ocsjs/compare/4.11.20...4.11.21) (2025-10-10) ### Bug Fixes * 优化软件同步功能 ([2c1ecfc](https://github.com/ocsjs/ocsjs/commit/2c1ecfc59be7f5fb1a212a9e271eb619b90ac383)) ## [4.11.20](https://github.com/ocsjs/ocsjs/compare/4.11.19...4.11.20) (2025-10-07) ### Bug Fixes * **script:** 修复软件开启同步后,其他非自动化辅助网页里面的脚本依然会同步的问题,造成用户以为当前页面可以使用软件辅助的错觉 ([65920e2](https://github.com/ocsjs/ocsjs/commit/65920e232a7ca9eaae7c163eea2254d350357c8b)) ## [4.11.19](https://github.com/ocsjs/ocsjs/compare/4.11.18...4.11.19) (2025-09-26) ### Bug Fixes * **core:** 修复题库请求时无法传递类型为0或者false的值 ([6c8a26f](https://github.com/ocsjs/ocsjs/commit/6c8a26f62c0bd31e3e7c22fe88bc689a420b508d)) ## [4.11.18](https://github.com/ocsjs/ocsjs/compare/4.11.17...4.11.18) (2025-09-25) ### Bug Fixes * **script:** 修复上个版本智慧树会长时间不播放的BUG ([aa7c66f](https://github.com/ocsjs/ocsjs/commit/aa7c66f05a31d72223331efada8dec5d29fe88cc)) ## [4.11.17](https://github.com/ocsjs/ocsjs/compare/4.11.8...4.11.17) (2025-09-25) ### Bug Fixes * **script:** 兼容超星整卷预览禁用后的作业和答题 ([07139d7](https://github.com/ocsjs/ocsjs/commit/07139d753584449950e4d8fbc79657fedca92355)) * **script:** 修复搜索结果AI标识重复出现的BUG ([2857f2f](https://github.com/ocsjs/ocsjs/commit/2857f2ff14267eff62b4de5be75b6b7948c4eab3)) * **script:** 优化旧版智慧树视频容易黑屏的问题 ([e2535ad](https://github.com/ocsjs/ocsjs/commit/e2535ad3b92241793a14e87ed59fe133aca9b27c)) * **script:** 智慧树答题时移动页面至左上角防止点击出错 ([7f835c0](https://github.com/ocsjs/ocsjs/commit/7f835c0b49e09c617617c1c6919ec00a8f83beac)) ### Features * **script:** 添加对智慧树2025-9月份教学空间-AI智慧学习支持 ([da9d229](https://github.com/ocsjs/ocsjs/commit/da9d229843bf2bf858a711ddfb3327a99bb6091f)) ### Performance Improvements * **script:** 修复中国大学MOOC无法提交讨论的BUG,优化中国大学MOOC日志显示 ([5b1ab57](https://github.com/ocsjs/ocsjs/commit/5b1ab57984bcbea691331a6d697a06b68f6bee96)) * **script:** 优化题库配置文案和第三方连接文案 ([e66d399](https://github.com/ocsjs/ocsjs/commit/e66d3991099fa0d7d245c1cf9757a84ccff80b13)) * **script:** 优化智慧树学习提示文案 ([5bc0545](https://github.com/ocsjs/ocsjs/commit/5bc0545e576120c260809fd3af9e0c4b6dc9b667)) ## [4.11.8](https://github.com/ocsjs/ocsjs/compare/4.11.5...4.11.8) (2025-09-15) ### Features * **script:** - 添加搜题结果AI标注显示 ([7783cee](https://github.com/ocsjs/ocsjs/commit/7783cee4d1898ab1029189a8dd112f70ad45ed5a)) ## [4.11.5](https://github.com/ocsjs/ocsjs/compare/4.11.4...4.11.5) (2025-09-09) ### Bug Fixes * 尝试修复Git打包错误的BUG ([30cbbbc](https://github.com/ocsjs/ocsjs/commit/30cbbbc8726b18c6508a18ef75b500206e8e4647)) ## [4.11.4](https://github.com/ocsjs/ocsjs/compare/4.11.3...4.11.4) (2025-09-09) ### Bug Fixes * 尝试修复Git打包错误的BUG ([abb6e7b](https://github.com/ocsjs/ocsjs/commit/abb6e7b4f919de3930186da9927827513a4f3390)) ## [4.11.3](https://github.com/ocsjs/ocsjs/compare/4.11.2...4.11.3) (2025-09-09) ### Bug Fixes * 对全部项目的依赖进行版本锁定 ([f760f6d](https://github.com/ocsjs/ocsjs/commit/f760f6d9ff78c3c8366b4c5b2eebb0b957abba19)) ## [4.11.2](https://github.com/ocsjs/ocsjs/compare/4.11.1...4.11.2) (2025-09-09) ### Bug Fixes * **script:** 优化代码 ([bdbcb47](https://github.com/ocsjs/ocsjs/commit/bdbcb472fed208246333d35cc375a955c631023b)) ## [4.11.1](https://github.com/ocsjs/ocsjs/compare/4.11.0...4.11.1) (2025-09-09) ### Bug Fixes * **script:** 优化代码 ([c08eece](https://github.com/ocsjs/ocsjs/commit/c08eece1984c6c3f6eda8e086e18b7150e46d3a9)) # [4.11.0](https://github.com/ocsjs/ocsjs/compare/4.10.0...4.11.0) (2025-09-09) ### Bug Fixes * **core:** 优化Playwright软件辅助底层对接,添加软件辅助点击可视化,修复元素滚动后点击错位的BUG ([e5daaae](https://github.com/ocsjs/ocsjs/commit/e5daaaef9fc088cb6ac419ca95fedc03924da264)) ### Features * **script:** 添加超星对 jxgmxy.com 域名的支持 ([b75b714](https://github.com/ocsjs/ocsjs/commit/b75b714eeb580d5fafc5e04004bab58cc87bbb70)) * **script:** 新增智慧树2025-9月份最新课程支持-新智慧课程 ([4f3780a](https://github.com/ocsjs/ocsjs/commit/4f3780a6af864ad4f7fb6c1ca435c9dce4b87c09)) # [4.10.0](https://github.com/ocsjs/ocsjs/compare/4.9.86...4.10.0) (2025-09-01) ### Features * **script:** 添加对2025-8月份智慧树新掌握度页面进行适配 ([07cab10](https://github.com/ocsjs/ocsjs/commit/07cab10e6593611680681983b9de12c4101f07de)) * **script:** 新增智慧职教AI课程学习和作业脚本 ([27ec1fb](https://github.com/ocsjs/ocsjs/commit/27ec1fbb33772294e07e72f9e3c16b7d3ee6f629)) ## [4.9.86](https://github.com/ocsjs/ocsjs/compare/4.9.85...4.9.86) (2025-06-08) ### Bug Fixes * **script:** 添加职教云卡死提示解决方法 ([05493be](https://github.com/ocsjs/ocsjs/commit/05493be36f61db90f1fa1d007dc8ef4b63f38a71)) ## [4.9.85](https://github.com/ocsjs/ocsjs/compare/4.9.84...4.9.85) (2025-06-08) ### Bug Fixes * **script:** 修复职教云获取数据错误的问题 ([5565799](https://github.com/ocsjs/ocsjs/commit/55657994c8716647ae62f3ce510ba9f91747665d)) ## [4.9.84](https://github.com/ocsjs/ocsjs/compare/4.9.83...4.9.84) (2025-06-07) ### Bug Fixes * **script:** 修复智慧职教自定义全局倍速后导致其他脚本倍速选项也被修改的BUG ([5877a6c](https://github.com/ocsjs/ocsjs/commit/5877a6cd7a68589909544b5b04d197de694adadd)) ## [4.9.83](https://github.com/ocsjs/ocsjs/compare/4.9.81...4.9.83) (2025-06-07) ### Bug Fixes * 修改脚本更新链接到教程官网的更新教程 ([dd24c40](https://github.com/ocsjs/ocsjs/commit/dd24c4091f93a34d5fe10e4542dda426e4953ffb)) * **script:** 修复职教云卡在获取数据弹窗 ([8acf60d](https://github.com/ocsjs/ocsjs/commit/8acf60d90a8033dd7ba2c8ec1c7ae16b391eb737)) ## [4.9.81](https://github.com/ocsjs/ocsjs/compare/4.9.79...4.9.81) (2025-05-29) ### Bug Fixes * **script:** 兼容职教云添加章节层级名字后无法获取数据的问题 ([afa37cf](https://github.com/ocsjs/ocsjs/commit/afa37cf4ed47da77a2cc2711b240b449c69f7a2a)) * **script:** 添加智慧树智慧课程跳转模式选择 ([822ab99](https://github.com/ocsjs/ocsjs/commit/822ab995421c1abe1b6997f5a8da3d807a5de3cd)) ## [4.9.79](https://github.com/ocsjs/ocsjs/compare/4.9.78...4.9.79) (2025-05-24) ### Bug Fixes * **script:** 修复职教云章节列表前面有章节序号时无法读取数据的BUG ([6ffd21e](https://github.com/ocsjs/ocsjs/commit/6ffd21e42c0c87bddd5d54c396465647985bb6aa)) ## [4.9.78](https://github.com/ocsjs/ocsjs/compare/4.9.77...4.9.78) (2025-05-18) ### Bug Fixes * **script:** 修复职教云获取数据时跳转其他网页的BUG ([4237698](https://github.com/ocsjs/ocsjs/commit/4237698017e7d1bc03a3f91d91c2ad0f0280e9bb)) ## [4.9.77](https://github.com/ocsjs/ocsjs/compare/4.9.76...4.9.77) (2025-05-11) ### Bug Fixes * 修复职教云0资源库无法下一章的BUG,兼容职教云部分PPT任务点 ([69230c4](https://github.com/ocsjs/ocsjs/commit/69230c4fc73af9f5e23ae908a66eeb58581d6063)) ## [4.9.76](https://github.com/ocsjs/ocsjs/compare/4.9.71...4.9.76) (2025-05-09) ### Bug Fixes * **script:** 优化智慧树翻转课(校内课) ([af04c36](https://github.com/ocsjs/ocsjs/commit/af04c36824516f342152f96559af31101745187b)) ### Features * 优化支持职教云最新版页面,支持自动切换下一章(包含大章节切换) ([c73a1cc](https://github.com/ocsjs/ocsjs/commit/c73a1ccb2e66b99c4f9cb6b93efea7c1940cfc81)) ### Performance Improvements * 持续优化软件辅助安装提示 ([90d9cd1](https://github.com/ocsjs/ocsjs/commit/90d9cd13d7cce7fccdb5b385d2dca98ab912591b)) * 优化倍速设置选项,减少不必要的选项数量 ([566ff84](https://github.com/ocsjs/ocsjs/commit/566ff845cc04dc45f8a8bcb47bcb88de274b4690)) * 优化超星随机答题文案,提示只有在搜不到答案的时候才使用随机答题 ([3e07067](https://github.com/ocsjs/ocsjs/commit/3e07067f6d86d35c99c0359fc202152905468109)) ## [4.9.71](https://github.com/ocsjs/ocsjs/compare/4.9.70...4.9.71) (2025-05-07) ### Bug Fixes * **script:** 修复新版智慧树无法关闭弹窗的BUG ([7b356d3](https://github.com/ocsjs/ocsjs/commit/7b356d3b79f4e44111283e5579ae25d2a2bb3cc3)) ## [4.9.70](https://github.com/ocsjs/ocsjs/compare/4.9.59...4.9.70) (2025-05-05) ### Bug Fixes * **script:** 修复超星搜题时无法读取选项内图片链接的BUG ([d57cb8f](https://github.com/ocsjs/ocsjs/commit/d57cb8fbd7fd191bad3deb05b988d01651f67f28)) * **script:** 修复超星章节测试多行题目第二行存在题目类型时无法解析题目的BUG ([3cdfa3c](https://github.com/ocsjs/ocsjs/commit/3cdfa3c8ccd1c25053c83a252a0b973c10c608a5)) * **script:** 修复超星自动答题时填空题选项读取到额外冗余文字的BUG:(段落格式,字号,字体) ([f95f2ea](https://github.com/ocsjs/ocsjs/commit/f95f2ea4d2e9f69f8053a2e18ab1d0b2e7cd7442)) * **script:** 修复智慧树-智慧课程无法自动切换下一个的BUG ([b02d864](https://github.com/ocsjs/ocsjs/commit/b02d864eacd667428da73a6db68d810e55e4f39d)) * **script:** 修复智慧树-智慧课程无法自动下一章的BUG ([48b8567](https://github.com/ocsjs/ocsjs/commit/48b856794b46ab64db102a4a1563cce3e21593ea)) * **script:** 修复智慧树-智慧职教无法学习文本任务点的问题,新增智慧职教已完成任务点过滤功能 ([aaf7774](https://github.com/ocsjs/ocsjs/commit/aaf7774215a703569ea80458f10937c4d9e1c330)) * **script:** 优化智慧树-智慧课程播放逻辑 ([8ea5248](https://github.com/ocsjs/ocsjs/commit/8ea5248851a61b1355b49d9ca26f6d571b8688d2)) ### Features * **script:** 对题库配置做唯一化处理 ([4bc0112](https://github.com/ocsjs/ocsjs/commit/4bc011222e16723e6cdcbb123789891a6a0d80fc)) * **script:** 适配智慧树智慧课程学习和作业(包括AI助教和智慧课程),适配智慧树新版课程页面,适配智慧课程掌握度自动答题 ([7cd9dfd](https://github.com/ocsjs/ocsjs/commit/7cd9dfd28f02b1316283b4b331525939ec546fa7)) * **script:** 支持智慧树智慧课程链接任务点 ([285140f](https://github.com/ocsjs/ocsjs/commit/285140ff3c73aae41ffa7213d3daf3ca76e4c72a)) ### Performance Improvements * **script:** 修改软件辅助启动提示 ([0021b5d](https://github.com/ocsjs/ocsjs/commit/0021b5dd85fd5dd9b0f45dfd94175a7c3b34dd68)) ## [4.9.59](https://github.com/ocsjs/ocsjs/compare/4.9.57...4.9.59) (2025-03-31) ### Bug Fixes * typo ([57d4077](https://github.com/ocsjs/ocsjs/commit/57d4077bb0951648fba6fe4a9f30cee7a4567a05)) ### Features * 添加超星 ccqmxx.com 域名适配支持 ([79eb947](https://github.com/ocsjs/ocsjs/commit/79eb94744e442a73022d59d1dca67b13035f30aa)) * **script:** 添加题库配置相同检测,如果相同则提示 ([9acb07e](https://github.com/ocsjs/ocsjs/commit/9acb07e0bf5fbf6cf0d6d02f47fdce7a21e4a235)) ## [4.9.57](https://github.com/ocsjs/ocsjs/compare/4.9.55...4.9.57) (2025-03-14) ### Features * 添加智慧树-智慧课程掌握度答题功能 ([316eb6d](https://github.com/ocsjs/ocsjs/commit/316eb6d6b6cd4d3fe98fa13be6833779fb77fdf2)) ### Performance Improvements * 添加智慧树-智慧课程掌握度学习提示 ([908df79](https://github.com/ocsjs/ocsjs/commit/908df793965358d19c5cf87b792a7f47f2446a7d)) ## [4.9.55](https://github.com/ocsjs/ocsjs/compare/4.9.54...4.9.55) (2025-03-14) ### Bug Fixes * 超星任务点检测优化 ([ce6c3ac](https://github.com/ocsjs/ocsjs/commit/ce6c3ac829f21b075592d0ad7cea7d670c7e132c)) ## [4.9.54](https://github.com/ocsjs/ocsjs/compare/4.9.52...4.9.54) (2025-03-13) ### Bug Fixes * 优化智慧树智慧课程视频检测,修复智慧树智慧课程检测不到任务点问题 ([2f3777d](https://github.com/ocsjs/ocsjs/commit/2f3777d4d072da3163ed21cb630488f3fd89ed61)) ## [4.9.52](https://github.com/ocsjs/ocsjs/compare/4.9.51...4.9.52) (2025-03-12) ### Bug Fixes * 修复 4.9.51 版本题库配置解析问题 ([b3d3e32](https://github.com/ocsjs/ocsjs/commit/b3d3e32e8b61f1bef7e7b94d631eccd47838a6dc)) ## [4.9.51](https://github.com/ocsjs/ocsjs/compare/4.9.46...4.9.51) (2025-03-12) ## [4.9.46](https://github.com/ocsjs/ocsjs/compare/4.9.45...4.9.46) (2024-12-22) ### Performance Improvements * 优化智慧树考试提示文案 ([b1e92a3](https://github.com/ocsjs/ocsjs/commit/b1e92a31b0a667d7f3cc1ae9cf462ba0277dcc2b)) ## [4.9.45](https://github.com/ocsjs/ocsjs/compare/4.9.44...4.9.45) (2024-12-21) ### Bug Fixes * **script:** 修复上个版本图片链接处理导致的无法选择BUG ([baaed06](https://github.com/ocsjs/ocsjs/commit/baaed067f08f164821019c7b71287305f842b4d9)) ## [4.9.44](https://github.com/ocsjs/ocsjs/compare/4.9.43...4.9.44) (2024-12-21) ### Bug Fixes * **core:** 修复图片题解析错误导致的URL重复,图片重复显示等问题 ([f5cc59f](https://github.com/ocsjs/ocsjs/commit/f5cc59f8e6a87a7144d0540f64a4a586594c78ef)) ## [4.9.43](https://github.com/ocsjs/ocsjs/compare/4.9.42...4.9.43) (2024-12-20) ## [4.9.42](https://github.com/ocsjs/ocsjs/compare/4.9.41...4.9.42) (2024-12-06) ## [4.9.41](https://github.com/ocsjs/ocsjs/compare/4.9.40...4.9.41) (2024-12-03) ## [4.9.40](https://github.com/ocsjs/ocsjs/compare/4.9.39...4.9.40) (2024-11-10) ### Bug Fixes * **script:** 优化职教云资源库无法答题的问题 ([c94c41e](https://github.com/ocsjs/ocsjs/commit/c94c41eb46ace8fa63d0296d8199941a58a46314)) ## [4.9.39](https://github.com/ocsjs/ocsjs/compare/4.9.37...4.9.39) (2024-11-09) ### Bug Fixes * 修复 select 选择框显示问题 ([9f5b741](https://github.com/ocsjs/ocsjs/commit/9f5b741147cb100848517ebd4d7d131159a3689e)) ## [4.9.37](https://github.com/ocsjs/ocsjs/compare/4.9.36...4.9.37) (2024-11-06) ### Features * 添加超星自动跳转未完成任务点功能 ([24927e6](https://github.com/ocsjs/ocsjs/commit/24927e63e765d990ef6f0971b40121f8507463fd)) ## [4.9.36](https://github.com/ocsjs/ocsjs/compare/4.9.35...4.9.36) (2024-11-06) ### Bug Fixes * **script:** 修复选择输入框中默认值匹配错误导致的超星自动保存选择'save',但是默认值依旧是80的问题 ([a2b639f](https://github.com/ocsjs/ocsjs/commit/a2b639fd5c22ef94a5d352cf6a62d79af6261ac7)) ## [4.9.35](https://github.com/ocsjs/ocsjs/compare/4.9.34...4.9.35) (2024-10-21) ## [4.9.34](https://github.com/ocsjs/ocsjs/compare/4.9.33...4.9.34) (2024-10-18) ## [4.9.33](https://github.com/ocsjs/ocsjs/compare/4.9.32...4.9.33) (2024-10-17) ## [4.9.32](https://github.com/ocsjs/ocsjs/compare/4.9.31...4.9.32) (2024-10-09) ## [4.9.31](https://github.com/ocsjs/ocsjs/compare/4.9.29...4.9.31) (2024-09-13) ## [4.9.29](https://github.com/ocsjs/ocsjs/compare/4.9.28...4.9.29) (2024-08-10) ### Bug Fixes * **scirpt:** 修复资源库最新版链接类名改变问题 ([341b37f](https://github.com/ocsjs/ocsjs/commit/341b37f42c815fc7e401e23636a2388ab592cbe5)) ## [4.9.28](https://github.com/ocsjs/ocsjs/compare/4.9.25...4.9.28) (2024-07-14) ### Bug Fixes * **script:** 解决超星部分套壳网站无法自动下一章的BUG ([e8f05b6](https://github.com/ocsjs/ocsjs/commit/e8f05b6b8741d88903755910d072020fcf6a695a)) * **script:** 优化对超星视频下载错误的处理,将自动跳过错误视频 ([b4f32c8](https://github.com/ocsjs/ocsjs/commit/b4f32c8cccf9a7d97107d2570b2d36e3e187809f)) ### Performance Improvements * **script:** 优化版本更新验证,不符合版本书写规范的话向用户提示报错 ([70dd13b](https://github.com/ocsjs/ocsjs/commit/70dd13b5c5cf58ccaf09cdc513c31985445023fe)) ## [4.9.25](https://github.com/ocsjs/ocsjs/compare/4.9.24...4.9.25) (2024-07-13) ### Bug Fixes * **script:** 新增智慧职教 webtrn.cn 套壳网站支持,新增对30分钟学习弹窗的关闭功能 ([b17a19f](https://github.com/ocsjs/ocsjs/commit/b17a19f0c87e94579d2abba09afdadd98bce8e7b)) ## [4.9.24](https://github.com/ocsjs/ocsjs/compare/4.9.23...4.9.24) (2024-06-25) ## [4.9.23](https://github.com/ocsjs/ocsjs/compare/4.9.21...4.9.23) (2024-06-25) ### Bug Fixes * **script:** 修复职教云资源库,课件类型为NAN的BUG ([8b8768e](https://github.com/ocsjs/ocsjs/commit/8b8768e311b41717cb7df9d73417cf2a700c107a)) * **script:** 修复职教云资源库重复下一章的BUG ([b566a5c](https://github.com/ocsjs/ocsjs/commit/b566a5ccd66b285b994f57f05dc6e454b21ed059)) ## [4.9.21](https://github.com/ocsjs/ocsjs/compare/4.9.20...4.9.21) (2024-06-19) ### Bug Fixes * **core:** 修复题库配置 AnswererWrapper 自定义字段解析 handler 函数调用两次的BUG ([1e15377](https://github.com/ocsjs/ocsjs/commit/1e15377e0ed7b51f9777221607fa047800e7f765)) * **script:** 修复搜索时换行被删除导致,两行合并造成的文字合并,导致搜索引擎可能出现的的搜索问题 ([b318c1e](https://github.com/ocsjs/ocsjs/commit/b318c1e227ebc0fe06c567139e1a09f307c106ec)) ## [4.9.20](https://github.com/ocsjs/ocsjs/compare/4.9.19...4.9.20) (2024-06-13) ### Features * **script:** 新增 中国大学MOOC 考试功能 ([7286fa4](https://github.com/ocsjs/ocsjs/commit/7286fa47105a1ab8e06f7b4cca1c71354e59d015)) ## [4.9.19](https://github.com/ocsjs/ocsjs/compare/4.9.18...4.9.19) (2024-06-05) ### Bug Fixes * **core:** 修复使用精确匹配时,脚本的多选题搜索结果会全部显示已完成的BUG ([65cfc48](https://github.com/ocsjs/ocsjs/commit/65cfc4832bd1bb070169b02ba7672f3aa347c494)) * **script:** 添加完成率百分比符号 ([beec370](https://github.com/ocsjs/ocsjs/commit/beec3707cf1de48685a7cbb899d70b0e0003ce98)) ## [4.9.18](https://github.com/ocsjs/ocsjs/compare/4.9.17...4.9.18) (2024-06-04) ### Bug Fixes * **script:** 修复全域名版本无法显示BUG ([6d52ec4](https://github.com/ocsjs/ocsjs/commit/6d52ec4983ce452fc072774abc358936380f4166)) ## [4.9.17](https://github.com/ocsjs/ocsjs/compare/4.9.16...4.9.17) (2024-05-24) ### Bug Fixes * **script:** 修复上个版本任务点检测不到的BUG ([8bf7551](https://github.com/ocsjs/ocsjs/commit/8bf7551b41cd543bd3f345ec63320c8fa661a4e6)) ## [4.9.16](https://github.com/ocsjs/ocsjs/compare/4.9.12...4.9.16) (2024-05-23) ### Bug Fixes * **script:** 优化超星人脸识别检测警告 ([311d8ad](https://github.com/ocsjs/ocsjs/commit/311d8adaf8c46eb66bb6373dae645ceb8856a70b)) * **script:** 优化超星题库配置为空警告 ([9257770](https://github.com/ocsjs/ocsjs/commit/92577708f5737b445c986090470d78bd58d27999)) ### Features * **script:** 新增对超星链接任务点,音频PPT任务点的支持,优化代码,删除视频错误检测功能。 ([e90f1bb](https://github.com/ocsjs/ocsjs/commit/e90f1bbc3f13b966b020303e0498bd73e65844fd)) ## [4.9.12](https://github.com/ocsjs/ocsjs/compare/4.9.8...4.9.12) (2024-05-11) ### Bug Fixes * **script:** 添加超星积分课的使用提示 ([ea74563](https://github.com/ocsjs/ocsjs/commit/ea7456324d60d41df699946207a6b1b73bf7b516)) * **script:** 修复超星课程,章节测试偶尔出现卡死的BUG ([24b499d](https://github.com/ocsjs/ocsjs/commit/24b499d36c143810a49d6bb000e7d345317ac131)) * **script:** 修复超星课程进入时需要人脸识别,但是疯狂跳转新版本导致的人脸识别疯狂刷新的BUG ([4fb764b](https://github.com/ocsjs/ocsjs/commit/4fb764bd53411006c435a591167cb48bb0ec4a74)) * **script:** 修复超星英语判断题的文本BUG ([5c785ab](https://github.com/ocsjs/ocsjs/commit/5c785ab3828897604d861fb521a2b17c9afbd4b4)) ## [4.9.8](https://github.com/ocsjs/ocsjs/compare/4.9.7...4.9.8) (2024-05-10) ## [4.9.7](https://github.com/ocsjs/ocsjs/compare/4.9.6...4.9.7) (2024-05-10) ### Bug Fixes * **script:** 修复 '"@ocsjs/core"' has no exported member 'createQuestionTitleExtra'. BUG ([9f06abe](https://github.com/ocsjs/ocsjs/commit/9f06abee6747192c9fbe4b29ae82003e56481734)) ## [4.9.6](https://github.com/ocsjs/ocsjs/compare/4.9.4...4.9.6) (2024-05-10) ### Bug Fixes * 更新 easy-us API ([0150b49](https://github.com/ocsjs/ocsjs/commit/0150b49ca661920a6a9c723a37af237c53d62b95)) * 删除无用API ([8c8425f](https://github.com/ocsjs/ocsjs/commit/8c8425f337c7f001bbdc3bb74ef8e3849c413e65)) * **script:** 修复英语前缀 【True or False】 的BUG ([01e1418](https://github.com/ocsjs/ocsjs/commit/01e141884cdd4b9ef65a6c489286aacc99cb2198)) * **script:** 优化题库配置的开启和关闭按钮 ([b9197c6](https://github.com/ocsjs/ocsjs/commit/b9197c6eab4558061d8167b4fe7d5b1098bf83a4)) * **script:** 优化题库配置的开启和关闭按钮 ([39b3046](https://github.com/ocsjs/ocsjs/commit/39b304671fb2991d7fbeac2ee1fabb757ebc50c4)) ## [4.9.4](https://github.com/ocsjs/ocsjs/compare/4.9.3...4.9.4) (2024-05-04) ### Bug Fixes * **script:** 修复无法移动面板至边缘导致自动登录失效的BUG ([a05d0a1](https://github.com/ocsjs/ocsjs/commit/a05d0a1cb1e354dccbb52a567b858a4eb7276e63)) ## [4.9.3](https://github.com/ocsjs/ocsjs/compare/4.9.2...4.9.3) (2024-05-04) ## [4.9.2](https://github.com/ocsjs/ocsjs/compare/4.9.1...4.9.2) (2024-05-04) ### Bug Fixes * 导出 $elelment 对象,便于可修改 $modal 第二个参数 ([13a1b6d](https://github.com/ocsjs/ocsjs/commit/13a1b6d9d8e211e9b92558b950fbb0e96b0d6174)) ## [4.9.1](https://github.com/ocsjs/ocsjs/compare/4.9.0...4.9.1) (2024-05-04) # [4.9.0](https://github.com/ocsjs/ocsjs/compare/4.8.30...4.9.0) (2024-05-03) ### Features * **all:** 优化API,分离UI层为新项目 easy-us ([ecc4ff6](https://github.com/ocsjs/ocsjs/commit/ecc4ff6a56577d40a59aea47418138ad9b6c04f1)) ## [4.8.30](https://github.com/ocsjs/ocsjs/compare/4.8.29...4.8.30) (2024-05-01) ### Bug Fixes * **script:** 解决超星视频播放时出现: The play() request was interrupted by a call to pause() BUG ([4791bd8](https://github.com/ocsjs/ocsjs/commit/4791bd8c4a8f06890079bf748c0f547fd728ec94)) ### Features * **script:** 新增对职教云资源库的 spocjob 作业的支持 ([007d39a](https://github.com/ocsjs/ocsjs/commit/007d39af4fbfb474e47f180b8670479c0d7e9b4b)) ## [4.8.29](https://github.com/ocsjs/ocsjs/compare/4.8.27...4.8.29) (2024-04-20) ## [4.8.27](https://github.com/ocsjs/ocsjs/compare/4.8.26...4.8.27) (2024-03-31) ## [4.8.26](https://github.com/ocsjs/ocsjs/compare/4.8.25...4.8.26) (2024-03-29) ### Features * **script:** 新增超星 cugbonline.cn 域名支持 ([359425a](https://github.com/ocsjs/ocsjs/commit/359425a92b93fc7fd93a3d55b69526d29db38d22)) ## [4.8.25](https://github.com/ocsjs/ocsjs/compare/4.8.24...4.8.25) (2024-03-28) ### Bug Fixes * **script:** 修复添加额外菜单栏后,窗口无法正常最大化/最小化的BUG ([22c45bc](https://github.com/ocsjs/ocsjs/commit/22c45bc15d31ab1999761182d6ddbffec499e0ef)) ## [4.8.24](https://github.com/ocsjs/ocsjs/compare/4.8.13...4.8.24) (2024-03-28) ### Bug Fixes * **core:** 修复窗口隐藏/显示 快捷键无效的BUG ([8002c82](https://github.com/ocsjs/ocsjs/commit/8002c8246339f936cfbafc677f9c053a6e0cfcbc)) * **core:** 修复单选题答案回调内容不一致的BUG ([4265263](https://github.com/ocsjs/ocsjs/commit/4265263821e781cd3636c275120f8b987c63bbbf)) * **core:** 修复设置超出最大最小值,导致变更时,本地的值没有同步导致刷新后依然没有发生变化的BUG ([0b05507](https://github.com/ocsjs/ocsjs/commit/0b05507875340c57689b5825f2d5efd8a4e6ebef)) * **core:** 修复新版答题算法中搜题线程参数无效的BUG,始终只有一个搜题线程在运行 ([9e052f8](https://github.com/ocsjs/ocsjs/commit/9e052f85909b947f8fd53851233cdee93c47ca9a)) * **core:** 修复已经在暂停状态,但是依然在答题的BUG ([3f04e21](https://github.com/ocsjs/ocsjs/commit/3f04e21fdaef3ee5972aa259399e5384b11f51a7)) * **core:** 优化搜索结果的显示BUG,已搜到答案的题目显示未匹配到正确答案的BUG,以及答题进度不一致的显示BUG ([2fd9a72](https://github.com/ocsjs/ocsjs/commit/2fd9a72fb493bfe26593055346bcfe089feda8b9)) * **script:** 全面优化答题速度,优化存在题库缓存时无需执行搜题间隔等待,优化特定网课勾选答案时需要停顿,正常网课无需停顿的情况,极大加速了答案勾选过程。 ([1ca3448](https://github.com/ocsjs/ocsjs/commit/1ca34480d7010c176f79d471f2ba264716782561)) * **script:** 修复智慧树答题时题库缓存未正确存储的BUG ([978d927](https://github.com/ocsjs/ocsjs/commit/978d927f71b1f7e764d97257206615b0f7719791)) * **script:** 修复重新答题按钮点击后,答题完成后的一些操作依然在进行的BUG ([e7148a3](https://github.com/ocsjs/ocsjs/commit/e7148a368144ab55b6708c4e03d23f7de6262b8b)) ### Features * **core:** 新增额外菜单栏功能,新增超星额外菜单栏快捷页面跳转按钮 ([c2066a0](https://github.com/ocsjs/ocsjs/commit/c2066a056439270f41a2f8cb0eee61a73690717e)) * **script:** 新增【职教云】资源库作业支持 ([3cf9968](https://github.com/ocsjs/ocsjs/commit/3cf996824d1ff647499b3f8a12e6084eaf7ddf2d)) ## [4.8.13](https://github.com/ocsjs/ocsjs/compare/4.8.11...4.8.13) (2024-03-14) ### Bug Fixes * **core:** 全局设置中部分元素的值为空时恢复默认值 ([9d539d0](https://github.com/ocsjs/ocsjs/commit/9d539d045df7db40035cf00eba157f10b389e623)) * **core:** 优化答题模块算法 ([545d9bc](https://github.com/ocsjs/ocsjs/commit/545d9bc5c98ff1b342789de9ec681e2e34efed0e)) ## [4.8.11](https://github.com/ocsjs/ocsjs/compare/4.8.8...4.8.11) (2024-03-13) ### Bug Fixes * **script:** 修复请求记录开启后无法读取内容的BUG ([31d2a85](https://github.com/ocsjs/ocsjs/commit/31d2a854e9ccbbef22fa1ad0ca0256f4fe493561)) ### Features * **script:** 对 TikuAdapter 做适配提示 ([cf2fe10](https://github.com/ocsjs/ocsjs/commit/cf2fe109faf5990086de68fb1f7213f0d4807500)) ## [4.8.8](https://github.com/ocsjs/ocsjs/compare/4.8.7...4.8.8) (2024-03-05) ### Bug Fixes * **script:** 优化超星视频重复播放时没有重置视频进度的问题 ([c167ff0](https://github.com/ocsjs/ocsjs/commit/c167ff094ffe4287002cd3e1f0b08b92a0a82ceb)) ## [4.8.7](https://github.com/ocsjs/ocsjs/compare/4.8.6...4.8.7) (2024-03-01) ### Bug Fixes * **script:** 修复2倍速被删除的BUG ([a861685](https://github.com/ocsjs/ocsjs/commit/a861685bea01f3bedad32c2828f2e2d3f38f94dd)) ## [4.8.6](https://github.com/ocsjs/ocsjs/compare/4.8.5...4.8.6) (2024-02-28) ### Bug Fixes * **script:** 修改软件辅助返回字段 ([270f104](https://github.com/ocsjs/ocsjs/commit/270f104d4b490c55e2b6fc5d0b58d4cf5518aa80)) ## [4.8.5](https://github.com/ocsjs/ocsjs/compare/4.8.0...4.8.5) (2024-02-28) ### Bug Fixes * **script:** 修复智慧树无法使用软件辅助,无法自动答题看视频的BUG ([a3cdb6f](https://github.com/ocsjs/ocsjs/commit/a3cdb6f13904879e0d3effd3874c567f34f468f2)) ### Features * **core:** 新增答案匹配模式选项 ([aab04af](https://github.com/ocsjs/ocsjs/commit/aab04af086a50aa72d2992087ecba69f60ad1447)) * **script:** 添加题库配置自动读取剪贴板功能 ([fe19557](https://github.com/ocsjs/ocsjs/commit/fe19557c7cc91c7909a62c3b1dc93aac5c641106)) ### Performance Improvements * **core:** 优化 tooltip ([9655442](https://github.com/ocsjs/ocsjs/commit/96554423807d329e712b08998798d3ca5a309e7f)) # [4.8.0](https://github.com/ocsjs/ocsjs/compare/4.7.46...4.8.0) (2024-02-26) ### Bug Fixes * **core:** 新答题器的旧字段搜索结果显示修改 ([d8511a0](https://github.com/ocsjs/ocsjs/commit/d8511a023f87d4e8c10c4aabacf4d5118ac75177)) * **core:** 修复当数字输入框是空格后无法正确读取的BUG ([fa7f2d5](https://github.com/ocsjs/ocsjs/commit/fa7f2d593fb42897352958d5a48c78a147671af1)) * **core:** 修复新版答题器当线程为1时,无法搜题的BUG ([bd2bd53](https://github.com/ocsjs/ocsjs/commit/bd2bd5384e788f4f1c521e766a1f9c25c90b96a7)) * **core:** 优化答题器算法 ([d29c74c](https://github.com/ocsjs/ocsjs/commit/d29c74c2ab2a470630567fad9a47605baa3e2d99)) * **core:** 优化跨域通信只在顶部页面刷新时删除临时监听变量,优化代码添加注释 ([0f07dc4](https://github.com/ocsjs/ocsjs/commit/0f07dc47500f8b527f1612731c2cf0d5fe8e36aa)) * **core:** 优化搜索结果显示,内部算法报错依然可以显示搜索结果 ([e002e3d](https://github.com/ocsjs/ocsjs/commit/e002e3d45dd4609d9401b9534ab8be396e979a4a)) * **script:** 添加超星英文判断题的冗余删除 ([35b8f07](https://github.com/ocsjs/ocsjs/commit/35b8f07990d365ee239056e2281c0390200809f5)) * **script:** 添加题库缓存开关 ([35226d3](https://github.com/ocsjs/ocsjs/commit/35226d34298fc0b39b773f612f99f39d8169b4dd)) * **script:** 修复解除复制粘贴限制功能无效的BUG ([3f560a8](https://github.com/ocsjs/ocsjs/commit/3f560a83bd472c073c7de91e51720b78119fdf06)) * **script:** 修复通知时间很短就消失的BUG ([cda009f](https://github.com/ocsjs/ocsjs/commit/cda009f4679fb7cfdce121f2379b68c0d46008b1)) * **script:** 优化超星闯关模式以及解锁模式的重复进入检测算法 ([b148232](https://github.com/ocsjs/ocsjs/commit/b14823248aa4b5620756a4525d56a5f5aacbc7c8)) * **script:** 优化超星英文判断题的解析 ([78be04e](https://github.com/ocsjs/ocsjs/commit/78be04e3dfb76fda8d5cbe5d850a0131a7d1bb08)) ### Features * 新增 answer_separator 答案分隔符设置 ([a742f80](https://github.com/ocsjs/ocsjs/commit/a742f808f43c9e6d9be010258b7d36cde83caf0e)) * **script:** 添加超星 gdhkmooc.com 域名支持 ([c63e6d6](https://github.com/ocsjs/ocsjs/commit/c63e6d69e903483754895c4b14e2266f07788ce5)) * **script:** 添加超星完成全部任务点后重新从开头学习功能 ([86454d3](https://github.com/ocsjs/ocsjs/commit/86454d31f227940298c866759e071622c76097bb)) * **script:** 添加对智慧树自动保存答案的解释 ([53f0846](https://github.com/ocsjs/ocsjs/commit/53f0846b3134453cf8d9bf7c11d777407e7a111a)) * **script:** 添加开发人员调试:显示Tab变量功能 ([a779b35](https://github.com/ocsjs/ocsjs/commit/a779b35f5e422d30a81d551db1ba84528b052d74)) * **script:** 添加职教云 flv 格式支持 ([08d6719](https://github.com/ocsjs/ocsjs/commit/08d6719695d6ca293cb3333197eaf4981195678f)) * **script:** 添加职教云的作业考试【客观填空题】的支持 ([92c5133](https://github.com/ocsjs/ocsjs/commit/92c51332130b31a2e1ece5deabc023a90fde57c3)) * **script:** 添加智慧职教MOOC的单行填空题支持 ([5361ec4](https://github.com/ocsjs/ocsjs/commit/5361ec40a59d0cdb399324dcec484468b5e8d639)) * **script:** 新增 $message 跨域调用功能,新增 $modal duration 参数,修复由于删除cors监听key导致的 undefined BUG ([320903f](https://github.com/ocsjs/ocsjs/commit/320903fdd9e1b82d47ae9ab475aae0eac0d0e2bc)) * **script:** 新增新版职教云作业填空题支持 ([a09dcac](https://github.com/ocsjs/ocsjs/commit/a09dcac71cda6e95a24f6ce5e22ba6c3830f0c3b)) * **script:** 新增职教云考试支持,txt文档查看支持 ([f95e8f8](https://github.com/ocsjs/ocsjs/commit/f95e8f84e03a45dd4afee2f517800512b112efa1)) * **script:** 优化题库配置文案,优化超星文案,并添加运行时的反馈消息提示 ([d61d3ee](https://github.com/ocsjs/ocsjs/commit/d61d3ee051f6c5b1f6af849987776592159d21bb)) * **script:** 在拓展应用中添加OCS全部配置的导入导出功能 ([0237e88](https://github.com/ocsjs/ocsjs/commit/0237e88c1157201911133405058c2b9eb3a43be6)) ### Performance Improvements * **core:** update remote-playwright-log ([3578781](https://github.com/ocsjs/ocsjs/commit/35787819b390d2bed230c79ad30283456a7d57b2)) * **script and core:** 优化软件辅助类型提示以及代码 ([3394ada](https://github.com/ocsjs/ocsjs/commit/3394ada9acb234ee987ae58ac75c2cf96eacde21)) * **script:** 简化 $console 生成的堆栈信息,减少存储量 ([2669525](https://github.com/ocsjs/ocsjs/commit/2669525fd942fa9a2e34e8166785e36891e1c5a3)) * **script:** 添加更多的倍速选项 ([adf7e3d](https://github.com/ocsjs/ocsjs/commit/adf7e3dfb03838cd5dfaf3ebe1b55691685c5a09)) ## [4.7.46](https://github.com/ocsjs/ocsjs/compare/4.7.35...4.7.46) (2023-12-20) ### Bug Fixes * **scrpt:** 优化题库配置在软件上的域名检测 ([04fba87](https://github.com/ocsjs/ocsjs/commit/04fba87036a8a7884fb40f7f5ab816778a453744)) ### Performance Improvements * **core:** type update ([3fd3864](https://github.com/ocsjs/ocsjs/commit/3fd3864ea536038f616f2e357d1eee377a130d03)) ## [4.7.35](https://github.com/ocsjs/ocsjs/compare/4.7.34...4.7.35) (2023-12-19) ### Features * **script:** 修复超星人脸验证时疯狂刷新的问题,新增超星人脸验证通知功能 ([e75786c](https://github.com/ocsjs/ocsjs/commit/e75786c890fc0db560a75db406195ead7ad81783)) ### Performance Improvements * **script:** 添加题库停用状态的开启提示 ([8e6aaed](https://github.com/ocsjs/ocsjs/commit/8e6aaedb13a3b3db51ffd0737ef037d628dc7033)) ## [4.7.34](https://github.com/ocsjs/ocsjs/compare/4.7.33...4.7.34) (2023-12-13) ### Bug Fixes * **script:** 优化 TikuAdapter 提示 ([43622ba](https://github.com/ocsjs/ocsjs/commit/43622ba2b48ead377c03fb52ee7c2778256b8002)) ## [4.7.33](https://github.com/ocsjs/ocsjs/compare/4.7.32...4.7.33) (2023-12-13) ### Bug Fixes * **script:** 修复部分脚本管理器无法读取 [@connect](https://github.com/connect) 头部元数据的问题 ([69cec2f](https://github.com/ocsjs/ocsjs/commit/69cec2f98ee5835baceb7796284d654ffbad5720)) ## [4.7.32](https://github.com/ocsjs/ocsjs/compare/4.7.30...4.7.32) (2023-12-13) ### Bug Fixes * **build:** 修复全域名通用版本不带官方域名的BUG ([ce042af](https://github.com/ocsjs/ocsjs/commit/ce042af9d4deb95753a4c76b94a9d4baea7f8a2a)) ### Performance Improvements * **script:** 添加 TikuAdapter 配置域名白名单提示 ([24d8026](https://github.com/ocsjs/ocsjs/commit/24d802626c5cf8f763f463b1d598227454d8e1e3)) ## [4.7.30](https://github.com/ocsjs/ocsjs/compare/4.7.29...4.7.30) (2023-12-11) ### Bug Fixes * **script:** 修改TikuAdapter type 类型解析 ([9aaeb7d](https://github.com/ocsjs/ocsjs/commit/9aaeb7d5b862eb7e848f0022dc1272bd543b71be)) ## [4.7.29](https://github.com/ocsjs/ocsjs/compare/4.7.27...4.7.29) (2023-12-11) ### Bug Fixes * **script:** 添加多版本脚本的更新模块适配 ([4a7aa19](https://github.com/ocsjs/ocsjs/commit/4a7aa19889c7d5f1a412de7e91491d6f52ace0c0)) ### Performance Improvements * **script:** 添加 TikuAdapter 解析器说明 ([d200b22](https://github.com/ocsjs/ocsjs/commit/d200b22212300cb5b055471a4767b13a428660f4)) ## [4.7.27](https://github.com/ocsjs/ocsjs/compare/4.7.25...4.7.27) (2023-12-11) ### Bug Fixes * **script:** 修复请求模块的重大BUG ([4b121bb](https://github.com/ocsjs/ocsjs/commit/4b121bb20c66e704bf32e09906a42bb7cf8566be)) ### Features * **script:** 新增 TikuAdapter 题库配置解析器,优化题库配置解析 ([1fc0d59](https://github.com/ocsjs/ocsjs/commit/1fc0d59313c2b2fe7f296b51340f3ff5d741e32e)) ## [4.7.25](https://github.com/ocsjs/ocsjs/compare/4.7.24...4.7.25) (2023-12-08) ### Bug Fixes * **core:** 复上个版本无法输入任何文字的BUG ([133dd69](https://github.com/ocsjs/ocsjs/commit/133dd69f700c2fd4bebaad37afc9fa69060038ec)) ## [4.7.24](https://github.com/ocsjs/ocsjs/compare/4.7.21...4.7.24) (2023-12-08) ### Bug Fixes * **core:** 优化对 headers 中 content-type 解析而实现传递不同的 data 数据 ([fcd962b](https://github.com/ocsjs/ocsjs/commit/fcd962bd27de2c9fbc5c3e57af9abd54ec6db5a3)) * **script:** 将中国大学MOOC的页面切换信息设置为警告信息 ([84f1d6e](https://github.com/ocsjs/ocsjs/commit/84f1d6e7881eaf886ec626952f159232d809acfc)) ### Features * **core:** 添加快捷键可以显示/隐藏 窗口功能 ([f33e67f](https://github.com/ocsjs/ocsjs/commit/f33e67f157d2ee6b1d8dfcfa3d783ddbc842a985)) ## [4.7.21](https://github.com/ocsjs/ocsjs/compare/4.7.20...4.7.21) (2023-12-05) ### Bug Fixes * **build:** 修复打包时不自动上传文件的BUG ([4983788](https://github.com/ocsjs/ocsjs/commit/4983788703e021d157b7562d0672848f00e04901)) ## [4.7.20](https://github.com/ocsjs/ocsjs/compare/4.7.19...4.7.20) (2023-12-05) ## [4.7.19](https://github.com/ocsjs/ocsjs/compare/4.7.18...4.7.19) (2023-12-05) ## [4.7.18](https://github.com/ocsjs/ocsjs/compare/4.7.17...4.7.18) (2023-12-05) ### Bug Fixes * **build:** 修复上个版本无法自动打包的BUG,优化打包代码 ([74a97cd](https://github.com/ocsjs/ocsjs/commit/74a97cd4f187f694f9b63df581a40f6c4a1382b8)) ## [4.7.17](https://github.com/ocsjs/ocsjs/compare/4.7.14...4.7.17) (2023-12-05) ### Features * **build:** 添加全域名通用脚本打包 user.common.user.js ([81f2cfa](https://github.com/ocsjs/ocsjs/commit/81f2cfa8e38f267f49930c1cc4dec402b8154835)) ### Performance Improvements * **script:** 添加请求记录方法过滤选项 HEAD ([63b58d5](https://github.com/ocsjs/ocsjs/commit/63b58d5354d5b086479a9d1a63bdadf797a71e51)) * **script:** 添加在线搜题题库配置为空的提示 ([7caf525](https://github.com/ocsjs/ocsjs/commit/7caf5252f9c7c35e69e9102bf70cdd554eae8e46)) ## [4.7.14](https://github.com/ocsjs/ocsjs/compare/4.7.7...4.7.14) (2023-12-05) ### Bug Fixes * **core:** 修复 attrs 属性的 style 特殊字段不生效的BUG, 将每个文本框设置最小的宽高 ([fdcc2ca](https://github.com/ocsjs/ocsjs/commit/fdcc2caf60f761d49fd8f0d22cf8e10cdf1803cc)) * **core:** 修复下拉选择框只有value值时不显示的BUG ([2d5f371](https://github.com/ocsjs/ocsjs/commit/2d5f371cca889c1b6ccf7acced557b0b9ae4f32f)) * **script:** 修复搜索结果序号过多时,序号页面超出页面的BUG ([8958910](https://github.com/ocsjs/ocsjs/commit/89589102791225401391f466bc36f1e7d2cd9506)) * **script:** 修复中国大学MOOC课堂测验答题完成后没有等待暂停时间步骤的BUG ([3bc76b1](https://github.com/ocsjs/ocsjs/commit/3bc76b1ef981d1fc63f51b4270a4b8af4ec924ef)) * **script:** 优化在线搜题功能,缓存搜索题目,优化划词功能 ([c4093b1](https://github.com/ocsjs/ocsjs/commit/c4093b1b64245c07db356a64094890eae314247c)) ### Features * **core:** 新增题库配置的字段解析器功能 ([f596488](https://github.com/ocsjs/ocsjs/commit/f59648871a8648ca50a15d3d162f2e01eb5a52ef)) * **script:** 添加超星 hnvist.cn fjlecb.cn 域名支持 ([afd86d6](https://github.com/ocsjs/ocsjs/commit/afd86d62425fd1140c48f764bf11102dcf1dac32)) * **script:** 新增超星旧版学习页面自动转换新版功能 ([9992a03](https://github.com/ocsjs/ocsjs/commit/9992a03597d4954049187b5d2ccce5cfe0430f9f)) * **script:** 新增开发人员请求记录调试页面 ([5baac27](https://github.com/ocsjs/ocsjs/commit/5baac2770e03a0222b8f800789d3743c1234e125)) ### Performance Improvements * **core:** request 模块的 gm http 的 data 使用 JSON.stringify 进行转换 ([7d4f660](https://github.com/ocsjs/ocsjs/commit/7d4f6602ecd7d0268b2e888c51a907f0a2cc4268)) * **script:** 删除超星多余日志 ([7be1071](https://github.com/ocsjs/ocsjs/commit/7be10712c36bd35c21175862eb7be915971d7b52)) ## [4.7.7](https://github.com/ocsjs/ocsjs/compare/4.7.1...4.7.7) (2023-11-30) ### Bug Fixes * **script:** 将超星提交功能设置为3秒延时 ([2161fa4](https://github.com/ocsjs/ocsjs/commit/2161fa461e0383278ff31b60475daa62702fdb3e)) * **script:** 新增题目冗余字段 ([596101f](https://github.com/ocsjs/ocsjs/commit/596101ff46b5fbd85bb7725fb9b9caeab6aa4169)) * **script:** 修复超星学习通章节测验已完成,但依然开始答题的BUG ([169eb4a](https://github.com/ocsjs/ocsjs/commit/169eb4aedc1e3351de6546891de9050510fe5ffc)) * **script:** 优化超星作业考试/章节测验 中 type 传递是 undefined 的BUG ([c2a1640](https://github.com/ocsjs/ocsjs/commit/c2a16402a5f8b97417cb3254faeccc2929d6ee53)) * **script:** 优化职教云-资源库:新增对 mp3 的支持,新增自动跳过测验功能 ([78634b4](https://github.com/ocsjs/ocsjs/commit/78634b4a35600d7baf48f4329e4970ce90c75b27)) ### Features * **core:** 优化答题uploadHandler API,新增 commonWork.options.onWorkerCreated 选项 ([c4154df](https://github.com/ocsjs/ocsjs/commit/c4154df1dbf4e2dfb94c92f2b4b83fd1c4c4d7c4)) * **script:** 新增中国大学MOOC,随堂测验自动答题功能 ([f79fb08](https://github.com/ocsjs/ocsjs/commit/f79fb08a93b928127d1e710d31ed0b0a5cf64f38)) ### Performance Improvements * **script:** 导出 ICourseProject 变量,开发者调试功能中可用 ([13d8787](https://github.com/ocsjs/ocsjs/commit/13d8787feafba0593f463246fb1fac4b707e1922)) * **script:** 增加超星任务点关闭后的开启提示 ([fe11e6e](https://github.com/ocsjs/ocsjs/commit/fe11e6ef7c1bc6360c1c8ea58cc7fe82e4552713)) ## [4.7.1](https://github.com/ocsjs/ocsjs/compare/4.7.0...4.7.1) (2023-11-25) ### Bug Fixes * **script:** 修复上个版本 职教云资源库获取课程信息失败 bug ([d97e538](https://github.com/ocsjs/ocsjs/commit/d97e538601a3d015c0e66609a5397033ab643a9c)) # [4.7.0](https://github.com/ocsjs/ocsjs/compare/4.6.32...4.7.0) (2023-11-25) ### Bug Fixes * **core:** 添加题库配置 method 大小写适配 ([d496be7](https://github.com/ocsjs/ocsjs/commit/d496be7cc5253c4b7ac8ba171dbd508ba9038d2a)) * **core:** 修复选项元素在没有 title 时显示为 undefined 的 BUG ([12ff0dc](https://github.com/ocsjs/ocsjs/commit/12ff0dc8c623ca0d4e38f230946d950ce18e26db)) * **core:** 优化选择下拉框设置,优化代码,修改之前由于无法获取value而使用的 $creator.selectOptions 的BUG ([2cf5318](https://github.com/ocsjs/ocsjs/commit/2cf5318030eb9427811db65973efe5b36eac2692)) * **script:** 将软件辅助的点击设置成点击元素中心点 ([a0e421d](https://github.com/ocsjs/ocsjs/commit/a0e421df0ceeba369bb9c64c95e0de7e21e2ff7b)) * **script:** 添加智慧树题目类型判断,修复某些题目不自动填写的BUG ([7bce433](https://github.com/ocsjs/ocsjs/commit/7bce4331ec9f504b6d887ec5e3d1443613200ce1)) * **script:** 修复职教云更新后-资源库不自动学习的BUG ([2d4d026](https://github.com/ocsjs/ocsjs/commit/2d4d0269d86e525ed288d322e231a4422572e461)) * **script:** 优化脚本字间距,搜索结果字间距和窗口间距 ([bcc4d87](https://github.com/ocsjs/ocsjs/commit/bcc4d87acbfd6aa78dce50d81bc561992f5fe7ab)) * **script:** 优化软件辅助错误提示 ([7f42cba](https://github.com/ocsjs/ocsjs/commit/7f42cbafa069d6350c74fe07a06e58d8782abe54)) * **script:** 优化智慧树学习提示 ([ee44189](https://github.com/ocsjs/ocsjs/commit/ee44189adac7b50bb2e35b9e7aca5eb1c4f73937)) * **script:** 优化智慧职教填空题 ([586b1fc](https://github.com/ocsjs/ocsjs/commit/586b1fc66468d3b55d4c359b84d1a4ec36f89f73)) ### Features * **script:** 添加超星学习通 视频内题目随机答题功能 ([e2d9d5d](https://github.com/ocsjs/ocsjs/commit/e2d9d5d84a11c62d5e21a2dba54be76f3f7e3ff4)) * **script:** 添加超星学习通-章节页面自动切换脚本 ([c0ac30c](https://github.com/ocsjs/ocsjs/commit/c0ac30c0b60a72de097f53dcdb94cd9247bd0e60)) * **script:** 添加职教云遇到讨论课件时自动跳过 ([994ba3e](https://github.com/ocsjs/ocsjs/commit/994ba3e9055581b14a2fb61aa680bb9f2327eb2c)) * **script:** 新增网课 中国大学MOOC 的学习、作业脚本支持 ([17445e5](https://github.com/ocsjs/ocsjs/commit/17445e5478f398d3580bc561eee2542804872478)) * **script:** 新增智慧职教-学习中心自动学习功能 ([0a8de35](https://github.com/ocsjs/ocsjs/commit/0a8de35bd99d74a43064a5f0213069e1d9ad80a1)) * **script:** 优化全部网课平台的系统通知功能,添加任务点完成提示,添加智慧树验证码提示 ([7e30125](https://github.com/ocsjs/ocsjs/commit/7e30125fd0858bd9c2067de0e7f3ad95bf639ff9)) ## [4.6.32](https://github.com/ocsjs/ocsjs/compare/4.6.29...4.6.32) (2023-11-01) ### Bug Fixes * **script:** 新增超星其他题,类型支持 ([ee2cf4d](https://github.com/ocsjs/ocsjs/commit/ee2cf4dfd987e9adf62f7d2767c7dff3560def53)) ### Features * **core:** 添加优先级选项,排序特定脚本的执行速度 ([049234c](https://github.com/ocsjs/ocsjs/commit/049234c280835363d1e56581cbe782d8e98424e6)) * **script:** 新增职教云资源库 pdf 支持,新增职教云作业脚本 ([f703288](https://github.com/ocsjs/ocsjs/commit/f703288641ecf794635b50922a0dab3b717f3446)) ## [4.6.28](https://github.com/ocsjs/ocsjs/compare/4.6.27...4.6.28) (2023-10-24) ## [4.6.29](https://github.com/ocsjs/ocsjs/compare/4.6.28...4.6.29) (2023-10-24) ### Bug Fixes * **script:** 修复软件配置同步失败问题 ([98eeb3d](https://github.com/ocsjs/ocsjs/commit/98eeb3de94e6a434bf84f1d64a2036624203df19)) ## [4.6.28](https://github.com/ocsjs/ocsjs/compare/4.6.27...4.6.28) (2023-10-24) ### Bug Fixes * **script:** 修复上个版本无法答题的BUG ([7918a3d](https://github.com/ocsjs/ocsjs/commit/7918a3d7bf4b7e0bfb78fc4a7ac1a9e6058445d7)) ## [4.6.27](https://github.com/ocsjs/ocsjs/compare/4.6.25...4.6.27) (2023-10-24) ### Bug Fixes * **script:** 修复超星章节测试题库被禁用的时候依然使用的BUG ([fff8cc4](https://github.com/ocsjs/ocsjs/commit/fff8cc4d41e49cf8c4f0d8f13f9cde3143505434)) * **script:** 修复智慧树检测习惯分出错 ([436eedd](https://github.com/ocsjs/ocsjs/commit/436eedd449c15d74be8b4b07fdd33d7c01e3db0e)) ## [4.6.25](https://github.com/ocsjs/ocsjs/compare/4.6.23...4.6.25) (2023-10-22) ### Bug Fixes * **script:** 深度优化智慧树弹窗BUG,以及倍速清晰度不选择BUG ([f61498c](https://github.com/ocsjs/ocsjs/commit/f61498c4640cbe8583b33d6d46a922341c3a57ae)) ### Features * **script:** 新增可以设置不被软件配置同步覆盖的设置,修复智慧树学习记录刷新后清空的BUG ([b35f87f](https://github.com/ocsjs/ocsjs/commit/b35f87fc5fd01a9de37fdc2855da2f6dfd8fc9df)) ## [4.6.23](https://github.com/ocsjs/ocsjs/compare/4.6.22...4.6.23) (2023-10-20) ### Bug Fixes * **core:** 优化核心域名匹配逻辑 ([8cbcc94](https://github.com/ocsjs/ocsjs/commit/8cbcc9412726c673e9f1ad8898a46ded1f6502c4)) * **script:** 持续优化智慧树倍速和清晰度选择功能 ([86d133b](https://github.com/ocsjs/ocsjs/commit/86d133bab4fc3063dc39335f61bf7a6f88226fd1)) * **script:** 将软件同步功能加快到 onactive ([db44761](https://github.com/ocsjs/ocsjs/commit/db4476158b39765c28cfe5491cacfedc4a193c2b)) * **script:** 优化超星新课程页面不显示使用提示的问题 ([3daf262](https://github.com/ocsjs/ocsjs/commit/3daf2623a97cd1a6d22aa3c3fd6bfc1e2b960f39)) * **script:** 优化智慧树弹窗答题 ([e7a7a1f](https://github.com/ocsjs/ocsjs/commit/e7a7a1f12246da929d106e69be50aca60d519117)) ## [4.6.22](https://github.com/ocsjs/ocsjs/compare/4.6.19...4.6.22) (2023-10-19) ### Bug Fixes * **script:** 修复智慧树需要调整窗口的BUG ([45c5f0b](https://github.com/ocsjs/ocsjs/commit/45c5f0b6a834b151d8955d05bf62cfa4ecb228c0)) ### Features * **script:** 兼容智慧职教套壳网站 courshare.cn ([7a2a088](https://github.com/ocsjs/ocsjs/commit/7a2a08832cafaefc70cefc7a3cec63131796655a)) * **script:** 收录超星套壳域名 qutjxjy.cn ynny.cn ([2d9b6fc](https://github.com/ocsjs/ocsjs/commit/2d9b6fcc81fa8ddce44fcc24d1756a6ef07624af)) ## [4.6.19](https://github.com/ocsjs/ocsjs/compare/4.6.10...4.6.19) (2023-10-15) ### Bug Fixes * **script:** 补充答题冗余字段删除数据 ([6d3544d](https://github.com/ocsjs/ocsjs/commit/6d3544d8fb49db88e943cdccfa9fb171792c1078)) * **script:** 修复软件配置同步后,日志页面全锁定的BUG ([e6f0610](https://github.com/ocsjs/ocsjs/commit/e6f0610e42c7bb0137cfc7beb79921a3ef1e869b)) * **script:** 修复使用软件同步时,智慧树学习自动暂停无效的BUG,以及存在验证码时自动暂停的优化。 ([8aa4bcf](https://github.com/ocsjs/ocsjs/commit/8aa4bcfd7f85e6c3c00afbb99fb361b4a70f1a73)) * **script:** 优化窗口大小自动调整,优化窗口大小警告模块 ([6882026](https://github.com/ocsjs/ocsjs/commit/6882026b2b9f2441d689add73895bed8797774f6)) ### Features * **script:** 将软件辅助警告修改成弹窗 ([6b88c29](https://github.com/ocsjs/ocsjs/commit/6b88c298befbee314e3bda309cdd426d04d2dc41)) * **script:** 添加超星强制答题提示 ([0067efc](https://github.com/ocsjs/ocsjs/commit/0067efc7e7dc7f09018709ad0b5f0ac636807eef)) * **script:** 添加对自动调整窗口开关的重启提示 ([46b957b](https://github.com/ocsjs/ocsjs/commit/46b957b6706bf501d8a22b3b0f336e5f83deb31f)) * **script:** 添加职教云【资源库】支持 ([da6f135](https://github.com/ocsjs/ocsjs/commit/da6f135276ace1394c4072f2a4cd02625753a689)) ## [4.6.10](https://github.com/ocsjs/ocsjs/compare/4.6.9...4.6.10) (2023-10-02) ## [4.6.9](https://github.com/ocsjs/ocsjs/compare/4.6.7...4.6.9) (2023-10-02) ### Bug Fixes * **script:** 修复智慧树考试脚本无法使用的BUG ([5f3f972](https://github.com/ocsjs/ocsjs/commit/5f3f9729687f93a5d2e36161883b822d14075518)) ### Features * **script:** 新增超星阅读任务无限阅读功能 ([137f739](https://github.com/ocsjs/ocsjs/commit/137f7397cef6554260db10f0d2a7535ca3db8073)) ## [4.6.7](https://github.com/ocsjs/ocsjs/compare/4.6.5...4.6.7) (2023-09-26) ### Bug Fixes * **script:** 微调智慧树窗口大小要求,宽2200,高1200 ([f438b79](https://github.com/ocsjs/ocsjs/commit/f438b79512463396cba1ca59aa93422b06ead276)) ### Features * **script:** 新增功能:超星强制答题功能,没有黄色任务点的章节测试也可以运行自动答题。 ([453d254](https://github.com/ocsjs/ocsjs/commit/453d254aa575b23ad7571b811dc618a467ae1826)) ## [4.6.5](https://github.com/ocsjs/ocsjs/compare/4.6.4...4.6.5) (2023-09-25) ### Bug Fixes * **script:** 修复智慧树调成窗口大于最小值后依然说不对的BUG ([be94200](https://github.com/ocsjs/ocsjs/commit/be9420097c81574b9b6b4a910808f27f19cbdf23)) ## [4.6.4](https://github.com/ocsjs/ocsjs/compare/4.6.3...4.6.4) (2023-09-24) ### Features * **script:** 添加智慧树窗口自动调节功能的选项按钮 ([0543ca1](https://github.com/ocsjs/ocsjs/commit/0543ca1e6a8acd92bdc2b1889beb7a4d8c0a2caf)) ## [4.6.3](https://github.com/ocsjs/ocsjs/compare/4.6.2...4.6.3) (2023-09-24) ### Bug Fixes * **script:** 添加智慧树窗口检测提示 ([849e28d](https://github.com/ocsjs/ocsjs/commit/849e28ddf7c57a5b7457d6f2b63a250a66f3792a)) ### Features * **script:** 添加自动设置窗口大小功能,避免元素无法点击 ([6aed250](https://github.com/ocsjs/ocsjs/commit/6aed250ea05a65c39ff5b5c36783510b2210781d)) ## [4.6.2](https://github.com/ocsjs/ocsjs/compare/4.6.1...4.6.2) (2023-09-22) ### Bug Fixes * **script:** 修复上个版本智慧树作业BUG ([e782403](https://github.com/ocsjs/ocsjs/commit/e7824039f22862ebbd412f43c37b248bea4b0232)) ## [4.6.1](https://github.com/ocsjs/ocsjs/compare/4.6.0...4.6.1) (2023-09-22) ### Bug Fixes * **script:** 优化智慧树刷课逻辑,增加流畅度 ([3a93157](https://github.com/ocsjs/ocsjs/commit/3a9315773fab3b7b8561ff57e189d58ff1a68150)) # [4.6.0](https://github.com/ocsjs/ocsjs/compare/4.5.8...4.6.0) (2023-09-22) ### Bug Fixes * **script:** 新增软件辅助功能,全面优化智慧树,共享课学习作业考试以及学分课视频 ([18a2725](https://github.com/ocsjs/ocsjs/commit/18a2725328b7917b8417ff8111e8a09fb4e09052)) * **script:** 优化超星最新考试页面支持 ([9e71239](https://github.com/ocsjs/ocsjs/commit/9e712393e26b8faced900a4e56bc8c4f0dc00294)) ## [4.5.8](https://github.com/ocsjs/ocsjs/compare/4.5.5...4.5.8) (2023-09-15) ### Bug Fixes * **script:** 修复部分课程不显示超星阅读任务的提示页面 ([907bb63](https://github.com/ocsjs/ocsjs/commit/907bb634a82889aa933a6989ab9c3d46cbba01c3)) * **script:** 修复在中国大学MOOC中点击事件监听被修改的问题,将 addEventListener('click') 改成 onlick ([b1eba8f](https://github.com/ocsjs/ocsjs/commit/b1eba8ff742738ff412f94b2f0e6ba83819250a4)) * **script:** 修复智慧树最新版脚本被检测的问题 ([adfe7a0](https://github.com/ocsjs/ocsjs/commit/adfe7a0cee1994396714cd3e01868cad409476c5)) ## [4.5.5](https://github.com/ocsjs/ocsjs/compare/4.5.4...4.5.5) (2023-09-12) ### Features * **script:** 使更新模块可视化 ([0e27628](https://github.com/ocsjs/ocsjs/commit/0e27628d319db704f9849926a9602d1406f43e63)) ## [4.5.4](https://github.com/ocsjs/ocsjs/compare/4.5.3...4.5.4) (2023-09-12) ### Bug Fixes * **core and script:** 修复上个版本出现的弹窗底部消失的问题,优化超星自动答题后台日志 ([37e6749](https://github.com/ocsjs/ocsjs/commit/37e6749a5bbef7788b2bd3b6efd2aba98581ceba)) ## [4.5.3](https://github.com/ocsjs/ocsjs/compare/4.5.0...4.5.3) (2023-09-12) ### Features * **core:** 添加跳过版本的功能 ([63913b2](https://github.com/ocsjs/ocsjs/commit/63913b222e53b128a26d8aa3b4dc5dee654b67ae)) * **script:** 添加新版职教云音频的播放 ([b7b47b9](https://github.com/ocsjs/ocsjs/commit/b7b47b9c0cb884e00cc98a6473cbbcdbdc933138)) * **script:** 添加新版职教云DOC文档学习功能 ([9a5babd](https://github.com/ocsjs/ocsjs/commit/9a5babd3379bc15e6c9cc0fe8c5f91a7d10bd5cb)) # [4.5.0](https://github.com/ocsjs/ocsjs/compare/4.4.35...4.5.0) (2023-09-09) ### Features * **script:** 重写职教云刷课逻辑 ([1b50d5b](https://github.com/ocsjs/ocsjs/commit/1b50d5b330db5676b94777245657793f5c55162d)) ## [4.4.35](https://github.com/ocsjs/ocsjs/compare/4.4.34...4.4.35) (2023-09-04) ### Bug Fixes * 优化自动发布的说明文档 ([cc84599](https://github.com/ocsjs/ocsjs/commit/cc84599a36f6afd8a10efd13d0b9decf6ca6608c)) * **script:** 修复超星选择题选中的答案被取消的BUG ([808709a](https://github.com/ocsjs/ocsjs/commit/808709a7503c3d34c2463d3156c1fde26deb8267)) * test ([c70ec9b](https://github.com/ocsjs/ocsjs/commit/c70ec9b94d166c5f6ab4a4aa649f7ca8f004199a)) ## [4.4.34](https://github.com/ocsjs/ocsjs/compare/4.4.33...4.4.34) (2023-08-25) ### Bug Fixes * 优化打包文件 ([3fd8657](https://github.com/ocsjs/ocsjs/commit/3fd86577271cd49746b64bae9a7131f7dc99c83f)) ## [4.4.33](https://github.com/ocsjs/ocsjs/compare/4.4.32...4.4.33) (2023-08-25) ### Bug Fixes * 优化github actions ([9f6608c](https://github.com/ocsjs/ocsjs/commit/9f6608cd775ac121ce5fcb070ba0b1b699a54cc6)) ## [4.4.32](https://github.com/ocsjs/ocsjs/compare/4.4.31...4.4.32) (2023-08-25) ### Bug Fixes * **script:** 优化智慧树最新异常检测 ([b634a46](https://github.com/ocsjs/ocsjs/commit/b634a4694a1fcf88956f6dcde9f8e7a8b891fccd)) ## [4.4.31](https://github.com/ocsjs/ocsjs/compare/4.4.30...4.4.31) (2023-08-05) ### Bug Fixes * **script:** 修复超星判断题更新后,文字优化不兼容的问题 ([f7beff9](https://github.com/ocsjs/ocsjs/commit/f7beff9e109c5c07a2f27df567c7cff319a4caff)) ## [4.4.30](https://github.com/ocsjs/ocsjs/compare/4.4.29...4.4.30) (2023-06-22) ### Bug Fixes * **script:** 新增超星分录题支持 ([68c007b](https://github.com/ocsjs/ocsjs/commit/68c007bb7ad890e57cec8cd5c39ff30b00ee599f)) ## [4.4.29](https://github.com/ocsjs/ocsjs/compare/4.4.25...4.4.29) (2023-06-22) ### Bug Fixes * **build:** 对脚本打包的 match 元数据进行去重 ([9de7d2c](https://github.com/ocsjs/ocsjs/commit/9de7d2c99a97c0786a3aa3dc9981643eedfe407d)) * **script:** 将题库配置为空提醒设置为一直显示 ([d07bdbf](https://github.com/ocsjs/ocsjs/commit/d07bdbfd0735b4ccd4bb9fabef6a6f5f15a73707)) * **script:** 新增超星题目类型支持:资料题 ([b6d3a00](https://github.com/ocsjs/ocsjs/commit/b6d3a006fb64b1f5aa58f806a68cfc56980ea340)) * **script:** 修复超星作业考试文本框无法自动保存BUG ([ec58efe](https://github.com/ocsjs/ocsjs/commit/ec58efedc5d047ddb1c66b32023df0e24709f315)) ## [4.4.25](https://github.com/ocsjs/ocsjs/compare/4.4.24...4.4.25) (2023-05-25) ### Bug Fixes * **core:** 修复上个版本题库连接超时问题 ([1c045ef](https://github.com/ocsjs/ocsjs/commit/1c045ef549a75717981bd99e97b4ecbee9d6f20d)) ## [4.4.24](https://github.com/ocsjs/ocsjs/compare/4.4.23...4.4.24) (2023-05-25) ### Bug Fixes * **script and core:** 修复题库状态检测无限执行的BUG,优化 onrender 中的监听器执行逻辑 ([cbe7f0b](https://github.com/ocsjs/ocsjs/commit/cbe7f0b9a1a2bdb47f0dff09739b41270b556c70)) * **script:** 优化智慧职教使用提示 ([ca5d4b3](https://github.com/ocsjs/ocsjs/commit/ca5d4b3ea4f29a265497ae0b401f3e28a575b081)) ## [4.4.23](https://github.com/ocsjs/ocsjs/compare/4.4.22...4.4.23) (2023-05-20) ## [4.4.22](https://github.com/ocsjs/ocsjs/compare/4.4.21...4.4.22) (2023-05-20) ### Bug Fixes * **build:** update build file ([c5d50b1](https://github.com/ocsjs/ocsjs/commit/c5d50b14bc4d834a3d5b8c82931abae8b15fca70)) ## [4.4.21](https://github.com/ocsjs/ocsjs/compare/4.4.20...4.4.21) (2023-05-20) ## [4.4.20](https://github.com/ocsjs/ocsjs/compare/4.4.19...4.4.20) (2023-05-20) ## [4.4.19](https://github.com/ocsjs/ocsjs/compare/4.4.15...4.4.19) (2023-05-20) ### Bug Fixes * **all:** 修复添加配置分隔线后,OCS配置同步锁的样式显示出错 ([a257084](https://github.com/ocsjs/ocsjs/commit/a25708419505672e40617ccc2a9088a6038ce971)) * **script:** 添加软件自动登录辅助页面 ([a2bfa20](https://github.com/ocsjs/ocsjs/commit/a2bfa20014c84d8ee7d59032eca01bac225d5d0e)) ### Features * **all:** 添加配置分割线,便于设置区域分组 ([980d591](https://github.com/ocsjs/ocsjs/commit/980d591c92466c79d2a35f40c8dacd6b633ed7a7)) ## [4.4.15](https://github.com/ocsjs/ocsjs/compare/4.4.14...4.4.15) (2023-05-16) ### Bug Fixes * **core:** 规范搜题请求处理,修复在url中存在变量时多加一个问号的BUG ([c0f561d](https://github.com/ocsjs/ocsjs/commit/c0f561d8c2ec6577f6594f0920bde9c686c23a22)), closes [#97](https://github.com/ocsjs/ocsjs/issues/97) ## [4.4.14](https://github.com/ocsjs/ocsjs/compare/4.4.13...4.4.14) (2023-05-16) ### Bug Fixes * **script:** 修复智慧职教多层级任务检测 ([64aaf13](https://github.com/ocsjs/ocsjs/commit/64aaf138762d21c3916c7dc7003a4ae3040b3655)) * **script:** 优化题库状态停用显示 ([a012a56](https://github.com/ocsjs/ocsjs/commit/a012a569a5817f2af8ec655755d78b23447b48e8)) ## [4.4.13](https://github.com/ocsjs/ocsjs/compare/4.4.12...4.4.13) (2023-05-12) ### Bug Fixes * **script:** 修复题库缓存不是最新的BUG ([a7a89b9](https://github.com/ocsjs/ocsjs/commit/a7a89b98d7ee672b47dc63fb36ba6eaa9e37c87d)) ## [4.4.12](https://github.com/ocsjs/ocsjs/compare/4.4.11...4.4.12) (2023-05-12) ### Bug Fixes * **script:** 修复题库配置清空后会留下两个小括号的bug ([0993a52](https://github.com/ocsjs/ocsjs/commit/0993a52d52dca43951279076783ca047a10dc234)) ## [4.4.11](https://github.com/ocsjs/ocsjs/compare/4.4.10...4.4.11) (2023-05-12) ### Bug Fixes * **script:** 继续优化智慧树作业考试检测 ([2a337bb](https://github.com/ocsjs/ocsjs/commit/2a337bb56c3a5f5a157ff80c213c14521d8f0c4c)) ## [4.4.10](https://github.com/ocsjs/ocsjs/compare/4.4.9...4.4.10) (2023-05-11) ## [4.4.9](https://github.com/ocsjs/ocsjs/compare/4.4.8...4.4.9) (2023-05-11) ## [4.4.8](https://github.com/ocsjs/ocsjs/compare/4.4.7...4.4.8) (2023-05-11) ## [4.4.7](https://github.com/ocsjs/ocsjs/compare/4.4.5...4.4.7) (2023-05-11) ### Bug Fixes * **script:** 将更新通知的请求方法改成油猴跨域请求,防止有些页面的 safe 策略阻止请求。 ([6613973](https://github.com/ocsjs/ocsjs/commit/6613973f00fbbda9b5d5f3591475fecd7e6a47c4)) * **script:** 优化智慧树考试作业提示 ([3703e97](https://github.com/ocsjs/ocsjs/commit/3703e976a19ba835aa1d6f7d7c5f860134802950)) ### Features * **script:** 新增题库开关功能 ([07f3cfc](https://github.com/ocsjs/ocsjs/commit/07f3cfcba517edd86cd38f4c0fcabd99ef3ed4fa)) ## [4.4.5](https://github.com/ocsjs/ocsjs/compare/4.4.4...4.4.5) (2023-05-10) ## [4.4.4](https://github.com/ocsjs/ocsjs/compare/4.4.3...4.4.4) (2023-05-10) ### Bug Fixes * **script:** 修复智慧树考试作业进入后不自动开始,而是需要刷新才能开始的BUG ([8f197ec](https://github.com/ocsjs/ocsjs/commit/8f197ec3d3418663698e9495de4909c1b2123ee5)) ## [4.4.3](https://github.com/ocsjs/ocsjs/compare/4.4.2...4.4.3) (2023-05-09) ## [4.4.2](https://github.com/ocsjs/ocsjs/compare/4.4.1...4.4.2) (2023-05-09) ### Bug Fixes * **script:** 修复智慧树作业考试阅读理解小题读取失败的BUG ([37e42f1](https://github.com/ocsjs/ocsjs/commit/37e42f10adfb3198dbb4738dbfd232d0bd736421)) ## [4.4.1](https://github.com/ocsjs/ocsjs/compare/4.4.0...4.4.1) (2023-05-09) ### Bug Fixes * **script:** 优化没有题库配置的提示 ([dc55244](https://github.com/ocsjs/ocsjs/commit/dc552440b5a15a1ff2639b64af851da6582056a0)) # [4.4.0](https://github.com/ocsjs/ocsjs/compare/4.3.7...4.4.0) (2023-05-09) ### Bug Fixes * **core:** 修复搜索结果图片显示问题 ([0ebc3f9](https://github.com/ocsjs/ocsjs/commit/0ebc3f9c057438611a4791818fd2f5030c16eb65)) * **core:** 优化单选题选项ABCD冗余并没有去掉的BUG ([f88fd86](https://github.com/ocsjs/ocsjs/commit/f88fd8698fc9a265df5ab3c9a3205fe9a81b4be4)) * **script:** 全面优化自动答题逻辑,并将搜索结果直接显示在各自的脚本面板下,无需反复跳转查看。 ([8712b24](https://github.com/ocsjs/ocsjs/commit/8712b24ee47bda38598e057e44188acd6f5a46fa)) * **script:** 修复智慧树图片题的BUG ([5adda0b](https://github.com/ocsjs/ocsjs/commit/5adda0be86a1530697539ac412a714d112d6962e)) * **script:** 优化日志显示 ([bc7521a](https://github.com/ocsjs/ocsjs/commit/bc7521a2313fc64d8be56be4b34956e06736dd4c)) * **script:** 优化智慧树视频加载缓慢时无法自动播放的BUG ([640bc72](https://github.com/ocsjs/ocsjs/commit/640bc72f70d9cebd9d9b2f110f11b99515a4fe33)) * **script:** 优化智慧树校内课作业 ([c877ed6](https://github.com/ocsjs/ocsjs/commit/c877ed6551569883afff330c37255982c3511424)) * **script:** 优化智慧职教MOOC的自动学习逻辑 ([9694773](https://github.com/ocsjs/ocsjs/commit/96947732d8da48018fb4ce9c8979941a859cb55b)) * **script:** 智慧树考试强制添加保存弹窗,并从头开始每题保存,防止用户切换题目导致保存失败 ([f6c0e22](https://github.com/ocsjs/ocsjs/commit/f6c0e22ebb610c6b59079e8cedd07ba8cd3e8438)) * **script:** update build script ([4aa68f1](https://github.com/ocsjs/ocsjs/commit/4aa68f16c4cf68487db298ca91e66d3b9ba90c51)) ### Features * **script:** 增加搜索结果与题目同步显示功能 ([f793d43](https://github.com/ocsjs/ocsjs/commit/f793d431cd51ca54dd984ff36d1ef1884833b8c2)) * **script:** 增加职教云考试功能 ([c9bc7ad](https://github.com/ocsjs/ocsjs/commit/c9bc7ad5cdf07c292b380cedc273fc407329e02e)) ## [4.3.7](https://github.com/ocsjs/ocsjs/compare/4.3.5...4.3.7) (2023-04-24) ### Bug Fixes * **script:** 修复智慧职教MOOC学院自动答题无法处理判断题的BUG ([b573684](https://github.com/ocsjs/ocsjs/commit/b5736845e38ec1e80281b66fc1f8bb152ec703dd)) ### Features * **script:** 添加职教云作业自动答题 ([4db11a4](https://github.com/ocsjs/ocsjs/commit/4db11a40b7cfbac1e7eda26e50e2bde88d4ee6a5)) ### Performance Improvements * **core:** add woker.onElementSearched args[1] : root ([c0244e9](https://github.com/ocsjs/ocsjs/commit/c0244e998f7893761557d7354d232de9438e00c2)) ## [4.3.5](https://github.com/ocsjs/ocsjs/compare/4.3.3...4.3.5) (2023-04-24) ### Bug Fixes * **script:** 修复题库缓存搜索时每题只出一个结果的BUG ([222735a](https://github.com/ocsjs/ocsjs/commit/222735a98cf94f09e6b69eb313c98283e42561a9)) * **script:** 修复智慧职教MOOC自动答题的填空题无法填空的BUG ([565f879](https://github.com/ocsjs/ocsjs/commit/565f8799518912a65c818baf55a8671576af659f)) ## [4.3.3](https://github.com/ocsjs/ocsjs/compare/4.3.2...4.3.3) (2023-04-24) ### Bug Fixes * **script:** 撤回智慧树图片识别,否则导致作业考试无法使用。 ([fe5b197](https://github.com/ocsjs/ocsjs/commit/fe5b197f1ab4a004d69c44c3460fc94ed7a4e1d6)) ## [4.3.2](https://github.com/ocsjs/ocsjs/compare/4.3.1...4.3.2) (2023-04-24) ### Bug Fixes * **script:** 修复一些显示BUG ([eb4cbc1](https://github.com/ocsjs/ocsjs/commit/eb4cbc1067a97afa4fb81424a9332d393e591982)) ## [4.3.1](https://github.com/ocsjs/ocsjs/compare/4.3.0...4.3.1) (2023-04-24) ### Bug Fixes * **script:** 帮助智慧树修复图片题无法显示的BUG ([bb4c5aa](https://github.com/ocsjs/ocsjs/commit/bb4c5aa49d46f5c077a3855b392ba036db6672e6)) # [4.3.0](https://github.com/ocsjs/ocsjs/compare/4.2.31...4.3.0) (2023-04-24) ### Bug Fixes * **app:** 优化浏览器环境问题 ([090c7af](https://github.com/ocsjs/ocsjs/commit/090c7af154e34fa7aa0833a586cab7e709cc16c8)) * **core:** 修复题库搜题时出现 Cannot convert object to primitive value 问题 ([9af5f3f](https://github.com/ocsjs/ocsjs/commit/9af5f3ff5ab93fcd87deefbf7af9963636f342ea)) * **scirpt:** 优化搜索结果的显示,并且添加快捷百度一下按钮 ([e074a95](https://github.com/ocsjs/ocsjs/commit/e074a95c647e58281648a0ba83c17c069e4de015)) * **script:** 添加超星自动答题后暂停提示 ([0635db9](https://github.com/ocsjs/ocsjs/commit/0635db94916fcace2e44ef5f7d3fa74114021aa0)) * **script:** 新增超星匹配域名: hnsyu.net ([481445f](https://github.com/ocsjs/ocsjs/commit/481445f46903ef018f125cd17a33e431a979b267)) * **script:** 新增超星视频加载失败检测功能 ([1b1e9b8](https://github.com/ocsjs/ocsjs/commit/1b1e9b8b7697b89f0f5c2aef28aff064d42db606)) * **script:** 修复上个版本智慧职教MOOC学院中作业自动答题题目为空的BUG ([e095493](https://github.com/ocsjs/ocsjs/commit/e095493a3fbd767c2d0d0e90131d6ddc1ec0481f)) * **script:** 修复手贱导致的判断题乱选的BUG ([4cfa3c8](https://github.com/ocsjs/ocsjs/commit/4cfa3c86b948115819ec3e8e9c5a7ebed0b7ebd0)) * **script:** 优化 $modal API ,修复 onClose 执行逻辑 ([37cd819](https://github.com/ocsjs/ocsjs/commit/37cd819f27d7b750c76d0db652b35ad5d604a7aa)) * **script:** 优化超星编辑框复制粘贴问题 ([b263146](https://github.com/ocsjs/ocsjs/commit/b26314689183338e3b4ee3db73258e4e1d538df6)) * **script:** 优化脚本教程,优化搜索结果,删除独立的通知提示和版本日志,转移到脚本首页中。 ([12d9cef](https://github.com/ocsjs/ocsjs/commit/12d9cef9937fd11c73d2b0180f59d4f067777822)) * **script:** 优化屏蔽复制粘贴限制 ([3c124c3](https://github.com/ocsjs/ocsjs/commit/3c124c300233592224f127df8ddcf6cbe65363ce)) * **script:** 优化搜索结果空白的BUG ([5de62f5](https://github.com/ocsjs/ocsjs/commit/5de62f5f9f590c4afe4db2a9b91af8f125481847)) * **script:** 优化智慧树学习逻辑,用户手动切换视频时脚本可以重新生效。 ([ad5757c](https://github.com/ocsjs/ocsjs/commit/ad5757c81fa7e5139e4097c6326996172e0e12a3)) ### Features * **core:** 优化 el API 方便自定义样式 ([e3468f7](https://github.com/ocsjs/ocsjs/commit/e3468f7762b71976338b7a7e74eb920e797e069e)) * **script:** 将题库缓存储域切换成本地存储 ([6969b52](https://github.com/ocsjs/ocsjs/commit/6969b523cf8a6cc8436f101be7b44ae2ce2ca41d)) * **script:** 添加题库缓存功能 ([38bde15](https://github.com/ocsjs/ocsjs/commit/38bde1561acfc5e1cf2961846d55ae5705b46514)) * **script:** 添加智慧职教MOOC学院的作业自动答题功能,优化刷课逻辑。 ([e2954f4](https://github.com/ocsjs/ocsjs/commit/e2954f412e726af2b26f153d0cb980c2d8273608)) ## [4.2.31](https://github.com/ocsjs/ocsjs/compare/4.2.29...4.2.31) (2023-04-20) ### Bug Fixes * **script:** 修复超星图片题选择BUG ([93c520c](https://github.com/ocsjs/ocsjs/commit/93c520cfcea739cb12d3d0c11d05065e0f1e947a)) * **script:** 优化智慧树共享课和校内课的作业和考试 ([c80b08c](https://github.com/ocsjs/ocsjs/commit/c80b08cec8099c335b7a2c0a38cbacf1585f5610)) ## [4.2.29](https://github.com/ocsjs/ocsjs/compare/4.2.26...4.2.29) (2023-04-20) ### Bug Fixes * **script:** 优化超星繁体字识别,优化http网站下复制粘贴问题 ([f98f2c4](https://github.com/ocsjs/ocsjs/commit/f98f2c4f439f02b6a0ef045c8b9a5f61625d8c8e)) * **script:** 优化智慧树作业考试文字识别BUG ([317f136](https://github.com/ocsjs/ocsjs/commit/317f136611b4175955f0fe8c646100aac0d925cb)) * **scripts:** 修复超星倍速提示没有显示的BUG ([ec02677](https://github.com/ocsjs/ocsjs/commit/ec02677a56181b760015e79f9446cbdd2a58b485)) ## [4.2.26](https://github.com/ocsjs/ocsjs/compare/4.2.15...4.2.26) (2023-04-19) ### Bug Fixes * **app:** 修复软件批量创建中无法导出模板的BUG ([7ba565e](https://github.com/ocsjs/ocsjs/commit/7ba565e569d003ac91d124bc09c001816df97edc)) * **app:** 修复OCR初始化软件路径读取问题 ([2ef18e0](https://github.com/ocsjs/ocsjs/commit/2ef18e0e92dd65abda4516df9b9ee7a2f12f4d1c)) * **scirpt:** 在OCS软件中不显示软件配置同步的面板 ([baf3815](https://github.com/ocsjs/ocsjs/commit/baf3815ec8670fed7d27250c4deb9a4c0094676a)) * **script and app:** 修复脚本题库检测BUG,优化软件更新提示 ([7e217d1](https://github.com/ocsjs/ocsjs/commit/7e217d1a68ce840275bdf94ba3fa2265a73d3f13)) * **script:** - 优化超星章节测试题目解析 ([6b39e40](https://github.com/ocsjs/ocsjs/commit/6b39e401cf7c9ad1af58651b1067b722c55236c0)) * **script:** 对超星繁体字库加载进行异常处理 ([7a0c825](https://github.com/ocsjs/ocsjs/commit/7a0c825aec72d1af1cf7e9b33eb78660aea17713)) * **script:** 删除清空答案选项 ([06936a5](https://github.com/ocsjs/ocsjs/commit/06936a558f67f1e9e80d9369e52697ed0f557e2f)) * **script:** 修复超星输入框无法复制粘贴的BUG ([d8a3c30](https://github.com/ocsjs/ocsjs/commit/d8a3c300bcb7f25a186bb3e857f9d0e3c8aa5b9f)) * **script:** 修复超星章节测试出现年份丢失的BUG ([941c0e9](https://github.com/ocsjs/ocsjs/commit/941c0e9ceeefc7270b1f6740f1061e7aed952e39)) * **script:** 修复超星章节测试出现年份丢失的BUG ([2bc1ce1](https://github.com/ocsjs/ocsjs/commit/2bc1ce11bc6ecb0e34300611c3e0c2360624a2fc)) * **script:** 修复搜索结果答案是图片但没有显示的BUG ([860d1fe](https://github.com/ocsjs/ocsjs/commit/860d1feb2fd480a8ba8cc6fcdfe9eda9ca06004a)) * **script:** 修复智慧树考试无法识别题目的BUG ([5140d95](https://github.com/ocsjs/ocsjs/commit/5140d95aeb46ba13244eae73e3d3eb595c3cee1d)) * **script:** 优化软件同步设置,优化题库状态显示 ([4e3cc96](https://github.com/ocsjs/ocsjs/commit/4e3cc967fc653f333cbb897404ac1f8f8ae65ab8)) * **scripts:** 每次渲染强制更新通知和版本日志 ([7cc5347](https://github.com/ocsjs/ocsjs/commit/7cc53479e00205c1fae88c6ae3e353874bdff413)) ### Features * **script:** 添加题目冗余字段自定义删除功能 ([198e240](https://github.com/ocsjs/ocsjs/commit/198e2408fa90325731b8eae8e5b8e6e42609d102)) ## [4.2.15](https://github.com/ocsjs/ocsjs/compare/4.2.11...4.2.15) (2023-04-04) ### Bug Fixes * **all:** 修复软件配置同步导致的各种问题 ([6040d70](https://github.com/ocsjs/ocsjs/commit/6040d7098e5c0ca38b27064bd2fdbaf71ac83fbe)) * **script:** 修复搜索结果超时后全部题库都显示超时的BUG ([a11b2a5](https://github.com/ocsjs/ocsjs/commit/a11b2a5fa65bc52aca28dd731c954ee36ded2f6d)) * **script:** 修复通知和版本日志在有些页面访问不了的BUG ([7fa694a](https://github.com/ocsjs/ocsjs/commit/7fa694ad6cce5b3edc5cbf4c50bfada3b95aa7a7)) * **script:** 优化全局设置题库检测,增加防抖 ([a1de6ab](https://github.com/ocsjs/ocsjs/commit/a1de6ab38ab8e08313eba3f50572b7d9659e9e59)) ## [4.2.11](https://github.com/ocsjs/ocsjs/compare/3.13.0...4.2.11) (2023-04-03) ### Bug Fixes * **all:** 修改项目链接 enncy/online-course-script => ocsjs/ocsjs ([e7f0229](https://github.com/ocsjs/ocsjs/commit/e7f02292548e89517585e7a3a96e6063078eecd6)) * **app:** 使用JSON消除自定义脚本的响应式特性,修复每个浏览器自定义脚数据相同的问题。 ([af7b97e](https://github.com/ocsjs/ocsjs/commit/af7b97e2c5e0a426813a231e02b66072c57db8ee)) * **app:** 新增软件 zip 打包方式,修复工作区丢失的BUG ([8fe1357](https://github.com/ocsjs/ocsjs/commit/8fe13574285871f56234223264b1ae64a0c27a77)) * **app:** 修复部分用户加载不出导航页的BUG ([19f4d10](https://github.com/ocsjs/ocsjs/commit/19f4d102edbacaff21cd324189599941e7e78739)) * **app:** 修复软件标题栏重载时不自动切换的BUG ([25f45ce](https://github.com/ocsjs/ocsjs/commit/25f45ce643c3e30a32d2f9e89a425e7ec6e57d8b)) * **app:** 修复软件浏览器标签输入时不自动提示的BUG ([6aececb](https://github.com/ocsjs/ocsjs/commit/6aececb4511929732a5e03a4c41c2d8172bc633c)) * **app:** 修复软件运行途中删除文件会导致所有浏览器关闭的BUG ([45a2c22](https://github.com/ocsjs/ocsjs/commit/45a2c226ca1033f2ac9c95f3498772eb06856529)) * **app:** 修改app加载文件以及油猴插件路径问题 ([cbf6930](https://github.com/ocsjs/ocsjs/commit/cbf69306236d0b91d822ea27345cae3e4579a1ab)) * **app:** 优化 app remote 模块,修改成异步通信,修复同步通信导致的页面卡死问题。 ([9a81721](https://github.com/ocsjs/ocsjs/commit/9a817212ae1a57e88a8738425de8c51218bfeefa)) * **app:** 优化软件导航页,将ocs-app接口代理删除,全局使用15319端口进行访问,修改浏览器启动选项,使浏览器环境更接近真实浏览器 ([9c8d3fd](https://github.com/ocsjs/ocsjs/commit/9c8d3fd6f1986b5a1834696a9b8362109125132b)) * **app:** 优化软件更新程序,并修复更新通知抽搐问题 ([460e79a](https://github.com/ocsjs/ocsjs/commit/460e79a07f45e26f8d64136bf40f42ab2f3a83e7)) * **app:** 优化软件监控问题,以及启动脚本,和浏览器状态处理优化。 ([7b7475d](https://github.com/ocsjs/ocsjs/commit/7b7475df86b3c3e509dfb8e1e44d4770fff00dfd)) * **app:** 优化软件自动化脚本,添加类型声明 ([b3f1565](https://github.com/ocsjs/ocsjs/commit/b3f1565505a5331f2761e333c06dcd971c41f800)) * **app:** app version update ([5ddce81](https://github.com/ocsjs/ocsjs/commit/5ddce8114f8121d42a76917d0b34013d86eb68bc)) * **app:** remove public build ([d3e4340](https://github.com/ocsjs/ocsjs/commit/d3e4340b82381e78daf56d02f5ccb2ae8e8da7ee)) * **core:** 持续优化超星直播回放脚本 ([1d3b399](https://github.com/ocsjs/ocsjs/commit/1d3b399e790616cfade8aed6e3d81dcc2402da5d)) * **core:** 调整职教云任务读取速度 ([b2adafb](https://github.com/ocsjs/ocsjs/commit/b2adafbade5b894f7c723b3e197ea44a3919cdcd)) * **core:** 更改 $gm 和 $ 修改后的代码 ([941ea30](https://github.com/ocsjs/ocsjs/commit/941ea307206c2e197134b8499977e3568332b6bf)) * **core:** 更新超星未完成章节会出现提示框的问题 ([ebd75d1](https://github.com/ocsjs/ocsjs/commit/ebd75d17500effb477363a48db00e60b36825ed8)) * **core:** 更新教程链接 ([f07fe39](https://github.com/ocsjs/ocsjs/commit/f07fe3906a1bbfe360d5174cf2bfacbfe319bb88)) * **core:** 更新智慧树脚本 ([dc04328](https://github.com/ocsjs/ocsjs/commit/dc04328309092e4630d16d1b7111936354d85d6c)) * **core:** 继续修复上个版本问题 ([41ca48c](https://github.com/ocsjs/ocsjs/commit/41ca48c83966035f536de822f8d25a58a0f7ac68)) * **core:** 将 dropdown 的 opiton 元素改成 div 元素 ([5a8877e](https://github.com/ocsjs/ocsjs/commit/5a8877e531b19a344d58e11b1956c787186dc005)) * **core:** 将 unsafeWindow 全局变量封装成 useUnsafeWindow ([1c0f0bb](https://github.com/ocsjs/ocsjs/commit/1c0f0bbf71661249b81bfed12472fd7bc389ebf2)) * **core:** 将超星视频进度步数调整为0.25,倍速颗粒度控制更高。 ([1702aa9](https://github.com/ocsjs/ocsjs/commit/1702aa9484bc5007a8f04cb0f1600af72ab28477)) * **core:** 将整个项目修改成跨域响应式模式 ([10aaf0a](https://github.com/ocsjs/ocsjs/commit/10aaf0ae4d415d68e3a22bba303962c17f43355f)) * **core:** 脚本学习核心修改,适配 useSettings 和 useContext 两个 API ([b4e38b1](https://github.com/ocsjs/ocsjs/commit/b4e38b144cdd23504432a17950aafcbbcd6723c2)) * **core:** 解决cx任务点未完成出现弹窗的问题 ([d9a6738](https://github.com/ocsjs/ocsjs/commit/d9a67389dabee1dde144f96df4cf45909b290072)) * **core:** 删除超星在作业考试中题目多余内容导致的正确率下降 ([5ff27e5](https://github.com/ocsjs/ocsjs/commit/5ff27e5558500671d9cb30d96d1175be86e48dd1)) * **core:** 删除全局变量 OCS ([c74195d](https://github.com/ocsjs/ocsjs/commit/c74195d0d3192e6854c8da0d32262634f4470de2)) * **core:** 添加本地存储初始化时删除无用字段 ([dc57a4a](https://github.com/ocsjs/ocsjs/commit/dc57a4a33f20ccd11922d806425acdeb30a8a45a)) * **core:** 为所有参数提供默认值,防止页面空白或者头部空白。 ([baf6086](https://github.com/ocsjs/ocsjs/commit/baf6086567d1cc4b1681fcdf5d818260ccbe8dd6)) * **core:** 限制请求超时时间最小为10 ([f3fc59e](https://github.com/ocsjs/ocsjs/commit/f3fc59e211b6f86c70c4a9a80a6c0bb3b4b82a0d)) * **core:** 新增ICVE字段 ([abbcfec](https://github.com/ocsjs/ocsjs/commit/abbcfec64d25622415c863233029c68878d1d4a5)) * **core:** 修复 store 环境检测问题 ([cd14eb0](https://github.com/ocsjs/ocsjs/commit/cd14eb07e8b33746fc72b1d35e2aaba85a85b07f)) * **core:** 修复 StringUtils 导入问题 ([50b83dd](https://github.com/ocsjs/ocsjs/commit/50b83dd3942cf6801da203b168c55ae70d63c3be)) * **core:** 修复$message不能永久显示bug ([3eec4b8](https://github.com/ocsjs/ocsjs/commit/3eec4b8b386c241c40a4df2813e45f10c62f45d2)) * **core:** 修复不能关闭路线切换的BUG ([a5694f1](https://github.com/ocsjs/ocsjs/commit/a5694f1acecb7962c0e39d5b6890b5b86b365d03)) * **core:** 修复部分页面存在不执行 interactive 生命周期的问题 ([1f9a8cc](https://github.com/ocsjs/ocsjs/commit/1f9a8cc32b47c9a46a6c77089f5d1b1dd192f625)) * **core:** 修复超星复习模式自动切换的BUG ([64c63cc](https://github.com/ocsjs/ocsjs/commit/64c63cc19bf0f97de7409a1f3c87ab4321726365)) * **core:** 修复超星视频答题永远只选2个选项的BUG ([1209ee7](https://github.com/ocsjs/ocsjs/commit/1209ee7362a3bd10e7745393a944f686b0fca431)) * **core:** 修复超星音频任务不能播放的BUG ([2777c69](https://github.com/ocsjs/ocsjs/commit/2777c691322b8d511b56e44e08384ed15f29827e)) * **core:** 修复答题结果中存在答案但是不选的BUG ([8129842](https://github.com/ocsjs/ocsjs/commit/8129842fbdc6187ab93a07ba4e34a38f66fed998)) * **core:** 修复打包后文件中文变成Unicode编码的问题 ([ea6da2c](https://github.com/ocsjs/ocsjs/commit/ea6da2cea453efbf6cd7da3d15f30ff7f1aa7961)) * **core:** 修复代码中带有特殊字符时,unicode 转换失败 ([fb0cb21](https://github.com/ocsjs/ocsjs/commit/fb0cb211a6f87de37fe4f0ea9d2cb5956e9857b2)) * **core:** 修复跨域问题 ([8ad21bf](https://github.com/ocsjs/ocsjs/commit/8ad21bfcbd4130ab71a15813beabeec045223ef2)) * **core:** 修复每次页面加载都要删除core监听队列的bug ([230d43a](https://github.com/ocsjs/ocsjs/commit/230d43a0e9a3fe047c787c588a3e1285f46638e8)) * **core:** 修复日志面板不会实时滚动的BUG ([a0b9fff](https://github.com/ocsjs/ocsjs/commit/a0b9fffb6967a34397eef6597483ccdff8772319)) * **core:** 修复视频频繁停止导致的频繁验证码 ([62f708e](https://github.com/ocsjs/ocsjs/commit/62f708e9e1a3e15582efd0618ca085b9aa578535)) * **core:** 修复数字输入可以超过范围的BUG ([edbd3bc](https://github.com/ocsjs/ocsjs/commit/edbd3bce0385f404d9dcad58d5b831c61c7f9545)) * **core:** 修复题库配置报错异常未捕获的BUG ([4e04da0](https://github.com/ocsjs/ocsjs/commit/4e04da0ccfd938e58d4f0239be861a882fb01d56)) * **core:** 修复职教云进度不显示的BUG ([480cfa3](https://github.com/ocsjs/ocsjs/commit/480cfa3a71d91a2252e73f9ae09db6221a97c8a0)) * **core:** 修复职教云任务获取出现子节点BUG ([ba63921](https://github.com/ocsjs/ocsjs/commit/ba639211dedd4d50b56e7a8ea1e02d1c25c73346)) * **core:** 修复职教云子节点读取的问题,优化任务列表,优化学习脚本 ([5a11f13](https://github.com/ocsjs/ocsjs/commit/5a11f138151bc0a8b0297b1f09ed17e8c2589488)) * **core:** 修复智慧树倍速失效的BUG ([7943442](https://github.com/ocsjs/ocsjs/commit/79434421cc1b9c28c547b7c45b08ea762459dd26)) * **core:** 修复智慧树视脚本的频路径匹配 ([b03dd08](https://github.com/ocsjs/ocsjs/commit/b03dd08d178ac9e89fb042a5bf97520eda1de10e)) * **core:** 修复智慧树视频测验弹窗无法关闭的BUG ([fc0741f](https://github.com/ocsjs/ocsjs/commit/fc0741f7903992d877ee4b8b1b04e272f81b7a4d)) * **core:** 修复select,range等对value不显示的bug ([201b3ad](https://github.com/ocsjs/ocsjs/commit/201b3add0ffcbfbdb41157071e1b8d660cb38ac4)) * **core:** 修复zhs弹窗关闭的问题 ([a688768](https://github.com/ocsjs/ocsjs/commit/a688768c39984561cee8d2ecfee7f6ad07d2f78b)) * **core:** 修改 $creator.tooltip 的卡死BUG,很多元素时会导致卡顿,这里将 tooltip 变成单个全局元素。 ([da59c28](https://github.com/ocsjs/ocsjs/commit/da59c2899bb9ef37aca5aee2000e00311cad1d73)) * **core:** 修改 userjs.templete 模板文件,兼容跨域响应式,并修改 homepage 等字段 ([f945a80](https://github.com/ocsjs/ocsjs/commit/f945a8043e5e62f4b92f8650fc332278cdfb974b)) * **core:** 修改生命周期执行规则 ([99b66fb](https://github.com/ocsjs/ocsjs/commit/99b66fb884f2b88eef72fa402e932bfa40e71148)) * **core:** 修改搜索结果文案 ([00f185e](https://github.com/ocsjs/ocsjs/commit/00f185e2cba817cbe4d58e28c1d8248bdd656b9d)) * **core:** 修改元素是否存在的判断 ([345fbcc](https://github.com/ocsjs/ocsjs/commit/345fbccd675dee604e3b4fd21d6c787a38c6575d)) * **core:** 修改cdn地址 ([013e3ba](https://github.com/ocsjs/ocsjs/commit/013e3ba037b38b88a4728c4cbd478cd4e518953e)) * **core:** 移除页面反调试脚本至超星 ([77cb19c](https://github.com/ocsjs/ocsjs/commit/77cb19c73df4fdacaac7120bdd748a49a06c8091)) * **core:** 因全局变量删除,且需要封装 store 中多个变量的处理,以及防止变量污染,新增了 useContext 和 useSettings 的API,相应涉及代码同步更新。 ([6d9fa51](https://github.com/ocsjs/ocsjs/commit/6d9fa5112e81ce278f16a8248c3f9dfe3fed8e13)) * **core:** 优化 start 主函数,防止脚本重新执行 ([bef652d](https://github.com/ocsjs/ocsjs/commit/bef652d3407ebd07033c2eeb88b1cef9d3d14190)) * **core:** 优化超星直播回放 ([0727bea](https://github.com/ocsjs/ocsjs/commit/0727bea2c3d9c549770cfa9f5f305afdea91825f)) * **core:** 优化登录脚本,非空判断 ([684c441](https://github.com/ocsjs/ocsjs/commit/684c441401969c10172bf3c613058c750b5248d2)) * **core:** 优化获取远程本地软件题库配置功能 ([09986b5](https://github.com/ocsjs/ocsjs/commit/09986b5acaa066c2b259a0e1b69f38ece2898b0c)) * **core:** 优化题库解析器 ([67b50a4](https://github.com/ocsjs/ocsjs/commit/67b50a47fdc9fb5730e83d295249e93e046c12f7)) * **core:** 优化题库配置字段 ([d674f00](https://github.com/ocsjs/ocsjs/commit/d674f007c62c3882ad9426d8ee93c8360eef9347)) * **core:** 优化图片识别时图片链接显示的问题 ([d9778f6](https://github.com/ocsjs/ocsjs/commit/d9778f6268a4986a4ff3eed7542ee54f9e97242b)) * **core:** 优化响应式存储 ([b307f74](https://github.com/ocsjs/ocsjs/commit/b307f74140d382243c46dc80dafa372cf1b3ca47)) * **core:** 优化页面通讯以及构建 ([7a20041](https://github.com/ocsjs/ocsjs/commit/7a20041ce081f9675b447847ad8cf9324bf7305d)) * **core:** 优化智慧树脚本,适配 useContext 和 useSettings 两个 API ([a24d991](https://github.com/ocsjs/ocsjs/commit/a24d991e8a289dd524ae577036a4e165b92afbd1)) * **core:** 优化自动答题逻辑,修复有答案不选的BUG ([23df64d](https://github.com/ocsjs/ocsjs/commit/23df64d1d77a9556a1d105d72bc9810fb4c06c18)) * **core:** 优化OCS环境加载问题 ([2742ed4](https://github.com/ocsjs/ocsjs/commit/2742ed47892308879560742b1aa2bf2a08dbe6cf)) * **core:** 暂时删除视频答题功能 ([b8eeba5](https://github.com/ocsjs/ocsjs/commit/b8eeba52b2d8a8fdd16a90a9f7a3f73d9d8cc722)) * **core:** 增加答题器的 await 等待机制,多选题选择时需要 await 等待 ([853a30d](https://github.com/ocsjs/ocsjs/commit/853a30d0b9ebac9dca8721e0ce8ce838056e2e11)) * **root:** 修复 release.sh 打包文件错误时仍然执行的BUG ([6f14bd8](https://github.com/ocsjs/ocsjs/commit/6f14bd854954f1415705d4086d41eb852d1854d0)) * **root:** 优化构建发布release.sh文件 ([a855740](https://github.com/ocsjs/ocsjs/commit/a855740f0b8ffa107f5f580f2123c70baedb16f7)) * **scirpt:** 修复搜索结果显示与搜题不一致的BUG,优化各脚本,持续优化学习通任务检测算法。 ([f49737a](https://github.com/ocsjs/ocsjs/commit/f49737aa6227be382d608b08f079cf46ef3f8649)) * **script:** 持续修复超星问题 ([f2f08e2](https://github.com/ocsjs/ocsjs/commit/f2f08e20eb94466f5dd81dbb5a63ae7f33026911)) * **script:** 登录时将脚本至于左上方防止挡住软件操作 ([dc2bf1d](https://github.com/ocsjs/ocsjs/commit/dc2bf1d4added39f3967276dc6a05490115251e1)) * **script:** 删除超星的强制答题选项 ([60bf03c](https://github.com/ocsjs/ocsjs/commit/60bf03c6ecddb7a2faaee6667ac3033f4a6f4403)) * **script:** 删除脚本展开功能 ([ec1f527](https://github.com/ocsjs/ocsjs/commit/ec1f52798f4bf3b7fb1c4800445e3939a64b660b)) * **script:** 删除浏览器窗口检测,经过测试全屏游戏中也会进行误报。 ([bf30309](https://github.com/ocsjs/ocsjs/commit/bf30309f7cb331b239dc74d3733fa433c8dbcf2c)) * **script:** 使用固定端口与桌面软件通讯 ([7bc6de4](https://github.com/ocsjs/ocsjs/commit/7bc6de47bb0686f2353fbcbe56ed75bc93a5b95e)) * **script:** 添加环境检测,优化职教云逻辑 ([709e673](https://github.com/ocsjs/ocsjs/commit/709e673a4ed5efe755238b45645f4878d079117c)) * **script:** 修复超星多个视频同时播放的BUG ([51ea1b0](https://github.com/ocsjs/ocsjs/commit/51ea1b0a43c32d596450c39d3940e9f767287583)) * **script:** 修复超星频繁验证码的BUG ([8544e87](https://github.com/ocsjs/ocsjs/commit/8544e876d4ef63a7f6c3a918a9294f5feb8c5109)) * **script:** 修复超星章节测试填空题不填的问题 ([05a3cd1](https://github.com/ocsjs/ocsjs/commit/05a3cd11dadaa1700032b357727a4cf3ea807262)) * **script:** 修复超星BUG ([8fe0a40](https://github.com/ocsjs/ocsjs/commit/8fe0a4049d504d91e004c9a42ad3ac9ddea560b7)) * **script:** 修复窗口移除页面问题 ([bf2017a](https://github.com/ocsjs/ocsjs/commit/bf2017ae7d54c44cc8843ad5e361cd24751017b8)) * **script:** 修复答题器出现错误时会一直卡死 ([0f02745](https://github.com/ocsjs/ocsjs/commit/0f02745f020920b2f2fe7d8ab15f0e53509ae38c)) * **script:** 修复复制粘贴解除限制的BUG ([ebded93](https://github.com/ocsjs/ocsjs/commit/ebded937fa9a47926d7a977bcaae6f55597868b3)) * **script:** 修复通用-全局设置中题库状态检测BUG ([ebd6536](https://github.com/ocsjs/ocsjs/commit/ebd6536e26369eefc5d3ad8aa471e087f4f95b06)) * **script:** 修复学习通刷课逻辑 ([831f1d3](https://github.com/ocsjs/ocsjs/commit/831f1d33c7a9b350fe44c3121c6120f27720a2e4)) * **script:** 修复智慧树答题后不保存的BUG ([c56b54f](https://github.com/ocsjs/ocsjs/commit/c56b54f701d63feb3a47a2d53890348264e68be8)) * **script:** 修复智慧树作业答题完成后选择BUG ([8abf168](https://github.com/ocsjs/ocsjs/commit/8abf168d9efd5e8e170e8faa85b3285d7c847305)) * **script:** 优化播放函数 ([4204594](https://github.com/ocsjs/ocsjs/commit/4204594bc70fdd91d93f41bfd3975fba2a99e47a)) * **script:** 优化超星和智慧树脚本 ([cb5fe71](https://github.com/ocsjs/ocsjs/commit/cb5fe710be11d5652414b5470912a70a8bd2cc15)) * **script:** 优化窗口加载问题 ([e5cba2c](https://github.com/ocsjs/ocsjs/commit/e5cba2c809e6c385345e46121dc34afbacecc243)) ### Features * **all:** 脚本2.0与软件4.0更新完毕 ([6e04369](https://github.com/ocsjs/ocsjs/commit/6e043692f0fcfd4b5d56d785c2787c1bbdd22c9d)) * **all:** 脚本更新至 4.0 , 软件更新至 2.0 ([7cb995f](https://github.com/ocsjs/ocsjs/commit/7cb995f6d062ba79e09852f42be3565b8f107cd0)) * **app and web:** 将 electron 升级到 23.0.0 , 删除 electron 原生窗口样式 frame: false,自定义 titlebar ,并优化主题切换。 ([961235f](https://github.com/ocsjs/ocsjs/commit/961235fab116575b88aa5f71f0ca6aac87f71c0c)) * **app:** 将OCR识别模块整合打包,修复OCR路径存在空格执行失败的BUG ([9609af6](https://github.com/ocsjs/ocsjs/commit/9609af6d6f53b72b010de69e9df89294d0b27faa)) * **app:** 删除超星智慧树的其他登录,新增自定义链接进入。 ([5851511](https://github.com/ocsjs/ocsjs/commit/58515115728dfacfee83e1c67c3bb1efcb4ddb59)) * **app:** 文件中新增图片预览一栏,定时更新页面图像,无需在多个浏览器中切换查看。 ([526dbd6](https://github.com/ocsjs/ocsjs/commit/526dbd63e7bfa57828d1950517644801021db91b)) * **app:** 新增网页打开脚本,将ocr模块一同打包 ([7686cf1](https://github.com/ocsjs/ocsjs/commit/7686cf1a5a9d462e0ce0b8a3ff7c0e6235da562d)) * **app:** 新增仪表盘功能,新增浏览器拓展管理功能,新增新手教程功能,新增批量运行文件功能,并重写文件管理系统。 ([8adfede](https://github.com/ocsjs/ocsjs/commit/8adfede9f790cdeae588c95a3e5d7134802fffb1)) * **app:** 新增用户脚本,可自定义网络脚本添加到本地脚本,并自动载入本地脚本,极大拓展软件功能。 ([7926b35](https://github.com/ocsjs/ocsjs/commit/7926b354ffc9f14dc2404f45dd869da96495a7f2)) * **app:** 新增资源加载器,优化各种资源加载问题,移除原有的OCR模块打包方式。 ([1b83d8a](https://github.com/ocsjs/ocsjs/commit/1b83d8af6388fa568d83521c06e9322736c31b48)) * **app:** 修改app主进程从 commonjs 改成 ts ([3e29f82](https://github.com/ocsjs/ocsjs/commit/3e29f821cd1f5414c68ae0f944c512f8e94bdfb3)) * **app:** 优化 script 工作线程,规范代码,修复人工错误导致的无限递归。 ([156b2f7](https://github.com/ocsjs/ocsjs/commit/156b2f71ed4d42fae620b89c179f3b86ebaf2064)) * **core and app:** 新增软件服务端,使脚本可读取软件信息。 ([6f4ff54](https://github.com/ocsjs/ocsjs/commit/6f4ff54bb3a83fcb273a48ef41b5ba973d6d2f01)) * **core:** - 拆分 core ,- 添加多线程答题 - 添加超星作业考试功能 - 将 core/utils 全部进行 $ 前缀声明以便区分 - 优化搜题结果元素。 ([a252e85](https://github.com/ocsjs/ocsjs/commit/a252e855fb03d8335c28cb563c60b96a9317a0c1)) * **core:** 4.0 init ([4a43ee9](https://github.com/ocsjs/ocsjs/commit/4a43ee9bbb4af37ebf4317f87f5508067acd5139)) * **core:** 4.0 init ([7151eb3](https://github.com/ocsjs/ocsjs/commit/7151eb3643054021feadcdfb512871ba3916e4e1)) * **core:** 持续优化zhs答题功能 ([b120ce7](https://github.com/ocsjs/ocsjs/commit/b120ce718ea84db17a6d5fc3f43ad3214139b7de)) * **core:** 处理 onbeforeunload 执行结果 ([9e0fc87](https://github.com/ocsjs/ocsjs/commit/9e0fc87fad7652ab57bc3ce1080c3cf1ecb294e7)) * **core:** 给 script 的全部声明周期同样添加相同的事件触发 ([289c0d8](https://github.com/ocsjs/ocsjs/commit/289c0d8c746f60b9e69bc0dc22a1516505592db9)) * **core:** 将 OCSWoker 和 Script 都变成 EventEmitter 对象,实现内部的事件分发 ([2e0872c](https://github.com/ocsjs/ocsjs/commit/2e0872cf6cf2faa6088dbd1235aa32933b2b466f)) * **core:** 删除窗口关闭按钮,用户可以通过“窗口设置”进行隐藏窗口。 ([4a447e4](https://github.com/ocsjs/ocsjs/commit/4a447e4d5deb60dc2078862f4fb3dc36d92e9a67)) * **core:** 搜索结果页面新增复制题目按钮 ([4cbd032](https://github.com/ocsjs/ocsjs/commit/4cbd03217678a9426806f98828d76f2da664ba20)) * **core:** 添加 $creator 元素创建工具类变量,并且重写登录脚本,使用动态添加元素的方式重写。 ([ba94b16](https://github.com/ocsjs/ocsjs/commit/ba94b165eaefe2b95d5b349ce60fa88aec0d7f19)) * **core:** 添加 $modal.onClose 不管关闭还是确认和取消都会触发此函数 ([5cec198](https://github.com/ocsjs/ocsjs/commit/5cec1984bbf4da4e2bf8f518f9ba858a0c1bc340)) * **core:** 添加 onhistorychange 钩子 ([e8c1ecb](https://github.com/ocsjs/ocsjs/commit/e8c1ecb9ecbe7ec1a842952a0f7be58ac1d7964f)) * **core:** 添加 Script.configs 参数生产后 this.cfg 的代码提示 ([d31704e](https://github.com/ocsjs/ocsjs/commit/d31704e2716ca298e134a87cdc58189372e7b834)) * **core:** 添加 script.onrender 钩子 ([a81404b](https://github.com/ocsjs/ocsjs/commit/a81404b1071be98d486c8331dc066b99f17fa004)) * **core:** 添加 SearchResultsElement 元素 ([d66dffb](https://github.com/ocsjs/ocsjs/commit/d66dffb79ccab8726a4b6a8d90dc140f7637017e)) * **core:** 添加 StringUtils 工具类 ([4f1341a](https://github.com/ocsjs/ocsjs/commit/4f1341a7d57d4b667b0c07a71f7cd1a3a31b0fa8)) * **core:** 添加超星登录脚本 ([9b2e086](https://github.com/ocsjs/ocsjs/commit/9b2e086698cd2765d568538debcac75c6d701514)) * **core:** 添加面板选择器分组功能 ([226d1eb](https://github.com/ocsjs/ocsjs/commit/226d1eb8378d62bcb799e0c98b2a34e96fc9c446)) * **core:** 添加全局跨域通讯对象,并使用此技术重写模态框调出方法 ([81b1daf](https://github.com/ocsjs/ocsjs/commit/81b1daff19850cbb084a5eb6564e88ef2be4db7e)) * **core:** 添加使用教程, 重载 el 函数, 修改cors跨域模块使用 setTab, getTab 进行cors跨域标签分区. 优化页面选择逻辑 ([eaa6ae5](https://github.com/ocsjs/ocsjs/commit/eaa6ae5030fcfc31db905ed24969995d06014af3)) * **core:** 添加页面复制粘贴限制解除脚本 ([4acdfd0](https://github.com/ocsjs/ocsjs/commit/4acdfd018e8dd55360e78f66547d6a56541c4ffe)) * **core:** 添加在线搜题功能 ([dc26b2e](https://github.com/ocsjs/ocsjs/commit/dc26b2e0e52d59d015546b960e533c09f2d39e27)) * **core:** 添加智慧树自动答题功能 ([642901d](https://github.com/ocsjs/ocsjs/commit/642901d470a3b69f826dc8c9f331cbd306ebd713)) * **core:** 完成4.0基本架构 ([23129f4](https://github.com/ocsjs/ocsjs/commit/23129f43581acab6e367b6ff38f0fe4b66f04708)) * **core:** 完成脚本内容的元素和数值同步 ([4d1dc60](https://github.com/ocsjs/ocsjs/commit/4d1dc60985b5b9623dd61a351babd8640f5a4259)) * **core:** 完成智慧树登录脚本,学习脚本,修复BUG ([f07dd0f](https://github.com/ocsjs/ocsjs/commit/f07dd0fdaa468df626ba5fa03a503bfc8cb72bd5)) * **core:** 新增 $creator 多个API ([d245726](https://github.com/ocsjs/ocsjs/commit/d245726df9f4847b8a8df47375a13e03b35d7fb4)) * **core:** 新增 $message 和 $model 方法进行用户交互 ([c6d365c](https://github.com/ocsjs/ocsjs/commit/c6d365ce13f046ed93971d9991dfcfad3990b419)) * **core:** 新增 通用-搜索结果 显示 ([9d9ead8](https://github.com/ocsjs/ocsjs/commit/9d9ead85747ccd94f922d7f50573ff99d9b9761a)) * **core:** 新增 智慧职教mooc脚本 , 优化职教云脚本 ([0e44978](https://github.com/ocsjs/ocsjs/commit/0e44978c57e81afcaf28b55c1adfdb4eac297db0)) * **core:** 新增 Script.methods 方法可自定义对外暴露函数,优化搜索结果的显示。 ([53043d1](https://github.com/ocsjs/ocsjs/commit/53043d1590f09c557ae0059233806dd43c06653e)) * **core:** 新增 SCript.pin 方法,置顶某个面板 ([766f87e](https://github.com/ocsjs/ocsjs/commit/766f87e3f03c6216706b67a1cd6801c55c08f78b)) * **core:** 新增 unsafeWindow 全局变量 ([9438a1e](https://github.com/ocsjs/ocsjs/commit/9438a1efc4451adf5160ca9067407deaa9707c5b)) * **core:** 新增超星直播回放视频脚本 ([3788a6f](https://github.com/ocsjs/ocsjs/commit/3788a6fc5bb852b4847025b4df88a5b9499ce4de)) * **core:** 新增跨域响应式特性 ([d7f967e](https://github.com/ocsjs/ocsjs/commit/d7f967ec27fd3d67de9d8132dd60100765727384)) * **core:** 新增智慧树:清晰度选择,定时停止,视频总时长计算,等功能 ([7204f26](https://github.com/ocsjs/ocsjs/commit/7204f26f7b2c3c6700df1f7d9d08a43371cd8c35)) * **core:** 新增智慧树的视频画质选择功能 ([290ce51](https://github.com/ocsjs/ocsjs/commit/290ce51032e2a8b773b61275ada58002ade0aa22)) * **core:** 新增智慧树视频反反混淆脚本 ([5de857f](https://github.com/ocsjs/ocsjs/commit/5de857fb12f229fbed1284d507bac87500e5b6ce)) * **core:** 新增智慧树验证码检测功能 ([dcf73b3](https://github.com/ocsjs/ocsjs/commit/dcf73b317cb7492a971a74c77af03e91a13eb1bb)) * **core:** 新增智慧职教(职教云)脚本 ([59c0cc2](https://github.com/ocsjs/ocsjs/commit/59c0cc2b66fa1af2418e682bec014342f3ed0e7f)) * **core:** 新增j脚本热更新功能,大大提升开发效率 ([42ba110](https://github.com/ocsjs/ocsjs/commit/42ba110b2cc8e54e043b73979216a8f22c9017fe)) * **core:** 修改脚本声明写法,修改 project.scripts 由数组变成对象声明,好处是可以由 project.scripts.[脚本名].cfg.xxx 进行类型推断实现类型提示。 ([b73fdb0](https://github.com/ocsjs/ocsjs/commit/b73fdb021f9b050278f321c7d353f81e8869d964)) * **core:** 优化 cx 的 Project 新写法 ([61f466a](https://github.com/ocsjs/ocsjs/commit/61f466a0e4e85a59985bb59c84ca7efc04975213)) * **core:** 优化使用$creator ([23efe42](https://github.com/ocsjs/ocsjs/commit/23efe4274f1b6604e85189a39bc47cdc7e5542b1)) * **core:** 优化渲染工程 ([31b5728](https://github.com/ocsjs/ocsjs/commit/31b5728843b44f244bb3abbf4f5f72f79259350e)) * **core:** 优化智慧树文字识别脚本 ([608e760](https://github.com/ocsjs/ocsjs/commit/608e76076d63d3b22c55c0baaf2df008e0b32313)) * **core:** 增加多线程查题功能 ([be3b85c](https://github.com/ocsjs/ocsjs/commit/be3b85cdf382c9add8e5b155c2b1a97dbbb46bd4)) * **core:** 增加智慧树习惯分检测,学习记录查询,答题手动控制 ([e753da9](https://github.com/ocsjs/ocsjs/commit/e753da9cee93b0873e5d2be7b09c7770451cef7a)) * **core:** 支持跨域调出模态框 ([f1f4271](https://github.com/ocsjs/ocsjs/commit/f1f427166d21622fed299399456e235386a69cb6)) * **core:** 智慧职教(职教云)发布 ([f0eb02f](https://github.com/ocsjs/ocsjs/commit/f0eb02f2ada42fb64a5f3767689ddf7af0c303e9)) * **script and core:** 添加全局错误捕获功能 ([476483d](https://github.com/ocsjs/ocsjs/commit/476483da346104b63766810953fa4a254ed0a72f)) * **script:** 超星刷课逻辑重写,兼容旧版浏览器CSS样式 ([184e0e9](https://github.com/ocsjs/ocsjs/commit/184e0e9910aa1eccf55b89d6a360e9a218d648d8)) * **script:** 添加浏览器版本检测 ([5c81815](https://github.com/ocsjs/ocsjs/commit/5c81815d81ad1dad5ed998a5dfe14a4db0935021)) * **script:** 添加页面关闭提示 ([477fb01](https://github.com/ocsjs/ocsjs/commit/477fb01e8e92e14c71382daa24ce9852d7bd9c4a)) * **script:** 添加智慧职教音频支持 ([0e6402c](https://github.com/ocsjs/ocsjs/commit/0e6402c019afbaeca24da2900c3a81771faa5249)) * **script:** 新增【职教云】和【智慧职教】脚本 ([0cf366a](https://github.com/ocsjs/ocsjs/commit/0cf366a5ef082496a0108902fd58dfa874e9eb98)) * **script:** 新增浏览器最小化检测脚本 ([4934f65](https://github.com/ocsjs/ocsjs/commit/4934f6535ed2a7c1d0f078ba136b5213f5d8ba12)) * **script:** 修改浏览器下载链接,新增脚本版本更新检测 ([9265c2b](https://github.com/ocsjs/ocsjs/commit/9265c2ba09639121c417e9042fcd0230ca3da00a)) * **script:** version release 4.0.5 ([d7c4574](https://github.com/ocsjs/ocsjs/commit/d7c4574faa2da036a5b04f4a48b6840dd74461b9)) * **script:** version update to 4.1.0 ([216596f](https://github.com/ocsjs/ocsjs/commit/216596fde62d1591696dde101a49eff27619085c)) * **utils:** 新增utils包,其内置各种实用工具。其中新增脚本打包器,可对打包流程进行优化。 ([01879dd](https://github.com/ocsjs/ocsjs/commit/01879dd251cdb299df799631262c268a3de827af)) ### Performance Improvements * **core:** 更新 MoelElement 参数以及实现 ([219ca6f](https://github.com/ocsjs/ocsjs/commit/219ca6faab611da9c9aa9c6afd68cdc5bcb3fa71)) * **core:** 添加 SearchResultsElement 元素映射 ([6f86f6f](https://github.com/ocsjs/ocsjs/commit/6f86f6f4dffc42d28983e902c677372703defa53)) * **core:** 添加defineScript中的domain和hide字段,实现动态修改脚本头部信息的功能。 ([a13ab50](https://github.com/ocsjs/ocsjs/commit/a13ab5008ff34f589d9ddd0184c5e320afc2180a)) # [3.13.0](https://github.com/ocsjs/ocsjs/compare/3.12.3...3.13.0) (2022-05-23) ### Features * **core:** 添加页面反调试脚本 ([1174617](https://github.com/ocsjs/ocsjs/commit/1174617cee99dd4d2e546d79279160ba1afea40e)) * **core:** 新增超星视频中答题功能 ([3f925cb](https://github.com/ocsjs/ocsjs/commit/3f925cba72910fce0353df421dec75a137f24e4e)) * **core:** 新增网课视频选项:显示视频进度 ([b6086df](https://github.com/ocsjs/ocsjs/commit/b6086df349fa50434e199c88c8fff6414bed4c14)) ## [3.12.3](https://github.com/ocsjs/ocsjs/compare/3.12.2...3.12.3) (2022-05-22) ### Bug Fixes * **core:** 修复误删最大长宽导致的超出页面范围 ([9b0bcd4](https://github.com/ocsjs/ocsjs/commit/9b0bcd4d0f26fb8591950e73563be78a5c3d876d)) ## [3.12.2](https://github.com/ocsjs/ocsjs/compare/3.12.0...3.12.2) (2022-05-21) ### Bug Fixes * **core:** 修复某些填空题识别不出的BUG ([c2c1c3d](https://github.com/ocsjs/ocsjs/commit/c2c1c3d2a7fafbe569b25531070b1803e07ccfe6)) ### Features * **core:** 新增自动答题选项:强制提交 ([e0ff3a2](https://github.com/ocsjs/ocsjs/commit/e0ff3a2c64d9e9de061889d2ad145ed29a492cb1)) # [3.12.0](https://github.com/ocsjs/ocsjs/compare/3.11.0...3.12.0) (2022-05-21) ### Bug Fixes * **core:** 删除多余输出 ([8cee017](https://github.com/ocsjs/ocsjs/commit/8cee0173c670633d665bddd24c4884fa3ad3f621)) * **core:** 修改userjs打包代码未加分号报错 BUG ([e0ec319](https://github.com/ocsjs/ocsjs/commit/e0ec319150ec2f42a4a1256ae9250a3bfd1ae779)) * **core:** 优化多选题答案分割判断 ([7da6f92](https://github.com/ocsjs/ocsjs/commit/7da6f92074a4a9ced316be312aa664ea3f75af52)) ### Features * **core:** 新增随机作答功能 ([ecc2a87](https://github.com/ocsjs/ocsjs/commit/ecc2a87c24e2e81e82a6f1d4fe737b3ccab69b8c)) * **core:** 新增图片题识别脚本,新增搜索结果显示题目图片和答案图片 ([fd483c3](https://github.com/ocsjs/ocsjs/commit/fd483c3bbbe9af689e3b006c21ae12cdeccdd73a)) # [3.11.0](https://github.com/ocsjs/ocsjs/compare/3.10.6...3.11.0) (2022-05-21) ### Bug Fixes * **core:** 新增未经压缩代码的打包 ([4099fc4](https://github.com/ocsjs/ocsjs/commit/4099fc428d6cfa3b835bf7c89f29bb63b11ad090)) ### Features * **core:** 新增userjs未经压缩代码打包 ([42badc8](https://github.com/ocsjs/ocsjs/commit/42badc85967abb2486c3a6895b8ffd8f9155f05a)) ## [3.10.6](https://github.com/ocsjs/ocsjs/compare/3.10.4...3.10.6) (2022-05-20) ### Bug Fixes * **core:** 修复题库配置解析器BUG ([3986a49](https://github.com/ocsjs/ocsjs/commit/3986a49a1323bc17f8cca54763586c23f6e03907)) ## [3.10.4](https://github.com/ocsjs/ocsjs/compare/3.10.2...3.10.4) (2022-05-20) ### Bug Fixes * **app:** 修复浏览器报错BUG ([fd3780c](https://github.com/ocsjs/ocsjs/commit/fd3780c2d9fdb58016b06f090696056934888f89)) * **core:** 修复超星考试页面样式问题 ([03fa9be](https://github.com/ocsjs/ocsjs/commit/03fa9bed5e176377e921deffbacf0212ccd85e34)) * **core:** 修复题库配置BUG ([2b8c939](https://github.com/ocsjs/ocsjs/commit/2b8c9391cecb7aed7e55b47694772feaed2e45fc)) ## [3.10.2](https://github.com/ocsjs/ocsjs/compare/3.10.1...3.10.2) (2022-05-19) ### Bug Fixes * **core:** 还原文件,修改 release 执行错误但继续打包发布的BUG ([201ae0f](https://github.com/ocsjs/ocsjs/commit/201ae0f6face34db36c9edffe0e323e744ea0106)) ## [3.10.1](https://github.com/ocsjs/ocsjs/compare/3.10.0...3.10.1) (2022-05-19) ### Bug Fixes * **app:** 修复软件浏览器选择BUG,并停止火狐浏览器使用。 ([5e2559b](https://github.com/ocsjs/ocsjs/commit/5e2559bfdea087806246120da566410b54a39c0b)) * **core:** 修改 typr 库,减少部分打包体积。 ([51e26de](https://github.com/ocsjs/ocsjs/commit/51e26debe37c9ad45e181a9d67528244863e7b33)) # [3.10.0](https://github.com/ocsjs/ocsjs/compare/3.9.6...3.10.0) (2022-05-17) ### Features * **core:** 新增超星繁体字识别选项 - 字典识别 ([2a241d6](https://github.com/ocsjs/ocsjs/commit/2a241d6fe987316b335e57dd9b8b19be188f1805)) * **core:** 新增超星强制答题功能 ([e7c4fc0](https://github.com/ocsjs/ocsjs/commit/e7c4fc060c30b341c17fefa0148fde5170069c87)) ## [3.9.6](https://github.com/ocsjs/ocsjs/compare/3.9.5...3.9.6) (2022-05-16) ### Bug Fixes * 修复日志记录问题 ([c9f2147](https://github.com/ocsjs/ocsjs/commit/c9f21473a755a9af6e7bbeed4095f2209d64cb6e)) * **app:** 修复软件文件重命名时,运行文件名不同步的BUG ([28a1af1](https://github.com/ocsjs/ocsjs/commit/28a1af141217c83cc891952731b585153d6d3d59)) * **app:** 修复智慧树登录后白屏错误的BUG ([9622f7e](https://github.com/ocsjs/ocsjs/commit/9622f7e4c809e64be76c2cd4309e01f1ba9a4e44)) ### Features * **app:** 新增软件自动选择浏览器路径功能 ([eb56f67](https://github.com/ocsjs/ocsjs/commit/eb56f67e6885badcfa252fc716ac6e203560fc2a)) * **app:** 新增自定义脚本载入路径功能, 路径将托管到官方服务器 ([289cefe](https://github.com/ocsjs/ocsjs/commit/289cefecae8b7c3ed900bba1cb99150438a0a842)) ## [3.9.5](https://github.com/ocsjs/ocsjs/compare/3.8.0...3.9.5) (2022-05-16) ### Bug Fixes * 修复新增 shadowroot 后,复制粘贴脚本失效的BUG ([081a99e](https://github.com/ocsjs/ocsjs/commit/081a99ee62a47f427e02b33e3497e5f95e6dedfa)) * 优化超星识别时可能遇到选项按钮被删除的BUG ([ef396b4](https://github.com/ocsjs/ocsjs/commit/ef396b4a3bc9f10284529c3cbb857edba9c927ca)) ### Features * 新增 AnswererWrapper 参数: headers 和 type ([8c970bb](https://github.com/ocsjs/ocsjs/commit/8c970bb52a9b6ef83618b7f1dc2d55fa26045024)) * 新增题库配置跨域模块,可对不同域名的服务器进行跨域访问,并且新增 root 环境变量,可访问元素题目的跟节点元素对象。 ([4e8ea1c](https://github.com/ocsjs/ocsjs/commit/4e8ea1c6bdc84a1b0ce84b24af48a91cff0830af)) # [3.8.0](https://github.com/ocsjs/ocsjs/compare/3.7.4...3.8.0) (2022-05-11) ### Bug Fixes * 避免重复劫持函数导致页面内存移除 ([067ee58](https://github.com/ocsjs/ocsjs/commit/067ee58e6ffa74657a8adf213ad32fcda0799243)) * 修复智慧树倍速不能立刻改变的BUG ([39d7402](https://github.com/ocsjs/ocsjs/commit/39d7402d804ad5adb8556150d58ae9277f97229f)) ### Features * + 新增消息提示 + 修复火狐底部版本不显示的BUG + 优化eslint代码 + 增加文字识别错误提示 + 将原有弹出框修改为消息提示 + 增加API:message ([534bee3](https://github.com/ocsjs/ocsjs/commit/534bee3b6e449b9c725cbdec9efa27979c6545ed)) * 使用ShadowRoot对脚本进行加固 ([346d9d1](https://github.com/ocsjs/ocsjs/commit/346d9d109499e303704f7a05414631d9c7e3b11c)) ## [3.7.4](https://github.com/ocsjs/ocsjs/compare/3.7.3...3.7.4) (2022-05-09) ## [3.7.3](https://github.com/ocsjs/ocsjs/compare/3.7.2...3.7.3) (2022-05-08) ## [3.7.2](https://github.com/ocsjs/ocsjs/compare/3.7.0...3.7.2) (2022-05-07) ### Bug Fixes * 修改 store 初始化位置 ([808eb08](https://github.com/ocsjs/ocsjs/commit/808eb080a50e75ecef151010b74891ce272938c9)) ### Features * 添加智慧树文本识别脚本和屏蔽视频检测脚本 ([85ddac1](https://github.com/ocsjs/ocsjs/commit/85ddac119874d5b6877dfc8fa29ecdff70fec4ed)) # [3.7.0](https://github.com/ocsjs/ocsjs/compare/3.6.4...3.7.0) (2022-05-04) ### Features * 添加答题等待时间,方便检查或者使用其他答题工具。 ([12f2960](https://github.com/ocsjs/ocsjs/commit/12f2960a47f3eb22cc9bbf91bf49c2ace54ea89e)) ## [3.6.4](https://github.com/ocsjs/ocsjs/compare/3.6.2...3.6.4) (2022-05-04) ### Bug Fixes * 深度优化OCR ([a21a8ad](https://github.com/ocsjs/ocsjs/commit/a21a8adf3b209ae5e65aff26c134da2a7b1f0fd2)) * 修复填空题多个填空不填的BUG ([4a0f031](https://github.com/ocsjs/ocsjs/commit/4a0f031b244a800b1e6ff39d3b1b99160372e66d)) ## [3.6.2](https://github.com/ocsjs/ocsjs/compare/3.6.1...3.6.2) (2022-04-30) ### Bug Fixes * 修改 OCR 脚本加载路径, 确保能够访问 work 和 core 脚本 ([9b1544a](https://github.com/ocsjs/ocsjs/commit/9b1544a8e2b002d78471d387e507967e20281020)) ## [3.6.1](https://github.com/ocsjs/ocsjs/compare/3.6.0...3.6.1) (2022-04-30) ### Bug Fixes * 修改环境依赖 ([9c69d35](https://github.com/ocsjs/ocsjs/commit/9c69d3581a84a8e50d87dd7a5d7ed3f446e45295)) # [3.6.0](https://github.com/ocsjs/ocsjs/compare/3.5.5...3.6.0) (2022-04-29) ### Features * 新增智慧树共享课考试脚本 ([5ba2022](https://github.com/ocsjs/ocsjs/commit/5ba2022e084f7af9ef01309e7d88f8d42436732a)) ## [3.5.5](https://github.com/ocsjs/ocsjs/compare/3.5.4...3.5.5) (2022-04-28) ### Bug Fixes * 适当增大了文本便于识别 ([483d68b](https://github.com/ocsjs/ocsjs/commit/483d68b44720babd1a2f8432661712d869433ad4)) ## [3.5.4](https://github.com/ocsjs/ocsjs/compare/3.5.3...3.5.4) (2022-04-28) ### Bug Fixes * 优化OCR空格问题,还有上个版本OCR锁初始化的问题 ([82a84d7](https://github.com/ocsjs/ocsjs/commit/82a84d778aa99849f26fdd87447f41c7bdda72e2)) ## [3.5.3](https://github.com/ocsjs/ocsjs/compare/3.5.2...3.5.3) (2022-04-28) ### Bug Fixes * 优化 OCR , 解决题目选项没有识别的BUG ([e534658](https://github.com/ocsjs/ocsjs/commit/e534658665be9863d19fc52ee787f7cad696bdd8)) ## [3.5.2](https://github.com/ocsjs/ocsjs/compare/3.5.1...3.5.2) (2022-04-27) ### Bug Fixes * 优化 OCR 加载逻辑 ([9dd69dc](https://github.com/ocsjs/ocsjs/commit/9dd69dce3073b137999c753b50f7fec8ee03a8a2)) ## [3.5.1](https://github.com/ocsjs/ocsjs/compare/3.5.0...3.5.1) (2022-04-27) ### Bug Fixes * 优化 OCR 数据加载问题 ([ed958a9](https://github.com/ocsjs/ocsjs/commit/ed958a9a47de90763daf7b59c7c1fa3abd698b7f)) # [3.5.0](https://github.com/ocsjs/ocsjs/compare/3.4.5...3.5.0) (2022-04-27) ### Bug Fixes * 修复超星有时不能自动下一章的BUG ([7c5516f](https://github.com/ocsjs/ocsjs/commit/7c5516f731295ccc2fd73129bf659dd8b339d2f1)) ### Features * 繁体字乱码识别功能 ([ec01cd1](https://github.com/ocsjs/ocsjs/commit/ec01cd168a4f0f4111e7cb6e3c1597b07d7dfd14)) * 开发智慧树倍速选项 ([83015b7](https://github.com/ocsjs/ocsjs/commit/83015b73bdb0bda4f23af04b3814b8cd7e21d721)) ## [3.4.5](https://github.com/ocsjs/ocsjs/compare/3.4.4...3.4.5) (2022-04-23) ### Bug Fixes * 修复软件重命名有时会无效的BUG ([48fb520](https://github.com/ocsjs/ocsjs/commit/48fb520263fabb4740138705bf55d5086e425907)) * 优化软件启动加载 ([be866d1](https://github.com/ocsjs/ocsjs/commit/be866d1eb966a64aefe5fd95ffaf2c1c53ae76d8)) ## [3.4.4](https://github.com/ocsjs/ocsjs/compare/3.4.3...3.4.4) (2022-04-22) ### Bug Fixes * 修复提交设置的BUG ([612dccc](https://github.com/ocsjs/ocsjs/commit/612dcccc40bec2871cccf92bb46c2210da76623e)) ## [3.4.3](https://github.com/ocsjs/ocsjs/compare/3.4.2...3.4.3) (2022-04-21) ### Bug Fixes * 删除无用的选项:搜题错误时暂停 ([e30446c](https://github.com/ocsjs/ocsjs/commit/e30446c2627e628a8aa1c4bb7843c32734131e80)) ## [3.4.2](https://github.com/ocsjs/ocsjs/compare/3.4.1...3.4.2) (2022-04-19) ### Bug Fixes * 修复软件设置空白的BUG ([f757a3b](https://github.com/ocsjs/ocsjs/commit/f757a3b8ae6233eee6fd187471d866b7ec25028a)) * 修复自动答题提交设置保存不了的BUG ([fb9bd39](https://github.com/ocsjs/ocsjs/commit/fb9bd394c246b7446173fba0d956431c300b6577)) * app version upate ([505ce22](https://github.com/ocsjs/ocsjs/commit/505ce22fc11c799da5c22b6f183182ea0b43eb6d)) ## [3.4.1](https://github.com/ocsjs/ocsjs/compare/3.4.0...3.4.1) (2022-04-17) ### Bug Fixes * 修复ABCD纯答案直接点击的BUG ([9df1222](https://github.com/ocsjs/ocsjs/commit/9df1222dfa7569ee0995284cbc988ddda7c292bc)) # [3.4.0](https://github.com/ocsjs/ocsjs/compare/3.3.14...3.4.0) (2022-04-17) ### Features * 新增智慧树学分课作业脚本 ([978bf47](https://github.com/ocsjs/ocsjs/commit/978bf47feec507910ae62e0be60a79ffd8d46941)) ## [3.3.14](https://github.com/ocsjs/ocsjs/compare/3.3.13...3.3.14) (2022-04-15) ### Bug Fixes * 修复 release.sh 版本命令 ([652a022](https://github.com/ocsjs/ocsjs/commit/652a022b1f895e0ec848dc0b54f238d4f8192772)) ## [3.3.13](https://github.com/ocsjs/ocsjs/compare/v3.3.12...3.3.13) (2022-04-15) ## [3.3.12](https://github.com/ocsjs/ocsjs/compare/v3.3.11...v3.3.12) (2022-04-13) ## [3.3.11](https://github.com/ocsjs/ocsjs/compare/v3.3.10...v3.3.11) (2022-04-13) ### Bug Fixes * 修复 paste 和 input 共存导致的粘贴BUG ([370e46f](https://github.com/ocsjs/ocsjs/commit/370e46fe3eecffbb5b63c5576dfe6447e79860bd)) ## [3.3.10](https://github.com/ocsjs/ocsjs/compare/v3.3.9...v3.3.10) (2022-04-13) ### Bug Fixes * 修复不能右键复制粘贴的BUG ([9af5480](https://github.com/ocsjs/ocsjs/commit/9af5480038ba11bd47937bff889ee084c72fb04b)) ## [3.3.9](https://github.com/ocsjs/ocsjs/compare/v3.3.8...v3.3.9) (2022-04-13) ### Bug Fixes * 修复上个版本store加载问题 ([48fdc1a](https://github.com/ocsjs/ocsjs/commit/48fdc1a57fbcfa587f2dc42f338249a7b3222985)) ## [3.3.8](https://github.com/ocsjs/ocsjs/compare/v3.3.7...v3.3.8) (2022-04-13) ### Bug Fixes * app 兼容OCS助手最新版的响应式特性 ([eaa66dd](https://github.com/ocsjs/ocsjs/commit/eaa66dd9a1c647f12602363f08003c9254ba0f7d)) ### Features * 新增全局存储功能,使用油猴自带API实现。 ([be13a5b](https://github.com/ocsjs/ocsjs/commit/be13a5bd71884ef9f9913a8e2c391da9ecedef4b)) ## [3.3.7](https://github.com/ocsjs/ocsjs/compare/v3.3.6...v3.3.7) (2022-04-13) ### Bug Fixes * 支持纯浏览器端的加载,仅用于核心API的调用。 ([cf9dbe8](https://github.com/ocsjs/ocsjs/commit/cf9dbe8d779c05ba804aab43c242ddde1af8e7c6)) ### Features * 删除调试输出 ([a096447](https://github.com/ocsjs/ocsjs/commit/a096447a45573e6bfb05d249aad374e2e56530dd)) ## [3.3.6](https://github.com/ocsjs/ocsjs/compare/v3.3.5...v3.3.6) (2022-04-13) ### Bug Fixes * 修复打包压缩选项 ([9425289](https://github.com/ocsjs/ocsjs/commit/9425289ed6cc864f95f59414783f88db1968d140)) ### Features * 新增超星 : 屏蔽作业考试填空简答题粘贴限制 功能 ([591971e](https://github.com/ocsjs/ocsjs/commit/591971eea5065b4097e6b24516cb6d5a36497f76)) ## [3.2.20](https://github.com/ocsjs/ocsjs/compare/v3.2.19...v3.2.20) (2022-04-09) ### Bug Fixes * 延长一点视频暂停后启动的时间,防止超星鬼畜。 ([837d873](https://github.com/ocsjs/ocsjs/commit/837d873d29146de7741e9cfb4b4e988633767551)) ## [3.3.5](https://github.com/ocsjs/ocsjs/compare/v3.3.4...v3.3.5) (2022-04-13) ### Bug Fixes * 修复搜索结果 undefined 的 BUG, 修复超星章节测试填空题BUG ([97500c5](https://github.com/ocsjs/ocsjs/commit/97500c591315eb4fdc97b799e77e788ccdf32def)) ## [3.3.4](https://github.com/ocsjs/ocsjs/compare/v3.3.3...v3.3.4) (2022-04-13) ### Bug Fixes * 修复超星考试作业页面不能复制粘贴的BUG ([4c64c00](https://github.com/ocsjs/ocsjs/commit/4c64c009f6bf87ad63d5b7f717daef339782bdc4)) * 修复上个版本响应式导致的倍速,音量失效的BUG ([5182d02](https://github.com/ocsjs/ocsjs/commit/5182d02d0fc1a9e11f7b7b2bc5ee3e0387c2b74e)) * 修复智慧树复习模式的BUG,修复已经播放完的视频但没有完成却跳过的BUG ([3cd12a7](https://github.com/ocsjs/ocsjs/commit/3cd12a72312faec10200a1a4bb700fe17e3e3682)) ## [3.3.3](https://github.com/ocsjs/ocsjs/compare/v3.3.2...v3.3.3) (2022-04-12) ### Bug Fixes * 兼容库模式,并且优化自动答题答案显示 ([fcd2311](https://github.com/ocsjs/ocsjs/commit/fcd2311b3b2564a3a06b2da102cdf841fbd19fc1)) ### Features * 新增自动答题答案预览功能 ([29b17e8](https://github.com/ocsjs/ocsjs/commit/29b17e8d055272882aebf672dbf76006f5509fc1)) ## [3.3.2](https://github.com/ocsjs/ocsjs/compare/v3.3.1...v3.3.2) (2022-04-12) ### Bug Fixes * 修复闯关模式因为刷新太快任务点没有出现导致重复的BUG ([dad5eb3](https://github.com/ocsjs/ocsjs/commit/dad5eb3db14c112e7c8f65c6214402502f2a8764)) * 修复答题配置输入后会消失的BUG, 新增音量设置 ([34cd585](https://github.com/ocsjs/ocsjs/commit/34cd585203623e606e459fd7761f77001a0b6911)) ## [3.3.1](https://github.com/ocsjs/ocsjs/compare/v3.3.0...v3.3.1) (2022-04-11) # [3.3.0](https://github.com/ocsjs/ocsjs/compare/v3.2.20...v3.3.0) (2022-04-11) ### Bug Fixes * 修复各种问题,并且兼容了响应式特性 ([0c42947](https://github.com/ocsjs/ocsjs/commit/0c42947afa70f75f57435d5bebdd1dd48141754a)) * 修改打包方式为压缩打包 ([333661f](https://github.com/ocsjs/ocsjs/commit/333661f4c3a5338721e56227ef0e6fcb53ea872f)) ### Features * 切换网络路线 ([0a0a5dd](https://github.com/ocsjs/ocsjs/commit/0a0a5dd20ce80cb503f3c57d8b6aa2c25e261b8d)) * 响应式特性 ([5e21f0a](https://github.com/ocsjs/ocsjs/commit/5e21f0a1c17b1aca8ba22692c6649c6bae397d51)) * vnode 重构成 tsx , 并且新增数据响应式特性 ([18c5a78](https://github.com/ocsjs/ocsjs/commit/18c5a7836cd11222f8301687fff930b5e93583c0)) ## [3.2.20](https://github.com/ocsjs/ocsjs/compare/v3.2.19...v3.2.20) (2022-04-09) ### Bug Fixes * 延长一点视频暂停后启动的时间,防止超星鬼畜。 ([837d873](https://github.com/ocsjs/ocsjs/commit/837d873d29146de7741e9cfb4b4e988633767551)) ## [3.2.19](https://github.com/ocsjs/ocsjs/compare/v3.2.18...v3.2.19) (2022-04-08) ### Bug Fixes * 修复切换路线后倍速无效的BUG ([4e3f78b](https://github.com/ocsjs/ocsjs/commit/4e3f78b78f5ae3467d4546fc23d4aba0aa8fda4e)) ## [3.2.18](https://github.com/ocsjs/ocsjs/compare/v3.2.16...v3.2.18) (2022-04-08) ### Features * 新增超星视频路线切换功能 ([8721e48](https://github.com/ocsjs/ocsjs/commit/8721e4895c46393b210a1f707b61f5b7a446ecee)) ## [3.2.16](https://github.com/ocsjs/ocsjs/compare/v3.2.14...v3.2.16) (2022-04-08) ### Bug Fixes * 修复快捷键有时候失效的问题 ([0d5df16](https://github.com/ocsjs/ocsjs/commit/0d5df16cbd18936180935d02642cf68faea8c22f)) ## [3.2.14](https://github.com/ocsjs/ocsjs/compare/v3.2.13...v3.2.14) (2022-04-08) ### Features * 新增隐藏按钮 ([dce116e](https://github.com/ocsjs/ocsjs/commit/dce116e6f268bffe5ef1e178980c4318b47ea755)) * 新增ocs快捷键,可重置位置,优化面板初始位置 ([af7d231](https://github.com/ocsjs/ocsjs/commit/af7d231d9af520ace066190981196975a4d08213)) ## [3.2.13](https://github.com/ocsjs/ocsjs/compare/v3.2.12...v3.2.13) (2022-04-07) ### Features * 彻底修复超星验证码问题 ([0cf69f2](https://github.com/ocsjs/ocsjs/commit/0cf69f22f070d47ed72cd4c1d5b898b6ad3e2e14)) ## [3.2.12](https://github.com/ocsjs/ocsjs/compare/v3.2.11...v3.2.12) (2022-04-05) ### Features * 新增超星支持域名 edu.cn , 修复多个视频播放时,播放完成继续播放的BUG ([959cc9b](https://github.com/ocsjs/ocsjs/commit/959cc9b2b38e6b573823ee5c9976f92089bdc5bb)) ## [3.2.11](https://github.com/ocsjs/ocsjs/compare/v3.2.10...v3.2.11) (2022-04-04) ### Bug Fixes * 修改点击间隔 ([176d83e](https://github.com/ocsjs/ocsjs/commit/176d83eaaa210b562ab38458ee15a4969c6b080e)) ## [3.2.10](https://github.com/ocsjs/ocsjs/compare/v3.2.9...v3.2.10) (2022-04-04) ### Bug Fixes * 继续优化答题问题 ([2dfd14d](https://github.com/ocsjs/ocsjs/commit/2dfd14d57e195615a257ac21b0db6fa167714768)) ## [3.2.9](https://github.com/ocsjs/ocsjs/compare/v3.2.8...v3.2.9) (2022-04-04) ### Bug Fixes * 修复超星重复暂停的BUG ([c5c7681](https://github.com/ocsjs/ocsjs/commit/c5c768176e53c7266c9e90b97ec351943627143a)) ## [3.2.8](https://github.com/ocsjs/ocsjs/compare/v3.2.7...v3.2.8) (2022-04-04) ### Features * 新增解除右键,复制粘贴限制的功能, 修复超星播放视频重复卡死的BUG ([fe9b59a](https://github.com/ocsjs/ocsjs/commit/fe9b59ac28baded93f30539daf262a18870fedf0)) ## [3.2.7](https://github.com/ocsjs/ocsjs/compare/v3.2.6...v3.2.7) (2022-04-04) ### Features * 答案判断优化 ([801e4f3](https://github.com/ocsjs/ocsjs/commit/801e4f3a0ffe66c6979833bae113f868bbba3f38)) ## [3.2.6](https://github.com/ocsjs/ocsjs/compare/v3.2.5...v3.2.6) (2022-04-03) ### Bug Fixes * 修复软件1.2.0自动更新BUG ([512b61c](https://github.com/ocsjs/ocsjs/commit/512b61c26f8b72828cd875b9ecf867dcc879f3b7)) * 修复智慧树作业重复关闭的BUG ([2288d61](https://github.com/ocsjs/ocsjs/commit/2288d6119af7efa97761e2bc8cca3393b62e507f)) ### Features * 新增 common 包 , 修复脚本执行BUG , 修复通知 , 新增打包脚本 scripts 文件夹 ([0831102](https://github.com/ocsjs/ocsjs/commit/083110233cc5462ed5d7d2c44bfc7141f9c8f9e8)) * aPP版本更新 1.2.0 ([4ebd3cb](https://github.com/ocsjs/ocsjs/commit/4ebd3cbdc5775c7c4f60fd6c00f621297fed7d00)) ## [3.2.5](https://github.com/ocsjs/ocsjs/compare/v3.2.4...v3.2.5) (2022-04-01) ### Bug Fixes * 修复答题时多选不全的BUG, 修复答题时答案为空报错的BUG ([75177b5](https://github.com/ocsjs/ocsjs/commit/75177b553b269282b16e812c6e65e1ffb8edad01)) * 修改远程信息获取路径 ([b80d091](https://github.com/ocsjs/ocsjs/commit/b80d0914bd91482d2e75978d3d6676a92717d91e)) ### Features * 自动更新功能, scripts 分包 , 浏览器端修改为 core 文件夹 ([f536614](https://github.com/ocsjs/ocsjs/commit/f53661497d92911c4daea12ae2936471169b3e5a)) ## [3.2.4](https://github.com/ocsjs/ocsjs/compare/v3.2.3...v3.2.4) (2022-03-31) ### Bug Fixes * 修复上个版本视频跳过问题 ([1719bbd](https://github.com/ocsjs/ocsjs/commit/1719bbdfba8cc21bb10cad8c16ec7ae6127ae6af)) * 修复油猴脚本更新BUG ([fd043dd](https://github.com/ocsjs/ocsjs/commit/fd043ddc3a6d9d3a3863c158379d7e1afd37a1d6)) ## [3.2.3](https://github.com/ocsjs/ocsjs/compare/v3.2.2...v3.2.3) (2022-03-30) ### Features * 添加任务点是否完成检测,不重复执行已完成任务点, 修复判断题不选择的BUG ([dbc1a86](https://github.com/ocsjs/ocsjs/commit/dbc1a86c5793cecfbd5521ee24d4373da3b3ee5e)) ## [3.2.2](https://github.com/ocsjs/ocsjs/compare/v3.2.1...v3.2.2) (2022-03-27) ### Features * 添加智慧树学分课脚本 ([37b112a](https://github.com/ocsjs/ocsjs/commit/37b112ae8f1488b4b07ce4a986ac798df8d0bd9e)) ## [3.2.1](https://github.com/ocsjs/ocsjs/compare/3.2.0...v3.2.1) (2022-03-27) ### Features * 添加禁止弹窗脚本 ([2b6be41](https://github.com/ocsjs/ocsjs/commit/2b6be413847fa55367d72ee5b16570d8ef8a218b)) # [3.2.0](https://github.com/ocsjs/ocsjs/compare/v3.1.11...3.2.0) (2022-03-26) ### Features * **app:** 软件更新,支持超星学习作业考试,支持脚本自动更新 ([f500119](https://github.com/ocsjs/ocsjs/commit/f500119bed4d05d775b635de672c3b3e16e5a363)) ## [3.1.11](https://github.com/ocsjs/ocsjs/compare/v3.1.10...v3.1.11) (2022-03-26) ### Features * 更新软件,加载时自动更新ocs脚本 ([e16d954](https://github.com/ocsjs/ocsjs/commit/e16d954ff5bb4a084fa4dd6ff4e6dc6b9643f483)) ## [3.1.10](https://github.com/ocsjs/ocsjs/compare/v3.1.9...v3.1.10) (2022-03-26) ### Bug Fixes * 修复脚本只能在油猴环境下执行的BUG ([4f081c9](https://github.com/ocsjs/ocsjs/commit/4f081c90ee20e67a7f4e2afb43098e0c4308201e)) ## [3.1.9](https://github.com/ocsjs/ocsjs/compare/v3.1.8...v3.1.9) (2022-03-26) ## [3.1.8](https://github.com/ocsjs/ocsjs/compare/v3.1.7...v3.1.8) (2022-03-26) ### Bug Fixes * 修改答案结果判断bug ([a2808d0](https://github.com/ocsjs/ocsjs/commit/a2808d09b6ffa37376b25682cc4c8cd1cf0be5d7)) ## [3.1.7](https://github.com/ocsjs/ocsjs/compare/3.0.0-beta.9...v3.1.7) (2022-03-25) ### Bug Fixes * 修改题库配置设置,取消必选,添加判断,如果没有题库设置,则不开始自动答题。 ([b25ef93](https://github.com/ocsjs/ocsjs/commit/b25ef9323d0fe5c9d8437e3f50551a5e47e20dfa)) * 修改油猴配置 ([db8a733](https://github.com/ocsjs/ocsjs/commit/db8a733adaadb263fa2ed05233bd0b03f93c2e68)) * **cx:** 兼容cx选项获取不到,以及设置保存bug ([5a2e672](https://github.com/ocsjs/ocsjs/commit/5a2e67214d45053475574f4e16d3023909f6b132)) * **script:** 修复答题解析器bug, 更新做题标题获取,修复学习时章节测验类型获取失败bug, 更新 app 的脚本 ([659955b](https://github.com/ocsjs/ocsjs/commit/659955b818b41b7f25506e93caa888392e37aba1)) * **script:** 修改答题配置解析器 ([6e4332f](https://github.com/ocsjs/ocsjs/commit/6e4332f3855319e59f0bf5981fb713b7463cb531)) * **style:** 修改样式引入 ([8a6ab90](https://github.com/ocsjs/ocsjs/commit/8a6ab903ef661b89bc81bdc2298cdb28c2f40f9e)) * **url:** 修改 url cdn 资源 ([5cad7c1](https://github.com/ocsjs/ocsjs/commit/5cad7c19b6976d5825c982ccbede011d157b94a8)) # [3.0.0-beta.9](https://github.com/ocsjs/ocsjs/compare/3.0.0-beta.6...3.0.0-beta.9) (2022-03-24) ### Bug Fixes * **index:** 修改数据路径 ([c97bdaf](https://github.com/ocsjs/ocsjs/commit/c97bdaf00aef72ce9c71068573cd4d9933d15590)) ### Features * 版本更新至 beta.6 ([cc63a07](https://github.com/ocsjs/ocsjs/commit/cc63a076ddb79235f8ca12c0c30b500c5d8be43e)) * 新增答题器,新增题库配置选项,新增zhs作业功能 ([449c008](https://github.com/ocsjs/ocsjs/commit/449c008af2f961c6a6ca12788309fabcc8334cfc)) * 修改为单进程软件,支持.ocs文件的点击加载,新增软件标题栏,删除原生标题栏 ([8427936](https://github.com/ocsjs/ocsjs/commit/8427936e931ca8bfcc86240393d40c7583f95abc)) * **all:** 版本更新 ([3b17a92](https://github.com/ocsjs/ocsjs/commit/3b17a9242d6cc12330e8bbb5ff15c88957507ce2)) * **scripts:** 添加超星学习,考试,作业脚本。 添加日志,搜题结果面板。 ([94d99d4](https://github.com/ocsjs/ocsjs/commit/94d99d4fb32ce9b58e88e0fa8f0812adea7f89a6)) # [3.0.0-beta.6](https://github.com/ocsjs/ocsjs/compare/3.0.0-beta.5...3.0.0-beta.6) (2022-03-15) ### Features * **app | script:** 新增 script/browser 端的面板显示,替换之前的油猴头部信息加载模式,添加app主进程端的脚本调用 ([f9cf10f](https://github.com/ocsjs/ocsjs/commit/f9cf10f6757a45d520c1c2ff5532bceaa9298f07)) * **web:** 添加文件关闭编辑功能,取消页面所有动画效果,删除'关于'页面,新增'帮助'一栏。 ([1f4d4ad](https://github.com/ocsjs/ocsjs/commit/1f4d4ad7da78d97282524dd9325fc9c4ac1468f5)) # [3.0.0-beta.5](https://github.com/ocsjs/ocsjs/compare/0675ac6a631e8946a52e7e4e655b28faee8248d4...3.0.0-beta.5) (2022-03-07) ### Bug Fixes * **package.json:** fix dependency security ([948662a](https://github.com/ocsjs/ocsjs/commit/948662a580aab60a3ff70c64111aa36c40b5a4ce)) * **web:** 优化侧边栏: 优化文件列表头部,优化文件列表搜索 ([84acc62](https://github.com/ocsjs/ocsjs/commit/84acc62870db173211165408c8fe665df556ebf0)) ### Features * add glup ([15c0015](https://github.com/ocsjs/ocsjs/commit/15c0015141f4104888813dda905fc723843f66ca)) * **all:** 添加终端显示,添加脚本执行,添加文件属性 ([cf5dc9a](https://github.com/ocsjs/ocsjs/commit/cf5dc9a2d17057c5ac2db7df43e9e2a5b26e664f)) * **app and web:** 添加文件夹管理,添加右键菜单,初始化设置和关于页面 ([24930ad](https://github.com/ocsjs/ocsjs/commit/24930ad1e32508e227fb36b7008fb2981438cbb7)) * **app and web:** add elctron-builder in app , init page view in web ([fe60df6](https://github.com/ocsjs/ocsjs/commit/fe60df65dfeed1607ab89a941fc6b6eb627132fc)) * **init:** init project ([0675ac6](https://github.com/ocsjs/ocsjs/commit/0675ac6a631e8946a52e7e4e655b28faee8248d4)) * **packages:** init packages : web app scripts ([24e5386](https://github.com/ocsjs/ocsjs/commit/24e5386ec86dec33cc696fda6b5956785e2c1359)) * **script|web|app:** add commander line support, and update web view ([35efb39](https://github.com/ocsjs/ocsjs/commit/35efb39f34e4dc0d4e4914516f95d0ff467f8f37)) * **script:** add cx and zhs login ([8d69e16](https://github.com/ocsjs/ocsjs/commit/8d69e166fd73cf2ae2d700aa772b78159efbdeaa)) * **script:** change folder name, and add browser script ([c540500](https://github.com/ocsjs/ocsjs/commit/c540500b0cef50e7c2944d3ea6331e93c3e00e52)) * **scripts-tempaermonkey:** add tempermonkey support, update browser export ([456af02](https://github.com/ocsjs/ocsjs/commit/456af0250ff33fada0930f01270d1e147f27e2f7)) * **scripts/cx:** add new login : phone-code login ([494e523](https://github.com/ocsjs/ocsjs/commit/494e523ffda8ddf5c9320be5861b42234fab4d91)) * **scripts:** 修改 package.json , 调整登录api ([614c628](https://github.com/ocsjs/ocsjs/commit/614c628a749bc6dd4e1556fd9fbdb026c82d6937)) * **scripts:** add script package ([6f03fb4](https://github.com/ocsjs/ocsjs/commit/6f03fb4d3545f02f8ccfeb14fa7b5d8169c15348)) * **test and cx login:** update tests README.md and add new login way of cx : phone-code-login ([ab46a1d](https://github.com/ocsjs/ocsjs/commit/ab46a1df98c73351518847a629ddaeb7f51a2d4d)) * **web:** 添加文件解析,使用懒加载进行文件的显示 ([0da0f02](https://github.com/ocsjs/ocsjs/commit/0da0f024741191ce408354ffc148c83dc7bea4f3)) * **web:** 添加文件拖拽,文件搜索功能 ([fb1b7c9](https://github.com/ocsjs/ocsjs/commit/fb1b7c93c1679e484769362724c2818c68898145)) * **web:** 添加重命名功能,添加目录展开记录保存,添加帮助页面 ([0b90cc2](https://github.com/ocsjs/ocsjs/commit/0b90cc20d33c21b406b7d6426df812aed03eec1c)) * **web:** 文件编辑,文件拓展 ([9f2f4fb](https://github.com/ocsjs/ocsjs/commit/9f2f4fb6b4fd37b7526239bacf45135ff5874cbc)) * **web:** 支持文件(夹)拖拽放置,文件(夹)的创建,删除 ([705cc21](https://github.com/ocsjs/ocsjs/commit/705cc210bf382a3a92962f03526366edc0e18398)) ================================================ FILE: CHANGELOG_CURRENT.md ================================================ ## [4.13.4](https://github.com/ocsjs/ocsjs/compare/4.13.2...4.13.4) (2026-04-23) ### Features * **script:** 兼容超星长时阅读任务点(限制翻页时间) ([ac34ba2](https://github.com/ocsjs/ocsjs/commit/ac34ba2d543afe9dc09c1803b558f7b37dbce095)) * **script:** 添加超星域名支持:jnzyjsxy.cn ([a27cb49](https://github.com/ocsjs/ocsjs/commit/a27cb49b9111e3317120c33eff75981de4b19ed8)) ================================================ FILE: CHANGELOG_SIMPLIFIED.md ================================================ ## 4.13.4 (2026-04-23) ### ✨ 更新内容 * 兼容超星长时阅读任务点(限制翻页时间) > * 添加超星域名支持:jnzyjsxy.cn > ## 4.13.2 (2026-04-22) ### 🔧 修复内容 * 修复智慧树26年上半年studywisdomh5复习模式错误的问题 > ### ✨ 更新内容 * 兼容雨课堂AI学伴自动学习 > ## 4.12.39 (2026-04-20) ### 🔧 修复内容 * 修复智慧职教题目识别丢失img的src问题 > * 修复智慧职教题目识别丢失img的src问题,使用与学习通页面相同的方法 > * 修复智慧职教题目识别丢失img的src问题,使用与学习通页面相同的方法,且考虑纯图片无法识别问题 > * 修复智慧职教题目识别丢失img的src问题,使用与学习通页面相同的方法,且考虑纯图片无法识别问题 > ## 4.12.38 (2026-04-20) ### 🔧 修复内容 * 兼容26年上半年智慧树新版studywisdomh5网课更新 > ## 4.12.37 (2026-04-15) ### 🔧 修复内容 * 修复超星某些章节测试无法自动答题的BUG > ### ✨ 更新内容 * 添加智慧树2026上学期新版HIKE AI教学中心视频学习功能 > ### ⚡ 优化提升 * 适配超星积分课的阅读任务不会自动开始阅读的问题 > * 添加超星阅读界面的使用提示 > * 优化超星跳转时间太长的问题,改成智能等待时间 > ## 4.12.32 (2026-04-09) ### 🔧 修复内容 * 尝试修复智慧树AI学伴课程过一段时间才会播放的BUG > ### ⚡ 优化提升 * 优化超星闯关模式重复提示 > ## 4.12.30 (2026-03-20) ### ⚡ 优化提升 * 添加智慧树新智慧形态考试菜单进入菜单栏 > ## 4.12.29 (2026-03-20) ### 🔧 修复内容 * 兼容职教云xls任务点自动学习 > * 修复智慧树-新形态课程链接任务点无法跳过的BUG > ### ✨ 更新内容 * 添加智慧树-新形态课程自动考试功能 > ## 4.12.26 (2026-03-18) ### 🔧 修复内容 * 修复职教云PPT仅有2页时无法完成的BUG > * 修复智慧职教AI测验答题时填空功能失效的BUG > ### ✨ 更新内容 * 添加新版职教云自动跳转旧版功能 > ## 4.12.23 (2026-03-17) ### 🔧 修复内容 * 修复上个版本无法打包的BUG > ## 4.12.22 (2026-03-17) ### 🔧 修复内容 * 添加智慧树-AI教学中心-题目作业自动答题功能 > * 修复图片题中的图片在解析时重复出现的BUG > * 修复职教云资源库无法跳过测验的问题 > * 修复智慧职教AI测验答题时无法填空的BUG > ### ⚡ 优化提升 * 更新API,添加新API $msg,可同时显示气泡和打印日志 > * 添加答题日志输出开关功能 > ## 4.12.16 (2026-03-06) ### 🔧 修复内容 * 修复智慧课程AI伴学的视频无法检测,以及掌握度只有一题的情况无法答题的BUG > * 优化 eslint 报错 > ## 4.12.15 (2026-03-04) ### 🔧 修复内容 * 修复2026上学期智慧树-新智慧课程无法自动下一章的BUG > ## 4.12.14 (2026-02-28) ### ✨ 更新内容 * 添加ZE题库域名白名单授权 > ## 4.12.13 (2026-02-25) ### 🔧 修复内容 * 适配 2026 上学期智慧树-新智慧学习界面 > ## 4.12.12 (2025-12-29) ### 🔧 修复内容 * 修复 github workflow 运行后 release 不显示更新内容的BUG > ## 4.12.11 (2025-12-29) ### 🔧 修复内容 * 添加 git workflow 输出 > ## 4.12.10 (2025-12-29) ### 🔧 修复内容 * 修复 github workflow 运行后 release 不显示更新内容的BUG > ## 4.12.9 (2025-12-29) ### 🔧 修复内容 * 修复 github workflow 运行后 release 不显示更新内容的BUG > ## 4.12.8 (2025-12-29) ### 🔧 修复内容 * 持续修复 github workflow 运行后 release 不显示更新内容的BUG > ## 4.12.7 (2025-12-29) ### 🔧 修复内容 * 修复 github workflow 运行后 release 不显示更新内容的BUG > ## 4.12.6 (2025-12-29) ### 🔧 修复内容 * 适配2025-12月智慧树新智慧学习域名 > * 修复超星学习通没配置题库无法跳过章节测试的BUG > ## 4.12.4 (2025-12-16) ### 🔧 修复内容 * 修复答案为圆圈数字时无法答题的BUG > ## 4.12.3 (2025-12-03) ### 🔧 修复内容 * 添加职教云资源库修复功能按钮 > * 修复中国大学MOOC新版考试无法运行的BUG > ### ⚡ 优化提升 * 加强智慧树作业考试自动保存警告 > # 4.12.0 (2025-11-27) ### 🔧 修复内容 * 修复超星答题时无法点击快速定位到题库配置界面问题 > ### ✨ 更新内容 * 添加软件辅助鼠标在空闲状态下执行辅助点击等功能,防止与用户抢夺控制权 > * 添加自定义答题器,可自定义题目类型、答题器等参数,自由度更高 > * 兼容超星繁体字判断题适配 > ### ⚡ 优化提升 * 优化 waitForElement API > ## 4.11.98 (2025-11-24) ### 🔧 修复内容 * 修复上个版本无法打包的BUG > ## 4.11.97 (2025-11-24) ### 🔧 修复内容 * 修复高级设置无法打开的BUG > ## 4.11.96 (2025-11-24) ### 🔧 修复内容 * 修复中国大学MOOC视频题目重复答题的BUG > ## 4.11.95 (2025-11-23) ### ⚡ 优化提升 * aPI优化 > ## 4.11.94 (2025-11-23) ### 🔧 修复内容 * 题库缓存清空后弹窗数量文字归零 > * 修复超星默认跳转模式无法自动滚动页面到当前章节列表问题 > ### ✨ 更新内容 * 对移动端进行拖动面板适配 > ### ⚡ 优化提升 * 优化题库搜索题目显示,可兼容长题目例如阅读理解上下文显示 > ## 4.11.86 (2025-11-20) ### 🔧 修复内容 * 修复上个版本类型报错 > ## 4.11.85 (2025-11-20) ### 🔧 修复内容 * 将超星环境调整脚本整合到内部,防止全局污染 > * 修复中国大学MOOC作业答题错乱问题 > ## 4.11.81 (2025-11-14) ### 🔧 修复内容 * 修复职教云资源库附件视频任务无法完成的BUG > * 修复职教云资源库总是无法学满进度的BUG > ## 4.11.78 (2025-11-13) ### 🔧 修复内容 * 修改职教云资源库视频PPT跳转间隔为5秒 > ## 4.1.76 (2025-11-12) ### 🔧 修复内容 * 修复职教云资源库PPT总是会返回第一页才结束的BUG > ## 4.11.77 (2025-11-12) ### 🔧 修复内容 * 修复职教云资源库PPT总是会返回第一页才结束的BUG > ## 4.1.76 (2025-11-12) ### 🔧 修复内容 * 修复职教云资源库PPT总是会返回第一页才结束的BUG > ## 4.11.75 (2025-11-12) ### 🔧 修复内容 * 修复超星在章节列表页面提示已进入学习界面的BUG > ## 4.11.74 (2025-11-12) ### 🔧 修复内容 * 修复超星视频加载失败无法重启的BUG > ## 4.11.73 (2025-11-12) ### 🔧 修复内容 * 使用软件辅助模拟输入填空适配智慧树智慧课程掌握度填空题 > * 添加智慧树倍速最高风险说明 > * 修复配置多个题库其中某个超时无法答题的BUG >, closes [#270](https://github.com/ocsjs/ocsjs/issues/270) ## 4.11.69 (2025-11-09) ### 🔧 修复内容 * 修复隐藏窗口按钮无法使用的BUG > * 添加中国大学MOOC空白页自动跳转的功能 > * 修复中国大学MOOC视频答题和章节测试冲突的问题 > ### ✨ 更新内容 * 适配智慧树-AI教学中心-智慧课程作业答题功能 > * 修复智慧树倍速失效的BUG > ## 4.11.64 (2025-11-07) ### 🔧 修复内容 * 答案匹配时移除标点符号保证匹配更加准确 > * 修复上个版本中国大学MOCC考试部分题目答题错误问题 > ## 4.11.61 (2025-11-06) ### 🔧 修复内容 * 修复职教云资源库点击PPT过快的BUG > * 修复智慧树新形态课程(智慧课程)某些外链无法完成并跳过的BUG > * 优化超星非整卷预览考试流畅度,添加搜索结果对于部分答页面题过程可控的菜单 > ### ✨ 更新内容 * 支持搜索结果界面可控制答题进程功能 > * 支持中国大学MOOC自动考试功能 > ### ⚡ 优化提升 * 添加超星凌晨刷课文案提示 > * 添加多个答题页面菜单自动注册功能 > ## 4.11.51 (2025-11-05) ### 🔧 修复内容 * 兼容搜题结果格式A#B#C#D解析为多选题 > ### ✨ 更新内容 * 兼容职教云在线课程内容单独任务点连续学习功能,添加PPT翻阅速度调整选项 > ### ⚡ 优化提升 * 添加智慧树共享课作业开启前阅读须知功能 > ## 4.11.47 (2025-11-05) ### 🔧 修复内容 * 优化智慧树卡巴斯基文案 > * 修复窗口反复闪烁的BUG > * 修复中国大学MOOC无法完成富文本任务的BUG > * 持续修复超星跳转未完成任务点模式无法使用的BUG > * 兼容职教云资源库新版PPT > * 兼容智慧树-AI助教课程PPT和文档功能 > * 修复超星视频暂停后长时间才播放的BUG > * 修复超星跳转未完成任务点模式无法使用的BUG > * 修复智慧树-AI助教倍速无法点击的BUG > * 修复智慧树-AI助教课程无法自动跳转小节和PPT的BUG > * 优化多选题纯答案无法解析的BUG > * 优化智慧树音量开始播放后没有立即调整的问题 > ### ✨ 更新内容 * 软件配置同步后添加脚本双击配置页面强制取消功能 > * 添加超星下一章切换后自动滚动到该元素的功能 > * 添加超星页面加载后自动滚动到当前任务点的功能 > * 添加超星章节测试答题时自动显示搜索结果 > * 添加全局快捷菜单自动注册功能 > * 添加最小化/切屏窗口警告功能,优化文案 > * 优化超星章节测试答题时自动打开搜索结果功能 > * 支持中国大学MOOC视频内弹窗答题 > ### ⚡ 优化提升 * 降低超星倍速警告阈值为2倍速,优化倍速警告 > * 添加智慧树作业禁止同时打开多个界面答题文案 > * 修改搜题超时默认时间为2分钟,最大值为3分钟 > ## 4.11.22 (2025-10-16) ### 🔧 修复内容 * 兼容2025下半年超星PPT新版无法完成的问题 > ## 4.11.21 (2025-10-10) ### 🔧 修复内容 * 优化软件同步功能 > ## 4.11.20 (2025-10-07) ### 🔧 修复内容 * 修复软件开启同步后,其他非自动化辅助网页里面的脚本依然会同步的问题,造成用户以为当前页面可以使用软件辅助的错觉 > ## 4.11.19 (2025-09-26) ### 🔧 修复内容 * 修复题库请求时无法传递类型为0或者false的值 > ## 4.11.18 (2025-09-25) ### 🔧 修复内容 * 修复上个版本智慧树会长时间不播放的BUG > ## 4.11.17 (2025-09-25) ### 🔧 修复内容 * 兼容超星整卷预览禁用后的作业和答题 > * 修复搜索结果AI标识重复出现的BUG > * 优化旧版智慧树视频容易黑屏的问题 > * 智慧树答题时移动页面至左上角防止点击出错 > ### ✨ 更新内容 * 添加对智慧树2025-9月份教学空间-AI智慧学习支持 > ### ⚡ 优化提升 * 修复中国大学MOOC无法提交讨论的BUG,优化中国大学MOOC日志显示 > * 优化题库配置文案和第三方连接文案 > * 优化智慧树学习提示文案 > ## 4.11.8 (2025-09-15) ### ✨ 更新内容 * - 添加搜题结果AI标注显示 > ## 4.11.5 (2025-09-09) ### 🔧 修复内容 * 尝试修复Git打包错误的BUG > ## 4.11.4 (2025-09-09) ### 🔧 修复内容 * 尝试修复Git打包错误的BUG > ## 4.11.3 (2025-09-09) ### 🔧 修复内容 * 对全部项目的依赖进行版本锁定 > ## 4.11.2 (2025-09-09) ### 🔧 修复内容 * 优化代码 > ## 4.11.1 (2025-09-09) ### 🔧 修复内容 * 优化代码 > # 4.11.0 (2025-09-09) ### 🔧 修复内容 * 优化Playwright软件辅助底层对接,添加软件辅助点击可视化,修复元素滚动后点击错位的BUG > ### ✨ 更新内容 * 添加超星对 jxgmxy.com 域名的支持 > * 新增智慧树2025-9月份最新课程支持-新智慧课程 > # 4.10.0 (2025-09-01) ### ✨ 更新内容 * 添加对2025-8月份智慧树新掌握度页面进行适配 > * 新增智慧职教AI课程学习和作业脚本 > ## 4.9.86 (2025-06-08) ### 🔧 修复内容 * 添加职教云卡死提示解决方法 > ## 4.9.85 (2025-06-08) ### 🔧 修复内容 * 修复职教云获取数据错误的问题 > ## 4.9.84 (2025-06-07) ### 🔧 修复内容 * 修复智慧职教自定义全局倍速后导致其他脚本倍速选项也被修改的BUG > ## 4.9.83 (2025-06-07) ### 🔧 修复内容 * 修改脚本更新链接到教程官网的更新教程 > * 修复职教云卡在获取数据弹窗 > ## 4.9.81 (2025-05-29) ### 🔧 修复内容 * 兼容职教云添加章节层级名字后无法获取数据的问题 > * 添加智慧树智慧课程跳转模式选择 > ## 4.9.79 (2025-05-24) ### 🔧 修复内容 * 修复职教云章节列表前面有章节序号时无法读取数据的BUG > ## 4.9.78 (2025-05-18) ### 🔧 修复内容 * 修复职教云获取数据时跳转其他网页的BUG > ## 4.9.77 (2025-05-11) ### 🔧 修复内容 * 修复职教云0资源库无法下一章的BUG,兼容职教云部分PPT任务点 > ## 4.9.76 (2025-05-09) ### 🔧 修复内容 * 优化智慧树翻转课(校内课) > ### ✨ 更新内容 * 优化支持职教云最新版页面,支持自动切换下一章(包含大章节切换) > ### ⚡ 优化提升 * 持续优化软件辅助安装提示 > * 优化倍速设置选项,减少不必要的选项数量 > * 优化超星随机答题文案,提示只有在搜不到答案的时候才使用随机答题 > ## 4.9.71 (2025-05-07) ### 🔧 修复内容 * 修复新版智慧树无法关闭弹窗的BUG > ## 4.9.70 (2025-05-05) ### 🔧 修复内容 * 修复超星搜题时无法读取选项内图片链接的BUG > * 修复超星章节测试多行题目第二行存在题目类型时无法解析题目的BUG > * 修复超星自动答题时填空题选项读取到额外冗余文字的BUG:(段落格式,字号,字体) > * 修复智慧树-智慧课程无法自动切换下一个的BUG > * 修复智慧树-智慧课程无法自动下一章的BUG > * 修复智慧树-智慧职教无法学习文本任务点的问题,新增智慧职教已完成任务点过滤功能 > * 优化智慧树-智慧课程播放逻辑 > ### ✨ 更新内容 * 对题库配置做唯一化处理 > * 适配智慧树智慧课程学习和作业(包括AI助教和智慧课程),适配智慧树新版课程页面,适配智慧课程掌握度自动答题 > * 支持智慧树智慧课程链接任务点 > ### ⚡ 优化提升 * 修改软件辅助启动提示 > ## 4.9.59 (2025-03-31) ### 🔧 修复内容 * typo > ### ✨ 更新内容 * 添加超星 ccqmxx.com 域名适配支持 > * 添加题库配置相同检测,如果相同则提示 > ## 4.9.57 (2025-03-14) ### ✨ 更新内容 * 添加智慧树-智慧课程掌握度答题功能 > ### ⚡ 优化提升 * 添加智慧树-智慧课程掌握度学习提示 > ## 4.9.55 (2025-03-14) ### 🔧 修复内容 * 超星任务点检测优化 > ## 4.9.54 (2025-03-13) ### 🔧 修复内容 * 优化智慧树智慧课程视频检测,修复智慧树智慧课程检测不到任务点问题 > ## 4.9.52 (2025-03-12) ### 🔧 修复内容 * 修复 4.9.51 版本题库配置解析问题 > ## 4.9.46 (2024-12-22) ### ⚡ 优化提升 * 优化智慧树考试提示文案 > ## 4.9.45 (2024-12-21) ### 🔧 修复内容 * 修复上个版本图片链接处理导致的无法选择BUG > ## 4.9.44 (2024-12-21) ### 🔧 修复内容 * 修复图片题解析错误导致的URL重复,图片重复显示等问题 > ## 4.9.40 (2024-11-10) ### 🔧 修复内容 * 优化职教云资源库无法答题的问题 > ## 4.9.39 (2024-11-09) ### 🔧 修复内容 * 修复 select 选择框显示问题 > ## 4.9.37 (2024-11-06) ### ✨ 更新内容 * 添加超星自动跳转未完成任务点功能 > ## 4.9.36 (2024-11-06) ### 🔧 修复内容 * 修复选择输入框中默认值匹配错误导致的超星自动保存选择'save',但是默认值依旧是80的问题 > ## 4.9.29 (2024-08-10) ### 🔧 修复内容 * 修复资源库最新版链接类名改变问题 > ## 4.9.28 (2024-07-14) ### 🔧 修复内容 * 解决超星部分套壳网站无法自动下一章的BUG > * 优化对超星视频下载错误的处理,将自动跳过错误视频 > ### ⚡ 优化提升 * 优化版本更新验证,不符合版本书写规范的话向用户提示报错 > ## 4.9.25 (2024-07-13) ### 🔧 修复内容 * 新增智慧职教 webtrn.cn 套壳网站支持,新增对30分钟学习弹窗的关闭功能 > ## 4.9.23 (2024-06-25) ### 🔧 修复内容 * 修复职教云资源库,课件类型为NAN的BUG > * 修复职教云资源库重复下一章的BUG > ## 4.9.21 (2024-06-19) ### 🔧 修复内容 * 修复题库配置 AnswererWrapper 自定义字段解析 handler 函数调用两次的BUG > * 修复搜索时换行被删除导致,两行合并造成的文字合并,导致搜索引擎可能出现的的搜索问题 > ## 4.9.20 (2024-06-13) ### ✨ 更新内容 * 新增 中国大学MOOC 考试功能 > ## 4.9.19 (2024-06-05) ### 🔧 修复内容 * 修复使用精确匹配时,脚本的多选题搜索结果会全部显示已完成的BUG > * 添加完成率百分比符号 > ## 4.9.18 (2024-06-04) ### 🔧 修复内容 * 修复全域名版本无法显示BUG > ## 4.9.17 (2024-05-24) ### 🔧 修复内容 * 修复上个版本任务点检测不到的BUG > ## 4.9.16 (2024-05-23) ### 🔧 修复内容 * 优化超星人脸识别检测警告 > * 优化超星题库配置为空警告 > ### ✨ 更新内容 * 新增对超星链接任务点,音频PPT任务点的支持,优化代码,删除视频错误检测功能。 > ## 4.9.12 (2024-05-11) ### 🔧 修复内容 * 添加超星积分课的使用提示 > * 修复超星课程,章节测试偶尔出现卡死的BUG > * 修复超星课程进入时需要人脸识别,但是疯狂跳转新版本导致的人脸识别疯狂刷新的BUG > * 修复超星英语判断题的文本BUG > ## 4.9.7 (2024-05-10) ### 🔧 修复内容 * 修复 '"@ocsjs/core"' has no exported member 'createQuestionTitleExtra'. BUG > ## 4.9.6 (2024-05-10) ### 🔧 修复内容 * 更新 easy-us API > * 删除无用API > * 修复英语前缀 【True or False】 的BUG > * 优化题库配置的开启和关闭按钮 > * 优化题库配置的开启和关闭按钮 > ## 4.9.4 (2024-05-04) ### 🔧 修复内容 * 修复无法移动面板至边缘导致自动登录失效的BUG > ## 4.9.2 (2024-05-04) ### 🔧 修复内容 * 导出 $elelment 对象,便于可修改 $modal 第二个参数 > # 4.9.0 (2024-05-03) ### ✨ 更新内容 * 优化API,分离UI层为新项目 easy-us > ## 4.8.30 (2024-05-01) ### 🔧 修复内容 * 解决超星视频播放时出现: The play() request was interrupted by a call to pause() BUG > ### ✨ 更新内容 * 新增对职教云资源库的 spocjob 作业的支持 > ## 4.8.26 (2024-03-29) ### ✨ 更新内容 * 新增超星 cugbonline.cn 域名支持 > ## 4.8.25 (2024-03-28) ### 🔧 修复内容 * 修复添加额外菜单栏后,窗口无法正常最大化/最小化的BUG > ## 4.8.24 (2024-03-28) ### 🔧 修复内容 * 修复窗口隐藏/显示 快捷键无效的BUG > * 修复单选题答案回调内容不一致的BUG > * 修复设置超出最大最小值,导致变更时,本地的值没有同步导致刷新后依然没有发生变化的BUG > * 修复新版答题算法中搜题线程参数无效的BUG,始终只有一个搜题线程在运行 > * 修复已经在暂停状态,但是依然在答题的BUG > * 优化搜索结果的显示BUG,已搜到答案的题目显示未匹配到正确答案的BUG,以及答题进度不一致的显示BUG > * 全面优化答题速度,优化存在题库缓存时无需执行搜题间隔等待,优化特定网课勾选答案时需要停顿,正常网课无需停顿的情况,极大加速了答案勾选过程。 > * 修复智慧树答题时题库缓存未正确存储的BUG > * 修复重新答题按钮点击后,答题完成后的一些操作依然在进行的BUG > ### ✨ 更新内容 * 新增额外菜单栏功能,新增超星额外菜单栏快捷页面跳转按钮 > * 新增【职教云】资源库作业支持 > ## 4.8.13 (2024-03-14) ### 🔧 修复内容 * 全局设置中部分元素的值为空时恢复默认值 > * 优化答题模块算法 > ## 4.8.11 (2024-03-13) ### 🔧 修复内容 * 修复请求记录开启后无法读取内容的BUG > ### ✨ 更新内容 * 对 TikuAdapter 做适配提示 > ## 4.8.8 (2024-03-05) ### 🔧 修复内容 * 优化超星视频重复播放时没有重置视频进度的问题 > ## 4.8.7 (2024-03-01) ### 🔧 修复内容 * 修复2倍速被删除的BUG > ## 4.8.6 (2024-02-28) ### 🔧 修复内容 * 修改软件辅助返回字段 > ## 4.8.5 (2024-02-28) ### 🔧 修复内容 * 修复智慧树无法使用软件辅助,无法自动答题看视频的BUG > ### ✨ 更新内容 * 新增答案匹配模式选项 > * 添加题库配置自动读取剪贴板功能 > ### ⚡ 优化提升 * 优化 tooltip > # 4.8.0 (2024-02-26) ### 🔧 修复内容 * 新答题器的旧字段搜索结果显示修改 > * 修复当数字输入框是空格后无法正确读取的BUG > * 修复新版答题器当线程为1时,无法搜题的BUG > * 优化答题器算法 > * 优化跨域通信只在顶部页面刷新时删除临时监听变量,优化代码添加注释 > * 优化搜索结果显示,内部算法报错依然可以显示搜索结果 > * 添加超星英文判断题的冗余删除 > * 添加题库缓存开关 > * 修复解除复制粘贴限制功能无效的BUG > * 修复通知时间很短就消失的BUG > * 优化超星闯关模式以及解锁模式的重复进入检测算法 > * 优化超星英文判断题的解析 > ### ✨ 更新内容 * 新增 answer_separator 答案分隔符设置 > * 添加超星 gdhkmooc.com 域名支持 > * 添加超星完成全部任务点后重新从开头学习功能 > * 添加对智慧树自动保存答案的解释 > * 添加开发人员调试:显示Tab变量功能 > * 添加职教云 flv 格式支持 > * 添加职教云的作业考试【客观填空题】的支持 > * 添加智慧职教MOOC的单行填空题支持 > * 新增 $message 跨域调用功能,新增 $modal duration 参数,修复由于删除cors监听key导致的 undefined BUG > * 新增新版职教云作业填空题支持 > * 新增职教云考试支持,txt文档查看支持 > * 优化题库配置文案,优化超星文案,并添加运行时的反馈消息提示 > * 在拓展应用中添加OCS全部配置的导入导出功能 > ### ⚡ 优化提升 * update remote-playwright-log > * 优化软件辅助类型提示以及代码 > * 简化 $console 生成的堆栈信息,减少存储量 > * 添加更多的倍速选项 > ## 4.7.46 (2023-12-20) ### 🔧 修复内容 * 优化题库配置在软件上的域名检测 > ### ⚡ 优化提升 * type update > ## 4.7.35 (2023-12-19) ### ✨ 更新内容 * 修复超星人脸验证时疯狂刷新的问题,新增超星人脸验证通知功能 > ### ⚡ 优化提升 * 添加题库停用状态的开启提示 > ## 4.7.34 (2023-12-13) ### 🔧 修复内容 * 优化 TikuAdapter 提示 > ## 4.7.33 (2023-12-13) ### 🔧 修复内容 * 修复部分脚本管理器无法读取 [@connect](https://github.com/connect) 头部元数据的问题 > ## 4.7.32 (2023-12-13) ### 🔧 修复内容 * 修复全域名通用版本不带官方域名的BUG > ### ⚡ 优化提升 * 添加 TikuAdapter 配置域名白名单提示 > ## 4.7.30 (2023-12-11) ### 🔧 修复内容 * 修改TikuAdapter type 类型解析 > ## 4.7.29 (2023-12-11) ### 🔧 修复内容 * 添加多版本脚本的更新模块适配 > ### ⚡ 优化提升 * 添加 TikuAdapter 解析器说明 > ## 4.7.27 (2023-12-11) ### 🔧 修复内容 * 修复请求模块的重大BUG > ### ✨ 更新内容 * 新增 TikuAdapter 题库配置解析器,优化题库配置解析 > ## 4.7.25 (2023-12-08) ### 🔧 修复内容 * 复上个版本无法输入任何文字的BUG > ## 4.7.24 (2023-12-08) ### 🔧 修复内容 * 优化对 headers 中 content-type 解析而实现传递不同的 data 数据 > * 将中国大学MOOC的页面切换信息设置为警告信息 > ### ✨ 更新内容 * 添加快捷键可以显示/隐藏 窗口功能 > ## 4.7.21 (2023-12-05) ### 🔧 修复内容 * 修复打包时不自动上传文件的BUG > ## 4.7.18 (2023-12-05) ### 🔧 修复内容 * 修复上个版本无法自动打包的BUG,优化打包代码 > ## 4.7.17 (2023-12-05) ### ✨ 更新内容 * 添加全域名通用脚本打包 user.common.user.js > ### ⚡ 优化提升 * 添加请求记录方法过滤选项 HEAD > * 添加在线搜题题库配置为空的提示 > ## 4.7.14 (2023-12-05) ### 🔧 修复内容 * 修复 attrs 属性的 style 特殊字段不生效的BUG, 将每个文本框设置最小的宽高 > * 修复下拉选择框只有value值时不显示的BUG > * 修复搜索结果序号过多时,序号页面超出页面的BUG > * 修复中国大学MOOC课堂测验答题完成后没有等待暂停时间步骤的BUG > * 优化在线搜题功能,缓存搜索题目,优化划词功能 > ### ✨ 更新内容 * 新增题库配置的字段解析器功能 > * 添加超星 hnvist.cn fjlecb.cn 域名支持 > * 新增超星旧版学习页面自动转换新版功能 > * 新增开发人员请求记录调试页面 > ### ⚡ 优化提升 * request 模块的 gm http 的 data 使用 JSON.stringify 进行转换 > * 删除超星多余日志 > ## 4.7.7 (2023-11-30) ### 🔧 修复内容 * 将超星提交功能设置为3秒延时 > * 新增题目冗余字段 > * 修复超星学习通章节测验已完成,但依然开始答题的BUG > * 优化超星作业考试/章节测验 中 type 传递是 undefined 的BUG > * 优化职教云-资源库:新增对 mp3 的支持,新增自动跳过测验功能 > ### ✨ 更新内容 * 优化答题uploadHandler API,新增 commonWork.options.onWorkerCreated 选项 > * 新增中国大学MOOC,随堂测验自动答题功能 > ### ⚡ 优化提升 * 导出 ICourseProject 变量,开发者调试功能中可用 > * 增加超星任务点关闭后的开启提示 > ## 4.7.1 (2023-11-25) ### 🔧 修复内容 * 修复上个版本 职教云资源库获取课程信息失败 bug > # 4.7.0 (2023-11-25) ### 🔧 修复内容 * 添加题库配置 method 大小写适配 > * 修复选项元素在没有 title 时显示为 undefined 的 BUG > * 优化选择下拉框设置,优化代码,修改之前由于无法获取value而使用的 $creator.selectOptions 的BUG > * 将软件辅助的点击设置成点击元素中心点 > * 添加智慧树题目类型判断,修复某些题目不自动填写的BUG > * 修复职教云更新后-资源库不自动学习的BUG > * 优化脚本字间距,搜索结果字间距和窗口间距 > * 优化软件辅助错误提示 > * 优化智慧树学习提示 > * 优化智慧职教填空题 > ### ✨ 更新内容 * 添加超星学习通 视频内题目随机答题功能 > * 添加超星学习通-章节页面自动切换脚本 > * 添加职教云遇到讨论课件时自动跳过 > * 新增网课 中国大学MOOC 的学习、作业脚本支持 > * 新增智慧职教-学习中心自动学习功能 > * 优化全部网课平台的系统通知功能,添加任务点完成提示,添加智慧树验证码提示 > ## 4.6.32 (2023-11-01) ### 🔧 修复内容 * 新增超星其他题,类型支持 > ### ✨ 更新内容 * 添加优先级选项,排序特定脚本的执行速度 > * 新增职教云资源库 pdf 支持,新增职教云作业脚本 > ## 4.6.29 (2023-10-24) ### 🔧 修复内容 * 修复软件配置同步失败问题 > ## 4.6.28 (2023-10-24) ### 🔧 修复内容 * 修复上个版本无法答题的BUG > ## 4.6.27 (2023-10-24) ### 🔧 修复内容 * 修复超星章节测试题库被禁用的时候依然使用的BUG > * 修复智慧树检测习惯分出错 > ## 4.6.25 (2023-10-22) ### 🔧 修复内容 * 深度优化智慧树弹窗BUG,以及倍速清晰度不选择BUG > ### ✨ 更新内容 * 新增可以设置不被软件配置同步覆盖的设置,修复智慧树学习记录刷新后清空的BUG > ## 4.6.23 (2023-10-20) ### 🔧 修复内容 * 优化核心域名匹配逻辑 > * 持续优化智慧树倍速和清晰度选择功能 > * 将软件同步功能加快到 onactive > * 优化超星新课程页面不显示使用提示的问题 > * 优化智慧树弹窗答题 > ## 4.6.22 (2023-10-19) ### 🔧 修复内容 * 修复智慧树需要调整窗口的BUG > ### ✨ 更新内容 * 兼容智慧职教套壳网站 courshare.cn > * 收录超星套壳域名 qutjxjy.cn ynny.cn > ## 4.6.19 (2023-10-15) ### 🔧 修复内容 * 补充答题冗余字段删除数据 > * 修复软件配置同步后,日志页面全锁定的BUG > * 修复使用软件同步时,智慧树学习自动暂停无效的BUG,以及存在验证码时自动暂停的优化。 > * 优化窗口大小自动调整,优化窗口大小警告模块 > ### ✨ 更新内容 * 将软件辅助警告修改成弹窗 > * 添加超星强制答题提示 > * 添加对自动调整窗口开关的重启提示 > * 添加职教云【资源库】支持 > ## 4.6.9 (2023-10-02) ### 🔧 修复内容 * 修复智慧树考试脚本无法使用的BUG > ### ✨ 更新内容 * 新增超星阅读任务无限阅读功能 > ## 4.6.7 (2023-09-26) ### 🔧 修复内容 * 微调智慧树窗口大小要求,宽2200,高1200 > ### ✨ 更新内容 * 新增功能:超星强制答题功能,没有黄色任务点的章节测试也可以运行自动答题。 > ## 4.6.5 (2023-09-25) ### 🔧 修复内容 * 修复智慧树调成窗口大于最小值后依然说不对的BUG > ## 4.6.4 (2023-09-24) ### ✨ 更新内容 * 添加智慧树窗口自动调节功能的选项按钮 > ## 4.6.3 (2023-09-24) ### 🔧 修复内容 * 添加智慧树窗口检测提示 > ### ✨ 更新内容 * 添加自动设置窗口大小功能,避免元素无法点击 > ## 4.6.2 (2023-09-22) ### 🔧 修复内容 * 修复上个版本智慧树作业BUG > ## 4.6.1 (2023-09-22) ### 🔧 修复内容 * 优化智慧树刷课逻辑,增加流畅度 > # 4.6.0 (2023-09-22) ### 🔧 修复内容 * 新增软件辅助功能,全面优化智慧树,共享课学习作业考试以及学分课视频 > * 优化超星最新考试页面支持 > ## 4.5.8 (2023-09-15) ### 🔧 修复内容 * 修复部分课程不显示超星阅读任务的提示页面 > * 修复在中国大学MOOC中点击事件监听被修改的问题,将 addEventListener('click') 改成 onlick > * 修复智慧树最新版脚本被检测的问题 > ## 4.5.5 (2023-09-12) ### ✨ 更新内容 * 使更新模块可视化 > ## 4.5.4 (2023-09-12) ### 🔧 修复内容 * 修复上个版本出现的弹窗底部消失的问题,优化超星自动答题后台日志 > ## 4.5.3 (2023-09-12) ### ✨ 更新内容 * 添加跳过版本的功能 > * 添加新版职教云音频的播放 > * 添加新版职教云DOC文档学习功能 > # 4.5.0 (2023-09-09) ### ✨ 更新内容 * 重写职教云刷课逻辑 > ## 4.4.35 (2023-09-04) ### 🔧 修复内容 * 优化自动发布的说明文档 > * 修复超星选择题选中的答案被取消的BUG > * test > ## 4.4.34 (2023-08-25) ### 🔧 修复内容 * 优化打包文件 > ## 4.4.33 (2023-08-25) ### 🔧 修复内容 * 优化github actions > ## 4.4.32 (2023-08-25) ### 🔧 修复内容 * 优化智慧树最新异常检测 > ## 4.4.31 (2023-08-05) ### 🔧 修复内容 * 修复超星判断题更新后,文字优化不兼容的问题 > ## 4.4.30 (2023-06-22) ### 🔧 修复内容 * 新增超星分录题支持 > ## 4.4.29 (2023-06-22) ### 🔧 修复内容 * 对脚本打包的 match 元数据进行去重 > * 将题库配置为空提醒设置为一直显示 > * 新增超星题目类型支持:资料题 > * 修复超星作业考试文本框无法自动保存BUG > ## 4.4.25 (2023-05-25) ### 🔧 修复内容 * 修复上个版本题库连接超时问题 > ## 4.4.24 (2023-05-25) ### 🔧 修复内容 * 修复题库状态检测无限执行的BUG,优化 onrender 中的监听器执行逻辑 > * 优化智慧职教使用提示 > ## 4.4.22 (2023-05-20) ### 🔧 修复内容 * update build file > ## 4.4.19 (2023-05-20) ### 🔧 修复内容 * 修复添加配置分隔线后,OCS配置同步锁的样式显示出错 > * 添加软件自动登录辅助页面 > ### ✨ 更新内容 * 添加配置分割线,便于设置区域分组 > ## 4.4.15 (2023-05-16) ### 🔧 修复内容 * 规范搜题请求处理,修复在url中存在变量时多加一个问号的BUG >, closes [#97](https://github.com/ocsjs/ocsjs/issues/97) ## 4.4.14 (2023-05-16) ### 🔧 修复内容 * 修复智慧职教多层级任务检测 > * 优化题库状态停用显示 > ## 4.4.13 (2023-05-12) ### 🔧 修复内容 * 修复题库缓存不是最新的BUG > ## 4.4.12 (2023-05-12) ### 🔧 修复内容 * 修复题库配置清空后会留下两个小括号的bug > ## 4.4.11 (2023-05-12) ### 🔧 修复内容 * 继续优化智慧树作业考试检测 > ## 4.4.7 (2023-05-11) ### 🔧 修复内容 * 将更新通知的请求方法改成油猴跨域请求,防止有些页面的 safe 策略阻止请求。 > * 优化智慧树考试作业提示 > ### ✨ 更新内容 * 新增题库开关功能 > ## 4.4.4 (2023-05-10) ### 🔧 修复内容 * 修复智慧树考试作业进入后不自动开始,而是需要刷新才能开始的BUG > ## 4.4.2 (2023-05-09) ### 🔧 修复内容 * 修复智慧树作业考试阅读理解小题读取失败的BUG > ## 4.4.1 (2023-05-09) ### 🔧 修复内容 * 优化没有题库配置的提示 > # 4.4.0 (2023-05-09) ### 🔧 修复内容 * 修复搜索结果图片显示问题 > * 优化单选题选项ABCD冗余并没有去掉的BUG > * 全面优化自动答题逻辑,并将搜索结果直接显示在各自的脚本面板下,无需反复跳转查看。 > * 修复智慧树图片题的BUG > * 优化日志显示 > * 优化智慧树视频加载缓慢时无法自动播放的BUG > * 优化智慧树校内课作业 > * 优化智慧职教MOOC的自动学习逻辑 > * 智慧树考试强制添加保存弹窗,并从头开始每题保存,防止用户切换题目导致保存失败 > * update build script > ### ✨ 更新内容 * 增加搜索结果与题目同步显示功能 > * 增加职教云考试功能 > ## 4.3.7 (2023-04-24) ### 🔧 修复内容 * 修复智慧职教MOOC学院自动答题无法处理判断题的BUG > ### ✨ 更新内容 * 添加职教云作业自动答题 > ### ⚡ 优化提升 * add woker.onElementSearched args[1] : root > ## 4.3.5 (2023-04-24) ### 🔧 修复内容 * 修复题库缓存搜索时每题只出一个结果的BUG > * 修复智慧职教MOOC自动答题的填空题无法填空的BUG > ## 4.3.3 (2023-04-24) ### 🔧 修复内容 * 撤回智慧树图片识别,否则导致作业考试无法使用。 > ## 4.3.2 (2023-04-24) ### 🔧 修复内容 * 修复一些显示BUG > ## 4.3.1 (2023-04-24) ### 🔧 修复内容 * 帮助智慧树修复图片题无法显示的BUG > # 4.3.0 (2023-04-24) ### 🔧 修复内容 * 优化浏览器环境问题 > * 修复题库搜题时出现 Cannot convert object to primitive value 问题 > * 优化搜索结果的显示,并且添加快捷百度一下按钮 > * 添加超星自动答题后暂停提示 > * 新增超星匹配域名: hnsyu.net > * 新增超星视频加载失败检测功能 > * 修复上个版本智慧职教MOOC学院中作业自动答题题目为空的BUG > * 修复手贱导致的判断题乱选的BUG > * 优化 $modal API ,修复 onClose 执行逻辑 > * 优化超星编辑框复制粘贴问题 > * 优化脚本教程,优化搜索结果,删除独立的通知提示和版本日志,转移到脚本首页中。 > * 优化屏蔽复制粘贴限制 > * 优化搜索结果空白的BUG > * 优化智慧树学习逻辑,用户手动切换视频时脚本可以重新生效。 > ### ✨ 更新内容 * 优化 el API 方便自定义样式 > * 将题库缓存储域切换成本地存储 > * 添加题库缓存功能 > * 添加智慧职教MOOC学院的作业自动答题功能,优化刷课逻辑。 > ## 4.2.31 (2023-04-20) ### 🔧 修复内容 * 修复超星图片题选择BUG > * 优化智慧树共享课和校内课的作业和考试 > ## 4.2.29 (2023-04-20) ### 🔧 修复内容 * 优化超星繁体字识别,优化http网站下复制粘贴问题 > * 优化智慧树作业考试文字识别BUG > * 修复超星倍速提示没有显示的BUG > ## 4.2.26 (2023-04-19) ### 🔧 修复内容 * 修复软件批量创建中无法导出模板的BUG > * 修复OCR初始化软件路径读取问题 > * 在OCS软件中不显示软件配置同步的面板 > * 修复脚本题库检测BUG,优化软件更新提示 > * - 优化超星章节测试题目解析 > * 对超星繁体字库加载进行异常处理 > * 删除清空答案选项 > * 修复超星输入框无法复制粘贴的BUG > * 修复超星章节测试出现年份丢失的BUG > * 修复超星章节测试出现年份丢失的BUG > * 修复搜索结果答案是图片但没有显示的BUG > * 修复智慧树考试无法识别题目的BUG > * 优化软件同步设置,优化题库状态显示 > * 每次渲染强制更新通知和版本日志 > ### ✨ 更新内容 * 添加题目冗余字段自定义删除功能 > ## 4.2.15 (2023-04-04) ### 🔧 修复内容 * 修复软件配置同步导致的各种问题 > * 修复搜索结果超时后全部题库都显示超时的BUG > * 修复通知和版本日志在有些页面访问不了的BUG > * 优化全局设置题库检测,增加防抖 > ## 4.2.11 (2023-04-03) ### 🔧 修复内容 * 修改项目链接 enncy/online-course-script => ocsjs/ocsjs > * 使用JSON消除自定义脚本的响应式特性,修复每个浏览器自定义脚数据相同的问题。 > * 新增软件 zip 打包方式,修复工作区丢失的BUG > * 修复部分用户加载不出导航页的BUG > * 修复软件标题栏重载时不自动切换的BUG > * 修复软件浏览器标签输入时不自动提示的BUG > * 修复软件运行途中删除文件会导致所有浏览器关闭的BUG > * 修改app加载文件以及油猴插件路径问题 > * 优化 app remote 模块,修改成异步通信,修复同步通信导致的页面卡死问题。 > * 优化软件导航页,将ocs-app接口代理删除,全局使用15319端口进行访问,修改浏览器启动选项,使浏览器环境更接近真实浏览器 > * 优化软件更新程序,并修复更新通知抽搐问题 > * 优化软件监控问题,以及启动脚本,和浏览器状态处理优化。 > * 优化软件自动化脚本,添加类型声明 > * app version update > * remove public build > * 持续优化超星直播回放脚本 > * 调整职教云任务读取速度 > * 更改 $gm 和 $ 修改后的代码 > * 更新超星未完成章节会出现提示框的问题 > * 更新教程链接 > * 更新智慧树脚本 > * 继续修复上个版本问题 > * 将 dropdown 的 opiton 元素改成 div 元素 > * 将 unsafeWindow 全局变量封装成 useUnsafeWindow > * 将超星视频进度步数调整为0.25,倍速颗粒度控制更高。 > * 将整个项目修改成跨域响应式模式 > * 脚本学习核心修改,适配 useSettings 和 useContext 两个 API > * 解决cx任务点未完成出现弹窗的问题 > * 删除超星在作业考试中题目多余内容导致的正确率下降 > * 删除全局变量 OCS > * 添加本地存储初始化时删除无用字段 > * 为所有参数提供默认值,防止页面空白或者头部空白。 > * 限制请求超时时间最小为10 > * 新增ICVE字段 > * 修复 store 环境检测问题 > * 修复 StringUtils 导入问题 > * 修复$message不能永久显示bug > * 修复不能关闭路线切换的BUG > * 修复部分页面存在不执行 interactive 生命周期的问题 > * 修复超星复习模式自动切换的BUG > * 修复超星视频答题永远只选2个选项的BUG > * 修复超星音频任务不能播放的BUG > * 修复答题结果中存在答案但是不选的BUG > * 修复打包后文件中文变成Unicode编码的问题 > * 修复代码中带有特殊字符时,unicode 转换失败 > * 修复跨域问题 > * 修复每次页面加载都要删除core监听队列的bug > * 修复日志面板不会实时滚动的BUG > * 修复视频频繁停止导致的频繁验证码 > * 修复数字输入可以超过范围的BUG > * 修复题库配置报错异常未捕获的BUG > * 修复职教云进度不显示的BUG > * 修复职教云任务获取出现子节点BUG > * 修复职教云子节点读取的问题,优化任务列表,优化学习脚本 > * 修复智慧树倍速失效的BUG > * 修复智慧树视脚本的频路径匹配 > * 修复智慧树视频测验弹窗无法关闭的BUG > * 修复select,range等对value不显示的bug > * 修复zhs弹窗关闭的问题 > * 修改 $creator.tooltip 的卡死BUG,很多元素时会导致卡顿,这里将 tooltip 变成单个全局元素。 > * 修改 userjs.templete 模板文件,兼容跨域响应式,并修改 homepage 等字段 > * 修改生命周期执行规则 > * 修改搜索结果文案 > * 修改元素是否存在的判断 > * 修改cdn地址 > * 移除页面反调试脚本至超星 > * 因全局变量删除,且需要封装 store 中多个变量的处理,以及防止变量污染,新增了 useContext 和 useSettings 的API,相应涉及代码同步更新。 > * 优化 start 主函数,防止脚本重新执行 > * 优化超星直播回放 > * 优化登录脚本,非空判断 > * 优化获取远程本地软件题库配置功能 > * 优化题库解析器 > * 优化题库配置字段 > * 优化图片识别时图片链接显示的问题 > * 优化响应式存储 > * 优化页面通讯以及构建 > * 优化智慧树脚本,适配 useContext 和 useSettings 两个 API > * 优化自动答题逻辑,修复有答案不选的BUG > * 优化OCS环境加载问题 > * 暂时删除视频答题功能 > * 增加答题器的 await 等待机制,多选题选择时需要 await 等待 > * 修复 release.sh 打包文件错误时仍然执行的BUG > * 优化构建发布release.sh文件 > * 修复搜索结果显示与搜题不一致的BUG,优化各脚本,持续优化学习通任务检测算法。 > * 持续修复超星问题 > * 登录时将脚本至于左上方防止挡住软件操作 > * 删除超星的强制答题选项 > * 删除脚本展开功能 > * 删除浏览器窗口检测,经过测试全屏游戏中也会进行误报。 > * 使用固定端口与桌面软件通讯 > * 添加环境检测,优化职教云逻辑 > * 修复超星多个视频同时播放的BUG > * 修复超星频繁验证码的BUG > * 修复超星章节测试填空题不填的问题 > * 修复超星BUG > * 修复窗口移除页面问题 > * 修复答题器出现错误时会一直卡死 > * 修复复制粘贴解除限制的BUG > * 修复通用-全局设置中题库状态检测BUG > * 修复学习通刷课逻辑 > * 修复智慧树答题后不保存的BUG > * 修复智慧树作业答题完成后选择BUG > * 优化播放函数 > * 优化超星和智慧树脚本 > * 优化窗口加载问题 > ### ✨ 更新内容 * 脚本2.0与软件4.0更新完毕 > * 脚本更新至 4.0 , 软件更新至 2.0 > * 将 electron 升级到 23.0.0 , 删除 electron 原生窗口样式 frame: false,自定义 titlebar ,并优化主题切换。 > * 将OCR识别模块整合打包,修复OCR路径存在空格执行失败的BUG > * 删除超星智慧树的其他登录,新增自定义链接进入。 > * 文件中新增图片预览一栏,定时更新页面图像,无需在多个浏览器中切换查看。 > * 新增网页打开脚本,将ocr模块一同打包 > * 新增仪表盘功能,新增浏览器拓展管理功能,新增新手教程功能,新增批量运行文件功能,并重写文件管理系统。 > * 新增用户脚本,可自定义网络脚本添加到本地脚本,并自动载入本地脚本,极大拓展软件功能。 > * 新增资源加载器,优化各种资源加载问题,移除原有的OCR模块打包方式。 > * 修改app主进程从 commonjs 改成 ts > * 优化 script 工作线程,规范代码,修复人工错误导致的无限递归。 > * 新增软件服务端,使脚本可读取软件信息。 > * - 拆分 core ,- 添加多线程答题 - 添加超星作业考试功能 - 将 core/utils 全部进行 $ 前缀声明以便区分 - 优化搜题结果元素。 > * 4.0 init > * 4.0 init > * 持续优化zhs答题功能 > * 处理 onbeforeunload 执行结果 > * 给 script 的全部声明周期同样添加相同的事件触发 > * 将 OCSWoker 和 Script 都变成 EventEmitter 对象,实现内部的事件分发 > * 删除窗口关闭按钮,用户可以通过“窗口设置”进行隐藏窗口。 > * 搜索结果页面新增复制题目按钮 > * 添加 $creator 元素创建工具类变量,并且重写登录脚本,使用动态添加元素的方式重写。 > * 添加 $modal.onClose 不管关闭还是确认和取消都会触发此函数 > * 添加 onhistorychange 钩子 > * 添加 Script.configs 参数生产后 this.cfg 的代码提示 > * 添加 script.onrender 钩子 > * 添加 SearchResultsElement 元素 > * 添加 StringUtils 工具类 > * 添加超星登录脚本 > * 添加面板选择器分组功能 > * 添加全局跨域通讯对象,并使用此技术重写模态框调出方法 > * 添加使用教程, 重载 el 函数, 修改cors跨域模块使用 setTab, getTab 进行cors跨域标签分区. 优化页面选择逻辑 > * 添加页面复制粘贴限制解除脚本 > * 添加在线搜题功能 > * 添加智慧树自动答题功能 > * 完成4.0基本架构 > * 完成脚本内容的元素和数值同步 > * 完成智慧树登录脚本,学习脚本,修复BUG > * 新增 $creator 多个API > * 新增 $message 和 $model 方法进行用户交互 > * 新增 通用-搜索结果 显示 > * 新增 智慧职教mooc脚本 , 优化职教云脚本 > * 新增 Script.methods 方法可自定义对外暴露函数,优化搜索结果的显示。 > * 新增 SCript.pin 方法,置顶某个面板 > * 新增 unsafeWindow 全局变量 > * 新增超星直播回放视频脚本 > * 新增跨域响应式特性 > * 新增智慧树:清晰度选择,定时停止,视频总时长计算,等功能 > * 新增智慧树的视频画质选择功能 > * 新增智慧树视频反反混淆脚本 > * 新增智慧树验证码检测功能 > * 新增智慧职教(职教云)脚本 > * 新增j脚本热更新功能,大大提升开发效率 > * 修改脚本声明写法,修改 project.scripts 由数组变成对象声明,好处是可以由 project.scripts.[脚本名].cfg.xxx 进行类型推断实现类型提示。 > * 优化 cx 的 Project 新写法 > * 优化使用$creator > * 优化渲染工程 > * 优化智慧树文字识别脚本 > * 增加多线程查题功能 > * 增加智慧树习惯分检测,学习记录查询,答题手动控制 > * 支持跨域调出模态框 > * 智慧职教(职教云)发布 > * 添加全局错误捕获功能 > * 超星刷课逻辑重写,兼容旧版浏览器CSS样式 > * 添加浏览器版本检测 > * 添加页面关闭提示 > * 添加智慧职教音频支持 > * 新增【职教云】和【智慧职教】脚本 > * 新增浏览器最小化检测脚本 > * 修改浏览器下载链接,新增脚本版本更新检测 > * version release 4.0.5 > * version update to 4.1.0 > * 新增utils包,其内置各种实用工具。其中新增脚本打包器,可对打包流程进行优化。 > ### ⚡ 优化提升 * 更新 MoelElement 参数以及实现 > * 添加 SearchResultsElement 元素映射 > * 添加defineScript中的domain和hide字段,实现动态修改脚本头部信息的功能。 > # 3.13.0 (2022-05-23) ### ✨ 更新内容 * 添加页面反调试脚本 > * 新增超星视频中答题功能 > * 新增网课视频选项:显示视频进度 > ## 3.12.3 (2022-05-22) ### 🔧 修复内容 * 修复误删最大长宽导致的超出页面范围 > ## 3.12.2 (2022-05-21) ### 🔧 修复内容 * 修复某些填空题识别不出的BUG > ### ✨ 更新内容 * 新增自动答题选项:强制提交 > # 3.12.0 (2022-05-21) ### 🔧 修复内容 * 删除多余输出 > * 修改userjs打包代码未加分号报错 BUG > * 优化多选题答案分割判断 > ### ✨ 更新内容 * 新增随机作答功能 > * 新增图片题识别脚本,新增搜索结果显示题目图片和答案图片 > # 3.11.0 (2022-05-21) ### 🔧 修复内容 * 新增未经压缩代码的打包 > ### ✨ 更新内容 * 新增userjs未经压缩代码打包 > ## 3.10.6 (2022-05-20) ### 🔧 修复内容 * 修复题库配置解析器BUG > ## 3.10.4 (2022-05-20) ### 🔧 修复内容 * 修复浏览器报错BUG > * 修复超星考试页面样式问题 > * 修复题库配置BUG > ## 3.10.2 (2022-05-19) ### 🔧 修复内容 * 还原文件,修改 release 执行错误但继续打包发布的BUG > ## 3.10.1 (2022-05-19) ### 🔧 修复内容 * 修复软件浏览器选择BUG,并停止火狐浏览器使用。 > * 修改 typr 库,减少部分打包体积。 > # 3.10.0 (2022-05-17) ### ✨ 更新内容 * 新增超星繁体字识别选项 - 字典识别 > * 新增超星强制答题功能 > ## 3.9.6 (2022-05-16) ### 🔧 修复内容 * 修复日志记录问题 > * 修复软件文件重命名时,运行文件名不同步的BUG > * 修复智慧树登录后白屏错误的BUG > ### ✨ 更新内容 * 新增软件自动选择浏览器路径功能 > * 新增自定义脚本载入路径功能, 路径将托管到官方服务器 > ## 3.9.5 (2022-05-16) ### 🔧 修复内容 * 修复新增 shadowroot 后,复制粘贴脚本失效的BUG > * 优化超星识别时可能遇到选项按钮被删除的BUG > ### ✨ 更新内容 * 新增 AnswererWrapper 参数: headers 和 type > * 新增题库配置跨域模块,可对不同域名的服务器进行跨域访问,并且新增 root 环境变量,可访问元素题目的跟节点元素对象。 > # 3.8.0 (2022-05-11) ### 🔧 修复内容 * 避免重复劫持函数导致页面内存移除 > * 修复智慧树倍速不能立刻改变的BUG > ### ✨ 更新内容 * + 新增消息提示 + 修复火狐底部版本不显示的BUG + 优化eslint代码 + 增加文字识别错误提示 + 将原有弹出框修改为消息提示 + 增加API:message > * 使用ShadowRoot对脚本进行加固 > ## 3.7.2 (2022-05-07) ### 🔧 修复内容 * 修改 store 初始化位置 > ### ✨ 更新内容 * 添加智慧树文本识别脚本和屏蔽视频检测脚本 > # 3.7.0 (2022-05-04) ### ✨ 更新内容 * 添加答题等待时间,方便检查或者使用其他答题工具。 > ## 3.6.4 (2022-05-04) ### 🔧 修复内容 * 深度优化OCR > * 修复填空题多个填空不填的BUG > ## 3.6.2 (2022-04-30) ### 🔧 修复内容 * 修改 OCR 脚本加载路径, 确保能够访问 work 和 core 脚本 > ## 3.6.1 (2022-04-30) ### 🔧 修复内容 * 修改环境依赖 > # 3.6.0 (2022-04-29) ### ✨ 更新内容 * 新增智慧树共享课考试脚本 > ## 3.5.5 (2022-04-28) ### 🔧 修复内容 * 适当增大了文本便于识别 > ## 3.5.4 (2022-04-28) ### 🔧 修复内容 * 优化OCR空格问题,还有上个版本OCR锁初始化的问题 > ## 3.5.3 (2022-04-28) ### 🔧 修复内容 * 优化 OCR , 解决题目选项没有识别的BUG > ## 3.5.2 (2022-04-27) ### 🔧 修复内容 * 优化 OCR 加载逻辑 > ## 3.5.1 (2022-04-27) ### 🔧 修复内容 * 优化 OCR 数据加载问题 > # 3.5.0 (2022-04-27) ### 🔧 修复内容 * 修复超星有时不能自动下一章的BUG > ### ✨ 更新内容 * 繁体字乱码识别功能 > * 开发智慧树倍速选项 > ## 3.4.5 (2022-04-23) ### 🔧 修复内容 * 修复软件重命名有时会无效的BUG > * 优化软件启动加载 > ## 3.4.4 (2022-04-22) ### 🔧 修复内容 * 修复提交设置的BUG > ## 3.4.3 (2022-04-21) ### 🔧 修复内容 * 删除无用的选项:搜题错误时暂停 > ## 3.4.2 (2022-04-19) ### 🔧 修复内容 * 修复软件设置空白的BUG > * 修复自动答题提交设置保存不了的BUG > * app version upate > ## 3.4.1 (2022-04-17) ### 🔧 修复内容 * 修复ABCD纯答案直接点击的BUG > # 3.4.0 (2022-04-17) ### ✨ 更新内容 * 新增智慧树学分课作业脚本 > ## 3.3.14 (2022-04-15) ### 🔧 修复内容 * 修复 release.sh 版本命令 > ## 3.3.11 (2022-04-13) ### 🔧 修复内容 * 修复 paste 和 input 共存导致的粘贴BUG > ## 3.3.10 (2022-04-13) ### 🔧 修复内容 * 修复不能右键复制粘贴的BUG > ## 3.3.9 (2022-04-13) ### 🔧 修复内容 * 修复上个版本store加载问题 > ## 3.3.8 (2022-04-13) ### 🔧 修复内容 * app 兼容OCS助手最新版的响应式特性 > ### ✨ 更新内容 * 新增全局存储功能,使用油猴自带API实现。 > ## 3.3.7 (2022-04-13) ### 🔧 修复内容 * 支持纯浏览器端的加载,仅用于核心API的调用。 > ### ✨ 更新内容 * 删除调试输出 > ## 3.3.6 (2022-04-13) ### 🔧 修复内容 * 修复打包压缩选项 > ### ✨ 更新内容 * 新增超星 : 屏蔽作业考试填空简答题粘贴限制 功能 > ## 3.2.20 (2022-04-09) ### 🔧 修复内容 * 延长一点视频暂停后启动的时间,防止超星鬼畜。 > ## 3.3.5 (2022-04-13) ### 🔧 修复内容 * 修复搜索结果 undefined 的 BUG, 修复超星章节测试填空题BUG > ## 3.3.4 (2022-04-13) ### 🔧 修复内容 * 修复超星考试作业页面不能复制粘贴的BUG > * 修复上个版本响应式导致的倍速,音量失效的BUG > * 修复智慧树复习模式的BUG,修复已经播放完的视频但没有完成却跳过的BUG > ## 3.3.3 (2022-04-12) ### 🔧 修复内容 * 兼容库模式,并且优化自动答题答案显示 > ### ✨ 更新内容 * 新增自动答题答案预览功能 > ## 3.3.2 (2022-04-12) ### 🔧 修复内容 * 修复闯关模式因为刷新太快任务点没有出现导致重复的BUG > * 修复答题配置输入后会消失的BUG, 新增音量设置 > # 3.3.0 (2022-04-11) ### 🔧 修复内容 * 修复各种问题,并且兼容了响应式特性 > * 修改打包方式为压缩打包 > ### ✨ 更新内容 * 切换网络路线 > * 响应式特性 > * vnode 重构成 tsx , 并且新增数据响应式特性 > ## 3.2.20 (2022-04-09) ### 🔧 修复内容 * 延长一点视频暂停后启动的时间,防止超星鬼畜。 > ## 3.2.19 (2022-04-08) ### 🔧 修复内容 * 修复切换路线后倍速无效的BUG > ## 3.2.18 (2022-04-08) ### ✨ 更新内容 * 新增超星视频路线切换功能 > ## 3.2.16 (2022-04-08) ### 🔧 修复内容 * 修复快捷键有时候失效的问题 > ## 3.2.14 (2022-04-08) ### ✨ 更新内容 * 新增隐藏按钮 > * 新增ocs快捷键,可重置位置,优化面板初始位置 > ## 3.2.13 (2022-04-07) ### ✨ 更新内容 * 彻底修复超星验证码问题 > ## 3.2.12 (2022-04-05) ### ✨ 更新内容 * 新增超星支持域名 edu.cn , 修复多个视频播放时,播放完成继续播放的BUG > ## 3.2.11 (2022-04-04) ### 🔧 修复内容 * 修改点击间隔 > ## 3.2.10 (2022-04-04) ### 🔧 修复内容 * 继续优化答题问题 > ## 3.2.9 (2022-04-04) ### 🔧 修复内容 * 修复超星重复暂停的BUG > ## 3.2.8 (2022-04-04) ### ✨ 更新内容 * 新增解除右键,复制粘贴限制的功能, 修复超星播放视频重复卡死的BUG > ## 3.2.7 (2022-04-04) ### ✨ 更新内容 * 答案判断优化 > ## 3.2.6 (2022-04-03) ### 🔧 修复内容 * 修复软件1.2.0自动更新BUG > * 修复智慧树作业重复关闭的BUG > ### ✨ 更新内容 * 新增 common 包 , 修复脚本执行BUG , 修复通知 , 新增打包脚本 scripts 文件夹 > * aPP版本更新 1.2.0 > ## 3.2.5 (2022-04-01) ### 🔧 修复内容 * 修复答题时多选不全的BUG, 修复答题时答案为空报错的BUG > * 修改远程信息获取路径 > ### ✨ 更新内容 * 自动更新功能, scripts 分包 , 浏览器端修改为 core 文件夹 > ## 3.2.4 (2022-03-31) ### 🔧 修复内容 * 修复上个版本视频跳过问题 > * 修复油猴脚本更新BUG > ## 3.2.3 (2022-03-30) ### ✨ 更新内容 * 添加任务点是否完成检测,不重复执行已完成任务点, 修复判断题不选择的BUG > ## 3.2.2 (2022-03-27) ### ✨ 更新内容 * 添加智慧树学分课脚本 > ## 3.2.1 (2022-03-27) ### ✨ 更新内容 * 添加禁止弹窗脚本 > # 3.2.0 (2022-03-26) ### ✨ 更新内容 * 软件更新,支持超星学习作业考试,支持脚本自动更新 > ## 3.1.11 (2022-03-26) ### ✨ 更新内容 * 更新软件,加载时自动更新ocs脚本 > ## 3.1.10 (2022-03-26) ### 🔧 修复内容 * 修复脚本只能在油猴环境下执行的BUG > ## 3.1.8 (2022-03-26) ### 🔧 修复内容 * 修改答案结果判断bug > ## 3.1.7 (2022-03-25) ### 🔧 修复内容 * 修改题库配置设置,取消必选,添加判断,如果没有题库设置,则不开始自动答题。 > * 修改油猴配置 > * 兼容cx选项获取不到,以及设置保存bug > * 修复答题解析器bug, 更新做题标题获取,修复学习时章节测验类型获取失败bug, 更新 app 的脚本 > * 修改答题配置解析器 > * 修改样式引入 > * 修改 url cdn 资源 > # 3.0.0-beta.9 (2022-03-24) ### 🔧 修复内容 * 修改数据路径 > ### ✨ 更新内容 * 版本更新至 beta.6 > * 新增答题器,新增题库配置选项,新增zhs作业功能 > * 修改为单进程软件,支持.ocs文件的点击加载,新增软件标题栏,删除原生标题栏 > * 版本更新 > * 添加超星学习,考试,作业脚本。 添加日志,搜题结果面板。 > # 3.0.0-beta.6 (2022-03-15) ### ✨ 更新内容 * 新增 script/browser 端的面板显示,替换之前的油猴头部信息加载模式,添加app主进程端的脚本调用 > * 添加文件关闭编辑功能,取消页面所有动画效果,删除'关于'页面,新增'帮助'一栏。 > # 3.0.0-beta.5 (2022-03-07) ### 🔧 修复内容 * fix dependency security > * 优化侧边栏: 优化文件列表头部,优化文件列表搜索 > ### ✨ 更新内容 * add glup > * 添加终端显示,添加脚本执行,添加文件属性 > * 添加文件夹管理,添加右键菜单,初始化设置和关于页面 > * add elctron-builder in app , init page view in web > * init project > * init packages : web app scripts > * add commander line support, and update web view > * add cx and zhs login > * change folder name, and add browser script > * add tempermonkey support, update browser export > * add new login : phone-code login > * 修改 package.json , 调整登录api > * add script package > * update tests README.md and add new login way of cx : phone-code-login > * 添加文件解析,使用懒加载进行文件的显示 > * 添加文件拖拽,文件搜索功能 > * 添加重命名功能,添加目录展开记录保存,添加帮助页面 > * 文件编辑,文件拓展 > * 支持文件(夹)拖拽放置,文件(夹)的创建,删除 > ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2022 enncy Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================
# OCS 网课助手 > OCS (Online Course Script) 网课刷课脚本,帮助大学生解决网课难题 ![GitHub Repo stars](https://img.shields.io/github/stars/ocsjs/ocsjs) ![npm](https://img.shields.io/npm/v/ocsjs?color=red) ![NPM](https://img.shields.io/npm/l/ocsjs) ![今日安装](https://img.shields.io/badge/dynamic/json?color=orange&label=今日安装&query=$.data.today_install&url=https://scriptcat.org/api/v2/scripts/367) ![总共安装](https://img.shields.io/badge/dynamic/json?color=red&label=总共安装&query=$.data.total_install&url=https://scriptcat.org/api/v2/scripts/367)
## 官网及教程 [https://docs.ocsjs.com](https://docs.ocsjs.com)
================================================ FILE: package.json ================================================ { "name": "ocsjs", "version": "4.13.4", "description": "ocs - online course script 在线网络课程辅助工具", "types": "./lib/src/core/index.d.ts", "main": "./lib/src/core/index.js", "files": [ "lib", "dist" ], "scripts": { "dev": "gulp -f ./scripts/dev-core.js & npm run changelog:simplify", "build": "gulp -f ./scripts/build-core.js", "tsc": "pnpm lint && gulp -f ./scripts/tsc.js", "lint": "pnpm format && eslint ./packages --ext .ts,.tsx,.js,.jsx --fix", "format": "prettier -c ./.prettierrc.json **/*.ts **/*.js **/*.css --write", "release": "sh ./scripts/release.sh", "pub:minor": "npm version minor && pnpm build:core && npm publish", "pub:major": "npm version major && pnpm build:core && npm publish", "changelog:simplify": "gulp -f ./scripts/simplify_changelog.js", "changelog": "conventional-changelog -p angular -i CHANGELOG.md --same-file -r 0", "changelog:current": "conventional-changelog -p angular -o CHANGELOG_CURRENT.md -r 1", "init-commitizen": "commitizen init cz-conventional-changelog --save --save-exact" }, "devDependencies": { "@html-eslint/eslint-plugin": "^0.15.0", "@types/node": "^17.0.16", "@typescript-eslint/eslint-plugin": "^5.19.0", "@typescript-eslint/parser": "^5.19.0", "browser-env": "^3.3.0", "conventional-changelog-cli": "^2.2.2", "cz-conventional-changelog": "^3.3.0", "del": "^6.0.0", "eslint": "^7.32.0", "eslint-config-prettier": "^8.5.0", "eslint-config-standard": "^16.0.3", "eslint-plugin-import": "^2.22.1", "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-promise": "^4.2.1", "eslint-plugin-vue": "^9.8.0", "gulp": "^4.0.2", "gulp-cli": "^2.3.0", "gulp-zip": "^5.1.0", "prettier": "^2.6.2", "typescript": "^4.5.5" }, "repository": { "type": "git", "url": "git+https://github.com/ocsjs/ocsjs.git" }, "keywords": [ "ocs", "script", "playwright", "puppeteer", "electron", "vue", "ant-design-vue", "typescript" ], "author": "enncy", "license": "MIT", "bugs": { "url": "https://github.com/ocsjs/ocsjs/issues" }, "homepage": "https://github.com/ocsjs/ocsjs#readme", "config": { "commitizen": { "path": "./node_modules/cz-conventional-changelog" } }, "dependencies": { "@microsoft/tsdoc": "^0.14.2" } } ================================================ FILE: packages/core/.gitignore ================================================ # customize test/ stats.html src/assets/css docs/ # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* .pnpm-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage *.lcov # nyc test coverage .nyc_output # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # Snowpack dependency directory (https://snowpack.dev/) web_modules/ # TypeScript cache *.tsbuildinfo # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional stylelint cache .stylelintcache # Microbundle cache .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variable files .env .env.development.local .env.test.local .env.production.local .env.local # parcel-bundler cache (https://parceljs.org/) .cache .parcel-cache # Next.js build output .next out # Nuxt.js build / generate output .nuxt dist # Gatsby files .cache/ # Comment in the public line in if your project uses Gatsby and not Next.js # https://nextjs.org/blog/next-9-1#public-directory-support # public # vuepress build output .vuepress/dist # vuepress v2.x temp and cache directory .temp .cache # Docusaurus cache and generated files .docusaurus # Serverless directories .serverless/ # FuseBox cache .fusebox/ # DynamoDB Local files .dynamodb/ # TernJS port file .tern-port # Stores VSCode versions used for testing VSCode extensions .vscode-test # yarn v2 .yarn/cache .yarn/unplugged .yarn/build-state.yml .yarn/install-state.gz .pnp.* ================================================ FILE: packages/core/.npmignore ================================================ # customize test/ stats.html assets/ src/ # Dependency directories node_modules/ ================================================ FILE: packages/core/package.json ================================================ { "name": "@ocsjs/core", "version": "4.0.4", "description": "core package of ocs", "main": "./lib/index.js", "types": "./lib/index.d.ts", "files": [ "lib", "dist" ], "repository": { "type": "git", "url": "git+https://github.com/ocsjs/ocsjs.git" }, "keywords": [ "ocs", "script", "playwright", "puppeteer", "electron", "vue", "ant-design-vue", "typescript" ], "author": "enncy", "license": "MIT", "bugs": { "url": "https://github.com/ocsjs/ocsjs/issues" }, "homepage": "https://github.com/ocsjs/ocsjs#readme", "devDependencies": { "@types/event-emitter": "^0.3.3", "@types/lodash": "^4.14.194", "@types/string-similarity": "^4.0.0", "@types/tampermonkey": "^4.0.5", "dotenv": "^16.0.3", "rollup-plugin-visualizer": "^5.9.0", "typedoc": "^0.23.23", "vite": "^2.9.15", "vite-plugin-banner": "^0.6.1" }, "dependencies": { "easy-us": "^0.0.60", "events": "^3.3.0", "lodash": "^4.17.21", "playwright-core": "^1.40.1", "semver": "^7.3.5", "string-similarity": "^4.0.4" } } ================================================ FILE: packages/core/src/core/answer-wrapper/answer.wrapper.handler.ts ================================================ import { AnswererWrapper, SearchInformation, Result } from './interface'; import { request } from '../utils/request'; import { $ } from '../../utils'; export const AnswerWrapperHandlerConfig = { // 超时时间,单位毫秒 timeout_seconds: 60 }; /** * * 默认题库配置解析器 * * @example * * ```js * * // 假设有一个接口 : https://example.com/search?title=1+2,2+3 * // 此接口返回 {code: 1, data: { answers: [3 , 5] , title:'1+2' }, msg:'成功'} * * defaultAnswerWrapperHandler({ * titleElements: Array.from(document.querySelector('.title')) * }, * [ * // 可以有多个构造器,最终通过 answerPath 一起合并到一个列表并返回 * { * url: 'https://example.com/search', * method: 'get', * answerPath: 'data.answers', * data:{ * title: 'titleElements[0]' // 1+2,2+3 * } * } * ]) // [3 , 5] * * * ``` * * @param elements 题目元素 * @param answererWrappers 题库配置器数组 * @returns */ export async function defaultAnswerWrapperHandler( answererWrappers: AnswererWrapper[], // 上下文解析环境 env: { title?: string; options?: string; type?: string; [x: string]: any; } ): Promise { const searchInfos: SearchInformation[] = []; const temp: AnswererWrapper[] = JSON.parse(JSON.stringify(answererWrappers)); if (temp.length === 0) { throw new Error('题库配置不能为空,请配置后重新开始自动答题。'); } // 多线程请求 await Promise.all( temp.map(async (wrapper) => { // 解构数据,并赋初始值 const { name = '未知题库', homepage = '#', method = 'get', type = 'fetch', contentType = 'json', headers = {}, data: wrapperData = {}, handler = 'return (res)=> [JSON.stringify(res), undefined]' } = wrapper; try { // 答案列表 let results: Result[] = []; // 请求数据 let requestData; // 请求地址 let url: URL; if (method.toLocaleLowerCase() === 'get') { url = new URL(resolvePlaceHolder(wrapper.url, { encodeURI: true })); /** * 如果 data 存在数据并且 method 为 get,则将 data 数据拼接到 url 上,覆盖原有的 url 同名参数 * data 参数的优先级高于 url 参数 */ Object.keys(wrapperData).forEach((key) => { // searchParams.set 方法会自动编码,所以不需要 encodeURI: true url.searchParams.set(key, resolvePlaceHolder(wrapperData[key])); }); // get 的请求数据为空 requestData = {}; } else if (method.toLocaleLowerCase() === 'post') { url = new URL(wrapper.url); // 构造请求数据 const data: Record = Object.create({}); /** 构造一个请求数据 */ Object.keys(wrapperData).forEach((key) => { // 如果存在字段解析器 if (typeof (wrapperData as any)[key] === 'object' && Reflect.has((wrapperData as any)[key], 'handler')) { // eslint-disable-next-line no-new-func const handler = Function(Reflect.get((wrapperData as any)[key], 'handler'))(); if (typeof handler !== 'function') { throw new Error('data 字段解析器必须返回一个函数'); } const result = handler(env); Reflect.set(data, key, result); } else { // 解析data数据 Reflect.set(data, key, resolvePlaceHolder(wrapperData[key])); } }); requestData = data; } else { throw new Error('不支持的请求方式'); } // 发送请求 const responseData = await Promise.race([ request(url.toString(), { method, // 历史遗留的命名问题 responseType: contentType, data: requestData, type, headers: JSON.parse(JSON.stringify(headers || {})) }), $.sleep((AnswerWrapperHandlerConfig.timeout_seconds ?? 60) * 1000) ]); if (responseData === undefined) { throw new Error('题库请求超时,可能是题库问题,或者请检查网络或者重试。'); } /** 从 handler 获取搜索到的题目和回答 */ // eslint-disable-next-line no-new-func const responseHandler = Function(handler)(); if (typeof responseHandler !== 'function') { throw new Error('handler 响应处理器必须返回一个函数'); } const info = responseHandler(responseData); if (info && Array.isArray(info)) { /** 如果返回一个二维数组 */ if (info.every((item: any) => Array.isArray(item))) { results = results.concat( info.map((item: any) => ({ question: item[0], answer: item[1], extra_data: item[2] || {} })) ); } else { results.push({ question: info[0], answer: info[1], extra_data: info[2] || {} }); } } searchInfos.push({ url: wrapper.url, name, homepage, results, response: responseData, data: requestData }); } catch (error) { console.error(error); searchInfos.push({ url: wrapper.url, name, homepage, results: [], response: undefined, data: undefined, error: (error as any)?.message || '题库连接失败' }); } }) ); // 替换占位符 function resolvePlaceHolder(data: any, options?: { encodeURI?: boolean }) { if (typeof data === 'string') { const matches = data.match(/\${(.*?)}/g) || []; matches.forEach((placeHolder) => { /** 获取占位符的值 */ const value: any = env[placeHolder.replace(/\${(.*)}/, '$1')]; data = data.replace(placeHolder, options?.encodeURI ? encodeURIComponent(value) : value); }); } else if (typeof data === 'object') { // 递归替换 const keys = Object.keys(data); for (const key of keys) { data[key] = resolvePlaceHolder(data[key], options); } } return data; } return searchInfos; } ================================================ FILE: packages/core/src/core/answer-wrapper/answer.wrapper.parser.ts ================================================ import { AnswererWrapper } from './interface'; import { request } from '../utils/request'; /** * 解析题库配置 数据来源可以是 url , base64 , json , json-string */ export class AnswerWrapperParser { /** 从 json 字符串中解析 */ static fromObject(json: any): AnswererWrapper[] { const aw: AnswererWrapper[] = json; if (aw && Array.isArray(aw)) { if (aw.length) { for (let i = 0; i < aw.length; i++) { const item = aw[i]; if (typeof item.name !== 'string') { throw new Error(`第 ${i + 1} 个题库的 名字(name) 为空`); } if (typeof item.url !== 'string') { throw new Error(`第 ${i + 1} 个题库的 接口地址(url) 为空`); } if (typeof item.handler !== 'string') { throw new Error(`第 ${i + 1} 个题库的 解析器(handler) 为空`); } if (item.headers && typeof item.headers !== 'object') { throw new Error(`第 ${i + 1} 个题库的 头部信息(header) 应为 对象 格式`); } if (item.data && typeof item.data !== 'object') { throw new Error(`第 ${i + 1} 个题库的 提交数据(data) 应为 对象 格式`); } const contentTypes = ['json', 'text'] as AnswererWrapper['contentType'][]; if (item.contentType && contentTypes.every((i) => i !== item.contentType)) { throw new Error(`第 ${i + 1} 个题库的 contentType 必须为以下选项中的一个 ${contentTypes.join(', ')}`); } const methods = ['post', 'get'] as AnswererWrapper['method'][]; if (item.method && methods.every((i) => i !== item.method)) { throw new Error(`第 ${i + 1} 个题库的 method 必须为以下选项中的一个 ${methods.join(', ')}`); } const types = ['fetch', 'GM_xmlhttpRequest'] as AnswererWrapper['type'][]; if (item.type && types.every((i) => i !== item.type)) { throw new Error(`第 ${i + 1} 个题库的 type 必须为以下选项中的一个 ${types.join(', ')}`); } } return aw; } else { throw new Error('题库为空!'); } } else { throw new Error('题库配置格式错误!'); } } static fromJSONString(json: string) { const raw = json.toString(); try { return JSON.parse(raw); } catch { throw new Error(`格式错误,必须为:json字符串 或 题库配置链接`); } } /** 从 url 中解析 */ static async fromURL(url: string) { const text = await request(url, { responseType: 'text', method: 'get', type: 'fetch' }); return this.fromJSONString(text); } /** 从 base64 解析 */ static fromBase64(base64: string) { return this.fromJSONString(Buffer.from(base64, 'base64').toString('utf8')); } /** * 解析题库配置 数据来源可以是 url , base64 , json , json-string */ static from(value: any): AnswererWrapper[] | Promise { if (typeof value === 'string') { if (value.startsWith('http')) { return this.fromURL(value); } else { return this.fromJSONString(value); } } else { return this.fromObject(value); } } } ================================================ FILE: packages/core/src/core/answer-wrapper/index.ts ================================================ export * from './answer.wrapper.handler'; export * from './answer.wrapper.parser'; export * from './interface'; ================================================ FILE: packages/core/src/core/answer-wrapper/interface.ts ================================================ /** 题目答案 */ export interface Result { question: string; answer: string; extra_data?: object; } /** 题库查询信息 */ export interface SearchInformation { results: Result[]; name: string; url?: string; /** 主页 */ homepage?: string; /** 题目答案 */ /** 请求响应内容 */ response?: any; /** 请求发起内容 */ data?: any; /** 错误数据 */ error?: string; } /** * 题库配置器 */ export interface AnswererWrapper { /** 答题器请求路径 */ url: string; /** 题库名字 */ name: string; /** 题库网址 */ homepage?: string; /** 请求数据 */ data?: Record; /** 请求方法 */ method: 'post' | 'get'; /** 定义 handler 中的参数类型 */ contentType: 'json' | 'text'; /** 请求模式 */ type: 'fetch' | 'GM_xmlhttpRequest'; /** 附带请求头 */ headers: Record; /** * 此选项是个字符串, 使用 [Function(string)](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function) 构造方法进行解析生成方法 * * 方法传入一个参数 : 请求获取到的文本 ,可以使用 contentType 定义文本类型 * * 对返回的数据进行自定义解析 * * 并且返回一个数组 : `[题目, 答案]` * * 或者二维数据 : `[[题目1, 答案1],[题目2, 答案2], ...` * * 如果搜不到则返回 undefined * * @example * * ```js * { * handler: `return (res)=> res.code === 0 ? undefined : [res.question, undefined]` * } * ``` * */ handler: string; } ================================================ FILE: packages/core/src/core/utils/dom.ts ================================================ import { RawElements, SearchedElements } from '../worker/interface'; /** * 与 {@link domSearchAll } 相同,区别是这个只返回单个元素,而不是一个元素数组 * @param root * @param wrapper * @returns */ export function domSearch( /** 搜索构造器 */ wrapper: E, root: HTMLElement | Document = window.document ): SearchedElements { const obj = Object.create({}); Reflect.ownKeys(wrapper).forEach((key) => { const item = wrapper[key.toString()]; Reflect.set( obj, key, typeof item === 'string' ? root.querySelector(item) : typeof item === 'function' ? item(root) : item.map((fun) => fun(root)) ); }); return obj; } /** * 元素搜索 * * @example * * const { title , btn , arr } = domSearch(document.body,{ * title: '.title' * btn: ()=> '.btn', * arr: ()=> Array.from(document.body.querySelectorAll('.function-arr')) * }) * * console.log(title) // 等价于 Array.from(document.body.querySelectorAll('.title')) * console.log(btn)// 等价于 Array.from(document.body.querySelectorAll('.btn')) */ export function domSearchAll( /** 搜索构造器 */ wrapper: E, root: HTMLElement | Document = window.document ): SearchedElements { const obj = Object.create({}); Reflect.ownKeys(wrapper).forEach((key) => { const item = wrapper[key.toString()]; Reflect.set( obj, key, typeof item === 'string' ? Array.from(root.querySelectorAll(item)) : typeof item === 'function' ? item(root) : item.map((fun) => fun(root)) ); }); return obj; } ================================================ FILE: packages/core/src/core/utils/index.ts ================================================ export * from './dom'; export * from './string'; export * from './request'; ================================================ FILE: packages/core/src/core/utils/request.ts ================================================ import { $ } from '../../utils/common'; /** * 发起请求 * @param url 请求地址 * @param opts 请求参数 */ export function request( url: string, opts: { type: 'fetch' | 'GM_xmlhttpRequest'; method?: 'get' | 'post' | 'head'; responseType?: T; headers?: Record; data?: Record; } ): Promise { return new Promise((resolve, reject) => { try { /** 默认参数 */ const { responseType = 'json', method = 'get', type = 'fetch', data = {}, headers = {} } = opts || {}; /** 环境变量 */ const env = $.isInBrowser() ? 'browser' : 'node'; /** 如果是跨域模式并且是浏览器环境 */ if (type === 'GM_xmlhttpRequest' && env === 'browser') { if (typeof GM_xmlhttpRequest !== 'undefined') { const contentType = headers['Content-Type'] || headers['content-type']; const requestData = contentType === 'application/x-www-form-urlencoded' ? new URLSearchParams(data).toString() : Object.keys(data).length ? JSON.stringify(data) : undefined; // eslint-disable-next-line no-undef GM_xmlhttpRequest({ url, method: method.toUpperCase() as 'GET' | 'HEAD' | 'POST', data: requestData, headers: Object.keys(headers).length ? headers : undefined, responseType: responseType === 'json' ? 'json' : undefined, onload: (response) => { if (response.status === 200) { if (responseType === 'json') { try { resolve(JSON.parse(response.responseText)); } catch (error) { reject(error); } } else { resolve(response.responseText || ''); } } else { reject(response.responseText); } }, onerror: (err) => { console.error('GM_xmlhttpRequest error', err); reject(err); } }); } else { reject(new Error('GM_xmlhttpRequest is not defined')); } } else { const fet: typeof fetch = env === 'node' ? require('node-fetch').default : fetch; fet(url, { body: method === 'post' ? JSON.stringify(data) : undefined, method, headers }) .then((response) => { if (responseType === 'json') { response.json().then(resolve).catch(reject); } else { // @ts-ignore response.text().then(resolve).catch(reject); } }) .catch((error) => { reject(new Error(error)); }); } } catch (error) { reject(error); } }); } ================================================ FILE: packages/core/src/core/utils/string.ts ================================================ import { findBestMatch, Rating } from 'string-similarity'; /** * 删除特殊字符, 并且全部转小写,只保留英文,数字,中文 * @param str * @returns */ export function clearString(str: string, ...exclude: string[]) { exclude.push(...['①②③④⑤⑥⑦⑧⑨']); return str .trim() .toLocaleLowerCase() .replace(RegExp(`[^\\u2E80-\\u9FFFA-Za-z0-9${exclude.join('')}]*`, 'g'), ''); } /** * 答案相似度匹配 , 返回相似度对象列表 Array<{@link Rating}> * * 相似度计算算法 : https://www.npmjs.com/package/string-similarity * * @param answers 答案列表 * @param options 选项列表 * * * @example * * ```js * * answerSimilar( ['3'], ['1+2','3','4','错误的选项'] ) // [0, 1, 0, 0] * * answerSimilar( ['hello world','console.log("hello world")'], ['console.log("hello world")','hello world','1','错误的选项'] ) // [1, 1, 0, 0] * * ``` * */ export function answerSimilar(answers: string[], options: string[]): Rating[] { const _answers = answers.map(removeRedundant).map((a) => clearString(a)); const _options = options.map(removeRedundant).map((o) => clearString(o)); const similar = _answers.length !== 0 ? _options.map((option) => { if (option.trim() === '') { return { rating: 0, target: '' }; } return findBestMatch(option, _answers).bestMatch; }) : _options.map(() => ({ rating: 0, target: '' })); return similar; } /** * 精准匹配模式,返回符合的选项字符串列表 * @param answers 答案列表 * @param options 选项列表 */ export function answerExactMatch(answers: string[], options: string[]): string[] { const _answers = answers.map(removeRedundant); const _options = options.map(removeRedundant); const result = _answers.length !== 0 ? _options.filter((option) => { return _answers.find((answer) => answer.trim() === option.trim()); }) : []; return result; } /** * 删除题目选项中开头的冗余字符串 */ export function removeRedundant(str: string) { return str?.trim().replace(/[A-Z]{1}[^A-Za-z0-9\u2E80-\u9FFF]+([A-Za-z0-9\u2E80-\u9FFF]+)/, '$1') || ''; } ================================================ FILE: packages/core/src/core/worker/index.ts ================================================ export * from './interface'; export * from './utils'; export { OCSWorker, CustomOCSWorker } from './worker'; export { createDefaultQuestionResolver } from './question.resolver'; ================================================ FILE: packages/core/src/core/worker/interface.ts ================================================ import { SearchInformation } from '../answer-wrapper/interface'; export type ElementResolver = (root: HTMLElement | Document) => R; export type RawElements = Record< string | symbol, string | ElementResolver | ElementResolver[] > & { /** 题目元素选择器 */ title?: string | ElementResolver | ElementResolver[]; /** 题目选项的元素选择器 */ options?: string | ElementResolver | ElementResolver[]; }; export type SearchedElements = Record & { /** 题目元素选择器 */ title?: T extends Array ? (undefined | ArrayType)[] : T; /** 题目选项的元素选择器 */ options?: T extends Array ? (undefined | ArrayType)[] : T; }; /** 题目类型 */ export type QuestionTypes = 'single' | 'multiple' | 'completion' | 'judgement' | undefined; export type AnswerMatchMode = 'exact' | 'similar'; /** 答题器上下文 */ export interface WorkContext { root: HTMLElement; elements: SearchedElements; searchInfos: SearchInformation[]; type: QuestionTypes; /** 答案分隔符 */ answerSeparators?: string[]; /** * 答案匹配模式 * exact : 精准匹配模式, 只有答案相同才匹配 * similar : 相似匹配, 只要答案相似就匹配 */ answerMatchMode: AnswerMatchMode; } /** 答案题目处理器结果 */ export interface ResolverResult { [x: string]: any; finish: boolean; } /** 答题结果 */ export interface WorkResult { /** 查题完毕 */ requested: boolean; /** 答题完毕 */ resolved: boolean; result?: ResolverResult; error?: string; ctx?: WorkContext; } /** * 简化的答题结果 一般用于存储到本地 * * 为什么不直接用 {@link WorkResult} ,因为对象里太多嵌套对象,一旦结果超过10个以上,可能导致 I/O 变慢,并且页面卡顿。 */ export interface SimplifyWorkResult { /** 题目 */ question: string; /** 题目类型 */ type: QuestionTypes; /** 答题错误信息 */ error?: string; /** 是否完成 */ finish?: boolean; /** 正在等待 查题 线程处理 */ requested: boolean; /** 正在等待 答题 线程处理 */ resolved: boolean; /** 查题信息 */ searchInfos: { /** 题目名 */ name: SearchInformation['name']; /** 题库链接 */ homepage?: SearchInformation['homepage']; /** 题库搜索错误信息 */ error?: string; /** 搜索结果 [题目,答案,额外数据] */ results: [string, string, object][]; }[]; } /** 答案题目处理器 */ export type QuestionResolver = ( /** 查题信息 */ searchInfos: SearchInformation[], /** 选项 */ options: HTMLElement[], handler: ( type: QuestionTypes, answer: string, option: HTMLElement, ctx: WorkContext> ) => void | Promise ) => Promise; /** * 使用默认工作器 * * 需要自定义 handler */ export interface DefaultWork { /** 工作器的题目类型 */ type?: QuestionTypes | { (ctx: WorkContext): QuestionTypes }; /** * 处理器, 每个题目的处理器, 实例可看默认的 zhs `作业脚本` 写法 : https://github.com/ocsjs/ocsjs/blob/3.0/packages/scripts/src/browser/zhs/work.ts * * * @param type 题目类型 * @param answer 根据 查题器 解析出来的正确答案, * @param element 目标选项的dom对象 * * @example * * ```js { elements:{ // 必须提供 options 元素选择器,代表题目的子选项 options: '.subject .option' }, work: { // 自定义处理器例子: handler(type, answer, option, ctx) { // 如果是选择题,则获取目标按钮,并点击 if (type === "judgement" || type === "single" || type === "multiple") { if (!option.querySelector("input")?.checked) { option.click(); } } // 如果是填空题,则获取 textarea 元素并输入答案 else if (type === "completion") { const text = option.querySelector("textarea"); if (text) { text.value = answer; } } }, }, } * ``` * */ handler: ( type: QuestionTypes, answer: string, option: HTMLElement, ctx: WorkContext> ) => void; } /** * 自定义工作器 * * 如果默认工作器不满足需求,可以自定义 * * ```js * * { * elements:{ * inputs: 'input', * }, * // 简单例子 * work({ root, elements, searchResults}){ * for(const input of elements.inputs){ * if(searchResults.map(res=>res.answers.map(ans=>ans.answer)).includes(input.value)){ * input.click() * return true * } * } * return false * } * * } * * ``` * */ export type CustomWork = (ctx: WorkContext) => Promise; /** 查题器的类型 */ export type AnswererType = ( elements: SearchedElements, ctx: WorkContext> ) => SearchInformation[] | Promise; /** * 答题器参数 */ export interface WorkOptions { /** 父元素 */ root: string | HTMLElement[]; /** dom元素解析器,可以在 WorkContext.elements 中使用解析后的元素 */ elements: E; /** 查题器 */ answerer: AnswererType; /** 工作器 */ work: DefaultWork | CustomWork; /** 多线程数量(个) */ thread?: number; /** 分隔符 */ answerSeparators?: string[]; /** 答案匹配模式 */ answerMatchMode?: AnswerMatchMode; /** 当元素被搜索到 */ onElementSearched?: (elements: SearchedElements, root: HTMLElement) => void | Promise; /** 监听搜题结果 */ onAnswerSearched?: ( searchInfo: SearchInformation, currentResult: WorkResult, currentIndex: number ) => void | Promise; /** 监听答题结果 */ onResultsUpdate?: (currentResult: WorkResult, currentIndex: number, res: WorkResult[]) => void | Promise; } export interface CustomWorkOptions { period: number; questions: () => { text: string; type: QuestionTypes }[] | Promise<{ text: string; type: QuestionTypes }[]>; answerer: (question: string) => SearchInformation[] | Promise; resolver: (searchInfos: SearchInformation[]) => ResolverResult | Promise; /** 监听答题结果 */ onResultsUpdate?: ( currentResult: SimplifyWorkResult, currentIndex: number, res: SimplifyWorkResult[] ) => void | Promise; } export type WorkUploadType = 'save' | 'nomove' | 'force' | number; export type WorkerEvents = { /** 答题开始 */ start: () => void; /** 答题结果 */ done: () => void; /** 关闭答题 */ close: () => void; /** 暂停答题 */ stop: () => void; /** 继续答题 */ continuate: () => void; }; ================================================ FILE: packages/core/src/core/worker/question.resolver.ts ================================================ import { QuestionResolver, WorkContext } from './interface'; import { resolvePlainAnswer, splitAnswer } from './utils'; import { answerSimilar, removeRedundant, clearString, answerExactMatch } from '../utils/string'; import { StringUtils } from '../../utils/string'; import { Rating } from 'string-similarity'; /** 默认答案题目处理器 */ export function createDefaultQuestionResolver( ctx: WorkContext ): Record<'single' | 'multiple' | 'completion' | 'judgement', QuestionResolver> { return { /** * 单选题处理器 * * 在多个题库给出的答案中,找出最相似的答案 */ async single(infos, options, handler) { const allAnswer = infos .map((res) => res.results.map((res) => splitAnswer(res.answer, ctx.answerSeparators)).flat()) .flat(); const optionStrings = options.map((o) => removeRedundant(o.innerText)); let ratings: Rating[] = []; if (ctx.answerMatchMode === 'similar') { /** 配对选项的相似度 */ ratings = answerSimilar(allAnswer, optionStrings); /** 找出最相似的选项 */ let index = -1; let max = 0; let ans = ''; ratings.forEach((rating, i) => { if (rating.rating > max) { max = rating.rating; index = i; ans = rating.target; } }); // 存在选项,并且相似度超过 60 % if (index !== -1 && max > 0.6) { /** 经自定义的处理器进行处理 */ await handler('single', ans, options[index], ctx); return { finish: true, ratings: ratings.map((r) => r.rating) }; } } else if (ctx.answerMatchMode === 'exact') { const result = answerExactMatch(allAnswer, optionStrings); const index = optionStrings.findIndex((option) => result.includes(option)); if (result.length) { await handler('single', options[index].innerText, options[index], ctx); return { finish: true }; } } // 是否为纯ABCD答案 for (const info of infos) { for (const res of info.results) { const ans = StringUtils.nowrap(res.answer, '').trim(); if (ans.length === 1 && /[A-Z]/.test(ans)) { const index = ans.charCodeAt(0) - 65; if (options[index] === undefined) { continue; } await handler('single', options[index].innerText, options[index], ctx); return { finish: true, option: options[index] }; } } } return { finish: false, allAnswer, ratings: ratings.map((r) => r.rating), options: optionStrings }; }, /** * 多选题处理器 * * 匹配每个题库的答案,找出匹配数量最多的题库,并且选择 */ async multiple(infos, options, handler) { /** 最终的回答列表 */ const targetAnswers: string[][] = []; /** 最终的选项 */ const targetOptions: HTMLElement[][] = []; type SimilarResult = { /** 匹配的选项 */ options: HTMLElement[]; /** 匹配的答案 */ answers: string[]; ratings: number[]; /** 总匹配度 */ similarSum: number; /** 匹配数量 */ similarCount: number; }; const similar_list: SimilarResult[] = []; const exact_list: HTMLElement[][] = []; const results = infos.map((info) => info.results).flat(); /** * 遍历题库结果 * 选出结果中包含答案最多的一个 */ for (let i = 0; i < results.length; i++) { const result = results[i]; // 每个答案可能存在多个选项需要分割 const answers = splitAnswer(result.answer.trim(), ctx.answerSeparators); if (ctx.answerMatchMode === 'similar') { const matchResult: SimilarResult = { options: [], answers: [], ratings: [], similarSum: 0, similarCount: 0 }; // 判断选项是否完全存在于答案里面 for (const option of options) { const ans = answers.find((answer) => answer.includes(removeRedundant(option.textContent || option.innerText)) ); if (ans) { matchResult.options.push(option); matchResult.answers.push(ans); matchResult.ratings.push(1); matchResult.similarSum += 1; matchResult.similarCount += 1; } } const ratingResult: SimilarResult = { options: [], answers: [], ratings: [], similarSum: 0, similarCount: 0 }; // 相似度匹配 const ratings = answerSimilar( answers, options.map((o) => removeRedundant(o.innerText)) ); for (let j = 0; j < ratings.length; j++) { const rating = ratings[j]; if (rating.rating > 0.6) { ratingResult.options.push(options[j]); ratingResult.answers.push(ratings[j].target); ratingResult.ratings.push(ratings[j].rating); ratingResult.similarSum += rating.rating; ratingResult.similarCount += 1; } } // 如果全匹配大于 相似度匹配 if (matchResult.similarSum > ratingResult.similarSum) { similar_list[i] = matchResult; } else { similar_list[i] = ratingResult; } } else if (ctx.answerMatchMode === 'exact') { exact_list[i] = answerExactMatch( answers, options.map((o) => removeRedundant(o.innerText)) ) .map((option) => options.find((o) => removeRedundant(o.innerText) === option)) .filter(Boolean) as HTMLElement[]; } } if (ctx.answerMatchMode === 'similar') { const sorted_similar_list = similar_list .filter((i) => i.similarCount !== 0) .sort((a, b) => { const bsc = b.similarCount * 100; const asc = a.similarCount * 100; const bss = b.similarSum; const ass = a.similarSum; // similarCount 由于是匹配的数量,其结果决定排序, // similarSum 是匹配精度,其结果决定同样数量的情况下,哪一个的精度更高 // 高到低排序 return bsc + bss - asc + ass; }); if (sorted_similar_list[0]) { for (let i = 0; i < sorted_similar_list[0].options.length; i++) { await handler('multiple', sorted_similar_list[0].answers[i], sorted_similar_list[0].options[i], ctx); } return { finish: true, sorted_similar_list, targetOptions, targetAnswers }; } } else if (ctx.answerMatchMode === 'exact') { const sorted_exact_list = exact_list.sort((a, b) => b.length - a.length); if (sorted_exact_list[0]?.length) { for (let i = 0; i < sorted_exact_list[0].length; i++) { await handler('multiple', sorted_exact_list[0][i].innerText, sorted_exact_list[0][i], ctx); } return { finish: true, sorted_exact_list: sorted_exact_list.map((i) => i.map((e) => e.innerText)), targetOptions, targetAnswers }; } } // 如果都没找到答案 const plainOptions = []; // 纯ABCD答案 for (const result of results) { const ans = StringUtils.nowrap(result.answer, '').trim(); const plainAnswer = resolvePlainAnswer(ans); if (plainAnswer) { for (const char of ans) { const index = char.charCodeAt(0) - 65; if (options[index] === undefined) { continue; } await handler('multiple', options[index].innerText, options[index], ctx); plainOptions.push(options[index]); } } } if (plainOptions.length) { return { finish: true, plainOptions }; } else { return { finish: false }; } }, /** 判断题处理器 */ async judgement(infos, options, handler) { for (const answers of infos.map((info) => info.results.map((res) => res.answer))) { const correctWords = [ '是', '对', '正确', '确定', '√', '对的', '是的', '正确的', 'true', 'True', 'T', 'yes', '1' ]; const incorrectWords = [ '非', '否', '错', '错误', '×', 'X', '错的', '不对', '不正确的', '不正确', '不是', '不是的', 'false', 'False', 'F', 'no', '0' ]; /** 答案显示正确 */ const answerShowCorrect = answers.find((answer) => matches(answer, correctWords)); /** 答案显示错误 */ const answerShowIncorrect = answers.find((answer) => matches(answer, incorrectWords)); if (answerShowCorrect || answerShowIncorrect) { let option: HTMLElement | undefined; for (const el of options) { /** 选项显示正确 */ const textShowCorrect = matches(el.innerText, correctWords); /** 选项显示错误 */ const textShowIncorrect = matches(el.innerText, incorrectWords); if (answerShowCorrect && textShowCorrect) { option = el; await handler('judgement', answerShowCorrect, el, ctx); break; } if (answerShowIncorrect && textShowIncorrect) { option = el; await handler('judgement', answerShowIncorrect, el, ctx); break; } } return { finish: true, option }; } function matches(target: string, options: string[]) { return options.some( (option) => clearString(removeRedundant(option), '√', '×') === clearString(removeRedundant(target), '√', '×') ); } } return { finish: false }; }, /** 填空题处理器 */ async completion(infos, options, handler) { for (const answers of infos.map((info) => info.results.map((res) => res.answer))) { // 排除空答案 let ans = answers.filter((ans) => ans); if (ans.length === 1) { ans = splitAnswer(ans[0], ctx.answerSeparators); } if ( ans.length !== 0 && /** 答案数量要和文本框数量一致,或者文本框只有一个 */ (ans.length === options.length || options.length === 1) ) { if (ans.length === options.length) { for (let index = 0; index < options.length; index++) { const element = options[index]; await handler('completion', ans[index], element, ctx); } return { finish: true }; } else if (options.length === 1) { await handler('completion', ans.join(' '), options[0], ctx); return { finish: true }; } return { finish: false }; } } return { finish: false }; } }; } ================================================ FILE: packages/core/src/core/worker/utils.ts ================================================ import { QuestionTypes, WorkContext } from './interface'; /** 默认题目类型解析器 */ export function defaultWorkTypeResolver(ctx: WorkContext): QuestionTypes | undefined { function count(selector: string) { let c = 0; for (const option of ctx.elements.options || []) { if (option?.querySelector(selector) !== null) { c++; } } return c; } return count('[type="radio"]') === 2 ? 'judgement' : count('[type="radio"]') > 2 ? 'single' : count('[type="checkbox"]') > 2 ? 'multiple' : count('textarea') >= 1 ? 'completion' : undefined; } /** 判断答案是否为A-Z的文本, 并且字符序号依次递增, 并且 每个字符是否都只出现了一次 */ export function isPlainAnswer(answer: string) { answer = answer.trim(); if (answer.length > 8 || !/[A-Z]/.test(answer)) { return false; } const counter: any = {}; let min = 0; for (let i = 0; i < answer.length; i++) { if (answer.charCodeAt(i) < min) { return false; } min = answer.charCodeAt(i); counter[min] = (counter[min] || 0) + 1; } // 判断每个字符是否都只出现了一次 for (const key in counter) { if (counter[key] !== 1) { return false; } } return true; } /** * 判断是否为纯ABCD多选答案,但是中间存在分隔符 * @param answer 答案 */ export function resolvePlainAnswer(answer: string) { const resolve = answer .trim() .replace(/[,,、 #]/g, '') .trim(); if (isPlainAnswer(resolve)) { return resolve; } } /** 分割答案 */ export function splitAnswer(answer: string, separators = ['===', '#', '---', '###', '|', ';', ';']) { answer = answer.trim(); if (answer.length === 0) { return []; } separators = separators.length === 0 ? ['===', '#', '---', '###', '|', ';', ';'] : separators; separators = separators.filter((el) => el.trim().length > 0); try { // 如果是 json 格式的多选答案 const json = JSON.parse(answer); if (Array.isArray(json)) { return json.map(String).filter((el) => el.trim().length > 0); } } catch { for (const sep of separators) { if (answer.split(sep).length > 1) { return answer.split(sep).filter((el) => el.trim().length > 0); } } } return [answer]; } ================================================ FILE: packages/core/src/core/worker/worker.ts ================================================ import { $, CommonEventEmitter } from 'easy-us'; import { domSearchAll } from '../utils/dom'; import { CustomWorkOptions, RawElements, ResolverResult, SimplifyWorkResult, WorkContext, WorkerEvents, WorkOptions, WorkResult, WorkUploadType } from './interface'; import { createDefaultQuestionResolver } from './question.resolver'; import { defaultWorkTypeResolver } from './utils'; import { AnswerWrapperHandlerConfig } from '../answer-wrapper'; /** * 自动答题器, 传入一些指定的配置, 就可以进行自动答题。 * * @param work 工作器, 传入一个方法可自定义工作器,或者使用默认的工作器,详情: {@link WorkOptions.work} * @param answerer 查题器, : 默认是 {@link defaultAnswerWrapperHandler} * */ export class OCSWorker extends CommonEventEmitter { opts: WorkOptions; isRunning = false; isClose = false; isStop = false; totalQuestionCount = 0; constructor(opts: WorkOptions) { super(); this.opts = opts; } /** 启动答题器 */ async doWork(options?: { enable_debug?: boolean }) { this.emit('start'); this.isRunning = true; this.once('close', () => { this.isClose = true; }); this.on('stop', () => { this.isStop = true; }); this.on('continuate', () => { this.isStop = false; }); /** 寻找题目父节点 */ const questionRoots: HTMLElement[] | null = typeof this.opts.root === 'string' ? Array.from(document.querySelectorAll(this.opts.root)) : this.opts.root; this.totalQuestionCount += questionRoots.length; if (options?.enable_debug) { console.debug('开始答题', this); console.debug('题目数量: ', questionRoots.length); console.debug('父节点列表: ', questionRoots); } /** 答题结果 */ const results: WorkResult[] = []; if (questionRoots.length === 0) { throw new Error('未找到任何题目,答题结束。'); } /** 搜索元素 */ for (const questionRoot of questionRoots) { // 初始化上下文 const ctx: WorkContext = { searchInfos: [], root: questionRoot, elements: domSearchAll(this.opts.elements, questionRoot), type: undefined, answerSeparators: this.opts.answerSeparators, answerMatchMode: this.opts.answerMatchMode || 'similar' }; /** 执行元素搜索钩子 */ await this.opts.onElementSearched?.(ctx.elements, questionRoot); /** 排除掉 null 的元素 */ ctx.elements.title = ctx.elements.title?.filter(Boolean) as HTMLElement[]; ctx.elements.options = ctx.elements.options?.filter(Boolean) as HTMLElement[]; /** 获取题目类型 */ if (typeof this.opts.work === 'object') { ctx.type = this.opts.work.type === undefined ? // 使用默认解析器 defaultWorkTypeResolver(ctx) : // 自定义解析器 typeof this.opts.work.type === 'string' ? this.opts.work.type : this.opts.work.type(ctx); } results.push({ requested: false, resolved: false, ctx: ctx }); } if (options?.enable_debug) { console.debug('上下文已初始化: ', results); } /** 请求答案的线程 */ const requestThread = async (index: number) => { let error: string | undefined; const result = results[index]; const ctx = result.ctx || ({} as WorkContext); /** 强行关闭 */ if (this.isClose === true) { this.isRunning = false; return; } /** 检查是否暂停中 */ if (this.isStop) { await waitForContinuate(() => this.isStop); } /** 查找答案 */ ctx.searchInfos = []; if (options?.enable_debug) { console.groupEnd(); console.group( '开始搜题: ', ctx.elements.title ?.map((t) => t?.innerText) .filter(Boolean) .join(', ') .slice(0, 20) ); console.log('ctx', result.ctx); } try { ctx.searchInfos = (await this.opts.answerer(ctx.elements, ctx)) || []; // 答案为 undefined 的情况, 需要赋值给一个空字符串,因为可能传回的题目中带有其他提示信息,或者题目里包含答案。 ctx.searchInfos.forEach((info) => { info.results = info.results.map((ans) => { ans.answer = ans.answer ? ans.answer.trim() : ''; return ans; }); }); } catch (err) { error = String(err); } result.ctx = ctx; result.requested = true; result.error = error; if (options?.enable_debug) { console.log('搜题结果: ', ctx.searchInfos); } /** 回调 */ await this.opts.onResultsUpdate?.(results[index], index, results); }; const waitForRequested = async (result: WorkResult) => { return new Promise((resolve, reject) => { const interval = setInterval(() => { if (result?.requested === true) { clearInterval(interval); clearTimeout(timeout); resolve(); } }, 200); const timeout = setTimeout(() => { clearInterval(interval); reject(new Error('答题超时!')); }, (AnswerWrapperHandlerConfig.timeout_seconds + 10) * 1000); }); }; /** 答题线程, */ const resolverThread = async () => { for (let index = 0; index < results.length; index++) { const result = results[index]; let error: string | undefined; let res: ResolverResult | undefined; /** 强行关闭 */ if (this.isClose === true) { this.isRunning = false; return; } try { /** 检查是否暂停中 */ if (this.isStop) { await waitForContinuate(() => this.isStop); } /** 等待搜题完毕 */ await waitForRequested(result); } catch (err) { // 超时错误 } try { if (result.ctx && result.ctx.searchInfos.length !== 0) { /** 开始处理 */ if (typeof this.opts.work === 'object') { if (result.ctx.elements.options) { /** 使用默认处理器 */ if (result.ctx.type) { const resolver = createDefaultQuestionResolver(result.ctx)[result.ctx.type]; const handler = this.opts.work.handler; res = await resolver(result.ctx.searchInfos, result.ctx.elements.options as HTMLElement[], handler); } else { error = '题目类型解析失败, 请自行提供解析器, 或者忽略此题。'; } } else { error = 'elements.options 为空 ! 使用默认处理器, 必须提供题目选项的选择器。'; } } else { /** 使用自定义处理器 */ const work = this.opts.work; res = await work(result.ctx); } } else { error = '搜索不到答案, 请重新运行, 或者忽略此题。'; } } catch (err) { error = (err as any)?.message || err; } result.error = error; /** 修改答题结果 */ result.result = res || { finish: false }; /** 设置答题完成 */ result.resolved = true; if (options?.enable_debug) { console.log( '答题完成: ', result.ctx?.elements.title ?.map((t) => t?.innerText) .join(', ') .slice(0, 20), result ); } /** 回调 */ await this.opts.onResultsUpdate?.(result, index, results); } }; /** * 搜题和答题分为两个线程 */ /** 多线程搜题 */ const requestThreadHandler = async () => { /** 线程锁 */ const locks: number[] = []; const waitForLock = () => { return new Promise((resolve, reject) => { const interval = setInterval(() => { if (locks.length > 0) { const lock = locks.shift(); if (lock) { resolve(lock); clearInterval(interval); clearTimeout(timeout); } } }, 100); const timeout = setTimeout(() => { clearInterval(interval); reject(new Error('获取线程锁超时!')); }, 3 * 60 * 1000); }); }; const requestThreads: Function[] = []; for (let index = 0; index < results.length; index++) { requestThreads.push(() => requestThread(index)); } for (let index = 0; index < (this.opts.thread || 1); index++) { locks.push(index + 1); } let requestFinished = 0; const promises: Function[] = []; for (let index = 0; index < (this.opts.thread || 1); index++) { promises.push(async () => { try { while (requestFinished < results.length && requestThreads.length > 0 && this.isClose === false) { const thread = requestThreads.shift(); if (thread) { const lock = await waitForLock(); await thread(); requestFinished++; locks.push(lock); } } } catch (err) { console.error(err); } }); } await Promise.all(promises.map((f) => f())); }; /** 答题线程 */ await Promise.all([resolverThread(), requestThreadHandler()]); this.isRunning = false; return results; } /** 答题结果处理器 */ uploadHandler(options: { // doWork 的返回值结果 results: WorkResult[]; // 提交类型 type: WorkUploadType; /** * 是否上传处理器 * * @param uploadable 是否可以上传 * @param finishedRate 完成率 */ callback: (finishedRate: number, uploadable: boolean) => void | Promise; }) { const { results, type, callback } = options; if (type !== 'nomove') { let finished = 0; for (const result of results) { if (result.result?.finish) { finished++; } } const rate = results.length === 0 ? 0 : (finished / results.length) * 100; if (type === 'force') { return callback(rate, true); } else { return callback(rate, type === 'save' ? false : rate >= parseFloat(type.toString())); } } } } export class CustomOCSWorker extends CommonEventEmitter { opts: CustomWorkOptions; isRunning = false; isClose = false; isStop = false; constructor(opts: CustomWorkOptions) { super(); this.opts = opts; } /** 启动答题器 */ async doWork(options?: { enable_debug?: boolean }) { this.emit('start'); this.isRunning = true; this.once('close', () => { this.isClose = true; }); this.on('stop', () => { this.isStop = true; }); this.on('continuate', () => { this.isStop = false; }); const questions = await this.opts.questions?.(); if (options?.enable_debug) { console.debug('开始答题', this); console.debug('题目数量: ', this.opts.questions.length); } const results: SimplifyWorkResult[] = []; for (let index = 0; index < questions.length; index++) { /** 强行关闭 */ if (this.isClose === true) { this.isRunning = false; return; } /** 检查是否暂停中 */ if (this.isStop) { await waitForContinuate(() => this.isStop); } const question = questions[index]; results[index] = { question: question.text, requested: false, resolved: false, searchInfos: [], type: question.type, finish: false, error: '' }; try { const infos = await this.opts.answerer(question.text); results[index].searchInfos = infos.map((i) => ({ name: i.name, homepage: i.homepage, results: i.results.map((r) => [r.question, r.answer, r.extra_data || {}]), error: i.error })); results[index].requested = true; this.opts.onResultsUpdate?.(results[index], index, results); try { const resolved = await this.opts.resolver(infos); results[index].finish = resolved.finish; results[index].error = resolved.error; results[index].resolved = true; } catch (err) { results[index].finish = false; results[index].error = err instanceof Error ? err.message : String(err); results[index].resolved = true; } this.opts.onResultsUpdate?.(results[index], index, results); } catch (err) { results[index].requested = true; results[index].resolved = false; results[index].finish = true; results[index].error = err instanceof Error ? err.message : String(err); this.opts.onResultsUpdate?.(results[index], index, results); } await $.sleep(this.opts.period); } } } async function waitForContinuate(isStopping: () => boolean) { if (isStopping()) { await new Promise((resolve, reject) => { const interval = setInterval(() => { if (isStopping() === false) { clearInterval(interval); resolve(); } }, 200); }); } } ================================================ FILE: packages/core/src/index.ts ================================================ export * from './utils'; export * from './core/utils'; export * from './core/worker'; export * from './core/answer-wrapper'; ================================================ FILE: packages/core/src/utils/common.ts ================================================ import debounce from 'lodash/debounce'; /** * 公共的工具库 */ export const $ = { /** 创建唯一id , 不带横杠 */ uuid() { return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (c) { const r = (Math.random() * 16) | 0; const v = c === 'x' ? r : (r & 0x3) | 0x8; return v.toString(16); }); }, /** * 生成随机数, 使用 Math.round 取整 * @param min 最小值 * @param max 最大值 */ random(min: number, max: number) { return Math.round(Math.random() * (max - min)) + min; }, /** * 暂停 * @param period 毫秒 */ async sleep(period: number): Promise { return new Promise((resolve) => { setTimeout(resolve, period); }); }, /** * 当前是否处于浏览器环境 */ isInBrowser(): boolean { return typeof window !== 'undefined' && typeof window.document !== 'undefined'; }, /** * 使元素变成纯文本对象,(跨域时对象上下文会被销毁) * @param el 元素 */ elementToRawObject(el: HTMLElement | undefined | null) { return { innerText: el?.innerText, innerHTML: el?.innerHTML, textContent: el?.textContent } as any; }, /** * 监听页面宽度变化 * @param el 任意元素,如果此元素被移除,则不执行 resize 回调 * @param handler resize 回调 */ onresize(el: E, handler: (el: E) => void) { const resize = debounce(() => { /** * 如果元素被删除,则移除监听器 * 不使用 el.parentElement 是因为如果是顶级元素,例如 shadowRoot 中的一级子元素, el.parentNode 不为空,但是 el.parentElement 为空 */ if (el.parentNode === null) { window.removeEventListener('resize', resize); } else { handler(el); } }, 200); resize(); window.addEventListener('resize', resize); }, /** 是否处于顶级 window ,而不是子 iframe */ isInTopWindow() { return self === top; }, /** * 创建弹出窗口 * @param url 地址 * @param winName 窗口名 * @param w 宽 * @param h 高 * @param scroll 滚动条 */ createCenteredPopupWindow( url: string, winName: string, opts: { width: number; height: number; scrollbars: boolean; resizable: boolean; } ) { const { width, height, scrollbars, resizable } = opts; const LeftPosition = screen.width ? (screen.width - width) / 2 : 0; const TopPosition = screen.height ? (screen.height - height) / 2 : 0; const settings = 'height=' + height + ',width=' + width + ',top=' + TopPosition + ',left=' + LeftPosition + ',scrollbars=' + (scrollbars ? 'yes' : 'no') + ',resizable=' + (resizable ? 'yes' : 'no'); return window.open(url, winName, settings); }, transition: async ( el: HTMLElement, properties: keyof CSSStyleDeclaration, duration_ms: number, val: any, options?: { reset_ms?: number; timing_function?: string; } ) => { return new Promise((resolve) => { const original_val = Reflect.get(el.style, properties) || ''; el.style.transition = `${String(properties)} ${duration_ms}s ${options?.timing_function || 'ease-in-out'}`; Reflect.set(el.style, properties, val); el.addEventListener('transitionend', function handler() { el.removeEventListener('transitionend', handler); setTimeout(() => { Reflect.set(el.style, properties, original_val); setTimeout(() => { el.style.transition = ''; }, duration_ms * 1000); }, (options?.reset_ms || 0) * 1000); resolve(); }); }); } }; ================================================ FILE: packages/core/src/utils/const.ts ================================================ export const $const = { TAB_UID: '_uid_', TAB_URLS: '_urls_', TAB_CURRENT_PANEL_NAME: '_current_panel_name_' }; ================================================ FILE: packages/core/src/utils/index.ts ================================================ export * from './common'; export * from './string'; export * from './const'; export * from './playwright'; ================================================ FILE: packages/core/src/utils/playwright.ts ================================================ import type { Page } from 'playwright-core'; import { request } from '../core/utils'; import { $ } from './common'; import { $elements, $message } from 'easy-us'; export type Base64 = string; interface ClickOptions { /** * Defaults to `left`. */ button?: 'left' | 'right' | 'middle'; /** * defaults to 1. See [UIEvent.detail]. */ clickCount?: number; /** * Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0. */ delay?: number; /** * Whether to bypass the [actionability](https://playwright.dev/docs/actionability) checks. Defaults to `false`. */ force?: boolean; /** * Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores * current modifiers back. If not specified, currently pressed modifiers are used. */ modifiers?: Array<'Alt' | 'Control' | 'Meta' | 'Shift'>; /** * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You * can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as * navigating to inaccessible pages. Defaults to `false`. */ noWaitAfter?: boolean; /** * A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of * the element. */ position?: { x: number; y: number; }; /** * When true, the call requires selector to resolve to a single element. If given selector resolves to more than one * element, the call throws an exception. */ strict?: boolean; /** * Maximum time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `actionTimeout` * option in the config, or by using the * [browserContext.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-default-timeout) * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#page-set-default-timeout) methods. */ timeout?: number; /** * When set, this method only performs the [actionability](https://playwright.dev/docs/actionability) checks and skips the action. Defaults * to `false`. Useful to wait until the element is ready for the action without performing it. */ trial?: boolean; } export interface RemotePage { click: (selectorOrElement: string | Element, options?: ClickOptions) => Promise; check: Page['check']; dblclick: Page['dblclick']; bringToFront: Page['bringToFront']; dragAndDrop: Page['dragAndDrop']; fill: Page['fill']; focus: Page['focus']; hover: Page['hover']; screenshot: Page['screenshot']; selectOption: Page['selectOption']; setInputFiles: Page['setInputFiles']; tap: Page['tap']; press: Page['press']; reload: (...args: Parameters) => Promise; waitForRequest(...args: Parameters): Promise<{ url: string; method: string; headers: Record; postData: string; }>; waitForResponse(...args: Parameters): Promise<{ body: string; headers: Record; status: number; url: string; }>; waitForSelector(...args: Parameters): Promise; ['keyboard.type']: Page['keyboard']['type']; ['keyboard.press']: Page['keyboard']['press']; ['mouse.wheel']: Page['mouse']['wheel']; ['mouse.click']: Page['mouse']['click']; ['mouse.dblclick']: Page['mouse']['dblclick']; ['mouse.down']: Page['mouse']['down']; ['mouse.up']: Page['mouse']['up']; ['mouse.move']: Page['mouse']['move']; } const ListOfActions = [ 'click', 'check', 'dblclick', 'bringToFront', 'dragAndDrop', 'fill', 'focus', 'hover', 'screenshot', 'selectOption', 'setInputFiles', 'tap', 'press', 'reload', 'waitForRequest', 'waitForResponse', 'waitForSelector', 'keyboard.type', 'keyboard.press', 'mouse.wheel', 'mouse.click', 'mouse.dblclick', 'mouse.down', 'mouse.up', 'mouse.move' ]; const mouseIdleRequireActions = [ 'click', 'dblclick', 'dragAndDrop', 'fill', 'hover', 'tap', 'press', 'keyboard.type', 'keyboard.press', 'mouse.wheel', 'mouse.click', 'mouse.dblclick', 'mouse.down', 'mouse.up', 'mouse.move' ]; export class RemotePlaywright { private static authToken = ''; private static currentPage: RemotePage | undefined = undefined; static async getRemotePage( show_debug_cursor?: boolean, logger?: (...args: any[]) => void ): Promise { if (this.currentPage) { return this.currentPage; } /** * OCS桌面端后端无法拦截 GM_xmlhttpRequest ,所以这里使用 fetch 请求动作执行,然后后端根据key判断是否允许执行 */ if (!this.authToken) { try { this.authToken = await request('http://localhost:15319/get-actions-key', { type: 'GM_xmlhttpRequest', method: 'get', responseType: 'text' }); this.currentPage = this.createRemotePage(this.authToken, { show_debug_cursor, logger }); return this.currentPage; } catch (e) { console.log(e); return undefined; } } else { this.currentPage = this.createRemotePage(this.authToken, { show_debug_cursor, logger }); return this.currentPage; } } private static createRemotePage( authToken: string, configs?: { show_debug_cursor?: boolean; logger?: (...args: any[]) => void } ) { const page = Object.create({}); configs = configs || {}; configs.logger = configs.logger || console.debug; for (const property of ListOfActions) { Reflect.set(page, property, async (...args: any[]) => { let data; if (mouseIdleRequireActions.includes(property)) { // 等待鼠标静止/空闲 await waitForMouseIdle(); } if (property === 'click') { if (args[0] instanceof Element) { const el = args[0] as HTMLElement; const options = (args[1] || {}) as ClickOptions; await scrollToElement(el); // 如果是传入的元素对象,那么就解析元素的坐标进行点击 // 这里滑动的时间可能会比较长,取决于页面的长度,所以这里多等待一点时间 await $.sleep(500); const rect = el.getBoundingClientRect(); // 移动可能阻挡点击的脚本面板 const elFromPoint = $elements.root?.elementFromPoint( rect.left + rect.width / 2, rect.top + rect.height / 2 ); if (elFromPoint && $elements.root && $elements.root.contains(elFromPoint)) { // 如果元素在根节点内,则隐藏面板 const panel = $elements.root.querySelector('container-element'); if (panel) { $message.info({ content: '检测到脚本阻挡点击位置,已自动移开', duration: 2 }); await $.transition(panel, 'left', 0.1, rect.left + rect.width / 2 + 100 + 'px', { reset_ms: 1 }); } } // 显示鼠标位置 if (configs?.show_debug_cursor) { showMousePointer(el); } data = { page: window.location.href, property: 'mouse.click', args: [ rect.left + rect.width / 2, rect.top + rect.height / 2, { button: options.button, clickCount: options.clickCount, delay: options.delay } ] }; } else if (typeof args[0] === 'string') { const el = document.querySelector(args[0]) as HTMLElement; if (el) { await scrollToElement(el); // 显示鼠标位置 if (configs?.show_debug_cursor) { showMousePointer(el); } } } } if (!data) { data = { page: window.location.href, property: property, args: args }; } configs?.logger?.('[RP]: ', JSON.stringify(data)); try { // 这里为什么不写前缀 http://localhost:15319,因为有 Content-Security-Policy , 这里我们借用后台的URL代理去进行处理,只要包含 ocs-script-actions 即可轻松绕过 Content-Security-Policy 限制 const res = await request('/ocs-script-actions', { type: 'fetch', method: 'post', responseType: ['waitForRequest', 'waitForResponse', 'reload'].includes(property) ? 'json' : 'text', headers: { 'auth-token': authToken }, data: data }); return res; } catch (e) { configs?.logger?.('[RP-ERROR]: ', JSON.stringify(data)); return undefined; } }); } console.log(page); return page; } } function scrollToElement(el: HTMLElement) { el.scrollIntoView({ behavior: 'smooth', block: 'center' }); // 等待动作完成 return $.sleep(200); } function showMousePointer(el: HTMLElement) { setTimeout(() => { const rect = el.getBoundingClientRect(); // 显示鼠标位置 const div = document.createElement('div'); div.textContent = ''; div.style.position = 'fixed'; div.style.zIndex = '99999'; div.style.width = '20px'; div.style.height = '20px'; div.style.border = '2px solid red'; div.style.borderRadius = '50%'; div.style.left = rect.left + rect.width / 2 - 11 + 'px'; div.style.top = rect.top + rect.height / 2 - 11 + 'px'; document.body.append(div); setTimeout(() => { div.remove(); }, 500); }, 100); } function waitForMouseIdle(timeout: number = 200): Promise { return new Promise((resolve) => { let timer: any; const default_timer = setTimeout(() => { window.removeEventListener('mousemove', onMouseMove); resolve(); }, timeout); // 最多等timeout + 1000ms function onMouseMove() { clearTimeout(default_timer); if (timer) { clearTimeout(timer); } timer = setTimeout(() => { window.removeEventListener('mousemove', onMouseMove); resolve(); }, timeout); } window.addEventListener('mousemove', onMouseMove); }); } ================================================ FILE: packages/core/src/utils/string.ts ================================================ /** * 字符串工具库 */ export const $string = { /** * 驼峰转目标字符串 * @param value */ humpToTarget(value: string, target: string) { return value .replace(/([A-Z])/g, target + '$1') .toLowerCase() .split(target) .slice(1) .join(target); } }; export class StringUtils { _text: string; constructor(_text: string) { this._text = _text; } /** * 删除换行符 * @param replace_str 替代的字符串 */ static nowrap(str: string, replace_str: string) { return str?.replace(/\n/g, replace_str) || ''; } /** * 删除换行符 * @param replace_str 替代的字符串 */ nowrap(replace_str: string) { this._text = StringUtils.nowrap(this._text, replace_str); return this; } /** 删除空格,多个空格只留一个 */ static nospace(str?: string) { return str?.replace(/ +/g, ' ') || ''; } /** 删除空格,多个空格只留一个 */ nospace() { this._text = StringUtils.nospace(this._text); return this; } /** 删除特殊字符 */ static noSpecialChar(str?: string) { return str?.replace(/[^\w\s]/gi, '') || ''; } /** 删除特殊字符 */ noSpecialChar() { this._text = StringUtils.noSpecialChar(this._text); return this; } /** 最大长度,剩余显示省略号 */ static max(str: string, len: number) { return str.length > len ? str.substring(0, len) + '...' : str; } /** 最大长度,剩余显示省略号 */ max(len: number) { this._text = StringUtils.max(this._text, len); return this; } /** 隐藏字符串 */ static hide(str: string, start: number, end: number, replacer: string = '*') { // 从 start 到 end 中间的字符串全部替换成 replacer return str.substring(0, start) + str.substring(start, end).replace(/./g, replacer) + str.substring(end); } /** 隐藏字符串 */ hide(start: number, end: number, replacer: string = '*') { this._text = StringUtils.hide(this._text, start, end, replacer); return this; } /** * 根据字符串创建 StringUtils 对象 * @param text 字符串 */ static of(text: string) { return new StringUtils(text); } toString() { return this._text; } } ================================================ FILE: packages/core/tsconfig.json ================================================ { "compilerOptions": { "target": "es6", "module": "commonjs", "outDir": "./lib", "declaration": true, "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "lib": ["DOM", "ES2020"], "jsx": "preserve", "resolveJsonModule": true, "downlevelIteration": true }, "include": ["./src/**/*.ts"] } ================================================ FILE: packages/core/typedoc.json ================================================ { // Comments are supported, like tsconfig.json "$schema": "https://typedoc.org/schema.json", "entryPoints": ["./src/elements/index.ts", "./src/utils/index.ts"], "out": "docs", "cleanOutputDir": true, "darkHighlightTheme": "github-dark", "lightHighlightTheme": "github-light", "name": "OCS - API", "readme": "../../README.md", "customCss": "./assets/docs.css", "kindSortOrder": ["Variable", "Property", "Method", "Function"] } ================================================ FILE: packages/core/vite.config.ts ================================================ import { visualizer } from 'rollup-plugin-visualizer'; import { defineConfig } from 'vite'; import banner from 'vite-plugin-banner'; import { author, description, homepage, license, name } from '../../package.json'; import dotenv from 'dotenv'; const bannerContent = ` /*! * ${name} ( ${homepage} ) * ${description} * copyright ${author} * license ${license} */ `; dotenv.config(); // https://vitejs.dev/config/ export default defineConfig({ build: { /** 取消css代码分离 */ cssCodeSplit: false, /** 输出路径 */ outDir: process.env.VITE_BUILD_PATH, /** 清空输出路径 */ emptyOutDir: false, /** 是否压缩代码 */ minify: false, /** 打包库, 全局名字为 OCS */ lib: { entry: './src/index.ts', name: 'OCS', fileName: () => 'core.js', formats: ['umd'] } }, plugins: [visualizer(), banner(bannerContent)] }); ================================================ FILE: packages/scripts/.eslintrc.json ================================================ { "extends": ["../../.eslintrc.json"], "globals": { "core": "readonly", "scripts": "readonly" } } ================================================ FILE: packages/scripts/.gitignore ================================================ # customize test/ stats.html src/assets/css # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* .pnpm-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage *.lcov # nyc test coverage .nyc_output # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # Snowpack dependency directory (https://snowpack.dev/) web_modules/ # TypeScript cache *.tsbuildinfo # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional stylelint cache .stylelintcache # Microbundle cache .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variable files .env .env.development.local .env.test.local .env.production.local .env.local # parcel-bundler cache (https://parceljs.org/) .cache .parcel-cache # Next.js build output .next out # Nuxt.js build / generate output .nuxt dist # Gatsby files .cache/ # Comment in the public line in if your project uses Gatsby and not Next.js # https://nextjs.org/blog/next-9-1#public-directory-support # public # vuepress build output .vuepress/dist # vuepress v2.x temp and cache directory .temp .cache # Docusaurus cache and generated files .docusaurus # Serverless directories .serverless/ # FuseBox cache .fusebox/ # DynamoDB Local files .dynamodb/ # TernJS port file .tern-port # Stores VSCode versions used for testing VSCode extensions .vscode-test # yarn v2 .yarn/cache .yarn/unplugged .yarn/build-state.yml .yarn/install-state.gz .pnp.* ================================================ FILE: packages/scripts/assets/css/style.css ================================================ /** 默认字体 */ /** 输入框默认边距 */ ul, ol { line-height: 26px; padding-left: 22px; margin: 0px; } a { color: #1890ff; } hr { border-style: solid; border-color: #63636346; border-width: 0px; border-bottom: 1px solid #63636346; margin-block-start: 1em; margin-block-end: 1em; } .base-style-active-form-control { border: 1px solid #ffffff00; } .base-style-active-form-control:focus { border: 1px solid #0e8de290; box-shadow: 0px 0px 4px #0e8de252; } .base-style-active-form-control:focus:not([type='checkbox'], [type='radio']) { border: 1px solid #0e8de290; box-shadow: 0px 0px 4px #0e8de252; background-color: white !important; } .base-style-active-form-control:hover { background-color: #ebeef4; } .base-style-input { outline: none; border: 1px solid #ffffff00; padding: 2px 8px; margin: 0px; background-color: #eef2f7; border-radius: 2px; color: black; } .base-style-input::placeholder { color: #bababa; } .base-style-switch { appearance: none; -moz-appearance: none; -webkit-appearance: none; width: fit-content; min-width: 36px; height: 20px; border-radius: 100px; display: flex; align-items: center; padding: 2px 4px; transition: all 0.2s ease-in-out; width: auto; background: gainsboro; } .base-style-switch:checked { background: #1890ff; } .base-style-switch:disabled { background-color: #f7f7f78b; } .base-style-switch:checked::before { transform: translate(100%, 0px); } .base-style-switch::before { background-color: #fff; border-radius: 9px; box-shadow: 0 2px 4px #00230b33; width: 14px; height: 14px; content: ''; } .base-style-button { appearance: none; -moz-appearance: none; -webkit-appearance: none; border-radius: 4px; background-color: white; border: 1px solid #2c92ff; color: #409eff; cursor: pointer !important; margin-bottom: 4px; } .base-style-button:active { box-shadow: 0px 0px 8px #0e8de2a5; } .base-style-button + .base-style-button { margin-left: 12px; } .base-style-button:hover { background-color: #7abbff24; } .base-style-button.danger:hover { background-color: #ffdede86; } .base-style-button:disabled { background-color: white; border: 1px solid #c0c0c0; color: #aeaeae; cursor: not-allowed; } .base-style-button.danger { color: #f36669; border-color: #dd5a5d; } .base-style-button:disabled:active { box-shadow: none; } .base-style-button-secondary { appearance: none; -moz-appearance: none; -webkit-appearance: none; border-radius: 4px; border: 1px solid #2c92ff; color: #409eff; cursor: pointer !important; margin-bottom: 4px; color: gray; background-color: white; border: 1px solid #dcdcdc; } .base-style-button-secondary:active { box-shadow: 0px 0px 8px #0e8de2a5; } .base-style-button-secondary + .base-style-button-secondary { margin-left: 12px; } .base-style-button-secondary:hover { background-color: #7abbff24; } .base-style-button-secondary.danger:hover { background-color: #ffdede86; } .base-style-button-secondary:disabled { background-color: white; border: 1px solid #c0c0c0; color: #aeaeae; cursor: not-allowed; } .base-style-button-secondary.danger { color: #f36669; border-color: #dd5a5d; } .base-style-button-secondary:disabled:active { box-shadow: none; } container-element.hidden { display: none; } container-element.minimize { min-width: unset; } container-element { position: fixed; top: 10%; left: 10%; z-index: 99999; text-align: left; min-width: 300px; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; color: #636363; box-shadow: 0 0 24px -12px #3f3f3f; border-radius: 8px; letter-spacing: 0.5px; border: 1px solid #c1c1c1; } header-element { display: flex; align-items: center; background-color: white; border-radius: 8px 8px 0px 0px; user-select: none; padding: 4px; padding-bottom: 0px; } header-element .extra-menu-bar { width: 100%; padding: 4px; padding-bottom: 0px; margin-top: 4px; border-top: 1px solid #e8e8e8; /** 默认隐藏,一直到需要激活的时候再更改 */ display: none; } header-element .extra-menu-bar .script-panel-link { appearance: none; -moz-appearance: none; -webkit-appearance: none; border-radius: 4px; border: 1px solid #2c92ff; color: #409eff; cursor: pointer !important; margin-bottom: 4px; color: gray; background-color: white; border: 1px solid #dcdcdc; padding-bottom: 2px; margin-bottom: 0px; } header-element .extra-menu-bar .script-panel-link:active { box-shadow: 0px 0px 8px #0e8de2a5; } header-element .extra-menu-bar .script-panel-link + header-element .extra-menu-bar .script-panel-link { margin-left: 12px; } header-element .extra-menu-bar .script-panel-link:hover { background-color: #7abbff24; } header-element .extra-menu-bar .script-panel-link.danger:hover { background-color: #ffdede86; } header-element .extra-menu-bar .script-panel-link:disabled { background-color: white; border: 1px solid #c0c0c0; color: #aeaeae; cursor: not-allowed; } header-element .extra-menu-bar .script-panel-link.danger { color: #f36669; border-color: #dd5a5d; } header-element .extra-menu-bar .script-panel-link:disabled:active { box-shadow: none; } header-element .extra-menu-bar .script-panel-link.active { background-color: #1890ff1a; border-color: #1890ff; color: #1890ff; } header-element .extra-menu-bar .script-panel-link + .script-panel-link { margin-left: 4px; } header-element .profile { flex: 1; cursor: move; } header-element .switch:hover, header-element .dropdown:hover { background-color: #f3f3f3; } header-element .close:hover { background-color: #ff000038; } header-element .switch, header-element .close { cursor: pointer; } header-element .dropdown { line-height: 24px; text-decoration: underline; } header-element .switch, header-element .close, header-element .profile { display: inline-flex; align-items: center; padding: 0px 8px; } .logo { width: 18px; height: 18px; cursor: pointer; } .project-selector { display: flex; align-items: center; } .project-selector select { background: #ffffff00; border-radius: 4px; border: 1px solid #63636346; padding: 4px; } .project-selector.expand-all { display: none; } .body { overflow: auto; width: auto; height: 100%; } script-panel-element { display: block; background-color: white; border-radius: 0px 0px 8px 8px; padding: 0px 8px 12px 8px; overflow: auto; } script-panel-element .script-panel-body { padding: 0px 8px; } script-panel-element + script-panel-element { margin-top: 12px; } .card + .card { margin-top: 12px; } .card { background-color: white; border-radius: 2px; padding: 0px 8px; } .notes { background: #0099ff0e; border-left: 4px solid #0099ff65; width: -webkit-fill-available; margin: 0px 8px; line-height: 26px; letter-spacing: 1px; } .secondary { font-size: 12px; color: #8b8b8b; } .tooltip-container { z-index: 99999999999999; margin: 12px 0px 0px 12px; padding: 4px; color: black; background: #f0f0f0; box-shadow: 0px 0px 4px #949494; position: fixed; white-space: normal; max-width: 200px; height: auto; border-radius: 2px; line-height: 18px; } .configs-container.lock { filter: blur(1px); user-select: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; } .configs-container .lock-wrapper { cursor: not-allowed !important; border-radius: 4px; position: absolute; left: 0px; z-index: 1; display: inline-flex; align-items: center; justify-content: center; } .configs-container .lock-message { background-color: #ffffff7d; border-radius: 4px; box-shadow: 0px 0px 12px #6a6a6a98; padding: 2px; } .configs { display: table; background: #e1e1e107; width: -webkit-fill-available; } .configs .configs-body { display: table-row-group; } .configs .configs-body config-element + config-element label { padding-top: 4px; } .configs .configs-body config-element + config-element .config-wrapper { padding-top: 4px; } .configs .configs-body config-element { width: 100%; display: table-row; line-height: 26px; } .configs .configs-body config-element label { white-space: nowrap; color: #4e5969; display: table-cell; padding-right: 12px; text-align: left; vertical-align: top; margin-right: 12px; } .configs .configs-body config-element .config-wrapper { display: table-cell; vertical-align: middle; /** check box 的样式 */ } .configs .configs-body config-element .config-wrapper select { outline: none; border: none; border: 1px solid #e4e4e4; border-radius: 4px; padding: 2px 8px; border: 1px solid #ffffff00; } .configs .configs-body config-element .config-wrapper select:focus { border: 1px solid #0e8de290; box-shadow: 0px 0px 4px #0e8de252; } .configs .configs-body config-element .config-wrapper select:focus:not([type='checkbox'], [type='radio']) { border: 1px solid #0e8de290; box-shadow: 0px 0px 4px #0e8de252; background-color: white !important; } .configs .configs-body config-element .config-wrapper select:hover { background-color: #ebeef4; } .configs .configs-body config-element .config-wrapper textarea { padding: 2px 8px; outline: none; border: none; border: 1px solid #ffffff00; } .configs .configs-body config-element .config-wrapper textarea:focus { border: 1px solid #0e8de290; box-shadow: 0px 0px 4px #0e8de252; } .configs .configs-body config-element .config-wrapper textarea:focus:not([type='checkbox'], [type='radio']) { border: 1px solid #0e8de290; box-shadow: 0px 0px 4px #0e8de252; background-color: white !important; } .configs .configs-body config-element .config-wrapper textarea:hover { background-color: #ebeef4; } .configs .configs-body config-element .config-wrapper input:not([type='button']) { outline: none; padding: 2px 8px; margin: 0px; background-color: #eef2f7; border-radius: 2px; color: black; border: 1px solid #ffffff00; } .configs .configs-body config-element .config-wrapper input:not([type='button'])::placeholder { color: #bababa; } .configs .configs-body config-element .config-wrapper input:not([type='button']):focus { border: 1px solid #0e8de290; box-shadow: 0px 0px 4px #0e8de252; } .configs .configs-body config-element .config-wrapper input:not([type='button']):focus:not([type='checkbox'], [type='radio']) { border: 1px solid #0e8de290; box-shadow: 0px 0px 4px #0e8de252; background-color: white !important; } .configs .configs-body config-element .config-wrapper input:not([type='button']):hover { background-color: #ebeef4; } .configs .configs-body config-element .config-wrapper input[type='range'] { padding: 0px; } .configs .configs-body config-element .config-wrapper input[type='button'] { appearance: none; -moz-appearance: none; -webkit-appearance: none; border-radius: 4px; background-color: white; border: 1px solid #2c92ff; color: #409eff; cursor: pointer !important; margin-bottom: 4px; } .configs .configs-body config-element .config-wrapper input[type='button']:active { box-shadow: 0px 0px 8px #0e8de2a5; } .configs .configs-body config-element .config-wrapper input[type='button'] + .configs .configs-body config-element .config-wrapper input[type='button'] { margin-left: 12px; } .configs .configs-body config-element .config-wrapper input[type='button']:hover { background-color: #7abbff24; } .configs .configs-body config-element .config-wrapper input[type='button'].danger:hover { background-color: #ffdede86; } .configs .configs-body config-element .config-wrapper input[type='button']:disabled { background-color: white; border: 1px solid #c0c0c0; color: #aeaeae; cursor: not-allowed; } .configs .configs-body config-element .config-wrapper input[type='button'].danger { color: #f36669; border-color: #dd5a5d; } .configs .configs-body config-element .config-wrapper input[type='button']:disabled:active { box-shadow: none; } .configs .configs-body config-element .config-wrapper input[type='checkbox'] { appearance: none; -moz-appearance: none; -webkit-appearance: none; width: fit-content; min-width: 36px; height: 20px; border-radius: 100px; display: flex; align-items: center; padding: 2px 4px; transition: all 0.2s ease-in-out; width: auto; background: gainsboro; } .configs .configs-body config-element .config-wrapper input[type='checkbox']:checked { background: #1890ff; } .configs .configs-body config-element .config-wrapper input[type='checkbox']:disabled { background-color: #f7f7f78b; } .configs .configs-body config-element .config-wrapper input[type='checkbox']:checked::before { transform: translate(100%, 0px); } .configs .configs-body config-element .config-wrapper input[type='checkbox']::before { background-color: #fff; border-radius: 9px; box-shadow: 0 2px 4px #00230b33; width: 14px; height: 14px; content: ''; } .configs .configs-body config-element .config-wrapper input:not([type='checkbox'], [type='radio']), .configs .configs-body config-element .config-wrapper textarea, .configs .configs-body config-element .config-wrapper select { width: -webkit-fill-available; font-size: inherit; } .configs .configs-body config-element .config-wrapper input[type='checkbox'], .configs .configs-body config-element .config-wrapper input[type='radio'], .configs .configs-body config-element .config-wrapper input[type='range'] { accent-color: #0e8ee2; } .configs .configs-body config-element .config-wrapper > *:not(.tooltip) { background-color: #eef2f7; border-radius: 2px; color: black; float: right; } .configs .configs-body config-element .config-wrapper > *:disabled { cursor: not-allowed; background-color: #f7f7f78b; } .message-container { margin-bottom: 4px; position: absolute; bottom: 100%; left: 50%; width: 100%; transform: translate(-50%, 0px); min-width: 300px; } .message-container message-element { display: flex; border-radius: 4px; padding: 4px 12px; margin-bottom: 4px; } .message-container message-element .message-content-container { margin-right: 8px; flex: auto; } .message-container message-element .message-text { letter-spacing: 1px; font-weight: bold; } .message-container message-element .message-closer { width: 18px; min-width: 18px; cursor: pointer; background-color: #ffffffb3; color: #a1a1a1; border-radius: 100%; text-align: center; height: 18px; vertical-align: middle; font-size: 12px; } .message-container message-element.error { background-color: #ffe6e6; color: #c70208; border: 1px solid #ff6b6ded; } .message-container message-element.info { background-color: #c9e7ff; color: #004d95; border: 1px solid #1890ff69; } .message-container message-element.success { background-color: #e8ffe0; color: #3e8d0d; border: 1px solid #6fd91d; } .message-container message-element.warn { background-color: #ffefc8; color: #9b7400; border: 1px solid #ffc107; } modal-element { position: absolute; top: 50%; left: 50%; background-color: white; border-radius: 4px; box-shadow: 0px 0px 24px -12px black; border: 1px solid #929292; height: fit-content; transform: translate(-50%, -50%); padding: 12px 18px 18px 18px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; z-index: 99999999999; line-height: 24px; } modal-element .modal-profile { zoom: 0.9; color: #969696; user-select: none; margin-bottom: 4px; } modal-element .modal-title { font-size: 18px; font-weight: bold; user-select: none; } modal-element .modal-body { margin: 12px 0px; overflow: auto; } modal-element .modal-footer { display: flex; white-space: nowrap; justify-content: end; align-items: end; } modal-element .modal-footer > * + * { margin-left: 12px; } modal-element .modal-input { outline: none; padding: 2px 8px; margin: 0px; background-color: #eef2f7; border-radius: 2px; color: black; border: 1px solid #ffffff00; width: -webkit-fill-available; } modal-element .modal-input::placeholder { color: #bababa; } modal-element .modal-input:focus { border: 1px solid #0e8de290; box-shadow: 0px 0px 4px #0e8de252; } modal-element .modal-input:focus:not([type='checkbox'], [type='radio']) { border: 1px solid #0e8de290; box-shadow: 0px 0px 4px #0e8de252; background-color: white !important; } modal-element .modal-input:hover { background-color: #ebeef4; } modal-element .modal-cancel-button { appearance: none; -moz-appearance: none; -webkit-appearance: none; border-radius: 4px; border: 1px solid #2c92ff; color: #409eff; cursor: pointer !important; margin-bottom: 4px; color: gray; background-color: white; border: 1px solid #dcdcdc; } modal-element .modal-cancel-button:active { box-shadow: 0px 0px 8px #0e8de2a5; } modal-element .modal-cancel-button + modal-element .modal-cancel-button { margin-left: 12px; } modal-element .modal-cancel-button:hover { background-color: #7abbff24; } modal-element .modal-cancel-button.danger:hover { background-color: #ffdede86; } modal-element .modal-cancel-button:disabled { background-color: white; border: 1px solid #c0c0c0; color: #aeaeae; cursor: not-allowed; } modal-element .modal-cancel-button.danger { color: #f36669; border-color: #dd5a5d; } modal-element .modal-cancel-button:disabled:active { box-shadow: none; } modal-element .modal-confirm-button { appearance: none; -moz-appearance: none; -webkit-appearance: none; border-radius: 4px; background-color: white; border: 1px solid #2c92ff; color: #409eff; cursor: pointer !important; margin-bottom: 4px; } modal-element .modal-confirm-button:active { box-shadow: 0px 0px 8px #0e8de2a5; } modal-element .modal-confirm-button + modal-element .modal-confirm-button { margin-left: 12px; } modal-element .modal-confirm-button:hover { background-color: #7abbff24; } modal-element .modal-confirm-button.danger:hover { background-color: #ffdede86; } modal-element .modal-confirm-button:disabled { background-color: white; border: 1px solid #c0c0c0; color: #aeaeae; cursor: not-allowed; } modal-element .modal-confirm-button.danger { color: #f36669; border-color: #dd5a5d; } modal-element .modal-confirm-button:disabled:active { box-shadow: none; } modal-element.alert .modal-input, modal-element.alert .modal-cancel-button { display: none; } modal-element.alert .modal-confirm-button { margin: 0; } modal-element.prompt .modal-input, modal-element.prompt .modal-cancel-button, modal-element.prompt .modal-confirm-button { display: block; } modal-element.confirm .modal-input { display: none; } .modal-wrapper { width: 100%; height: 100%; z-index: 9999; position: fixed; top: 0px; left: 0px; z-index: 9999999; background-color: rgba(0, 0, 0, 0.265); color: #636363; font: 14px Menlo, Monaco, Consolas, 'Courier New', monospace; } .pointer { cursor: pointer; } .separator { display: flex; align-items: center; text-align: center; padding: 4px 0px 8px 0px; } .separator::before, .separator::after { content: ''; flex: 1; border-bottom: 1px solid #63636346; } .separator:not(:empty)::before { margin-right: 0.25em; } .separator:not(:empty)::after { margin-left: 0.25em; } container-element.minimize .body, container-element.minimize header-element .dropdown, container-element.minimize .footer { display: none; } container-element.minimize header-element { padding: 8px; border-radius: 8px; box-shadow: 0px 0px 24px -12px black; } .user-guide > li { padding: 4px 0px; } .search-infos-num { width: 26px; margin: 2px; height: 20px; border-radius: 4px; display: inline-block; background-color: white; text-align: center; cursor: pointer; border: 1px solid #b6b6b6; } .search-infos-num.requested { border: 1px solid #63b4ff; color: #63b4ff; } .search-infos-num.active { background-color: #127de1 !important; color: white; } .search-infos-num.error { border: 1px solid #ff8789ed; background-color: #ff6b6ded; color: white; } .search-infos-num.finish { background-color: #63b4ff; border: 1px solid #63b4ff; color: white; } search-infos-element { display: block; overflow: auto; } search-infos-element .search-result { margin-bottom: 12px; padding-left: 12px; } search-infos-element .search-result .question { font-weight: bold; max-height: 200px; overflow: auto; } search-infos-element .search-result .answer { color: #7c7c7c; } search-infos-element .search-result .answer code { border-bottom: 1px solid #dcdcdc; padding: 2px 0px; border-radius: 2px; margin: 4px; line-height: 22px; } search-infos-element .search-result .answer code + code { margin-left: 4px; } search-infos-element .search-result .search-result-answer-tag { padding: 2px 6px; border-radius: 2px; font-size: 12px; cursor: pointer; margin-right: 6px; } search-infos-element .search-result .search-result-answer-tag + .search-result-answer-tag { margin-left: 4px; } search-infos-element .search-result .search-result-answer-tag.blue { background-color: #e6f7ff; border: 1px solid #91d5ff; color: #1890ff; } search-infos-element .search-result .search-result-answer-tag.green { background-color: #f6ffed; border: 1px solid #b7eb8f; color: #52c41a; } search-infos-element .search-result .search-result-answer-tag.gray { background-color: #fafafa; border: 1px solid #d9d9d9; color: #595959; } search-infos-element .search-result .search-result-answer-tag.red { background-color: #fff1f0; border: 1px solid #ffa39e; color: #ff4d4f; } search-infos-element .search-result .search-result-answer-tag.yellow { background-color: #fffbe6; border: 1px solid #ffe58f; color: #faad14; } search-infos-element .search-result-question-type { background-color: #e6f7ff; border: 1px solid #91d5ff; color: #1890ff; margin-right: 8px; padding: 0px 4px; border-radius: 4px; } search-infos-element .error { color: #ff6b6ded; display: inline-block; padding-left: 12px; } .copy, .question-title-extra-btn { margin-left: 4px; padding: 2px 4px; border-radius: 2px; box-shadow: 0 0 4px #b1b1b1; cursor: pointer !important; font-weight: normal; font-size: 12px; } .work-result-question-container { position: absolute; width: 400px; left: -100%; top: 0px; background: white; border: 1px solid #cbcbcb; border-radius: 4px; box-shadow: 0px 0px 12px #d1cfcf; padding: 12px; } .work-result-question-container .close-search-result { font-size: 12px; margin-left: 8px; text-decoration: underline; color: gray; cursor: pointer; } .work-result-list { max-height: 400px; overflow: auto; margin: 12px 0px; padding: 6px; border: 1px solid #e1e1e1; border-radius: 4px; } .search-info-title { border: 1px solid #e1e1e1; border-radius: 4px; padding: 8px 12px; margin-bottom: 12px; line-height: 20px; max-height: 400px; overflow: auto; } .search-info-details { margin-left: 4px; } .console { max-height: 300px; max-width: 400px; overflow: auto; background-color: #292929; padding: 12px 6px; color: #ececec; font-size: 12px; } .console .item { padding: 3px 0px; border-radius: 2px; } .console .item .time { color: #757575; } .console .item .info { background-color: #2196f3a3; } .console .item .warn { background-color: #ffc107db; } .console .item .error { background-color: #f36c71cc; } .console .item .debug, .console .item .log { background-color: #9e9e9ec4; } .console *::selection { background-color: #ffffff6b; } .markdown { max-width: 400px; max-height: 50vh; overflow: auto; } .markdown code { padding: 2px 4px; background-color: #f0f0f0; border-radius: 6px; font-size: 12px; } .markdown blockquote { padding: 4px 4px 4px 12px; margin: 0px; color: #b5b5b5; border-left: #ababab 2px solid; } .markdown blockquote p { margin: 0px; } .markdown h1, .markdown h2, .markdown h3, .markdown h4, .markdown h5, .markdown h6, .markdown p { margin: 8px 0px; } .dropdown { position: relative; display: inline-block; } .dropdown.active .dropdown-trigger-element { color: #1890ff; } .dropdown-trigger-element { cursor: pointer; } .dropdown-content { display: none; position: absolute; background-color: #ffffff; overflow: auto; box-shadow: 0px 8px 16px 0px #00000033; z-index: 1; border-radius: 4px; padding: 8px 12px; min-width: 120px; } .dropdown-content.show { display: block; } .dropdown-content { cursor: pointer; z-index: 999; } .dropdown-content .dropdown-option { padding-left: 4px; white-space: nowrap; } .dropdown-content .dropdown-option:hover { background-color: #f3f3f3; } .dropdown-content .dropdown-option.active { background-color: #1890ff1a; color: #1890ff; } .space { display: inline-flex; } .config-details { animation: fade-in 0.5s; } .config-details label { padding-left: 12px; } .alert-info-wrapper { margin-bottom: 8px; } .alert-info-wrapper .result-info { padding: 12px; text-align: center; border-radius: 6px; } .alert-info-wrapper .unresolved { color: #a1a1a1; background-color: #f7f7f7; } .alert-info-wrapper .no-answer { color: #a1a1a1; background-color: #f7f7f7; } .alert-info-wrapper .error { color: #ff4d4f; background-color: #fff1f0; } message-element { animation: show 0.5s; } script-panel-element > div, script-panel-link, container-element, modal-element { animation: fade-in 0.3s; } @keyframes show { 0% { transform: translateY(20px); opacity: 0; } 100% { transform: translateY(0); opacity: 1; } } @keyframes fade-in { 0% { opacity: 0; } 100% { opacity: 1; } } @keyframes fade-out { 0% { opacity: 1; } 100% { opacity: 0; } } .checkbox-label { display: inline-block !important; position: relative; cursor: pointer; font-size: 16px; color: #2c3e50; } /* 隐藏原始复选框 */ .checkbox-input { position: absolute; opacity: 0; width: 0; height: 0; } /* 自定义按钮样式 */ .checkbox-label::after { content: ''; display: inline-block; border-radius: 50%; transition: all 0.1s ease; position: relative; margin-left: 4px; vertical-align: middle; } /* 向下箭头(未选中状态) */ .checkbox-label::before { content: '🔽'; position: absolute; width: 0; height: 0; right: 8px; transition: all 0.1s ease; z-index: 2; } /* 向上箭头(选中状态) */ .checked .checkbox-label::before { content: '🔼'; } ================================================ FILE: packages/scripts/assets/less/style.less ================================================ /** 默认字体 */ @base-font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; /** 输入框默认边距 */ @form-control-padding: 2px 8px; @form-control-bg: #eef2f7; ul, ol { line-height: 26px; padding-left: 22px; margin: 0px; } a { color: #1890ff; } hr { border-style: solid; border-color: #63636346; border-width: 0px; border-bottom: 1px solid #63636346; margin-block-start: 1em; margin-block-end: 1em; } .base-style-active-form-control { border: 1px solid #ffffff00; &:focus { border: 1px solid #0e8de290; box-shadow: 0px 0px 4px #0e8de252; } &:focus:not([type='checkbox'], [type='radio']) { border: 1px solid #0e8de290; box-shadow: 0px 0px 4px #0e8de252; background-color: white !important; } &:hover { background-color: #ebeef4; } } .base-style-input { outline: none; border: 1px solid #ffffff00; padding: @form-control-padding; margin: 0px; background-color: @form-control-bg; border-radius: 2px; color: black; &::placeholder { color: #bababa; } } .base-style-switch { appearance: none; -moz-appearance: none; -webkit-appearance: none; width: fit-content; min-width: 36px; height: 20px; border-radius: 100px; display: flex; align-items: center; padding: 2px 4px; transition: all 0.2s ease-in-out; width: auto; background: gainsboro; &:checked { background: #1890ff; } &:disabled { background-color: #f7f7f78b; } &:checked::before { transform: translate(100%, 0px); } &::before { background-color: #fff; border-radius: 9px; box-shadow: 0 2px 4px #00230b33; width: 14px; height: 14px; content: ''; } } .base-style-button { appearance: none; -moz-appearance: none; -webkit-appearance: none; border-radius: 4px; background-color: white; border: 1px solid #2c92ff; color: #409eff; cursor: pointer !important; margin-bottom: 4px; &:active { box-shadow: 0px 0px 8px #0e8de2a5; } & + & { margin-left: 12px; } &:hover { background-color: #7abbff24; } &.danger:hover { background-color: #ffdede86; } &:disabled { background-color: white; border: 1px solid #c0c0c0; color: #aeaeae; cursor: not-allowed; } &.danger { color: #f36669; border-color: #dd5a5d; } &:disabled:active { box-shadow: none; } } .base-style-button-secondary { .base-style-button(); color: gray; background-color: white; border: 1px solid #dcdcdc; } container-element.hidden { display: none; } container-element.minimize { min-width: unset; } container-element { position: fixed; top: 10%; left: 10%; z-index: 99999; text-align: left; min-width: 300px; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; color: #636363; box-shadow: 0 0 24px -12px #3f3f3f; border-radius: 8px; letter-spacing: 0.5px; border: 1px solid #c1c1c1; } @base-height: 32px; @size: 18px; header-element { display: flex; align-items: center; background-color: white; border-radius: 8px 8px 0px 0px; user-select: none; padding: 4px; padding-bottom: 0px; .extra-menu-bar { width: 100%; padding: 4px; padding-bottom: 0px; margin-top: 4px; border-top: 1px solid #e8e8e8; /** 默认隐藏,一直到需要激活的时候再更改 */ display: none; .script-panel-link { .base-style-button-secondary(); padding-bottom: 2px; margin-bottom: 0px; } .script-panel-link.active { background-color: #1890ff1a; border-color: #1890ff; color: #1890ff; } .script-panel-link + .script-panel-link { margin-left: 4px; } } .profile { flex: 1; cursor: move; } .switch:hover, .dropdown:hover { background-color: #f3f3f3; } .close:hover { background-color: #ff000038; } .switch, .close { cursor: pointer; } .dropdown { line-height: 24px; text-decoration: underline; } .switch, .close, .profile { display: inline-flex; align-items: center; padding: 0px 8px; } } .logo { width: 18px; height: 18px; cursor: pointer; } .project-selector { display: flex; align-items: center; select { background: #ffffff00; border-radius: 4px; border: 1px solid #63636346; padding: 4px; } } .project-selector.expand-all { display: none; } .body { overflow: auto; width: auto; height: 100%; } script-panel-element { display: block; background-color: white; border-radius: 0px 0px 8px 8px; padding: 0px 8px 12px 8px; overflow: auto; .script-panel-body { padding: 0px 8px; } } script-panel-element + script-panel-element { margin-top: 12px; } .card + .card { margin-top: 12px; } .card { background-color: white; border-radius: 2px; padding: 0px 8px; } .notes { background: #0099ff0e; border-left: 4px solid #0099ff65; width: -webkit-fill-available; margin: 0px 8px; line-height: 26px; letter-spacing: 1px; } .secondary { font-size: 12px; color: #8b8b8b; } .tooltip-container { z-index: 99999999999999; margin: 12px 0px 0px 12px; padding: 4px; color: black; background: #f0f0f0; box-shadow: 0px 0px 4px #949494; position: fixed; white-space: normal; max-width: 200px; height: auto; border-radius: 2px; line-height: 18px; } .configs-container { &.lock { filter: blur(1px); user-select: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; } .lock-wrapper { cursor: not-allowed !important; border-radius: 4px; position: absolute; left: 0px; z-index: 1; display: inline-flex; align-items: center; justify-content: center; } .lock-message { background-color: #ffffff7d; border-radius: 4px; box-shadow: 0px 0px 12px #6a6a6a98; padding: 2px; } } .configs { display: table; background: #e1e1e107; width: -webkit-fill-available; .configs-body { display: table-row-group; config-element + config-element { label { padding-top: 4px; } .config-wrapper { padding-top: 4px; } } config-element { width: 100%; display: table-row; line-height: 26px; label { white-space: nowrap; color: #4e5969; display: table-cell; padding-right: 12px; text-align: left; vertical-align: top; margin-right: 12px; } .config-wrapper { display: table-cell; vertical-align: middle; select { outline: none; border: none; border: 1px solid #e4e4e4; border-radius: 4px; padding: @form-control-padding; .base-style-active-form-control(); } textarea { padding: @form-control-padding; outline: none; border: none; .base-style-active-form-control(); } input:not([type='button']) { .base-style-input(); .base-style-active-form-control(); } input[type='range'] { // 取消左右间隔,否则滚动条不能滚到头尾 padding: 0px; } input[type='button'] { .base-style-button(); } /** check box 的样式 */ input[type='checkbox'] { .base-style-switch(); } input:not([type='checkbox'], [type='radio']), textarea, select { width: -webkit-fill-available; font-size: inherit; } input[type='checkbox'], input[type='radio'], input[type='range'] { accent-color: #0e8ee2; } } .config-wrapper > *:not(.tooltip) { background-color: @form-control-bg; border-radius: 2px; color: black; float: right; } .config-wrapper > *:disabled { cursor: not-allowed; background-color: #f7f7f78b; } } } } // 消息推送 .message-container { margin-bottom: 4px; position: absolute; bottom: 100%; left: 50%; width: 100%; transform: translate(-50%, 0px); min-width: 300px; // 提示内容 message-element { display: flex; border-radius: 4px; padding: 4px 12px; margin-bottom: 4px; .message-content-container { margin-right: 8px; flex: auto; } .message-text { letter-spacing: 1px; font-weight: bold; } // 关闭按钮 .message-closer { width: 18px; min-width: 18px; cursor: pointer; background-color: #ffffffb3; color: #a1a1a1; border-radius: 100%; text-align: center; height: 18px; vertical-align: middle; font-size: 12px; } &.error { background-color: #ffe6e6; color: #c70208; border: 1px solid #ff6b6ded; } &.info { background-color: #c9e7ff; color: #004d95; border: 1px solid #1890ff69; } &.success { background-color: #e8ffe0; color: #3e8d0d; border: 1px solid #6fd91d; } &.warn { background-color: #ffefc8; color: #9b7400; border: 1px solid #ffc107; } } } modal-element { position: absolute; top: 50%; left: 50%; background-color: white; border-radius: 4px; box-shadow: 0px 0px 24px -12px black; border: 1px solid #929292; height: fit-content; transform: translate(-50%, -50%); padding: 12px 18px 18px 18px; font-family: @base-font-family; z-index: 99999999999; line-height: 24px; .modal-profile { zoom: 0.9; color: #969696; user-select: none; margin-bottom: 4px; } .modal-title { font-size: 18px; font-weight: bold; user-select: none; } .modal-body { margin: 12px 0px; overflow: auto; } .modal-footer { display: flex; white-space: nowrap; justify-content: end; align-items: end; > * + * { margin-left: 12px; } } .modal-input { .base-style-input(); .base-style-active-form-control(); width: -webkit-fill-available; } .modal-cancel-button { .base-style-button(); color: gray; background-color: white; border: 1px solid #dcdcdc; } .modal-confirm-button { .base-style-button(); } } modal-element.alert { .modal-input, .modal-cancel-button { display: none; } .modal-confirm-button { margin: 0; } } modal-element.prompt { .modal-input, .modal-cancel-button, .modal-confirm-button { display: block; } } modal-element.confirm { .modal-input { display: none; } } .modal-wrapper { width: 100%; height: 100%; z-index: 9999; position: fixed; top: 0px; left: 0px; z-index: 9999999; background-color: rgba(0, 0, 0, 0.265); color: #636363; font: 14px @base-font-family; } .pointer { cursor: pointer; } .separator { display: flex; align-items: center; text-align: center; padding: 4px 0px 8px 0px; &::before, &::after { content: ''; flex: 1; border-bottom: 1px solid #63636346; } &:not(:empty)::before { margin-right: 0.25em; } &:not(:empty)::after { margin-left: 0.25em; } } container-element.minimize { .body, header-element .dropdown, .footer { display: none; } } container-element.minimize header-element { padding: 8px; border-radius: 8px; box-shadow: 0px 0px 24px -12px black; } .user-guide { & > li { padding: 4px 0px; } } .search-infos-num { width: 26px; margin: 2px; height: 20px; border-radius: 4px; display: inline-block; background-color: white; text-align: center; cursor: pointer; border: 1px solid #b6b6b6; &.requested { border: 1px solid #63b4ff; color: #63b4ff; } &.active { background-color: #127de1 !important; color: white; } &.error { border: 1px solid #ff8789ed; background-color: #ff6b6ded; color: white; } &.finish { background-color: #63b4ff; border: 1px solid #63b4ff; color: white; } } search-infos-element { display: block; overflow: auto; .search-result { margin-bottom: 12px; padding-left: 12px; .question { font-weight: bold; max-height: 200px; overflow: auto; } .answer { color: #7c7c7c; code { // background-color: #f3f3f3; border-bottom: 1px solid #dcdcdc; padding: 2px 0px; border-radius: 2px; margin: 4px; line-height: 22px; } code + code { margin-left: 4px; } } .search-result-answer-tag { padding: 2px 6px; border-radius: 2px; font-size: 12px; cursor: pointer; margin-right: 6px; } .search-result-answer-tag + .search-result-answer-tag { margin-left: 4px; } .search-result-answer-tag.blue { background-color: #e6f7ff; border: 1px solid #91d5ff; color: #1890ff; } .search-result-answer-tag.green { background-color: #f6ffed; border: 1px solid #b7eb8f; color: #52c41a; } .search-result-answer-tag.gray { background-color: #fafafa; border: 1px solid #d9d9d9; color: #595959; } .search-result-answer-tag.red { background-color: #fff1f0; border: 1px solid #ffa39e; color: #ff4d4f; } .search-result-answer-tag.yellow { background-color: #fffbe6; border: 1px solid #ffe58f; color: #faad14; } } .search-result-question-type { background-color: #e6f7ff; border: 1px solid #91d5ff; color: #1890ff; margin-right: 8px; padding: 0px 4px; border-radius: 4px; } .error { color: #ff6b6ded; display: inline-block; padding-left: 12px; } } .copy, .question-title-extra-btn { margin-left: 4px; padding: 2px 4px; border-radius: 2px; box-shadow: 0 0 4px #b1b1b1; cursor: pointer !important; font-weight: normal; font-size: 12px; } .work-result-question-container { position: absolute; width: 400px; left: -100%; top: 0px; background: white; border: 1px solid #cbcbcb; border-radius: 4px; box-shadow: 0px 0px 12px #d1cfcf; padding: 12px; .close-search-result { font-size: 12px; margin-left: 8px; text-decoration: underline; color: gray; cursor: pointer; } } .work-result-list { max-height: 400px; overflow: auto; margin: 12px 0px; padding: 6px; border: 1px solid #e1e1e1; border-radius: 4px; } .search-info-title { border: 1px solid #e1e1e1; border-radius: 4px; padding: 8px 12px; margin-bottom: 12px; line-height: 20px; max-height: 400px; overflow: auto; } .search-info-details { margin-left: 4px; } .console { max-height: 300px; max-width: 400px; overflow: auto; background-color: #292929; padding: 12px 6px; color: #ececec; font-size: 12px; .item { padding: 3px 0px; border-radius: 2px; .time { color: #757575; } .info { background-color: #2196f3a3; } .warn { background-color: #ffc107db; } .error { background-color: #f36c71cc; } .debug, .log { background-color: #9e9e9ec4; } } // 文字选中样式 & *::selection { background-color: #ffffff6b; } } .markdown { max-width: 400px; max-height: 50vh; overflow: auto; code { padding: 2px 4px; background-color: #f0f0f0; border-radius: 6px; font-size: 12px; } blockquote { padding: 4px 4px 4px 12px; margin: 0px; color: #b5b5b5; border-left: #ababab 2px solid; p { margin: 0px; } } h1, h2, h3, h4, h5, h6, p { margin: 8px 0px; } } .dropdown { position: relative; display: inline-block; &.active { .dropdown-trigger-element { color: #1890ff; } } } .dropdown-trigger-element { cursor: pointer; } .dropdown-content { display: none; position: absolute; background-color: #ffffff; overflow: auto; box-shadow: 0px 8px 16px 0px #00000033; z-index: 1; border-radius: 4px; padding: 8px 12px; min-width: 120px; &.show { display: block; } } .dropdown-content { cursor: pointer; z-index: 999; .dropdown-option { padding-left: 4px; white-space: nowrap; } .dropdown-option:hover { background-color: #f3f3f3; } .dropdown-option.active { background-color: #1890ff1a; color: #1890ff; } } .space { display: inline-flex; } .config-details { label { padding-left: 12px; } animation: fade-in 0.5s; } .alert-info-wrapper { margin-bottom: 8px; .result-info { padding: 12px; text-align: center; border-radius: 6px; } .unresolved { color: #a1a1a1; background-color: #f7f7f7; } .no-answer { color: #a1a1a1; background-color: #f7f7f7; } .error { color: #ff4d4f; background-color: #fff1f0; } } message-element { animation: show 0.5s; } script-panel-element > div, script-panel-link, container-element, modal-element { animation: fade-in 0.3s; } @keyframes show { 0% { transform: translateY(20px); opacity: 0; } 100% { transform: translateY(0); opacity: 1; } } @keyframes fade-in { 0% { opacity: 0; } 100% { opacity: 1; } } @keyframes fade-out { 0% { opacity: 1; } 100% { opacity: 0; } } .checkbox-label { display: inline-block !important; position: relative; cursor: pointer; font-size: 16px; color: #2c3e50; } /* 隐藏原始复选框 */ .checkbox-input { position: absolute; opacity: 0; width: 0; height: 0; } /* 自定义按钮样式 */ .checkbox-label::after { content: ''; display: inline-block; border-radius: 50%; transition: all 0.1s ease; position: relative; margin-left: 4px; vertical-align: middle; } /* 向下箭头(未选中状态) */ .checkbox-label::before { content: '🔽'; position: absolute; width: 0; height: 0; right: 8px; // border-left: 10px solid transparent; // border-right: 10px solid transparent; // border-top: 15px solid rgb(0, 0, 0); transition: all 0.1s ease; z-index: 2; } /* 向上箭头(选中状态) */ .checked .checkbox-label::before { content: '🔼'; } ================================================ FILE: packages/scripts/entry.common.js ================================================ /* eslint-disable no-undef */ /// // 环境检测 if ( [ 'GM_getTab', 'GM_saveTab', 'GM_setValue', 'GM_getValue', 'unsafeWindow', 'GM_listValues', 'GM_deleteValue', 'GM_notification', 'GM_xmlhttpRequest', 'GM_getResourceText', 'GM_addValueChangeListener', 'GM_removeValueChangeListener' ].some((api) => typeof Reflect.get(globalThis, api) === 'undefined') ) { const open = confirm( `OCS网课脚本不支持当前的脚本管理器(${GM_info.scriptHandler})。` + '请前往 https://docs.ocsjs.com/docs/script 下载指定的脚本管理器,例如 “Scriptcat 脚本猫” 或者 “Tampermonkey 油猴”' ); if (open) { window.location.href = 'https://docs.ocsjs.com/docs/script'; } return; } const { start, definedProjects, CommonProject, RenderScript } = OCS; const infos = GM_info; (function () { 'use strict'; const projects = definedProjects(); // 运行脚本 start({ projects: projects, renderConfig: { renderScript: RenderScript, styles: [STYLE], defaultPanelName: CommonProject.scripts.guide.namespace, title: `OCS-全域名通用版-${infos.script.version}` }, updatePage: GM_info.scriptHandler === 'Tampermonkey' ? 'https://greasyfork.org/zh-CN/scripts/481438' : 'https://scriptcat.org/zh-CN/script-show-page/1398' }); })(); ================================================ FILE: packages/scripts/entry.dev.js ================================================ /* eslint-disable no-undef */ /// // 环境检测 if ( [ 'GM_getTab', 'GM_saveTab', 'GM_setValue', 'GM_getValue', 'unsafeWindow', 'GM_listValues', 'GM_deleteValue', 'GM_notification', 'GM_xmlhttpRequest', 'GM_getResourceText', 'GM_addValueChangeListener', 'GM_removeValueChangeListener' ].some((api) => typeof Reflect.get(globalThis, api) === 'undefined') ) { const open = confirm( `OCS网课脚本不支持当前的脚本管理器(${GM_info.scriptHandler})。` + '请前往 https://docs.ocsjs.com/docs/script 下载指定的脚本管理器,例如 “Scriptcat 脚本猫” 或者 “Tampermonkey 油猴”' ); if (open) { window.location.href = 'https://docs.ocsjs.com/docs/script'; } return; } const { start, definedProjects, CommonProject, RenderScript } = OCS; const infos = GM_info; (function () { 'use strict'; const projects = definedProjects(); // 运行脚本 start({ projects: projects, renderConfig: { renderScript: RenderScript, styles: [GM_getResourceText('STYLE')], defaultPanelName: CommonProject.scripts.guide.namespace, title: `OCS DEV-${infos.script.version}` }, updatePage: 'https://docs.ocsjs.com/docs/update' }); })(); ================================================ FILE: packages/scripts/entry.js ================================================ /* eslint-disable no-undef */ /// // 环境检测 if ( [ 'GM_getTab', 'GM_saveTab', 'GM_setValue', 'GM_getValue', 'unsafeWindow', 'GM_listValues', 'GM_deleteValue', 'GM_notification', 'GM_xmlhttpRequest', 'GM_getResourceText', 'GM_addValueChangeListener', 'GM_removeValueChangeListener' ].some((api) => typeof Reflect.get(globalThis, api) === 'undefined') ) { const open = confirm( `OCS网课脚本不支持当前的脚本管理器(${GM_info.scriptHandler})。` + '请前往 https://docs.ocsjs.com/docs/script 下载指定的脚本管理器,例如 “Scriptcat 脚本猫” 或者 “Tampermonkey 油猴”' ); if (open) { window.location.href = 'https://docs.ocsjs.com/docs/script'; } return; } const { start, definedProjects, CommonProject, RenderScript } = OCS; const infos = GM_info; (function () { 'use strict'; const projects = definedProjects(); // 运行脚本 start({ projects: projects, renderConfig: { renderScript: RenderScript, styles: [STYLE], defaultPanelName: CommonProject.scripts.guide.namespace, title: `OCS-${infos.script.version}` }, updatePage: 'https://docs.ocsjs.com/docs/update' }); })(); ================================================ FILE: packages/scripts/global.d.ts ================================================ import * as Core from './src/index'; declare global { export declare const OCS: typeof Core; export declare const STYLE: string; } declare module 'dom-to-image-more' { import domToImage = require('dom-to-image'); export = domToImage; } ================================================ FILE: packages/scripts/global.js ================================================ /// /// ================================================ FILE: packages/scripts/package.json ================================================ { "name": "@ocsjs/scripts", "version": "4.0.1", "description": "core package of ocs", "main": "./lib/index.js", "types": "./lib/index.d.ts", "files": [ "lib" ], "repository": { "type": "git", "url": "git+https://github.com/ocsjs/ocsjs.git" }, "keywords": [ "ocs", "script", "playwright", "puppeteer", "electron", "vue", "ant-design-vue", "typescript" ], "author": "enncy", "license": "MIT", "bugs": { "url": "https://github.com/ocsjs/ocsjs/issues" }, "homepage": "https://github.com/ocsjs/ocsjs#readme", "devDependencies": { "@types/marked": "^4.0.8", "@types/md5": "^2.3.2", "@types/semver": "^7.5.0", "@types/tampermonkey": "^4.0.5", "dotenv": "^16.0.3", "rollup-plugin-visualizer": "^5.9.0", "vite": "^2.9.15", "vite-plugin-banner": "^0.6.1" }, "dependencies": { "@ocsjs/core": "workspace:*", "@rollup/plugin-commonjs": "^24.0.0", "browser-env": "^3.3.0", "easy-us": "^0.0.60", "lodash": "^4.17.21", "markdown-it": "^13.0.1", "marked": "^4.2.12", "md5": "^2.3.0", "semver": "^7.3.5", "typr.js": "^1.0.0" } } ================================================ FILE: packages/scripts/src/elements/search.infos.ts ================================================ import { SimplifyWorkResult, splitAnswer, $, QuestionTypes } from '@ocsjs/core'; import { $ui, h } from 'easy-us'; import { createQuestionTitleExtra } from '../utils'; /** * 判断是否有图片链接,如果有则使用 标签包裹,但如果已经被 包裹则不处理 */ const transformImgLinkOfQuestion = (question: string) => { // 防止题目中包含 img 标签元素,所以先统一吧 img 标签替换成链接 const dom = new DOMParser().parseFromString(question, 'text/html'); for (const img of Array.from(dom.querySelectorAll('img'))) { img.replaceWith(img.src); } // 最后将所有图片链接替换成 img 标签 return dom.documentElement.innerHTML.replace(/https?:\/\/.+?\.(png|jpg|jpeg|gif)/g, (img) => { return ``; }); }; /** * 搜索结果元素 */ export class SearchInfosElement extends HTMLElement { /** 搜索结果 [题目,答案] */ infos: SimplifyWorkResult['searchInfos'] = []; /** 当前的题目 */ question: string = ''; type: QuestionTypes; connectedCallback() { const question = transformImgLinkOfQuestion(this.question || '无'); const type_text = { single: '单选题', multiple: '多选题', judgement: '判断题', completion: '填空题' }; const type_label = this.type ? Reflect.get(type_text, this.type) : ''; this.append( h( 'div', [ ...(type_label ? [h('span', { className: 'search-result-question-type' }, type_label)] : []), h('span', { innerHTML: question }), createQuestionTitleExtra(this.question) ], (div) => { div.className = 'search-info-title'; } ) ); this.append( ...this.infos.map((info) => { return h('details', { open: true, className: 'search-info-details' }, [ h('summary', [h('a', { href: info.homepage, innerText: info.name, target: '_blank' })]), ...(info.error ? /** 显示错误信息 */ [h('span', { className: 'error' }, [info.error || '网络错误或者未知错误'])] : /** 显示结果列表 */ [] ).concat([ ...info.results.map((ans) => { const title = transformImgLinkOfQuestion(ans[0] || this.question || '无'); const answer = transformImgLinkOfQuestion(ans[1] || '无'); const extra_data = JSON.parse(JSON.stringify(ans[2] || {})); if (extra_data.ai) { extra_data.tags = extra_data.tags || []; extra_data.tags.push({ text: 'AI', title: '此答案由 AI 生成,仅供参考', color: 'blue' }); } if (extra_data.cache) { extra_data.tags = extra_data.tags || []; extra_data.tags.push({ text: '题库缓存', title: '此答案来自本地缓存,由在线题库搜索后保存在本地。\n- 清空缓存:请前往通用-拓展应用-题库缓存\n- 关闭缓存:请前往通用-全局设置-题库缓存', color: 'gray' }); } return h('div', { className: 'search-result' }, [ /** 题目 */ h('div', { className: 'question' }, [h('span', { innerHTML: title })]), /** 答案 */ h('div', { className: 'answer' }, [ h('span', '答案:'), ...(extra_data.tags ? extra_data.tags.map((tag: { text: string; title: string; color: string }) => $ui.tooltip( h('span', { className: 'search-result-answer-tag ' + tag.color, innerHTML: tag.text, title: tag.title, dataset: { title: tag.title } }) ) ) : []), ...splitAnswer(answer).map((a) => h('code', { innerHTML: a })) ]) ]); }) ]) ]); }) ); $.onresize(this, (sr) => { sr.style.maxHeight = window.innerHeight / 2 + 'px'; }); } } ================================================ FILE: packages/scripts/src/index.ts ================================================ import { Project } from 'easy-us'; import { CommonProject } from './projects/common'; import { ZHSProject } from './projects/zhs'; import { CXProject } from './projects/cx'; import { BackgroundProject } from './projects/background'; import { IcveMoocProject } from './projects/icve'; import { ZJYProject } from './projects/zjy'; import { ICourseProject } from './projects/icourse'; import { YKTProject } from './projects/yuketang'; /** 导出所有的 OCS 核心模块 */ export * from '@ocsjs/core'; /** 导出启动函数,以及全局对象 */ export { start, $elements, $store } from 'easy-us'; /** 导出本包的核心脚本工程,开发者调试的时候使用 BackgroundProject 中的注入脚本,访问脚本 window 上下文 */ export { BackgroundProject } from './projects/background'; export { CommonProject } from './projects/common'; export { ZHSProject } from './projects/zhs'; export { CXProject } from './projects/cx'; export { ZJYProject } from './projects/zjy'; export { IcveMoocProject } from './projects/icve'; export { ICourseProject } from './projects/icourse'; export { YKTProject } from './projects/yuketang'; export { RenderScript } from './render'; export function definedProjects(): Project[] { return [ ZHSProject, CXProject, IcveMoocProject, ZJYProject, ICourseProject, YKTProject, CommonProject, BackgroundProject ]; } ================================================ FILE: packages/scripts/src/projects/background.ts ================================================ import { RemotePlaywright, request } from '@ocsjs/core'; import { $ui, $gm, $message, $modal, $store, Project, Script, StoreListenerType, h, $, MessageElement, $menu } from 'easy-us'; import semver_gt from 'semver/functions/gt'; import semver_valid from 'semver/functions/valid'; import { CommonProject } from './common'; import { CXProject, definedProjects, ICourseProject, IcveMoocProject, YKTProject, ZHSProject, ZJYProject } from '..'; import { RenderScript } from '../render'; import { SearchInfosElement } from '../elements/search.infos'; import { $render } from '../utils/render'; const state = { console: { listenerIds: { logs: 0 as StoreListenerType } }, app: { listenerIds: { sync: 0 as StoreListenerType, connected: 0 as StoreListenerType, closeSync: 0 as StoreListenerType } } }; export type LogType = 'log' | 'info' | 'debug' | 'warn' | 'error'; type RequestList = { id: string; url: string; method: string; type: string; data: any; headers: any; response?: string; error?: string; time: number; }[]; /** 后台进程,处理与PC软件端的通讯,以及其他后台操作 */ export const BackgroundProject = Project.create({ name: '后台', domains: [], scripts: { elementRegister: new Script({ name: '🔗 元素注册', hideInPanel: true, matches: [['所有页面', /.*/]], onstart() { // 注册自定义元素 $.loadCustomElements([SearchInfosElement]); } }), console: new Script({ name: '📄 日志输出', matches: [['所有', /.*/]], namespace: 'render.console', configs: { logs: { defaultValue: [] as { type: LogType; content: string; time: number; stack: string }[] } }, onrender({ panel }) { const getTypeDesc = (type: LogType) => type === 'info' ? '信息' : type === 'error' ? '错误' : type === 'warn' ? '警告' : type === 'debug' ? '调试' : '日志'; const createLog = (log: { type: LogType; content: string; time: number; stack: string }) => { const date = new Date(log.time); const item = h( 'div', { title: '双击复制日志信息', className: 'item' }, [ h( 'span', { className: 'time' }, `${date.getHours().toFixed(0).padStart(2, '0')}:${date.getMinutes().toFixed(0).padStart(2, '0')} ` ), h('span', { className: log.type }, `[${getTypeDesc(log.type)}]`), h('span', ':' + log.content) ] ); item.addEventListener('dblclick', () => { navigator.clipboard.writeText( Object.keys(log) .map((k) => `${k}: ${(log as any)[k]}`) .join('\n') ); }); return item; }; const showLogs = () => { const div = h('div', { className: 'card console' }); const logs = this.cfg.logs.map((log) => createLog(log)); if (logs.length) { div.replaceChildren(...logs); } else { div.replaceChildren( h('div', '暂无任何日志', (div) => { div.style.textAlign = 'center'; }) ); } return { div, logs }; }; /** * 判断滚动条是否滚到底部 */ const isScrollBottom = (div: HTMLElement) => { const { scrollHeight, scrollTop, clientHeight } = div; return scrollTop + clientHeight + 50 > scrollHeight; }; const { div, logs } = showLogs(); this.offConfigChange(state.console.listenerIds.logs); state.console.listenerIds.logs = this.onConfigChange('logs', (logs) => { const log = createLog(logs[logs.length - 1]); div.append(log); setTimeout(() => { if (isScrollBottom(div)) { log.scrollIntoView(); } }, 10); }); const show = () => { panel.body.replaceChildren(div); setTimeout(() => { logs[logs.length - 1]?.scrollIntoView(); }, 10); }; show(); } }), appConfigSync: new Script({ name: '🔄️ 软件配置同步', namespace: 'background.app', matches: [['所有页面', /./]], // 如果是在OCS软件中则不显示此页面 hideInPanel: $gm.getInfos() === undefined, configs: { notes: { defaultValue: $ui.notes([ [ h('span', [ '如果您使用', h('a', { href: 'https://docs.ocsjs.com/docs/app', target: '_blank' }, 'OCS桌面软件'), '启动浏览器,并使用此脚本,' ]), '我们会同步软件中的配置到此脚本上,方便多个浏览器的管理。', '窗口设置以及后台面板所有设置不会进行同步。' ], '如果不是,您可以忽略此脚本。' ]).outerHTML }, sync_status: { defaultValue: 'unconnect' as 'not_playwright_environment' | 'unconnect' | 'not_open_sync' | 'synced' }, closeSync: { defaultValue: false, label: '关闭同步', attrs: { type: 'checkbox' } } }, onrender({ panel }) { // 同步面板不会被锁定 panel.lockWrapper.remove(); panel.configsContainer.classList.remove('lock'); const update = () => { if (this.cfg.closeSync) { const tip = h('div', { className: 'notes card' }, ['已关闭同步。']); panel.body.replaceChildren(h('hr'), tip); } else if (this.cfg.sync_status === 'synced') { const tip = h('div', { className: 'notes card' }, [`已成功同步软件中的配置.`]); panel.body.replaceChildren(h('hr'), tip); } else if (this.cfg.sync_status === 'unconnect') { const tip = h('div', { className: 'notes card' }, ['未同步软件配置,可能是桌面软件未启动。']); panel.body.replaceChildren(h('hr'), tip); } else if (this.cfg.sync_status === 'not_playwright_environment') { const tip = h('div', { className: 'notes card' }, ['当前浏览器不是由桌面端软件启动,无法同步配置。']); panel.body.replaceChildren(h('hr'), tip); } else if (this.cfg.sync_status === 'not_open_sync') { const tip = h('div', { className: 'notes card' }, ['桌面端软件未开启配置同步功能']); panel.body.replaceChildren(h('hr'), tip); } else if (this.cfg.sync_status === 'empty_config') { const tip = h('div', { className: 'notes card' }, ['已成功连接到软件,但配置为空。']); panel.body.replaceChildren(h('hr'), tip); } else { const tip = h('div', { className: 'notes card' }, ['同步状态未知,请稍后重试。']); panel.body.replaceChildren(h('hr'), tip); } }; update(); this.offConfigChange(state.app.listenerIds.sync); this.offConfigChange(state.app.listenerIds.connected); this.offConfigChange(state.app.listenerIds.closeSync); state.app.listenerIds.connected = this.onConfigChange('sync_status', update); state.app.listenerIds.closeSync = this.onConfigChange('closeSync', (closeSync) => { if (closeSync) { this.cfg.sync_status = 'not_open_sync'; $message.success({ content: '已关闭同步,刷新页面后生效' }); } }); }, async onactive() { if ($.isInTopWindow()) { if (this.cfg.closeSync) { $console.log('配置同步已关闭'); return; } this.cfg.sync_status = 'unconnect'; try { const res = await request('http://localhost:15319/browser', { type: 'GM_xmlhttpRequest', method: 'get', responseType: 'json' }); if (!res) { this.cfg.sync_status = 'unconnect'; return; } const open_sync = await request('http://localhost:15319/is-browser-config-sync', { type: 'GM_xmlhttpRequest', method: 'get', responseType: 'text' }); if (open_sync !== 'true') { this.cfg.sync_status = 'not_open_sync'; return; } if (Object.keys(res).length === 0) { this.cfg.sync_status = 'not_open_sync'; return; } // 自OCS软件 2.8.21 版本后特殊字段,用于标记不进行同步的字段 // 通过OCS playwright 启动的浏览器会自动返回数据 // 不使用 http 防止某些 Content-Security-Policy 限制 const environment_res = await request('/ocs-environment', { type: 'fetch', method: 'get' }); const environment = environment_res?.environment; if (!environment || environment !== 'playwright') { this.cfg.sync_status = 'not_playwright_environment'; return; } // 排除几个特殊的设置 for (const key in res) { if (Object.prototype.hasOwnProperty.call(res, key)) { // 排除渲染脚本的设置 if (RenderScript.namespace && key.startsWith(RenderScript.namespace)) { Reflect.deleteProperty(res, key); } // 排除后台脚本的设置 for (const scriptKey in BackgroundProject.scripts) { if (Object.prototype.hasOwnProperty.call(BackgroundProject.scripts, scriptKey)) { const script: Script = Reflect.get(BackgroundProject.scripts, scriptKey); if (script.namespace && key.startsWith(script.namespace)) { Reflect.deleteProperty(res, key); } } } } } // 排除那些不用同步的配置 for (const project of definedProjects()) { for (const key in project.scripts) { if (Object.prototype.hasOwnProperty.call(project.scripts, key)) { const script = project.scripts[key]; for (const ck in script.configs) { if (Object.prototype.hasOwnProperty.call(script.configs, ck)) { if (script.configs[ck].extra?.appConfigSync === false) { Reflect.deleteProperty(res, $.namespaceKey(script.namespace, ck)); } } } } } } // 同步所有的配置 for (const key in res) { if (Object.prototype.hasOwnProperty.call(res, key)) { $store.set(key, res[key]); } } // 锁定面板 for (const project of definedProjects()) { // 排除后台脚本的锁定 if (project.name === BackgroundProject.name) { continue; } for (const key in project.scripts) { if (Object.prototype.hasOwnProperty.call(project.scripts, key)) { const script = project.scripts[key]; const originalRender = script.onrender; // 重新定义渲染函数。在渲染后添加锁定面板的代码 script.onrender = ({ panel, header }) => { originalRender?.({ panel, header }); if (panel.configsContainer.children.length) { panel.configsContainer.classList.add('lock'); panel.lockWrapper.style.width = (panel.configsContainer.clientWidth ?? panel.clientWidth) + 'px'; panel.lockWrapper.style.height = (panel.configsContainer.clientHeight ?? panel.clientHeight) + 'px'; panel.configsContainer.prepend(panel.lockWrapper); panel.lockWrapper.title = '🚫已同步OCS桌面版软件配置,如需修改请在桌面版软件的左侧栏设置-通用设置-OCS配置,中进行修改。\n\n或者前往脚本悬浮窗:后台-软件配置同步 关闭配置同步功能。\n\n可双击强制修改,并关闭同步配置'; panel.lockWrapper = $ui.tooltip(panel.lockWrapper); panel.lockWrapper.addEventListener('dblclick', () => { panel.configsContainer.classList.remove('lock'); panel.lockWrapper.remove(); script.onrender = originalRender; $message.warn({ content: '已解除配置同步,可正常修改配置。想开启同步请前往:后台-软件配置同步', duration: 10 }); this.cfg.closeSync = true; if (script.panel && script.header) { script.onrender?.({ panel: script.panel, header: script.header }); } }); } }; // 重新执行渲染 if (script.panel && script.header) { script.onrender({ panel: script.panel, header: script.header }); } } } } this.cfg.sync_status = 'synced'; } catch (e) { console.error(e); this.cfg.sync_status = 'unconnect'; } } } }), update: new Script({ name: '📥 更新模块', matches: [['所有页面', /.*/]], namespace: 'background.update', configs: { notes: { defaultValue: '脚本自动更新模块,如果有新的版本会自动通知。' }, autoNotify: { defaultValue: true, label: '开启更新通知', attrs: { type: 'checkbox', title: '当有最新的版本时自动弹窗通知,默认开启' } }, notToday: { defaultValue: -1 }, ignoreVersions: { defaultValue: [] as string[] } }, methods() { return { getLastVersion: async () => { return (await request('https://cdn.ocsjs.com/ocs-version.json?t=' + Date.now(), { method: 'get', type: 'GM_xmlhttpRequest' })) as { 'last-version': string; resource: Record; notes: string[] }; } }; }, async onrender({ panel }) { const version = await this.methods.getLastVersion(); const infos = $gm.getInfos(); if (!infos) { return; } const changeLog = h('button', { className: 'base-style-button-secondary' }, '📄查看更新日志'); changeLog.onclick = () => CommonProject.scripts.apps.methods.showChangelog(); const updatePage = this.startConfig?.updatePage || ''; panel.body.replaceChildren( h('div', { className: 'card' }, [ h('hr'), h('div', ['最新版本:' + version['last-version'] + ' - ', changeLog]), h('hr'), h('div', '当前版本:' + infos.script.version), h('div', '脚本管理器:' + infos.scriptHandler), h('div', ['脚本更新链接:', h('a', { target: '_blank', href: updatePage }, [updatePage || '无'])]) ]) ); console.log('versions', { notToday: this.cfg.notToday, ignoreVersions: this.cfg.ignoreVersions, version: version }); }, oncomplete() { if (this.cfg.autoNotify && $.isInTopWindow()) { if (this.cfg.notToday === -1 || this.cfg.notToday !== new Date().getDate()) { const infos = $gm.getInfos(); if (infos) { // 版本表达式验证 if (!!semver_valid(infos.script.version) === false) { $message.error(`当前版本号 (${infos.script.version}) 不符合semver版本书写规范,请重新修改版本。`); return; } // 避免阻挡用户操作,这里等页面运行一段时间后再进行更新提示 setTimeout(async () => { const version = await this.methods.getLastVersion(); const last = version['last-version']; if ( // 跳过主动忽略的版本 this.cfg.ignoreVersions.includes(last) === false && // 版本比较 semver_gt(last, infos.script.version) ) { const updatePage = this.startConfig?.updatePage || ''; const modal = $modal.confirm({ maskCloseable: false, width: 600, content: $ui.notes([`检测到新版本发布 ${last} :`, [...(version.notes || [])]]), footer: h('div', [ h('button', { className: 'base-style-button-secondary', innerText: '跳过此版本' }, (btn) => { btn.onclick = () => { this.cfg.ignoreVersions = [...this.cfg.ignoreVersions, last]; modal?.remove(); }; }), h('button', { className: 'base-style-button-secondary', innerText: '今日不再提示' }, (btn) => { btn.onclick = () => { this.cfg.notToday = new Date().getDate(); modal?.remove(); }; }), h('button', { className: 'base-style-button', innerText: '前往更新' }, (btn) => { btn.onclick = () => { if (updatePage) { window.open(updatePage, '_blank'); modal?.remove(); } else { $message.error({ content: '无法前往更新页面,更新链接为空' }); } }; }) ]) }); } }, 5 * 1000); } } } } }), dev: new Script({ name: '🛠️ 开发者调试', namespace: 'background.dev', matches: [['所有页面', /./]], configs: { notes: { defaultValue: '开发人员调试用。
注入OCS_CONTEXT全局变量。用户可忽略此页面。' }, show_debug_cursor: { defaultValue: true, label: '软件辅助点击时显示鼠标位置', attrs: { type: 'checkbox' } }, enable_answerer_debug: { defaultValue: true, label: '开启答题日志输出', attrs: { type: 'checkbox' } } }, methods() { return { getRemotePlaywrightCurrentPage: () => { return RemotePlaywright.getRemotePage(this.cfg.show_debug_cursor, console.debug); } }; }, onrender({ panel }) { const injectBtn = h('button', { className: 'base-style-button' }, '点击注入全局变量'); injectBtn.addEventListener('click', () => { $gm.unsafeWindow.OCS_CONTEXT = self; }); const showTabDataBtn = h('button', { className: 'base-style-button' }, '显示Tab存储'); $gm.getTab((tab) => { const els: HTMLElement[] = []; for (const key in tab) { if (Object.prototype.hasOwnProperty.call(tab, key)) { els.push(h('div', [h('b', key + ' : '), h('code', JSON.stringify(tab[key]))])); } } showTabDataBtn.addEventListener('click', () => { $modal.simple({ content: h('div', els), width: window.document.documentElement.clientWidth / 2 }); }); }); panel.body.replaceChildren(h('div', { className: 'card' }, [h('hr'), injectBtn, showTabDataBtn])); } }), appLoginHelper: new Script({ name: '软件登录辅助', matches: [ ['超星登录', 'passport2.chaoxing.com/login'], ['智慧树登录', 'passport.zhihuishu.com/login'], ['职教云登录', 'zjy2.icve.com.cn/portal/login.html'], ['智慧职教登录', 'sso.icve.com.cn/sso/auth'] ], hideInPanel: true, oncomplete() { // 将面板移动至左侧顶部,防止挡住软件登录 if ($.isInTopWindow()) { $render.moveToEdge(); } } }), errorHandle: new Script({ name: '全局错误捕获', matches: [['', /.*/]], hideInPanel: true, onstart() { const projects = definedProjects(); for (const project of projects) { for (const key in project.scripts) { if (Object.prototype.hasOwnProperty.call(project.scripts, key)) { const script = project.scripts[key]; script.on('scripterror', (err) => { const msg = `[${project.name} - ${script.name}] : ${err}`; console.error(msg); $console.error(msg); }); } } } } }), requestList: new Script({ name: '📄 请求记录', matches: [['', /.*/]], priority: 99, configs: { notes: { defaultValue: $ui.notes([ '开发人员请求调试记录页面,小白勿入,最多只记录最近的100个请求数据', '可打开F12控制台查看请求日志,或者下方的请求列表' ]).outerHTML }, enable: { label: '开启请求记录', attrs: { type: 'checkbox' }, defaultValue: false }, methodFilter: { label: '方法过滤', tag: 'select', attrs: { placeholder: '选择选项' }, options: [['none', '无'], ['GET'], ['POST'], ['OPTIONS'], ['HEAD']], defaultValue: 'none' }, typeFilter: { label: '类型过滤', tag: 'select', attrs: { placeholder: '选择选项' }, options: [ ['none', '无'], ['gmxhr', '油猴API请求(gmxhr)'], ['fetch', '普通请求(fetch)'] ], defaultValue: 'none' }, searchValue: { label: '内容搜索', attrs: { placeholder: '搜索 URL/请求体/响应' }, defaultValue: '' }, list: { defaultValue: [] as RequestList } }, methods() { const render = (list: RequestList) => { this.panel?.body.replaceChildren(); this.panel?.body.append( h('div', { className: 'card' }, [ h('div', { style: { padding: '8px 0px', textAlign: 'end' } }, [ h( 'button', { className: 'base-style-button-secondary', style: { marginRight: '12px' }, innerText: '🗑️清空记录' }, (btn) => { btn.onclick = () => { this.cfg.list = []; render(this.cfg.list); }; } ), h('button', { className: 'base-style-button', innerText: '🔍执行搜索' }, (btn) => { btn.onclick = () => { if ( this.cfg.methodFilter === 'none' && this.cfg.typeFilter === 'none' && this.cfg.searchValue === '' ) { render(this.cfg.list); } else { const list = this.cfg.list .filter((item) => { if ( this.cfg.methodFilter !== 'none' && item.method.toLowerCase() !== this.cfg.methodFilter.toLowerCase() ) { return false; } return true; }) .filter((item) => { if (this.cfg.typeFilter !== 'none' && item.type !== this.cfg.typeFilter) { return false; } return true; }) .filter((item) => { if ( (this.cfg.searchValue && item.url.includes(this.cfg.searchValue)) || item.data?.includes(this.cfg.searchValue) || item.response?.includes(this.cfg.searchValue) ) { return true; } return false; }); render(list); } }; }) ]), h( 'div', { style: { backgroundColor: '#292929', overflow: 'auto', maxHeight: window.innerHeight / 2 + 'px' } }, [ ...(list.length === 0 ? [h('div', { style: { color: 'white', textAlign: 'center' } }, '暂无数据')] : []), ...list.map((item) => // @ts-ignore h( 'div', { title: Object.entries(item) .map(([key, val]) => key === 'time' ? `${key} : ${new Date(val).toLocaleString().replace(/\//g, '-')}` : `${key} : ${val}` ) .join('\n'), style: { maxWidth: '800px', padding: '4px 0px', margin: '4px 0px', // @ts-ignore textWrap: 'nowrap' } }, [ h('div', [ h('span', { style: { marginRight: '8px' } }, new Date(item.time).toLocaleTimeString()), h( 'span', { style: { backgroundColor: '#2196f3a3', color: '#ececec', marginRight: '8px', padding: '0px 2px' } }, item.method ), h( 'span', { style: { color: item.response ? '#4eb74e' : '#eb6262', marginRight: '8px' } }, '●' ), h( 'div', { style: { display: 'inline-block', color: '#ececec' } }, item.url ? (item.url.length > 100 ? item.url.slice(0, 100) + '...' : item.url) : '-' ) ]), h( 'div', { style: { overflow: 'hidden', fontSize: '12px', color: '#8f8f8f' } }, item.data ? 'data: ' + item.data : '' ), h( 'div', { style: { overflow: 'hidden', fontSize: '12px', color: '#8f8f8f' } }, item.response ? 'resp: ' + item.response : item.error ? 'err : ' + item.error : '' ) ] ) ) ] ) ]) ); }; return { render: render }; }, onrender() { this.methods.render(this.cfg.list); }, onstart() { // 兼容其他环境 if ($gm.isInGMContext() === false) { return; } /* global GM_xmlhttpRequest RequestInfo RequestInit */ /* eslint-disable no-global-assign */ const gmRequest = GM_xmlhttpRequest; const originalFetch = fetch; const getId = () => Math.random().toString(16).slice(2); const addRecord = (item: (typeof this.cfg.list)[number]) => { this.cfg.list = [item, ...this.cfg.list]; if (this.cfg.list.length > 100) { this.cfg.list = this.cfg.list.slice(0, 100); } }; const setItem = (id: string, response: string | undefined, error: string | undefined) => { const list: typeof this.cfg.list = JSON.parse(JSON.stringify(this.cfg.list)); const index = list.findIndex((item) => item.id === id); if (index !== -1) { list[index].response = response; list[index].error = error; } this.cfg.list = list; }; // @ts-ignore GM_xmlhttpRequest = (details: any) => { if (this.cfg.enable) { const id = getId(); const data = { id: id, url: details.url, method: details.method || 'unknown', type: 'gmxhr', data: details.data, headers: details.headers, response: '', error: '', time: Date.now() }; addRecord(data); const onload = details.onload; const onerror = details.onerror; details.onload = function (response: any) { setItem(id, response.responseText, ''); data.response = details.responseType === 'json' ? response.response : response.responseText; console.log('%c [请求成功]', 'color: green; font-weight: bold', data.url, data); onload?.apply(this, [response]); }; details.onerror = function (response: any) { setItem(id, '', response.error); data.error = response.error; console.log('%c [请求失败]', 'color: red; font-weight: bold', data.url, data); onerror?.apply(this, [response]); }; } return gmRequest.apply(this, [details as any]); }; // @ts-ignore fetch = (input: URL | RequestInfo, init?: RequestInit | undefined) => { if (this.cfg.enable) { const id = getId(); const data = { id: id, url: typeof input === 'string' ? input : input instanceof URL ? input.href : input.url, method: init?.method || 'unknown', type: 'fetch', data: init?.body, headers: init?.headers, response: '', error: '', time: Date.now() }; addRecord(data); const res = originalFetch.apply(this, [input, init]); res .then((result) => { return result.clone().text(); }) .then((result) => { setItem(id, result, ''); data.response = result; console.log('%c [请求成功]', 'color: green; font-weight: bold', data.url, data); }); res.catch((err) => { setItem(id, '', String(err)); data.error = String(err); console.log('%c [请求失败]', 'color: red; font-weight: bold', data.url, data); }); return res; } else { return originalFetch.apply(this, [input, init]); } }; } }), environmentDetect: new Script({ name: '🤖 环境检测', matches: [['所有页面', /.*/]], hideInPanel: true, oncomplete() { if (self !== top) return; const matches = [ CXProject.scripts.studyDispatcher.matches, ZHSProject.scripts['gxk-study'].matches, ZHSProject.scripts.hike.matches, ZHSProject.scripts['smart-study'].matches, ZHSProject.scripts['wisdom-study'].matches, ZHSProject.scripts['xnk-study'].matches, ICourseProject.scripts.study.matches, IcveMoocProject.scripts.study.matches, ZJYProject.scripts.study.matches ] .flat() .map((m) => (Array.isArray(m) ? m[1] : m)); const url = window.location.href; const match = matches.some((regex) => { return typeof regex === 'string' ? url.includes(regex) : regex.test(url); }); if (!match) { return; } let messageElement: MessageElement | undefined; visibleDetect(); function visibleDetect() { setTimeout(() => { if (!messageElement?.isConnected) messageElement = undefined; if (document.visibilityState === 'hidden' && !messageElement) { messageElement = $message.warn({ content: '⚠️检测到浏览器最小化/切屏,脚本可能无法正常运行,请保持网课页面在前台!(如果您正在全屏游戏中可以忽略此警告)', duration: 0 }); } visibleDetect(); }, 1000); } } }), menus: new Script({ name: '📁 菜单管理', hideInPanel: true, matches: [['所有页面', /.*/]], async onactive() { const currentStudyScript = [ [CXProject.scripts.studyDispatcher, CXProject.scripts.study], CXProject.scripts.work, CXProject.scripts.autoRead, ZHSProject.scripts['gxk-study'], ZHSProject.scripts['xnk-study'], ZHSProject.scripts.hike, ZHSProject.scripts['smart-study'], ZHSProject.scripts['wisdom-study'], ZHSProject.scripts['xnk-study'], ZHSProject.scripts['gxk-work'], ZHSProject.scripts['xnk-work'], ZHSProject.scripts['hike-work'], ZHSProject.scripts['smart-work'], ZHSProject.scripts['smart-exam'], ZHSProject.scripts['xnk-work'], [ICourseProject.scripts.dispatcher, ICourseProject.scripts.study], ICourseProject.scripts.work, [ZJYProject.scripts.dispatcher, ZJYProject.scripts.study], ZJYProject.scripts.work, IcveMoocProject.scripts.study, IcveMoocProject.scripts.work, YKTProject.scripts.ai ] .map((m) => { const url = window.location.href; const data = { matches: Array.isArray(m) ? m[0].matches : m.matches, target: Array.isArray(m) ? m[1] : m }; if ( data.matches.some((regexp) => { const r = Array.isArray(regexp) ? regexp[1] : regexp; return typeof r === 'string' ? url.includes(r) : r.test(url); }) ) { return data.target; } return undefined; }) .find((m) => m !== undefined); // 注册快捷菜单 await $menu('🏠', { scriptPanelLink: CommonProject.scripts.guide }); if (currentStudyScript) await $menu('🖥️', { scriptPanelLink: currentStudyScript }); await $menu('🔎', { scriptPanelLink: CommonProject.scripts.workResults }); await $menu('⚙️', { scriptPanelLink: CommonProject.scripts.settings }); await $menu('📥', { scriptPanelLink: BackgroundProject.scripts.update }); await $menu('📄', { scriptPanelLink: BackgroundProject.scripts.console }); } }) } }); type Console = Record void>; /** 日志对象,存储日志并显示在日志面板 */ export const $console: Console = new Proxy({} as Console, { get(target, key) { return (...msg: any[]) => { let logs = BackgroundProject.scripts.console.cfg.logs; if (logs.length > 50) { logs = logs.slice(-50); } const stack_str = Error().stack || ''; // 简化堆栈信息 const stacks = stack_str .replace('Error', '') .match(/at (.*) \(.+:\/\/.+:(.+):(.+)\)/g) ?.map((s) => { const match = s.match(/at (.*) \(.+:\/\/.+:(.+):(.+)\)/) || []; return [match[1], match[2], match[3]]; }); logs = logs.concat({ type: key.toString() as LogType, content: msg.join(' '), time: Date.now(), stack: JSON.stringify([stack_str.split('\n')[0], ...(stacks || [])]) }); BackgroundProject.scripts.console.cfg.logs = logs; }; } }); ================================================ FILE: packages/scripts/src/projects/common.ts ================================================ import debounce from 'lodash/debounce'; import { defaultAnswerWrapperHandler, AnswerWrapperParser, request, SimplifyWorkResult, $, WorkUploadType, AnswerWrapperHandlerConfig } from '@ocsjs/core'; import { $message, h, $gm, $store, Project, Script, $modal, StoreListenerType, $ui } from 'easy-us'; import type { AnswerMatchMode, AnswererWrapper, SearchInformation } from '@ocsjs/core'; import { CXProject, ICourseProject, IcveMoocProject, ZHSProject, ZJYProject } from '../index'; import { markdown } from '../utils/markdown'; import { enableCopy } from '../utils'; import { SearchInfosElement } from '../elements/search.infos'; import { RenderScript } from '../render'; import { dropdownStyle } from '../utils/configs'; const TAB_WORK_RESULTS_KEY = 'common.work-results.results'; const state = { workResult: { /** * 题目位置同步处理器 */ questionPositionSyncHandler: { cx: (index: number) => { const el = document.querySelectorAll('[id*="sigleQuestionDiv"], .questionLi')?.item(index); if (el) { window.scrollTo({ top: el.getBoundingClientRect().top + window.pageYOffset - 50, behavior: 'smooth' }); } }, 'zhs-gxk': (index: number) => { document.querySelectorAll('.answerCard_list ul li').item(index)?.click(); }, 'zhs-xnk': (index: number) => { document.querySelectorAll('.jobclassallnumber-div li[questionid]').item(index)?.click(); }, 'zhs-smart': (index: number) => { document.querySelectorAll('[role="treeitem"] .font-sec-style-node').item(index)?.click(); }, 'zhs-fusion': (index: number) => { document.querySelectorAll('.right-box .list .item').item(index)?.click(); }, 'zhs-hike': (index: number) => { document.querySelectorAll('.q_main_right .card_ul .card_li').item(index)?.click(); }, icve: (index: number) => { document.querySelectorAll(`.sheet_nums [id*="sheetSeq"]`).item(index)?.click(); }, zjy: (index: number) => { document .querySelectorAll('.subjectDet') .item(index) ?.scrollIntoView({ behavior: 'smooth', block: 'center' }); }, icourse: (index: number) => { document .querySelectorAll('.u-questionItem,[class*=questionBody]') .item(index) ?.scrollIntoView({ behavior: 'smooth', block: 'center' }); } } }, setting: { listenerIds: { aw: 0 as StoreListenerType } } }; /** * 题库缓存类型 */ type QuestionCache = { title: string; answer: string; from: string; homepage: string; ai?: boolean }; export const CommonProject = Project.create({ name: '通用', domains: [], scripts: { guide: new Script({ name: '🏠 使用教程', matches: [['所有页面', /.*/]], namespace: 'common.guide', configs: { notes: { defaultValue: $ui.notes([ '打开任意网课平台,进入视频、作业页面等待脚本运行,', '任何疑问请查看上方交流群,进群后带截图进行反馈。', '温馨提示: ', '⚠️ 禁止与其他脚本一起使用,否则会不兼容导致无法运行!', '⚠️ 禁止最小化浏览器、切屏,否则可能导致脚本无法运行!' ]).outerHTML } }, onrender({ panel }) { const guide = createGuide(); guide.style.width = '480px'; panel.body.replaceChildren(guide); } }), settings: new Script({ name: '⚙️ 全局设置', matches: [['所有页面', /.*/]], namespace: 'common.settings', configs: { notes: { defaultValue: $ui.notes([ '✨鼠标移动到按钮或者输入框,可以看到提示!', '想要自动答题必须设置 “题库配置” ', '设置后进入章节测试,作业,考试页面即可自动答题。' ]).outerHTML }, answererWrappers: { separator: '自动答题设置', defaultValue: [] as AnswererWrapper[] }, /** * 禁用的题库 */ disabledAnswererWrapperNames: { defaultValue: [] as string[] }, answererWrappersButton: { label: '题库配置', defaultValue: '点击配置', attrs: { type: 'button' }, onload() { const aws: any[] = CommonProject.scripts.settings.cfg.answererWrappers || []; this.value = aws.length ? '当前有' + aws.length + '个可用题库,点击重新配置' : '点击配置'; this.onclick = () => { const aw: any[] = CommonProject.scripts.settings.cfg.answererWrappers || []; const copy = $ui.copy('复制题库配置', JSON.stringify(aw, null, 4)); const list = h('div', [ h('div', aw.length ? ['以下是已经解析过的题库配置:', copy] : ''), ...createAnswererWrapperList(aw) ]); const textarea = h( 'textarea', { className: 'modal-input', style: { minHeight: '250px', width: 'calc(100% - 20px)', maxWidth: '100%' }, placeholder: aw.length ? '重新输入题库配置' : '输入你的题库配置...,不会请看上方填写教程' }, aw.length === 0 ? '' : JSON.stringify(aw, null, 4) ); const select = $ui.tooltip( h( 'select', { className: 'base-style-active-form-control', style: { backgroundColor: '#eef2f7', borderRadius: '2px', padding: '2px 8px' } }, [ h('option', '默认'), h( 'option', { title: '大学生网课题库接口适配器: 将不同的题库整合为一个API接口。详细查看 https://github.com/DokiDoki1103/tikuAdapter' }, 'TikuAdapter' ) ] ) ); const modal = $modal.prompt({ width: 600, maskCloseable: false, content: $ui.notes([ [ h('div', [ '题库配置填写教程:', h('a', { href: 'https://docs.ocsjs.com/docs/work' }, 'https://docs.ocsjs.com/docs/work') ]) ], [ h('div', [ '⚠️ 如果无法粘贴,请点->:', h('button', '读取剪贴板', (btn) => { btn.classList.add('base-style-button'); btn.onclick = () => { navigator.clipboard.readText().then((result) => { textarea.value = result; }); }; }), ',并同意浏览器上方的剪贴板读取申请。' ]) ], ['⚠️ 如果想添加多个不同的题库配置,请在每个配置之间使用三个井号隔开: ###。'], ['⚠️ 配置第三方题库出现网页弹窗的,点击永久允许连接。'], ...(aw.length ? [list] : []) ]), footer: h('div', { style: { width: '100%' } }, [ textarea, h('div', { style: { display: 'flex', flexWrap: 'wrap', marginTop: '12px', fontSize: '12px' } }, [ h('div', ['解析器:', select], (div) => { div.style.marginRight = '12px'; div.style.flex = '1'; }), h('div', { style: { flex: '1', display: 'flex', flexWrap: 'wrap', justifyContent: 'end' } }, [ h('button', '清空题库配置', (btn) => { btn.className = 'modal-cancel-button'; btn.style.marginRight = '48px'; btn.onclick = () => { $modal.confirm({ content: '确定要清空题库配置吗?', onConfirm: () => { $message.success({ content: '已清空,在答题前请记得重新配置。' }); modal?.remove(); CommonProject.scripts.settings.cfg.answererWrappers = []; this.value = '点击配置'; } }); }; }), h('button', '关闭', (btn) => { btn.className = 'modal-cancel-button'; btn.style.marginRight = '12px'; btn.onclick = () => modal?.remove(); }), h('button', '保存配置', (btn) => { btn.className = 'modal-confirm-button'; btn.onclick = async () => { const connects: string[] = $gm.getMetadataFromScriptHead('connect'); const value = textarea.value; if (!value) { $modal.alert({ content: h('div', '不能为空!') }); return; } if (value.includes('adapter-service/search') && (select.value === 'TikuAdapter') === false) { $modal.alert({ content: h('div', [ '检测到您可能正在使用 ', h( 'a', { href: 'https://github.com/DokiDoki1103/tikuAdapter#readme' }, 'TikuAdapter 题库' ), ',但是您选择的解析器不是 TikuAdapter,请选择 TikuAdapter 解析器,并填写接口地址即可,例如:http://localhost:8060/adapter-service/search,或者忽略此警告。' ]), confirmButtonText: '切换至 TikuAdapter 解析器,并识别接口地址', onConfirm() { const origin = textarea.value.match(/http:\/\/(.+)\/adapter-service\/search/)?.[1] || ''; textarea.value = `http://${origin}/adapter-service/search`; select.value = 'TikuAdapter'; } }); return; } try { let awsResult: AnswererWrapper[] = []; if (select.value === 'TikuAdapter') { if (value.startsWith('http') === false) { $modal.alert({ content: h('div', [ '格式错误,TikuAdapter解析器只能解析 url 链接,请重新输入!或者查看:', h( 'a', { href: 'https://github.com/DokiDoki1103/tikuAdapter#readme' }, 'https://github.com/DokiDoki1103/tikuAdapter#readme' ) ]) }); return; } select.value = '默认'; awsResult.push({ name: 'TikuAdapter题库', url: value, homepage: 'https://github.com/DokiDoki1103/tikuAdapter', method: 'post', type: 'GM_xmlhttpRequest', contentType: 'json', headers: {}, data: { // eslint-disable-next-line no-template-curly-in-string question: '${title}', options: { handler: "return (env)=>env.options?.split('\\n')" }, type: { handler: " return (env)=> env.type === 'single' ? 0 : env.type === 'multiple' ? 1 : env.type === 'completion' ? 3 : env.type === 'judgement' ? 4 : undefined" } }, handler: "return (res)=>res.answer.allAnswer.map(i=>([res.question,i.join('#')]))" }); } else { const contents = value .split('###') .map((i) => i.trim()) .filter(Boolean); for (const content of contents) { awsResult.push(...(await AnswerWrapperParser.from(content))); } } // 为空判断 if (awsResult.length === 0) { $modal.alert({ content: '题库配置不能为空,请重新配置。' }); return; } // 唯一化处理 const result_set: AnswererWrapper[] = []; for (const res of awsResult) { if (result_set.find((i) => JSON.stringify(i) === JSON.stringify(res))) { continue; } result_set.push(res); } awsResult = result_set; // 判断新旧是否一致,如果一致则提示 if ( JSON.stringify(CommonProject.scripts.settings.cfg.answererWrappers) === JSON.stringify(awsResult) ) { $modal.alert({ content: h('div', ['题库配置没有变化,请重新配置!']) }); return; } CommonProject.scripts.settings.cfg.answererWrappers = awsResult; this.value = '当前有' + awsResult.length + '个可用题库'; $modal.confirm({ width: 600, content: h('div', [ h('div', [ '🎉 配置成功,', h('b', ' 刷新网页后 '), '重新进入', h('b', ' 答题页面 '), '即可。', '解析到的题库如下所示:' ]), ...createAnswererWrapperList(awsResult) ]), onConfirm: () => { if ($gm.isInGMContext()) { top?.document.location.reload(); } }, ...($gm.isInGMContext() ? { confirmButtonText: '立即刷新', cancelButtonText: '稍后刷新' } : {}) }); // 格式化文本 textarea.value = JSON.stringify(awsResult, null, 4); // 检测 connects.length 是因为 如果在软件的软件设置全局配置中,上下文的 GM_info 会变成空 if (connects.length) { // 检测是否有域名白名单 const notAllowed: string[] = []; // 如果是通用版本,则不检测 if (connects.includes('*')) { return; } for (const aw of awsResult) { if (connects.some((connect) => new URL(aw.url).hostname.includes(connect)) === false) { notAllowed.push(aw.url); } } if (notAllowed.length) { $modal.alert({ width: 600, maskCloseable: false, title: '⚠️警告', content: h('div', [ h('div', [ '配置成功,但检测到以下 域名/ip 不在脚本的白名单中,请安装 : ', h( 'a', { href: 'https://docs.ocsjs.com/docs/other/api#全域名通用版本' }, 'OCS全域名通用版本' ), ',或者手动添加 @connect ,否则无法进行请求。', h( 'ul', notAllowed.map((url) => h('li', new URL(url).hostname)) ) ]) ]) }); } } } catch (e: any) { $modal.alert({ content: h('div', [h('div', '解析失败,原因如下 :'), h('div', e.message)]) }); } }; }) ]) ]) ]) }); }; } }, upload: { label: '答题完成后', tag: 'select', defaultValue: 80 as WorkUploadType, options: [ ['save', '自动保存', '完成后自动保存答案, 注意如果你开启了随机作答, 有可能分辨不出答案是否正确。'], ['nomove', '不保存也不提交', '等待时间过后将会自动下一节, 适合在测试脚本时使用。'], ...([10, 20, 30, 40, 50, 60, 70, 80, 90].map((rate) => [ rate, `搜到${rate}%的题目则自动提交`, `例如: 100题中查询到 ${rate} 题的答案,(答案不一定正确), 则会自动提交。` ]) as [any, string, string][]), ['100', '每个题目都查到答案才自动提交', '答案不一定正确'], ['force', '强制自动提交', '不管答案是否正确直接强制自动提交,如需开启,请配合随机作答谨慎使用。'] ], attrs: { title: '自动答题完成后的设置,目前仅在 超星学习通的章节测试 中生效, 鼠标悬浮在选项上可以查看每个选项的具体解释。' } }, thread: { label: '线程数量(个)', attrs: { type: 'number', min: 1, step: 1, max: 3, title: '同一时间内答题线程工作的数量(例子:三个线程则代表一秒内同时搜索三道题),过多可能导致题库服务器压力过大,请适当调低。' }, defaultValue: 1 }, 'work-when-no-job': { defaultValue: false, label: '(仅超星)强制答题', attrs: { type: 'checkbox', title: '当章节测试左上角并没有黄色任务点的时候依然进行答题(没有任务点说明此作业可能不计入总成绩,如果老师要求则可以开启)' } }, 'randomWork-choice': { defaultValue: false, label: '(仅超星)随机选择', attrs: { type: 'checkbox', title: '题库搜索不到答案时,随机选择任意一个选项' } }, 'randomWork-complete': { defaultValue: false, label: '(仅超星)随机填空', attrs: { type: 'checkbox', title: '题库搜索不到答案时,随机填写以下任意一个文案' } }, 'randomWork-completeTexts-textarea': { defaultValue: ['不会', '不知道', '不清楚', '不懂', '不会写'].join('\n'), label: '(仅超星)随机填空文案', tag: 'textarea', showIf: 'common.settings.randomWork-complete', attrs: { title: '每行一个,随机填入', style: { minWidth: '200px', minHeight: '50px' } }, onload(el) { el.addEventListener('change', () => { if (String(el.value).trim() === '') { el.value = el.defaultValue; } }); } }, advancedSettings: { ...dropdownStyle, defaultValue: false, label: '高级设置', attrs: { type: 'checkbox', title: '请谨慎使用高级设置,可能会影响答题效果,小白在未理解的情况下谨慎调整。' } }, answerWrapperHandlerTimeout: { showIf: 'common.settings.advancedSettings', elementClassName: 'config-details', label: '搜题最大耗时(秒)', attrs: { type: 'number', min: 10, step: 1, max: 3 * 60, title: '搜题超时时间,单位为秒,超过这个时间直接放弃,进行下一题搜索。' }, defaultValue: 120 }, stopSecondWhenFinish: { showIf: 'common.settings.advancedSettings', elementClassName: 'config-details', label: '答题结束后暂停(秒)', attrs: { type: 'number', min: 3, step: 1, max: 9999, title: '自动答题脚本结束后暂停的时间(方便查看和检查)。' }, defaultValue: 3 }, period: { showIf: 'common.settings.advancedSettings', elementClassName: 'config-details', label: '搜题间隔(秒)', attrs: { type: 'number', min: 1, step: 1, max: 60, title: '每道题的搜题间隔时间,不建议太低,避免增加服务器压力。' }, defaultValue: 3 }, answerSeparators: { showIf: 'common.settings.advancedSettings', elementClassName: 'config-details', label: '答案分隔符', attrs: { title: "分隔答案的符号,例如:答案1#答案2#答案3,分隔符为 #, 使用英文逗号进行隔开 : ',' " }, defaultValue: ['===', '#', '---', '###', '|', ';', ';'].join(','), onload(el) { el.addEventListener('change', () => { if (String(el.value).trim() === '') { el.value = el.defaultValue; } }); } }, answerMatchMode: { showIf: 'common.settings.advancedSettings', elementClassName: 'config-details', label: '答案匹配模式', tag: 'select', defaultValue: 'similar' as AnswerMatchMode, options: [ ['similar', '相似匹配', '答案相似度达到60%以上就匹配'], ['exact', '精确匹配', '答案必须完全一致才匹配'] ] }, redundanceWordsText: { showIf: 'common.settings.advancedSettings', elementClassName: 'config-details', defaultValue: [ '单选题(必考)', '填空题(必考)', '多选题(必考)', '(单选题)', '(多选题)', '(判断题)', '(填空题)', '【单选题】', '【多选题】', '【填空题】', '【判断题】', '【單選题】', '【多選题】', '【判斷题】', '【Single Choice】', '【Multiple Choice】', '【single choice】', '【multiple choice】', '【True or False】' ].join('\n'), label: '题目冗余字段自动删除', tag: 'textarea', attrs: { title: '在搜题的时候自动删除多余的文字,以便提高搜题的准确度,每行一个。', style: { minWidth: '200px', minHeight: '50px' } }, onload(el) { el.addEventListener('change', () => { if (String(el.value).trim() === '') { el.value = el.defaultValue; } }); } }, notification: { separator: '其他设置', label: '系统通知', attrs: { title: '允许脚本发送系统通知,只有重要事情发生时会发送系统通知,尽量避免用户受到骚扰(在电脑屏幕右侧显示通知弹窗,例如脚本执行完毕,图形验证码,版本更新等通知)。' }, tag: 'select', defaultValue: 'only-notify' as 'only-notify' | 'notify-and-voice' | 'all' | 'no-notify', options: [ ['only-notify', '只显示右下角通知'], ['notify-and-voice', '通知以及提示音(叮的一声)'], ['all', '通知,提示音,以及任务栏闪烁提示'], ['no-notify', '关闭系统通知'] ] }, notificationWebhooks: { label: '通知回调', attrs: { title: // eslint-disable-next-line no-template-curly-in-string '发送系统通知时发送回调请求,用于专业开发人员对接其他通知系统。(每行填写一个URL,顺序发送GET请求,${message} 为消息占位符,可用于消息变量替换)' }, tag: 'textarea', defaultValue: '' }, enableQuestionCaches: { label: '题库缓存功能', defaultValue: true, attrs: { type: 'checkbox', title: '详情请前往 通用-其他应用-题库拓展查看。' } } }, methods() { return { /** * 获取自动答题配置,包括题库配置 */ getWorkOptions: () => { // 使用 json 深拷贝,防止修改原始配置 const workOptions: typeof this.cfg = JSON.parse(JSON.stringify(this.cfg)); /** * 过滤掉被禁用的题库 */ workOptions.answererWrappers = workOptions.answererWrappers.filter( (aw) => this.cfg.disabledAnswererWrapperNames.find((daw) => daw === aw.name) === undefined ); return workOptions; }, /** * 根据全局设置的配置,发起通知 * @param content * @param opts */ notificationBySetting: ( content: string, opts?: { extraTitle?: string; /** 显示时间,单位为秒,默认为 30 秒, 0 则表示一直存在 */ duration?: number; /** 通知点击时 */ onclick?: () => void; /** 通知关闭时 */ ondone?: () => void; } ) => { if (this.cfg.notification !== 'no-notify') { $gm.notification(content, { extraTitle: opts?.extraTitle, duration: opts?.duration ?? 30, important: this.cfg.notification === 'all', silent: this.cfg.notification === 'only-notify' }); const message = (opts?.extraTitle ? opts?.extraTitle + ':' : '') + content; const webhooks = this.cfg.notificationWebhooks .split('\n') .map((i) => i.trim()) .filter(Boolean); for (const webhook of webhooks) { let resolved_webhook = webhook; // eslint-disable-next-line no-template-curly-in-string resolved_webhook = webhook.replace('${message}', encodeURIComponent(message)); request(resolved_webhook, { method: 'get', type: 'GM_xmlhttpRequest' }) .then((result) => { console.debug('通知回调成功', { webhook: resolved_webhook, result }); }) .catch((err) => { console.debug('通知回调失败', { webhook: resolved_webhook, err }); }); } } } }; }, // 实时更新内部设置 oncomplete() { AnswerWrapperHandlerConfig.timeout_seconds = this.cfg.answerWrapperHandlerTimeout; this.onConfigChange('answerWrapperHandlerTimeout', (sec) => { AnswerWrapperHandlerConfig.timeout_seconds = sec; }); }, onrender({ panel }) { // 因为需要用到 GM_xhr 所以判断是否处于用户脚本环境 if ($gm.isInGMContext()) { panel.body.replaceChildren(...(this.cfg.answererWrappers.length ? [h('hr')] : [])); const testNotification = h( 'button', { className: 'base-style-button', disabled: this.cfg.answererWrappers.length === 0 }, '📢测试系统通知' ); testNotification.onclick = () => { this.methods.notificationBySetting('这是一条测试通知'); }; const refresh = h( 'button', { className: 'base-style-button', disabled: this.cfg.answererWrappers.length === 0 }, '🔄️刷新题库状态' ); refresh.onclick = () => { updateState(); }; const tableContainer = h('div'); refresh.style.display = 'none'; tableContainer.style.display = 'none'; panel.body.append(h('div', { style: { display: 'flex' } }, [testNotification, refresh]), tableContainer); // 更新题库状态 const updateState = async () => { // 清空元素 tableContainer.replaceChildren(); let loadedCount = 0; if (this.cfg.answererWrappers.length) { refresh.style.display = 'block'; tableContainer.style.display = 'block'; refresh.textContent = '🚫正在加载题库状态...'; refresh.setAttribute('disabled', 'true'); const table = h('table'); table.style.width = '100%'; this.cfg.answererWrappers.forEach(async (item) => { const t = Date.now(); let success = false; let error; const isDisabled = this.cfg.disabledAnswererWrapperNames.find((name) => name === item.name); const res = isDisabled ? false : await Promise.race([ (async () => { try { return await request(new URL(item.url).origin + '/?t=' + t, { type: 'GM_xmlhttpRequest', method: 'head', responseType: 'text' }); } catch (err) { error = err; return false; } })(), (async () => { await $.sleep(10 * 1000); return false; })() ]); if (typeof res === 'string') { success = true; } else { success = false; } const body = h('tbody'); body.append(h('td', item.name)); body.append( h('td', [ $ui.tooltip( h( 'span', { title: isDisabled ? '题目已经被停用,请在上方题库配置中点击开启。' : '' }, success ? '连接成功🟢' : isDisabled ? '已停用⚪' : error ? '连接失败🔴' : '连接超时🟡' ) ) ]) ); body.append(h('td', `延迟 : ${success ? Date.now() - t : '---'}/ms`)); table.append(body); loadedCount++; if (loadedCount === this.cfg.answererWrappers.length) { setTimeout(() => { refresh.textContent = '🔄️刷新题库状态'; refresh.removeAttribute('disabled'); }, 2000); } }); tableContainer.append(table); } else { refresh.style.display = 'none'; tableContainer.style.display = 'none'; } }; updateState(); this.offConfigChange(state.setting.listenerIds.aw); state.setting.listenerIds.aw = this.onConfigChange('answererWrappers', (_, __, remote) => { if (remote === false) { updateState(); } }); } } }), workResults: new Script({ name: '🔎 搜索结果', matches: [['所有页面', /.*/]], namespace: 'common.work-results', configs: { notes: { defaultValue: $ui.notes(['点击题目序号,查看搜索结果', '如果没有搜到,可能是题库没有收录该题目答案']) .outerHTML }, /** * 显示类型 * list: 显示为题目列表 * numbers: 显示为序号列表 */ type: { label: '显示类型', tag: 'select', options: [ ['numbers', '序号列表'], ['questions', '题目列表'] ], attrs: { title: '使用题目列表可能会造成页面卡顿。' }, defaultValue: 'numbers' as 'questions' | 'numbers' }, totalQuestionCount: { defaultValue: 0 }, requestedCount: { defaultValue: 0 }, resolvedCount: { defaultValue: 0 }, currentResultIndex: { defaultValue: 0 }, questionPositionSyncHandlerType: { defaultValue: undefined as keyof typeof state.workResult.questionPositionSyncHandler | undefined } }, methods() { return { /** * 从搜索结果中计算状态,并更新 */ updateWorkStateByResults: (results: { requested: boolean; resolved: boolean }[]) => { this.cfg.totalQuestionCount = results.length; this.cfg.requestedCount = results.filter((result) => result.requested).length; this.cfg.resolvedCount = results.filter((result) => result.resolved).length; }, /** * 更新状态 */ updateWorkState: (state: { totalQuestionCount: number; requestedCount: number; resolvedCount: number }) => { this.cfg.totalQuestionCount = state.totalQuestionCount; this.cfg.requestedCount = state.requestedCount; this.cfg.resolvedCount = state.resolvedCount; }, /** * 刷新状态 */ refreshState: () => { this.cfg.totalQuestionCount = 0; this.cfg.requestedCount = 0; this.cfg.resolvedCount = 0; }, /** * 清空搜索结果 */ clearResults: () => { return $store.setTab(TAB_WORK_RESULTS_KEY, []); }, getResults(): Promise | undefined { return $store.getTab(TAB_WORK_RESULTS_KEY) || undefined; }, setResults(results: SimplifyWorkResult[]) { return $store.setTab(TAB_WORK_RESULTS_KEY, results); }, async appendResults(results: SimplifyWorkResult[]) { const data = (await $store.getTab(TAB_WORK_RESULTS_KEY)) || []; data.push(...results); return $store.setTab(TAB_WORK_RESULTS_KEY, data); }, /** * 刷新搜索结果状态,清空搜索结果,置顶搜索结果面板 */ init(opts?: { questionPositionSyncHandlerType?: keyof typeof state.workResult.questionPositionSyncHandler }) { CommonProject.scripts.workResults.cfg.questionPositionSyncHandlerType = opts?.questionPositionSyncHandlerType; // 刷新搜索结果状态 CommonProject.scripts.workResults.methods.refreshState(); // 清空搜索结果 CommonProject.scripts.workResults.methods.clearResults(); }, /** * 创建搜索结果面板 * @param mount 挂载点 */ createWorkResultsPanel: (mount?: HTMLElement) => { const container = mount || h('div'); container.style.width = '400px'; /** 记录滚动高度 */ let scrollPercent = 0; /** 列表 */ const list = h('div', { className: 'work-result-list' }); /** 是否悬浮在题目上 */ let mouseoverIndex = -1; list.onscroll = () => { scrollPercent = list.scrollTop / list.scrollHeight; }; /** 给序号设置样式 */ const setNumStyle = (result: SimplifyWorkResult, num: HTMLElement, index: number) => { if (result.requested) { num.classList.add('requested'); } if (index === this.cfg.currentResultIndex) { num.classList.add('active'); } if (result.finish) { num.classList.add('finish'); } else { if ( result.requested && result.resolved && (result.error?.trim().length !== 0 || result.searchInfos.length === 0 || result.finish === false) ) { num.classList.add('error'); } } }; /** 渲染结果面板 */ const render = debounce(async () => { const results: SimplifyWorkResult[] | undefined = await CommonProject.scripts.workResults.methods.getResults(); if (results?.length) { // 如果序号指向的结果为空,则代表已经被清空,则重新让index变成0 if (results[this.cfg.currentResultIndex] === undefined) { this.cfg.currentResultIndex = 0; } // 渲染序号或者题目列表 if (this.cfg.type === 'numbers') { const resultContainer = h('div', { className: 'work-result-container' }); list.style.marginBottom = '12px'; list.style.overflow = 'auto'; list.style.maxHeight = '300px'; /** 渲染序号 */ const nums = results.map((result, index) => { return h('span', { className: 'search-infos-num', innerText: (index + 1).toString() }, (num) => { setNumStyle(result, num, index); num.onclick = () => { for (const n of nums) { n.classList.remove('active'); } num.classList.add('active'); // 更新显示序号 this.cfg.currentResultIndex = index; // 重新渲染结果列表 resultContainer.replaceChildren(createResult(result)); // 触发页面题目元素同步器 if (this.cfg.questionPositionSyncHandlerType) { state.workResult.questionPositionSyncHandler[this.cfg.questionPositionSyncHandlerType]?.( index ); } }; }); }); list.replaceChildren(...nums); // 初始显示指定序号的结果 resultContainer.replaceChildren(createResult(results[this.cfg.currentResultIndex])); container.replaceChildren(list, resultContainer); } else { /** 左侧题目列表 */ list.style.overflow = 'auto'; list.style.maxHeight = window.innerHeight / 2 + 'px'; /** 右侧结果 */ const resultContainer = h('div', { className: 'work-result-question-container' }); const nums: HTMLSpanElement[] = []; /** 左侧渲染题目列表 */ const questions = results.map((result, index) => { /** 左侧序号 */ const num = h( 'span', { className: 'search-infos-num', innerHTML: (index + 1).toString() }, (num) => { num.style.marginRight = '12px'; num.style.display = 'inline-block'; setNumStyle(result, num, index); } ); nums.push(num); return h( 'div', [num, result.question], (question) => { question.onmouseover = () => { mouseoverIndex = index; // 重新渲染结果列表 resultContainer.replaceChildren(createResult(result)); }; question.onmouseleave = () => { mouseoverIndex = -1; // 重新显示指定序号的结果 resultContainer.replaceChildren(createResult(results[this.cfg.currentResultIndex])); }; question.onclick = () => { for (const n of nums) { n.classList.remove('active'); } for (const q of questions) { q.classList.remove('active'); } nums[index].classList.add('active'); question.classList.add('active'); // 更新显示序号 this.cfg.currentResultIndex = index; // 重新渲染结果列表 resultContainer.replaceChildren(createResult(result)); // 触发页面题目元素同步器 if (this.cfg.questionPositionSyncHandlerType) { state.workResult.questionPositionSyncHandler[this.cfg.questionPositionSyncHandlerType]?.( index ); } }; } ); }); list.replaceChildren(...questions); // 初始显示指定序号的结果 if (mouseoverIndex === -1) { resultContainer.replaceChildren(createResult(results[this.cfg.currentResultIndex])); } else { resultContainer.replaceChildren(createResult(results[mouseoverIndex])); } container.replaceChildren( h('div', [list, h('div', {}, [resultContainer])], (div) => { div.style.display = 'flex'; }) ); } } else { container.replaceChildren( h('div', { className: 'alert-info-wrapper' }, [ h('div', '暂无任何搜索结果~', (div) => { div.style.marginTop = '12px'; div.className = 'result-info no-answer'; }) ]) ); } /** 恢复高度 */ list.scrollTo({ top: scrollPercent * list.scrollHeight, behavior: 'auto' }); const tip = h('div', [ h('div', { className: 'search-infos-num' }, '1'), ' 表示等待处理中', h('br'), h('div', { className: 'search-infos-num requested' }, '1'), ' 表示已完成搜索 ', h('br'), h('div', { className: 'search-infos-num finish' }, '1'), ' 表示已搜索已答题 ' ]); /** 添加信息 */ container.prepend( h('hr'), h( 'div', [ $ui.space( [ h('span', `已搜题: ${this.cfg.requestedCount}/${this.cfg.totalQuestionCount}`), h('span', `已答题: ${this.cfg.resolvedCount}/${this.cfg.totalQuestionCount}`), h('a', '提示', (btn) => { btn.style.cursor = 'pointer'; btn.onclick = () => { $modal.confirm({ content: tip, footer: undefined }); }; }), $ui.tooltip( h('a', '清空结果', (btn) => { btn.title = '仅用于不会自动清空搜索结果的场景,例如超星非整卷预览模式'; btn.style.cursor = 'pointer'; btn.onclick = () => { this.methods.clearResults(); const { panel, header } = CXProject.scripts.work; if (panel && header) { CXProject.scripts.work.onrender?.({ panel, header }); CommonProject.scripts.workResults.onrender?.({ panel, header }); } }; }) ) ], { separator: '|' } ) ], (div) => { div.style.textAlign = 'center'; div.style.fontSize = '12px'; } ) ); }, 100); /** 渲染结果列表 */ const createResult = (result: SimplifyWorkResult | undefined) => { if (result) { let info: HTMLElement | null = null; if (result.requested === false && result.resolved === false) { info = h('div', { className: 'result-info unresolved' }, '等待搜索中... 🔍'); } else if (result.error) { info = h('div', { className: 'result-info error' }, '❌ ' + result.error); } else if (result.searchInfos.length === 0) { info = h('div', { className: 'result-info no-answer' }, '❌ 题库没搜索到答案'); } else { info = result.finish ? null : result.resolved === false ? h('div', { className: 'result-info unresolved' }, '等待顺序答题中... ⏱️') : h('div', { className: 'result-info error' }, '❌ 此题未完成, 可能是没有匹配的选项。'); } return h('div', [ h('div', { className: 'alert-info-wrapper' }, [info ?? h('div')]), h(SearchInfosElement, { infos: result.searchInfos, question: result.question, type: result.type }) ]); } else { return h('div', 'undefined'); } }; render(); this.onConfigChange('type', render); this.onConfigChange('requestedCount', render); this.onConfigChange('resolvedCount', render); $store.addChangeListener(TAB_WORK_RESULTS_KEY, render); return container; } }; }, onrender({ panel }) { panel.body.replaceChildren(this.methods.createWorkResultsPanel()); } }), onlineSearch: new Script({ name: '🔎 在线搜题', matches: [['所有页面', /.*/]], namespace: 'common.online-search', configs: { notes: { defaultValue: '查题前请在 “通用-全局设置” 中设置题库配置,才能进行在线搜题。' }, selectSearch: { label: '划词搜索', defaultValue: true, attrs: { type: 'checkbox', title: '使用鼠标滑动选择页面中的题目进行搜索。' } }, searchValue: { sync: true, label: '搜索题目', tag: 'textarea', attrs: { placeholder: '输入题目,请尽量保证题目完整,不要漏字', style: { minWidth: '300px', minHeight: '64px' } }, defaultValue: '' } }, oncomplete() { document.addEventListener( 'selectionchange', debounce(() => { if (this.cfg.selectSearch) { const val = document.getSelection()?.toString() || ''; if (val) { this.cfg.searchValue = val; } } }, 500) ); }, onrender({ panel }) { const content = h('div', '', (content) => { content.style.marginBottom = '12px'; }); const search = async (value: string) => { if (CommonProject.scripts.settings.cfg.answererWrappers.length === 0) { $modal.alert({ content: '请先在 通用-全局设置 配置题库,才能进行在线搜题。' }); return; } content.replaceChildren(h('span', '搜索中...')); if (value) { const t = Date.now(); const infos = await defaultAnswerWrapperHandler(CommonProject.scripts.settings.cfg.answererWrappers, { title: value }); // 耗时计算 const resume = ((Date.now() - t) / 1000).toFixed(2); content.replaceChildren( h( 'div', [ h('hr'), h( 'div', { style: { color: '#a1a1a1' } }, `搜索到 ${infos.map((i) => i.results).flat().length} 个结果,共耗时 ${resume} 秒` ), h(SearchInfosElement, { infos: infos.map((info) => ({ results: info.results.map( (res) => [res.question, res.answer, res.extra_data] as [string, string, object] ), homepage: info.homepage, name: info.name })), question: value }) ], (div) => { div.classList.add('card'); div.style.width = '480px'; } ) ); } else { content.replaceChildren(h('span', '题目不能为空!')); } }; const button = h('button', '搜索', (button) => { button.className = 'base-style-button'; button.style.width = '120px'; button.onclick = () => { search(this.cfg.searchValue); }; }); const searchContainer = h('div', { style: { textAlign: 'end' } }, [button]); panel.body.append(h('div', [content, searchContainer])); } }), /** 渲染脚本,窗口渲染主要脚本 */ render: RenderScript, hack: new Script({ name: '页面复制粘贴限制解除', matches: [['所有页面', /.*/]], hideInPanel: true, onactive() { enableCopy([document, document.body]); }, oncomplete() { enableCopy([document, document.body]); insertCopyableStyle(); setTimeout(() => { enableCopy([document, document.body]); insertCopyableStyle(); }, 3000); } }), disableDialog: new Script({ name: '禁止弹窗', matches: [['所有页面', /.*/]], hideInPanel: true, priority: 1, onstart() { function disableDialog(msg: string) { $modal.alert({ profile: '弹窗来自:' + location.origin, content: msg }); } try { $gm.unsafeWindow.alert = disableDialog; window.alert = disableDialog; } catch (e) { console.error(e); } } }), apps: new Script({ name: '📱 拓展应用', matches: [['', /.*/]], namespace: 'common.apps', configs: { notes: { defaultValue: '这里是一些其他的应用或者拓展功能。' }, /** * 题库缓存 */ localQuestionCaches: { defaultValue: [] as QuestionCache[], extra: { appConfigSync: false } } }, methods() { return { /** * 添加题库缓存 */ addQuestionCache: async (...questionCacheItems: QuestionCache[]) => { const questionCaches: QuestionCache[] = this.cfg.localQuestionCaches; for (const item of questionCacheItems) { // 去重 if (questionCaches.find((c) => c.title === item.title && c.answer === item.answer) === undefined) { questionCaches.unshift(item); } } // 限制数量 questionCaches.splice(200); this.cfg.localQuestionCaches = questionCaches; }, addQuestionCacheFromWorkResult(swr: SimplifyWorkResult[]) { CommonProject.scripts.apps.methods.addQuestionCache( ...swr .map((r) => r.searchInfos .map((i) => i.results .filter((res) => res[1]) .map((res) => ({ title: r.question, answer: res[1], from: i.name.replace(/【题库缓存】/g, ''), homepage: i.homepage || '' })) .flat() ) .flat() ) .flat() ); }, /** * 将题库缓存作为题库并进行题目搜索 * @param title 题目 * @param whenSearchEmpty 当搜索结果为空,或者题库缓存功能被关闭时执行的函数 */ searchAnswerInCaches: async ( title: string, whenSearchEmpty: () => SearchInformation[] | Promise ): Promise => { if (CommonProject.scripts.settings.cfg.enableQuestionCaches === false) { return await whenSearchEmpty(); } let results: SearchInformation[] = []; const caches = this.cfg.localQuestionCaches; for (const cache of caches) { if (cache.title.trim() === title.trim()) { results.push({ name: cache.from, homepage: cache.homepage, results: [{ answer: cache.answer, question: cache.title, extra_data: { ai: cache.ai, cache: true } }] }); } } if (results.length === 0) { results = await whenSearchEmpty(); } return results; }, /** * 查看更新日志 */ async showChangelog() { const changelog = h('div', { className: 'markdown card', innerHTML: '加载中...', style: { maxWidth: '600px' } }); $modal.simple({ width: 600, content: h('div', [ h('div', { className: 'notes card' }, [ $ui.notes(['此页面实时更新,遇到问题可以查看最新版本是否修复。']) ]), changelog ]) }); const md = await request('https://cdn.ocsjs.com/articles/ocs/changelog.md?t=' + Date.now(), { type: 'GM_xmlhttpRequest', responseType: 'text', method: 'get' }); changelog.innerHTML = markdown(md); } }; }, onrender({ panel }) { const btnStyle: Partial = { padding: '6px 12px', margin: '4px', marginBottom: '8px', boxShadow: '0px 0px 4px #bebebe', borderRadius: '8px', cursor: 'pointer' }; const cachesBtn = h('div', { innerText: '💾 题库缓存', style: btnStyle }, (btn) => { btn.onclick = () => { const questionCaches = this.cfg.localQuestionCaches; const list = questionCaches.map((c) => h( 'div', { className: 'question-cache', style: { margin: '8px', border: '1px solid lightgray', borderRadius: '4px', padding: '8px' } }, [ h('div', { className: 'title' }, [ $ui.tooltip( h( 'span', { title: `来自:${c.from || '未知题库'}\n主页:${c.homepage || '未知主页'}`, style: { fontWeight: 'bold' } }, c.title ) ) ]), h('div', { className: 'answer', style: { marginTop: '6px' } }, c.answer) ] ) ); const countEl = h('span', ['当前缓存数量:' + questionCaches.length]); $modal.simple({ width: 800, content: h('div', [ h('div', { className: 'notes card' }, [ $ui.notes([ '题库缓存是将题库的题目和答案保存在内存,在重复使用时可以直接从内存获取,不需要再次请求题库。', '以下是当前存储的题库,默认存储200题,当前页面关闭后会自动清除。' ]) ]), h('div', { className: 'card' }, [ $ui.space( [ countEl, $ui.button('清空题库缓存', {}, (btn) => { btn.onclick = () => { this.cfg.localQuestionCaches = []; countEl.innerText = '当前缓存数量:0'; list.forEach((el) => el.remove()); }; }) ], { separator: '|' } ) ]), h( 'div', questionCaches.length === 0 ? [h('div', { style: { textAlign: 'center' } }, '暂无题库缓存')] : list ) ]) }); }; }); const exportSetting = $ui.tooltip( h( 'div', { innerText: '📤 导出全部设置', style: btnStyle, title: '导出全部页面的设置,包括全局设置,题库配置,学习设置等等。(文件后缀名为:.ocssetting)' }, (btn) => { btn.onclick = () => { const setting = Object.create({}); for (const key of $store.list()) { const val = $store.get(key); if (val) { Reflect.set(setting, key, val); } } const blob = new Blob([JSON.stringify(setting, null, 2)], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = h('a', { href: url, download: 'ocs-setting-export.ocssetting' }); a.click(); URL.revokeObjectURL(url); }; } ) ); const importSetting = $ui.tooltip( h( 'div', { innerText: '📥 导入全部设置', style: btnStyle, title: '导入并且覆盖当前的全部设置。(文件后缀名为:.ocssetting)' }, (btn) => { btn.onclick = () => { const input = h('input', { type: 'file', accept: '.ocssetting' }); input.onchange = async () => { const file = input.files?.[0]; if (file) { const setting = await file.text(); const obj = JSON.parse(setting); for (const key of Object.keys(obj)) { $store.set(key, obj[key]); } $message.success({ content: '设置导入成功,页面即将刷新。', duration: 3 }); setTimeout(() => { location.reload(); }, 3000); } }; input.click(); }; } ) ); [cachesBtn, exportSetting, importSetting].forEach((btn) => { btn.onmouseover = () => { btn.style.boxShadow = '0px 0px 4px #0099ff9c'; }; btn.onmouseout = () => { btn.style.boxShadow = '0px 0px 4px #bebebe'; }; }); const sep = (text: string) => h('div', { className: 'separator', style: { padding: '4px 0px' } }, text); panel.body.replaceChildren( h('div', [sep('题库拓展'), cachesBtn, sep('其他功能'), exportSetting, importSetting]) ); } }) } }); function insertCopyableStyle() { const style = document.createElement('style'); style.innerHTML = ` html * { -webkit-user-select: text !important; -khtml-user-select: text !important; -moz-user-select: text !important; -ms-user-select: text !important; user-select: text !important; }`; document.head.append(style); } function createAnswererWrapperList(aw: AnswererWrapper[]) { return aw.map((item) => h( 'details', [ h('summary', [ $ui.space([ (() => { let isDisabled = CommonProject.scripts.settings.cfg.disabledAnswererWrapperNames.includes(item.name); const checkbox = h('input', { type: 'checkbox', checked: !isDisabled, className: 'base-style-switch' }); checkbox.onclick = () => { isDisabled = !isDisabled; if (isDisabled) { CommonProject.scripts.settings.cfg.disabledAnswererWrapperNames = [ ...CommonProject.scripts.settings.cfg.disabledAnswererWrapperNames, item.name ]; $message.warn({ content: '题库:' + item.name + ' 已被停用,如需开启请在:通用-全局设置-题库配置中开启。', duration: 30 }); } else { CommonProject.scripts.settings.cfg.disabledAnswererWrapperNames = CommonProject.scripts.settings.cfg.disabledAnswererWrapperNames.filter( (name) => name !== item.name ); $message.success({ content: '题库:' + item.name + ' 已启用。', duration: 3 }); } }; checkbox.title = '点击停用或者启用题库,停用题库后将无法在自动答题中查询题目'; return $ui.tooltip(checkbox); })(), h('span', item.name) ]) ]), h('ul', [ h('li', ['名字\t', item.name]), h('li', { innerHTML: `官网\t${item.homepage || '无'}` }), h('li', ['接口\t', item.url]), h('li', ['请求方法\t', item.method]), h('li', ['请求类型\t', item.type]), h('li', ['请求头\t', JSON.stringify(item.headers, null, 4) || '无']), h('li', ['请求体\t', JSON.stringify(item.data, null, 4) || '无']) ]) ], (details) => { details.style.paddingLeft = '12px'; } ) ); } const createGuide = () => { const showProjectDetails = (project: Project) => { $modal.simple({ title: project.name, width: 800, content: h('div', [ h('div', [ '运行域名:', ...(project.domains || []).map((d) => h( 'a', { href: d.startsWith('http') ? d : 'https://' + d, target: '_blank', style: { margin: '0px 4px' } }, d ) ) ]), h('div', '脚本列表:'), h( 'ul', Object.keys(project.scripts) .sort((a, b) => (project.scripts[b].hideInPanel ? -1 : 1)) .map((key) => { const script = project.scripts[key]; return h( 'li', [ h('b', script.name), $ui.notes([ h('span', ['操作面板:', script.hideInPanel ? '隐藏' : '显示']), [ '运行页面:', h( 'ul', script.matches .map((m) => (Array.isArray(m) ? m : (['无描述', m] as [string, string | RegExp]))) .map((i) => h('li', [ i[0], ':', i[1] instanceof RegExp ? i[1].toString().replace(/\\/g, '').slice(1, -1) : h('span', i[1]) ]) ) ) ] ]) ], (li) => { li.style.marginBottom = '12px'; } ); }), (ul) => { ul.style.padding = '12px 24px'; ul.style.border = '1px solid #e1e1e1'; ul.style.borderRadius = '4px'; ul.style.maxHeight = '400px'; ul.style.overflow = 'auto'; ul.style.paddingLeft = '42px'; } ) ]) }); }; const gotoHome = h('button', { className: 'base-style-button-secondary' }, '🏡官网教程'); gotoHome.onclick = () => window.open('https://docs.ocsjs.com', '_blank'); const contactUs = h('button', { className: 'base-style-button-secondary' }, '🗨️交流群'); contactUs.onclick = () => window.open('https://docs.ocsjs.com/docs/about#交流方式', '_blank'); const changeLog = h('button', { className: 'base-style-button-secondary' }, '📄更新日志'); changeLog.onclick = () => CommonProject.scripts.apps.methods.showChangelog(); const cardStyle: Partial = { border: '1px solid #eee', borderRadius: '4px', padding: '8px', paddingTop: '4px' }; return h('div', { className: 'user-guide' }, [ h('div', { style: cardStyle }, [ h('div', { style: { marginBottom: '4px', fontWeight: 'bold' } }, [ '✨兼容的网课平台:', h('span', { className: 'secondary', style: { fontWeight: 'normal' } }, '(未适配的平台将无法运行,请等待适配)') ]), h('div', [ ...[CXProject, ZHSProject, ZJYProject, IcveMoocProject, ICourseProject].map((project) => { const btn = h('button', { className: 'base-style-button-secondary', style: { margin: '4px' } }, [ project.name ]); btn.onclick = () => { showProjectDetails(project); }; return btn; }) ]) ]), h('div', { style: { ...cardStyle, marginTop: '12px' } }, [ h('div', { style: { marginBottom: '8px', fontWeight: 'bold' } }, '🌐快捷访问:'), gotoHome, contactUs, changeLog ]) ]); }; ================================================ FILE: packages/scripts/src/projects/cx.ts ================================================ /** global Ext videojs getTeacherAjax jobs */ import { OCSWorker, defaultAnswerWrapperHandler, $, StringUtils, request, createDefaultQuestionResolver, DefaultWork, splitAnswer, domSearch, domSearchAll, SearchInformation } from '@ocsjs/core'; import { $modal, h, $store, MessageElement, Project, Script, $el, $gm, $$el, $ui, cors, $message } from 'easy-us'; import { CommonProject } from './common'; import { workNotes, volume, playbackRate, dropdownStyle } from '../utils/configs'; import { answerWrapperEmptyWarning, commonWork, optimizationElementWithImage, removeRedundantWords, simplifyWorkResult } from '../utils/work'; import md5 from 'md5'; // @ts-ignore import Typr from 'typr.js'; import { $console, BackgroundProject } from './background'; import { CommonWorkOptions, playMedia } from '../utils'; import { waitForElement, waitForMedia } from '../utils/study'; // @ts-ignore let top: Window = globalThis.top; try { /** * * 将繁体字映射载入内存。 * 为什么不存 localStorage 和 GM_setValue * localStorage: 存在被检测风险,谁都能访问 * GM_setValue: 文件太大影响I/O速度 */ // @ts-ignore top.typrMapping = top.typrMapping || undefined; // @ts-ignore 任务点 top.jobs = top.jobs || []; // @ts-ignore 当前视频 top.currentMedia = top.currentMedia || undefined; // 加 try 是因为跨域面板无法操作 } catch {} const state = { study: { videojs: Object.create({}), hacked: false, answererWrapperUnsetMessage: undefined as MessageElement | undefined, playbackRateWarningListenerId: 0 } }; type VideoQuizStrategy = 'random' | 'ignore'; type Attachment = { /** 只有当 module 为 音视频(并且已经播放完成)时才会有这个属性 */ isPassed: boolean | undefined; /** 是否为任务点(音视频播放完成后此属性不存在) */ job: boolean | undefined; /** 这里注意,如果当前章节测试不是任务点,则没有 jobid 或者未空字符串 */ jobid?: string; property: { mid: string; /** 任务点id,固定存在 */ _jobid: string; module: 'insertbook' | 'insertdoc' | 'insertflash' | 'work' | 'insertaudio' | 'insertvideo'; name?: string; author?: string; bookname?: string; publisher?: string; title?: string; }; }; type Job = { mid: string; attachment: Attachment; func: { (): Promise } | undefined; }; export const CXProject = Project.create({ name: '超星学习通', domains: [ 'chaoxing.com', 'edu.cn', 'org.cn', // 学银在线 'xueyinonline.com', /** 其他域名 */ 'hnsyu.net', 'qutjxjy.cn', 'ynny.cn', 'hnvist.cn', 'fjlecb.cn', 'gdhkmooc.com', 'cugbonline.cn', 'zjelib.cn', 'cqrspx.cn', 'neauce.com', 'zhihui-yun.com', 'cqie.cn', 'ccqmxx.com', 'jxgmxy.com', 'jnzyjsxy.cn', // 超星学习通PPT,2025下半年更新的PTT图书新域名 'sslibrary.com' ], scripts: { /** * 创建超星独立脚本防止污染其他脚本环境 */ env: new Script({ name: '环境准备脚本', matches: [['所有页面', /.*/]], hideInPanel: true, onstart() { /** * 于 4.9.20 后更新,出现顶层套壳页面跨域 : * top : zjelib.cn * iframe : mooc1.xxx.zjelib.cn/.../mycourse/studentstudy/...