[
  {
    "path": ".eslintrc.cjs",
    "content": "/* eslint-env node */\nrequire(\"@rushstack/eslint-patch/modern-module-resolution\")\n\nmodule.exports = {\n    root: true,\n    extends: [\n        \"plugin:vue/vue3-essential\",\n        \"eslint:recommended\",\n        \"@vue/eslint-config-typescript\",\n        \"@vue/eslint-config-prettier\"\n    ],\n    parserOptions: {\n        ecmaVersion: \"latest\"\n    },\n    rules: {\n        \"no-unused-vars\": [\"off\", { \"vars\": \"all\", \"args\": \"after-used\", \"ignoreRestSiblings\": false }]\n    }\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\n.DS_Store\ndist\ndist-ssr\ncoverage\n*.local\n\n/cypress/videos/\n/cypress/screenshots/\n\n# Editor directories and files\n**/.vscode/*\n!.vscode/extensions.json\n.idea\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n**/*.pem\n"
  },
  {
    "path": ".prettierrc.json",
    "content": "{\n    \"tabWidth\": 4,\n    \"useTabs\": false,\n    \"semi\": false,\n    \"trailingComma\": \"none\",\n    \"bracketSameLine\": false,\n    \"printWidth\": 120\n}"
  },
  {
    "path": ".vscode/extensions.json",
    "content": "{\n  \"recommendations\": [\"Vue.volar\", \"Vue.vscode-typescript-vue-plugin\"]\n}\n"
  },
  {
    "path": "README.md",
    "content": "# 不支持 spoc\n\n# 反馈所需信息\n\n反馈课程更新问题，需要提新 issue，带上课程 id、课程链接和测验截止时间，我会在截止时间前修，上班太忙了\n\n# 插件简介\n\n实现对于中国大学MOOC的\n- 非在线测评题的自动答案查询，包括单选题、多选题、判断题、填空题、简答题，支持测验与作业及考试\n- 互评阶段的自动评分、自动点评\n\n下载地址：[Github release v2.2.1](https://github.com/ginnnnnn666/GinsMooc/releases/tag/v2.2.1)\n\n在线使用：[GinsMooc](https://ginnnnnn.top/mooc/)\n\n# 功能介绍\n\n在测试的准备页面，将会自动检查是否准备就绪，若为否将自动更新课程\n\n![](/public/extension-updating.png)\n\n进入测验后，将显示“获取答案”按钮，点击即可\n\n![](/public/extension-single-choice.png)\n![](/public/extension-multiple-choice.png)\n![](/public/extension-completion.png)\n![](/public/extension-homework.png)\n\n作业的互评阶段支持自动评分、自动点评\n\n![](/public/extension-auto-evaluate-1.png)\n![](/public/extension-auto-evaluate-2.png)\n\n\n# 安装介绍\n\n下载安装包后，将其解压至文件夹内\n\n在浏览器地址栏中输入`edge://extensions`（谷歌浏览器为`chrome://extensions`）\n\n打开开发者模式\n\n![](/public/extension-developer-mode.png)\n\n点击“加载解压缩的扩展”，选择刚刚解压到的文件夹，即可开始使用\n\n![](/public/extension-load-decompression.png)\n"
  },
  {
    "path": "env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "extension/.eslintrc.cjs",
    "content": "/* eslint-env node */\nrequire('@rushstack/eslint-patch/modern-module-resolution')\n\nmodule.exports = {\n    root: true,\n    'extends': [\n        'plugin:vue/vue3-essential',\n        'eslint:recommended',\n        '@vue/eslint-config-typescript',\n        '@vue/eslint-config-prettier'\n    ],\n    parserOptions: {\n        ecmaVersion: 'latest'\n    }\n}\n"
  },
  {
    "path": "extension/.prettierrc.json",
    "content": "{\n    \"tabWidth\": 4,\n    \"useTabs\": false,\n    \"semi\": false,\n    \"trailingComma\": \"none\",\n    \"bracketSameLine\": false,\n    \"printWidth\": 120\n}"
  },
  {
    "path": "extension/README.md",
    "content": "# GinsMooc Extention\n\nThis template should help get you started developing with Vue 3 in Vite.\n\n## Recommended IDE Setup\n\n[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).\n\n## Type Support for `.vue` Imports in TS\n\nTypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.\n\nIf the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:\n\n1. Disable the built-in TypeScript Extension\n    1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette\n    2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`\n2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.\n\n## Customize configuration\n\nSee [Vite Configuration Reference](https://vitejs.dev/config/).\n\n## Project Setup\n\n```sh\nnpm install\n```\n\n### Compile and Hot-Reload for Development\n\n```sh\nnpm run dev\n```\n\n### Type-Check, Compile and Minify for Production\n\n```sh\nnpm run build\n```\n\n### Lint with [ESLint](https://eslint.org/)\n\n```sh\nnpm run lint\n```\n"
  },
  {
    "path": "extension/env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "extension/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\">\n    <link rel=\"icon\" href=\"/favicon.ico\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Vite App</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "extension/package.json",
    "content": "{\n    \"name\": \"ginsmooc-extention\",\n    \"version\": \"0.0.0\",\n    \"private\": true,\n    \"scripts\": {\n        \"dev\": \"vite\",\n        \"build\": \"run-p type-check build-only\",\n        \"preview\": \"vite preview\",\n        \"build-only\": \"vite build\",\n        \"type-check\": \"vue-tsc --noEmit\",\n        \"lint\": \"eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore\"\n    },\n    \"dependencies\": {\n        \"axios\": \"^1.3.2\",\n        \"element-plus\": \"^2.2.29\",\n        \"jssha\": \"^3.3.0\",\n        \"vue\": \"^3.2.45\",\n        \"vue-router\": \"^4.1.6\"\n    },\n    \"devDependencies\": {\n        \"@rushstack/eslint-patch\": \"^1.1.4\",\n        \"@types/chrome\": \"^0.0.212\",\n        \"@types/node\": \"^18.11.12\",\n        \"@vitejs/plugin-vue\": \"^4.0.0\",\n        \"@vue/eslint-config-prettier\": \"^7.0.0\",\n        \"@vue/eslint-config-typescript\": \"^11.0.0\",\n        \"@vue/tsconfig\": \"^0.1.3\",\n        \"eslint\": \"^8.22.0\",\n        \"eslint-plugin-vue\": \"^9.3.0\",\n        \"npm-run-all\": \"^4.1.5\",\n        \"prettier\": \"^2.7.1\",\n        \"typescript\": \"~4.7.4\",\n        \"vite\": \"^4.0.0\",\n        \"vue-tsc\": \"^1.0.12\"\n    }\n}\n"
  },
  {
    "path": "extension/release/content-scripts/index-ad710f80.js",
    "content": "var lt=Object.defineProperty;var ut=(e,t,n)=>t in e?lt(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var k=(e,t,n)=>(ut(e,typeof t!=\"symbol\"?t+\"\":t,n),n);(function(){const t=document.createElement(\"link\").relList;if(t&&t.supports&&t.supports(\"modulepreload\"))return;for(const s of document.querySelectorAll('link[rel=\"modulepreload\"]'))r(s);new MutationObserver(s=>{for(const o of s)if(o.type===\"childList\")for(const i of o.addedNodes)i.tagName===\"LINK\"&&i.rel===\"modulepreload\"&&r(i)}).observe(document,{childList:!0,subtree:!0});function n(s){const o={};return s.integrity&&(o.integrity=s.integrity),s.referrerPolicy&&(o.referrerPolicy=s.referrerPolicy),s.crossOrigin===\"use-credentials\"?o.credentials=\"include\":s.crossOrigin===\"anonymous\"?o.credentials=\"omit\":o.credentials=\"same-origin\",o}function r(s){if(s.ep)return;s.ep=!0;const o=n(s);fetch(s.href,o)}})();const Fe=async e=>new Promise(t=>{setTimeout(()=>{t(\"\")},e)}),ge=async e=>new Promise(t=>{const n=setInterval(()=>{console.log(\"check wait for\"),e()&&(clearInterval(n),t(\"\"))},50)}),W=e=>{const t=new RegExp(\"(^|&)\"+e+\"=([^&]*)(&|$)\"),n=window.location.search.substring(1).match(t)||window.location.hash.substring(window.location.hash.search(/\\?/)+1).match(t);return n?decodeURIComponent(n[2]):null},_e=e=>{const t=\"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890\";let n=\"\";for(let r=0;r<e;r++)n+=t[Math.floor(Math.random()*t.length)];return n};class dt extends Array{constructor(){super();k(this,\"id\");k(this,\"node\");this.node=document.createElement(\"gin\"),this.id=_e(8),this.node.id=`gin-auto-${this.id}`,document.body.appendChild(this.node)}add(n){const r=new ft(this,n);return super.push(r),r}}class ft{constructor(t,n){k(this,\"id\");k(this,\"node\");k(this,\"parent\");k(this,\"value\");this.parent=t,this.node=document.createElement(\"gin\"),this.id=_e(8),this.node.id=this.id,this.parent.node.appendChild(this.node),this.value=n}get(){return this.value}set(t){if(this.value!==t){const n=this.value;this.value=t,this.node.dispatchEvent(new CustomEvent(\"change\",{detail:{oldValue:n,newValue:this.value}}))}this.node.dispatchEvent(new CustomEvent(\"set\"))}addEventListenr(t,n){this.node.addEventListener(t,n)}}function je(e,t){return function(){return e.apply(t,arguments)}}const{toString:pt}=Object.prototype,{getPrototypeOf:he}=Object,ee=(e=>t=>{const n=pt.call(t);return e[n]||(e[n]=n.slice(8,-1).toLowerCase())})(Object.create(null)),T=e=>(e=e.toLowerCase(),t=>ee(t)===e),te=e=>t=>typeof t===e,{isArray:q}=Array,$=te(\"undefined\");function ht(e){return e!==null&&!$(e)&&e.constructor!==null&&!$(e.constructor)&&S(e.constructor.isBuffer)&&e.constructor.isBuffer(e)}const De=T(\"ArrayBuffer\");function mt(e){let t;return typeof ArrayBuffer<\"u\"&&ArrayBuffer.isView?t=ArrayBuffer.isView(e):t=e&&e.buffer&&De(e.buffer),t}const yt=te(\"string\"),S=te(\"function\"),Ue=te(\"number\"),ne=e=>e!==null&&typeof e==\"object\",wt=e=>e===!0||e===!1,V=e=>{if(ee(e)!==\"object\")return!1;const t=he(e);return(t===null||t===Object.prototype||Object.getPrototypeOf(t)===null)&&!(Symbol.toStringTag in e)&&!(Symbol.iterator in e)},Et=T(\"Date\"),gt=T(\"File\"),bt=T(\"Blob\"),St=T(\"FileList\"),xt=e=>ne(e)&&S(e.pipe),Ot=e=>{let t;return e&&(typeof FormData==\"function\"&&e instanceof FormData||S(e.append)&&((t=ee(e))===\"formdata\"||t===\"object\"&&S(e.toString)&&e.toString()===\"[object FormData]\"))},Tt=T(\"URLSearchParams\"),At=e=>e.trim?e.trim():e.replace(/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g,\"\");function z(e,t,{allOwnKeys:n=!1}={}){if(e===null||typeof e>\"u\")return;let r,s;if(typeof e!=\"object\"&&(e=[e]),q(e))for(r=0,s=e.length;r<s;r++)t.call(null,e[r],r,e);else{const o=n?Object.getOwnPropertyNames(e):Object.keys(e),i=o.length;let c;for(r=0;r<i;r++)c=o[r],t.call(null,e[c],c,e)}}function qe(e,t){t=t.toLowerCase();const n=Object.keys(e);let r=n.length,s;for(;r-- >0;)if(s=n[r],t===s.toLowerCase())return s;return null}const Ie=(()=>typeof globalThis<\"u\"?globalThis:typeof self<\"u\"?self:typeof window<\"u\"?window:global)(),ve=e=>!$(e)&&e!==Ie;function le(){const{caseless:e}=ve(this)&&this||{},t={},n=(r,s)=>{const o=e&&qe(t,s)||s;V(t[o])&&V(r)?t[o]=le(t[o],r):V(r)?t[o]=le({},r):q(r)?t[o]=r.slice():t[o]=r};for(let r=0,s=arguments.length;r<s;r++)arguments[r]&&z(arguments[r],n);return t}const Rt=(e,t,n,{allOwnKeys:r}={})=>(z(t,(s,o)=>{n&&S(s)?e[o]=je(s,n):e[o]=s},{allOwnKeys:r}),e),Nt=e=>(e.charCodeAt(0)===65279&&(e=e.slice(1)),e),Ct=(e,t,n,r)=>{e.prototype=Object.create(t.prototype,r),e.prototype.constructor=e,Object.defineProperty(e,\"super\",{value:t.prototype}),n&&Object.assign(e.prototype,n)},Lt=(e,t,n,r)=>{let s,o,i;const c={};if(t=t||{},e==null)return t;do{for(s=Object.getOwnPropertyNames(e),o=s.length;o-- >0;)i=s[o],(!r||r(i,e,t))&&!c[i]&&(t[i]=e[i],c[i]=!0);e=n!==!1&&he(e)}while(e&&(!n||n(e,t))&&e!==Object.prototype);return t},Pt=(e,t,n)=>{e=String(e),(n===void 0||n>e.length)&&(n=e.length),n-=t.length;const r=e.indexOf(t,n);return r!==-1&&r===n},kt=e=>{if(!e)return null;if(q(e))return e;let t=e.length;if(!Ue(t))return null;const n=new Array(t);for(;t-- >0;)n[t]=e[t];return n},Bt=(e=>t=>e&&t instanceof e)(typeof Uint8Array<\"u\"&&he(Uint8Array)),Ft=(e,t)=>{const r=(e&&e[Symbol.iterator]).call(e);let s;for(;(s=r.next())&&!s.done;){const o=s.value;t.call(e,o[0],o[1])}},_t=(e,t)=>{let n;const r=[];for(;(n=e.exec(t))!==null;)r.push(n);return r},jt=T(\"HTMLFormElement\"),Dt=e=>e.toLowerCase().replace(/[-_\\s]([a-z\\d])(\\w*)/g,function(n,r,s){return r.toUpperCase()+s}),be=(({hasOwnProperty:e})=>(t,n)=>e.call(t,n))(Object.prototype),Ut=T(\"RegExp\"),He=(e,t)=>{const n=Object.getOwnPropertyDescriptors(e),r={};z(n,(s,o)=>{let i;(i=t(s,o,e))!==!1&&(r[o]=i||s)}),Object.defineProperties(e,r)},qt=e=>{He(e,(t,n)=>{if(S(e)&&[\"arguments\",\"caller\",\"callee\"].indexOf(n)!==-1)return!1;const r=e[n];if(S(r)){if(t.enumerable=!1,\"writable\"in t){t.writable=!1;return}t.set||(t.set=()=>{throw Error(\"Can not rewrite read-only method '\"+n+\"'\")})}})},It=(e,t)=>{const n={},r=s=>{s.forEach(o=>{n[o]=!0})};return q(e)?r(e):r(String(e).split(t)),n},vt=()=>{},Ht=(e,t)=>(e=+e,Number.isFinite(e)?e:t),oe=\"abcdefghijklmnopqrstuvwxyz\",Se=\"0123456789\",Me={DIGIT:Se,ALPHA:oe,ALPHA_DIGIT:oe+oe.toUpperCase()+Se},Mt=(e=16,t=Me.ALPHA_DIGIT)=>{let n=\"\";const{length:r}=t;for(;e--;)n+=t[Math.random()*r|0];return n};function $t(e){return!!(e&&S(e.append)&&e[Symbol.toStringTag]===\"FormData\"&&e[Symbol.iterator])}const zt=e=>{const t=new Array(10),n=(r,s)=>{if(ne(r)){if(t.indexOf(r)>=0)return;if(!(\"toJSON\"in r)){t[s]=r;const o=q(r)?[]:{};return z(r,(i,c)=>{const d=n(i,s+1);!$(d)&&(o[c]=d)}),t[s]=void 0,o}}return r};return n(e,0)},Jt=T(\"AsyncFunction\"),Vt=e=>e&&(ne(e)||S(e))&&S(e.then)&&S(e.catch),a={isArray:q,isArrayBuffer:De,isBuffer:ht,isFormData:Ot,isArrayBufferView:mt,isString:yt,isNumber:Ue,isBoolean:wt,isObject:ne,isPlainObject:V,isUndefined:$,isDate:Et,isFile:gt,isBlob:bt,isRegExp:Ut,isFunction:S,isStream:xt,isURLSearchParams:Tt,isTypedArray:Bt,isFileList:St,forEach:z,merge:le,extend:Rt,trim:At,stripBOM:Nt,inherits:Ct,toFlatObject:Lt,kindOf:ee,kindOfTest:T,endsWith:Pt,toArray:kt,forEachEntry:Ft,matchAll:_t,isHTMLForm:jt,hasOwnProperty:be,hasOwnProp:be,reduceDescriptors:He,freezeMethods:qt,toObjectSet:It,toCamelCase:Dt,noop:vt,toFiniteNumber:Ht,findKey:qe,global:Ie,isContextDefined:ve,ALPHABET:Me,generateString:Mt,isSpecCompliantForm:$t,toJSONObject:zt,isAsyncFn:Jt,isThenable:Vt};function m(e,t,n,r,s){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=new Error().stack,this.message=e,this.name=\"AxiosError\",t&&(this.code=t),n&&(this.config=n),r&&(this.request=r),s&&(this.response=s)}a.inherits(m,Error,{toJSON:function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:a.toJSONObject(this.config),code:this.code,status:this.response&&this.response.status?this.response.status:null}}});const $e=m.prototype,ze={};[\"ERR_BAD_OPTION_VALUE\",\"ERR_BAD_OPTION\",\"ECONNABORTED\",\"ETIMEDOUT\",\"ERR_NETWORK\",\"ERR_FR_TOO_MANY_REDIRECTS\",\"ERR_DEPRECATED\",\"ERR_BAD_RESPONSE\",\"ERR_BAD_REQUEST\",\"ERR_CANCELED\",\"ERR_NOT_SUPPORT\",\"ERR_INVALID_URL\"].forEach(e=>{ze[e]={value:e}});Object.defineProperties(m,ze);Object.defineProperty($e,\"isAxiosError\",{value:!0});m.from=(e,t,n,r,s,o)=>{const i=Object.create($e);return a.toFlatObject(e,i,function(d){return d!==Error.prototype},c=>c!==\"isAxiosError\"),m.call(i,e.message,t,n,r,s),i.cause=e,i.name=e.name,o&&Object.assign(i,o),i};const Gt=null;function ue(e){return a.isPlainObject(e)||a.isArray(e)}function Je(e){return a.endsWith(e,\"[]\")?e.slice(0,-2):e}function xe(e,t,n){return e?e.concat(t).map(function(s,o){return s=Je(s),!n&&o?\"[\"+s+\"]\":s}).join(n?\".\":\"\"):t}function Kt(e){return a.isArray(e)&&!e.some(ue)}const Wt=a.toFlatObject(a,{},null,function(t){return/^is[A-Z]/.test(t)});function re(e,t,n){if(!a.isObject(e))throw new TypeError(\"target must be an object\");t=t||new FormData,n=a.toFlatObject(n,{metaTokens:!0,dots:!1,indexes:!1},!1,function(h,E){return!a.isUndefined(E[h])});const r=n.metaTokens,s=n.visitor||u,o=n.dots,i=n.indexes,d=(n.Blob||typeof Blob<\"u\"&&Blob)&&a.isSpecCompliantForm(t);if(!a.isFunction(s))throw new TypeError(\"visitor must be a function\");function f(p){if(p===null)return\"\";if(a.isDate(p))return p.toISOString();if(!d&&a.isBlob(p))throw new m(\"Blob is not supported. Use a Buffer instead.\");return a.isArrayBuffer(p)||a.isTypedArray(p)?d&&typeof Blob==\"function\"?new Blob([p]):Buffer.from(p):p}function u(p,h,E){let g=p;if(p&&!E&&typeof p==\"object\"){if(a.endsWith(h,\"{}\"))h=r?h:h.slice(0,-2),p=JSON.stringify(p);else if(a.isArray(p)&&Kt(p)||(a.isFileList(p)||a.endsWith(h,\"[]\"))&&(g=a.toArray(p)))return h=Je(h),g.forEach(function(L,ct){!(a.isUndefined(L)||L===null)&&t.append(i===!0?xe([h],ct,o):i===null?h:h+\"[]\",f(L))}),!1}return ue(p)?!0:(t.append(xe(E,h,o),f(p)),!1)}const l=[],w=Object.assign(Wt,{defaultVisitor:u,convertValue:f,isVisitable:ue});function b(p,h){if(!a.isUndefined(p)){if(l.indexOf(p)!==-1)throw Error(\"Circular reference detected in \"+h.join(\".\"));l.push(p),a.forEach(p,function(g,C){(!(a.isUndefined(g)||g===null)&&s.call(t,g,a.isString(C)?C.trim():C,h,w))===!0&&b(g,h?h.concat(C):[C])}),l.pop()}}if(!a.isObject(e))throw new TypeError(\"data must be an object\");return b(e),t}function Oe(e){const t={\"!\":\"%21\",\"'\":\"%27\",\"(\":\"%28\",\")\":\"%29\",\"~\":\"%7E\",\"%20\":\"+\",\"%00\":\"\\0\"};return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g,function(r){return t[r]})}function me(e,t){this._pairs=[],e&&re(e,this,t)}const Ve=me.prototype;Ve.append=function(t,n){this._pairs.push([t,n])};Ve.toString=function(t){const n=t?function(r){return t.call(this,r,Oe)}:Oe;return this._pairs.map(function(s){return n(s[0])+\"=\"+n(s[1])},\"\").join(\"&\")};function Qt(e){return encodeURIComponent(e).replace(/%3A/gi,\":\").replace(/%24/g,\"$\").replace(/%2C/gi,\",\").replace(/%20/g,\"+\").replace(/%5B/gi,\"[\").replace(/%5D/gi,\"]\")}function Ge(e,t,n){if(!t)return e;const r=n&&n.encode||Qt,s=n&&n.serialize;let o;if(s?o=s(t,n):o=a.isURLSearchParams(t)?t.toString():new me(t,n).toString(r),o){const i=e.indexOf(\"#\");i!==-1&&(e=e.slice(0,i)),e+=(e.indexOf(\"?\")===-1?\"?\":\"&\")+o}return e}class Xt{constructor(){this.handlers=[]}use(t,n,r){return this.handlers.push({fulfilled:t,rejected:n,synchronous:r?r.synchronous:!1,runWhen:r?r.runWhen:null}),this.handlers.length-1}eject(t){this.handlers[t]&&(this.handlers[t]=null)}clear(){this.handlers&&(this.handlers=[])}forEach(t){a.forEach(this.handlers,function(r){r!==null&&t(r)})}}const Te=Xt,Ke={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1},Yt=typeof URLSearchParams<\"u\"?URLSearchParams:me,Zt=typeof FormData<\"u\"?FormData:null,en=typeof Blob<\"u\"?Blob:null,tn={isBrowser:!0,classes:{URLSearchParams:Yt,FormData:Zt,Blob:en},protocols:[\"http\",\"https\",\"file\",\"blob\",\"url\",\"data\"]},We=typeof window<\"u\"&&typeof document<\"u\",nn=(e=>We&&[\"ReactNative\",\"NativeScript\",\"NS\"].indexOf(e)<0)(typeof navigator<\"u\"&&navigator.product),rn=(()=>typeof WorkerGlobalScope<\"u\"&&self instanceof WorkerGlobalScope&&typeof self.importScripts==\"function\")(),sn=Object.freeze(Object.defineProperty({__proto__:null,hasBrowserEnv:We,hasStandardBrowserEnv:nn,hasStandardBrowserWebWorkerEnv:rn},Symbol.toStringTag,{value:\"Module\"})),x={...sn,...tn};function on(e,t){return re(e,new x.classes.URLSearchParams,Object.assign({visitor:function(n,r,s,o){return x.isNode&&a.isBuffer(n)?(this.append(r,n.toString(\"base64\")),!1):o.defaultVisitor.apply(this,arguments)}},t))}function an(e){return a.matchAll(/\\w+|\\[(\\w*)]/g,e).map(t=>t[0]===\"[]\"?\"\":t[1]||t[0])}function cn(e){const t={},n=Object.keys(e);let r;const s=n.length;let o;for(r=0;r<s;r++)o=n[r],t[o]=e[o];return t}function Qe(e){function t(n,r,s,o){let i=n[o++];if(i===\"__proto__\")return!0;const c=Number.isFinite(+i),d=o>=n.length;return i=!i&&a.isArray(s)?s.length:i,d?(a.hasOwnProp(s,i)?s[i]=[s[i],r]:s[i]=r,!c):((!s[i]||!a.isObject(s[i]))&&(s[i]=[]),t(n,r,s[i],o)&&a.isArray(s[i])&&(s[i]=cn(s[i])),!c)}if(a.isFormData(e)&&a.isFunction(e.entries)){const n={};return a.forEachEntry(e,(r,s)=>{t(an(r),s,n,0)}),n}return null}function ln(e,t,n){if(a.isString(e))try{return(t||JSON.parse)(e),a.trim(e)}catch(r){if(r.name!==\"SyntaxError\")throw r}return(n||JSON.stringify)(e)}const ye={transitional:Ke,adapter:[\"xhr\",\"http\"],transformRequest:[function(t,n){const r=n.getContentType()||\"\",s=r.indexOf(\"application/json\")>-1,o=a.isObject(t);if(o&&a.isHTMLForm(t)&&(t=new FormData(t)),a.isFormData(t))return s?JSON.stringify(Qe(t)):t;if(a.isArrayBuffer(t)||a.isBuffer(t)||a.isStream(t)||a.isFile(t)||a.isBlob(t))return t;if(a.isArrayBufferView(t))return t.buffer;if(a.isURLSearchParams(t))return n.setContentType(\"application/x-www-form-urlencoded;charset=utf-8\",!1),t.toString();let c;if(o){if(r.indexOf(\"application/x-www-form-urlencoded\")>-1)return on(t,this.formSerializer).toString();if((c=a.isFileList(t))||r.indexOf(\"multipart/form-data\")>-1){const d=this.env&&this.env.FormData;return re(c?{\"files[]\":t}:t,d&&new d,this.formSerializer)}}return o||s?(n.setContentType(\"application/json\",!1),ln(t)):t}],transformResponse:[function(t){const n=this.transitional||ye.transitional,r=n&&n.forcedJSONParsing,s=this.responseType===\"json\";if(t&&a.isString(t)&&(r&&!this.responseType||s)){const i=!(n&&n.silentJSONParsing)&&s;try{return JSON.parse(t)}catch(c){if(i)throw c.name===\"SyntaxError\"?m.from(c,m.ERR_BAD_RESPONSE,this,null,this.response):c}}return t}],timeout:0,xsrfCookieName:\"XSRF-TOKEN\",xsrfHeaderName:\"X-XSRF-TOKEN\",maxContentLength:-1,maxBodyLength:-1,env:{FormData:x.classes.FormData,Blob:x.classes.Blob},validateStatus:function(t){return t>=200&&t<300},headers:{common:{Accept:\"application/json, text/plain, */*\",\"Content-Type\":void 0}}};a.forEach([\"delete\",\"get\",\"head\",\"post\",\"put\",\"patch\"],e=>{ye.headers[e]={}});const we=ye,un=a.toObjectSet([\"age\",\"authorization\",\"content-length\",\"content-type\",\"etag\",\"expires\",\"from\",\"host\",\"if-modified-since\",\"if-unmodified-since\",\"last-modified\",\"location\",\"max-forwards\",\"proxy-authorization\",\"referer\",\"retry-after\",\"user-agent\"]),dn=e=>{const t={};let n,r,s;return e&&e.split(`\n`).forEach(function(i){s=i.indexOf(\":\"),n=i.substring(0,s).trim().toLowerCase(),r=i.substring(s+1).trim(),!(!n||t[n]&&un[n])&&(n===\"set-cookie\"?t[n]?t[n].push(r):t[n]=[r]:t[n]=t[n]?t[n]+\", \"+r:r)}),t},Ae=Symbol(\"internals\");function H(e){return e&&String(e).trim().toLowerCase()}function G(e){return e===!1||e==null?e:a.isArray(e)?e.map(G):String(e)}function fn(e){const t=Object.create(null),n=/([^\\s,;=]+)\\s*(?:=\\s*([^,;]+))?/g;let r;for(;r=n.exec(e);)t[r[1]]=r[2];return t}const pn=e=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim());function ie(e,t,n,r,s){if(a.isFunction(r))return r.call(this,t,n);if(s&&(t=n),!!a.isString(t)){if(a.isString(r))return t.indexOf(r)!==-1;if(a.isRegExp(r))return r.test(t)}}function hn(e){return e.trim().toLowerCase().replace(/([a-z\\d])(\\w*)/g,(t,n,r)=>n.toUpperCase()+r)}function mn(e,t){const n=a.toCamelCase(\" \"+t);[\"get\",\"set\",\"has\"].forEach(r=>{Object.defineProperty(e,r+n,{value:function(s,o,i){return this[r].call(this,t,s,o,i)},configurable:!0})})}let se=class{constructor(t){t&&this.set(t)}set(t,n,r){const s=this;function o(c,d,f){const u=H(d);if(!u)throw new Error(\"header name must be a non-empty string\");const l=a.findKey(s,u);(!l||s[l]===void 0||f===!0||f===void 0&&s[l]!==!1)&&(s[l||d]=G(c))}const i=(c,d)=>a.forEach(c,(f,u)=>o(f,u,d));return a.isPlainObject(t)||t instanceof this.constructor?i(t,n):a.isString(t)&&(t=t.trim())&&!pn(t)?i(dn(t),n):t!=null&&o(n,t,r),this}get(t,n){if(t=H(t),t){const r=a.findKey(this,t);if(r){const s=this[r];if(!n)return s;if(n===!0)return fn(s);if(a.isFunction(n))return n.call(this,s,r);if(a.isRegExp(n))return n.exec(s);throw new TypeError(\"parser must be boolean|regexp|function\")}}}has(t,n){if(t=H(t),t){const r=a.findKey(this,t);return!!(r&&this[r]!==void 0&&(!n||ie(this,this[r],r,n)))}return!1}delete(t,n){const r=this;let s=!1;function o(i){if(i=H(i),i){const c=a.findKey(r,i);c&&(!n||ie(r,r[c],c,n))&&(delete r[c],s=!0)}}return a.isArray(t)?t.forEach(o):o(t),s}clear(t){const n=Object.keys(this);let r=n.length,s=!1;for(;r--;){const o=n[r];(!t||ie(this,this[o],o,t,!0))&&(delete this[o],s=!0)}return s}normalize(t){const n=this,r={};return a.forEach(this,(s,o)=>{const i=a.findKey(r,o);if(i){n[i]=G(s),delete n[o];return}const c=t?hn(o):String(o).trim();c!==o&&delete n[o],n[c]=G(s),r[c]=!0}),this}concat(...t){return this.constructor.concat(this,...t)}toJSON(t){const n=Object.create(null);return a.forEach(this,(r,s)=>{r!=null&&r!==!1&&(n[s]=t&&a.isArray(r)?r.join(\", \"):r)}),n}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([t,n])=>t+\": \"+n).join(`\n`)}get[Symbol.toStringTag](){return\"AxiosHeaders\"}static from(t){return t instanceof this?t:new this(t)}static concat(t,...n){const r=new this(t);return n.forEach(s=>r.set(s)),r}static accessor(t){const r=(this[Ae]=this[Ae]={accessors:{}}).accessors,s=this.prototype;function o(i){const c=H(i);r[c]||(mn(s,i),r[c]=!0)}return a.isArray(t)?t.forEach(o):o(t),this}};se.accessor([\"Content-Type\",\"Content-Length\",\"Accept\",\"Accept-Encoding\",\"User-Agent\",\"Authorization\"]);a.reduceDescriptors(se.prototype,({value:e},t)=>{let n=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(r){this[n]=r}}});a.freezeMethods(se);const R=se;function ae(e,t){const n=this||we,r=t||n,s=R.from(r.headers);let o=r.data;return a.forEach(e,function(c){o=c.call(n,o,s.normalize(),t?t.status:void 0)}),s.normalize(),o}function Xe(e){return!!(e&&e.__CANCEL__)}function J(e,t,n){m.call(this,e??\"canceled\",m.ERR_CANCELED,t,n),this.name=\"CanceledError\"}a.inherits(J,m,{__CANCEL__:!0});function yn(e,t,n){const r=n.config.validateStatus;!n.status||!r||r(n.status)?e(n):t(new m(\"Request failed with status code \"+n.status,[m.ERR_BAD_REQUEST,m.ERR_BAD_RESPONSE][Math.floor(n.status/100)-4],n.config,n.request,n))}const wn=x.hasStandardBrowserEnv?{write(e,t,n,r,s,o){const i=[e+\"=\"+encodeURIComponent(t)];a.isNumber(n)&&i.push(\"expires=\"+new Date(n).toGMTString()),a.isString(r)&&i.push(\"path=\"+r),a.isString(s)&&i.push(\"domain=\"+s),o===!0&&i.push(\"secure\"),document.cookie=i.join(\"; \")},read(e){const t=document.cookie.match(new RegExp(\"(^|;\\\\s*)(\"+e+\")=([^;]*)\"));return t?decodeURIComponent(t[3]):null},remove(e){this.write(e,\"\",Date.now()-864e5)}}:{write(){},read(){return null},remove(){}};function En(e){return/^([a-z][a-z\\d+\\-.]*:)?\\/\\//i.test(e)}function gn(e,t){return t?e.replace(/\\/?\\/$/,\"\")+\"/\"+t.replace(/^\\/+/,\"\"):e}function Ye(e,t){return e&&!En(t)?gn(e,t):t}const bn=x.hasStandardBrowserEnv?function(){const t=/(msie|trident)/i.test(navigator.userAgent),n=document.createElement(\"a\");let r;function s(o){let i=o;return t&&(n.setAttribute(\"href\",i),i=n.href),n.setAttribute(\"href\",i),{href:n.href,protocol:n.protocol?n.protocol.replace(/:$/,\"\"):\"\",host:n.host,search:n.search?n.search.replace(/^\\?/,\"\"):\"\",hash:n.hash?n.hash.replace(/^#/,\"\"):\"\",hostname:n.hostname,port:n.port,pathname:n.pathname.charAt(0)===\"/\"?n.pathname:\"/\"+n.pathname}}return r=s(window.location.href),function(i){const c=a.isString(i)?s(i):i;return c.protocol===r.protocol&&c.host===r.host}}():function(){return function(){return!0}}();function Sn(e){const t=/^([-+\\w]{1,25})(:?\\/\\/|:)/.exec(e);return t&&t[1]||\"\"}function xn(e,t){e=e||10;const n=new Array(e),r=new Array(e);let s=0,o=0,i;return t=t!==void 0?t:1e3,function(d){const f=Date.now(),u=r[o];i||(i=f),n[s]=d,r[s]=f;let l=o,w=0;for(;l!==s;)w+=n[l++],l=l%e;if(s=(s+1)%e,s===o&&(o=(o+1)%e),f-i<t)return;const b=u&&f-u;return b?Math.round(w*1e3/b):void 0}}function Re(e,t){let n=0;const r=xn(50,250);return s=>{const o=s.loaded,i=s.lengthComputable?s.total:void 0,c=o-n,d=r(c),f=o<=i;n=o;const u={loaded:o,total:i,progress:i?o/i:void 0,bytes:c,rate:d||void 0,estimated:d&&i&&f?(i-o)/d:void 0,event:s};u[t?\"download\":\"upload\"]=!0,e(u)}}const On=typeof XMLHttpRequest<\"u\",Tn=On&&function(e){return new Promise(function(n,r){let s=e.data;const o=R.from(e.headers).normalize();let{responseType:i,withXSRFToken:c}=e,d;function f(){e.cancelToken&&e.cancelToken.unsubscribe(d),e.signal&&e.signal.removeEventListener(\"abort\",d)}let u;if(a.isFormData(s)){if(x.hasStandardBrowserEnv||x.hasStandardBrowserWebWorkerEnv)o.setContentType(!1);else if((u=o.getContentType())!==!1){const[h,...E]=u?u.split(\";\").map(g=>g.trim()).filter(Boolean):[];o.setContentType([h||\"multipart/form-data\",...E].join(\"; \"))}}let l=new XMLHttpRequest;if(e.auth){const h=e.auth.username||\"\",E=e.auth.password?unescape(encodeURIComponent(e.auth.password)):\"\";o.set(\"Authorization\",\"Basic \"+btoa(h+\":\"+E))}const w=Ye(e.baseURL,e.url);l.open(e.method.toUpperCase(),Ge(w,e.params,e.paramsSerializer),!0),l.timeout=e.timeout;function b(){if(!l)return;const h=R.from(\"getAllResponseHeaders\"in l&&l.getAllResponseHeaders()),g={data:!i||i===\"text\"||i===\"json\"?l.responseText:l.response,status:l.status,statusText:l.statusText,headers:h,config:e,request:l};yn(function(L){n(L),f()},function(L){r(L),f()},g),l=null}if(\"onloadend\"in l?l.onloadend=b:l.onreadystatechange=function(){!l||l.readyState!==4||l.status===0&&!(l.responseURL&&l.responseURL.indexOf(\"file:\")===0)||setTimeout(b)},l.onabort=function(){l&&(r(new m(\"Request aborted\",m.ECONNABORTED,e,l)),l=null)},l.onerror=function(){r(new m(\"Network Error\",m.ERR_NETWORK,e,l)),l=null},l.ontimeout=function(){let E=e.timeout?\"timeout of \"+e.timeout+\"ms exceeded\":\"timeout exceeded\";const g=e.transitional||Ke;e.timeoutErrorMessage&&(E=e.timeoutErrorMessage),r(new m(E,g.clarifyTimeoutError?m.ETIMEDOUT:m.ECONNABORTED,e,l)),l=null},x.hasStandardBrowserEnv&&(c&&a.isFunction(c)&&(c=c(e)),c||c!==!1&&bn(w))){const h=e.xsrfHeaderName&&e.xsrfCookieName&&wn.read(e.xsrfCookieName);h&&o.set(e.xsrfHeaderName,h)}s===void 0&&o.setContentType(null),\"setRequestHeader\"in l&&a.forEach(o.toJSON(),function(E,g){l.setRequestHeader(g,E)}),a.isUndefined(e.withCredentials)||(l.withCredentials=!!e.withCredentials),i&&i!==\"json\"&&(l.responseType=e.responseType),typeof e.onDownloadProgress==\"function\"&&l.addEventListener(\"progress\",Re(e.onDownloadProgress,!0)),typeof e.onUploadProgress==\"function\"&&l.upload&&l.upload.addEventListener(\"progress\",Re(e.onUploadProgress)),(e.cancelToken||e.signal)&&(d=h=>{l&&(r(!h||h.type?new J(null,e,l):h),l.abort(),l=null)},e.cancelToken&&e.cancelToken.subscribe(d),e.signal&&(e.signal.aborted?d():e.signal.addEventListener(\"abort\",d)));const p=Sn(w);if(p&&x.protocols.indexOf(p)===-1){r(new m(\"Unsupported protocol \"+p+\":\",m.ERR_BAD_REQUEST,e));return}l.send(s||null)})},de={http:Gt,xhr:Tn};a.forEach(de,(e,t)=>{if(e){try{Object.defineProperty(e,\"name\",{value:t})}catch{}Object.defineProperty(e,\"adapterName\",{value:t})}});const Ne=e=>`- ${e}`,An=e=>a.isFunction(e)||e===null||e===!1,Ze={getAdapter:e=>{e=a.isArray(e)?e:[e];const{length:t}=e;let n,r;const s={};for(let o=0;o<t;o++){n=e[o];let i;if(r=n,!An(n)&&(r=de[(i=String(n)).toLowerCase()],r===void 0))throw new m(`Unknown adapter '${i}'`);if(r)break;s[i||\"#\"+o]=r}if(!r){const o=Object.entries(s).map(([c,d])=>`adapter ${c} `+(d===!1?\"is not supported by the environment\":\"is not available in the build\"));let i=t?o.length>1?`since :\n`+o.map(Ne).join(`\n`):\" \"+Ne(o[0]):\"as no adapter specified\";throw new m(\"There is no suitable adapter to dispatch the request \"+i,\"ERR_NOT_SUPPORT\")}return r},adapters:de};function ce(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new J(null,e)}function Ce(e){return ce(e),e.headers=R.from(e.headers),e.data=ae.call(e,e.transformRequest),[\"post\",\"put\",\"patch\"].indexOf(e.method)!==-1&&e.headers.setContentType(\"application/x-www-form-urlencoded\",!1),Ze.getAdapter(e.adapter||we.adapter)(e).then(function(r){return ce(e),r.data=ae.call(e,e.transformResponse,r),r.headers=R.from(r.headers),r},function(r){return Xe(r)||(ce(e),r&&r.response&&(r.response.data=ae.call(e,e.transformResponse,r.response),r.response.headers=R.from(r.response.headers))),Promise.reject(r)})}const Le=e=>e instanceof R?e.toJSON():e;function _(e,t){t=t||{};const n={};function r(f,u,l){return a.isPlainObject(f)&&a.isPlainObject(u)?a.merge.call({caseless:l},f,u):a.isPlainObject(u)?a.merge({},u):a.isArray(u)?u.slice():u}function s(f,u,l){if(a.isUndefined(u)){if(!a.isUndefined(f))return r(void 0,f,l)}else return r(f,u,l)}function o(f,u){if(!a.isUndefined(u))return r(void 0,u)}function i(f,u){if(a.isUndefined(u)){if(!a.isUndefined(f))return r(void 0,f)}else return r(void 0,u)}function c(f,u,l){if(l in t)return r(f,u);if(l in e)return r(void 0,f)}const d={url:o,method:o,data:o,baseURL:i,transformRequest:i,transformResponse:i,paramsSerializer:i,timeout:i,timeoutMessage:i,withCredentials:i,withXSRFToken:i,adapter:i,responseType:i,xsrfCookieName:i,xsrfHeaderName:i,onUploadProgress:i,onDownloadProgress:i,decompress:i,maxContentLength:i,maxBodyLength:i,beforeRedirect:i,transport:i,httpAgent:i,httpsAgent:i,cancelToken:i,socketPath:i,responseEncoding:i,validateStatus:c,headers:(f,u)=>s(Le(f),Le(u),!0)};return a.forEach(Object.keys(Object.assign({},e,t)),function(u){const l=d[u]||s,w=l(e[u],t[u],u);a.isUndefined(w)&&l!==c||(n[u]=w)}),n}const et=\"1.6.7\",Ee={};[\"object\",\"boolean\",\"number\",\"function\",\"string\",\"symbol\"].forEach((e,t)=>{Ee[e]=function(r){return typeof r===e||\"a\"+(t<1?\"n \":\" \")+e}});const Pe={};Ee.transitional=function(t,n,r){function s(o,i){return\"[Axios v\"+et+\"] Transitional option '\"+o+\"'\"+i+(r?\". \"+r:\"\")}return(o,i,c)=>{if(t===!1)throw new m(s(i,\" has been removed\"+(n?\" in \"+n:\"\")),m.ERR_DEPRECATED);return n&&!Pe[i]&&(Pe[i]=!0,console.warn(s(i,\" has been deprecated since v\"+n+\" and will be removed in the near future\"))),t?t(o,i,c):!0}};function Rn(e,t,n){if(typeof e!=\"object\")throw new m(\"options must be an object\",m.ERR_BAD_OPTION_VALUE);const r=Object.keys(e);let s=r.length;for(;s-- >0;){const o=r[s],i=t[o];if(i){const c=e[o],d=c===void 0||i(c,o,e);if(d!==!0)throw new m(\"option \"+o+\" must be \"+d,m.ERR_BAD_OPTION_VALUE);continue}if(n!==!0)throw new m(\"Unknown option \"+o,m.ERR_BAD_OPTION)}}const fe={assertOptions:Rn,validators:Ee},P=fe.validators;let Q=class{constructor(t){this.defaults=t,this.interceptors={request:new Te,response:new Te}}async request(t,n){try{return await this._request(t,n)}catch(r){if(r instanceof Error){let s;Error.captureStackTrace?Error.captureStackTrace(s={}):s=new Error;const o=s.stack?s.stack.replace(/^.+\\n/,\"\"):\"\";r.stack?o&&!String(r.stack).endsWith(o.replace(/^.+\\n.+\\n/,\"\"))&&(r.stack+=`\n`+o):r.stack=o}throw r}}_request(t,n){typeof t==\"string\"?(n=n||{},n.url=t):n=t||{},n=_(this.defaults,n);const{transitional:r,paramsSerializer:s,headers:o}=n;r!==void 0&&fe.assertOptions(r,{silentJSONParsing:P.transitional(P.boolean),forcedJSONParsing:P.transitional(P.boolean),clarifyTimeoutError:P.transitional(P.boolean)},!1),s!=null&&(a.isFunction(s)?n.paramsSerializer={serialize:s}:fe.assertOptions(s,{encode:P.function,serialize:P.function},!0)),n.method=(n.method||this.defaults.method||\"get\").toLowerCase();let i=o&&a.merge(o.common,o[n.method]);o&&a.forEach([\"delete\",\"get\",\"head\",\"post\",\"put\",\"patch\",\"common\"],p=>{delete o[p]}),n.headers=R.concat(i,o);const c=[];let d=!0;this.interceptors.request.forEach(function(h){typeof h.runWhen==\"function\"&&h.runWhen(n)===!1||(d=d&&h.synchronous,c.unshift(h.fulfilled,h.rejected))});const f=[];this.interceptors.response.forEach(function(h){f.push(h.fulfilled,h.rejected)});let u,l=0,w;if(!d){const p=[Ce.bind(this),void 0];for(p.unshift.apply(p,c),p.push.apply(p,f),w=p.length,u=Promise.resolve(n);l<w;)u=u.then(p[l++],p[l++]);return u}w=c.length;let b=n;for(l=0;l<w;){const p=c[l++],h=c[l++];try{b=p(b)}catch(E){h.call(this,E);break}}try{u=Ce.call(this,b)}catch(p){return Promise.reject(p)}for(l=0,w=f.length;l<w;)u=u.then(f[l++],f[l++]);return u}getUri(t){t=_(this.defaults,t);const n=Ye(t.baseURL,t.url);return Ge(n,t.params,t.paramsSerializer)}};a.forEach([\"delete\",\"get\",\"head\",\"options\"],function(t){Q.prototype[t]=function(n,r){return this.request(_(r||{},{method:t,url:n,data:(r||{}).data}))}});a.forEach([\"post\",\"put\",\"patch\"],function(t){function n(r){return function(o,i,c){return this.request(_(c||{},{method:t,headers:r?{\"Content-Type\":\"multipart/form-data\"}:{},url:o,data:i}))}}Q.prototype[t]=n(),Q.prototype[t+\"Form\"]=n(!0)});const K=Q;let Nn=class tt{constructor(t){if(typeof t!=\"function\")throw new TypeError(\"executor must be a function.\");let n;this.promise=new Promise(function(o){n=o});const r=this;this.promise.then(s=>{if(!r._listeners)return;let o=r._listeners.length;for(;o-- >0;)r._listeners[o](s);r._listeners=null}),this.promise.then=s=>{let o;const i=new Promise(c=>{r.subscribe(c),o=c}).then(s);return i.cancel=function(){r.unsubscribe(o)},i},t(function(o,i,c){r.reason||(r.reason=new J(o,i,c),n(r.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(t){if(this.reason){t(this.reason);return}this._listeners?this._listeners.push(t):this._listeners=[t]}unsubscribe(t){if(!this._listeners)return;const n=this._listeners.indexOf(t);n!==-1&&this._listeners.splice(n,1)}static source(){let t;return{token:new tt(function(s){t=s}),cancel:t}}};const Cn=Nn;function Ln(e){return function(n){return e.apply(null,n)}}function Pn(e){return a.isObject(e)&&e.isAxiosError===!0}const pe={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(pe).forEach(([e,t])=>{pe[t]=e});const kn=pe;function nt(e){const t=new K(e),n=je(K.prototype.request,t);return a.extend(n,K.prototype,t,{allOwnKeys:!0}),a.extend(n,t,null,{allOwnKeys:!0}),n.create=function(s){return nt(_(e,s))},n}const y=nt(we);y.Axios=K;y.CanceledError=J;y.CancelToken=Cn;y.isCancel=Xe;y.VERSION=et;y.toFormData=re;y.AxiosError=m;y.Cancel=y.CanceledError;y.all=function(t){return Promise.all(t)};y.spread=Ln;y.isAxiosError=Pn;y.mergeConfig=_;y.AxiosHeaders=R;y.formToJSON=e=>Qe(a.isHTMLForm(e)?new FormData(e):e);y.getAdapter=Ze.getAdapter;y.HttpStatusCode=kn;y.default=y;const rt=y,{Axios:Vn,AxiosError:Gn,CanceledError:Kn,isCancel:Wn,CancelToken:Qn,VERSION:Xn,all:Yn,Cancel:Zn,isAxiosError:Bn,spread:er,toFormData:tr,AxiosHeaders:nr,HttpStatusCode:rr,formToJSON:sr,getAdapter:or,mergeConfig:ir}=rt,ke={checkTestExist:{url:\"/mooc/test/:tid\",method:\"GET\"},selectQustion:{url:\"/mooc/test/:tid\",method:\"POST\"},getAnnouncement:{url:\"/mooc/announcement\",method:\"GET\"},getNewExamInfo:{url:\"https://www.icourse163.org/mm-tiku/web/j/mocExamBean.getPaper.rpc\",method:\"POST\"},getNotice:{url:\"/mooc/notice/extension\",method:\"GET\"}},Fn=\"https://ginnnnnn.top/api\";async function _n(e,t,n){try{return await new Promise((r,s)=>{let o=ke[e].url;if(t)for(const[i,c]of Object.entries(t)){const d=RegExp(`(/):${i}(/)?`,\"g\");d.test(o)&&(o=o.replaceAll(d,`$1${c}$2`),Reflect.deleteProperty(t,i))}if(o.indexOf(\"http\")!=0&&(o=`${Fn}${o}`),n)for(const[i,c]of Object.entries(n))typeof c==\"object\"&&Reflect.set(n,i,JSON.stringify(c));rt({url:o,method:ke[e].method,params:t||{},data:n||{},headers:{\"Content-Type\":\"application/x-www-form-urlencoded\"}}).then(i=>{let c=\"\",d=!1;i.status!==200||!i.data?c=\"请求出错\":i.data.msg&&(c=i.data.msg,i.data.status===200&&(d=!0)),c&&console.log(c),r(i.data)}).catch(i=>{let c=i;Bn(i)&&(c=i.message),console.log(c),s(i)})})}catch{return{}}}const st=()=>_n,jn=()=>{const[e,t]=[new Array,new Array],n=document.querySelectorAll('input[id^=\"op_\"]');for(let s=0;s<n.length;s++){const o=n.item(s);e.push(Number.parseInt(o.id.split(\"_\")[2].slice(0,-13)))}const r=document.getElementsByClassName(\"m-FillBlank examMode u-questionItem\");for(let s=0;s<r.length;s++){const i=r.item(s).querySelector(\".j-richTxt\");t.push(i.innerText)}return{oidList:e,titleList:t}},Dn=(e,t)=>{if(e)for(const r of e)document.querySelector(`input[id*=\"_${r}\"]`).classList.add(\"gin-answer\");const n=document.getElementsByClassName(\"m-FillBlank examMode u-questionItem\");for(let r=0;r<n.length;r++){const o=n.item(r).querySelector(\".j-richTxt\"),i=Reflect.get(t,o.innerText),c=document.createElement(\"div\");c.innerHTML=i.title,o.appendChild(c);const d=i.stdAnswer.split(\"##%_YZPRLFH_%##\"),f=document.createElement(\"div\");for(let u=0;u<d.length;u++){const l=document.createElement(\"span\");l.classList.add(\"gin-answer-item\"),l.innerHTML=d[u],f.append(l),u!==d.length-1&&f.append(\" / \")}o.append(f)}},Un=e=>{var n,r;const t=document.getElementsByClassName(\"f-richEditorText j-richTxt f-fl\");for(let s=0;s<t.length;s++){const o=document.createElement(\"div\");o.classList.add(\"gin-answer-item\"),o.innerHTML=(n=Reflect.get(e,`${s}`))==null?void 0:n.answer,(r=t.item(s))==null||r.append(o)}},ot=()=>{var t,n,r,s;const e=document.getElementsByClassName(\"u-questionItem u-analysisQuestion analysisMode\");for(let o=0;o<e.length;o++){const i=(t=e.item(o))==null?void 0:t.getElementsByClassName(\"s\");for(let d=0;d<i.length;d++){const f=(r=(n=i.item(d))==null?void 0:n.lastElementChild)==null?void 0:r.querySelector(\"input\");f.checked=!0}const c=(s=e.item(o))==null?void 0:s.querySelector(\"textarea\");c.value=\"666\"}},qn=async(e,t)=>{const n=document.querySelector(\".u-homework-evaAction .bottombtnwrap .j-submitbtn\"),r=document.querySelector(\".u-homework-evaAction .xlinfo\"),s=document.querySelector(\".u-homework-evaAction .xlinfo .j-gotonext\");for(let o=0;o<e;o++)await ge(()=>r.style.display===\"none\"),ot(),await Fe(1e3),console.log(new Date),n.click(),await ge(()=>r.style.display!==\"none\"),t(o+1,e),s.click()},In=async()=>{var s,o;const e=st();let t=(s=document.getElementById(\"app\"))==null?void 0:s.getElementsByTagName(\"form\").item(0);for(;!t;)await Fe(1e3),t=(o=document.getElementById(\"app\"))==null?void 0:o.getElementsByTagName(\"form\").item(0);const n=async()=>{const i=await e(\"getNewExamInfo\",{csrfKey:document.cookie.match(/NTESSTUDYSI=([a-z0-9]+);/)[1]},{answerformId:W(\"aid\"),examId:W(\"eid\")});let c=[];for(let u of i.result.questions)for(let l of u.optionDtos)c.push(l.id);const d=await e(\"selectQustion\",{tid:i.result.tid},{oidList:c}),f=document.querySelectorAll(\".ant-checkbox-group>div, .ant-radio-group>div\");for(let u of d.data.choiceAns)f[c.indexOf(u)].classList.add(\"gin-answer-item\")},r=document.createElement(\"button\");r.className=\"ant-btn ant-btn-primary\",r.setAttribute(\"style\",\"margin-bottom: 16px\"),r.onclick=n,r.innerText=\"获取答案\",t==null||t.before(r)},X=st(),M=new dt,N=M.add(void 0),A=M.add(-1);let B=null;const[Y,F,Z]=[M.add(!1),M.add(!1),M.add(!1)];location.href.indexOf(\"newExam\")!==-1&&In();const vn=async()=>{if(O.innerText!==\"正在获取答案，请稍后...\"){if(O.innerText=\"正在获取答案，请稍后...\",N.get()===\"quiz\"){const e=await X(\"selectQustion\",{tid:B},jn());Dn(e.data.choiceAns,e.data.completionAns)}else if(N.get()===\"homework\"){const e=await X(\"selectQustion\",{tid:B},{});Un(e.data.homeworkAns)}O.innerText=\"\"}},it=document.createElement(\"style\");it.innerText=`\n    input.gin-answer:not(:checked) + label, #GinsMooc, .gin-answer-item {\n        background-color: #d9ecff;\n    }\n    .learnPageContentLeft {\n        background: rgb(240, 242, 245);\n    }\n    #GinsMooc {\n        margin-bottom: 12px !important;\n    }\n    .gin-function {\n        display: flex;\n        align-items: center;\n    }\n    .gin-function .u-btn {\n        margin-right: 16px;\n    }\n    .gin-state-tips {\n        font-size: 14px;\n    }\n`;document.head.append(it);const I=document.createElement(\"div\");I.id=\"GinsMooc\";I.classList.add(\"m-learnbox\");var Be;(Be=document.querySelector(\".learnPageContentLeft\"))==null||Be.prepend(I);if(location.href.indexOf(\"/spoc\")!==-1){const e=document.createElement(\"div\");e.innerHTML=\"当前课程为 SPOC 课程，可能无法获取答案。SPOC 课程可能会有关联的对外课程，你可以尝试搜索并加入，题目大概率是一样的\",I.prepend()}const Hn=async()=>{var t;const e=(await X(\"getNotice\",{version:\"v2.2.1\"},void 0)).data;if(console.log(e),!((t=localStorage.getItem(\"Gins-ignore-notice\"))!=null&&t.split(\",\").find(n=>Number.parseInt(n)===e.id))){const n=document.createElement(\"div\");n.innerHTML=e.content;const r=document.createElement(\"a\");r.innerText=\"不再提醒\",r.onclick=()=>{const s=localStorage.getItem(\"Gins-ignore-notice\");localStorage.setItem(\"Gins-ignore-notice\",s?`${s},${e.id}`:`${e.id}`),n.remove(),r.remove()},r.style.marginLeft=\"16px\",n.append(r),I.prepend(n)}};Hn();const v=document.createElement(\"div\");v.classList.add(\"gin-function\");I.append(v);const j=document.createElement(\"button\");j.classList.add(\"u-btn\",\"u-btn-default\",\"f-dn\");j.onclick=vn;j.innerText=\"获取答案\";v.append(j);const D=document.createElement(\"button\");D.classList.add(\"u-btn\",\"u-btn-default\",\"f-dn\");D.onclick=()=>{ot(),window.scroll({top:document.documentElement.scrollHeight,behavior:\"smooth\"})};D.innerText=\"一键互评\";v.append(D);const U=document.createElement(\"button\");U.classList.add(\"u-btn\",\"u-btn-default\",\"f-dn\");U.onclick=()=>{qn(5,(e,t)=>{e>=t?O.innerText=`已完成 ${t} 次互评`:O.innerText=`自动互评中（${e} / ${t}）`})};U.innerText=\"自动互评\";v.append(U);const O=document.createElement(\"div\");O.classList.add(\"gin-state-tips\");v.append(O);window.addEventListener(\"hashchange\",()=>{B=W(\"id\"),location.hash.indexOf(\"quiz\")!==-1||location.hash.indexOf(\"examObject\")!==-1?N.set(\"quiz\"):location.hash.indexOf(\"hw\")!==-1||location.hash.indexOf(\"examSubjective\")!==-1?N.set(\"homework\"):N.set(void 0)});window.setInterval(()=>{Z.set(location.hash.indexOf(\"examlist\")!==-1);const e=document.querySelector(\".j-prepare.prepare\");F.set(e&&!e.classList.contains(\"f-dn\")||document.querySelector(\".j-homework-paper\")!==null||Z.get()),Y.set(document.querySelector(\".u-questionItem.u-analysisQuestion.analysisMode\")!==null)},100);const at=async()=>{if(console.log(\"onTestChange\",N.get(),F.get(),Z.get(),B),N.get()===\"quiz\"&&!F.get()||N.get()===\"homework\"?j.classList.remove(\"f-dn\"):j.classList.add(\"f-dn\"),A.set(-2),F.get()&&B)if((await X(\"checkTestExist\",{tid:B,type:\"isExisting\"},void 0)).data.existing){A.set(-1);return}else{const t=new EventSource(`https://ginnnnnn.top/api/mooc/course/refresh/${W(\"tid\")}`);t.onmessage=n=>{console.log(n.data);const r=JSON.parse(n.data);r&&r.total>0&&A.set(Math.round(r.finished/r.total*100)),(A.value===100||r.status===400)&&(t.close(),r.msg&&A.set(-1))}}else if(!Z.get()){A.set(-1);return}},Mn=()=>{console.log(\"onModeChange\",Y.get()),Y.get()?(D.classList.remove(\"f-dn\"),U.classList.remove(\"f-dn\")):(D.classList.add(\"f-dn\"),U.classList.add(\"f-dn\"))};Y.addEventListenr(\"change\",Mn);F.addEventListenr(\"change\",at);N.addEventListenr(\"change\",at);A.addEventListenr(\"set\",()=>{switch(A.get()){case-2:O.innerText=\"正在检查课程...\";break;case-1:O.innerText=F.get()?\"已准备就绪\":\"\";break;default:O.innerText=`正在更新课程...${A.get()}%`;break}});\n"
  },
  {
    "path": "extension/release/manifest.json",
    "content": "{\n    \"manifest_version\": 3,\n    \"name\": \"GinsMooc Extension\",\n    \"version\": \"2.2.1\",\n    \"description\": \"A Chrome extension to get the mooc answers and evaluate auto automatically.\",\n    \"icons\": {\n        \"16\": \"icons/favicon16.png\",\n        \"32\": \"icons/favicon32.png\",\n        \"48\": \"icons/favicon48.png\",\n        \"128\": \"icons/favicon128.png\"\n    },\n    \"author\": { \"email\": \"ginnnnnn@qq.com\" },\n    \"homepage_url\": \"https://ginnnnnn.top/mooc\",\n    \"content_scripts\": [\n        {\n            \"matches\": [\"https://www.icourse163.org/*learn/*?tid=*\", \"https://www.icourse163.org/mooc/main/newExam*\"],\n            \"js\": [\"content-scripts/index-ad710f80.js\"],\n            \"run_at\": \"document_end\"\n        }\n    ]\n}"
  },
  {
    "path": "extension/src/main.ts",
    "content": "import { CustomRefList } from \"./plugins/react\"\nimport { useApiAccess } from \"./plugins/apiAccess\"\nimport { sleep, getUrlParam } from \"./plugins/tool\"\nimport { getQuizQuestionKeys, setQuizAnswer, setHomeworkAnswer, autoEvaluate, batchEvaluate } from \"./plugins/mooc\"\nimport { newExamHandle } from \"./newExam\"\n\nconst apiAccess = useApiAccess()\nconst refList = new CustomRefList()\nconst testType = refList.add(<\"quiz\" | \"homework\" | undefined>undefined)\nconst newCourseState = refList.add(-1)\nlet testId: string | null = null\nconst [analysis, prepare, examlist] = [refList.add(false), refList.add(false), refList.add(false)]\n\nif (location.href.indexOf(\"newExam\") !== -1) {\n    newExamHandle()\n}\n\nconst getAnswer = async () => {\n    if (stateTips.innerText === \"正在获取答案，请稍后...\") {\n        return\n    }\n    stateTips.innerText = \"正在获取答案，请稍后...\"\n    if (testType.get() === \"quiz\") {\n        const answers = await apiAccess(\"selectQustion\", { tid: testId as string }, getQuizQuestionKeys())\n        setQuizAnswer(answers.data.choiceAns as number[], answers.data.completionAns as Object)\n    } else if (testType.get() === \"homework\") {\n        const answers = await apiAccess(\"selectQustion\", { tid: testId as string }, {})\n        setHomeworkAnswer(answers.data.homeworkAns as Object)\n    }\n    stateTips.innerText = \"\"\n}\n\nconst styleNode = document.createElement(\"style\")\nstyleNode.innerText = `\n    input.gin-answer:not(:checked) + label, #GinsMooc, .gin-answer-item {\n        background-color: #d9ecff;\n    }\n    .learnPageContentLeft {\n        background: rgb(240, 242, 245);\n    }\n    #GinsMooc {\n        margin-bottom: 12px !important;\n    }\n    .gin-function {\n        display: flex;\n        align-items: center;\n    }\n    .gin-function .u-btn {\n        margin-right: 16px;\n    }\n    .gin-state-tips {\n        font-size: 14px;\n    }\n`\ndocument.head.append(styleNode)\n\nconst wrapperNode = document.createElement(\"div\")\nwrapperNode.id = \"GinsMooc\"\nwrapperNode.classList.add(\"m-learnbox\")\ndocument.querySelector(\".learnPageContentLeft\")?.prepend(wrapperNode)\n\nif (location.href.indexOf(\"/spoc\") !== -1) {\n    const spocTipsNode = document.createElement(\"div\")\n    spocTipsNode.innerHTML = \"当前课程为 SPOC 课程，可能无法获取答案。SPOC 课程可能会有关联的对外课程，你可以尝试搜索并加入，题目大概率是一样的\"\n    wrapperNode.prepend()\n}\n\nconst setNotice = async () => {\n    const notice = (await apiAccess(\"getNotice\", { version: \"v2.2.1\" }, undefined)).data\n    console.log(notice)\n    if (\n        !localStorage\n            .getItem(\"Gins-ignore-notice\")\n            ?.split(\",\")\n            .find((item) => Number.parseInt(item) === notice.id)\n    ) {\n        const noticeNode = document.createElement(\"div\")\n        noticeNode.innerHTML = notice.content\n        const closeBtn = document.createElement(\"a\")\n        closeBtn.innerText = \"不再提醒\"\n        closeBtn.onclick = () => {\n            const origin = localStorage.getItem(\"Gins-ignore-notice\")\n            localStorage.setItem(\"Gins-ignore-notice\", origin ? `${origin},${notice.id}` : `${notice.id}`)\n            noticeNode.remove()\n            closeBtn.remove()\n        }\n        closeBtn.style.marginLeft = \"16px\"\n        noticeNode.append(closeBtn)\n        wrapperNode.prepend(noticeNode)\n    }\n}\nsetNotice()\n\nconst functionNode = document.createElement(\"div\")\nfunctionNode.classList.add(\"gin-function\")\nwrapperNode.append(functionNode)\n\nconst getAnswerBtn = document.createElement(\"button\")\ngetAnswerBtn.classList.add(\"u-btn\", \"u-btn-default\", \"f-dn\")\ngetAnswerBtn.onclick = getAnswer\ngetAnswerBtn.innerText = \"获取答案\"\nfunctionNode.append(getAnswerBtn)\n\nconst evaluateBtn = document.createElement(\"button\")\nevaluateBtn.classList.add(\"u-btn\", \"u-btn-default\", \"f-dn\")\nevaluateBtn.onclick = () => {\n    autoEvaluate()\n    window.scroll({ top: document.documentElement.scrollHeight, behavior: \"smooth\" })\n}\nevaluateBtn.innerText = \"一键互评\"\nfunctionNode.append(evaluateBtn)\n\nconst batchEvaluateBtn = document.createElement(\"button\")\nbatchEvaluateBtn.classList.add(\"u-btn\", \"u-btn-default\", \"f-dn\")\nbatchEvaluateBtn.onclick = () => {\n    batchEvaluate(5, (finish: number, total: number) => {\n        if (finish >= total) {\n            stateTips.innerText = `已完成 ${total} 次互评`\n        } else {\n            stateTips.innerText = `自动互评中（${finish} / ${total}）`\n        }\n    })\n}\nbatchEvaluateBtn.innerText = \"自动互评\"\nfunctionNode.append(batchEvaluateBtn)\n\n\nconst stateTips = document.createElement(\"div\")\nstateTips.classList.add(\"gin-state-tips\")\nfunctionNode.append(stateTips)\n\nwindow.addEventListener(\"hashchange\", () => {\n    testId = getUrlParam(\"id\")\n    if (location.hash.indexOf(\"quiz\") !== -1 || location.hash.indexOf(\"examObject\") !== -1) {\n        testType.set(\"quiz\")\n    } else if (location.hash.indexOf(\"hw\") !== -1 || location.hash.indexOf(\"examSubjective\") !== -1) {\n        testType.set(\"homework\")\n    } else {\n        testType.set(undefined)\n    }\n})\n\nwindow.setInterval(() => {\n    examlist.set(location.hash.indexOf(\"examlist\") !== -1)\n    const prepareNode = document.querySelector(\".j-prepare.prepare\")\n    prepare.set(\n        (prepareNode && !prepareNode.classList.contains(\"f-dn\")) ||\n            document.querySelector(\".j-homework-paper\") !== null ||\n            examlist.get()\n    )\n    analysis.set(document.querySelector(\".u-questionItem.u-analysisQuestion.analysisMode\") !== null)\n}, 100);\n\nconst onTestChange = async () => {\n    console.log(\"onTestChange\", testType.get(), prepare.get(), examlist.get(), testId)\n    if ((testType.get() === \"quiz\" && !prepare.get()) || testType.get() === \"homework\") {\n        getAnswerBtn.classList.remove(\"f-dn\")\n    } else {\n        getAnswerBtn.classList.add(\"f-dn\")\n    }\n\n    newCourseState.set(-2)\n    if (prepare.get() && testId) {\n        const res = await apiAccess(\"checkTestExist\", { tid: testId, type: \"isExisting\" }, undefined)\n        if (res.data.existing) {\n            newCourseState.set(-1)\n            return\n        } else {\n            const eventSource = new EventSource(`https://ginnnnnn.top/api/mooc/course/refresh/${getUrlParam(\"tid\")}`)\n            eventSource.onmessage = (event) => {\n                console.log(event.data)\n                const state = JSON.parse(event.data)\n                if (state && state.total > 0) {\n                    newCourseState.set(Math.round((state.finished / state.total) * 100))\n                }\n                if (newCourseState.value === 100 || state.status === 400) {\n                    eventSource.close()\n                    if (state.msg) {\n                        newCourseState.set(-1)\n                    }\n                }\n            }\n        }\n    } else if (!examlist.get()) {\n        newCourseState.set(-1)\n        return\n    }\n}\n\nconst onModeChange = () => {\n    console.log(\"onModeChange\", analysis.get())\n    if (analysis.get()) {\n        evaluateBtn.classList.remove(\"f-dn\")\n        batchEvaluateBtn.classList.remove(\"f-dn\")\n    } else {\n        evaluateBtn.classList.add(\"f-dn\")\n        batchEvaluateBtn.classList.add(\"f-dn\")\n    }\n}\n\nanalysis.addEventListenr(\"change\", onModeChange)\nprepare.addEventListenr(\"change\", onTestChange)\ntestType.addEventListenr(\"change\", onTestChange)\nnewCourseState.addEventListenr(\"set\", () => {\n    switch (newCourseState.get()) {\n        case -2:\n            stateTips.innerText = \"正在检查课程...\"\n            break\n        case -1:\n            stateTips.innerText = prepare.get() ? \"已准备就绪\" : \"\"\n            break\n        default:\n            stateTips.innerText = `正在更新课程...${newCourseState.get()}%`\n            break\n    }\n})\n"
  },
  {
    "path": "extension/src/newExam.ts",
    "content": "import { useApiAccess } from \"./plugins/apiAccess\"\nimport { sleep, getUrlParam } from \"./plugins/tool\"\n\nexport const newExamHandle = async () => {\n    const apiAccess = useApiAccess()\n    let form = document.getElementById(\"app\")?.getElementsByTagName(\"form\").item(0)\n\n    while (!form) {\n        await sleep(1000)\n        form = document.getElementById(\"app\")?.getElementsByTagName(\"form\").item(0)\n    }\n\n    const getAnswer = async () => {\n        const info = await apiAccess(\n            \"getNewExamInfo\",\n            { csrfKey: document.cookie.match(/NTESSTUDYSI=([a-z0-9]+);/)![1] },\n            { answerformId: getUrlParam(\"aid\")!, examId: getUrlParam(\"eid\")! }\n        )\n        let oidList: Array<number> = []\n        for (let question of info.result.questions) {\n            for (let option of question.optionDtos) {\n                oidList.push(option.id)\n            }\n        }\n        const answers = await apiAccess(\"selectQustion\", { tid: info.result.tid }, { oidList: oidList })\n        const optionElements = document.querySelectorAll(\n            \".ant-checkbox-group>div, .ant-radio-group>div\"\n        ) as NodeListOf<HTMLDivElement>\n        // console.log(optionElements)\n        for (let id of answers.data.choiceAns!) {\n            optionElements[oidList.indexOf(id)].classList.add(\"gin-answer-item\")\n        }\n    }\n\n    const getAnswerBtn = document.createElement(\"button\")\n    getAnswerBtn.className = \"ant-btn ant-btn-primary\"\n    getAnswerBtn.setAttribute(\"style\", \"margin-bottom: 16px\")\n    getAnswerBtn.onclick = getAnswer\n    getAnswerBtn.innerText = \"获取答案\"\n    form?.before(getAnswerBtn)\n}\n"
  },
  {
    "path": "extension/src/plugins/apiAccess.ts",
    "content": "import type { ApiKeyType, ApiResponseType, ApiRequestType } from \"../type/api\"\nimport type { App } from \"vue\"\nimport { isAxiosError } from \"axios\"\nimport apiInfo from \"../type/api\"\nimport axios from \"axios\"\n\nconst baseUrl = \"https://ginnnnnn.top/api\"\n\nasync function apiAccess<T extends ApiKeyType>(api: T): Promise<ApiResponseType[T]>\nasync function apiAccess<T extends ApiKeyType>(\n    api: T,\n    params: ApiRequestType[T][\"params\"],\n    data: ApiRequestType[T][\"data\"]\n): Promise<ApiResponseType[T]>\n\n/** 函数重载 */\nasync function apiAccess<T extends ApiKeyType>(\n    api: T,\n    params?: ApiRequestType[T][\"params\"],\n    data?: ApiRequestType[T][\"data\"]\n) {\n    /** 错误处理，主要catch 404，调用者不再需要try-catch */\n    try {\n        return await new Promise<ApiResponseType[T]>((resolve, reject) => {\n            /** 查询参数转动态路由参数 */\n            let url = apiInfo[api].url\n            if (params) {\n                for (const [key, val] of Object.entries(params)) {\n                    const reg = RegExp(`(/):${key}(/)?`, \"g\")\n                    if (reg.test(url)) {\n                        url = url.replaceAll(reg, `$1${val}$2`)\n                        Reflect.deleteProperty(params, key)\n                    }\n                }\n            }\n            if (url.indexOf(\"http\") != 0) {\n                url = `${baseUrl}${url}`\n            }\n            /** 将对象转为json字符串 */\n            if (data) {\n                for (const [key, val] of Object.entries(data)) {\n                    if (typeof val === \"object\") {\n                        Reflect.set(data, key, JSON.stringify(val))\n                    }\n                }\n            }\n            /** 异步发送请求 */\n            axios<ApiResponseType[T]>({\n                url: url,\n                method: apiInfo[api].method,\n                params: params || {},\n                data: data || {},\n                headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" }\n            })\n                .then((res) => {\n                    let message = \"\",\n                        success = false\n                    if (res.status !== 200 || !res.data) {\n                        message = \"请求出错\"\n                    } else if (res.data.msg) {\n                        message = res.data.msg\n                        if (res.data.status === 200) {\n                            success = true\n                        }\n                    }\n                    if (message) {\n                        console.log(message)\n                    }\n                    resolve(res.data)\n                })\n                .catch((error) => {\n                    let message = error\n                    if (isAxiosError(error)) {\n                        message = error.message\n                    }\n                    console.log(message)\n                    reject(error)\n                })\n        })\n    } catch {\n        return {}\n    }\n}\n\nexport const useApiAccess = () => apiAccess\n\nexport default {\n    install: (app: App) => {\n        app.config.globalProperties.$apiAccess = apiAccess\n    }\n}\n"
  },
  {
    "path": "extension/src/plugins/mooc.ts",
    "content": "import type { quiz, option } from \"../type/mooc\"\nimport { sleep, waitFor } from \"./tool\"\n\nconst getQuizQuestionKeys = () => {\n    const [oidList, titleList] = [new Array<number>(), new Array<string>()]\n    const choices = document.querySelectorAll('input[id^=\"op_\"]')\n    for (let i = 0; i < choices.length; i++) {\n        const choice = choices.item(i) as HTMLInputElement\n        oidList.push(Number.parseInt(choice.id.split(\"_\")[2].slice(0, -13)))\n    }\n    const completions = document.getElementsByClassName(\"m-FillBlank examMode u-questionItem\")\n    for (let i = 0; i < completions.length; i++) {\n        const questionNode = completions.item(i) as HTMLDivElement\n        const titleNode = questionNode.querySelector(\".j-richTxt\") as HTMLDivElement\n        titleList.push(titleNode.innerText)\n    }\n    return { oidList, titleList }\n}\n\nconst setQuizAnswer = (choiceAns: number[], completionAns: Object) => {\n    if (choiceAns) {\n        for (const id of choiceAns) {\n            const node = document.querySelector(`input[id*=\"_${id}\"]`) as HTMLInputElement\n            node.classList.add(\"gin-answer\")\n        }\n    }\n    const completions = document.getElementsByClassName(\"m-FillBlank examMode u-questionItem\")\n    for (let i = 0; i < completions.length; i++) {\n        const questionNode = completions.item(i) as HTMLDivElement\n        const titleNode = questionNode.querySelector(\".j-richTxt\") as HTMLDivElement\n        const question = Reflect.get(completionAns, titleNode.innerText) as quiz\n\n        const newTitleNode = document.createElement(\"div\")\n        newTitleNode.innerHTML = question.title\n        titleNode.appendChild(newTitleNode)\n\n        const answerList = (<string>question.stdAnswer).split(\"##%_YZPRLFH_%##\")\n        const answerListNode = document.createElement(\"div\")\n        for (let j = 0; j < answerList.length; j++) {\n            const answerNode = document.createElement(\"span\")\n            answerNode.classList.add(\"gin-answer-item\")\n            answerNode.innerHTML = answerList[j]\n            answerListNode.append(answerNode)\n            if (j !== answerList.length - 1) {\n                answerListNode.append(\" / \")\n            }\n        }\n        titleNode.append(answerListNode)\n    }\n}\n\nconst setHomeworkAnswer = (homeworkAns: Object) => {\n    const homeworks = document.getElementsByClassName(\"f-richEditorText j-richTxt f-fl\")\n    for (let i = 0; i < homeworks.length; i++) {\n        const answerNode = document.createElement(\"div\")\n        answerNode.classList.add('gin-answer-item')\n        answerNode.innerHTML = Reflect.get(homeworkAns, `${i}`)?.answer as string\n        homeworks.item(i)?.append(answerNode)\n    }\n}\n\nconst autoEvaluate = () => {\n    const analysis = document.getElementsByClassName(\"u-questionItem u-analysisQuestion analysisMode\")\n    for (let i = 0; i < analysis.length; i++) {\n        const radioGroup = analysis.item(i)?.getElementsByClassName(\"s\") as HTMLCollection\n        for (let j = 0; j < radioGroup.length; j++) {\n            const radio = radioGroup.item(j)?.lastElementChild?.querySelector(\"input\") as HTMLInputElement\n            radio.checked = true\n        }\n        const textarea = analysis.item(i)?.querySelector(\"textarea\") as HTMLTextAreaElement\n        textarea.value = \"666\"\n    }\n}\n\nconst batchEvaluate = async (times: number, updateCaller: (finish: number, total: number) => any) => {\n    const submitBtn = document.querySelector(\".u-homework-evaAction .bottombtnwrap .j-submitbtn\") as HTMLDivElement\n    const xlinfo = document.querySelector(\".u-homework-evaAction .xlinfo\") as HTMLDivElement\n    const nextBtn = document.querySelector(\".u-homework-evaAction .xlinfo .j-gotonext\") as HTMLDivElement\n    for (let i = 0; i < times; i++) {\n        await waitFor(() => xlinfo.style.display === 'none')\n        autoEvaluate()\n        await sleep(1000)\n        console.log(new Date())\n        submitBtn.click()\n        await waitFor(() => xlinfo.style.display !== 'none')\n        updateCaller(i + 1, times)\n        nextBtn.click()\n    }\n}\n\nexport { getQuizQuestionKeys, setHomeworkAnswer, setQuizAnswer, autoEvaluate, batchEvaluate }\n"
  },
  {
    "path": "extension/src/plugins/react.ts",
    "content": "import { randomString } from \"./tool\"\n\nclass CustomRefList extends Array<CustomRef<any>> {\n    id: string\n    node: HTMLElement\n\n    constructor() {\n        super()\n        this.node = document.createElement(\"gin\")\n        this.id = randomString(8)\n        this.node.id = `gin-auto-${this.id}`\n        document.body.appendChild(this.node)\n    }\n\n    add<T>(value: T): CustomRef<T> {\n        const add = new CustomRef(this, value)\n        super.push(add)\n        return add\n    }\n}\n\nclass CustomRef<T> {\n    id: string\n    node: HTMLElement\n    parent: CustomRefList\n    value: T\n\n    constructor(parent: CustomRefList, value: T) {\n        this.parent = parent\n        this.node = document.createElement(\"gin\")\n        this.id = randomString(8)\n        this.node.id = this.id\n        this.parent.node.appendChild(this.node)\n        this.value = value\n    }\n\n    get(): T {\n        return this.value\n    }\n\n    set(value: T): void {\n        if (this.value !== value) {\n            const oldValue = this.value\n            this.value = value\n            this.node.dispatchEvent(new CustomEvent(\"change\", { detail: { oldValue, newValue: this.value } }))\n        }\n        this.node.dispatchEvent(new CustomEvent(\"set\"))\n    }\n\n    addEventListenr(eventName: \"change\" | \"set\", callback: (ev: Event) => void): void {\n        this.node.addEventListener(eventName, callback)\n    }\n}\n\nexport { CustomRefList }"
  },
  {
    "path": "extension/src/plugins/tool.ts",
    "content": "const sleep = async (ms: number) => {\n    return new Promise((resolve) => {\n        setTimeout(() => {\n            resolve('')\n        }, ms)\n    })\n}\n\nconst waitFor = async (checker: any) => {\n    return new Promise((resolve) => {\n        const checkDisplay = setInterval(() => {\n            console.log('check wait for')\n            if (checker()) {\n                clearInterval(checkDisplay);\n                resolve('');\n            }\n        }, 50);\n    });\n}\n\nconst getUrlParam = (key: string) => {\n    const reg = new RegExp('(^|&)' + key + '=([^&]*)(&|$)')\n    const result = window.location.search.substring(1).match(reg)\n        || window.location.hash.substring((window.location.hash.search(/\\?/)) + 1).match(reg)\n    return result ? decodeURIComponent(result[2]) : null\n}\n\nconst randomString = (length: number) => {\n    const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'\n    let result = ''\n    for (let i = 0; i < length; i++) {\n        result += chars[Math.floor(Math.random() * chars.length)]\n    }\n    return result\n}\n\nexport { sleep, getUrlParam, randomString, waitFor }"
  },
  {
    "path": "extension/src/type/api.ts",
    "content": "import type { homework, quiz, notice } from \"./mooc\"\n\ntype RequestType = {\n    params?: Object\n    data?: Object\n}\n\ntype Response<T = any> = {\n    status: number\n    data: T\n    msg: string\n}\n\nconst apiInfo = {\n    checkTestExist: {\n        url: \"/mooc/test/:tid\",\n        method: \"GET\"\n    },\n    selectQustion: {\n        url: \"/mooc/test/:tid\",\n        method: \"POST\"\n    },\n    getAnnouncement: {\n        url: \"/mooc/announcement\",\n        method: \"GET\"\n    },\n    getNewExamInfo: {\n        url: \"https://www.icourse163.org/mm-tiku/web/j/mocExamBean.getPaper.rpc\",\n        method: \"POST\"\n    },\n    getNotice: {\n        url: \"/mooc/notice/extension\",\n        method: \"GET\"\n    }\n}\n\nexport type ApiKeyType = keyof typeof apiInfo\n\nexport interface ApiResponseType {\n    checkTestExist: Response<{\n        existing: boolean\n    }>\n    selectQustion: Response<{\n        choiceAns?: number[]\n        completionAns?: Object\n        homeworkAns?: Object\n    }>\n    getAnnouncement: Response<string>\n    getNotice: Response<notice>\n    getNewExamInfo: {\n        code: number\n        result: {\n            tid: number\n            questions: { optionDtos: { id: number }[] }[]\n        }\n    }\n}\n\nexport interface ApiRequestType {\n    checkTestExist: RequestType & {\n        params: { tid: number | string; type: \"isExisting\" }\n    }\n    selectQustion: RequestType & {\n        params: { tid: number | string }\n        data: {\n            oidList?: number[]\n            titleList?: string[]\n        }\n    }\n    getNotice: RequestType & {\n        params: { version: string }\n    }\n    getNewExamInfo: RequestType & {\n        params: { csrfKey: string }\n        data: {\n            answerformId: number | string\n            examId: number | string\n        }\n    }\n}\n\nexport default apiInfo\n"
  },
  {
    "path": "extension/src/type/mooc.ts",
    "content": "export enum QuestionTypeEnumList {\n    SingleChoice = \"SINGLE_CHOICE\",\n    MultipleChoice = \"MULTIPLE_CHOICE\",\n    Completion = \"COMPLETION\",\n    Judge = \"JUDGEMENT\",\n    Homework = \"HOMEWORK\",\n    OnlineJudge = \"ONLINE_JUDGE\"\n}\n\ninterface course extends Object {\n    id: number\n    name: string\n    school: string\n    imageUrl: string\n}\n\ninterface test extends Object {\n    id: number\n    name: string\n    objective: boolean\n    releaseTime: string\n    deadline: string\n    chapterId: number\n    chapterName: string\n}\n\ninterface option extends Object {\n    id: number\n    content: string\n    answer: boolean\n}\n\ninterface quiz extends Object {\n    id: number\n    type:\n        | QuestionTypeEnumList.SingleChoice\n        | QuestionTypeEnumList.MultipleChoice\n        | QuestionTypeEnumList.Completion\n        | QuestionTypeEnumList.Judge\n    title: string\n    stdAnswer: string | null\n    optionList: option[] | null\n}\n\ninterface homework extends Object {\n    id: number\n    type: QuestionTypeEnumList.Homework | QuestionTypeEnumList.OnlineJudge\n    title: string\n    answer: string | null\n    description: string | null\n    memoryLimit: number | null\n    timeLimit: number | null\n}\n\ninterface notice extends Object {\n    id: number\n    content: string\n}\n\nexport type QuestionTypeEnum = typeof QuestionTypeEnumList\nexport type { course, test, option, quiz, homework, notice }\n"
  },
  {
    "path": "extension/tsconfig.config.json",
    "content": "{\n    \"extends\": \"@vue/tsconfig/tsconfig.node.json\",\n    \"include\": [\n        \"vite.config.*\",\n        \"vitest.config.*\",\n        \"cypress.config.*\",\n        \"playwright.config.*\"\n    ],\n    \"compilerOptions\": {\n        \"composite\": true,\n        \"types\": [\n            \"node\"\n        ]\n    }\n}"
  },
  {
    "path": "extension/tsconfig.json",
    "content": "{\n    \"extends\": \"@vue/tsconfig/tsconfig.web.json\",\n    \"include\": [\n        \"env.d.ts\",\n        \"src/**/*\",\n        \"src/**/*.vue\"\n    ],\n    \"compilerOptions\": {\n        \"baseUrl\": \".\",\n        \"paths\": {\n            \"@/*\": [\n                \"./src/*\"\n            ]\n        }\n    },\n    \"references\": [\n        {\n            \"path\": \"./tsconfig.config.json\"\n        }\n    ]\n}"
  },
  {
    "path": "extension/vite.config.ts",
    "content": "import { fileURLToPath, URL } from 'node:url'\n\nimport { defineConfig } from 'vite'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n    plugins: [],\n    resolve: {\n        alias: {\n            '@': fileURLToPath(new URL('./src', import.meta.url))\n        }\n    },\n    build: {\n        \"outDir\": \"./release/content-scripts\",\n        \"assetsDir\": \"./\"\n    }\n})\n"
  },
  {
    "path": "index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"zh\">\n\n<head>\n    <meta charset=\"UTF-8\">\n    <link rel=\"icon\" href=\"/favicon.png\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <!-- <meta name=\"referrer\" content=\"no-referrer\"> -->\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n    <title>Gins</title>\n    <script>\n        window.location.hash = \"\"\n    </script>\n    <script>\n        var _hmt = _hmt || [];\n        (function () {\n            var hm = document.createElement(\"script\");\n            hm.src = \"https://hm.baidu.com/hm.js?803f8581ce82c43a7f51eab256958890\";\n            var s = document.getElementsByTagName(\"script\")[0];\n            s.parentNode.insertBefore(hm, s);\n        })();\n    </script>\n    <script>\n        (function () {\n            var meta = document.createElement('meta')\n            meta.content = 'no-referrer'\n            meta.name = 'referrer'\n            document.getElementsByTagName('head')[0].appendChild(meta)\n        })()\n    </script>\n</head>\n\n<body>\n</body>\n\n</html>"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"ginsmooc\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"run-p type-check build-only\",\n    \"preview\": \"vite preview\",\n    \"build-only\": \"vite build\",\n    \"type-check\": \"vue-tsc --noEmit\",\n    \"lint\": \"eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore\"\n  },\n  \"dependencies\": {\n    \"@element-plus/icons-vue\": \"^2.1.0\",\n    \"@jridgewell/sourcemap-codec\": \"^1.4.14\",\n    \"@vueuse/core\": \"^9.11.1\",\n    \"axios\": \"^1.2.3\",\n    \"d3-cloud\": \"^1.2.5\",\n    \"element-plus\": \"^2.2.28\",\n    \"fuzzysort\": \"^2.0.4\",\n    \"vue\": \"^3.2.45\",\n    \"vue-router\": \"^4.1.6\"\n  },\n  \"devDependencies\": {\n    \"@rushstack/eslint-patch\": \"^1.1.4\",\n    \"@types/node\": \"^18.15.5\",\n    \"@vitejs/plugin-vue\": \"^4.0.0\",\n    \"@vue/eslint-config-prettier\": \"^7.0.0\",\n    \"@vue/eslint-config-typescript\": \"^11.0.0\",\n    \"@vue/tsconfig\": \"^0.1.3\",\n    \"eslint\": \"^8.22.0\",\n    \"eslint-plugin-vue\": \"^9.3.0\",\n    \"npm-run-all\": \"^4.1.5\",\n    \"prettier\": \"^2.7.1\",\n    \"typescript\": \"~4.7.4\",\n    \"vite\": \"^4.0.0\",\n    \"vue-tsc\": \"^1.0.12\"\n  }\n}\n"
  },
  {
    "path": "public/background.html",
    "content": "<!DOCTYPE html>\n<!--\n\tAerial by HTML5 UP\n\thtml5up.net | @ajlkn\n\tFree for personal and commercial use under the CCA 3.0 license (html5up.net/license)\n-->\n<html>\n    <head>\n        <meta charset=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, user-scalable=no\" />\n        <link rel=\"stylesheet\" href=\"css/main.css\" />\n        <noscript><link rel=\"stylesheet\" href=\"css/noscript.css\" /></noscript>\n    </head>\n    <body class=\"is-preload\">\n        <div id=\"wrapper\">\n            <div id=\"bg\"></div>\n            <div id=\"overlay\"></div>\n            <div id=\"main\">\n                <!-- Header -->\n                <header id=\"header\" style=\"text-shadow: 5px 5px 5px grey;\">\n                    <h1>Welcome</h1>\n                    <p>新年好！</p>\n                </header>\n            </div>\n        </div>\n        <script>\n            window.onload = function () {\n                document.body.classList.remove(\"is-preload\")\n            }\n            window.ontouchmove = function () {\n                return false\n            }\n            window.onorientationchange = function () {\n                document.body.scrollTop = 0\n            }\n        </script>\n        <script src=\"https://unpkg.com/magic-snowflakes/dist/snowflakes.min.js\"></script>\n        <script>\n            var sf = new Snowflakes();\n        </script>\n    </body>\n</html>\n"
  },
  {
    "path": "public/css/main.css",
    "content": "@import url(\"https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,900\");\n@import url(\"fontawesome-all.min.css\");\n\n/*\n\tAerial by HTML5 UP\n\thtml5up.net | @ajlkn\n\tFree for personal and commercial use under the CCA 3.0 license (html5up.net/license)\n*/\n\nhtml, body, div, span, applet, object,\niframe, h1, h2, h3, h4, h5, h6, p, blockquote,\npre, a, abbr, acronym, address, big, cite,\ncode, del, dfn, em, img, ins, kbd, q, s, samp,\nsmall, strike, strong, sub, sup, tt, var, b,\nu, i, center, dl, dt, dd, ol, ul, li, fieldset,\nform, label, legend, table, caption, tbody,\ntfoot, thead, tr, th, td, article, aside,\ncanvas, details, embed, figure, figcaption,\nfooter, header, hgroup, menu, nav, output, ruby,\nsection, summary, time, mark, audio, video {\n\tmargin: 0;\n\tpadding: 0;\n\tborder: 0;\n\tfont-size: 100%;\n\tfont: inherit;\n\tvertical-align: baseline;}\n\narticle, aside, details, figcaption, figure,\nfooter, header, hgroup, menu, nav, section {\n\tdisplay: block;}\n\nbody {\n\tline-height: 1;\n}\n\nol, ul {\n\tlist-style: none;\n}\n\nblockquote, q {\n\tquotes: none;\n}\n\n\tblockquote:before, blockquote:after, q:before, q:after {\n\t\tcontent: '';\n\t\tcontent: none;\n\t}\n\ntable {\n\tborder-collapse: collapse;\n\tborder-spacing: 0;\n}\n\nbody {\n\t-webkit-text-size-adjust: none;\n}\n\nmark {\n\tbackground-color: transparent;\n\tcolor: inherit;\n}\n\ninput::-moz-focus-inner {\n\tborder: 0;\n\tpadding: 0;\n}\n\ninput, select, textarea {\n\t-moz-appearance: none;\n\t-webkit-appearance: none;\n\t-ms-appearance: none;\n\tappearance: none;\n}\n\n/* Basic */\n\n\thtml {\n\t\tbox-sizing: border-box;\n\t}\n\n\t*, *:before, *:after {\n\t\tbox-sizing: inherit;\n\t}\n\n\tbody {\n\t\tbackground: #fff;\n\t\toverflow: hidden;\n\t}\n\n\t\tbody.is-preload *, body.is-preload *:before, body.is-preload *:after {\n\t\t\t-moz-animation: none !important;\n\t\t\t-webkit-animation: none !important;\n\t\t\t-ms-animation: none !important;\n\t\t\tanimation: none !important;\n\t\t\t-moz-transition: none !important;\n\t\t\t-webkit-transition: none !important;\n\t\t\t-ms-transition: none !important;\n\t\t\ttransition: none !important;\n\t\t}\n\n\tbody, input, select, textarea {\n\t\tcolor: #fff;\n\t\tfont-family: 'Source Sans Pro', sans-serif;\n\t\tfont-size: 15pt;\n\t\tfont-weight: 300 !important;\n\t\tletter-spacing: -0.025em;\n\t\tline-height: 1.75em;\n\t}\n\n\ta {\n\t\t-moz-transition: border-color 0.2s ease-in-out;\n\t\t-webkit-transition: border-color 0.2s ease-in-out;\n\t\t-ms-transition: border-color 0.2s ease-in-out;\n\t\ttransition: border-color 0.2s ease-in-out;\n\t\tborder-bottom: dotted 1px;\n\t\tcolor: inherit;\n\t\toutline: 0;\n\t\ttext-decoration: none;\n\t}\n\n\t\ta:hover {\n\t\t\tborder-color: transparent;\n\t\t}\n\n/* Icon */\n\n\t.icon {\n\t\ttext-decoration: none;\n\t\tposition: relative;\n\t}\n\n\t\t.icon:before {\n\t\t\t-moz-osx-font-smoothing: grayscale;\n\t\t\t-webkit-font-smoothing: antialiased;\n\t\t\tdisplay: inline-block;\n\t\t\tfont-style: normal;\n\t\t\tfont-variant: normal;\n\t\t\ttext-rendering: auto;\n\t\t\tline-height: 1;\n\t\t\ttext-transform: none !important;\n\t\t\tfont-family: 'Font Awesome 5 Free';\n\t\t\tfont-weight: 400;\n\t\t}\n\n\t\t.icon > .label {\n\t\t\tdisplay: none;\n\t\t}\n\n\t\t.icon.solid:before {\n\t\t\tfont-weight: 900;\n\t\t}\n\n\t\t.icon.brands:before {\n\t\t\tfont-family: 'Font Awesome 5 Brands';\n\t\t}\n\n/* Wrapper */\n\n\t@-moz-keyframes wrapper {\n\t\t0% {\n\t\t\topacity: 0;\n\t\t}\n\n\t\t100% {\n\t\t\topacity: 1;\n\t\t}\n\t}\n\n\t@-webkit-keyframes wrapper {\n\t\t0% {\n\t\t\topacity: 0;\n\t\t}\n\n\t\t100% {\n\t\t\topacity: 1;\n\t\t}\n\t}\n\n\t@-ms-keyframes wrapper {\n\t\t0% {\n\t\t\topacity: 0;\n\t\t}\n\n\t\t100% {\n\t\t\topacity: 1;\n\t\t}\n\t}\n\n\t@keyframes wrapper {\n\t\t0% {\n\t\t\topacity: 0;\n\t\t}\n\n\t\t100% {\n\t\t\topacity: 1;\n\t\t}\n\t}\n\n\t#wrapper {\n\t\t-moz-animation: wrapper 3s forwards;\n\t\t-webkit-animation: wrapper 3s forwards;\n\t\t-ms-animation: wrapper 3s forwards;\n\t\tanimation: wrapper 3s forwards;\n\t\theight: 100%;\n\t\tleft: 0;\n\t\topacity: 0;\n\t\tposition: fixed;\n\t\ttop: 0;\n\t\twidth: 100%;\n\t}\n\n/* BG */\n\n\t#bg {\n\t\t-moz-animation: bg 60s linear infinite;\n\t\t-webkit-animation: bg 60s linear infinite;\n\t\t-ms-animation: bg 60s linear infinite;\n\t\tanimation: bg 60s linear infinite;\n\t\t-moz-backface-visibility: hidden;\n\t\t-webkit-backface-visibility: hidden;\n\t\t-ms-backface-visibility: hidden;\n\t\tbackface-visibility: hidden;\n\t\t-moz-transform: translate3d(0,0,0);\n\t\t-webkit-transform: translate3d(0,0,0);\n\t\t-ms-transform: translate3d(0,0,0);\n\t\ttransform: translate3d(0,0,0);\n\t/* Set your background with this */\n\n\t\tbackground: #348cb2 url(\"https://gins-1255964181.cos.ap-beijing.myqcloud.com/bg.jpg\") bottom left;\n\t\tbackground-repeat: repeat-x;\n\t\theight: 100%;\n\t\tleft: 0;\n\t\topacity: 1;\n\t\tposition: fixed;\n\t\ttop: 0;\n\t}\n\n\t@-moz-keyframes bg {\n\t\t0% {\n\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\ttransform: translate3d(0,0,0);\n\t\t}\n\n\t\t100% {\n\t\t\t-moz-transform: translate3d(-2250px,0,0);\n\t\t\t-webkit-transform: translate3d(-2250px,0,0);\n\t\t\t-ms-transform: translate3d(-2250px,0,0);\n\t\t\ttransform: translate3d(-2250px,0,0);\n\t\t}\n\t}\n\n\t@-webkit-keyframes bg {\n\t\t0% {\n\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\ttransform: translate3d(0,0,0);\n\t\t}\n\n\t\t100% {\n\t\t\t-moz-transform: translate3d(-2250px,0,0);\n\t\t\t-webkit-transform: translate3d(-2250px,0,0);\n\t\t\t-ms-transform: translate3d(-2250px,0,0);\n\t\t\ttransform: translate3d(-2250px,0,0);\n\t\t}\n\t}\n\n\t@-ms-keyframes bg {\n\t\t0% {\n\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\ttransform: translate3d(0,0,0);\n\t\t}\n\n\t\t100% {\n\t\t\t-moz-transform: translate3d(-2250px,0,0);\n\t\t\t-webkit-transform: translate3d(-2250px,0,0);\n\t\t\t-ms-transform: translate3d(-2250px,0,0);\n\t\t\ttransform: translate3d(-2250px,0,0);\n\t\t}\n\t}\n\n\t@keyframes bg {\n\t\t0% {\n\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\ttransform: translate3d(0,0,0);\n\t\t}\n\n\t\t100% {\n\t\t\t-moz-transform: translate3d(-2250px,0,0);\n\t\t\t-webkit-transform: translate3d(-2250px,0,0);\n\t\t\t-ms-transform: translate3d(-2250px,0,0);\n\t\t\ttransform: translate3d(-2250px,0,0);\n\t\t}\n\t}\n\n\t#bg {\n\t\tbackground-size: auto 100%;\n\t\twidth: 6750px;\n\t}\n\n/* Overlay */\n\n\t@-moz-keyframes overlay {\n\t\t0% {\n\t\t\topacity: 0;\n\t\t}\n\n\t\t100% {\n\t\t\topacity: 1;\n\t\t}\n\t}\n\n\t@-webkit-keyframes overlay {\n\t\t0% {\n\t\t\topacity: 0;\n\t\t}\n\n\t\t100% {\n\t\t\topacity: 1;\n\t\t}\n\t}\n\n\t@-ms-keyframes overlay {\n\t\t0% {\n\t\t\topacity: 0;\n\t\t}\n\n\t\t100% {\n\t\t\topacity: 1;\n\t\t}\n\t}\n\n\t@keyframes overlay {\n\t\t0% {\n\t\t\topacity: 0;\n\t\t}\n\n\t\t100% {\n\t\t\topacity: 1;\n\t\t}\n\t}\n\n\t#overlay {\n\t\t-moz-animation: overlay 1.5s 1.5s forwards;\n\t\t-webkit-animation: overlay 1.5s 1.5s forwards;\n\t\t-ms-animation: overlay 1.5s 1.5s forwards;\n\t\tanimation: overlay 1.5s 1.5s forwards;\n\t\tbackground-attachment: fixed, fixed;\n\t\tbackground-image: url(\"images/overlay-pattern.png\"), url(\"images/overlay.svg\");\n\t\tbackground-position: top left, center center;\n\t\tbackground-repeat: repeat, no-repeat;\n\t\tbackground-size: auto, cover;\n\t\theight: 100%;\n\t\tleft: 0;\n\t\topacity: 0;\n\t\tposition: fixed;\n\t\ttop: 0;\n\t\twidth: 100%;\n\t}\n\n/* Main */\n\n\t#main {\n\t\theight: 100%;\n\t\tleft: 0;\n\t\tposition: fixed;\n\t\ttext-align: center;\n\t\ttop: 0;\n\t\twidth: 100%;\n\t}\n\n\t\t#main:before {\n\t\t\tcontent: '';\n\t\t\tdisplay: inline-block;\n\t\t\theight: 100%;\n\t\t\tmargin-right: 0;\n\t\t\tvertical-align: middle;\n\t\t\twidth: 1px;\n\t\t}\n\n/* Header */\n\n\t@-moz-keyframes header {\n\t\t0% {\n\t\t\t-moz-transform: translate3d(0,1em,0);\n\t\t\t-webkit-transform: translate3d(0,1em,0);\n\t\t\t-ms-transform: translate3d(0,1em,0);\n\t\t\ttransform: translate3d(0,1em,0);\n\t\t\topacity: 0;\n\t\t}\n\n\t\t100% {\n\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\ttransform: translate3d(0,0,0);\n\t\t\topacity: 1;\n\t\t}\n\t}\n\n\t@-webkit-keyframes header {\n\t\t0% {\n\t\t\t-moz-transform: translate3d(0,1em,0);\n\t\t\t-webkit-transform: translate3d(0,1em,0);\n\t\t\t-ms-transform: translate3d(0,1em,0);\n\t\t\ttransform: translate3d(0,1em,0);\n\t\t\topacity: 0;\n\t\t}\n\n\t\t100% {\n\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\ttransform: translate3d(0,0,0);\n\t\t\topacity: 1;\n\t\t}\n\t}\n\n\t@-ms-keyframes header {\n\t\t0% {\n\t\t\t-moz-transform: translate3d(0,1em,0);\n\t\t\t-webkit-transform: translate3d(0,1em,0);\n\t\t\t-ms-transform: translate3d(0,1em,0);\n\t\t\ttransform: translate3d(0,1em,0);\n\t\t\topacity: 0;\n\t\t}\n\n\t\t100% {\n\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\ttransform: translate3d(0,0,0);\n\t\t\topacity: 1;\n\t\t}\n\t}\n\n\t@keyframes header {\n\t\t0% {\n\t\t\t-moz-transform: translate3d(0,1em,0);\n\t\t\t-webkit-transform: translate3d(0,1em,0);\n\t\t\t-ms-transform: translate3d(0,1em,0);\n\t\t\ttransform: translate3d(0,1em,0);\n\t\t\topacity: 0;\n\t\t}\n\n\t\t100% {\n\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\ttransform: translate3d(0,0,0);\n\t\t\topacity: 1;\n\t\t}\n\t}\n\n\t@-moz-keyframes nav-icons {\n\t\t0% {\n\t\t\t-moz-transform: translate3d(0,1em,0);\n\t\t\t-webkit-transform: translate3d(0,1em,0);\n\t\t\t-ms-transform: translate3d(0,1em,0);\n\t\t\ttransform: translate3d(0,1em,0);\n\t\t\topacity: 0;\n\t\t}\n\n\t\t100% {\n\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\ttransform: translate3d(0,0,0);\n\t\t\topacity: 1;\n\t\t}\n\t}\n\n\t@-webkit-keyframes nav-icons {\n\t\t0% {\n\t\t\t-moz-transform: translate3d(0,1em,0);\n\t\t\t-webkit-transform: translate3d(0,1em,0);\n\t\t\t-ms-transform: translate3d(0,1em,0);\n\t\t\ttransform: translate3d(0,1em,0);\n\t\t\topacity: 0;\n\t\t}\n\n\t\t100% {\n\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\ttransform: translate3d(0,0,0);\n\t\t\topacity: 1;\n\t\t}\n\t}\n\n\t@-ms-keyframes nav-icons {\n\t\t0% {\n\t\t\t-moz-transform: translate3d(0,1em,0);\n\t\t\t-webkit-transform: translate3d(0,1em,0);\n\t\t\t-ms-transform: translate3d(0,1em,0);\n\t\t\ttransform: translate3d(0,1em,0);\n\t\t\topacity: 0;\n\t\t}\n\n\t\t100% {\n\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\ttransform: translate3d(0,0,0);\n\t\t\topacity: 1;\n\t\t}\n\t}\n\n\t@keyframes nav-icons {\n\t\t0% {\n\t\t\t-moz-transform: translate3d(0,1em,0);\n\t\t\t-webkit-transform: translate3d(0,1em,0);\n\t\t\t-ms-transform: translate3d(0,1em,0);\n\t\t\ttransform: translate3d(0,1em,0);\n\t\t\topacity: 0;\n\t\t}\n\n\t\t100% {\n\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\ttransform: translate3d(0,0,0);\n\t\t\topacity: 1;\n\t\t}\n\t}\n\n\t#header {\n\t\t-moz-animation: header 1s 2.25s forwards;\n\t\t-webkit-animation: header 1s 2.25s forwards;\n\t\t-ms-animation: header 1s 2.25s forwards;\n\t\tanimation: header 1s 2.25s forwards;\n\t\t-moz-backface-visibility: hidden;\n\t\t-webkit-backface-visibility: hidden;\n\t\t-ms-backface-visibility: hidden;\n\t\tbackface-visibility: hidden;\n\t\t-moz-transform: translate3d(0,0,0);\n\t\t-webkit-transform: translate3d(0,0,0);\n\t\t-ms-transform: translate3d(0,0,0);\n\t\ttransform: translate3d(0,0,0);\n\t\tcursor: default;\n\t\tdisplay: inline-block;\n\t\topacity: 0;\n\t\tposition: relative;\n\t\ttext-align: center;\n\t\ttop: -1em;\n\t\tvertical-align: middle;\n\t\twidth: 90%;\n\t}\n\n\t\t#header h1 {\n\t\t\tfont-size: 4.35em;\n\t\t\tfont-weight: 900;\n\t\t\tletter-spacing: -0.035em;\n\t\t\tline-height: 1em;\n\t\t}\n\n\t\t#header p {\n\t\t\tfont-size: 1.25em;\n\t\t\tmargin: 0.75em 0 0.25em 0;\n\t\t\topacity: 0.75;\n\t\t}\n\n\t\t#header nav {\n\t\t\tmargin: 1.5em 0 0 0;\n\t\t}\n\n\t\t\t#header nav li {\n\t\t\t\t-moz-animation: nav-icons 0.5s ease-in-out forwards;\n\t\t\t\t-webkit-animation: nav-icons 0.5s ease-in-out forwards;\n\t\t\t\t-ms-animation: nav-icons 0.5s ease-in-out forwards;\n\t\t\t\tanimation: nav-icons 0.5s ease-in-out forwards;\n\t\t\t\t-moz-backface-visibility: hidden;\n\t\t\t\t-webkit-backface-visibility: hidden;\n\t\t\t\t-ms-backface-visibility: hidden;\n\t\t\t\tbackface-visibility: hidden;\n\t\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\t\ttransform: translate3d(0,0,0);\n\t\t\t\tdisplay: inline-block;\n\t\t\t\theight: 5.35em;\n\t\t\t\tline-height: 5.885em;\n\t\t\t\topacity: 0;\n\t\t\t\tposition: relative;\n\t\t\t\ttop: 0;\n\t\t\t\twidth: 5.35em;\n\t\t\t}\n\n\t\t\t\t#header nav li:nth-child(1) {\n\t\t\t\t\t-moz-animation-delay: 2.5s;\n\t\t\t\t\t-webkit-animation-delay: 2.5s;\n\t\t\t\t\t-ms-animation-delay: 2.5s;\n\t\t\t\t\tanimation-delay: 2.5s;\n\t\t\t\t}\n\n\t\t\t\t#header nav li:nth-child(2) {\n\t\t\t\t\t-moz-animation-delay: 2.75s;\n\t\t\t\t\t-webkit-animation-delay: 2.75s;\n\t\t\t\t\t-ms-animation-delay: 2.75s;\n\t\t\t\t\tanimation-delay: 2.75s;\n\t\t\t\t}\n\n\t\t\t\t#header nav li:nth-child(3) {\n\t\t\t\t\t-moz-animation-delay: 3s;\n\t\t\t\t\t-webkit-animation-delay: 3s;\n\t\t\t\t\t-ms-animation-delay: 3s;\n\t\t\t\t\tanimation-delay: 3s;\n\t\t\t\t}\n\n\t\t\t\t#header nav li:nth-child(4) {\n\t\t\t\t\t-moz-animation-delay: 3.25s;\n\t\t\t\t\t-webkit-animation-delay: 3.25s;\n\t\t\t\t\t-ms-animation-delay: 3.25s;\n\t\t\t\t\tanimation-delay: 3.25s;\n\t\t\t\t}\n\n\t\t\t\t#header nav li:nth-child(5) {\n\t\t\t\t\t-moz-animation-delay: 3.5s;\n\t\t\t\t\t-webkit-animation-delay: 3.5s;\n\t\t\t\t\t-ms-animation-delay: 3.5s;\n\t\t\t\t\tanimation-delay: 3.5s;\n\t\t\t\t}\n\n\t\t\t\t#header nav li:nth-child(6) {\n\t\t\t\t\t-moz-animation-delay: 3.75s;\n\t\t\t\t\t-webkit-animation-delay: 3.75s;\n\t\t\t\t\t-ms-animation-delay: 3.75s;\n\t\t\t\t\tanimation-delay: 3.75s;\n\t\t\t\t}\n\n\t\t\t\t#header nav li:nth-child(7) {\n\t\t\t\t\t-moz-animation-delay: 4s;\n\t\t\t\t\t-webkit-animation-delay: 4s;\n\t\t\t\t\t-ms-animation-delay: 4s;\n\t\t\t\t\tanimation-delay: 4s;\n\t\t\t\t}\n\n\t\t\t\t#header nav li:nth-child(8) {\n\t\t\t\t\t-moz-animation-delay: 4.25s;\n\t\t\t\t\t-webkit-animation-delay: 4.25s;\n\t\t\t\t\t-ms-animation-delay: 4.25s;\n\t\t\t\t\tanimation-delay: 4.25s;\n\t\t\t\t}\n\n\t\t\t\t#header nav li:nth-child(9) {\n\t\t\t\t\t-moz-animation-delay: 4.5s;\n\t\t\t\t\t-webkit-animation-delay: 4.5s;\n\t\t\t\t\t-ms-animation-delay: 4.5s;\n\t\t\t\t\tanimation-delay: 4.5s;\n\t\t\t\t}\n\n\t\t\t\t#header nav li:nth-child(10) {\n\t\t\t\t\t-moz-animation-delay: 4.75s;\n\t\t\t\t\t-webkit-animation-delay: 4.75s;\n\t\t\t\t\t-ms-animation-delay: 4.75s;\n\t\t\t\t\tanimation-delay: 4.75s;\n\t\t\t\t}\n\n\t\t\t#header nav a {\n\t\t\t\t-webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n\t\t\t\t-webkit-touch-callout: none;\n\t\t\t\tborder: 0;\n\t\t\t\tdisplay: inline-block;\n\t\t\t}\n\n\t\t\t\t#header nav a:before {\n\t\t\t\t\t-moz-transition: all 0.2s ease-in-out;\n\t\t\t\t\t-webkit-transition: all 0.2s ease-in-out;\n\t\t\t\t\t-ms-transition: all 0.2s ease-in-out;\n\t\t\t\t\ttransition: all 0.2s ease-in-out;\n\t\t\t\t\tborder-radius: 100%;\n\t\t\t\t\tborder: solid 1px #fff;\n\t\t\t\t\tdisplay: block;\n\t\t\t\t\tfont-size: 1.75em;\n\t\t\t\t\theight: 2.5em;\n\t\t\t\t\tline-height: 2.5em;\n\t\t\t\t\tposition: relative;\n\t\t\t\t\ttext-align: center;\n\t\t\t\t\ttop: 0;\n\t\t\t\t\twidth: 2.5em;\n\t\t\t\t}\n\n\t\t\t\t#header nav a:hover {\n\t\t\t\t\tfont-size: 1.1em;\n\t\t\t\t}\n\n\t\t\t\t\t#header nav a:hover:before {\n\t\t\t\t\t\tbackground-color: rgba(255, 255, 255, 0.175);\n\t\t\t\t\t\tcolor: #fff;\n\t\t\t\t\t}\n\n\t\t\t\t#header nav a:active {\n\t\t\t\t\tfont-size: 0.95em;\n\t\t\t\t\tbackground: none;\n\t\t\t\t}\n\n\t\t\t\t\t#header nav a:active:before {\n\t\t\t\t\t\tbackground-color: rgba(255, 255, 255, 0.35);\n\t\t\t\t\t\tcolor: #fff;\n\t\t\t\t\t}\n\n\t\t\t\t#header nav a span {\n\t\t\t\t\tdisplay: none;\n\t\t\t\t}\n\n/* Footer */\n\n\t#footer {\n\t\tbackground-image: -moz-linear-gradient(top, rgba(0,0,0,0), rgba(0,0,0,0.5) 75%);\n\t\tbackground-image: -webkit-linear-gradient(top, rgba(0,0,0,0), rgba(0,0,0,0.5) 75%);\n\t\tbackground-image: -ms-linear-gradient(top, rgba(0,0,0,0), rgba(0,0,0,0.5) 75%);\n\t\tbackground-image: linear-gradient(top, rgba(0,0,0,0), rgba(0,0,0,0.5) 75%);\n\t\tbottom: 0;\n\t\tcursor: default;\n\t\theight: 6em;\n\t\tleft: 0;\n\t\tline-height: 8em;\n\t\tposition: absolute;\n\t\ttext-align: center;\n\t\twidth: 100%;\n\t}\n\n/* Wide */\n\n\t@media screen and (max-width: 1680px) {\n\n\t\t/* Basic */\n\n\t\t\tbody, input, select, textarea {\n\t\t\t\tfont-size: 13pt;\n\t\t\t}\n\n\t\t/* BG */\n\n\t\t\t@-moz-keyframes bg {\n\t\t\t\t0% {\n\t\t\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\t\t\ttransform: translate3d(0,0,0);\n\t\t\t\t}\n\n\t\t\t\t100% {\n\t\t\t\t\t-moz-transform: translate3d(-1500px,0,0);\n\t\t\t\t\t-webkit-transform: translate3d(-1500px,0,0);\n\t\t\t\t\t-ms-transform: translate3d(-1500px,0,0);\n\t\t\t\t\ttransform: translate3d(-1500px,0,0);\n\t\t\t\t}\n\n\t}\n\n\t\t@-webkit-keyframes bg {\n\t\t\t0% {\n\t\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\t\ttransform: translate3d(0,0,0);\n\t\t\t}\n\n\t\t\t100% {\n\t\t\t\t-moz-transform: translate3d(-1500px,0,0);\n\t\t\t\t-webkit-transform: translate3d(-1500px,0,0);\n\t\t\t\t-ms-transform: translate3d(-1500px,0,0);\n\t\t\t\ttransform: translate3d(-1500px,0,0);\n\t\t\t}\n\t\t\t}\n\n\t\t@-ms-keyframes bg {\n\t\t\t0% {\n\t\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\t\ttransform: translate3d(0,0,0);\n\t\t\t}\n\n\t\t\t100% {\n\t\t\t\t-moz-transform: translate3d(-1500px,0,0);\n\t\t\t\t-webkit-transform: translate3d(-1500px,0,0);\n\t\t\t\t-ms-transform: translate3d(-1500px,0,0);\n\t\t\t\ttransform: translate3d(-1500px,0,0);\n\t\t\t}\n\t\t}\n\n\t\t@keyframes bg {\n\t\t\t0% {\n\t\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\t\ttransform: translate3d(0,0,0);\n\t\t\t}\n\n\t\t\t100% {\n\t\t\t\t-moz-transform: translate3d(-1500px,0,0);\n\t\t\t\t-webkit-transform: translate3d(-1500px,0,0);\n\t\t\t\t-ms-transform: translate3d(-1500px,0,0);\n\t\t\t\ttransform: translate3d(-1500px,0,0);\n\t\t\t}\n\t\t}\n\n\t\t#bg {\n\t\t\tbackground-size: 1500px auto;\n\t\t\twidth: 4500px;\n\t\t} }\n\n/* Normal */\n\n\t@media screen and (max-width: 1280px) {\n\n\t\t/* Basic */\n\n\t\t\tbody, input, select, textarea {\n\t\t\t\tfont-size: 12pt;\n\t\t\t}\n\n\t\t/* BG */\n\n\t\t\t@-moz-keyframes bg {\n\t\t\t\t0% {\n\t\t\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\t\t\ttransform: translate3d(0,0,0);\n\t\t\t\t}\n\n\t\t\t\t100% {\n\t\t\t\t\t-moz-transform: translate3d(-750px,0,0);\n\t\t\t\t\t-webkit-transform: translate3d(-750px,0,0);\n\t\t\t\t\t-ms-transform: translate3d(-750px,0,0);\n\t\t\t\t\ttransform: translate3d(-750px,0,0);\n\t\t\t\t}\n\n\t}\n\n\t\t@-webkit-keyframes bg {\n\t\t\t0% {\n\t\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\t\ttransform: translate3d(0,0,0);\n\t\t\t}\n\n\t\t\t100% {\n\t\t\t\t-moz-transform: translate3d(-750px,0,0);\n\t\t\t\t-webkit-transform: translate3d(-750px,0,0);\n\t\t\t\t-ms-transform: translate3d(-750px,0,0);\n\t\t\t\ttransform: translate3d(-750px,0,0);\n\t\t\t}\n\t\t\t}\n\n\t\t@-ms-keyframes bg {\n\t\t\t0% {\n\t\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\t\ttransform: translate3d(0,0,0);\n\t\t\t}\n\n\t\t\t100% {\n\t\t\t\t-moz-transform: translate3d(-750px,0,0);\n\t\t\t\t-webkit-transform: translate3d(-750px,0,0);\n\t\t\t\t-ms-transform: translate3d(-750px,0,0);\n\t\t\t\ttransform: translate3d(-750px,0,0);\n\t\t\t}\n\t\t}\n\n\t\t@keyframes bg {\n\t\t\t0% {\n\t\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\t\ttransform: translate3d(0,0,0);\n\t\t\t}\n\n\t\t\t100% {\n\t\t\t\t-moz-transform: translate3d(-750px,0,0);\n\t\t\t\t-webkit-transform: translate3d(-750px,0,0);\n\t\t\t\t-ms-transform: translate3d(-750px,0,0);\n\t\t\t\ttransform: translate3d(-750px,0,0);\n\t\t\t}\n\t\t}\n\n\t\t#bg {\n\t\t\tbackground-size: 750px auto;\n\t\t\twidth: 2250px;\n\t\t} }\n\n/* Mobile */\n\n\t@media screen and (max-width: 736px) {\n\n\t\t/* Basic */\n\n\t\t\tbody {\n\t\t\t\tmin-width: 320px;\n\t\t\t}\n\n\t\t\tbody, input, select, textarea {\n\t\t\t\tfont-size: 11pt;\n\t\t\t}\n\n\t\t/* BG */\n\n\t\t\t@-moz-keyframes bg {\n\t\t\t\t0% {\n\t\t\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\t\t\ttransform: translate3d(0,0,0);\n\t\t\t\t}\n\n\t\t\t\t100% {\n\t\t\t\t\t-moz-transform: translate3d(-300px,0,0);\n\t\t\t\t\t-webkit-transform: translate3d(-300px,0,0);\n\t\t\t\t\t-ms-transform: translate3d(-300px,0,0);\n\t\t\t\t\ttransform: translate3d(-300px,0,0);\n\t\t\t\t}\n\n\t}\n\n\t\t@-webkit-keyframes bg {\n\t\t\t0% {\n\t\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\t\ttransform: translate3d(0,0,0);\n\t\t\t}\n\n\t\t\t100% {\n\t\t\t\t-moz-transform: translate3d(-300px,0,0);\n\t\t\t\t-webkit-transform: translate3d(-300px,0,0);\n\t\t\t\t-ms-transform: translate3d(-300px,0,0);\n\t\t\t\ttransform: translate3d(-300px,0,0);\n\t\t\t}\n\t\t\t}\n\n\t\t@-ms-keyframes bg {\n\t\t\t0% {\n\t\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\t\ttransform: translate3d(0,0,0);\n\t\t\t}\n\n\t\t\t100% {\n\t\t\t\t-moz-transform: translate3d(-300px,0,0);\n\t\t\t\t-webkit-transform: translate3d(-300px,0,0);\n\t\t\t\t-ms-transform: translate3d(-300px,0,0);\n\t\t\t\ttransform: translate3d(-300px,0,0);\n\t\t\t}\n\t\t}\n\n\t\t@keyframes bg {\n\t\t\t0% {\n\t\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\t\ttransform: translate3d(0,0,0);\n\t\t\t}\n\n\t\t\t100% {\n\t\t\t\t-moz-transform: translate3d(-300px,0,0);\n\t\t\t\t-webkit-transform: translate3d(-300px,0,0);\n\t\t\t\t-ms-transform: translate3d(-300px,0,0);\n\t\t\t\ttransform: translate3d(-300px,0,0);\n\t\t\t}\n\t\t}\n\n\t\t#bg {\n\t\t\tbackground-size: 300px auto;\n\t\t\twidth: 900px;\n\t\t}\n\n\t/* Header */\n\n\t\t#header h1 {\n\t\t\tfont-size: 2.5em;\n\t\t}\n\n\t\t#header p {\n\t\t\tfont-size: 1em;\n\t\t}\n\n\t\t#header nav {\n\t\t\tfont-size: 1em;\n\t\t}\n\n\t\t\t#header nav a:hover {\n\t\t\t\tfont-size: 1em;\n\t\t\t}\n\n\t\t\t#header nav a:active {\n\t\t\t\tfont-size: 1em;\n\t\t\t} }\n\n/* Mobile (Portrait) */\n\n\t@media screen and (max-width: 480px) {\n\n\t\t/* BG */\n\n\t\t\t@-moz-keyframes bg {\n\t\t\t\t0% {\n\t\t\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\t\t\ttransform: translate3d(0,0,0);\n\t\t\t\t}\n\n\t\t\t\t100% {\n\t\t\t\t\t-moz-transform: translate3d(-412.5px,0,0);\n\t\t\t\t\t-webkit-transform: translate3d(-412.5px,0,0);\n\t\t\t\t\t-ms-transform: translate3d(-412.5px,0,0);\n\t\t\t\t\ttransform: translate3d(-412.5px,0,0);\n\t\t\t\t}\n\n\t}\n\n\t\t@-webkit-keyframes bg {\n\t\t\t0% {\n\t\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\t\ttransform: translate3d(0,0,0);\n\t\t\t}\n\n\t\t\t100% {\n\t\t\t\t-moz-transform: translate3d(-412.5px,0,0);\n\t\t\t\t-webkit-transform: translate3d(-412.5px,0,0);\n\t\t\t\t-ms-transform: translate3d(-412.5px,0,0);\n\t\t\t\ttransform: translate3d(-412.5px,0,0);\n\t\t\t}\n\t\t\t}\n\n\t\t@-ms-keyframes bg {\n\t\t\t0% {\n\t\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\t\ttransform: translate3d(0,0,0);\n\t\t\t}\n\n\t\t\t100% {\n\t\t\t\t-moz-transform: translate3d(-412.5px,0,0);\n\t\t\t\t-webkit-transform: translate3d(-412.5px,0,0);\n\t\t\t\t-ms-transform: translate3d(-412.5px,0,0);\n\t\t\t\ttransform: translate3d(-412.5px,0,0);\n\t\t\t}\n\t\t}\n\n\t\t@keyframes bg {\n\t\t\t0% {\n\t\t\t\t-moz-transform: translate3d(0,0,0);\n\t\t\t\t-webkit-transform: translate3d(0,0,0);\n\t\t\t\t-ms-transform: translate3d(0,0,0);\n\t\t\t\ttransform: translate3d(0,0,0);\n\t\t\t}\n\n\t\t\t100% {\n\t\t\t\t-moz-transform: translate3d(-412.5px,0,0);\n\t\t\t\t-webkit-transform: translate3d(-412.5px,0,0);\n\t\t\t\t-ms-transform: translate3d(-412.5px,0,0);\n\t\t\t\ttransform: translate3d(-412.5px,0,0);\n\t\t\t}\n\t\t}\n\n\t\t#bg {\n\t\t\tbackground-size: 412.5px auto;\n\t\t\twidth: 1237.5px;\n\t\t}\n\n\t/* Header */\n\n\t\t#header nav {\n\t\t\tpadding: 0 1em;\n\t\t} }"
  },
  {
    "path": "public/css/noscript.css",
    "content": "/*\n\tAerial by HTML5 UP\n\thtml5up.net | @ajlkn\n\tFree for personal and commercial use under the CCA 3.0 license (html5up.net/license)\n*/\n\n/* Wrapper */\n\n\t#wrapper {\n\t\topacity: 1 !important;\n\t}\n\n/* Overlay */\n\n\t#overlay {\n\t\topacity: 1 !important;\n\t}\n\n/* Header */\n\n\t#header {\n\t\topacity: 1 !important;\n\t}\n\n\t\t#header nav li {\n\t\t\topacity: 1 !important;\n\t\t}"
  },
  {
    "path": "public/guess.java",
    "content": "package top.ginnnnnn.mooc.service.implement;\n\nimport java.security.SecureRandom;\nimport java.util.HashMap;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.redis.core.HashOperations;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.stereotype.Service;\n\nimport top.ginnnnnn.mooc.service.GameService;\n\n@Service\npublic class GameServiceImpl implements GameService {\n\n    private static final SecureRandom RNG = new SecureRandom();\n    private static final String GUESS_GAME_PREFIX = \"GinsGame-guess-\";\n    @Autowired\n    private StringRedisTemplate stringRedisTemplate;\n\n    public HashMap<String, Object> guess(String token, Double guess) {\n        final String key = GUESS_GAME_PREFIX + token;\n        init(key);\n        HashOperations<String, String, Object> hashOperations = stringRedisTemplate.opsForHash();\n\n        Double number = Double.valueOf((String)hashOperations.get(key, \"number\"));\n        boolean isLess = number > guess + 1e-6 / 2;\n        boolean isMore = number < guess - 1e-6 / 2;\n\n        boolean isPassed = !isLess && !isMore;\n        boolean isTalented = isPassed && !hashOperations.hasKey(key, \"previous\");\n\n        HashMap<String, Object> state = new HashMap<>(5);\n        state.put(\"less\", isLess);\n        state.put(\"more\", isMore);\n\n        hashOperations.putIfAbsent(key, \"previous\", \"0\");\n        hashOperations.increment(key, \"previous\", 1);\n        if (isPassed) {\n            hashOperations.put(key, \"number\", String.format(\"%f\", RNG.nextInt(1, 1000000) * 1e-6));\n            hashOperations.delete(key, \"previous\");\n            hashOperations.increment(key, \"passed\", 1);\n        }\n        if (isTalented) {\n            hashOperations.increment(key, \"talented\", 1);\n            state.put(\"reward\", Boolean.TRUE);\n        } else {\n            state.put(\"reward\", Boolean.FALSE);\n        }\n\n        HashMap<String, Object> ret = new HashMap<>(3);\n        ret.put(\"info\", infoBuild(key));\n        ret.put(\"state\", state);\n        return ret;\n\n    }\n\n    public HashMap<String, Object> getState(String token) {\n        final String key = GUESS_GAME_PREFIX + token;\n        init(key);\n        return infoBuild(key);\n    }\n\n    public boolean refresh(String token) {\n        final String key = GUESS_GAME_PREFIX + token;\n        init(key);\n        HashOperations<String, String, Object> hashOperations = stringRedisTemplate.opsForHash();\n\n        hashOperations.put(key, \"passed\", \"0\");\n        hashOperations.put(key, \"talented\", \"0\");\n        hashOperations.put(key, \"number\", String.format(\"%f\", RNG.nextInt(1, 1000000) * 1e-6));\n        hashOperations.delete(key, \"previous\");\n        return true;\n    }\n\n    private void init(String key) {\n        HashOperations<String, String, Object> hashOperations = stringRedisTemplate.opsForHash();\n        hashOperations.putIfAbsent(key, \"passed\", \"0\");\n        hashOperations.putIfAbsent(key, \"talented\", \"0\");\n        hashOperations.putIfAbsent(key, \"number\", String.format(\"%f\", RNG.nextInt(1, 1000000) * 1e-6));\n    }\n\n    private HashMap<String, Object> infoBuild(String key) {\n        HashOperations<String, String, Object> hashOperations = stringRedisTemplate.opsForHash();\n        HashMap<String, Object> info = new HashMap<>(3);\n        info.put(\"passed\", hashOperations.get(key, \"passed\"));\n        info.put(\"talented\", hashOperations.get(key, \"talented\"));\n        return info;\n    }\n\n}\n"
  },
  {
    "path": "public/sass/libs/_breakpoints.scss",
    "content": "// breakpoints.scss v1.0 | @ajlkn | MIT licensed */\n\n// Vars.\n\n\t/// Breakpoints.\n\t/// @var {list}\n\t$breakpoints: () !global;\n\n// Mixins.\n\n\t/// Sets breakpoints.\n\t/// @param {map} $x Breakpoints.\n\t@mixin breakpoints($x: ()) {\n\t\t$breakpoints: $x !global;\n\t}\n\n\t/// Wraps @content in a @media block targeting a specific orientation.\n\t/// @param {string} $orientation Orientation.\n\t@mixin orientation($orientation) {\n\t\t@media screen and (orientation: #{$orientation}) {\n\t\t\t@content;\n\t\t}\n\t}\n\n\t/// Wraps @content in a @media block using a given query.\n\t/// @param {string} $query Query.\n\t@mixin breakpoint($query: null) {\n\n\t\t$breakpoint: null;\n\t\t$op: null;\n\t\t$media: null;\n\n\t\t// Determine operator, breakpoint.\n\n\t\t\t// Greater than or equal.\n\t\t\t\t@if (str-slice($query, 0, 2) == '>=') {\n\n\t\t\t\t\t$op: 'gte';\n\t\t\t\t\t$breakpoint: str-slice($query, 3);\n\n\t\t\t\t}\n\n\t\t\t// Less than or equal.\n\t\t\t\t@elseif (str-slice($query, 0, 2) == '<=') {\n\n\t\t\t\t\t$op: 'lte';\n\t\t\t\t\t$breakpoint: str-slice($query, 3);\n\n\t\t\t\t}\n\n\t\t\t// Greater than.\n\t\t\t\t@elseif (str-slice($query, 0, 1) == '>') {\n\n\t\t\t\t\t$op: 'gt';\n\t\t\t\t\t$breakpoint: str-slice($query, 2);\n\n\t\t\t\t}\n\n\t\t\t// Less than.\n\t\t\t\t@elseif (str-slice($query, 0, 1) == '<') {\n\n\t\t\t\t\t$op: 'lt';\n\t\t\t\t\t$breakpoint: str-slice($query, 2);\n\n\t\t\t\t}\n\n\t\t\t// Not.\n\t\t\t\t@elseif (str-slice($query, 0, 1) == '!') {\n\n\t\t\t\t\t$op: 'not';\n\t\t\t\t\t$breakpoint: str-slice($query, 2);\n\n\t\t\t\t}\n\n\t\t\t// Equal.\n\t\t\t\t@else {\n\n\t\t\t\t\t$op: 'eq';\n\t\t\t\t\t$breakpoint: $query;\n\n\t\t\t\t}\n\n\t\t// Build media.\n\t\t\t@if ($breakpoint and map-has-key($breakpoints, $breakpoint)) {\n\n\t\t\t\t$a: map-get($breakpoints, $breakpoint);\n\n\t\t\t\t// Range.\n\t\t\t\t\t@if (type-of($a) == 'list') {\n\n\t\t\t\t\t\t$x: nth($a, 1);\n\t\t\t\t\t\t$y: nth($a, 2);\n\n\t\t\t\t\t\t// Max only.\n\t\t\t\t\t\t\t@if ($x == null) {\n\n\t\t\t\t\t\t\t\t// Greater than or equal (>= 0 / anything)\n\t\t\t\t\t\t\t\t\t@if ($op == 'gte') {\n\t\t\t\t\t\t\t\t\t\t$media: 'screen';\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Less than or equal (<= y)\n\t\t\t\t\t\t\t\t\t@elseif ($op == 'lte') {\n\t\t\t\t\t\t\t\t\t\t$media: 'screen and (max-width: ' + $y + ')';\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Greater than (> y)\n\t\t\t\t\t\t\t\t\t@elseif ($op == 'gt') {\n\t\t\t\t\t\t\t\t\t\t$media: 'screen and (min-width: ' + ($y + 1) + ')';\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Less than (< 0 / invalid)\n\t\t\t\t\t\t\t\t\t@elseif ($op == 'lt') {\n\t\t\t\t\t\t\t\t\t\t$media: 'screen and (max-width: -1px)';\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Not (> y)\n\t\t\t\t\t\t\t\t\t@elseif ($op == 'not') {\n\t\t\t\t\t\t\t\t\t\t$media: 'screen and (min-width: ' + ($y + 1) + ')';\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Equal (<= y)\n\t\t\t\t\t\t\t\t\t@else {\n\t\t\t\t\t\t\t\t\t\t$media: 'screen and (max-width: ' + $y + ')';\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Min only.\n\t\t\t\t\t\t\t@else if ($y == null) {\n\n\t\t\t\t\t\t\t\t// Greater than or equal (>= x)\n\t\t\t\t\t\t\t\t\t@if ($op == 'gte') {\n\t\t\t\t\t\t\t\t\t\t$media: 'screen and (min-width: ' + $x + ')';\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Less than or equal (<= inf / anything)\n\t\t\t\t\t\t\t\t\t@elseif ($op == 'lte') {\n\t\t\t\t\t\t\t\t\t\t$media: 'screen';\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Greater than (> inf / invalid)\n\t\t\t\t\t\t\t\t\t@elseif ($op == 'gt') {\n\t\t\t\t\t\t\t\t\t\t$media: 'screen and (max-width: -1px)';\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Less than (< x)\n\t\t\t\t\t\t\t\t\t@elseif ($op == 'lt') {\n\t\t\t\t\t\t\t\t\t\t$media: 'screen and (max-width: ' + ($x - 1) + ')';\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Not (< x)\n\t\t\t\t\t\t\t\t\t@elseif ($op == 'not') {\n\t\t\t\t\t\t\t\t\t\t$media: 'screen and (max-width: ' + ($x - 1) + ')';\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Equal (>= x)\n\t\t\t\t\t\t\t\t\t@else {\n\t\t\t\t\t\t\t\t\t\t$media: 'screen and (min-width: ' + $x + ')';\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Min and max.\n\t\t\t\t\t\t\t@else {\n\n\t\t\t\t\t\t\t\t// Greater than or equal (>= x)\n\t\t\t\t\t\t\t\t\t@if ($op == 'gte') {\n\t\t\t\t\t\t\t\t\t\t$media: 'screen and (min-width: ' + $x + ')';\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Less than or equal (<= y)\n\t\t\t\t\t\t\t\t\t@elseif ($op == 'lte') {\n\t\t\t\t\t\t\t\t\t\t$media: 'screen and (max-width: ' + $y + ')';\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Greater than (> y)\n\t\t\t\t\t\t\t\t\t@elseif ($op == 'gt') {\n\t\t\t\t\t\t\t\t\t\t$media: 'screen and (min-width: ' + ($y + 1) + ')';\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Less than (< x)\n\t\t\t\t\t\t\t\t\t@elseif ($op == 'lt') {\n\t\t\t\t\t\t\t\t\t\t$media: 'screen and (max-width: ' + ($x - 1) + ')';\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Not (< x and > y)\n\t\t\t\t\t\t\t\t\t@elseif ($op == 'not') {\n\t\t\t\t\t\t\t\t\t\t$media: 'screen and (max-width: ' + ($x - 1) + '), screen and (min-width: ' + ($y + 1) + ')';\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Equal (>= x and <= y)\n\t\t\t\t\t\t\t\t\t@else {\n\t\t\t\t\t\t\t\t\t\t$media: 'screen and (min-width: ' + $x + ') and (max-width: ' + $y + ')';\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t// String.\n\t\t\t\t\t@else {\n\n\t\t\t\t\t\t// Missing a media type? Prefix with \"screen\".\n\t\t\t\t\t\t\t@if (str-slice($a, 0, 1) == '(') {\n\t\t\t\t\t\t\t\t$media: 'screen and ' + $a;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Otherwise, use as-is.\n\t\t\t\t\t\t\t@else {\n\t\t\t\t\t\t\t\t$media: $a;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t}\n\n\t\t// Output.\n\t        @media #{$media} {\n\t\t\t\t@content;\n\t\t\t}\n\n\t}"
  },
  {
    "path": "public/sass/libs/_functions.scss",
    "content": "/// Removes a specific item from a list.\n/// @author Hugo Giraudel\n/// @param {list} $list List.\n/// @param {integer} $index Index.\n/// @return {list} Updated list.\n@function remove-nth($list, $index) {\n\n\t$result: null;\n\n\t@if type-of($index) != number {\n\t\t@warn \"$index: #{quote($index)} is not a number for `remove-nth`.\";\n\t}\n\t@else if $index == 0 {\n\t\t@warn \"List index 0 must be a non-zero integer for `remove-nth`.\";\n\t}\n\t@else if abs($index) > length($list) {\n\t\t@warn \"List index is #{$index} but list is only #{length($list)} item long for `remove-nth`.\";\n\t}\n\t@else {\n\n\t\t$result: ();\n\t\t$index: if($index < 0, length($list) + $index + 1, $index);\n\n\t\t@for $i from 1 through length($list) {\n\n\t\t\t@if $i != $index {\n\t\t\t\t$result: append($result, nth($list, $i));\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@return $result;\n\n}\n\n/// Gets a value from a map.\n/// @author Hugo Giraudel\n/// @param {map} $map Map.\n/// @param {string} $keys Key(s).\n/// @return {string} Value.\n@function val($map, $keys...) {\n\n\t@if nth($keys, 1) == null {\n\t\t$keys: remove-nth($keys, 1);\n\t}\n\n\t@each $key in $keys {\n\t\t$map: map-get($map, $key);\n\t}\n\n\t@return $map;\n\n}\n\n/// Gets a duration value.\n/// @param {string} $keys Key(s).\n/// @return {string} Value.\n@function _duration($keys...) {\n\t@return val($duration, $keys...);\n}\n\n/// Gets a font value.\n/// @param {string} $keys Key(s).\n/// @return {string} Value.\n@function _font($keys...) {\n\t@return val($font, $keys...);\n}\n\n/// Gets a misc value.\n/// @param {string} $keys Key(s).\n/// @return {string} Value.\n@function _misc($keys...) {\n\t@return val($misc, $keys...);\n}\n\n/// Gets a palette value.\n/// @param {string} $keys Key(s).\n/// @return {string} Value.\n@function _palette($keys...) {\n\t@return val($palette, $keys...);\n}\n\n/// Gets a size value.\n/// @param {string} $keys Key(s).\n/// @return {string} Value.\n@function _size($keys...) {\n\t@return val($size, $keys...);\n}"
  },
  {
    "path": "public/sass/libs/_mixins.scss",
    "content": "/// Makes an element's :before pseudoelement a FontAwesome icon.\n/// @param {string} $content Optional content value to use.\n/// @param {string} $category Optional category to use.\n/// @param {string} $where Optional pseudoelement to target (before or after).\n@mixin icon($content: false, $category: regular, $where: before) {\n\n\ttext-decoration: none;\n\n\t&:#{$where} {\n\n\t\t@if $content {\n\t\t\tcontent: $content;\n\t\t}\n\n\t\t-moz-osx-font-smoothing: grayscale;\n\t\t-webkit-font-smoothing: antialiased;\n\t\tdisplay: inline-block;\n\t\tfont-style: normal;\n\t\tfont-variant: normal;\n\t\ttext-rendering: auto;\n\t\tline-height: 1;\n\t\ttext-transform: none !important;\n\n\t\t@if ($category == brands) {\n\t\t\tfont-family: 'Font Awesome 5 Brands';\n\t\t}\n\t\t@elseif ($category == solid) {\n\t\t\tfont-family: 'Font Awesome 5 Free';\n\t\t\tfont-weight: 900;\n\t\t}\n\t\t@else {\n\t\t\tfont-family: 'Font Awesome 5 Free';\n\t\t\tfont-weight: 400;\n\t\t}\n\n\t}\n\n}\n\n/// Applies padding to an element, taking the current element-margin value into account.\n/// @param {mixed} $tb Top/bottom padding.\n/// @param {mixed} $lr Left/right padding.\n/// @param {list} $pad Optional extra padding (in the following order top, right, bottom, left)\n/// @param {bool} $important If true, adds !important.\n@mixin padding($tb, $lr, $pad: (0,0,0,0), $important: null) {\n\n\t@if $important {\n\t\t$important: '!important';\n\t}\n\n\t$x: 0.1em;\n\n\t@if unit(_size(element-margin)) == 'rem' {\n\t\t$x: 0.1rem;\n\t}\n\n\tpadding: ($tb + nth($pad,1)) ($lr + nth($pad,2)) max($x, $tb - _size(element-margin) + nth($pad,3)) ($lr + nth($pad,4)) #{$important};\n\n}\n\n/// Encodes a SVG data URL so IE doesn't choke (via codepen.io/jakob-e/pen/YXXBrp).\n/// @param {string} $svg SVG data URL.\n/// @return {string} Encoded SVG data URL.\n@function svg-url($svg) {\n\n\t$svg: str-replace($svg, '\"', '\\'');\n\t$svg: str-replace($svg, '%', '%25');\n\t$svg: str-replace($svg, '<', '%3C');\n\t$svg: str-replace($svg, '>', '%3E');\n\t$svg: str-replace($svg, '&', '%26');\n\t$svg: str-replace($svg, '#', '%23');\n\t$svg: str-replace($svg, '{', '%7B');\n\t$svg: str-replace($svg, '}', '%7D');\n\t$svg: str-replace($svg, ';', '%3B');\n\n\t@return url(\"data:image/svg+xml;charset=utf8,#{$svg}\");\n\n}"
  },
  {
    "path": "public/sass/libs/_vars.scss",
    "content": "// Misc.\n\t$misc: (\n\t\tbg:\t\t\t\t\t#348cb2 url(\"images/bg.jpg\") bottom left,\n\t\tbg-width:\t\t\t1500px\n\t);\n\n// Duration.\n\t$duration: (\n\t\tbg:\t\t\t\t\t60s,\n\t\twrapper:\t\t\t3s,\n\t\toverlay:\t\t\t1.5s,\n\t\theader:\t\t\t\t1s,\n\t\tnav-icons:\t\t\t0.5s\n\t);\n\n// Size.\n\t$size: (\n\t\tnav-icon-wrapper:\t5.35em,\n\t\tnav-icon:\t\t\t1.75em\n\t);\n\n// Font.\n\t$font: (\n\t);\n\n// Palette.\n\t$palette: (\n\t\tbg:\t\t\t\t\t#fff,\n\t\tfg:\t\t\t\t\t#fff,\n\n\t\tnav-icon: (\n\t\t\thover-bg:\t\trgba(255,255,255,0.175),\n\t\t\thover-fg:\t\t#fff,\n\t\t\tactive-bg:\t\trgba(255,255,255,0.35),\n\t\t\tactive-fg:\t\t#fff\n\t\t)\n\t);"
  },
  {
    "path": "public/sass/libs/_vendor.scss",
    "content": "// vendor.scss v1.0 | @ajlkn | MIT licensed */\n\n// Vars.\n\n\t/// Vendor prefixes.\n\t/// @var {list}\n\t$vendor-prefixes: (\n\t\t'-moz-',\n\t\t'-webkit-',\n\t\t'-ms-',\n\t\t''\n\t);\n\n\t/// Properties that should be vendorized.\n\t/// Data via caniuse.com, github.com/postcss/autoprefixer, and developer.mozilla.org\n\t/// @var {list}\n\t$vendor-properties: (\n\n\t\t// Animation.\n\t\t\t'animation',\n\t\t\t'animation-delay',\n\t\t\t'animation-direction',\n\t\t\t'animation-duration',\n\t\t\t'animation-fill-mode',\n\t\t\t'animation-iteration-count',\n\t\t\t'animation-name',\n\t\t\t'animation-play-state',\n\t\t\t'animation-timing-function',\n\n\t\t// Appearance.\n\t\t\t'appearance',\n\n\t\t// Backdrop filter.\n\t\t\t'backdrop-filter',\n\n\t\t// Background image options.\n\t\t\t'background-clip',\n\t\t\t'background-origin',\n\t\t\t'background-size',\n\n\t\t// Box sizing.\n\t\t\t'box-sizing',\n\n\t\t// Clip path.\n\t\t\t'clip-path',\n\n\t\t// Filter effects.\n\t\t\t'filter',\n\n\t\t// Flexbox.\n\t\t\t'align-content',\n\t\t\t'align-items',\n\t\t\t'align-self',\n\t\t\t'flex',\n\t\t\t'flex-basis',\n\t\t\t'flex-direction',\n\t\t\t'flex-flow',\n\t\t\t'flex-grow',\n\t\t\t'flex-shrink',\n\t\t\t'flex-wrap',\n\t\t\t'justify-content',\n\t\t\t'order',\n\n\t\t// Font feature.\n\t\t\t'font-feature-settings',\n\t\t\t'font-language-override',\n\t\t\t'font-variant-ligatures',\n\n\t\t// Font kerning.\n\t\t\t'font-kerning',\n\n\t\t// Fragmented borders and backgrounds.\n\t\t\t'box-decoration-break',\n\n\t\t// Grid layout.\n\t\t\t'grid-column',\n\t\t\t'grid-column-align',\n\t\t\t'grid-column-end',\n\t\t\t'grid-column-start',\n\t\t\t'grid-row',\n\t\t\t'grid-row-align',\n\t\t\t'grid-row-end',\n\t\t\t'grid-row-start',\n\t\t\t'grid-template-columns',\n\t\t\t'grid-template-rows',\n\n\t\t// Hyphens.\n\t\t\t'hyphens',\n\t\t\t'word-break',\n\n\t\t// Masks.\n\t\t\t'mask',\n\t\t\t'mask-border',\n\t\t\t'mask-border-outset',\n\t\t\t'mask-border-repeat',\n\t\t\t'mask-border-slice',\n\t\t\t'mask-border-source',\n\t\t\t'mask-border-width',\n\t\t\t'mask-clip',\n\t\t\t'mask-composite',\n\t\t\t'mask-image',\n\t\t\t'mask-origin',\n\t\t\t'mask-position',\n\t\t\t'mask-repeat',\n\t\t\t'mask-size',\n\n\t\t// Multicolumn.\n\t\t\t'break-after',\n\t\t\t'break-before',\n\t\t\t'break-inside',\n\t\t\t'column-count',\n\t\t\t'column-fill',\n\t\t\t'column-gap',\n\t\t\t'column-rule',\n\t\t\t'column-rule-color',\n\t\t\t'column-rule-style',\n\t\t\t'column-rule-width',\n\t\t\t'column-span',\n\t\t\t'column-width',\n\t\t\t'columns',\n\n\t\t// Object fit.\n\t\t\t'object-fit',\n\t\t\t'object-position',\n\n\t\t// Regions.\n\t\t\t'flow-from',\n\t\t\t'flow-into',\n\t\t\t'region-fragment',\n\n\t\t// Scroll snap points.\n\t\t\t'scroll-snap-coordinate',\n\t\t\t'scroll-snap-destination',\n\t\t\t'scroll-snap-points-x',\n\t\t\t'scroll-snap-points-y',\n\t\t\t'scroll-snap-type',\n\n\t\t// Shapes.\n\t\t\t'shape-image-threshold',\n\t\t\t'shape-margin',\n\t\t\t'shape-outside',\n\n\t\t// Tab size.\n\t\t\t'tab-size',\n\n\t\t// Text align last.\n\t\t\t'text-align-last',\n\n\t\t// Text decoration.\n\t\t\t'text-decoration-color',\n\t\t\t'text-decoration-line',\n\t\t\t'text-decoration-skip',\n\t\t\t'text-decoration-style',\n\n\t\t// Text emphasis.\n\t\t\t'text-emphasis',\n\t\t\t'text-emphasis-color',\n\t\t\t'text-emphasis-position',\n\t\t\t'text-emphasis-style',\n\n\t\t// Text size adjust.\n\t\t\t'text-size-adjust',\n\n\t\t// Text spacing.\n\t\t\t'text-spacing',\n\n\t\t// Transform.\n\t\t\t'transform',\n\t\t\t'transform-origin',\n\n\t\t// Transform 3D.\n\t\t\t'backface-visibility',\n\t\t\t'perspective',\n\t\t\t'perspective-origin',\n\t\t\t'transform-style',\n\n\t\t// Transition.\n\t\t\t'transition',\n\t\t\t'transition-delay',\n\t\t\t'transition-duration',\n\t\t\t'transition-property',\n\t\t\t'transition-timing-function',\n\n\t\t// Unicode bidi.\n\t\t\t'unicode-bidi',\n\n\t\t// User select.\n\t\t\t'user-select',\n\n\t\t// Writing mode.\n\t\t\t'writing-mode',\n\n\t);\n\n\t/// Values that should be vendorized.\n\t/// Data via caniuse.com, github.com/postcss/autoprefixer, and developer.mozilla.org\n\t/// @var {list}\n\t$vendor-values: (\n\n\t\t// Cross fade.\n\t\t\t'cross-fade',\n\n\t\t// Element function.\n\t\t\t'element',\n\n\t\t// Filter function.\n\t\t\t'filter',\n\n\t\t// Flexbox.\n\t\t\t'flex',\n\t\t\t'inline-flex',\n\n\t\t// Grab cursors.\n\t\t\t'grab',\n\t\t\t'grabbing',\n\n\t\t// Gradients.\n\t\t\t'linear-gradient',\n\t\t\t'repeating-linear-gradient',\n\t\t\t'radial-gradient',\n\t\t\t'repeating-radial-gradient',\n\n\t\t// Grid layout.\n\t\t\t'grid',\n\t\t\t'inline-grid',\n\n\t\t// Image set.\n\t\t\t'image-set',\n\n\t\t// Intrinsic width.\n\t\t\t'max-content',\n\t\t\t'min-content',\n\t\t\t'fit-content',\n\t\t\t'fill',\n\t\t\t'fill-available',\n\t\t\t'stretch',\n\n\t\t// Sticky position.\n\t\t\t'sticky',\n\n\t\t// Transform.\n\t\t\t'transform',\n\n\t\t// Zoom cursors.\n\t\t\t'zoom-in',\n\t\t\t'zoom-out',\n\n\t);\n\n// Functions.\n\n\t/// Removes a specific item from a list.\n\t/// @author Hugo Giraudel\n\t/// @param {list} $list List.\n\t/// @param {integer} $index Index.\n\t/// @return {list} Updated list.\n\t@function remove-nth($list, $index) {\n\n\t\t$result: null;\n\n\t\t@if type-of($index) != number {\n\t\t\t@warn \"$index: #{quote($index)} is not a number for `remove-nth`.\";\n\t\t}\n\t\t@else if $index == 0 {\n\t\t\t@warn \"List index 0 must be a non-zero integer for `remove-nth`.\";\n\t\t}\n\t\t@else if abs($index) > length($list) {\n\t\t\t@warn \"List index is #{$index} but list is only #{length($list)} item long for `remove-nth`.\";\n\t\t}\n\t\t@else {\n\n\t\t\t$result: ();\n\t\t\t$index: if($index < 0, length($list) + $index + 1, $index);\n\n\t\t\t@for $i from 1 through length($list) {\n\n\t\t\t\t@if $i != $index {\n\t\t\t\t\t$result: append($result, nth($list, $i));\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\t@return $result;\n\n\t}\n\n\t/// Replaces a substring within another string.\n\t/// @author Hugo Giraudel\n\t/// @param {string} $string String.\n\t/// @param {string} $search Substring.\n\t/// @param {string} $replace Replacement.\n\t/// @return {string} Updated string.\n\t@function str-replace($string, $search, $replace: '') {\n\n\t\t$index: str-index($string, $search);\n\n\t\t@if $index {\n\t\t\t@return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);\n\t\t}\n\n\t\t@return $string;\n\n\t}\n\n\t/// Replaces a substring within each string in a list.\n\t/// @param {list} $strings List of strings.\n\t/// @param {string} $search Substring.\n\t/// @param {string} $replace Replacement.\n\t/// @return {list} Updated list of strings.\n\t@function str-replace-all($strings, $search, $replace: '') {\n\n\t\t@each $string in $strings {\n\t\t\t$strings: set-nth($strings, index($strings, $string), str-replace($string, $search, $replace));\n\t\t}\n\n\t\t@return $strings;\n\n\t}\n\n// Mixins.\n\n\t/// Wraps @content in vendorized keyframe blocks.\n\t/// @param {string} $name Name.\n\t@mixin keyframes($name) {\n\n\t\t@-moz-keyframes #{$name} { @content; }\n\t\t@-webkit-keyframes #{$name} { @content; }\n\t\t@-ms-keyframes #{$name} { @content; }\n\t\t@keyframes #{$name} { @content; }\n\n\t}\n\n\t/// Vendorizes a declaration's property and/or value(s).\n\t/// @param {string} $property Property.\n\t/// @param {mixed} $value String/list of value(s).\n\t@mixin vendor($property, $value) {\n\n\t\t// Determine if property should expand.\n\t\t\t$expandProperty: index($vendor-properties, $property);\n\n\t\t// Determine if value should expand (and if so, add '-prefix-' placeholder).\n\t\t\t$expandValue: false;\n\n\t\t\t@each $x in $value {\n\t\t\t\t@each $y in $vendor-values {\n\t\t\t\t\t@if $y == str-slice($x, 1, str-length($y)) {\n\n\t\t\t\t\t\t$value: set-nth($value, index($value, $x), '-prefix-' + $x);\n\t\t\t\t\t\t$expandValue: true;\n\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t// Expand property?\n\t\t\t@if $expandProperty {\n\t\t\t    @each $vendor in $vendor-prefixes {\n\t\t\t        #{$vendor}#{$property}: #{str-replace-all($value, '-prefix-', $vendor)};\n\t\t\t    }\n\t\t\t}\n\n\t\t// Expand just the value?\n\t\t\t@elseif $expandValue {\n\t\t\t    @each $vendor in $vendor-prefixes {\n\t\t\t        #{$property}: #{str-replace-all($value, '-prefix-', $vendor)};\n\t\t\t    }\n\t\t\t}\n\n\t\t// Neither? Treat them as a normal declaration.\n\t\t\t@else {\n\t\t        #{$property}: #{$value};\n\t\t\t}\n\n\t}"
  },
  {
    "path": "public/sass/main.scss",
    "content": "@import 'libs/vars';\n@import 'libs/functions';\n@import 'libs/mixins';\n@import 'libs/vendor';\n@import 'libs/breakpoints';\n@import url(\"https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,900\");\n@import url(\"fontawesome-all.min.css\");\n\n/*\n\tAerial by HTML5 UP\n\thtml5up.net | @ajlkn\n\tFree for personal and commercial use under the CCA 3.0 license (html5up.net/license)\n*/\n\n// Breakpoints.\n\n\t@include breakpoints((\n\t\twide:      ( 1281px,  1680px ),\n\t\tnormal:    ( 737px,   1280px ),\n\t\tmobile:    ( 481px,   736px  ),\n\t\tmobilep:   ( null,    480px  )\n\t));\n\n// Mixins.\n\n\t@mixin bg($width) {\n\t\t@include keyframes('bg') {\n\t\t\t0%\t\t{ @include vendor('transform', 'translate3d(0,0,0)'); }\n\t\t\t100%\t{ @include vendor('transform', 'translate3d(#{$width * -1},0,0)'); }\n\t\t}\n\n\t\t#bg {\n\t\t\tbackground-size: $width auto;\n\t\t\twidth: ($width * 3);\n\t\t}\n\t}\n\n\t$delay-wrapper:\t\t\t_duration(wrapper) - 1s;\n\t$delay-overlay:\t\t\t$delay-wrapper - 0.5s;\n\t$delay-header:\t\t\t$delay-overlay + _duration(overlay) - 0.75s;\n\t$delay-nav-icons:\t\t$delay-header + _duration(header) - 1s;\n\t$delay-nav-icon:\t\t0.25s;\n\n// Reset.\n// Based on meyerweb.com/eric/tools/css/reset (v2.0 | 20110126 | License: public domain)\n\n\thtml, body, div, span, applet, object,\n\tiframe, h1, h2, h3, h4, h5, h6, p, blockquote,\n\tpre, a, abbr, acronym, address, big, cite,\n\tcode, del, dfn, em, img, ins, kbd, q, s, samp,\n\tsmall, strike, strong, sub, sup, tt, var, b,\n\tu, i, center, dl, dt, dd, ol, ul, li, fieldset,\n\tform, label, legend, table, caption, tbody,\n\ttfoot, thead, tr, th, td, article, aside,\n\tcanvas, details, embed, figure, figcaption,\n\tfooter, header, hgroup, menu, nav, output, ruby,\n\tsection, summary, time, mark, audio, video {\n\t\tmargin: 0;\n\t\tpadding: 0;\n\t\tborder: 0;\n\t\tfont-size: 100%;\n\t\tfont: inherit;\n\t\tvertical-align: baseline;\n\t}\n\n\tarticle, aside, details, figcaption, figure,\n\tfooter, header, hgroup, menu, nav, section {\n\t\tdisplay: block;\n\t}\n\n\tbody {\n\t\tline-height: 1;\n\t}\n\n\tol, ul {\n\t\tlist-style:none;\n\t}\n\n\tblockquote,\tq {\n\t\tquotes: none;\n\n\t\t&:before,\n\t\t&:after {\n\t\t\tcontent: '';\n\t\t\tcontent: none;\n\t\t}\n\t}\n\n\ttable {\n\t\tborder-collapse: collapse;\n\t\tborder-spacing: 0;\n\t}\n\n\tbody {\n\t\t-webkit-text-size-adjust: none;\n\t}\n\n\tmark {\n\t\tbackground-color: transparent;\n\t\tcolor: inherit;\n\t}\n\n\tinput::-moz-focus-inner {\n\t\tborder: 0;\n\t\tpadding: 0;\n\t}\n\n\tinput, select, textarea {\n\t\t-moz-appearance: none;\n\t\t-webkit-appearance: none;\n\t\t-ms-appearance: none;\n\t\tappearance: none;\n\t}\n\n/* Basic */\n\n\t// Set box model to border-box.\n\t// Based on css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice\n\t\thtml {\n\t\t\tbox-sizing: border-box;\n\t\t}\n\n\t\t*, *:before, *:after {\n\t\t\tbox-sizing: inherit;\n\t\t}\n\n\tbody {\n\t\tbackground: _palette(bg);\n\t\toverflow: hidden;\n\n\t\t// Stops initial animations until page loads.\n\t\t\t&.is-preload {\n\t\t\t\t*, *:before, *:after {\n\t\t\t\t\t@include vendor('animation', 'none !important');\n\t\t\t\t\t@include vendor('transition', 'none !important');\n\t\t\t\t}\n\t\t\t}\n\n\t}\n\n\tbody, input, select, textarea {\n\t\tcolor: _palette(fg);\n\t\tfont-family: 'Source Sans Pro', sans-serif;\n\t\tfont-size: 15pt;\n\t\tfont-weight: 300 !important;\n\t\tletter-spacing: -0.025em;\n\t\tline-height: 1.75em;\n\t}\n\n\ta {\n\t\t@include vendor('transition', 'border-color 0.2s ease-in-out');\n\t\tborder-bottom: dotted 1px;\n\t\tcolor: inherit;\n\t\toutline: 0;\n\t\ttext-decoration: none;\n\n\t\t&:hover {\n\t\t\tborder-color: transparent;\n\t\t}\n\t}\n\n/* Icon */\n\n\t.icon {\n\t\t@include icon;\n\t\tposition: relative;\n\n\t\t> .label {\n\t\t\tdisplay: none;\n\t\t}\n\n\t\t&.solid {\n\t\t\t&:before {\n\t\t\t\tfont-weight: 900;\n\t\t\t}\n\t\t}\n\n\t\t&.brands {\n\t\t\t&:before {\n\t\t\t\tfont-family: 'Font Awesome 5 Brands';\n\t\t\t}\n\t\t}\n\t}\n\n/* Wrapper */\n\n\t@include keyframes('wrapper') {\n\t\t0%\t\t{ opacity: 0; }\n\t\t100%\t{ opacity: 1; }\n\t}\n\n\t#wrapper {\n\t\t@include vendor('animation', 'wrapper #{_duration(wrapper)} forwards');\n\t\theight: 100%;\n\t\tleft: 0;\n\t\topacity: 0;\n\t\tposition: fixed;\n\t\ttop: 0;\n\t\twidth: 100%;\n\t}\n\n/* BG */\n\n\t#bg {\n\t\t@include vendor('animation', 'bg #{_duration(bg)} linear infinite');\n\t\t@include vendor('backface-visibility', 'hidden');\n\t\t@include vendor('transform', 'translate3d(0,0,0)');\n\n\t\t/* Set your background with this */\n\t\tbackground: _misc(bg);\n\n\t\tbackground-repeat: repeat-x;\n\t\theight: 100%;\n\t\tleft: 0;\n\t\topacity: 1;\n\t\tposition: fixed;\n\t\ttop: 0;\n\t}\n\n\t@include bg(_misc(bg-width) * 1.5);\n\n/* Overlay */\n\n\t@include keyframes('overlay') {\n\t\t0%\t\t{ opacity: 0; }\n\t\t100%\t{ opacity: 1; }\n\t}\n\n\t#overlay {\n\t\t@include vendor('animation', 'overlay #{_duration(overlay)} #{$delay-overlay} forwards');\n\t\tbackground-attachment: fixed, fixed;\n\t\tbackground-image: url('images/overlay-pattern.png'), url('images/overlay.svg');\n\t\tbackground-position: top left, center center;\n\t\tbackground-repeat: repeat, no-repeat;\n\t\tbackground-size: auto, cover;\n\t\theight: 100%;\n\t\tleft: 0;\n\t\topacity: 0;\n\t\tposition: fixed;\n\t\ttop: 0;\n\t\twidth: 100%;\n\t}\n\n/* Main */\n\n\t#main {\n\t\theight: 100%;\n\t\tleft: 0;\n\t\tposition: fixed;\n\t\ttext-align: center;\n\t\ttop: 0;\n\t\twidth: 100%;\n\n\t\t&:before {\n\t\t\tcontent: '';\n\t\t\tdisplay: inline-block;\n\t\t\theight: 100%;\n\t\t\tmargin-right: 0;\n\t\t\tvertical-align: middle;\n\t\t\twidth: 1px;\n\t\t}\n\t}\n\n/* Header */\n\n\t@include keyframes('header') {\n\t\t0%\t\t{ @include vendor('transform', 'translate3d(0,1em,0)'); opacity: 0; }\n\t\t100%\t{ @include vendor('transform', 'translate3d(0,0,0)'); opacity: 1; }\n\t}\n\n\t@include keyframes('nav-icons') {\n\t\t0%\t\t{ @include vendor('transform', 'translate3d(0,1em,0)'); opacity: 0; }\n\t\t100%\t{ @include vendor('transform', 'translate3d(0,0,0)'); opacity: 1; }\n\t}\n\n\t#header {\n\t\t@include vendor('animation', 'header #{_duration(header)} #{$delay-header} forwards');\n\t\t@include vendor('backface-visibility', 'hidden');\n\t\t@include vendor('transform', 'translate3d(0,0,0)');\n\t\tcursor: default;\n\t\tdisplay: inline-block;\n\t\topacity: 0;\n\t\tposition: relative;\n\t\ttext-align: center;\n\t\ttop: -1em;\n\t\tvertical-align: middle;\n\t\twidth: 90%;\n\n\t\th1 {\n\t\t\tfont-size: 4.35em;\n\t\t\tfont-weight: 900;\n\t\t\tletter-spacing: -0.035em;\n\t\t\tline-height: 1em;\n\t\t}\n\n\t\tp {\n\t\t\tfont-size: 1.25em;\n\t\t\tmargin: 0.75em 0 0.25em 0;\n\t\t\topacity: 0.75;\n\t\t}\n\n\t\tnav {\n\t\t\tmargin: 1.5em 0 0 0;\n\n\t\t\tli {\n\t\t\t\t@include vendor('animation', 'nav-icons #{_duration(nav-icons)} ease-in-out forwards');\n\t\t\t\t@include vendor('backface-visibility', 'hidden');\n\t\t\t\t@include vendor('transform', 'translate3d(0,0,0)');\n\t\t\t\tdisplay: inline-block;\n\t\t\t\theight: _size(nav-icon-wrapper);\n\t\t\t\tline-height: _size(nav-icon-wrapper) * 1.1;\n\t\t\t\topacity: 0;\n\t\t\t\tposition: relative;\n\t\t\t\ttop: 0;\n\t\t\t\twidth: _size(nav-icon-wrapper);\n\n\t\t\t\t@for $x from 1 through 10 {\n\t\t\t\t\t&:nth-child(#{$x}) {\n\t\t\t\t\t\t@include vendor('animation-delay', ($delay-nav-icons + ($x * $delay-nav-icon)) + '');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ta {\n\t\t\t\t-webkit-tap-highlight-color: rgba(0,0,0,0);\n\t\t\t\t-webkit-touch-callout: none;\n\t\t\t\tborder: 0;\n\t\t\t\tdisplay: inline-block;\n\n\t\t\t\t&:before {\n\t\t\t\t\t@include vendor('transition', 'all 0.2s ease-in-out');\n\t\t\t\t\tborder-radius: 100%;\n\t\t\t\t\tborder: solid 1px _palette(fg);\n\t\t\t\t\tdisplay: block;\n\t\t\t\t\tfont-size: _size(nav-icon);\n\t\t\t\t\theight: 2.5em;\n\t\t\t\t\tline-height: 2.5em;\n\t\t\t\t\tposition: relative;\n\t\t\t\t\ttext-align: center;\n\t\t\t\t\ttop: 0;\n\t\t\t\t\twidth: 2.5em;\n\t\t\t\t}\n\n\t\t\t\t&:hover {\n\t\t\t\t\tfont-size: 1.1em;\n\n\t\t\t\t\t&:before {\n\t\t\t\t\t\tbackground-color: _palette(nav-icon, hover-bg);\n\t\t\t\t\t\tcolor: _palette(nav-icon, hover-fg);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t&:active {\n\t\t\t\t\tfont-size: 0.95em;\n\t\t\t\t\tbackground: none;\n\n\t\t\t\t\t&:before {\n\t\t\t\t\t\tbackground-color: _palette(nav-icon, active-bg);\n\t\t\t\t\t\tcolor: _palette(nav-icon, active-fg);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tspan {\n\t\t\t\t\tdisplay: none;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n/* Footer */\n\n\t#footer {\n\t\t@include vendor('background-image', 'linear-gradient(top, rgba(0,0,0,0), rgba(0,0,0,0.5) 75%)');\n\t\tbottom: 0;\n\t\tcursor: default;\n\t\theight: 6em;\n\t\tleft: 0;\n\t\tline-height: 8em;\n\t\tposition: absolute;\n\t\ttext-align: center;\n\t\twidth: 100%;\n\t}\n\n/* Wide */\n\n\t@include breakpoint('<=wide') {\n\n\t\t/* Basic */\n\n\t\t\tbody, input, select, textarea {\n\t\t\t\tfont-size: 13pt;\n\t\t\t}\n\n\t\t/* BG */\n\n\t\t\t@include bg(_misc(bg-width));\n\n\t}\n\n/* Normal */\n\n\t@include breakpoint('<=normal') {\n\n\t\t/* Basic */\n\n\t\t\tbody, input, select, textarea {\n\t\t\t\tfont-size: 12pt;\n\t\t\t}\n\n\t\t/* BG */\n\n\t\t\t@include bg(_misc(bg-width) * 0.5);\n\n\t}\n\n/* Mobile */\n\n\t@include breakpoint('<=mobile') {\n\n\t\t/* Basic */\n\n\t\t\tbody {\n\t\t\t\tmin-width: 320px;\n\t\t\t}\n\n\t\t\tbody, input, select, textarea {\n\t\t\t\tfont-size: 11pt;\n\t\t\t}\n\n\t\t/* BG */\n\n\t\t\t@include bg(_misc(bg-width) * 0.2);\n\n\t\t/* Header */\n\n\t\t\t#header {\n\t\t\t\th1 {\n\t\t\t\t\tfont-size: 2.5em;\n\t\t\t\t}\n\n\t\t\t\tp {\n\t\t\t\t\tfont-size: 1em;\n\t\t\t\t}\n\n\t\t\t\tnav {\n\t\t\t\t\tfont-size: 1em;\n\n\t\t\t\t\ta {\n\t\t\t\t\t\t&:hover {\n\t\t\t\t\t\t\tfont-size: 1em;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t&:active {\n\t\t\t\t\t\t\tfont-size: 1em;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t}\n\n/* Mobile (Portrait) */\n\n\t@include breakpoint('<=mobilep') {\n\n\t\t/* BG */\n\n\t\t\t@include bg(_misc(bg-width) * 0.275);\n\n\t\t/* Header */\n\n\t\t\t#header {\n\t\t\t\tnav {\n\t\t\t\t\tpadding: 0 1em;\n\t\t\t\t}\n\t\t\t}\n\n\t}"
  },
  {
    "path": "public/sass/noscript.scss",
    "content": "@import 'libs/vars';\n@import 'libs/functions';\n@import 'libs/mixins';\n@import 'libs/vendor';\n@import 'libs/breakpoints';\n\n/*\n\tAerial by HTML5 UP\n\thtml5up.net | @ajlkn\n\tFree for personal and commercial use under the CCA 3.0 license (html5up.net/license)\n*/\n\n/* Wrapper */\n\n\t#wrapper {\n\t\topacity: 1 !important;\n\t}\n\n/* Overlay */\n\n\t#overlay {\n\t\topacity: 1 !important;\n\t}\n\n/* Header */\n\n\t#header {\n\t\topacity: 1 !important;\n\n\t\tnav {\n\t\t\tli {\n\t\t\t\topacity: 1 !important;\n\t\t\t}\n\t\t}\n\t}"
  },
  {
    "path": "src/App.vue",
    "content": "<script setup lang=\"ts\">\nimport { ref } from \"vue\"\nimport { useDark } from \"@vueuse/core\"\nimport {  Moon, Sunny } from \"@element-plus/icons-vue\"\nimport { Github } from \"@/components/icon\"\nimport router from \"@/router\"\n\nconst isDark = useDark()\n\nconst themeStyle = {\n    \"--el-switch-on-color\": \"var(--el-fill-color)\",\n    \"--el-switch-off-color\": \"var(--el-fill-color)\",\n    \"--el-switch-border-color\": \"var(--el-border-color-dark)\"\n}\n\nconst goIndex = () => router.push(\"/\")\nconst openGithub = () => window.open(\"https://github.com/ginnnnnn666/GinsMooc\")\nconst spaceCount = () => window.innerWidth < 768 ? 8 : 16\n\nconst defaultColor = \"var(--el-color-primary-dark-2)\"\nconst hoverColor = \"var(--el-color-primary)\"\nconst color = [defaultColor, hoverColor]\nconst githubBtnColor = ref(0)\n</script>\n\n<template>\n    <ElContainer>\n        <ElHeader class=\"header\">\n            <ElSpace :size=\"8\" class=\"nav\">\n                <ElMenu :default-active=\"$route.path\" mode=\"horizontal\" router class=\"nav-start\" :ellipsis=\"false\">\n                    <ElImage class=\"icon hidden-xs-only\" src=\"/favicon.png\" @click=\"goIndex()\"></ElImage>\n                    <ElMenuItem class=\"menu-item hidden-xs-only\" index=\"/mooc\">Mooc</ElMenuItem>\n                    <ElMenuItem class=\"menu-item hidden-xs-only\" index=\"/video\">Video</ElMenuItem>\n\n                    <ElImage class=\"icon small hidden-sm-and-up\" src=\"/favicon.png\" @click=\"goIndex()\"></ElImage>\n                    <ElMenuItem class=\"menu-item small hidden-sm-and-up\" index=\"/mooc\">Mooc</ElMenuItem>\n                    <!-- <ElMenuItem class=\"menu-item small hidden-sm-and-up\" index=\"/video\">Video</ElMenuItem> -->\n                </ElMenu>\n                <div class=\"nav-center\">\n                    <RouterView name=\"header\" />\n                </div>\n                <ElSpace class=\"nav-end\" :size=\"spaceCount()\">\n                    <ElSwitch v-model=\"isDark\" inline-prompt :active-icon=\"Moon\" :inactive-icon=\"Sunny\" :style=\"themeStyle\"\n                        class=\"theme-switch\"></ElSwitch>\n                    <ElIcon :color=\"color[githubBtnColor % 2]\" @click=\"openGithub\" @mouseenter=\"githubBtnColor++\"\n                        @mouseleave=\"githubBtnColor++\" style=\"cursor: pointer\" class=\"github-icon\">\n                        <Github />\n                    </ElIcon>\n                    <ElAvatar class=\"avatar\" :size=\"25\" src=\"/headicon.png\"></ElAvatar>\n                </ElSpace>\n            </ElSpace>\n        </ElHeader>\n        <ElMain class=\"main\">\n            <Suspense>\n                <RouterView />\n            </Suspense>\n        </ElMain>\n    </ElContainer>\n</template>\n\n<style scoped>\n.header {\n    height: 50px;\n    padding: 0;\n    border-bottom: 1px solid var(--el-border-color-light);\n    background-color: var(--el-bg-color);\n}\n\n@media only screen and (max-width: 768px) {\n    .header {\n        height: 40px;\n    }\n\n    .nav-start {\n        height: 40px !important;\n    }\n\n    .menu-item.small {\n        padding: 0 8px;\n        font-size: 13px;\n    }\n\n    .icon.small {\n        height: 20px;\n        width: 20px;\n        margin: 10px 16px 10px 16px;\n    }\n\n    .github-icon {\n        font-size: 26px !important;\n    }\n\n    .avatar {\n        height: 22px;\n        width: 22px;\n    }\n}\n\n.main {\n    padding: 0;\n    display: flex;\n    justify-content: center;\n}\n\n.nav {\n    display: flex;\n}\n\n.nav :nth-child(2) {\n    flex: 1;\n}\n\n.nav-start {\n    height: 49px;\n    border-bottom: 0;\n    background-color: transparent;\n    color: var(--el-text-color-primary);\n}\n\n.nav-end {\n    display: flex;\n    justify-content: end;\n}\n\n.github-icon {\n    font-size: 30px;\n}\n</style>\n\n<style>\nbody {\n    margin: 0;\n    height: 100vh;\n    transition: margin 0s;\n}\n\ni,\np {\n    margin: 0 !important;\n}\n\n::selection {\n    background-color: var(--el-color-primary-light-3);\n    color: var(--el-color-white);\n}\n\n.el-header,\n.el-footer,\n.el-main,\n.el-aside {\n    transition: all var(--el-transition-duration) !important;\n}\n\n.el-container {\n    height: 100%;\n    width: 100%;\n}\n\n.icon {\n    height: 24px;\n    width: 24px;\n    margin: 13px 30px;\n    cursor: pointer;\n}\n\n.theme-switch .el-switch__core .el-switch__inner .is-icon,\n.el-switch__core {\n    color: var(--el-text-color-primary);\n}\n\n.theme-switch .el-switch__core .el-switch__action {\n    background-color: var(--el-bg-color);\n}\n</style>\n"
  },
  {
    "path": "src/components/CourseCard.vue",
    "content": "<script setup lang=\"ts\">\nimport type { course } from \"@/type/mooc\"\nimport { ElImage } from \"element-plus\"\n\nconst props = defineProps<{\n    course: course\n}>()\n\nconst emit = defineEmits<{\n    (e: 'click', course: course, event: MouseEvent): void\n}>()\n</script>\n\n<template>\n    <li :class=\"{ 'course-card': true }\" @click=\"$emit('click', course, $event)\">\n        <ElImage :src=\"course.imageUrl\" class=\"course-card__img\" lazy></ElImage>\n        <div class=\"course-card__info\">\n            <div class=\"course-card__info-name\">{{ course.name }}</div>\n            <div class=\"course-card__info-school\">{{ course.school }}</div>\n        </div>\n    </li>\n</template>\n\n<style scoped>\n.course-card {\n    margin-bottom: 8px;\n    height: auto;\n    padding: 18px 12px 18px 24px;\n    display: flex;\n    border-radius: 8px;\n    transition: all 0.2s;\n    cursor: pointer;\n}\n.course-card:last-child {\n    margin-bottom: 0;\n}\n\n.course-card:active {\n    transform: scale(0.98);\n}\n\n.course-card.is-selected, .course-card:not(.is-selected):active {\n    background-color: var(--el-color-primary-light-5);\n    z-index: 5;\n}\n\n.course-card:not(.is-selected):hover {\n    background-color: var(--el-color-primary-light-7);\n}\n\n.course-card__img {\n    height: 48px;\n    width: 85px;\n    border-radius: 8px;\n    margin-right: 16px;\n}\n\n.course-card__info {\n    flex: 1;\n    display: flex;\n    width: 186px;\n    white-space: nowrap;\n    text-overflow: ellipsis;\n    overflow: hidden;\n    flex-direction: column;\n}\n\n.course-card__info-name {\n    flex: 1;\n    font-size: 18px;\n    color: var(--el-text-color-primary);\n    white-space: nowrap;\n    text-overflow: ellipsis;\n    overflow: hidden;\n}\n\n.course-card__info-school {\n    font-size: 12px;\n    color: var(--el-text-color-secondary);\n}\n</style>\n"
  },
  {
    "path": "src/components/QuestionCard.vue",
    "content": "<script setup lang=\"ts\">\nimport type { quiz, homework, option } from \"@/type/mooc\"\nimport { QuestionTypeEnumList } from \"@/type/mooc\"\nimport { Completion, SingleChoice, MultipleChoice, Homework, OnlineJudge } from \"@/components/question\"\n\nconst props = defineProps<{\n    data: quiz | homework\n    order: number\n}>()\n\nconst seq = (() => {\n    const ret = props.order.toString()\n    if (ret.length < 2) {\n        return \"0\" + ret\n    }\n    return ret\n})()\n\nconst isSingleChoice = (data: quiz | homework): data is quiz => {\n    return data.type === QuestionTypeEnumList.SingleChoice || data.type === QuestionTypeEnumList.Judge\n}\nconst isMultipleChoice = (data: quiz | homework): data is quiz => data.type === QuestionTypeEnumList.MultipleChoice\nconst isCompletion = (data: quiz | homework): data is quiz => data.type === QuestionTypeEnumList.Completion\nconst isRegularHomework = (data: quiz | homework): data is homework => data.type === QuestionTypeEnumList.Homework\nconst isOnlineJudge = (data: quiz | homework): data is homework => data.type === QuestionTypeEnumList.OnlineJudge\n</script>\n\n<template>\n    <ElContainer class=\"question-card\">\n        <SingleChoice v-if=\"isSingleChoice(data)\" :data=\"data\" :seq=\"seq\"></SingleChoice>\n        <MultipleChoice v-else-if=\"isMultipleChoice(data)\" :data=\"data\" :seq=\"seq\"></MultipleChoice>\n        <Completion v-else-if=\"isCompletion(data)\" :data=\"data\" :seq=\"seq\"></Completion>\n        <Homework v-else-if=\"isRegularHomework(data)\" :data=\"data\" :seq=\"seq\"></Homework>\n        <OnlineJudge v-else-if=\"isOnlineJudge(data)\" :data=\"data\" :seq=\"seq\"></OnlineJudge>\n    </ElContainer>\n</template>\n\n<style scoped>\n.question-card {\n    height: fit-content;\n    display: flex;\n    flex-direction: column;\n    padding: 20px 40px;\n    margin-top: 20px;\n    background-color: var(--el-bg-color);\n    box-shadow: var(--el-box-shadow-light);\n    transition: all 0.2s;\n}\n\n.question-card:hover {\n    transform: scale(1.02);\n}\n\n.question-card :deep(.question-card-header) {\n    margin-bottom: 14px;\n    color: var(--el-text-color-primary);\n    line-height: 1.5;\n}\n\n.question-card :deep(.question-card-body) {\n    color: var(--el-text-color-primary);\n    line-height: 1.5;\n}\n\n.question-card :deep(.question-card-body img) {\n    max-width: 100%;\n    height: auto;\n}\n\n.question-card :deep(.el-checkbox__label),\n.question-card :deep(.el-radio__label) {\n    white-space: normal;\n    line-height: 1.5;\n    color: var(--el-text-color-primary);\n}\n\n.question-card :deep(.question-card__seq) {\n    float: left;\n    margin-right: 8px;\n    font-weight: 700;\n    font-size: 16px;\n    color: var(--el-text-color-primary);\n}\n\n.question-card :deep(.question-card__title img) {\n    max-width: 100%;\n}\n\n@media only screen and (max-width: 768px) {\n    .question-card {\n        padding: 10px 20px;\n        margin-top: 8px;\n    }\n\n    .question-card :deep(.question-card-header) {\n        margin-bottom: 8px;\n    }\n\n    .question-card :deep(.question-card__seq),\n    .question-card :deep(.question-card__title) {\n        font-size: 14px;\n    }\n\n    .question-card :deep(.el-radio),\n    .question-card :deep(.el-checkbox) {\n        height: 24px;\n    }\n\n    .question-card :deep(.el-radio .el-radio__inner),\n    .question-card :deep(.el-checkbox .el-checkbox__inner) {\n        width: 12px;\n        height: 12px;\n    }\n\n    .question-card :deep(.el-checkbox .el-checkbox__inner:after) {\n        width: 2px;\n        height: 6px;\n    }\n\n    .question-card :deep(.el-radio .el-radio__label),\n    .question-card :deep(.el-checkbox .el-checkbox__label),\n    .question-card :deep(.question-card-body) {\n        font-size: 12px;\n    }\n\n    .question-card :deep(.el-input) {\n        --el-input-height: var(--el-component-size-small);\n        font-size: 12px;\n    }\n\n    .question-card :deep(.el-input__wrapper) {\n        padding: 1px 7px;\n    }\n\n    .question-card :deep(.el-input__inner) {\n        --el-input-inner-height: calc(var(--el-input-height, 24px) - 2px);\n    }\n}</style>\n"
  },
  {
    "path": "src/components/icon/Extension.vue",
    "content": "<template>\n    <svg viewBox=\"0 0 1024 1024\" width=\"200\" height=\"200\">\n        <path\n            d=\"M873.984 470.016q43.989333 0 75.989333 31.018667t32 75.008-32 75.008-75.989333 31.018667l-64 0 0 171.989333q0 34.005333-25.002667 59.008t-59.008 25.002667l-162.005333 0 0-64q0-48-34.005333-80.981333t-82.005333-32.981333-82.005333 32.981333-34.005333 80.981333l0 64-162.005333 0q-34.005333 0-59.008-25.002667t-25.002667-59.008l0-162.005333 64 0q48 0 80.981333-34.005333t32.981333-82.005333-32.981333-82.005333-80.981333-34.005333l-64 0 0-162.005333q0-34.005333 25.002667-59.008t59.008-25.002667l171.989333 0 0-64q0-43.989333 31.018667-75.989333t75.008-32 75.008 32 31.018667 75.989333l0 64 171.989333 0q34.005333 0 59.008 25.002667t25.002667 59.008l0 171.989333 64 0z\"\n            p-id=\"5381\"></path>\n    </svg>\n</template>\n"
  },
  {
    "path": "src/components/icon/Github.vue",
    "content": "<template>\n    <svg viewBox=\"0 0 24 24\" width=\"1.2em\" height=\"1.2em\">\n        <path\n            fill=\"currentColor\"\n            d=\"M12 2C6.475 2 2 6.475 2 12a9.994 9.994 0 0 0 6.838 9.488c.5.087.687-.213.687-.476c0-.237-.013-1.024-.013-1.862c-2.512.463-3.162-.612-3.362-1.175c-.113-.288-.6-1.175-1.025-1.413c-.35-.187-.85-.65-.013-.662c.788-.013 1.35.725 1.538 1.025c.9 1.512 2.338 1.087 2.912.825c.088-.65.35-1.087.638-1.337c-2.225-.25-4.55-1.113-4.55-4.938c0-1.088.387-1.987 1.025-2.688c-.1-.25-.45-1.275.1-2.65c0 0 .837-.262 2.75 1.026a9.28 9.28 0 0 1 2.5-.338c.85 0 1.7.112 2.5.337c1.912-1.3 2.75-1.024 2.75-1.024c.55 1.375.2 2.4.1 2.65c.637.7 1.025 1.587 1.025 2.687c0 3.838-2.337 4.688-4.562 4.938c.362.312.675.912.675 1.85c0 1.337-.013 2.412-.013 2.75c0 .262.188.574.688.474A10.016 10.016 0 0 0 22 12c0-5.525-4.475-10-10-10z\"\n        ></path>\n    </svg>\n</template>\n"
  },
  {
    "path": "src/components/icon/index.ts",
    "content": "import Github from \"./Github.vue\"\nimport Extension from \"./Extension.vue\"\n\nexport { Github, Extension }"
  },
  {
    "path": "src/components/index.ts",
    "content": "import CourseCard from \"./CourseCard.vue\"\nimport QuestionCard from \"./QuestionCard.vue\"\n\nexport {CourseCard, QuestionCard}"
  },
  {
    "path": "src/components/question/Completion.vue",
    "content": "<script setup lang=\"ts\">\nimport type { quiz } from \"@/type/mooc\"\nimport { ref } from \"vue\"\n\nconst props = defineProps<{\n    data: quiz\n    seq: string\n}>()\nconst stdAnswerArray = (props.data.stdAnswer as string).split(\"##%_YZPRLFH_%##\")\nconst [answer, backgroundColor, color] = [ref(\"\"), ref(\"transparent\"), ref(\"\")]\n\nconst checkAnswer = () => {\n    const answerArray = answer.value.split(\" / \")\n    for (const item of answerArray) {\n        if (stdAnswerArray.indexOf(item) === -1) {\n            backgroundColor.value = \"transparent\"\n            color.value = \"\"\n            return\n        }\n    }\n    backgroundColor.value = \"var(--el-color-success-light-8)\"\n    color.value = \"var(--el-color-primary)\"\n}\n\nconst setAnswer = () => {\n    let splicedAnswer = \"\"\n    const len = stdAnswerArray.length\n    for (let i = 0; i < len; i++) {\n        if (i > 0) {\n            splicedAnswer += \" / \"\n        }\n        splicedAnswer += stdAnswerArray[i]\n    }\n    answer.value = splicedAnswer\n    checkAnswer()\n}\nprops.data.title = props.data.title.replaceAll(/img\\d\\.ph\\.126\\.net/g, 'img-ph-mirror.nosdn.127.net')\n</script>\n\n<template>\n    <div class=\"question-card-header\" @click=\"setAnswer\">\n        <span class=\"question-card__seq\">{{ seq }}</span>\n        <div class=\"question-card__title\" v-html=\"data.title\"></div>\n    </div>\n    <div class=\"question-card-body\">\n        <ElInput\n            size=\"large\"\n            class=\"answer-input\"\n            v-model=\"answer\"\n            placeholder=\"在这里输入答案\"\n            @input=\"checkAnswer\"\n        ></ElInput>\n    </div>\n</template>\n\n<style scoped>\n.answer-input {\n    background-color: v-bind(backgroundColor);\n}\n.answer-input :deep(.el-input__wrapper, .el-input__inner) {\n    background-color: transparent;\n}\n.answer-input :deep(.el-input__inner) {\n    color: v-bind(color);\n}\n</style>\n"
  },
  {
    "path": "src/components/question/Homework.vue",
    "content": "<script setup lang=\"ts\">\nimport type { homework } from \"@/type/mooc\"\n\nconst props = defineProps<{\n    data: homework\n    seq: string\n}>()\nprops.data.title = props.data.title.replaceAll(/img\\d\\.ph\\.126\\.net/g, 'img-ph-mirror.nosdn.127.net')\nprops.data.answer = <string>props.data.answer?.replaceAll(/img\\d\\.ph\\.126\\.net/g, 'img-ph-mirror.nosdn.127.net')\n</script>\n\n<template>\n    <div class=\"question-card-header\">\n        <span class=\"question-card__seq\">{{ seq }}</span>\n        <div class=\"question-card__title\" v-html=\"data.title\"></div>\n    </div>\n    <div class=\"question-card-body\">\n        <span v-html=\"data.answer\"></span>\n    </div>\n</template>\n\n<style scoped></style>\n"
  },
  {
    "path": "src/components/question/MultipleChoice.vue",
    "content": "<script setup lang=\"ts\">\nimport type { quiz, option } from \"@/type/mooc\"\nimport { ref } from \"vue\"\n\nconst props = defineProps<{\n    data: quiz\n    seq: string\n}>()\nconst checkedList = ref(<Array<number>>new Array())\nconst answerList = (() => {\n    let ret = <Array<number>>new Array()\n    for (const item of <option[]>props.data.optionList) {\n        if (item.answer) {\n            ret.push(item.id)\n        }\n    }\n    return ret\n})()\n\nconst inAnswerList = (element: number) => answerList.indexOf(element) >= 0\nconst inCheckedList = (element: number) => checkedList.value.indexOf(element) >= 0\nconst checkedColor = (option: option) => {\n    if (checkedList.value.length !== answerList.length) {\n        return \"transparent\"\n    }\n    if (checkedList.value.every(inAnswerList)) {\n        return inAnswerList(option.id) ? \"var(--el-color-success-light-8)\" : \"transparent\"\n    }\n}\nconst setAnswer = () => {\n    checkedList.value.length = 0\n    answerList.forEach((value) => checkedList.value.push(value))\n}\nprops.data.title = props.data.title.replaceAll(/img\\d\\.ph\\.126\\.net/g, 'img-ph-mirror.nosdn.127.net')\nprops.data.optionList?.forEach((item) => {\n    item.content = item.content.replaceAll(/img\\d\\.ph\\.126\\.net/g, 'img-ph-mirror.nosdn.127.net')\n})\n</script>\n\n<template>\n    <div class=\"question-card-header\" @click=\"setAnswer\">\n        <span class=\"question-card__seq\">{{ seq }}</span>\n        <div class=\"question-card__title\" v-html=\"data.title\"></div>\n    </div>\n    <div class=\"question-card-body\">\n        <ElCheckboxGroup v-model=\"checkedList\" class=\"checkbox-group\">\n            <ElCheckbox\n                class=\"checkbox-item\"\n                :class=\"{ 'is-selected': inCheckedList(option.id) }\"\n                v-for=\"option in data.optionList\"\n                :label=\"option.id\"\n                size=\"large\"\n                style=\"margin: 0;\"\n                :style=\"{ 'background-color': checkedColor(option) }\"\n                ><span class=\"checkbox-item__content\" v-html=\"option.content\"></span\n            ></ElCheckbox>\n        </ElCheckboxGroup>\n    </div>\n</template>\n\n<style scoped>\n.checkbox-group {\n    display: flex;\n    flex-direction: column;\n    align-items: stretch;\n}\n.checkbox-item {\n    padding: 8px 8px;\n    height: auto !important;\n}\n.checkbox-item:not(.is-selected):hover {\n    background-color: var(--el-color-primary-light-8) !important;\n}\n</style>\n"
  },
  {
    "path": "src/components/question/OnlineJudge.vue",
    "content": "<script setup lang=\"ts\">\nimport type { homework } from \"@/type/mooc\"\nimport { computed } from \"vue\"\n\nconst props = defineProps<{\n    data: homework\n    seq: string\n}>()\nconst timeLimit = computed(() => `${props.data.memoryLimit! / 1000}s`)\nconst memoryLimit = computed(() => {\n    let memValue = props.data.memoryLimit! / 1024\n    return memValue < 1024 ? `${memValue}KB` : `${memValue / 1024}MB`\n})\nprops.data.title = props.data.title.replaceAll(/img\\d\\.ph\\.126\\.net/g, 'img-ph-mirror.nosdn.127.net')\nprops.data.description = <string>props.data.description?.replaceAll(/img\\d\\.ph\\.126\\.net/g, 'img-ph-mirror.nosdn.127.net')\n</script>\n\n<template>\n    <div class=\"question-card-header\">\n        <span class=\"question-card__seq\">{{ seq }}</span>\n        <div class=\"question-card__title\" v-html=\"data.title\"></div>\n    </div>\n    <div class=\"question-card-body\">\n        <div>\n            <ElTag>描述</ElTag>\n            <span class=\"question-desc\" v-html=\"data.description\"></span>\n        </div>\n        <ElRow>\n            <ElCol :span=\"12\"\n                ><ElTag type=\"danger\">时间限制</ElTag><span>{{ timeLimit }}</span></ElCol\n            >\n            <ElCol :span=\"12\"\n                ><ElTag type=\"danger\">内存限制</ElTag><span>{{ memoryLimit }}</span></ElCol\n            >\n        </ElRow>\n    </div>\n</template>\n\n<style scoped>\n</style>\n"
  },
  {
    "path": "src/components/question/SingleChoice.vue",
    "content": "<script setup lang=\"ts\">\nimport type { option, quiz } from \"@/type/mooc\"\nimport { ref } from \"vue\"\n\nconst props = defineProps<{\n    data: quiz\n    seq: string\n}>()\nconst isChecked = ref(0)\nconst answer = (() => {\n    for (const item of <option[]>props.data.optionList) {\n        if (item.answer) {\n            return item.id\n        }\n    }\n    return 0\n})()\n\nconst checkedColor = (option: option) => {\n    if (option.id !== isChecked.value) {\n        return \"transparent\"\n    }\n    return option.answer ? \"var(--el-color-success-light-8)\" : \"var(--el-color-error-light-8)\"\n}\nconst setAnswer = () => (isChecked.value = answer)\nprops.data.title = props.data.title.replaceAll(/img\\d\\.ph\\.126\\.net/g, 'img-ph-mirror.nosdn.127.net')\nprops.data.optionList?.forEach((item) => {\n    item.content = item.content.replaceAll(/img\\d\\.ph\\.126\\.net/g, 'img-ph-mirror.nosdn.127.net')\n})\n</script>\n\n<template>\n    <div class=\"question-card-header\" @click=\"setAnswer\">\n        <span class=\"question-card__seq\">{{ seq }}</span>\n        <div class=\"question-card__title\" v-html=\"data.title\"></div>\n    </div>\n    <div class=\"question-card-body\">\n        <ElRadioGroup v-model=\"isChecked\" class=\"radio-group\">\n            <ElRadio\n                class=\"radio-item\"\n                :class=\"{ 'is-selected': option.id === isChecked }\"\n                v-for=\"option in data.optionList\"\n                :label=\"option.id\"\n                size=\"large\"\n                style=\"margin: 0\"\n                :style=\"{ 'background-color': checkedColor(option) }\"\n                ><span class=\"radio-item__content\" v-html=\"option.content\"></span\n            ></ElRadio>\n        </ElRadioGroup>\n    </div>\n</template>\n\n<style scoped>\n.radio-group {\n    display: flex;\n    flex-direction: column;\n    align-items: stretch;\n}\n.radio-item {\n    padding: 8px 8px;\n    height: auto !important;\n}\n.radio-item :deep(img) {\n    max-width: 100%;\n}\n.radio-item:not(.is-selected):hover {\n    background-color: var(--el-color-primary-light-8) !important;\n}\n</style>\n"
  },
  {
    "path": "src/components/question/index.ts",
    "content": "import SingleChoice from \"./SingleChoice.vue\"\nimport MultipleChoice from \"./MultipleChoice.vue\"\nimport Completion from \"./Completion.vue\"\nimport Homework from \"./Homework.vue\"\nimport OnlineJudge from \"./OnlineJudge.vue\"\n\nexport { SingleChoice, MultipleChoice, Completion, Homework, OnlineJudge }"
  },
  {
    "path": "src/main.ts",
    "content": "import { createApp } from \"vue\"\nimport ElementPlus from 'element-plus'\nimport router from \"./router\"\nimport App from \"./App.vue\"\nimport * as ElementPlusIconsVue from '@element-plus/icons-vue'\nimport 'element-plus/dist/index.css'\nimport 'element-plus/theme-chalk/dark/css-vars.css'\nimport 'element-plus/theme-chalk/display.css'\n\nconst app = createApp(App)\n\napp.use(ElementPlus)\nfor (const [key, component] of Object.entries(ElementPlusIconsVue)) {\n    app.component(key, component)\n}\n\napp.use(router)\n\napp.mount(\"body\")\n"
  },
  {
    "path": "src/plugins/apiAccess.ts",
    "content": "import type { ApiKeyType, ApiResponseType, ApiRequestType } from \"../type/api\"\nimport type { App } from \"vue\"\nimport { isAxiosError } from \"axios\"\nimport { ElMessage } from \"element-plus\"\nimport apiInfo from \"../type/api\"\nimport axios from \"axios\"\n\nasync function apiAccess<T extends ApiKeyType>(api: T): Promise<ApiResponseType[T]>\nasync function apiAccess<T extends ApiKeyType>(\n    api: T,\n    params: ApiRequestType[T][\"params\"],\n    data: ApiRequestType[T][\"data\"]\n): Promise<ApiResponseType[T]>\n\n/** 函数重载 */\nasync function apiAccess<T extends ApiKeyType>(\n    api: T,\n    params?: ApiRequestType[T][\"params\"],\n    data?: ApiRequestType[T][\"data\"]\n) {\n    /** 错误处理，主要catch 404，调用者不再需要try-catch */\n    try {\n        return await new Promise<ApiResponseType[T]>((resolve, reject) => {\n            /** 查询参数转动态路由参数 */\n            let url = apiInfo[api].url\n            if (params) {\n                for (const [key, val] of Object.entries(params)) {\n                    const reg = RegExp(`(/):${key}(/)?`, \"g\")\n                    if (reg.test(url)) {\n                        url = url.replaceAll(reg, `$1${val}$2`)\n                        Reflect.deleteProperty(params, key)\n                    }\n                }\n            }\n            /** 将对象转为json字符串 */\n            if (data) {\n                for (const [key, val] of Object.entries(data)) {\n                    if (typeof val === \"object\") {\n                        Reflect.set(data, key, JSON.stringify(val))\n                    }\n                }\n            }\n            /** 异步发送请求 */\n            let urlPrefix = \"https://ginnnnnn.top/api\"\n            axios<ApiResponseType[T]>({\n                url: urlPrefix + url,\n                method: apiInfo[api].method,\n                params: params || {},\n                data: data || {},\n                headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" }\n            })\n                .then((res) => {\n                    let message = \"\",\n                        success = false\n                    if (res.status !== 200 || !res.data) {\n                        message = \"请求出错\"\n                    } else if (res.data.msg) {\n                        message = res.data.msg\n                        if (res.data.status === 200) {\n                            success = true\n                        }\n                    }\n                    if (message) {\n                        ElMessage({ message, type: success ? \"success\" : \"error\" })\n                    }\n                    resolve(res.data)\n                })\n                .catch((error) => {\n                    let message = error\n                    if (isAxiosError(error)) {\n                        message = error.message\n                    }\n                    ElMessage({ message, type: \"error\" })\n                    reject(error)\n                })\n        })\n    } catch {\n        return {}\n    }\n}\n\nexport const useApiAccess = () => apiAccess\n\nexport default {\n    install: (app: App) => {\n        app.config.globalProperties.$apiAccess = apiAccess\n    }\n}\n"
  },
  {
    "path": "src/plugins/tool.ts",
    "content": "const sleep = async (ms: number) => {\n    return new Promise((resolve) => {\n        setTimeout(() => {\n            resolve('');\n        }, ms)\n    })\n}\n\nexport { sleep } "
  },
  {
    "path": "src/router/index.ts",
    "content": "import { createRouter, createWebHistory } from \"vue-router\"\nimport { HomeView, MoocView, MoocHeader, MoocCourseDetail, MoocTest, VideoView } from \"@/views\"\nimport { ElMessage } from \"element-plus\"\n\nconst router = createRouter({\n    history: createWebHistory(import.meta.env.BASE_URL),\n    routes: [\n        {\n            path: \"/\",\n            name: \"Home\",\n            meta: { title: \"Gins\" },\n            component: HomeView\n        },\n        {\n            path: \"/ginnnnnn/video\",\n            name: \"Video\",\n            meta: { title: \"GinsVideo\" },\n            component: VideoView\n        },\n        {\n            path: \"/mooc\",\n            name: \"Mooc\",\n            components: {\n                default: MoocView,\n                header: MoocHeader\n            },\n            meta: { title: \"GinsMooc\" },\n            children: [\n                { path: \"course/:cid\", name: \"MoocCourse\", component: MoocCourseDetail, props: true },\n                { path: \"test/:tid\", name: \"MoocTest\", component: MoocTest, props: true }\n            ]\n        }\n    ]\n})\n\nrouter.beforeEach((to, from) => {\n    if (!to.name) {\n        ElMessage.error(\"路由错误\")\n        return { name: \"Home\" }\n    }\n    if (to.meta.title) {\n        document.title = <string>to.meta.title\n    }\n    return true\n})\n\nexport default router\n\nconst checkMoocItemId = (id: string) => /[0-9]/.test(id)"
  },
  {
    "path": "src/type/api.ts",
    "content": "import type { course, homework, notice, quiz, test } from \"./mooc\"\n\ntype RequestType = {\n    params?: Object\n    data?: Object\n}\n\ntype Response<T = any> = {\n    status: number\n    data: T\n    msg: string\n}\n\nexport interface courseList extends Object {\n    courseList: course[]\n    totalPages: number\n    currentPage: number\n}\n\nexport interface courseDetail extends Object {\n    course: course\n    testList: test[]\n}\n\nexport interface testDetail extends Object {\n    course: course\n    test: test\n    questionList: quiz[] | homework[]\n    totalPages: number\n}\n\nconst apiInfo = {\n    getCourseList: {\n        url: \"/mooc/course\",\n        method: \"GET\"\n    },\n    getCourseDetail: {\n        url: \"/mooc/course/:cid\",\n        method: \"GET\"\n    },\n    getTestDetail: {\n        url: \"/mooc/test/:tid\",\n        method: \"GET\"\n    },\n    getNotice: {\n        url: \"/mooc/notice/website\",\n        method: \"GET\"\n    }\n}\n\nexport type ApiKeyType = keyof typeof apiInfo\n\nexport interface ApiResponseType {\n    getCourseList: Response<courseList>\n    getCourseDetail: Response<courseDetail>\n    getTestDetail: Response<testDetail>\n    getNotice: Response<notice>\n}\n\nexport interface ApiRequestType {\n    getCourseList: RequestType & {\n        params: {\n            page?: number | string\n            search?: string\n            cid?: number | string\n        }\n    }\n    getCourseDetail: RequestType & {\n        params: { cid: number | string }\n    }\n    getTestDetail: RequestType & {\n        params: {\n            tid: number | string\n            page: number | string\n            search?: string\n        }\n    }\n    getNotice: RequestType & {\n        params: { version: string }\n    }\n}\n\nexport default apiInfo\n"
  },
  {
    "path": "src/type/globleProperties.ts",
    "content": "import { useApiAccess } from \"@/plugins/apiAccess\"\n\nconst apiAccess = useApiAccess()\ndeclare module 'vue' {\n    interface ComponentCustomProperties {\n        $apiAccess: typeof apiAccess\n    }\n}"
  },
  {
    "path": "src/type/mooc.ts",
    "content": "export enum QuestionTypeEnumList {\n    SingleChoice = \"SINGLE_CHOICE\",\n    MultipleChoice = \"MULTIPLE_CHOICE\",\n    Completion = \"COMPLETION\",\n    Judge = \"JUDGEMENT\",\n    Homework = \"HOMEWORK\",\n    OnlineJudge = \"ONLINE_JUDGE\"\n}\n\ninterface course extends Object {\n    id: number\n    name: string\n    school: string\n    imageUrl: string\n}\n\ninterface test extends Object {\n    id: number\n    name: string\n    objective: boolean\n    releaseTime: string\n    deadline: string\n    chapterId: number\n    chapterName: string\n}\n\ninterface option extends Object {\n    id: number\n    content: string\n    answer: boolean\n}\n\ninterface quiz extends Object {\n    id: number\n    type:\n        | QuestionTypeEnumList.SingleChoice\n        | QuestionTypeEnumList.MultipleChoice\n        | QuestionTypeEnumList.Completion\n        | QuestionTypeEnumList.Judge\n    title: string\n    stdAnswer: string | null\n    optionList: option[] | null\n}\n\ninterface homework extends Object {\n    id: number\n    type: QuestionTypeEnumList.Homework | QuestionTypeEnumList.OnlineJudge\n    title: string\n    answer: string | null\n    description: string | null\n    memoryLimit: number | null\n    timeLimit: number | null\n}\n\ninterface notice extends Object {\n    id: number\n    content: string\n}\n\nexport type QuestionTypeEnum = typeof QuestionTypeEnumList\nexport type { course, test, option, quiz, homework, notice }\n"
  },
  {
    "path": "src/views/BlogView.vue",
    "content": "<template>\n    <iframe class=\"background\" src=\"background.html\" allowtransparency=\"true\"></iframe>\n</template>\n\n<style scoped>\n.background {\n    width: 100vw;\n    height: 100%;\n    border: none;\n    background-color: transparent;\n}\n</style>"
  },
  {
    "path": "src/views/HomeView.vue",
    "content": "<script setup lang=\"ts\">\nimport BlogView from \"@/views/BlogView.vue\"\nimport { ref } from \"vue\"\n\nconst disabled = ref(true)\nconst c = () => {\n    if (disabled.value === false) {\n        console.log(\"click\")\n    }\n}\n</script>\n\n<template>\n    <div class=\"home\">\n        <div class=\"home-loading\" v-loading=\"true\" element-loading-text=\"Loading...\"></div>\n        <BlogView></BlogView>\n        <div class=\"home-footer\">\n            <ElLink href=\"https://beian.miit.gov.cn\" :underline=\"false\" target=\"_blank\" style=\"font-size: 12px\">\n                粤ICP备2022090073号</ElLink>\n            <span class=\"copyright\">©2022-2024 by gin</span>\n        </div>\n    </div>\n</template>\n\n<style scoped>\n.home-loading {\n    z-index: -1;\n    position: absolute;\n    height: calc(100vh - 50px);\n    width: 100%;\n}\n\n.home {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n}\n\n.home-footer {\n    z-index: 10;\n    position: absolute;\n    bottom: 0;\n    display: flex;\n    justify-content: center;\n    font-size: 12px;\n    padding-bottom: 12px;\n}\n\n.copyright {\n    margin-left: 16px;\n    display: flex;\n    align-items: center;\n    color: var(--el-text-color-regular);\n}\n</style>\n"
  },
  {
    "path": "src/views/MoocAside.vue",
    "content": "<script setup lang=\"ts\">\nimport type { course } from \"@/type/mooc\"\nimport type { courseList } from \"@/type/api\"\nimport { ref, watch } from \"vue\"\nimport { useApiAccess } from \"@/plugins/apiAccess\"\nimport { Plus, Search } from \"@element-plus/icons-vue\"\nimport { CourseCard } from \"@/components\"\nimport { sleep } from \"@/plugins/tool\"\nimport { useDark } from \"@vueuse/core\"\nimport router from \"@/router\"\nimport { ElMessage, ElScrollbar } from 'element-plus'\nimport axios from \"axios\"\n\nconst checkExist = async () => {\n    let total_page\n    let data: course[] = (await apiAccess(\"getCourseList\", { page: 0 }, undefined)).data.courseList\n\n}\n\nconst isDark = useDark()\nconst apiAccess = useApiAccess()\nconst props = defineProps<{\n    currentCourse: course\n}>()\nconst drawerVisible = ref(false)\nconst data = ref(<course[]>[])\nconst query = ref(\"\")\nconst refresh = ref(false)\nconst clicked = ref(false)\nconst cidCourseLoading = ref(false)\n\nconst showDrawer = () => (drawerVisible.value = true)\ndefineExpose({ showDrawer })\nconst defaultColor = \"var(--el-color-primary-dark-2)\"\nconst hoverColor = \"var(--el-color-primary)\"\nconst color = [defaultColor, hoverColor]\n\nconst newCourseId = ref(\"\")\nconst newCourseState = ref(-1)\nconst newCourseBtnColor = ref(0)\nconst newCourseDialogVisible = ref(false)\nconst newCourseDialogLocked = ref(false)\n\nconst onNewCourseIdInput = () => (newCourseId.value = newCourseId.value.replace(/[^0-9]/g, \"\"))\nconst onNewCourseClick = async () => {\n    newCourseDialogLocked.value = true\n    const eventSource = new EventSource(`https://ginnnnnn.top/api/mooc/course/refresh/${newCourseId.value}`)\n    eventSource.onmessage = (event) => {\n        console.log(event.data)\n        const state = JSON.parse(event.data)\n        if (state && state.total > 0) {\n            newCourseState.value = Math.round((state.finished / state.total) * 100)\n        }\n        if (newCourseState.value === 100 || state.status !== undefined) {\n            eventSource.close()\n            if (state.msg) {\n                state.status === 200 ? ElMessage.success(state.msg) : ElMessage.info(state.msg);\n            }\n            newCourseDialogLocked.value = false\n            newCourseState.value = -1\n            newCourseId.value = \"\"\n            newCourseDialogVisible.value = false\n            refresh.value = true\n            loadData()\n        }\n    }\n}\nconst onCourseClick = (course: course) => {\n    clicked.value = true\n    router.push({ path: `/mooc/course/${course.id}` })\n    drawerVisible.value = false\n}\n\nwatch(\n    () => query.value,\n    () => {\n        refresh.value = true\n        loadData()\n    }\n)\n\nwatch(\n    () => props.currentCourse?.id,\n    () => {\n        if (!clicked.value) {\n            refresh.value = true\n            loadData(props.currentCourse?.id)\n        }\n    }\n)\n\nlet [currentPage, totalPages] = [[0, -1], 0]\nconst loading = ref(false)\nconst disabled = () => loading.value || currentPage[1] >= totalPages - 1\nconst scrollbarRef1 = ref<InstanceType<typeof ElScrollbar>>()\nconst scrollbarRef2 = ref<InstanceType<typeof ElScrollbar>>()\n\nconst loadData = async (cid?: number, up?: boolean) => {\n    const scrollTop = (value: number) => {\n        scrollbarRef1.value && scrollbarRef1.value.setScrollTop(value)\n        scrollbarRef2.value && scrollbarRef2.value.setScrollTop(value)\n    }\n\n    if (cid) {\n        cidCourseLoading.value = true\n    } else if (refresh.value) {\n        currentPage = [0, -1]\n    }\n    loading.value = true\n    const res = (await apiAccess(\"getCourseList\", cid ? { cid } : { page: up ? --currentPage[0] : ++currentPage[1], search: query.value }, undefined)).data\n    loading.value = false\n    if (!cid && cidCourseLoading.value) {\n        return\n    }\n\n    if (cid) {\n        cidCourseLoading.value = false\n        const order = data.value.findIndex((course) => course.id === cid)\n        order !== -1 && scrollTop(order * 92)\n    } else if (refresh.value) {\n        scrollTop(0)\n    }\n\n    if (refresh.value) {\n        data.value = res.courseList\n        refresh.value = false\n        currentPage = [res.currentPage, res.currentPage]\n    } else if (up) {\n        data.value = res.courseList.concat(data.value)\n        currentPage[0] = res.currentPage\n    } else {\n        data.value = data.value.concat(res.courseList)\n        currentPage[1] = res.currentPage\n    }\n\n    totalPages = res.totalPages\n}\n\nlet courseListAside = document.querySelectorAll('.course-list .el-scrollbar__view')\nconst scrollCallback = (arg: { scrollLeft: number, scrollTop: number }) => {\n    if (courseListAside.length === 0) {\n        courseListAside = document.querySelectorAll('.course-list .el-scrollbar__view')\n    }\n    courseListAside.forEach((value) => {\n        if (!query.value && !loading.value && currentPage[0] > 0 && value.getBoundingClientRect().top > -500) {\n            loadData(undefined, true)\n        }\n    })\n}\n\nloadData(props.currentCourse?.id)\n</script>\n\n<template>\n    <ElContainer>\n        <ElMain class=\"aside-main\">\n            <ElScrollbar v-if=\"data\" class=\"course-list\" ref=\"scrollbarRef1\" :noresize=\"true\" @scroll=\"scrollCallback\">\n                <div v-infinite-scroll=\"loadData\" :infinite-scroll-disabled=\"disabled()\" :infinite-scroll-distance=\"500\"\n                    :infinite-scroll-immediate=\"false\">\n                    <CourseCard v-for=\"course in data\" :key=\"course.id\" :course=\"course\"\n                        :class=\"{ 'is-selected': currentCourse && course.id === currentCourse.id }\"\n                        @click=\"onCourseClick(course)\"></CourseCard>\n                </div>\n            </ElScrollbar>\n        </ElMain>\n        <ElFooter class=\"aside-footer\" height=\"50px\">\n            <ElIcon :size=\"20\" @click=\"newCourseDialogVisible = true\" style=\"cursor: pointer\" class=\"new-course-btn\"\n                :color=\"color[newCourseBtnColor % 2]\" @mouseenter=\"newCourseBtnColor++\" @mouseleave=\"newCourseBtnColor++\">\n                <Plus />\n            </ElIcon>\n            <ElInput v-model=\"query\" :suffix-icon=\"Search\" placeholder=\"按课程名或学校搜索\"></ElInput>\n        </ElFooter>\n    </ElContainer>\n    <ElDrawer v-model=\"drawerVisible\" :show-close=\"false\" title=\"课程列表\" :lock-scroll=\"false\" direction=\"ltr\"\n        class=\"aside-drawer\" :append-to-body=\"true\">\n        <ElScrollbar v-if=\"data\" class=\"course-list\" ref=\"scrollbarRef2\" @scroll=\"scrollCallback\">\n            <div v-infinite-scroll=\"loadData\" :infinite-scroll-disabled=\"disabled()\" :infinite-scroll-distance=\"500\"\n                :infinite-scroll-immediate=\"false\">\n                <CourseCard v-for=\"course in data\" :key=\"course.id\" :course=\"course\"\n                    :class=\"{ 'is-selected': currentCourse && course.id === currentCourse.id }\"\n                    @click=\"onCourseClick(course)\"></CourseCard>\n            </div>\n        </ElScrollbar>\n        <template #footer>\n            <ElIcon :size=\"20\" @click=\"newCourseDialogVisible = true\" style=\"cursor: pointer\" class=\"new-course-btn\"\n                :color=\"color[newCourseBtnColor % 2]\" @mouseenter=\"newCourseBtnColor++\" @mouseleave=\"newCourseBtnColor++\">\n                <Plus />\n            </ElIcon>\n            <ElInput v-model=\"query\" :suffix-icon=\"Search\" placeholder=\"按课程名或学校搜索\"></ElInput>\n        </template>\n    </ElDrawer>\n    <ElDialog v-model=\"newCourseDialogVisible\" :close-on-click-modal=\"!newCourseDialogLocked\"\n        :close-on-press-escape=\"!newCourseDialogLocked\" :show-close=\"!newCourseDialogLocked\" :append-to-body=\"true\">\n        <template #header><span>添加新课程</span></template>\n        <ElRow :gutter=\"16\">\n            <ElCol :span=\"6\">\n                <ElInput v-model=\"newCourseId\" placeholder=\"在此输入课程id\" maxlength=\"10\" @input=\"onNewCourseIdInput\"\n                    :disabled=\"newCourseDialogLocked\"></ElInput>\n            </ElCol>\n            <ElCol :span=\"2\">\n                <ElButton type=\"primary\" @click=\"onNewCourseClick\" :disabled=\"newCourseDialogLocked\">确认</ElButton>\n            </ElCol>\n            <ElCol :span=\"16\" v-if=\"newCourseDialogLocked\">\n                <ElProgress :text-inside=\"true\" :stroke-width=\"32\" :percentage=\"newCourseState\" status=\"success\">\n                </ElProgress>\n            </ElCol>\n        </ElRow>\n        <ElImage src=\"/new-course-help.png\" style=\"margin-top: 8px; box-shadow: var(--el-box-shadow-light)\"></ElImage>\n    </ElDialog>\n</template>\n\n<style scoped>\n.aside-header {\n    color: var(--el-text-color-primary);\n    padding: 0;\n}\n\n.aside-main {\n    padding: 16px 0 16px 16px;\n    overflow: hidden;\n}\n\n.course-list {\n    padding-right: 16px;\n}\n\n.aside-header-img {\n    width: 355px;\n    cursor: pointer;\n}\n\n.aside-footer {\n    display: flex;\n    align-items: center;\n    padding: 15px 16px;\n    border-top: 1px solid var(--el-border-color-light);\n}\n\n.new-course-btn {\n    margin-right: 16px !important;\n}\n</style>\n\n<style>\n.course-card.is-selected {\n    position: sticky;\n    top: 0;\n    bottom: 0;\n}\n\n.aside-drawer {\n    width: 355px !important;\n}\n\n.aside-drawer .el-drawer__body {\n    padding: 16px;\n}\n\n.aside-drawer .el-drawer__footer {\n    padding: 15px 16px;\n    display: flex;\n    align-items: center;\n}\n</style>\n"
  },
  {
    "path": "src/views/MoocCourseDetail.vue",
    "content": "<script setup lang=\"ts\">\nimport type { course, test } from \"@/type/mooc\"\nimport { watch, ref } from \"vue\"\nimport { useRoute, useRouter } from \"vue-router\"\nimport { useApiAccess } from \"@/plugins/apiAccess\"\nimport { ElMessage } from \"element-plus\";\n\nconst props = defineProps<{\n    cid: number | string\n    updateCurrentCourse: Function\n    updateCurrentTest: Function\n}>()\n\nconst [apiAccess, route, router] = [useApiAccess(), useRoute(), useRouter()]\nconst data = ref(<test[][]>new Array())\n\nconst courseDetailSetup = async (cid: number | string) => {\n    if (!/[0-9]+/g.test(String(cid))) {\n        ElMessage.error(\"id错误\")\n        router.replace({ name: \"Home\" })\n    }\n    const res = (await apiAccess(\"getCourseDetail\", { cid }, undefined)).data\n    const [testList, course] = [res.testList, res.course]\n    props.updateCurrentCourse(course)\n    props.updateCurrentTest(null)\n    const len = testList.length\n    let chapterIndex = -1\n    data.value.length = 0\n    for (let i = 0; i < len; i++) {\n        if (i === 0 || testList[i].chapterId !== testList[i - 1].chapterId) {\n            chapterIndex++\n            data.value.push(new Array())\n        }\n        data.value[chapterIndex].push(testList[i])\n    }\n}\n\ncourseDetailSetup(props.cid)\n\nwatch(\n    () => route.params,\n    async (toParams) => {\n        if (toParams[\"cid\"]) {\n            courseDetailSetup(<string>toParams[\"cid\"])\n        }\n    }\n)\n\nconst onTestClick = (test: test) => router.push({ path: `/mooc/test/${test.id}` })\nconst sizeCount = () => window.innerWidth < 768 ? 'small' : 'default'\n</script>\n\n<template>\n    <ElScrollbar v-if=\"data\" class=\"course-detail\" :always=\"true\">\n        <div class=\"chapter\" v-for=\"chapter in data\">\n            <div class=\"chapter__title\">{{ chapter[0].chapterName }}</div>\n            <div class=\"chapter__detail\">\n                <div class=\"test\" v-for=\"test in chapter\">\n                    <ElLink class=\"test__title\" :underline=\"false\" @click=\"onTestClick(test)\">{{ test.name }}</ElLink>\n                    <br class=\"hidden-sm-and-up\"/>\n                    <ElTag class=\"test__release-time\" type=\"success\" :size=\"sizeCount()\">{{ test.releaseTime }}</ElTag>\n                    <ElTag class=\"test__deadline\" type=\"danger\" :size=\"sizeCount()\">{{ test.deadline }}</ElTag>\n                </div>\n            </div>\n        </div>\n    </ElScrollbar>\n</template>\n\n<style scoped>\n.course-detail {\n    margin: 20px;\n    width: calc(100% - 40px);\n    height: calc(100% - 40px);\n    background-color: var(--el-bg-color);\n    box-shadow: var(--el-box-shadow-light);\n}\n.chapter {\n    max-width: 900px;\n    margin: 40px 0 0 40px;\n    padding-right: 40px;\n    color: var(--el-text-color-primary);\n}\n.chapter:last-child {\n    margin-bottom: 40px;\n}\n.chapter__title {\n    padding-left: 8px;\n    border-bottom: 1px solid var(--el-border-color);\n    font-size: 18px;\n    line-height: 1.5;\n}\n.chapter__detail {\n    padding: 0 20px 0 40px;\n}\n.test {\n    margin-top: 12px;\n    line-height: 28px;\n    vertical-align: middle;\n}\n.test__title {\n    font-size: 16px;\n    margin-bottom: 4px;\n    transition: all 0.2s;\n    line-height: 1.5;\n}\n.test__release-time {\n    margin-left: 20px;\n}\n.test__deadline {\n    margin-left: 12px;\n}\n.test__release-time,\n.test__deadline {\n    transition: all 0s;\n}\n\n@media screen and (max-width: 768px) {\n    .course-detail {\n        margin: 0;\n        width: 100%;\n        height: 100%;\n    }\n    .course-detail\n    .chapter {\n        margin: 16px 0 0 16px;\n        padding-right: 16px;\n    }\n    .chapter__title {\n        font-size: 16px;\n    }\n    .chapter__detail {\n        padding: 0 8px 0 16px;\n    }\n    .test {\n        margin-top: 8px;\n        line-height: 24px;\n    }\n    .test__title {\n        font-size: 14px;\n        margin-bottom: 2px;\n    }\n    .test__release-time {\n        margin-left: 16px;\n    }\n    .test__deadline {\n        margin-left: 8px;\n    }\n    .test__release-time {\n        margin-left: 0;\n    }\n}\n</style>\n"
  },
  {
    "path": "src/views/MoocHeader.vue",
    "content": "<script setup lang=\"ts\">\nimport { ref } from \"vue\"\nimport { Extension } from \"@/components/icon\"\nimport { useApiAccess } from \"@/plugins/apiAccess\"\nimport type { notice } from \"@/type/mooc\"\n\nconst defaultColor = \"var(--el-color-primary-dark-2)\"\nconst hoverColor = \"var(--el-color-primary)\"\nconst color = [defaultColor, hoverColor]\nconst extensionBtnColor = ref(0)\nconst extensionDialogVisible = ref(false)\nconst extensionCollapseValue = ref(\"1\")\n\nconst sizeCount = () => window.innerWidth < 768 ? 20 : 25\n\nconst apiAccess = useApiAccess()\nconst data = ref<notice>()\nconst getNotice = async () => {\n    data.value = (await apiAccess(\"getNotice\", { version: 'v1.0.0' }, undefined)).data\n}\n\ngetNotice()\n</script>\n\n<template>\n    <div class=\"mooc-header\">\n        <div class=\"mooc-notice\"><span class=\"hidden-xs-only\">{{ data?.content }}</span></div>\n        <div class=\"mooc-function\">\n            <ElIcon :color=\"color[extensionBtnColor % 2]\" :size=\"sizeCount()\" @mouseenter=\"extensionBtnColor++\"\n                @mouseleave=\"extensionBtnColor++\" @click=\"extensionDialogVisible = true\" style=\"cursor: pointer;\">\n                <Extension style=\"margin: 0;\" />\n            </ElIcon>\n        </div>\n    </div>\n    <ElDialog v-model=\"extensionDialogVisible\">\n        <template #header>GinsMooc Extension Helper</template>\n        <ElCollapse accordion style=\"line-height: 1.5;\" v-model=\"extensionCollapseValue\">\n            <ElCollapseItem title=\"简介\" name=\"1\">\n                <p>实现对于中国大学MOOC的</p>\n                <ul>\n                    <li>非在线测评题的自动答案查询，包括单选题、多选题、判断题、填空题、简答题，支持测验与作业及考试</li>\n                    <li>互评阶段的自动评分、自动点评</li>\n                </ul>\n                <p>下载地址：<ElLink href=\"/download/GinsMoocExtension_v2.2.1.zip\">\n                        https://ginnnnnn.top/download/GinsMoocExtension_v2.2.1.zip\n                    </ElLink>\n                </p>\n            </ElCollapseItem>\n            <ElCollapseItem title=\"功能介绍\" name=\"2\">\n                <p>在测试的准备页面，将会自动检查是否准备就绪，若为否将自动更新课程</p>\n                <ElImage class=\"extension-image\" src=\"/extension-updating.png\"></ElImage>\n                <p>进入测验后，将显示“获取答案”按钮，点击即可</p>\n                <ElImage class=\"extension-image\" src=\"/extension-single-choice.png\"></ElImage>\n                <ElImage class=\"extension-image\" src=\"/extension-multiple-choice.png\"></ElImage>\n                <ElImage class=\"extension-image\" src=\"/extension-completion.png\"></ElImage>\n                <ElImage class=\"extension-image\" src=\"/extension-homework.png\"></ElImage>\n                <p>作业的互评阶段支持自动评分、自动点评</p>\n                <ElImage class=\"extension-image\" src=\"/extension-auto-evaluate-1.png\"></ElImage>\n                <ElImage class=\"extension-image\" src=\"/extension-auto-evaluate-2.png\"></ElImage>\n            </ElCollapseItem>\n            <ElCollapseItem title=\"安装介绍\" name=\"3\">\n                <p>下载安装包后，将其解压至文件夹内</p>\n                <p>在浏览器地址栏中输入edge://extensions（谷歌浏览器为chrome://extensions）</p>\n                <p>打开开发者模式</p>\n                <ElImage class=\"extension-image\" src=\"/extension-developer-mode.png\"></ElImage>\n                <p>点击“加载解压缩的扩展”，选择刚刚解压到的文件夹，即可开始使用</p>\n                <ElImage class=\"extension-image\" src=\"/extension-load-decompression.png\"></ElImage>\n            </ElCollapseItem>\n        </ElCollapse>\n    </ElDialog>\n</template>\n\n<style scoped>\n.mooc-header {\n    display: flex;\n    align-items: center;\n}\n\n.mooc-notice {\n    flex: 1;\n    text-align: center;\n    color: var(--el-color-primary);\n    font-size: 14px;\n}\n\n.mooc-function {\n    flex: 0 !important;\n    width: min-content;\n    display: flex;\n    align-items: center;\n    justify-content: flex-end;\n    padding-right: 8px;\n}\n\n@media screen and (max-width: 768px) {\n    .mooc-function {\n        padding-right: 0;\n    }\n\n}\n\n.extension-image {\n    box-shadow: var(--el-box-shadow-light);\n    margin: 8px 0;\n}\n</style>\n"
  },
  {
    "path": "src/views/MoocTest.vue",
    "content": "<script setup lang=\"ts\">\nimport { watch, ref } from \"vue\"\nimport { useApiAccess } from \"@/plugins/apiAccess\"\nimport { QuestionCard } from \"@/components\"\nimport { useRoute, useRouter } from \"vue-router\"\nimport type { homework, quiz } from \"@/type/mooc\"\nimport { ElMessage } from \"element-plus\"\n\ninterface question {\n    data: quiz | homework\n    order: number\n}\n\nconst props = defineProps<{\n    tid: number | string\n    updateCurrentTest: Function\n    updateCurrentCourse: Function\n    search: string\n}>()\n\nconst [apiAccess, route, router] = [useApiAccess(), useRoute(), useRouter()]\nconst [left, right, all] = [ref(<question[]>new Array()), ref(<question[]>new Array()), ref(<question[]>new Array())]\nlet [leftLength, rightLength, nextPage, totalPages] = [0, 0, 0, 0]\nconst loading = ref(false)\nconst disabled = () => loading.value || nextPage >= totalPages\n\nconst loadData = async (tid: number | string = props.tid) => {\n    if (!/[0-9]+/g.test(String(tid))) {\n        ElMessage.error(\"id错误\")\n        router.replace({ name: \"Home\" })\n    }\n    loading.value = true\n    const res = (await apiAccess(\"getTestDetail\", { tid: tid, page: nextPage++, search: props.search }, undefined)).data\n    const [course, test, questionList, total] = [res.course, res.test, res.questionList, res.totalPages]\n    if (nextPage === 1) {\n        totalPages = total\n        props.updateCurrentTest(test)\n        props.updateCurrentCourse(course)\n    }\n    questionList.forEach((value, index) => {\n        const order = (nextPage - 1) * 20 + index + 1\n        all.value.push({ data: value, order: order })\n        if (leftLength <= rightLength) {\n            leftLength += value.toString().length\n            left.value.push({ data: value, order: order })\n        } else {\n            rightLength += value.toString().length\n            right.value.push({ data: value, order: order })\n        }\n    })\n    loading.value = false\n}\ndefineExpose({ loadData })\n\nloadData()\n\nwatch(\n    () => route.params,\n    async (toParams) => {\n        if (toParams[\"tid\"]) {\n            loadData(<string>toParams[\"tid\"])\n        }\n    }\n)\nwatch(\n    () => props.search,\n    () => {\n        [leftLength, rightLength, nextPage, totalPages] = [0, 0, 0, 0]\n        left.value = new Array()\n        right.value = new Array()\n        all.value = new Array()\n        loadData()\n    }\n)\n\nvar lastScrollTop = 0;\nvar moocHeader = document.getElementsByClassName('mooc-main-header').item(0) as HTMLElement;\nvar header = document.getElementsByClassName('header').item(0) as HTMLElement;\n\nconst scrollCallback = (arg: {scrollLeft: number, scrollTop: number}) => {\n    if (window.innerWidth >= 768) return\n    if (!moocHeader) moocHeader = document.getElementsByClassName('mooc-main-header').item(0) as HTMLElement\n    if (!header) header = document.getElementsByClassName('header').item(0) as HTMLElement\n    if (arg.scrollTop > lastScrollTop) {\n        moocHeader.style.display = 'none';\n        header.style.display = 'none';\n    } else {\n        moocHeader.style.display = 'flex';\n        header.style.display = 'block';\n    }\n    lastScrollTop = arg.scrollTop;\n}\n</script>\n\n<template>\n    <ElScrollbar class=\"test-detail\" :always=\"true\" @scroll=\"scrollCallback\" :noresize=\"true\">\n        <div v-infinite-scroll=\"loadData\" :infinite-scroll-disabled=\"disabled()\" :infinite-scroll-distance=\"500\">\n            <ElRow :gutter=\"20\" style=\"margin: 0 10px 20px 10px\" class=\"hidden-md-and-down\">\n                <ElCol :span=\"12\">\n                    <QuestionCard v-for=\"question in left\" :data=\"question.data\" :order=\"question.order\"></QuestionCard>\n                </ElCol>\n                <ElCol :span=\"12\">\n                    <QuestionCard\n                        v-for=\"question in right\"\n                        :data=\"question.data\"\n                        :order=\"question.order\"\n                    ></QuestionCard>\n                </ElCol>\n            </ElRow>\n            <ElCol class=\"hidden-lg-and-up question-card-small\">\n                <QuestionCard v-for=\"question in all\" :data=\"question.data\" :order=\"question.order\"></QuestionCard>\n            </ElCol>\n        </div>\n    </ElScrollbar>\n</template>\n\n<style scoped>\n.test-detail {\n    padding-right: 6px;\n}\n\n.question-card-small {\n    margin: 0 20px 20px 20px\n}\n\n@media only screen and (max-width: 768px) {\n    .question-card-small {\n        margin: 0 0;\n    }\n}\n</style>\n"
  },
  {
    "path": "src/views/MoocView.vue",
    "content": "<script setup lang=\"ts\">\nimport { watch, type Ref } from \"vue\"\nimport type { course, test } from \"@/type/mooc\"\nimport { ref, computed } from \"vue\"\nimport { MoocAside } from \"@/views\"\nimport { useRoute } from \"vue-router\"\nimport { Menu, Search, ArrowLeftBold } from \"@element-plus/icons-vue\"\nimport router from \"@/router\"\n\nconst currentCourse: Ref<course> = ref(<course>new Object())\nconst currentTest: Ref<test> = ref(<test>new Object())\nconst route = useRoute()\nconst aside = ref()\nconst quizQuery = ref(\"\")\n\nconst mainHeaderTitle = computed(() => {\n    let ret = \"\"\n    if (currentCourse.value && currentCourse.value.name) {\n        ret += currentCourse.value.name\n    }\n    if (currentTest.value && currentTest.value.name) {\n        ret += ret ? \" - \" : \"\"\n        ret += currentTest.value.name\n    }\n    return ret\n})\nconst emptyContent = computed(() => !(currentCourse.value.id || currentTest.value.id))\n\nconst updateCurrentCourse = (course: course) => (currentCourse.value = course)\nconst updateCurrentTest = (test: test) => (currentTest.value = test)\nconst setAllAnswer = () => {\n    const questions = <HTMLCollection>document.getElementsByClassName(\"question-card-header\")\n    for (const item of questions) {\n        ; (<HTMLElement>item).click()\n    }\n}\nconst onCourseBack = () => router.push({ path: `/mooc/course/${currentCourse.value.id}` })\nconst defaultColor = \"var(--el-color-primary-dark-2)\"\nconst hoverColor = \"var(--el-color-primary)\"\nconst color = [defaultColor, hoverColor]\nconst [menuBtnColor, backBtnColor] = [ref(0), ref(0)]\n\nwatch(\n    () => route.params,\n    (toParams) => {\n        if (toParams[\"cid\"] === undefined && toParams[\"tid\"] === undefined) {\n            updateCurrentCourse(<course>new Object())\n            updateCurrentTest(<test>new Object())\n        }\n        quizQuery.value = \"\"\n    }\n)\n</script>\n\n<template>\n    <ElContainer>\n        <ElAside class=\"mooc-aside\" width=\"355px\" :class=\"emptyContent ? '' : 'hidden-lg-only hidden-sm-and-down'\">\n            <Suspense>\n                <MoocAside :current-course=\"currentCourse\" ref=\"aside\"></MoocAside>\n            </Suspense>\n        </ElAside>\n        <ElMain class=\"mooc-main\">\n            <ElContainer>\n                <ElHeader class=\"mooc-main-header\">\n                    <ElIcon :color=\"color[menuBtnColor % 2]\" :size=\"22\" @click=\"aside?.showDrawer\"\n                        @mouseenter=\"menuBtnColor++\" @mouseleave=\"menuBtnColor++\"\n                        :style=\"emptyContent ? 'display: none;' : ''\" class=\"hidden-xl-only hidden-md-only icon-btn\">\n                        <Menu></Menu>\n                    </ElIcon>\n                    <ElIcon :color=\"color[backBtnColor % 2]\" :size=\"20\" @click=\"onCourseBack\"\n                        v-if=\"currentTest && currentTest.name\" @mouseenter=\"backBtnColor++\" @mouseleave=\"backBtnColor++\"\n                        class=\"icon-btn\">\n                        <ArrowLeftBold />\n                    </ElIcon>\n                    <span style=\"flex: 1\" class=\"mooc-header-title\">\n                        <span :class=\"{ 'hidden-xs-only': currentTest && currentTest.name }\"\n                            v-if=\"currentCourse && currentCourse.name\">{{ currentCourse.name }}</span>\n                        <template v-if=\"currentTest && currentTest.name\">\n                            <span class=\"hidden-xs-only\"> - </span>\n                            <span>{{ currentTest.name }}</span>\n                        </template>\n                    </span>\n                    <template v-if=\"currentTest && currentTest.objective\">\n                        <ElInput v-model=\"quizQuery\" :suffix-icon=\"Search\" placeholder=\"按题目搜索，图片以“【图片】”代替\"\n                            style=\"width: 400px\" class=\"hidden-xs-only\"></ElInput>\n                        <ElButton class=\"display-answer hidden-xs-only\" type=\"primary\" round plain @click=\"setAllAnswer\"\n                            style=\"margin-left: 16px\">显示答案</ElButton>\n                    </template>\n                </ElHeader>\n                <div class=\"hidden-sm-and-up small-function-bar\" v-if=\"currentTest && currentTest.objective\">\n                    <ElInput v-model=\"quizQuery\" :suffix-icon=\"Search\" placeholder=\"按题目搜索，图片以“【图片】”代替\" style=\"width: 400px\"\n                        size=\"small\"></ElInput>\n                    <ElButton class=\"display-answer\" type=\"primary\" round plain @click=\"setAllAnswer\"\n                        style=\"margin-left: 16px\" size=\"small\">显示答案</ElButton>\n                </div>\n                <div class=\"split-line hidden-sm-and-up\"></div>\n                <ElMain class=\"mooc-main-body\" style=\"height: calc(100vh - 100px)\">\n                    <RouterView :update-current-test=\"updateCurrentTest\" :update-current-course=\"updateCurrentCourse\"\n                        :search=\"quizQuery\"></RouterView>\n                </ElMain>\n            </ElContainer>\n        </ElMain>\n    </ElContainer>\n</template>\n\n<style scoped>\n.mooc-aside {\n    border-right: 1px solid var(--el-border-color-light);\n    overflow: hidden;\n    background-color: var(--el-bg-color);\n}\n\n.mooc-main {\n    padding: 0;\n}\n\n.mooc-main-header {\n    height: 50px;\n    display: flex;\n    align-items: center;\n    padding: 0 20px;\n    background-color: var(--el-bg-color);\n    font-weight: 700;\n    font-size: 18px;\n    color: var(--el-text-color-primary);\n}\n\n@media only screen and (max-width: 768px) {\n    .mooc-main-header {\n        height: 40px;\n        padding: 0 16px;\n        font-size: 16px;\n    }\n}\n\n.mooc-header-title {\n    flex: 1;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n}\n\n.small-function-bar {\n    display: flex;\n    align-items: center;\n    padding: 0 16px;\n    background-color: white;\n    height: 42px;\n}\n\n.split-line {\n    border-bottom: 1px solid var(--el-border-color-extra-light);\n    box-shadow: 0 2px 4px rgba(0, 0, 0, .08);\n}\n\n.mooc-main-header .display-answer {\n    transition: all var(--el-transition-duration);\n}\n\n.mooc-main-body {\n    padding: 0;\n    background-color: var(--el-fill-color-light);\n    overflow: hidden;\n}\n\n.icon-btn {\n    cursor: pointer;\n    padding-right: 8px;\n}\n</style>\n"
  },
  {
    "path": "src/views/VideoView.vue",
    "content": "<script setup lang=\"ts\">\nimport { ref, computed } from \"vue\"\nconst apiList = [\n    { name: \"JSONPlayer【有弹幕】\", url: \"https://jx.777jiexi.com/player/?url=\" },\n    { name: \"CKPlayer\", url: \"https://www.ckplayer.vip/jiexi/?url=\" },\n    { name: \"盘古\", url: \"https://www.pangujiexi.cc/jiexi.php?url=\" },\n    { name: \"老板\", url: \"https://vip.laobandq.com/jiexi.php?url=\" },\n    { name: \"OK\", url: \"https://okjx.cc/?url=\" },\n    { name: \"8090\", url: \"https://www.8090.la/8090/?url=\" },\n    { name: \"维多\", url: \"http://jx.ivito.cn/?url=\" },\n    { name: \"CKMOV\", url: \"https://www.ckmov.vip/api.php?url=\" },\n    { name: \"BL解析\", url: \"https://svip.bljiex.cc/?v=\" },\n    { name: \"1717\", url: \"https://www.1717yun.com/1717yun/?url=\" },\n    { name: \"M3U8\", url: \"https://dmjx.m3u8.tv/?url=\" },\n    { name: \"云解析\", url: \"http://jx.ppflv.com/?url=\" },\n    { name: \"虾米解析\", url: \"https://jx.xmflv.com/?url=\" },\n    { name: \"JYPlayer\", url: \"https://jx.playerjy.com/?url=\" }\n]\nconst [apiUrl, videoUrl] = [ref(localStorage.getItem(\"GinsVideo-apiUrl\")), ref(\"\")]\nconst src = computed(() => apiUrl.value + videoUrl.value)\nconst setApiUrl = (url: string) => {\n    localStorage.setItem(\"GinsVideo-apiUrl\", url)\n    apiUrl.value = url\n}\n</script>\n\n<template>\n    <ElContainer>\n        <ElAside class=\"video-aside\" width=\"305px\">\n            <ElScrollbar class=\"api-list\">\n                <div\n                    class=\"api-item\"\n                    :class=\"{ 'is-selected': apiUrl === api.url }\"\n                    v-for=\"api in apiList\"\n                    @click=\"setApiUrl(api.url)\"\n                >\n                    {{ api.name }}\n                </div>\n            </ElScrollbar>\n        </ElAside>\n        <ElMain class=\"video-main\">\n            <ElInput v-model=\"videoUrl\" class=\"video-input\" size=\"large\" placeholder=\"将视频地址复制到这里\"></ElInput>\n            <iframe\n                v-if=\"apiUrl && videoUrl\"\n                :src=\"src\"\n                height=\"675px\"\n                frameborder=\"0\"\n                allowfullscreen=\"true\"\n            ></iframe>\n            <ElEmpty v-else class=\"video-mask\" description=\"您还未选择接口或输入视频地址\"></ElEmpty>\n            <div class=\"disclaimers\">\n                <span>本系统只为内部交流学习，不以盈利为目的</span>\n                <span>所有资源均来源第三方资源，并不提供影片资源存储，录制、上传相关视频等，视频版权归属其合法持有人所有，本站不对使用者的行为负担任何法律责任</span>\n                <span>如果有因为本站而导致您的权益受到损害，请与我们联系，我们将理性对待，协助你解决相关问题</span>\n            </div>\n        </ElMain>\n    </ElContainer>\n</template>\n\n<style scoped>\n.video-aside {\n    border-right: 1px solid var(--el-border-color-light);\n    overflow: hidden;\n    background-color: var(--el-bg-color);\n    padding: 8px;\n}\n.video-main {\n    display: flex;\n    flex-direction: column;\n    background-color: var(--el-bg-color);\n}\n.video-input {\n    margin-bottom: 20px;\n}\n.video-mask {\n    padding: 0;\n    height: 675px;\n    background-color: var(--el-empty-fill-color-3);\n}\n.api-item {\n    padding: 8px 16px;\n    margin-bottom: 8px;\n    font-size: 16px;\n    line-height: 1.5;\n    border-radius: 8px;\n    background-color: var(--el-color-primary-light-8);\n    color: var(--el-menu-text-color);\n    transition: all 0.2s;\n    cursor: pointer;\n}\n.api-item:not(.is-selected):hover,\n.api-item.is-selected {\n    background-color: var(--el-color-primary-light-5);\n}\n.api-item:active {\n    transform: scale(0.98);\n}\n.disclaimers {\n    flex: 1;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: flex-end;\n    margin-top: 20px;\n    line-height: 1.5;\n    font-size: 12px;\n    color: var(--el-text-color-secondary);\n}\n</style>\n"
  },
  {
    "path": "src/views/index.ts",
    "content": "import HomeView from \"./HomeView.vue\"\nimport MoocView from \"./MoocView.vue\"\nimport MoocHeader from \"./MoocHeader.vue\"\nimport MoocAside from \"./MoocAside.vue\"\nimport MoocCourseDetail from \"./MoocCourseDetail.vue\"\nimport MoocTest from \"./MoocTest.vue\"\nimport VideoView from \"./VideoView.vue\"\n\nexport { HomeView, MoocView, MoocHeader, MoocAside, MoocCourseDetail, MoocTest, VideoView }"
  },
  {
    "path": "tsconfig.config.json",
    "content": "{\n    \"extends\": \"@vue/tsconfig/tsconfig.node.json\",\n    \"include\": [\n        \"vite.config.*\",\n        \"vitest.config.*\",\n        \"cypress.config.*\",\n        \"playwright.config.*\"\n    ],\n    \"compilerOptions\": {\n        \"composite\": true,\n        \"types\": [\n            \"node\"\n        ]\n    }\n}"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n    \"extends\": \"@vue/tsconfig/tsconfig.web.json\",\n    \"include\": [\n        \"env.d.ts\",\n        \"src/**/*\",\n        \"src/**/*.vue\"\n    ],\n    \"compilerOptions\": {\n        \"baseUrl\": \".\",\n        \"paths\": {\n            \"@/*\": [\n                \"./src/*\"\n            ]\n        },\n        \"types\": [\"element-plus/global\"]\n    },\n    \"references\": [\n        {\n            \"path\": \"./tsconfig.config.json\"\n        }\n    ]\n}"
  },
  {
    "path": "vite.config.ts",
    "content": "import { fileURLToPath, URL } from \"node:url\"\n\nimport { defineConfig } from \"vite\"\nimport vue from \"@vitejs/plugin-vue\"\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n    plugins: [ vue() ],\n    resolve: {\n        alias: {\n            \"@\": fileURLToPath(new URL(\"./src\", import.meta.url))\n        }\n    }\n})\n"
  }
]