├── .gitignore
├── .prettierrc
├── HostsSwitch.jpg
├── README.md
├── app.ico
├── default.aproj
├── dist
└── HostsSwitch.exe
├── lib
└── hosts.aardio
├── main.aardio
├── web
├── addition.75f59799.svg
├── download.779c3df5.svg
├── edit.e5e6ea37.svg
├── favicon.da3735d5.png
├── home.3a7af05d.svg
├── index.aardio
├── lock.623ff62a.svg
├── main.2a31e670.js
├── main.a46e3e18.css
├── record.56e10638.svg
├── recordFind.08a261bc.svg
├── refresh.0b748f9a.svg
├── save.8ed52c9e.svg
├── subtraction.600c820b.svg
└── unlock.6ef52b3f.svg
└── web_source
├── .gitignore
├── .prettierrc
├── package.json
├── src
├── asset
│ ├── addition.svg
│ ├── download.svg
│ ├── edit.svg
│ ├── favicon.png
│ ├── flag.svg
│ ├── home.svg
│ ├── interpretation.svg
│ ├── link.svg
│ ├── lock.svg
│ ├── menu.svg
│ ├── record.svg
│ ├── recordFind.svg
│ ├── refresh.svg
│ ├── save.svg
│ ├── subtraction.svg
│ └── unlock.svg
├── index.html
├── js
│ ├── CodeMirror
│ │ ├── hosts.css
│ │ ├── index.ts
│ │ └── modeHosts.js
│ ├── bus.ts
│ ├── contextMenu.ts
│ ├── dataAction
│ │ ├── DATA.d.ts
│ │ ├── DATA.ts
│ │ ├── defaultConfig.ts
│ │ ├── index.ts
│ │ └── render.ts
│ ├── dialog.ts
│ ├── init.ts
│ ├── main.ts
│ ├── textarea.ts
│ ├── types
│ │ └── aardio.d.ts
│ └── util.ts
├── lib
│ └── template.js
└── styles
│ ├── base.less
│ ├── index.less
│ └── ui.less
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 | .build
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 200,
3 | "tabWidth": 2,
4 | "semi": true,
5 | "useTabs": false,
6 | "singleQuote": true,
7 | "bracketSpacing": true
8 | }
--------------------------------------------------------------------------------
/HostsSwitch.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lnncoco/hosts-switch/bc6dfc271831091629d6bc30f4e2b651a6409157/HostsSwitch.jpg
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HostsSwitch
2 |
3 | 
4 |
5 | ## 介绍
6 |
7 | 这是一个为了练手做的小工具。看见`aardio`官方有一个修改版的[`hostsSwitchHelper`](https://github.com/aardio/hostsSwitchHelper),于是学习了一下实现。
8 | 自己重头设计实现了界面和逻辑,逻辑都在前端,只有涉及保存文件之类放到`aardio`里。
9 | 前端使用`LuLu UI`+`LESS`+`TypeScript`实现。
10 |
11 | 功能与`SwitchHosts!`类似,就是方便修改系统 Hosts 信息的小工具。与之不同的是,每个项都是一份独立的 Host 信息,可以自己手动输入,也可以设置`url`参数,方便网络`Hosts`更新(默认会走系统代理设置)。侧边`Switch开关`开启时会自动将所有开启的数据合并,然后存入系统的`Hosts`文件中。
12 |
13 | ### 目录
14 |
15 | `dist`:存放一份编译后的`exe`应用程序
16 | `lib`:`aardio`的用户库文件存放目录
17 | `web`:前端编译后的文件,`aardio`打包使用的文件
18 | `web_source`:前端项目源码。使用`parcel`工具
19 |
20 | ### 修改生成
21 |
22 | - 在`web_source`目录中执行 `npm i` 安装依赖
23 | - 安装完成后,执行 `npm run dev` 启动项目,然后在`aardio`中同时启动,此时`aardio`的页面会访问本地的前端项目,可以实时修改查看(不知道是`aardio`不兼容`parcel`还是本来就是如此,在浏览器直接访问`aardio`的依赖会报错导致无法正常显示程序)。要确保 `6060` 端口未被占用,否则需要修改响应代码
24 | - 修改完成后使用 `npm run build` 编译,程序会自动生成到父级的`web`目录下
25 |
--------------------------------------------------------------------------------
/app.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lnncoco/hosts-switch/bc6dfc271831091629d6bc30f4e2b651a6409157/app.ico
--------------------------------------------------------------------------------
/default.aproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/dist/HostsSwitch.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lnncoco/hosts-switch/bc6dfc271831091629d6bc30f4e2b651a6409157/dist/HostsSwitch.exe
--------------------------------------------------------------------------------
/lib/hosts.aardio:
--------------------------------------------------------------------------------
1 | namespace hosts;
2 |
3 | import fsys;
4 | import fsys.hosts;
5 | import fsys.table;
6 | import inet.http;
7 |
8 |
9 | fsys.hosts.ownCacls(); // hosts夺权,修正一些系统hosts无法编辑
10 |
11 | path = "~/";
12 | backupFileName = 'system.bak';
13 |
14 | // 配置文件
15 | config = fsys.table("~/config.hosts");
16 | config.load();
17 |
18 | // 获取完整保存文件路径
19 | getSavePath = function(name){
20 | if(!name) return null,false,'请输入文件名';
21 | var pathDir = ..io.fullpath(path);
22 | return pathDir ++ name,true,"";
23 | }
24 |
25 | // 判断文件是否存在
26 | isExistFile = function(name){
27 | return !!..io.exist(path++name);
28 | }
29 |
30 | /**
31 | * 获取系统Hosts
32 | **/
33 | getSystemHosts = function(){
34 | return fsys.hosts.loadText();
35 | }
36 |
37 | /**
38 | * 保存到系统Hosts
39 | * @param {string} content
40 | **/
41 | saveSystemHosts = function(content){
42 | return ..fsys.hosts.saveText(content);
43 | }
44 |
45 | /**
46 | * 刷新DNS缓存
47 | **/
48 | clearDnsCache = function(){
49 | fsys.hosts.flushDns();
50 | return true;
51 | }
52 |
53 | /**
54 | * 发送请求获取网络Hosts
55 | * @param {string} url
56 | * @param {Object} proxy
57 | **/
58 | requestHosts = function(url, proxy){
59 | if(!proxy) proxy = {}
60 | var http = inet.http(proxy.ua, proxy.host);
61 | if(proxy.user && proxy.password) http.setProxyAuth(proxy.user, proxy.password);
62 | var html,err,errCode = http.get(url);
63 | if(html) return { code = 0, message = 'success', html = html }
64 | else {
65 | if(http.statusCode) return { code = http.statusCode, message = http.lastResponse() }
66 | else return { code = errCode || 9, message = err }
67 | }
68 | }
69 |
70 | /**
71 | * 备份系统文件
72 | * @param {boolean} coverage 是否强制覆盖
73 | **/
74 | backupLocalHosts = function(coverage = false){
75 | var path,status,errMsg = getSavePath(backupFileName)
76 | if(!status) return status
77 | if(isExistFile(backupFileName) && !coverage) return status;
78 | return ..string.save(path, getSystemHosts());
79 | }
80 |
81 | /**
82 | * 获取所有数据
83 | * @param {table} data 所有数据
84 | **/
85 | getConfigData = function(){
86 | return config;
87 | }
88 |
89 | /**
90 | * 保存所有数据
91 | * @param {table} data 所有数据
92 | **/
93 | saveConfigData = function(data){
94 | config.main = data;
95 | config.time = ..time.now();
96 | return config.save();
97 | }
98 |
99 |
100 | /**intellisense(hosts)
101 | clearDnsCache() = 刷新DNS缓存
102 | requestHosts() = 从url获取Hosts信息
103 | getSystemHosts() = 获取系统的Hosts信息
104 | saveSystemHosts() = 保存到系统的Hosts
105 | backupLocalHosts() = 备份系统文件
106 | getConfigData() = 获取所有数据
107 | saveConfigData() = 保存所有数据
108 | end intellisense**/
109 |
--------------------------------------------------------------------------------
/main.aardio:
--------------------------------------------------------------------------------
1 | import fsys.hosts;
2 | import chrome.app;
3 | import hosts;
4 |
5 | var theApp = chrome.app();
6 |
7 | /*
8 | external定义的对象可以通过aardio进行调用
9 | */
10 | theApp.external = {
11 |
12 | //获取本地hosts
13 | getSystemHosts = hosts.getSystemHosts;
14 |
15 | //获取本地hosts
16 | saveSystemHosts = hosts.saveSystemHosts;
17 |
18 | //手动刷新dns缓存
19 | clearDnsCache = hosts.clearDnsCache;
20 |
21 | // 发送请求获取网络HOSTS
22 | requestHosts = hosts.requestHosts;
23 |
24 | // 备份系统文件
25 | backupLocalHosts = hosts.backupLocalHosts;
26 |
27 | // 获取配置信息
28 | getConfigData = hosts.getConfigData;
29 |
30 | // 保存配置信息
31 | saveConfigData = hosts.saveConfigData;
32 |
33 | }
34 |
35 | /*
36 | // 加载一个页面完成会触发这个事件(要求引用了"/aardio.js")
37 | theApp.onUrlReady = function($,url){
38 |
39 | }
40 |
41 | // 窗口关闭时触发
42 | theApp.onQuit = function($,url){
43 |
44 | }
45 | */
46 |
47 | //WebSocke/RPC中遇到的错误都会触发这个函数,可以在这里自定义错误处理方式
48 | theApp.ws.onError = function(hSocket,err){
49 | errput(err, "chrome/rpc error");//当然也可以在 global.onError 里自定义全部的错误信息怎么显示
50 | }
51 |
52 | var url = "/web/index.aardio"
53 |
54 | // 调试模式
55 | if(_STUDIO_INVOKED){
56 | import web.rest.client;
57 | if( web.rest.client().api("http://localhost:6060/",,"HostsSwitch").get() ){
58 | url = "http://localhost:6060/";
59 | }
60 | }
61 |
62 | // 窗口大小
63 | theApp.indexReady(
64 | function(){
65 | //theApp.setPos(,,820,430)
66 | }
67 | )
68 |
69 | // 正式的启动chrome进程,aardio会自动把下面的文件转换为服务端请求
70 | theApp.start(url);
71 |
72 | // 网页中可以调用 aardio.quit() 退出,也可以直接关闭chrome窗口退出
73 | win.loopMessage();
74 |
--------------------------------------------------------------------------------
/web/addition.75f59799.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/download.779c3df5.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/edit.e5e6ea37.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/favicon.da3735d5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lnncoco/hosts-switch/bc6dfc271831091629d6bc30f4e2b651a6409157/web/favicon.da3735d5.png
--------------------------------------------------------------------------------
/web/home.3a7af05d.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/index.aardio:
--------------------------------------------------------------------------------
1 |
HostsSwitch
--------------------------------------------------------------------------------
/web/lock.623ff62a.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/main.a46e3e18.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none}@charset "UTF-8";.ui-button{display:inline-block;line-height:20px;font-size:14px;text-align:center;color:#4c5161;border-radius:4px;border:1px solid #d0d0d5;padding:9px 15px;min-width:80px;background-color:#fff;background-repeat:no-repeat;background-position:50%;text-decoration:none;box-sizing:border-box;transition:border-color .15s,box-shadow .15s,opacity .15s;font-family:inherit;cursor:pointer;overflow:visible}.ui-button[width="100%"]{width:100%}div.ui-button{display:block}[type=submit]:not([class]){position:absolute;clip:rect(0 0 0 0)}@supports (-webkit-mask:none){[tabindex],[type=button],[type=submit],button{outline:0 none}}::-moz-focus-inner{border:0}button.ui-button,input.ui-button{height:40px}.ui-button:hover{color:#4c5161;text-decoration:none}.ui-button:not(.disabled):not(.loading):not(:disabled):hover{border-color:#ababaf;box-shadow:inset 0 1px 2px rgba(0,0,0,.01),inset 0 0 0 100px rgba(0,0,0,.05)}.ui-button:not(.disabled):not(.loading):not(:disabled):active{box-shadow:inset 0 1px 2px rgba(0,0,0,.1),inset 0 0 0 100px rgba(0,0,0,.1)}.ui-button[data-type=danger],.ui-button[data-type=error],.ui-button[data-type=primary],.ui-button[data-type=remind],.ui-button[data-type=success],.ui-button[data-type=warn],.ui-button[data-type=warning]{border:0;padding-top:10px;padding-bottom:10px;color:#fff}.ui-button[data-type=primary],.ui-button[data-type=remind]{background-color:#2a80eb}.ui-button[data-type=success]{background-color:#1cad70}.ui-button[data-type=warn],.ui-button[data-type=warning]{background-color:#f59b00}.ui-button[data-type=danger],.ui-button[data-type=error]{background-color:#eb4646}.ui-button.disabled,.ui-button:disabled{opacity:.4;cursor:default}.ui-button.loading{color:transparent!important;background-position:50%;background-repeat:no-repeat;position:relative;cursor:default}.ui-button.loading:before,input.ui-button.loading{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cpath d='M0 512a512 512 0 101024 0A512 512 0 100 512z' fill='%232a80eb'/%3E%3C/svg%3E"),url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cpath d='M0 512a512 512 0 101024 0A512 512 0 100 512z' fill='%232a80eb'/%3E%3C/svg%3E"),url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cpath d='M0 512a512 512 0 101024 0A512 512 0 100 512z' fill='%232a80eb'/%3E%3C/svg%3E");background-position-x:calc(50% - 10px),50%,calc(50% + 10px);background-position-y:18px,18px,18px;background-size:5px 5px;animation:bubbling 1s infinite}.ui-button[data-type]:not([data-type=normal]).loading:before,input.ui-button[data-type]:not([data-type=normal]).loading{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cpath d='M0 512a512 512 0 101024 0A512 512 0 100 512z' fill='%23fff'/%3E%3C/svg%3E"),url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cpath d='M0 512a512 512 0 101024 0A512 512 0 100 512z' fill='%23fff'/%3E%3C/svg%3E"),url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cpath d='M0 512a512 512 0 101024 0A512 512 0 100 512z' fill='%23fff'/%3E%3C/svg%3E")}.ui-button.loading:before{content:"";position:absolute;left:0;top:0;right:0;bottom:0;background-repeat:no-repeat}.ui-button.loading:before,div:invalid{width:20px;height:20px;background:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cpath d='M512 1024q-104 0-199-40-92-39-163-110T40 711Q0 616 0 512q0-15 10.5-25.5T36 476t25.5 10.5T72 512q0 90 35 171 33 79 94 140t140 95q81 34 171 34t171-35q79-33 140-94t95-140q34-81 34-171t-35-171q-33-79-94-140t-140-95q-81-34-171-34-15 0-25.5-10.5T476 36t10.5-25.5T512 0q104 0 199 40 92 39 163 110t110 163q40 95 40 199t-40 199q-39 92-110 163T711 984q-95 40-199 40z' fill='%232a80eb'/%3E%3C/svg%3E") no-repeat 50%;background-size:20px 20px;margin:auto;animation:spin 1s linear infinite}.ui-button[data-type]:not([data-type=normal]).loading:before,div:invalid{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cpath d='M512 1024q-104 0-199-40-92-39-163-110T40 711Q0 616 0 512q0-15 10.5-25.5T36 476t25.5 10.5T72 512q0 90 35 171 33 79 94 140t140 95q81 34 171 34t171-35q79-33 140-94t95-140q34-81 34-171t-35-171q-33-79-94-140t-140-95q-81-34-171-34-15 0-25.5-10.5T476 36t10.5-25.5T512 0q104 0 199 40 92 39 163 110t110 163q40 95 40 199t-40 199q-39 92-110 163T711 984q-95 40-199 40z' fill='%23fff'/%3E%3C/svg%3E")}@keyframes bubbling{0%{background-position-y:18px,18px,18px}15%{background-position-y:15px,18px,18px}30%{background-position-y:17px,15px,18px}45%{background-position-y:18px,17px,15px}60%{background-position-y:18px,18px,17px}}.ui-button.error{border-color:#eb4646!important}input[type=checkbox]:not(.ui-visible){position:absolute;opacity:0;width:20px;height:20px;cursor:pointer;z-index:-1}.ui-visible+.ui-checkbox{display:none}.ui-checkbox{display:inline-block;width:20px;height:20px;border:1px solid transparent;border-radius:4px;box-sizing:border-box;box-shadow:inset 0 1px,inset 1px 0,inset -1px 0,inset 0 -1px;background-color:#fff;background-clip:content-box;color:#d0d0d5;transition:color .2s,background-color .1s;-webkit-user-select:none;-ms-user-select:none;user-select:none;vertical-align:-5px;overflow:hidden}.ui-checkbox+label{margin-left:5px}:disabled+.ui-checkbox,:not(:disabled)+.ui-checkbox:hover{color:#ababaf}:focus+.ui-checkbox{color:#2a80eb}:focus+.ui-checkbox:hover{color:#0057c3}:checked+.ui-checkbox{color:#2a80eb;background-color:#2a80eb}:checked+.ui-checkbox:hover,:checked:focus+.ui-checkbox{color:#0057c3;background-color:#0057c3}.ui-checkbox:after{content:"";display:block;width:100%;height:100%;background:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cpath d='M920.185 224.838c-33.782-33.935-88.619-33.935-122.464 0L409.955 614.564 226.231 429.952c-33.782-33.935-88.68-33.935-122.461 0-33.784 33.997-33.784 89.108 0 123.044l244.925 246.118c33.782 33.998 88.68 33.998 122.463 0l449.028-451.201c33.843-33.967 33.843-89.048-.001-123.075z' fill='%23fff'/%3E%3C/svg%3E") no-repeat 50%;background-size:12px 12px;visibility:hidden}:checked+.ui-checkbox:after{visibility:visible}:disabled+.ui-checkbox{opacity:.38}.error.ui-checkbox{color:#eb4646}input[type=color]{opacity:0}.ui-color-input{width:40px;height:40px;margin:0;padding:0;border:0;opacity:0;vertical-align:middle;text-indent:-999px;position:relative;z-index:-1}.ui-color-track{display:inline;position:absolute;width:40px;height:40px;border-radius:4px;background-color:rgba(25,28,34,.2);cursor:pointer}.ui-color-thumb{display:block;margin:7px auto;width:24px;height:24px;border:1px solid #f7f9fa;border-radius:3px;background-color:#4c5161;background-clip:content-box;transition:background-color .25s}.ui-color-container{display:none;position:absolute;width:241px;padding:10px 12px;border-radius:4px;background-color:#f7f9fa;box-shadow:0 2px 5px rgba(0,0,0,.25);font-size:14px;animation:fadeIn .2s;z-index:21}.ui-color-switch{position:absolute;top:12px;right:12px;color:#2a80eb;border:1px solid transparent;height:20px;line-height:20px;padding:2px 5px;border-radius:3px;background:none;font-family:inherit;transition:border-color .2s,background-color .2s;box-sizing:content-box}.ui-color-switch:hover{border-color:#d0d0d5;background-color:#fff}.ui-color-switch:active{line-height:normal}.ui-color-current{padding-bottom:20px}.ui-color-current-square{display:inline-block;width:20px;height:20px;margin-right:5px;border-radius:3px;box-shadow:inset 0 1px rgba(0,0,0,.25),inset 0 -1px rgba(0,0,0,.25),inset 1px 0 rgba(0,0,0,.25),inset -1px 0 rgba(0,0,0,.25);background-color:currentColor;vertical-align:middle}.ui-color-current-input{width:72px;border:1px solid #d0d0d5;background-color:#fff;height:20px;line-height:20px;padding:2px 5px;border-radius:3px;margin-left:5px;font-size:13px;vertical-align:middle;box-sizing:content-box;text-transform:uppercase;color:inherit}.ui-color-body{min-height:100px}.ui-color-basic{overflow:hidden}.ui-color-lump-group{width:72px;float:left}.ui-color-lump{display:block;width:11px;height:11px;margin:0 1px 1px 0;box-sizing:border-box;background-color:currentColor}.ui-color-basic a.active,.ui-color-lump:hover{border:1px solid #fff}.ui-color-basic-l{width:12px;float:left;overflow:hidden}.ui-color-basic-r{margin-left:25px;overflow:hidden}.ui-color-basic-r .ui-color-lump{float:left}.ui-color-more{display:none;height:100px}.ui-color-more svg{width:100%;height:100%}.ui-color-more-l{width:180px;height:inherit;float:left;position:relative}.ui-color-cover-white{background-color:transparent;background-image:linear-gradient(180deg,transparent,grey);filter:progid:DXImageTransform.Microsoft.gradient(startcolorstr=#00808080,endcolorstr=#FF808080,gradientType=0);cursor:crosshair}.ui-color-circle{position:absolute;left:0;top:0;width:16px;height:16px;border:3px solid #fff;border-radius:20px;background-color:red;background-clip:content-box;-ms-transform:translate(-11px,-11px);transform:translate(-11px,-11px)}.ui-color-more-r{height:inherit;float:right;padding-right:8px;position:relative}.ui-color-more-fill{display:block;width:16px;height:100%;background-color:red}.ui-color-cover-white,.ui-color-more-cover{position:absolute;left:0;top:0;width:inherit;height:100px}.ui-color-more-cover{background-color:rgba(0,0,0,.01);-webkit-tap-highlight-color:transparent}.ui-color-more-arrow{position:absolute;right:0;top:100%}.ui-color-more-arrow:before{content:"";position:absolute;right:0;top:-5px;width:0;height:0;border-color:transparent #36383f transparent transparent;border-style:solid;border-width:5px 6px;overflow:hidden}.ui-color-footer{margin-top:15px;padding-bottom:5px;text-align:right}.ui-color-button-cancel,.ui-color-button-ensure{display:inline-block;width:80px;line-height:20px;padding-top:8px;padding-bottom:8px;margin-left:15px;border-radius:4px;text-align:center;font-size:14px;font-family:inherit;transition:box-shadow .2s,border-color .2s;cursor:pointer}.ui-color-button-cancel{border:1px solid #d0d0d5;background-color:#fff;color:#4c5161}.ui-color-button-cancel:hover{border-color:#ababaf;color:#4c5161}.ui-color-button-ensure{border:1px solid #2a80eb;background-color:#2a80eb;color:#fff}.ui-color-button-ensure:hover{color:#fff}.ui-color-button-cancel:hover,.ui-color-button-ensure:hover{box-shadow:inset 0 1px 2px rgba(0,0,0,.01),inset 0 0 0 100px rgba(0,0,0,.05)}.ui-color-button-cancel:active,.ui-color-button-ensure:active{box-shadow:inset 0 1px 2px rgba(0,0,0,.1),inset 0 0 0 100px rgba(0,0,0,.1)}datalist{display:none}.ui-datalist{display:none;position:absolute;animation:fadeIn .2s;z-index:19}.ui-datalist-datalist{max-height:304px;background-color:#fff;box-shadow:0 2px 5px rgba(0,0,0,.25);margin:0;padding:0;list-style:none;border:1px solid #d0d0d5;border:0 rgba(0,0,0,.2);font-size:14px;position:relative;overflow:auto;overscroll-behavior:none;-ms-scroll-chaining:none}.ui-datalist-datalist::-webkit-scrollbar{width:8px;height:8px}.ui-datalist-datalist::-webkit-scrollbar-thumb{background-color:#bbb;border-radius:8px}.ui-datalist-datalist::-webkit-scrollbar-thumb:hover{background-color:#aaa}.ui-datalist-datalist::-webkit-scrollbar-track-piece{background-color:#ddd}.ui-datalist-option{line-height:20px;padding:9px 12px;background-color:#fff;transition:background-color .15s;overflow:hidden;cursor:pointer}.ui-datalist-option:empty,.ui-datalist:empty{display:none}.ui-datalist-option:not(.disabled):hover{background-color:#f0f7ff}.ui-datalist-datalist>.selected{background-color:#e0f0ff}.ui-datalist-datalist>.disabled{opacity:.4;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ui-datalist-value{display:block;text-overflow:ellipsis;white-space:nowrap;color:inherit;overflow:hidden}.ui-datalist-label{float:right;color:#a2a9b6;font-size:12px}.ui-datalist-label+.ui-datalist-value{margin-right:60px}::-webkit-calendar-picker-indicator,::-webkit-clear-button,::-webkit-inner-spin-button{display:none}[type=date]::-webkit-datetime-edit-text{color:transparent;background:linear-gradient(180deg,transparent 9px,#4c5161 0,#4c5161 10px,transparent 0) no-repeat 50%;background-size:80% 100%}::-webkit-datetime-edit-ampm-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{background:none;color:#4c5161}:disabled::-webkit-datetime-edit-ampm-field,:disabled::-webkit-datetime-edit-day-field,:disabled::-webkit-datetime-edit-hour-field,:disabled::-webkit-datetime-edit-minute-field,:disabled::-webkit-datetime-edit-month-field,:disabled::-webkit-datetime-edit-text,:disabled::-webkit-datetime-edit-year-field{opacity:.4}:valid::-webkit-datetime-edit{visibility:hidden}.ui-date-input,.ui-date-range-input,.ui-month-input,.ui-month-range-input,.ui-time-input,.ui-year-input{-webkit-tap-highlight-color:transparent;position:relative}[class].ui-date-input>input,[class].ui-date-range-input>input,[class].ui-hour-input>input,[class].ui-month-input>input,[class].ui-month-range-input>input,[class].ui-time-input>input,[class].ui-year-input>input{font-family:Helvetica Neue,Helvetica,Arial,sans-serif;background:#fff;-webkit-appearance:none}.ui-input:hover>input[readonly]{border-color:#ababaf}.ui-input:active>input[readonly]{background-color:#f7f9fa}span.ui-date-input>input{width:125px}span.ui-time-input>input,span.ui-year-input>input{width:85px}span.ui-month-input>input{width:125px}span.ui-date-range-input>input{width:210px}span.ui-month-range-input>input{width:170px}@supports (-webkit-appearance:none) or (-moz-appearance:none){span.ui-time-input>[type=time]{width:auto;max-width:125px}}@supports not (-moz-appearance:none){span.ui-time-input>[type=time]{padding-right:33px}}.ui-date-arrow{position:absolute;left:0;right:0;top:0;bottom:0;border-right:4px solid transparent;cursor:pointer}.ui-date-arrow:before{content:"";position:absolute;width:20px;top:1px;bottom:1px;right:0;margin:auto;background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-205 297 200 200'%3E%3Cpath d='M-59.3 365.9c-5.1-5.2-13.4-5.2-18.5 0l-27.7 28.3-27.7-28.3c-5.1-5.2-13.4-5.2-18.5 0s-5.1 13.6 0 18.9l46.2 47.1 46.2-47.1c5.1-5.2 5.1-13.6 0-18.9z' fill='%232a80eb'/%3E%3C/svg%3E") no-repeat 100%;background-size:20px 20px}:disabled+.ui-date-arrow{cursor:default}:disabled+.ui-date-arrow:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-205 297 200 200'%3E%3Cpath d='M-59.3 365.9c-5.1-5.2-13.4-5.2-18.5 0l-27.7 28.3-27.7-28.3c-5.1-5.2-13.4-5.2-18.5 0s-5.1 13.6 0 18.9l46.2 47.1 46.2-47.1c5.1-5.2 5.1-13.6 0-18.9z' fill='%232a80eb' fill-opacity='.4'/%3E%3C/svg%3E")}.ui-date-arrow:before{background-color:#fff;transition:background-color .15s}.ui-input:active .ui-date-arrow:before,:disabled+.ui-date-arrow:before{background-color:#f7f9fa}.ui-date-container{display:inline-block;background-color:#fff;box-shadow:0 1px 3px rgba(0,0,0,.25);border:1px solid #d0d0d5;border-radius:4px;font-size:14px;font-family:Helvetica Neue,Helvetica,Arial,sans-serif;animation:fadeIn .2s}body>.ui-date-container{display:none;position:absolute;z-index:9}.ui-date-container a{text-decoration:none;transition:background-color .2s,color .2s}.ui-date-head{padding:5px 0 0;overflow:hidden}.ui-date-half{width:50%;float:left}.ui-date-next,.ui-date-prev{width:35px;height:30px;text-align:center;color:#b6bbc6;fill:currentColor}a.ui-date-prev:hover{color:#2a80eb}span.ui-date-prev{color:#ccd0d7}a.ui-date-next:hover{color:#2a80eb}span.ui-date-next{color:#ccd0d7}.ui-date-next>svg,.ui-date-prev>svg{display:block;width:20px;height:20px;margin:5px auto 0}.ui-date-next>svg{-ms-transform:rotate(180deg);transform:rotate(180deg)}.ui-date-prev{float:left}.ui-date-next{float:right}.ui-date-switch{display:block;line-height:30px;margin:0 35px;border-radius:2px;color:#4c5161;text-align:center}a.ui-date-item:not(.selected):hover,a.ui-date-switch:hover{color:#4c5161;background-color:#f0f0f2}.ui-date-x,.ui-hour-x,.ui-minute-x,.ui-month-x,.ui-year-x{width:225px}.ui-minute-x[data-step="1"]{width:328px}.ui-minute-x[data-step="2"]{width:276px}.ui-minute-body{padding-left:12px;padding-bottom:12px}.ui-hour-body{padding:8px 0 12px 12px}.ui-date-body,.ui-month-body,.ui-year-body{padding-left:5px;padding-right:2px;padding-bottom:5px}.ui-date-item{display:inline-block;border-radius:2px;text-align:center;font-size:13px;color:#4c5161}span.ui-date-item{opacity:.4}.ui-date-item.col0,.ui-date-item.col6,.ui-day-item.col0,.ui-day-item.col6{color:#eb4646}.ui-date-tr:last-child .ui-date-item:empty{display:none}.ui-hour-body .ui-date-item,.ui-minute-body .ui-date-item{width:45px;line-height:26px;margin-top:4px;margin-right:7px}.ui-month-body .ui-date-item,.ui-year-body .ui-date-item{width:45px;line-height:54px;margin-top:5px;margin-left:7px}.ui-date-now{display:block;line-height:30px;margin:0 5px 5px;text-align:center}.ui-day-x{padding:0 2px 0 5px;text-align:center}.ui-date-body .ui-date-item,.ui-day-item{display:inline-block;width:28px;line-height:28px;margin-right:3px;margin-top:1px;vertical-align:top}.ui-range-x{width:458px}.ui-range-body{overflow:hidden}.ui-range-body .ui-date-half+.ui-date-half{border-left:1px solid #f0f0f2;margin-left:-1px}.ui-range-footer{padding:5px 15px 15px;text-align:right}.ui-range-footer>.ui-button{margin-left:10px}.ui-date-container .selected{background-color:#2a80eb;color:#fff;cursor:default}.ui-date-container span.selected{opacity:.4}.ui-range-body .ui-date-half{width:229px}.ui-range-body .ui-date-body{padding-left:6px;padding-right:0}.ui-range-body .ui-month-body{margin-bottom:15px}.ui-range-date-body .selected{width:26px;line-height:26px;border:1px solid #2a80eb;border-right-width:4px;margin-right:0;border-radius:0}.ui-range-body .selected.col0{margin-left:-6px;border-left-width:7px}.ui-range-body .selected.col6{border-right-width:10px}.ui-range-body .selected.ui-date-first:not(.col0){border-top-left-radius:2px;border-bottom-left-radius:2px}.ui-range-body .selected.ui-date-last:not(.col6){border-top-right-radius:2px;border-bottom-right-radius:2px}.ui-range-body .ui-date-begin,.ui-range-body .ui-date-end{border-radius:0;width:26px;background-color:#fff;color:#4c5161}.ui-range-body .ui-date-begin{border-right-width:4px;border-left-width:1px;border-top-left-radius:2px;border-bottom-left-radius:2px}.ui-range-body .ui-date-end{margin-right:3px;border-right-width:1px;border-top-right-radius:2px;border-bottom-right-radius:2px}.ui-range-body .ui-date-begin:hover,.ui-range-body .ui-date-end:hover{background-color:#fff;color:#4c5161}.ui-range-body .ui-date-begin.col6{border-right-width:0;padding-right:9px}.ui-range-body .ui-date-begin.col0{border-left-width:1px;margin-left:0}.ui-range-body .ui-date-end.col0{border-left-width:0;padding-left:7px}.ui-range-body .ui-date-end.col6{border-right-width:1px}.ui-range-body .ui-date-begin.ui-date-end{margin-right:3px;padding:0;border:1px solid #2a80eb;border-radius:2px}.ui-range-body .ui-date-begin+.ui-date-end{border-left-width:1px;margin-right:3px}.ui-dialog-container{position:fixed;left:0;top:0;height:100%;width:100%;padding:0;border:0;background-color:rgba(25,28,34,.88);text-align:center;color:#4c5161;font-size:14px;white-space:nowrap;overflow:auto;z-index:19}.ui-dialog-container:not([open]){display:none}.ui-dialog-animation{animation:fadeIn .2s both}.ui-dialog-container+.ui-dialog-container{transition:background-color .2s;background-color:transparent}.ui-dialog-container:after{content:"";height:95%}.ui-dialog,.ui-dialog-container:after{display:inline-block;vertical-align:middle}.ui-dialog{margin-top:20px;margin-bottom:30px;text-align:left;min-width:400px;border-radius:4px;background-color:#f7f9fa;white-space:normal;outline:none;position:relative}.ui-dialog-animation .ui-dialog{animation:tinydown .25s}.ui-dialog-title{margin:0;line-height:30px;padding:15px 50px 0 25px;font-weight:700;font-size:14px;color:#4c5161;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default}.ui-dialog-close{position:absolute;top:8px;right:7px;width:40px;height:40px;border:0;background:none;transition:fill .2s;fill:#b6bbc6;cursor:pointer;z-index:1}.ui-dialog-close>svg{width:20px;height:20px}.ui-dialog-close:hover{background-color:#4c5161;background-color:transparent;fill:#4c5161}.ui-dialog-body{min-height:60px;padding:10px 25px 20px}.ui-dialog-title:empty~.ui-dialog-body{min-height:40px;padding-top:30px}.ui-dialog-body:after{content:"";display:table;clear:both}.ui-dialog-footer{padding:3px 25px 25px;margin-top:-3px;text-align:right;max-height:40px;opacity:1;transition:max-height .2s,opacity .2s .1s;overflow:hidden}.ui-dialog-footer:empty{max-height:0;opacity:0}.ui-dialog-footer .ui-button{margin-left:15px}.ui-dialog-footer .ui-button:first-child{margin-left:0}.ui-dialog-stretch{max-height:2000px;height:calc(100% - 50px)}.ui-dialog-stretch .ui-dialog-footer{position:absolute;left:0;bottom:0;right:0}.ui-dialog-stretch .ui-dialog-body{position:absolute;left:0;right:0;top:50px;bottom:90px;padding:0 0 0 25px;overflow:auto}.ui-dialog-remind{background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-105 197 400 400'%3E%3Cpath d='M95.5 197.5c110.5 0 200 89.5 200 200s-89.5 200-200 200-200-89.5-200-200 89.5-200 200-200z' fill='%232a80eb'/%3E%3Cpath d='M90.5 347.5h10c2.8 0 5 2.2 5 5v150c0 2.8-2.2 5-5 5h-10c-2.8 0-5-2.2-5-5v-150c0-2.8 2.2-5 5-5zm0-50h10c2.8 0 5 2.2 5 5v20c0 2.8-2.2 5-5 5h-10c-2.8 0-5-2.2-5-5v-20c0-2.8 2.2-5 5-5z' fill='%23fff'/%3E%3C/svg%3E"),none}.ui-dialog-success{background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cpath fill='%231cad70' d='M400.5 200.5c0 110.457-89.542 199.999-199.999 199.999C90.043 400.499.5 310.957.5 200.5m0 0C.5 90.042 90.043.5 200.501.5 310.958.5 400.5 90.042 400.5 200.5'/%3E%3Cpath fill='%23FFF' d='M286.398 147.132c-1.654-1.134-3.306-2.395-6.801-4.293-4.271-2.336-9.259 2.624-12.391 6.816l-77.641 102.279-47.916-63.522c-3.144-4.188-4.902-8.468-13.073-1.859-3.097 2.123.234-.361-3.969 2.881-3.884 3.064-4.105 8.598-.971 12.774 0 0 38.641 55.817 45.883 65.074 10.625 13.22 29.944 12.57 40.087 0 7.483-9.473 77.757-107.584 77.757-107.584 3.14-4.194 2.898-9.697-.965-12.566z'/%3E%3C/svg%3E"),none}.ui-dialog-warning{background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cpath fill='%23f59b00' d='M400.5 200.5c0 110.457-89.542 199.999-199.999 199.999C90.043 400.499.5 310.957.5 200.5m0 0C.5 90.042 90.043.5 200.501.5 310.958.5 400.5 90.042 400.5 200.5'/%3E%3Cpath fill='%23FFF' d='M195.503 100.503h10a5 5 0 015 5v150a5 5 0 01-5 5h-10a5 5 0 01-5-5v-150a5 5 0 015-5zm0 179.999h10a5 5 0 015 4.999v20.002a5 5 0 01-5 5h-10a5 5 0 01-5-5v-20.002a5 5 0 015-4.999z'/%3E%3C/svg%3E"),none}.ui-dialog-danger{background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cpath fill='%23eb4646' d='M400.5 200.5c0 110.457-89.542 199.999-199.999 199.999C90.043 400.499.5 310.957.5 200.5m0 0C.5 90.042 90.043.5 200.501.5 310.958.5 400.5 90.042 400.5 200.5'/%3E%3Cpath fill='%23FFF' d='M195.503 100.503h10a5 5 0 015 5v150a5 5 0 01-5 5h-10a5 5 0 01-5-5v-150a5 5 0 015-5zm0 179.999h10a5 5 0 015 4.999v20.002a5 5 0 01-5 5h-10a5 5 0 01-5-5v-20.002a5 5 0 015-4.999z'/%3E%3C/svg%3E"),none}.ui-dialog-alert,.ui-dialog-confirm{max-width:340px;min-height:40px;padding-top:10px;font-size:16px;word-wrap:break-word;overflow:hidden}.ui-dialog-danger,.ui-dialog-remind,.ui-dialog-success,.ui-dialog-warning{padding:20px 0 20px 60px;background-repeat:no-repeat;background-position:0 10px;background-size:40px 40px}.ui-dialog-alert>h6,.ui-dialog-confirm>h6{font-size:15px;margin-bottom:5px}.ui-dialog-alert>:first-child:only-child,.ui-dialog-confirm>:first-child:only-child{margin-top:0}.ui-dialog-alert>:first-child:not(:only-child),.ui-dialog-confirm>:first-child:not(:only-child){margin-top:-10px}.ui-dialog-alert>h6~p,.ui-dialog-confirm>h6~p{font-size:14px}.ui-dialog-alert>p,.ui-dialog-confirm>p{margin:0}.ui-dialog-loading .ui-dialog-close,.ui-dialog-loading .ui-dialog-footer,.ui-dialog-loading .ui-dialog-title{visibility:hidden}.ui-dialog-loading .ui-dialog-body{min-width:400px}@media screen and (max-width:480px){.ui-dialog,.ui-dialog-loading .ui-dialog-body{min-width:90vw}.ui-dialog-danger,.ui-dialog-remind,.ui-dialog-success,.ui-dialog-warning{padding-bottom:0}.ui-dialog-container-alert .ui-dialog,.ui-dialog-container-confirm .ui-dialog,.ui-dialog[style*="width: auto"]{max-width:calc(100vw - 16px)}}.ui-droplist-x{position:absolute;width:111px;padding:7px 0;background-color:#fff;box-shadow:0 2px 5px rgba(0,0,0,.25);border:1px solid #d0d0d5;border-radius:4px;font-size:14px;animation:fadeIn .2s;z-index:9}.ui-droplist-li{display:block;line-height:20px;padding:7px 12px 8px;color:#4c5161;text-decoration:none;cursor:pointer;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.ui-droplist-hr{border:0;border-bottom:1px solid #d0d0d5;margin:7px 12px;opacity:.4}a.ui-droplist-li:hover{color:#4c5161;background-color:#f0f7ff}a.ui-droplist-li:hover:after{background-position:0 -20px}span.ui-droplist-li{color:#a2a9b6;cursor:default}.ui-droplist-li[data-sublist]:before{-ms-transform:rotate(-90deg);transform:rotate(-90deg);margin-top:1px;float:right}.ui-droplist-x>.selected{background-color:#f1f9fe}.selected+.ui-droplist-xx>.ui-droplist-x{display:block}.ui-droplist-xx{position:relative}.ui-droplist-xx>.ui-droplist-x{display:none;left:calc(100% - 5px);top:-35px}.ui-droplist-xx>.reverse{left:auto;right:calc(100% - 5px)}.ui-droplist-arrow:empty,.ui-droplist-arrow:not(:empty):after,.ui-droplist-li[data-sublist]:before{content:"";display:inline-block;width:20px;height:20px;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-205 197 200 400'%3E%3Cpath d='M-59.7 271.6c-4.2-4.1-11-4.1-15.3 0l-30.5 29.6-30.5-29.6c-4.2-4.1-11-4.1-15.3 0-4.2 4.1-4.2 10.7 0 14.8l38.2 37c4.2 4.1 11 4.1 15.3 0l38.2-37c4.1-4.1 4.1-10.7-.1-14.8z' fill='%23a2a9b6'/%3E%3Cpath d='M-58.7 471.6c-4.2-4.1-11-4.1-15.3 0l-30.5 29.6-30.5-29.6c-4.2-4.1-11-4.1-15.3 0-4.2 4.1-4.2 10.7 0 14.8l38.2 37c4.2 4.1 11 4.1 15.3 0l38.2-37c4.1-4.1 4.1-10.7-.1-14.8z' fill='%232a80eb'/%3E%3C/svg%3E"),none;background-size:20px 40px;vertical-align:-5px}.ui-droplist-arrow:not(:empty):hover:after,a:hover .ui-droplist-arrow:empty{background-position:0 -20px}.ui-dropanel-x{position:absolute;width:260px;padding:20px;background-color:#fff;box-shadow:0 1px 3px rgba(0,0,0,.25);border:1px solid #d0d0d5;border:0 rgba(0,0,0,.2);font-size:14px;animation:fadeIn .2s;z-index:9}.ui-dropanel-title{line-height:20px;margin-top:-2px;margin-bottom:0;font-size:14px;font-weight:700}.ui-dropanel-close{position:absolute;top:3px;right:2px;width:40px;height:40px;border:0;background:none;margin:0;padding:0;transition:fill .2s;fill:#b6bbc6;cursor:pointer;z-index:1}.ui-dropanel-close svg{width:20px;height:20px}.ui-dropanel-close:hover{background-color:#4c5161;fill:#4c5161;background:none,none}.ui-dropanel-content{display:block;min-height:40px;padding:10px 0 20px}.ui-dropanel-footer{text-align:right}.ui-dropanel-footer .ui-button{margin-left:15px}.ui-dropanel-footer .ui-button:first-child{margin-left:0}input:not([type=search])::-ms-clear{display:none}[type=search]{-webkit-appearance:none}[type=search]::-webkit-search-cancel-button{-webkit-appearance:none;width:20px;height:20px;margin-right:-2px;background:#b6bbc6 url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZmlsbD0iI0ZGRiIgZD0iTTAgMHYyMDBoMjAwVjBIMHptMTUxLjYzNiAxMzYuNDgyYTcuMzc0IDcuMzc0IDAgMDEwIDEwLjQyN2wtNS4yMTIgNS4yMWE3LjM3MiA3LjM3MiAwIDAxLTEwLjQyNiAwbC0zNi40ODMtMzYuNDg1TDYzLjAzIDE1Mi4xMmE3LjM3IDcuMzcgMCAwMS0xMC40MjQgMGwtNS4yMS01LjIxYTcuMzcgNy4zNyAwIDAxMC0xMC40MjhMODMuODggMTAwIDQ3LjM5NiA2My41MTRhNy4zNjggNy4zNjggMCAwMTAtMTAuNDI1bDUuMjEtNS4yMWE3LjM2OCA3LjM2OCAwIDAxMTAuNDI2IDBMOTkuNTE2IDg0LjM2IDEzNiA0Ny44OGE3LjM3IDcuMzcgMCAwMTEwLjQyNSAwbDUuMjEyIDUuMjFhNy4zNyA3LjM3IDAgMDEwIDEwLjQyNUwxMTUuMTUzIDEwMGwzNi40ODMgMzYuNDgyeiIvPjwvc3ZnPg==) no-repeat 50%;background-size:20px 20px;-webkit-transition:background-color .15s;transition:background-color .15s;cursor:pointer}[type=search]::-webkit-search-cancel-button:hover{background-color:#4c5161}input[type=search]::-webkit-search-results-decoration{display:none}input:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px #fff inset;background-color:transparent}input[disabled],input[readonly]{cursor:default}.ui-input>input,input.ui-input{height:40px;line-height:20px;padding:9px 8px;border:1px solid #d0d0d5;border-radius:4px;background-color:#fff;box-sizing:border-box;font-size:14px;outline:none;color:#4c5161;transition:border-color .15s,background-color .15s}.ui-input:hover,.ui-input:hover>input{border-color:#ababaf}.ui-input:focus,.ui-input>input:focus{border-color:#2a80eb}span.ui-input{display:inline-block}div.ui-input>input,input[width="100%"]{width:100%}.ui-input-x{padding:0 56px 0 3px;border-radius:4px;overflow:hidden}.ui-input-x,.ui-input-x>input{position:relative;box-sizing:border-box}.ui-input-x>input{display:block;width:100%;height:40px;line-height:20px;padding:9px 0;border-bottom:0;border-top:0;border-color:transparent currentcolor;border-style:solid none;border-width:1px 0;right:-6px;color:#4c5161;font-size:14px;outline:none;background:none;background-clip:content-box;z-index:1}.ui-input-x>.ui-input{position:absolute;border:1px solid #d0d0d5;border-radius:4px;background-color:#fff;top:0;bottom:0;left:0;right:0;transition:border-color .15s,background-color .15s}.ui-input-x .ui-input-count{line-height:38px;padding:0 2px;color:#a2a9b6;font-size:12px;white-space:nowrap;position:absolute;font-family:sans-serif;right:8px;top:1px;z-index:1}.ui-input-count slash{margin:0 1px}.ui-input-x>input:not(:disabled):not([readonly]):hover~.ui-input{border-color:#ababaf}.ui-input-x>input:not(:disabled):not([readonly]):focus~.ui-input{border-color:#2a80eb}.ui-input-x .ui-placeholder{padding:9px 7px;z-index:1}.ui-input-search:not(input){position:relative}.ui-input-search>input:not(.ui-icon-search){padding-left:40px}.ui-input-search[align=right]>:not(.ui-icon-search){padding-right:40px;padding-left:9px}.ui-icon-search{position:absolute;left:3px;top:1px;width:20px;height:20px;color:#b6bbc6;background-color:transparent;border:solid transparent;border-width:9px 8px;box-sizing:content-box;transition:color .2s;padding:0;margin:0;text-indent:-99px;font-size:0;cursor:pointer;outline:0 none;overflow:hidden}[align=right]>.ui-icon-search{left:auto;right:3px}.ui-input-search:hover .ui-icon-search{color:#a2a9b6}.ui-input-search>:disabled~.ui-icon-search{color:#b6bbc6;cursor:default}.ui-input-search>:focus~.ui-icon-search{color:#2a80eb}.ui-input-search>.error~.ui-icon-search{color:#eb4646}.ui-icon-search:after,.ui-icon-search:before{content:"";position:absolute}.ui-icon-search:before{width:9px;height:9px;border:2px solid;border-radius:50%;left:2px;top:2px}.ui-icon-search:after{width:6px;border-top:2px solid;transform-origin:left;transform:scaleY(1.25) rotate(30deg);left:13px;top:11px}input.ui-icon-search{border:0;margin:1px;width:18px;height:18px;background:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cpath d='M638.72 638.72a256 256 0 10-361.984-361.984A256 256 0 00638.72 638.72zm58.71 119.04a384 384 0 1185.418-95.573l202.24 202.24a64.256 64.256 0 01.597 91.136 63.744 63.744 0 01-91.05-.598l-197.206-197.12z' fill='%23b6bbc6'/%3E%3C/svg%3E") 50%;background-clip:content-box;background-size:17.9px 17.9px;box-sizing:content-box}:focus~input.ui-icon-search{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cpath d='M638.72 638.72a256 256 0 10-361.984-361.984A256 256 0 00638.72 638.72zm58.71 119.04a384 384 0 1185.418-95.573l202.24 202.24a64.256 64.256 0 01.597 91.136 63.744 63.744 0 01-91.05-.598l-197.206-197.12z' fill='%232a80eb'/%3E%3C/svg%3E")}.error~input.ui-icon-search{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cpath d='M638.72 638.72a256 256 0 10-361.984-361.984A256 256 0 00638.72 638.72zm58.71 119.04a384 384 0 1185.418-95.573l202.24 202.24a64.256 64.256 0 01.597 91.136 63.744 63.744 0 01-91.05-.598l-197.206-197.12z' fill='%23eb4646'/%3E%3C/svg%3E")}.ui-input-x>:disabled~.ui-input,.ui-input>input:disabled,input.ui-input:disabled{background-color:#f7f9fa}.ui-input-x>input:disabled,.ui-input>input:disabled,input.ui-input:disabled{color:#a2a9b6}.ui-input:disabled:hover,.ui-input:hover>input:disabled,.ui-input>input:disabled:hover,.ui-input>input[readonly]:focus,.ui-input>input[readonly]:hover,.ui-input[readonly]:focus,.ui-input[readonly]:hover{border-color:#d0d0d5}.ui-input.error,.ui-input>.error{border-color:#eb4646!important}.ui-input-count.error,.ui-input-count>.error{color:#eb4646}.ui-kbd-tips{position:absolute;left:-9em;top:-9em;font-family:consolas,Liberation Mono,courier,monospace;font-size:12px;border-radius:2px;color:#fff;background:rgba(0,0,0,.75);opacity:.8;line-height:13px;padding:0 3px;z-index:99}.ui-kbd-tips kbd{font-family:inherit}.ui-outline.ui-outline{outline:1px dotted #2a80eb;outline:5px auto -webkit-focus-ring-color}.ui-lightip{display:none;width:25em;margin:0 auto;border-radius:2px;text-align:center;font-size:14px;line-height:20px;padding:15px 1em;background-color:#4c5161;animation:fadeIn .25s both;position:fixed;top:0;left:1rem;right:1rem;z-index:19;outline:none;cursor:default}.ui-lightip[open]{display:inline}.ui-lightip[data-status=success]{background-color:#1cad70}.ui-lightip[data-status=error]{background-color:#eb4646}.ui-lightip-text{display:inline-block;background-repeat:no-repeat;background-size:20px 20px;text-align:left;color:#fff}[data-status=success] .ui-lightip-text{padding-left:23px;margin-left:-5px;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cpath fill='%23FFF' d='M163.038 57.226c-5.217-4.162-5.713-4.289-11.674-7.244-2.683-1.344-6.633 2.113-8.569 4.67l-52.648 67.042-34.301-43.387c-1.94-2.558-5.516-3.499-8.2-2.293-6.11 3.095-5.496 2.992-10.715 7.029-2.386 1.883-2.535 5.245-.597 7.793 0 0 36.97 46.917 41.44 52.565 6.557 8.068 18.483 7.669 24.744 0 4.62-5.781 61.117-78.506 61.117-78.506 1.937-2.559 1.788-5.918-.597-7.669z'/%3E%3C/svg%3E")}[data-status=error] .ui-lightip-text{padding-left:23px;margin-left:-5px;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cpath fill='%23FFF' d='M116.153 99.999l36.484-36.486a7.369 7.369 0 000-10.423l-5.212-5.213a7.375 7.375 0 00-10.425 0l-36.484 36.485-36.485-36.485a7.374 7.374 0 00-10.424 0l-5.211 5.213a7.365 7.365 0 000 10.423L84.88 99.999l-36.483 36.485a7.369 7.369 0 000 10.426l5.211 5.213a7.38 7.38 0 0010.424 0l36.485-36.486L137 152.122a7.38 7.38 0 0010.425 0l5.212-5.213a7.372 7.372 0 000-10.426l-36.484-36.484z'/%3E%3C/svg%3E")}@media screen and (max-width:640px){.ui-lightip{width:auto;top:50%;left:50%;right:auto;max-width:calc(100% - 2rem - 2em);transform:translate(-50%,-50%)}}.ui-loading,ui-loading{text-align:center;cursor:default;box-sizing:border-box}ui-loading{display:inline-block;font-size:14px;line-height:20px;vertical-align:middle}ui-loading[align=center]{position:absolute;left:50%;top:50%;-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}ui-loading:not([rows]):not([spin]):before,ui-loading[rows]{display:block}ui-loading:not([rows]):not([spin]):after{display:none}ui-loading[rows="2"]{height:40px}ui-loading[rows="3"]{height:60px}ui-loading[rows="4"]{height:80px}ui-loading[rows="5"]{height:100px}ui-loading[rows="6"]{height:120px}ui-loading[rows="7"]{height:140px}ui-loading[rows="8"]{height:160px}ui-loading[rows="9"]{height:180px}ui-loading[rows="10"]{height:200px}ui-loading[rows="11"]{height:220px}ui-loading[rows="12"]{height:240px}ui-loading[rows="13"]{height:260px}ui-loading[rows="14"]{height:280px}ui-loading[rows="15"]{height:300px}ui-loading[width="100%"]{width:100%}ui-loading[height="100%"]{height:100%}.ui-loading:after,ui-loading:after{content:"";display:inline-block;height:100%;vertical-align:middle}.ui-loading-before,.ui-loading:before,ui-loading:before{content:"";display:inline-block;width:20px;height:20px;margin:0 .5em;background:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cpath d='M512 1024q-104 0-199-40-92-39-163-110T40 711Q0 616 0 512q0-15 10.5-25.5T36 476t25.5 10.5T72 512q0 90 35 171 33 79 94 140t140 95q81 34 171 34t171-35q79-33 140-94t95-140q34-81 34-171t-35-171q-33-79-94-140t-140-95q-81-34-171-34-15 0-25.5-10.5T476 36t10.5-25.5T512 0q104 0 199 40 92 39 163 110t110 163q40 95 40 199t-40 199q-39 92-110 163T711 984q-95 40-199 40z' fill='%232a80eb'/%3E%3C/svg%3E") no-repeat 50%;background-size:100%;animation:spin 1s linear infinite;vertical-align:-4px}.ui-loading[spin]:before,ui-loading[spin]:before{display:none}.ui-loading[size="1"]:before,[size="1"]>.ui-loading-before,ui-loading[size="1"]:before{width:10px;height:10px;vertical-align:0}.ui-loading[size="3"]:before,[size="3"]>.ui-loading-before,ui-loading[size="3"]:before{width:30px;height:30px;vertical-align:-8px}.ui-loading[size="4"]:before,[size="4"]>.ui-loading-before,ui-loading[size="4"]:before{width:40px;height:40px;vertical-align:-12px}ui-dot{display:inline-block;height:1em;line-height:1;text-align:left;vertical-align:-.25em;overflow:hidden}ui-dot:before{display:block;content:"...\A..\A.";white-space:pre-wrap;animation:dot 3s step-start infinite both}@keyframes dot{33%{transform:translateY(-2em)}66%{transform:translateY(-1em)}}ui-pagination{display:block;height:30px}.ui-page{display:inline-block;min-width:18px;padding-left:2px;padding-right:2px;margin-left:5px;margin-right:5px;height:28px;line-height:28px;border:1px solid transparent;text-align:center;color:#a2a9b6;font-size:14px;font-family:Helvetica Neue,Helvetica,Arial,sans-serif;transition:border-color .15s,background-color .15s;vertical-align:top;text-decoration:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;position:relative}span.ui-page{cursor:default}a.ui-page:hover{border-color:#b6bbc6;color:#a2a9b6;text-decoration:none}.ui-page>svg{width:20px;height:20px;margin-top:3px}.ui-page-next,.ui-page-prev{text-align:center;fill:currentColor;overflow:hidden}span.ui-page-next,span.ui-page-prev{color:#ccd0d7}.ui-page-next svg{-ms-transform:scaleX(-1);transform:scaleX(-1)}.ui-page-prev{margin-left:0}.ui-page-next{margin-right:0}.ui-page-ellipsis{display:inline-block}.ui-page-current{color:#fff;background-color:#2a80eb}.ui-page-text{color:#4c5161}.ui-page.loading>svg{visibility:hidden}.ui-page.loading:before{content:"";position:absolute;left:0;top:0;right:0;bottom:0;background-repeat:no-repeat;width:20px;height:20px;background:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cpath d='M512 1024q-104 0-199-40-92-39-163-110T40 711Q0 616 0 512q0-15 10.5-25.5T36 476t25.5 10.5T72 512q0 90 35 171 33 79 94 140t140 95q81 34 171 34t171-35q79-33 140-94t95-140q34-81 34-171t-35-171q-33-79-94-140t-140-95q-81-34-171-34-15 0-25.5-10.5T476 36t10.5-25.5T512 0q104 0 199 40 92 39 163 110t110 163q40 95 40 199t-40 199q-39 92-110 163T711 984q-95 40-199 40z' fill='%232a80eb'/%3E%3C/svg%3E") no-repeat 50%;background-size:20px 20px;margin:auto;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(1turn)}}::-webkit-input-placeholder{-webkit-transition:opacity .15s;color:#a2a9b6;line-height:inherit;font-size:14px}:focus::-webkit-input-placeholder{opacity:.38}::-moz-placeholder{transition:opacity .15s;color:#a2a9b6;font-size:14px}:focus::-moz-placeholder{opacity:.38}::placeholder{transition:opacity .15s;color:#a2a9b6;font-size:14px}:focus::placeholder{opacity:.38}:-ms-input-placeholder{transition:opacity .15s;color:#a2a9b6!important;font-size:14px}:focus:-ms-input-placeholder{opacity:.38}.ui-progress{display:inline-block;width:10em;height:.25em;vertical-align:calc(.25em - 2px);border:0;background-color:#d0d0d5;color:#2a80eb;border-radius:1em;overflow:hidden}.ui-progress::-moz-progress-bar{background-color:#2a80eb}.ui-progress::-webkit-progress-bar{background-color:#d0d0d5}.ui-progress::-webkit-progress-value{background-color:#2a80eb}progress[width="100%"]{width:100%}.ui-progress:indeterminate::-moz-progress-bar{background-color:inherit}.ui-progress::-ms-fill{border:none}.ui-progress[role]{display:inline-block;position:relative;box-sizing:border-box}.ui-progress[aria-valuenow]:before{background-color:#2a80eb}.ui-progress[aria-valuenow]{background:#d0d0d5}.ui-progress[aria-valuenow]:before{content:"";display:block;height:100%}input[type=radio]:not(.ui-visible){position:absolute;opacity:0;width:20px;height:20px;cursor:pointer;z-index:-1}.ui-visible+.ui-radio{display:none}.ui-radio{display:inline-block;width:20px;height:20px;border:1px solid #d0d0d5;border-radius:50%;background-color:#fff;box-sizing:border-box;vertical-align:-.5ex;-webkit-user-select:none;-ms-user-select:none;user-select:none;transition:border-color .2s;overflow:hidden}.ui-radio+label{margin-left:5px}:not(:disabled)+.ui-radio:hover{border-color:#ababaf}:focus+.ui-radio{border-color:#2a80eb}.ui-radio:before{content:"";display:block;width:10px;height:10px;margin:4px auto 0;border-radius:50%;background-color:#2a80eb;visibility:hidden}:checked+.ui-radio:before{visibility:visible}:disabled+.ui-radio{border-color:#ababaf;opacity:.38}.error.ui-radio{border-color:#eb4646}.ui-range-input,[type=range]{height:20px;margin:0;padding:0;touch-action:none;visibility:hidden;vertical-align:middle}div.ui-range-input>input{width:100%;visibility:hidden}.ui-range-input[width="100%"]{width:100%}.ui-range{display:inline-block;visibility:visible;transition:opacity .2s}.ui-range-track{height:4px;margin-top:8px;border-radius:4px;background-color:#a2a9b6;border-left:0 solid #2a80eb;text-align:left}.ui-range-thumb{width:16px;height:16px;position:absolute;margin:-7px 0 0 -9px;border-radius:20px;background-color:#fff;box-shadow:0 1px 3px 1px rgba(0,0,0,.25);transition:border-color .15s,background-color .15s;cursor:pointer;-webkit-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}@media (any-pointer:coarse){.ui-range-thumb{width:20px;height:20px;margin:-9px 0 0 -11px}}.ui-range-thumb:hover{border-color:#ababaf}.ui-range-thumb.active{background-color:#f7f9fa;box-shadow:0 0 1px 1px rgba(0,0,0,.25)}:disabled+.ui-range{opacity:.38;cursor:default}:disabled+.ui-range .ui-range-track{position:relative}:disabled+.ui-range .ui-range-thumb{cursor:inherit}select{display:inline-block;height:40px;margin:0;border:1px solid #d0d0d5;border-right-width:27px;opacity:.0001;font-size:14px;font-family:simsun;vertical-align:middle}select[width="100%"]{width:100%}select[multiple]{height:auto;font-size:38px;font-size:33px;font-family:simsun;padding:0;border-right-width:1px;vertical-align:top;cursor:pointer}select[multiple]>option{min-height:38px;height:38px;padding:0;font-size:inherit}select[multiple]>option:disabled{cursor:default}select[hidden],select[hidden]+.ui-select{display:none}select.ui-visible,select[opacity="1"],select[opacity="100%"],select[style*="opacity:1"],select[style*="opacity: 1"]{opacity:1;border-right-width:1px}.ui-select{display:inline-block;height:40px;line-height:20px;vertical-align:middle;font-size:14px;transition:opacity .2s;position:relative}[multiple]~.ui-select{vertical-align:top}.ui-select.active{z-index:3}.ui-select-button{display:block;height:20px;padding:9px 27px 9px 12px;color:#4c5161;border:1px solid #d0d0d5;border-radius:4px;background-color:#fff;text-decoration:none;transition:border-color .15s,background-color .15s;cursor:pointer}.ui-select-button:hover{color:#4c5161;border-color:#ababaf}:not(:disabled)+.ui-select>.ui-select-button:active{background-color:#f7f9fa}.active>a.ui-select-button{border-color:#2a80eb;border-radius:4px 4px 0 0}.reverse>a.ui-select-button{border-radius:0 0 4px 4px}.ui-select-text{display:block;width:100%;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.ui-select-icon,.ui-select-text:only-child:after{position:absolute;width:20px;height:20px;background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cpath fill='%232a80eb' d='M145.659 68.949a12.874 12.874 0 00-18.473 0L99.479 97.233 71.772 68.949a12.874 12.874 0 00-18.473 0c-5.099 5.208-5.099 13.648 0 18.857l46.18 47.14 46.181-47.14c5.099-5.208 5.099-13.649-.001-18.857z'/%3E%3C/svg%3E") no-repeat 50%;background-size:20px 20px;right:7px;top:10px}.ui-select-text:only-child:after{content:""}.ui-select-datalist{position:absolute;left:0;right:0;top:39px;display:none;max-height:304px;padding:0;border:1px solid #2a80eb;background-color:#fff;overflow:auto;overscroll-behavior:none;-ms-scroll-chaining:none}[multiple]~.ui-select>.ui-select-datalist{position:static;display:block;max-height:none;height:inherit;border-color:#d0d0d5;border-radius:4px;box-sizing:border-box}[multiple]:hover~.ui-select>.ui-select-datalist{border-color:#ababaf}[multiple]:focus~.ui-select>.ui-select-datalist{border-color:#2a80eb}.ui-select-datalist::-webkit-scrollbar{width:8px;height:8px}.ui-select-datalist::-webkit-scrollbar-thumb{background-color:#bbb;border-radius:8px}.ui-select-datalist::-webkit-scrollbar-thumb:hover{background-color:#aaa}.ui-select-datalist::-webkit-scrollbar-track-piece{background-color:#ddd}.active>.ui-select-datalist{display:block}.reverse>.ui-select-datalist{top:auto;bottom:39px}.ui-select-datalist-li{display:block;line-height:20px;padding:9px 12px;color:#4c5161;background-color:#fff;text-decoration:none;text-overflow:ellipsis;white-space:nowrap;transition:background-color .15s;overflow:hidden}.ui-select-datalist-li[href]{cursor:pointer}.ui-select-datalist-li:not(:only-child):empty{display:none}.ui-select-datalist-li:hover{color:#4c5161}.ui-select-datalist>.disabled{color:#a2a9b6;cursor:default}.ui-select-datalist>.selected{background-color:#e0f0ff}.ui-select-datalist-li[href]:hover,[multiple]~.ui-select .ui-select-datalist-li[href]{color:#4c5161;background-color:#f0f7ff}select:disabled{cursor:default}select:disabled+.ui-select{opacity:.38}select:disabled+.ui-select .ui-select-button,select:disabled+.ui-select .ui-select-datalist{cursor:default;border-color:#ababaf}.error .ui-select-button,.error[multiple]~.ui-select .ui-select-datalist,select.error{border-color:#eb4646}.ui-switch{display:inline-block;width:44px;height:26px;border:2px solid;border-radius:26px;background-color:currentColor;box-sizing:border-box;color:#b6bbc6;font-size:0;transition:all .2s;cursor:pointer;-webkit-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}.ui-switch:before{content:"";display:block;width:22px;height:22px;border-radius:50%;background-color:#fff;transition:margin-left .2s}:active+.ui-switch:before{box-shadow:inset 1px 1px 1px rgba(0,0,0,.1)}:checked+.ui-switch{color:#2a80eb}:checked+.ui-switch:before{margin-left:18px}:disabled+.ui-switch{opacity:.38;cursor:default}.ui-switch:hover,:focus+.ui-switch{color:#a2a9b6}:checked+.ui-switch:hover,:checked:focus+.ui-switch{color:#0057c3}:disabled+.ui-switch:hover{color:#b6bbc6}:checked:disabled+.ui-switch:hover{color:#2a80eb}.ui-tab-tabs{height:40px;line-height:40px;border-bottom:1px solid #d0d0d5;position:relative}.ui-tab-tab{float:left;margin-right:50px;font-size:16px;text-align:center;color:#4c5161;text-decoration:none;overflow:hidden;cursor:pointer}.ui-tab-tab a{display:block;color:#4c5161}.ui-tab-tab:hover,.ui-tab-tab:hover a{color:#2a80eb}.ui-tab-tabs .active,.ui-tab-tabs .active:hover,.ui-tab-tabs .active a{line-height:36px;padding-top:2px;margin-bottom:-1px;border-bottom:3px solid;color:#2a80eb;cursor:default}.ui-tab-line{display:none;position:absolute;bottom:-1px;left:0;width:0;border-bottom:3px solid #2a80eb;transition:all .35s}.ui-tab-tabs>.ui-tab-line~.ui-tab-tab{line-height:40px;padding-top:0;margin-bottom:0;border-bottom:0}.ui-tab-contents{margin-top:30px}.ui-tab-content:not([role]):not(:target),.ui-tab-content[role]:not(.active),.ui-tab-contents:target-within .ui-tab-content:not(:target){display:none}.ui-table{width:100%;line-height:21px;table-layout:fixed;border-spacing:0;border-collapse:collapse\9;font-size:14px;border:1px solid #d0d0d5}.ui-table th,.ui-table thead td{background-color:#f7f9fa;border-bottom:1px solid #ededed;font-weight:400;font-style:normal;margin:0}.ui-table th:not([class]):not([align]),.ui-table thead td:not([class]):not([align]){text-align:left}.ui-table tbody td{background-color:#fff;border-bottom:1px solid #ededed}.ui-table td,.ui-table th{padding-top:14px;padding-bottom:14px;padding-right:20px}.ui-table td:first-child,.ui-table th:first-child{padding-left:20px}.ui-table tr{cursor:default}.ui-table tr:last-child td{padding-bottom:15px;border-bottom:0}.ui-table~.ui-loading{height:300px}textarea{font-family:inherit}.ui-textarea>textarea,textarea.ui-textarea{line-height:20px;padding:9px 8px;border:1px solid #d0d0d5;border-radius:4px;background-color:#fff;outline:none;color:#4c5161;font-size:14px;transition:border-color .15s,background-color .15s;word-break:break-all;vertical-align:top;box-sizing:border-box;resize:none;overflow:auto}textarea[resize]{resize:both}textarea[resize=vertical]{resize:vertical}textarea[resize=horizontal]{resize:horizontal}div.ui-textarea>textarea{width:100%}.ui-textarea:hover,.ui-textarea>textarea:hover{border-color:#ababaf}.ui-textarea:focus,.ui-textarea>textarea:focus{border-color:#2a80eb}.ui-textarea-x{position:relative;padding:2px 9px 32px 3px;border-radius:4px;box-sizing:border-box;overflow:hidden}.ui-textarea-x>textarea{display:block;width:100%;line-height:20px;border:0;padding:7px 0 9px;right:-6px;color:#4c5161;font-size:14px;outline:none;background:none;word-break:break-all;overflow:auto;resize:none;position:relative;z-index:1}.ui-textarea-x>.ui-textarea{position:absolute;border:1px solid #d0d0d5;border-radius:4px;background-color:#fff;top:0;bottom:0;left:0;right:0;transition:border-color .15s,background-color .15s}.ui-textarea-x .ui-textarea-count{position:absolute;left:8px;right:10px;bottom:0;line-height:32px;color:#a2a9b6;font-size:12px;text-align:right;z-index:1}.ui-textarea-count slash{margin:0 1px}.ui-textarea-x:hover>.ui-textarea{border-color:#ababaf}.ui-textarea-x>textarea:focus~.ui-textarea{border-color:#2a80eb}.ui-textarea-x .ui-placeholder{padding:7px!important;z-index:1}.ui-textarea-resize{width:17px;height:17px;position:absolute;background:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cpath d='M765.558 510.004a93.65 93.65 0 10191.665 0 93.65 93.65 0 10-191.665 0zm0 311.456a93.65 93.65 0 10191.665 0 93.65 93.65 0 10-191.665 0zm-343.401 0a93.65 93.65 0 10191.665 0 93.65 93.65 0 10-191.665 0zm0-311.456a93.65 93.65 0 10191.665 0 93.65 93.65 0 10-191.665 0zM765.558 202.54a93.65 93.65 0 10191.665 0 93.65 93.65 0 10-191.665 0zM66.777 821.46a93.65 93.65 0 10191.665 0 93.65 93.65 0 10-191.665 0z' fill='%23BFBFBF'/%3E%3C/svg%3E") no-repeat 100% 100%;background-size:100%}.ui-textarea-x>textarea:disabled~.ui-textarea,.ui-textarea-x>textarea[readonly]~.ui-textarea,.ui-textarea:disabled,.ui-textarea>textarea:disabled,.ui-textarea>textarea[readonly],.ui-textarea[readonly]{background-color:#f7f9fa}.ui-textarea-x>[readonly]~.ui-textarea,.ui-textarea>textarea[readonly],.ui-textarea[readonly]{background-color:#fff}.ui-textarea:disabled:hover,.ui-textarea>textarea:disabled:hover,.ui-textarea>textarea[readonly]:focus,.ui-textarea>textarea[readonly]:hover,.ui-textarea[readonly]:focus,.ui-textarea[readonly]:hover{border-color:#d0d0d5}textarea:disabled,textarea[readonly]{resize:none}.ui-textarea.error,.ui-textarea>.error{border-color:#eb4646!important}.ui-textarea-count.error,.ui-textarea-count>.error{color:#eb4646}.ui-tips[data-title]{text-indent:0;position:relative;overflow:visible}.ui-tips[data-title]:after,.ui-tips[data-title]:before{position:absolute;left:50%;-ms-pointer-events:none;pointer-events:none;-ms-transform:translateX(-50%);transform:translateX(-50%);visibility:hidden}.ui-tips[data-title]:before{content:attr(data-title);bottom:calc(100% + 12px);max-width:250px;padding:6px 10px;line-height:18px;border-radius:3px;background-color:#373c42;text-align:left;color:#fff;font-size:12px;font-style:normal;white-space:nowrap}.ui-tips[data-title]:after{content:"";border:6px solid transparent;border-top-color:#373c42;bottom:100%}.ui-tips[data-title]:focus:after,.ui-tips[data-title]:focus:before,.ui-tips[data-title]:hover:after,.ui-tips[data-title]:hover:before{transition:visibility .1s .1s;visibility:visible}.ui-tips[data-title]:hover{outline:none}.reverse.ui-tips[data-title]:before{bottom:auto;top:calc(100% + 12px)}.reverse.ui-tips[data-title]:after{border-color:transparent transparent #373c42;bottom:auto;top:100%}.ui-tips-x{text-align:center;position:absolute;z-index:99}.ui-tips-content{display:block;max-width:20em;padding:6px 10px;line-height:18px;border-radius:3px;background-color:#373c42;color:#fff;font-size:12px;font-style:normal;text-align:left}.ui-tips-arrow{position:absolute;top:100%;left:calc(50% - 6px);border:6px solid transparent;border-top-color:#373c42}@media (any-hover:none){html{--hoverNone:"true"}}.ui-tips-x[data-direction=top]{margin-top:-12px}.ui-tips-x[data-direction=left]{margin-left:-12px}.ui-tips-x[data-direction=right]{margin-left:12px}.ui-tips-x[data-direction=bottom]{margin-top:12px}[data-direction=bottom] .ui-tips-arrow{top:-12px;border-color:transparent transparent #373c42}[data-direction=left] .ui-tips-arrow,[data-direction=right] .ui-tips-arrow{top:calc(50% - 6px)}[data-direction=left] .ui-tips-arrow{left:100%;border-color:transparent transparent transparent #373c42}[data-direction=right] .ui-tips-arrow{left:-12px;border-color:transparent #373c42 transparent transparent}[data-align="6-8"] .ui-tips-content,[data-align="8-6"] .ui-tips-content{max-width:600px}[data-align="2-3"] .ui-tips-content,[data-align="3-2"] .ui-tips-content{position:relative;left:10px}[data-align="1-4"] .ui-tips-content,[data-align="4-1"] .ui-tips-content{position:relative;left:-10px}.ui-tips-error:not(.none){animation:fadeIn .2s,fallDown .2s}.ui-tips-error .ui-tips-content{background-color:#eb4646}.ui-tips-error .ui-tips-arrow{border-top-color:#eb4646}.table-x{background-color:#fff;border:1px solid #d0d0d5}.table-x table{border:0}.table-error-x,.table-null-x{height:300px;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.table-error-x{line-height:300px}.table-null,.table-null-x:after{display:inline-block;vertical-align:middle}.table-null-x:after{content:"";width:0;height:100%}.table-null-x:empty:before{content:"暂无数据"}.table-error-x:empty:before{content:"数据获取失败"}.table-page-x{padding:10px 10px 10px 20px;margin-top:-1px;border-top:1px solid #ededed;background-color:#f7f9fa;position:relative;overflow:hidden}@media screen and (max-width:640px){.table-page-x{padding-left:10px;padding-right:5px}}.table-page-data{float:left;line-height:20px;padding-top:5px;font-size:14px}.table-page-total{margin:0 3px}.table-page-per{display:inline-block;margin-left:5px;cursor:pointer}.table-page-per:after{content:"";display:inline-block;width:20px;height:20px;background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-205 197 200 400'%3E%3Cpath d='M-59.7 271.6c-4.2-4.1-11-4.1-15.3 0l-30.5 29.6-30.5-29.6c-4.2-4.1-11-4.1-15.3 0-4.2 4.1-4.2 10.7 0 14.8l38.2 37c4.2 4.1 11 4.1 15.3 0l38.2-37c4.1-4.1 4.1-10.7-.1-14.8z' fill='%23a2a9b6'/%3E%3Cpath d='M-58.7 471.6c-4.2-4.1-11-4.1-15.3 0l-30.5 29.6-30.5-29.6c-4.2-4.1-11-4.1-15.3 0-4.2 4.1-4.2 10.7 0 14.8l38.2 37c4.2 4.1 11 4.1 15.3 0l38.2-37c4.1-4.1 4.1-10.7-.1-14.8z' fill='%232a80eb'/%3E%3C/svg%3E") no-repeat;background-size:20px 40px;vertical-align:-5px}.table-page-per:hover:after{color:#2a80eb;background-position:0 -20px}.table-page{float:right;padding-top:2px}.table-checkbox td:first-child,.table-checkbox th:first-child{width:20px;padding-right:0;padding-left:18px;text-align:right}.table-checkbox td:first-child+td,.table-checkbox th:first-child+th{padding-left:13px}.table-checkbox tr:hover td{background-color:#f0f7ff}.table-checkbox tr.selected td{background-color:#e0f0ff}.table-header{padding:12px 20px;border-bottom:1px solid #ededed}.table-title{line-height:56px;font-size:24px;margin:0}ul{margin:0;padding:0}li{list-style-type:none}body,html,nav,nav ul{height:100%;overflow:hidden}body{background-color:#eef0f5}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}body .ui-input>input,body input.ui-input{height:30px}body .ui-button{min-width:65px;line-height:10px}body button.ui-button,body input.ui-button{height:30px}.container .ui-switch{width:26px;height:16px}.container :checked+.ui-switch:before{margin-left:10px}.container .ui-switch:before{width:12px;height:12px}body .ui-lightip{width:20em;padding:10px 1em;opacity:.8}.container{height:100%;width:100%;position:relative;display:flex;justify-content:space-between}.container nav{flex:0 0 200px;height:auto;width:200px;position:relative}.container nav ul{padding:4px 2px 2px 0;overflow:auto}.container nav ul::-webkit-scrollbar{width:3px}.container nav ul::-webkit-scrollbar-thumb{border-radius:10px;background:#bcd}.container nav ul::-webkit-scrollbar-track{border-radius:0;background:#eef0f5}.container nav li{cursor:default;font-size:13px;height:40px;line-height:40px;padding-left:32px;position:relative;background:url(record.56e10638.svg) no-repeat 10px;background-size:15px;color:#666;border-left:3px solid #eef0f5;opacity:.6}.container nav li.home{background:url(home.3a7af05d.svg) no-repeat 10px;background-size:15px}.container nav li.net{background:url(recordFind.08a261bc.svg) no-repeat 10px;background-size:15px}.container nav li.active,.container nav li.mouse,.container nav li:hover{background-color:#bedff7;border-left:3px solid #51b0f5}.container nav li.active,.container nav li:hover{opacity:1;border-radius:2px}.container nav li:hover .option{cursor:pointer;position:absolute;width:18px;height:40px;right:43px;top:0;background:url(edit.e5e6ea37.svg) no-repeat 50%;background-size:15px;opacity:.5}.container nav li:hover .option:hover{opacity:.9}.container nav li .onoff{position:absolute;right:10px;top:4px}.container nav:after{content:"LnnCoCo";position:absolute;bottom:2px;left:2px;color:hsla(0,0%,80%,.6);font-size:12px;transform:scale(.9);cursor:default}.container main{flex:1}.container main .textarea{background-color:#fff;border:1px solid #ddd;border-radius:3px;height:98%;margin-top:3px;margin-right:3px;box-shadow:0 0 1px rgba(0,0,0,.1)}.container main .textarea textarea{height:100%;width:100%;border:none;outline:none;font-size:15px}.container main .textarea.local .CodeMirror:before{content:"";position:absolute;width:50%;height:62%;right:5px;bottom:5px;opacity:.06;background:url(record.56e10638.svg) no-repeat 100% 100%;background-size:contain}.container main .textarea.net .CodeMirror:before{content:"";position:absolute;width:50%;height:62%;right:5px;bottom:5px;opacity:.06;background:url(recordFind.08a261bc.svg) no-repeat 100% 100%;background-size:contain}.container main .textarea.lock .CodeMirror:before{content:"";position:absolute;width:50%;height:62%;right:5px;bottom:5px;opacity:.06;background:url(lock.623ff62a.svg) no-repeat 100% 100%;background-size:contain}.container main .textarea.home .CodeMirror:before{content:"";position:absolute;width:50%;height:62%;right:5px;bottom:5px;opacity:.06;background:url(home.3a7af05d.svg) no-repeat 100% 100%;background-size:contain}.container main .textarea.home .operation{display:block}.container main .textarea.home .operation .refresh{display:inline-block}.container main .textarea.net .operation{display:block}.container main .textarea.net .operation .net{display:inline-block}.container main .textarea.save .operation{display:block}.container main .textarea.save .operation .save{display:inline-block}.container main .textarea .operation{display:none;position:absolute;z-index:110;top:17px;right:22px;background-color:hsla(0,0%,74.1%,.226);height:20px;padding:2px 13px;border-radius:16px;transition:all .2s}.container main .textarea .operation .refresh{display:none;width:17px;height:20px;opacity:.2;margin:0 3px;background:url(refresh.0b748f9a.svg) no-repeat 50%;background-size:contain}.container main .textarea .operation .refresh:hover{cursor:pointer;opacity:.6}.container main .textarea .operation .net{display:none;width:17px;height:20px;opacity:.2;margin:0 3px;background:url(download.779c3df5.svg) no-repeat 50%;background-size:contain}.container main .textarea .operation .net:hover{cursor:pointer;opacity:.6}.container main .textarea .operation .save{display:none;width:17px;height:20px;opacity:.2;margin:0 3px;background:url(save.8ed52c9e.svg) no-repeat 50%;background-size:contain}.container main .textarea .operation .save:hover{cursor:pointer;opacity:.6}.dialog-item{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:360px;background-color:#fff;border:1px solid #ccc;border-radius:3px;box-shadow:0 2px 4px rgba(0,0,0,.15);display:none;z-index:100}.dialog-item.active{display:block}.dialog-item .item-hader{height:40px;line-height:40px;padding-left:15px;border-bottom:1px solid #e8e8e8;font-size:15px}.dialog-item .item-operation{overflow:hidden;padding:10px;border-top:1px solid #e8e8e8}.dialog-item .item-operation .ui-button{float:right}.dialog-item .item-operation .ui-button+.ui-button{margin-right:10px}.dialog-item .item-body{padding:20px 40px;font-size:14px}.dialog-item .item-body .form-item+.form-item{margin-top:15px}.dialog-item .item-body input{width:200px;font-size:13px}.shade{display:none;position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.3);z-index:99}.context-menu{position:absolute;width:111px;padding:5px 0;background-color:#fff;box-shadow:0 2px 4px rgba(0,0,0,.25);border:1px solid #d0d0d5;border-radius:2px;font-size:14px;animation:fadeIn .2s;z-index:200;left:-99999px;top:-99999px}.context-menu .menu-item{display:block;line-height:20px;padding:8px 10px 7px;color:#4c5161;text-decoration:none;cursor:pointer;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.context-menu .menu-item:hover{background-color:#f0f7ff}.context-menu .menu-item.disabled{cursor:default;pointer-events:none;opacity:.4}.context-menu .menu-item.disabled:hover{background-color:transparent}.context-menu .menu-item .icon{display:inline-block;width:13px;height:13px;transform:translateY(2px)}.context-menu .menu-item .icon img{width:100%;height:100%}.context-menu .menu-line{border:0;border-bottom:1px solid #d0d0d5;margin:7px 11px;opacity:.4}.context-menu .menu-target{cursor:default;padding-top:2px;padding-bottom:6px;font-size:12px;color:hsla(0,0%,72.5%,.6901960784313725);height:12px;line-height:12px;transform:scale(.8)}.CodeMirror{font-family:monospace;height:300px;color:#000;direction:ltr}.CodeMirror-lines{padding:4px 0}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{padding:0 4px}.CodeMirror-gutter-filler,.CodeMirror-scrollbar-filler{background-color:#fff}.CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999;white-space:nowrap}.CodeMirror-guttermarker{color:#000}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror-cursor{border-left:1px solid #000;border-right:none;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{width:auto;border:0!important;background:#7e7}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-fat-cursor-mark{background-color:rgba(20,255,20,.5)}.cm-animate-fat-cursor,.cm-fat-cursor-mark{-webkit-animation:blink 1.06s steps(1) infinite;-moz-animation:blink 1.06s steps(1) infinite;animation:blink 1.06s steps(1) infinite}.cm-animate-fat-cursor{width:auto;border:0;background-color:#7e7}@-moz-keyframes blink{50%{background-color:transparent}}@-webkit-keyframes blink{50%{background-color:transparent}}@keyframes blink{50%{background-color:transparent}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-rulers{position:absolute;left:0;right:0;top:-50px;bottom:0;overflow:hidden}.CodeMirror-ruler{border-left:1px solid #ccc;top:0;bottom:0;position:absolute}.cm-s-default .cm-header{color:#00f}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:700}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-type,.cm-s-default .cm-variable-3{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta,.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-invalidchar,.cm-s-default .cm-error{color:red}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0b0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#a22}.CodeMirror-matchingtag{background:rgba(255,150,0,.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:#fff}.CodeMirror-scroll{overflow:scroll!important;margin-bottom:-50px;margin-right:-50px;padding-bottom:50px;height:100%;outline:none;position:relative}.CodeMirror-sizer{position:relative;border-right:50px solid transparent}.CodeMirror-gutter-filler,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-vscrollbar{position:absolute;z-index:6;display:none;outline:none}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;min-height:100%;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;vertical-align:top;margin-bottom:-50px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:none!important;border:none!important}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-gutter-wrapper ::selection{background-color:transparent}.CodeMirror-gutter-wrapper ::-moz-selection{background-color:transparent}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:transparent;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent;-webkit-font-variant-ligatures:contextual;font-variant-ligatures:contextual}.CodeMirror-wrap pre.CodeMirror-line,.CodeMirror-wrap pre.CodeMirror-line-like{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;padding:.1px}.CodeMirror-rtl pre{direction:rtl}.CodeMirror-code{outline:none}.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber,.CodeMirror-scroll,.CodeMirror-sizer{-moz-box-sizing:content-box;box-sizing:content-box}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-cursor{position:absolute;pointer-events:none}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}.CodeMirror-focused div.CodeMirror-cursors,div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:#d7d4f0}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.cm-searching{background-color:#ffa;background-color:rgba(255,255,0,.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:""}span.CodeMirror-selectedtext{background:none}.cm-s-hosts.CodeMirror{background-color:#fff;color:#2e383c;line-height:1.4375}.cm-s-hosts .cm-comment{color:#75787b}.cm-s-hosts .cm-keyword,.cm-s-hosts .cm-property{color:#1d75b3}.cm-s-hosts .cm-atom,.cm-s-hosts .cm-number{color:rgba(29,57,196,.7686274509803922)}.cm-s-hosts .cm-node,.cm-s-hosts .cm-tag{color:#9c3328}.cm-s-hosts .cm-string{color:#b35e14}.cm-s-hosts .cm-qualifier,.cm-s-hosts .cm-variable{color:rgba(4,125,101,.8274509803921568)}.cm-s-hosts pre{padding:0}.cm-s-hosts .CodeMirror-gutters{border:none;border-right:10px solid transparent;background-color:transparent}.cm-s-hosts .CodeMirror-linenumber{padding:0;color:#e0e2e5}.cm-s-hosts .CodeMirror-guttermarker{color:#1d75b3}.cm-s-hosts .CodeMirror-guttermarker-subtle{color:#e0e2e5}.cm-s-hosts .CodeMirror-cursor{width:1px;border:0;background:rgba(155,157,162,.6);z-index:1}.cm-s-hosts .CodeMirror-activeline-background{background:rgba(232,242,255,.5)}.cm-s-hosts div.CodeMirror-selected{background:rgba(232,242,255,.8)}.CodeMirror-simplescroll-horizontal div,.CodeMirror-simplescroll-vertical div{position:absolute;background:#ccc;-moz-box-sizing:border-box;box-sizing:border-box;border:1px solid #bbb;border-radius:2px}.CodeMirror-simplescroll-horizontal,.CodeMirror-simplescroll-vertical{position:absolute;z-index:6;background:#eee}.CodeMirror-simplescroll-horizontal{bottom:0;left:0;height:8px}.CodeMirror-simplescroll-horizontal div{bottom:0;height:100%}.CodeMirror-simplescroll-vertical{right:0;top:0;width:8px}.CodeMirror-simplescroll-vertical div{right:0;width:100%}.CodeMirror-overlayscroll .CodeMirror-gutter-filler,.CodeMirror-overlayscroll .CodeMirror-scrollbar-filler{display:none}.CodeMirror-overlayscroll-horizontal div,.CodeMirror-overlayscroll-vertical div{position:absolute;background:#bcd;border-radius:3px}.CodeMirror-overlayscroll-horizontal,.CodeMirror-overlayscroll-vertical{position:absolute;z-index:6}.CodeMirror-overlayscroll-horizontal{bottom:0;left:0;height:6px}.CodeMirror-overlayscroll-horizontal div{bottom:0;height:100%}.CodeMirror-overlayscroll-vertical{right:0;top:0;width:6px}.CodeMirror-overlayscroll-vertical div{right:0;width:100%}.CodeMirror-dialog{position:absolute;left:0;right:0;background:inherit;z-index:15;padding:.1em .8em;overflow:hidden;color:inherit}.CodeMirror-dialog-top{border-bottom:1px solid #eee;top:0}.CodeMirror-dialog-bottom{border-top:1px solid #eee;bottom:0}.CodeMirror-dialog input{border:none;outline:none;background:transparent;width:20em;color:inherit;font-family:monospace}.CodeMirror-dialog button{font-size:70%}
--------------------------------------------------------------------------------
/web/record.56e10638.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/recordFind.08a261bc.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/refresh.0b748f9a.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/save.8ed52c9e.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/subtraction.600c820b.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/unlock.6ef52b3f.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web_source/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | .cache
4 | dist
5 | package-lock.json
--------------------------------------------------------------------------------
/web_source/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 200,
3 | "tabWidth": 2,
4 | "semi": true,
5 | "useTabs": false,
6 | "singleQuote": true,
7 | "bracketSpacing": true
8 | }
--------------------------------------------------------------------------------
/web_source/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hosts-switch",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "main.js",
6 | "dependencies": {
7 | "@types/codemirror": "0.0.102",
8 | "aardio": "^2.6.0",
9 | "codemirror": "^5.58.3",
10 | "lu2": "^2020.11.23",
11 | "normalize.css": "^8.0.1"
12 | },
13 | "devDependencies": {
14 | "@types/node": "^14.14.13",
15 | "less": "^3.13.0",
16 | "parcel-bundler": "^1.12.4",
17 | "parcel-plugin-clean-dist": "0.0.6",
18 | "typescript": "^4.1.3"
19 | },
20 | "scripts": {
21 | "dev": "parcel src/index.html --port 6060",
22 | "build": "parcel build src/index.html --no-source-maps -d ../web --out-file index.aardio --public-url /web/"
23 | },
24 | "keywords": [],
25 | "author": "LnnCoCo",
26 | "license": "ISC"
27 | }
28 |
--------------------------------------------------------------------------------
/web_source/src/asset/addition.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web_source/src/asset/download.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web_source/src/asset/edit.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web_source/src/asset/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lnncoco/hosts-switch/bc6dfc271831091629d6bc30f4e2b651a6409157/web_source/src/asset/favicon.png
--------------------------------------------------------------------------------
/web_source/src/asset/flag.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web_source/src/asset/home.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web_source/src/asset/interpretation.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web_source/src/asset/link.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web_source/src/asset/lock.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web_source/src/asset/menu.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web_source/src/asset/record.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web_source/src/asset/recordFind.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web_source/src/asset/refresh.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web_source/src/asset/save.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web_source/src/asset/subtraction.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web_source/src/asset/unlock.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web_source/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | HostsSwitch
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
25 |
26 |
27 |
28 |
29 |
30 |
修改配置
31 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
50 |
51 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/web_source/src/js/CodeMirror/hosts.css:
--------------------------------------------------------------------------------
1 | /* hosts theme for codemirror */
2 |
3 | /* Color scheme */
4 |
5 | .cm-s-hosts.CodeMirror {
6 | background-color: #ffffff;
7 | color: #2e383c;
8 | line-height: 1.4375;
9 | }
10 | .cm-s-hosts .cm-comment {
11 | color: #75787b;
12 | }
13 | .cm-s-hosts .cm-keyword,
14 | .cm-s-hosts .cm-property {
15 | color: #1d75b3;
16 | }
17 | .cm-s-hosts .cm-atom,
18 | .cm-s-hosts .cm-number {
19 | color: #1d39c4c4;
20 | }
21 | .cm-s-hosts .cm-node,
22 | .cm-s-hosts .cm-tag {
23 | color: #9c3328;
24 | }
25 | .cm-s-hosts .cm-string {
26 | color: #b35e14;
27 | }
28 | .cm-s-hosts .cm-variable,
29 | .cm-s-hosts .cm-qualifier {
30 | color: #047d65d3;
31 | }
32 |
33 | /* Editor styling */
34 |
35 | .cm-s-hosts pre {
36 | padding: 0;
37 | }
38 |
39 | .cm-s-hosts .CodeMirror-gutters {
40 | border: none;
41 | border-right: 10px solid transparent;
42 | background-color: transparent;
43 | }
44 |
45 | .cm-s-hosts .CodeMirror-linenumber {
46 | padding: 0;
47 | color: #e0e2e5;
48 | }
49 |
50 | .cm-s-hosts .CodeMirror-guttermarker {
51 | color: #1d75b3;
52 | }
53 | .cm-s-hosts .CodeMirror-guttermarker-subtle {
54 | color: #e0e2e5;
55 | }
56 |
57 | .cm-s-hosts .CodeMirror-cursor {
58 | width: 1px;
59 | border: 0;
60 | background: rgba(155, 157, 162, 0.6);
61 | z-index: 1;
62 | }
63 |
64 | .cm-s-hosts .CodeMirror-activeline-background {
65 | background: rgb(232, 242, 255, 0.5);
66 | }
67 | .cm-s-hosts div.CodeMirror-selected {
68 | background: rgb(232, 242, 255, 0.8);
69 | }
70 |
--------------------------------------------------------------------------------
/web_source/src/js/CodeMirror/index.ts:
--------------------------------------------------------------------------------
1 | import * as CodeMirror from 'codemirror/lib/codemirror';
2 | import 'codemirror/lib/codemirror.css';
3 | // 语法逻辑
4 | import './modeHosts';
5 | // 主题样式
6 | import './hosts.css';
7 | // 高亮当前行
8 | import 'codemirror/addon/selection/active-line';
9 | // 滚动条
10 | import 'codemirror/addon/scroll/simplescrollbars';
11 | import 'codemirror/addon/scroll/simplescrollbars.css';
12 | // 搜索
13 | import 'codemirror/addon/dialog/dialog';
14 | import 'codemirror/addon/dialog/dialog.css';
15 | import 'codemirror/addon/scroll/annotatescrollbar';
16 | import 'codemirror/addon/search/matchesonscrollbar';
17 | import 'codemirror/addon/search/jump-to-line';
18 | import 'codemirror/addon/search/search';
19 | import 'codemirror/addon/search/searchcursor';
20 | // 相关事件
21 | import { changeTextareaTip, eventTextareaSave } from '../textarea';
22 |
23 | let target = null;
24 | let CodeMirrorEditor = null;
25 | const options = {
26 | mode: 'hosts',
27 | theme: 'hosts',
28 | tabSize: 2,
29 | readOnly: false, // 只读模式
30 | lineNumbers: true, //显示行号
31 | styleActiveLine: true, // 高亮当前行
32 | lineWrapping: true, // 换行
33 | scrollbarStyle: 'overlay', // 滚动条样式
34 | extraKeys: {
35 | 'Ctrl-S': eventTextareaSave,
36 | },
37 | };
38 |
39 | /**
40 | * 初始化文本编辑器
41 | * @param {boolean} readOnly 是否只读
42 | */
43 | function init(readOnly: boolean = false) {
44 | options.readOnly = readOnly;
45 | target = document.getElementById('textarea');
46 | CodeMirrorEditor = CodeMirror.fromTextArea(target, options);
47 | CodeMirrorEditor.setSize('100%', '100%');
48 | CodeMirrorEditor.on('change', changeTextareaTip);
49 | }
50 |
51 | /**
52 | * 设置编辑器数据
53 | * @param {string} text 文本内容
54 | * @param {boolean} readOnly 是否只读
55 | */
56 | function setValue(text: string, readOnly: boolean = false) {
57 | CodeMirrorEditor.setValue(text);
58 | if (typeof readOnly === 'boolean') CodeMirrorEditor.setOption('readOnly', readOnly);
59 | }
60 |
61 | /**
62 | * 获取编辑器中文本数据
63 | */
64 | function getValue(): string {
65 | return CodeMirrorEditor.getValue();
66 | }
67 |
68 | /**
69 | * 设置当前项为只读
70 | * @param {boolean} readOnly 是否只读
71 | */
72 | function setReadOnly(readOnly: boolean) {
73 | return CodeMirrorEditor.setOption('readOnly', readOnly);
74 | }
75 |
76 | export default {
77 | target: CodeMirrorEditor,
78 | init,
79 | setValue,
80 | getValue,
81 | setReadOnly,
82 | };
83 |
--------------------------------------------------------------------------------
/web_source/src/js/CodeMirror/modeHosts.js:
--------------------------------------------------------------------------------
1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 | // Distributed under an MIT license: https://codemirror.net/LICENSE
3 |
4 | (function (mod) {
5 | if (typeof exports == 'object' && typeof module == 'object')
6 | // CommonJS
7 | mod(require('codemirror/lib/codemirror'));
8 | else if (typeof define == 'function' && define.amd)
9 | // AMD
10 | define(['codemirror/lib/codemirror'], mod);
11 | // Plain browser env
12 | else mod(CodeMirror);
13 | })(function (CodeMirror) {
14 | 'use strict';
15 |
16 | CodeMirror.defineMode('hosts', function (config, parserConfig) {
17 | var indentUnit = config.indentUnit;
18 | var curPunc;
19 |
20 | function tokenBase(stream, state) {
21 | var ch = stream.next();
22 | if (ch == '#') {
23 | stream.skipToEnd();
24 | return 'comment';
25 | }
26 |
27 | if (/\d/.test(ch)) {
28 | stream.eatWhile(/[\w\.]/);
29 | return 'number';
30 | }
31 |
32 | stream.eatWhile(/[\w\$_]/);
33 | return 'variable';
34 | }
35 |
36 | function Context(indented, column, type, align, prev) {
37 | this.indented = indented;
38 | this.column = column;
39 | this.type = type;
40 | this.align = align;
41 | this.prev = prev;
42 | }
43 |
44 | //Interface
45 | return {
46 | startState: function (basecolumn) {
47 | return {
48 | tokenize: null,
49 | context: new Context((basecolumn || 0) - indentUnit, 0, 'top', false),
50 | indented: 0,
51 | startOfLine: true,
52 | };
53 | },
54 |
55 | token: function (stream, state) {
56 | var ctx = state.context;
57 | if (stream.sol()) {
58 | if (ctx.align == null) ctx.align = false;
59 | state.indented = stream.indentation();
60 | state.startOfLine = true;
61 | }
62 | if (stream.eatSpace()) return null;
63 | curPunc = null;
64 | var style = (state.tokenize || tokenBase)(stream, state);
65 | if (style == 'comment') return style;
66 | if (ctx.align == null) ctx.align = true;
67 |
68 | if (curPunc == ctx.type) {
69 | state.context = state.context.prev;
70 | }
71 | state.startOfLine = false;
72 | return style;
73 | },
74 |
75 | electricChars: '{}',
76 | lineComment: '#',
77 | fold: 'brace',
78 | };
79 | });
80 |
81 | CodeMirror.defineMIME('hosts', {
82 | name: 'hosts',
83 | multiLineStrings: true,
84 | });
85 | });
86 |
--------------------------------------------------------------------------------
/web_source/src/js/bus.ts:
--------------------------------------------------------------------------------
1 | interface IBus {
2 | [key: string]: any;
3 | }
4 | const bus: IBus = {};
5 |
6 | export default bus;
7 |
--------------------------------------------------------------------------------
/web_source/src/js/contextMenu.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import BUS from './bus';
4 | import template from '~/lib/template';
5 | import dataAction from './dataAction';
6 | import CodeMirror from './CodeMirror';
7 | import { modifyTextareaLockState } from './textarea';
8 | import { refreshDnsCache } from './util';
9 | import { openDialog, setDialog, closeDialog, dialogApply } from './dialog';
10 | import iconModify from '~/asset/edit.svg';
11 | import iconRefresh from '~/asset/refresh.svg';
12 | import iconDownload from '~/asset/download.svg';
13 | import iconRemove from '~/asset/subtraction.svg';
14 | import iconAddition from '~/asset/addition.svg';
15 | import iconLock from '~/asset/lock.svg';
16 | import iconUnlock from '~/asset/unlock.svg';
17 |
18 | const contextMenu = {
19 | li: function (itemData: DATA.item) {
20 | const lock = { line: false, text: '锁定此项', icon: iconLock, action: 'lock' };
21 | const unlock = { line: false, text: '解锁此项', icon: iconUnlock, action: 'unlock' };
22 | const list = [
23 | { line: false, text: '修改参数', icon: iconModify, action: 'modify' },
24 | { line: false, text: '更新数据', icon: iconDownload, action: 'update' },
25 | { line: true },
26 | { line: false, text: '添加新项', icon: iconAddition, action: 'add' },
27 | { line: false, text: '删除此项', icon: iconRemove, action: 'remove' },
28 | ];
29 |
30 | // 固定插入在第一层次的最后
31 | let index = 0;
32 | for (const item of list) {
33 | if (item.line) {
34 | if (itemData.lock) list.splice(index, 0, unlock);
35 | else list.splice(index, 0, lock);
36 | break;
37 | }
38 | index++;
39 | }
40 |
41 | return list;
42 | },
43 | nav: function () {
44 | return [
45 | { line: false, text: '添加新项', icon: iconAddition, action: 'add' },
46 | { line: false, text: '全部更新', icon: iconDownload, action: 'updateAll' },
47 | { line: false, text: '刷新缓存', icon: iconRefresh, action: 'refresh' },
48 | ];
49 | },
50 | };
51 |
52 | const actionFn = {
53 | lock: function () {
54 | dataAction.modify(BUS.mouseTargetId, { lock: true }, () => {
55 | const activeData = dataAction.getActionItemInfo();
56 | if (activeData.id.toString() === BUS.mouseTargetId.toString()) {
57 | modifyTextareaLockState(true);
58 | CodeMirror.setReadOnly(true);
59 | }
60 | });
61 | },
62 | unlock: function () {
63 | dataAction.modify(BUS.mouseTargetId, { lock: false }, () => {
64 | const activeData = dataAction.getActionItemInfo();
65 | if (activeData.id.toString() === BUS.mouseTargetId.toString()) {
66 | modifyTextareaLockState(false);
67 | CodeMirror.setReadOnly(false);
68 | }
69 | });
70 | },
71 | modify: function () {
72 | const activeData = dataAction.getItemInfo(BUS.mouseTargetId);
73 | setDialog(activeData.name, activeData.url);
74 | openDialog();
75 | },
76 | add: function () {
77 | dataAction.add().active('-1').render(); // active到最新项
78 | },
79 | remove: function () {
80 | dataAction.remove(BUS.mouseTargetId).render();
81 | BUS.mouseTargetId = null; // 清除当前id指向
82 | },
83 | update: function () {
84 | dataAction.asyncUpdateHosts(BUS.mouseTargetId);
85 | },
86 | updateAll: function () {
87 | dataAction.asyncUpdateAllHosts();
88 | },
89 | refresh: function () {
90 | refreshDnsCache();
91 | },
92 | };
93 |
94 | /**
95 | * 初始化操作
96 | */
97 | function init() {
98 | window.oncontextmenu = function (event: MouseEvent) {
99 | event.preventDefault();
100 | show(event);
101 | return false;
102 | };
103 | // 单击dialog 取消按钮
104 | document.querySelector('#dialog_cancel').addEventListener('click', closeDialog, false);
105 | // 单击dialog 确定按钮
106 | document.querySelector('#dialog_apply').addEventListener('click', dialogApply, false);
107 |
108 | document.addEventListener('click', close);
109 | BUS.contextMenu.addEventListener('click', function (e) {
110 | for (const item of e.path) {
111 | if (item.tagName === 'LI') {
112 | const action = item.dataset.action;
113 | if (actionFn[action]) actionFn[action]();
114 | break;
115 | }
116 | }
117 | });
118 | }
119 |
120 | /**
121 | * 当前鼠标右键激活项增删指示状态
122 | * @param {boolean} status
123 | * @param {HTMLElement} target 要处理的DOM对象
124 | */
125 | function curMouseItemStatus(status: boolean, target?: HTMLElement) {
126 | if (status) return target.classList.add('mouse');
127 | document.querySelector('#nav ul .mouse')?.classList.remove('mouse');
128 | }
129 |
130 | /**
131 | * 判断是否是菜单项
132 | * @param {MouseEvent} event
133 | */
134 | function isNavItem(event: MouseEvent) {
135 | return (event as any).path.some((element) => {
136 | curMouseItemStatus(false);
137 | if (element.tagName === 'LI') {
138 | // 处理类名判断是否是菜单项
139 | const className = element.className;
140 | if (className && className.split(' ').includes('nav-item')) {
141 | curMouseItemStatus(true, element); // 当前右键激活项添加类名
142 | BUS.mouseTargetId = element.dataset.id; // 存储当前节点的id
143 | const itemData = dataAction.getItemInfo(BUS.mouseTargetId); // 获取当前项的数据
144 | BUS.contextMenu.innerHTML = template('template_contextMenu', { target: itemData.name, list: contextMenu.li(itemData), isLocal: !itemData.url, isHome: itemData.home }); // 渲染li右键菜单
145 | return true;
146 | }
147 | } else if (element.tagName === 'NAV') {
148 | BUS.contextMenu.innerHTML = template('template_contextMenu', { list: contextMenu.nav() }); // 渲染nav右键菜单
149 | return true;
150 | }
151 | return false;
152 | });
153 | }
154 |
155 | /**
156 | * 显示右键菜单
157 | * @param {MouseEvent} event
158 | */
159 | function show(event: MouseEvent) {
160 | if (!isNavItem(event)) return;
161 | // 获取当前鼠标位置
162 | let mouseX = event.clientX;
163 | let mouseY = event.clientY;
164 | // 判断边界值,防止菜单栏溢出可视窗口
165 | const winHeight = document.documentElement.clientHeight ?? document.body.clientHeight;
166 | if (mouseY > winHeight - BUS.contextMenu.offsetHeight) mouseY = winHeight - BUS.contextMenu.offsetHeight - 10;
167 | else mouseY = mouseY;
168 |
169 | BUS.contextMenu.style.left = `${mouseX}px`;
170 | BUS.contextMenu.style.top = `${mouseY}px`;
171 | }
172 |
173 | /**
174 | * 关闭右键菜单
175 | */
176 | function close() {
177 | // 使用 display: none 会导致offsetHeight获取高度为0的情况
178 | BUS.contextMenu.style.left = '-99999px';
179 | BUS.contextMenu.style.top = '-99999px';
180 | BUS.mouseTarget = null;
181 | curMouseItemStatus(false);
182 | }
183 |
184 | export default { init, show, close };
185 |
--------------------------------------------------------------------------------
/web_source/src/js/dataAction/DATA.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace DATA {
2 | export interface item {
3 | home?: boolean; // 本地使用Hosts项标识
4 | id: number; // 项唯一id
5 | name: string; // 项名称
6 | active: boolean; // 是否激活显示
7 | checked: boolean; // 是否开启
8 | lock: boolean; // 是否只读 默认false
9 | url: string; // url
10 | status: boolean; // 当网络获取报错时,此项为false
11 | hosts: string; // HOSTS文本内容
12 | }
13 | export type list = item[];
14 | }
15 |
--------------------------------------------------------------------------------
/web_source/src/js/dataAction/DATA.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import CodeMirror from '../CodeMirror';
3 | import { saveSystemHosts, saveConfigData } from '../util';
4 |
5 | // 存放host渲染数据
6 | let DATA: DATA.list = [];
7 |
8 | /**
9 | * 获取DATA数据
10 | */
11 | export function iniData(data: DATA.list) {
12 | DATA = JSON.parse(JSON.stringify(data));
13 | return true;
14 | }
15 |
16 | /**
17 | * 获取DATA数据
18 | */
19 | export function getData() {
20 | return DATA;
21 | }
22 |
23 | /**
24 | * 获取当前激活项的所有信息
25 | */
26 | export function getActionItemInfo() {
27 | for (const item of DATA) {
28 | if (item.active) return item;
29 | }
30 | DATA[0].active = true; // 如果没有激活项就默认激活第一个
31 | return DATA[0];
32 | }
33 |
34 | /**
35 | * 获取指定项的所有信息
36 | * @param {string} id
37 | */
38 | export function getItemInfo(id: string) {
39 | for (const item of DATA) {
40 | if (item.id.toString() === id.toString()) return item;
41 | }
42 | }
43 |
44 | /**
45 | * 获取指定项的Hosts信息
46 | * @param {string} id
47 | */
48 | export function getItemHosts(id: string) {
49 | for (const item of DATA) {
50 | if (item.id.toString() === id.toString()) return item.hosts;
51 | }
52 | }
53 |
54 | /**
55 | * 获取本地使用项(home项)
56 | */
57 | export function getUseItemInfo() {
58 | const use = DATA[0];
59 | if (use.home) return use;
60 | for (const item of DATA) {
61 | if (item.home) return item;
62 | }
63 | return false;
64 | }
65 |
66 | /**
67 | * 添加新的项
68 | * @param {string} name
69 | * @param {function} fn
70 | */
71 | export function add(name?: string, fn?: (list: DATA.list) => any) {
72 | DATA.push({
73 | id: DATA[DATA.length - 1].id + 1,
74 | name: name ? name : `新增项${DATA.length + 1}`,
75 | active: false,
76 | checked: false,
77 | lock: false,
78 | url: '',
79 | status: true,
80 | hosts: '',
81 | });
82 | fn?.(DATA);
83 | return true;
84 | }
85 |
86 | /**
87 | * 删除指定项参数
88 | * @param {string} id
89 | * @param {function} fn
90 | */
91 | export function remove(id: string, fn?: (list: DATA.list, home: DATA.item) => any) {
92 | let curIndex = -1;
93 | DATA.forEach((item, index) => {
94 | if (item.id.toString() === id.toString()) {
95 | curIndex = index;
96 | }
97 | });
98 | if (curIndex >= 0) {
99 | DATA.splice(curIndex, 1);
100 | updateUseHosts(fn);
101 | return true;
102 | }
103 | return false;
104 | }
105 |
106 | /**
107 | * 修改指定项参数
108 | * @param {string} id
109 | * @param {Object} params
110 | */
111 | function modify(id: string, params: { name?: string; url?: string; lock?: boolean }) {
112 | for (const item of DATA) {
113 | if (item.id.toString() === id.toString()) {
114 | if (params.name) item.name = params.name;
115 | if (params.url) item.url = params.url;
116 | if (typeof params.lock === 'boolean') {
117 | item.lock = params.lock;
118 | }
119 | break;
120 | }
121 | }
122 | return true;
123 | }
124 |
125 | /**
126 | * 修改多项的多项参数
127 | * @param {function} isFn 判断是否执行的函数
128 | * @param {function} modifyFn 需要执行的函数
129 | * @param {function} finalFn 完成后执行的函数
130 | */
131 | function modifyEvery(isFn: (curItem: DATA.item) => boolean, modifyFn: (item: DATA.item, index: number) => void, finalFn?: () => void) {
132 | if (isFn) {
133 | let index = 0;
134 | for (const item of DATA) {
135 | if (isFn(item)) modifyFn?.(item, index);
136 | index++;
137 | }
138 | finalFn?.();
139 | return true;
140 | }
141 | return false;
142 | }
143 |
144 | /**
145 | * 当前项状态激活(其他项切换为关闭)
146 | * @param {string} id
147 | * @param {function} fn
148 | */
149 | export function active(id: string, fn?: (list: DATA.list, home: DATA.item) => any) {
150 | let curItem: false | DATA.item = false;
151 | let curId = id.toString();
152 | if (id === '-1') curId = (DATA.length - 1).toString(); // 激活最后一项
153 | DATA.forEach((item) => {
154 | if (item.active) item.hosts = CodeMirror.getValue();
155 | // 只能一个激活
156 | if (item.id.toString() === curId) {
157 | item.active = true;
158 | curItem = item;
159 | } else item.active = false;
160 | });
161 | return updateUseHosts(fn);
162 | }
163 |
164 | /**
165 | * 当前项Switch状态切换
166 | * @param {string} id
167 | * @param {boolean} status
168 | * @param {function} fn
169 | */
170 | export function checked(id: string, status: boolean, fn?: (list: DATA.list, home: DATA.item) => any) {
171 | DATA.forEach((item) => {
172 | // 能够开启多个 多个合并到本地HOSTS
173 | if (item.id.toString() === id.toString()) item.checked = status;
174 | });
175 | return updateUseHosts(fn);
176 | }
177 |
178 | /**
179 | * 更新使用中Hosts数据(合并switch项为true的数据)
180 | * active checked add remove 调用
181 | * @param {function} fn
182 | */
183 | export function updateUseHosts(fn?: (list: DATA.list, home: DATA.item) => any) {
184 | const use = getUseItemInfo();
185 | if (!use) return false;
186 | let hosts = '';
187 | for (const item of DATA) {
188 | if (item.checked && item.status && !item.home) {
189 | if (hosts) hosts += '\r\n\r\n';
190 | hosts += item.hosts;
191 | }
192 | }
193 | use.hosts = hosts;
194 | saveSystemHosts(use.hosts); // 应用到系统
195 | saveConfigData(DATA); // System Hosts项变了 每次更新保存整体数据
196 | if (fn) return fn(DATA, use);
197 | return true;
198 | }
199 |
200 | export default {
201 | iniData,
202 | add,
203 | remove,
204 | // 改
205 | modify,
206 | modifyEvery,
207 | active,
208 | checked,
209 | updateUseHosts,
210 | // 查
211 | getData,
212 | getItemInfo,
213 | getActionItemInfo,
214 | getItemHosts,
215 | getUseItemInfo,
216 | };
217 |
--------------------------------------------------------------------------------
/web_source/src/js/dataAction/defaultConfig.ts:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | home: true, // 本地使用Hosts项标识
4 | id: 0,
5 | name: 'System Hosts',
6 | active: true,
7 | checked: true,
8 | lock: true, // 只能自定义新项修改 保存后自动混合入系统Hosts
9 | url: '',
10 | status: true,
11 | hosts: '',
12 | },
13 | {
14 | id: 1,
15 | name: 'Local Hosts',
16 | active: false,
17 | checked: true,
18 | lock: false,
19 | url: '',
20 | status: true,
21 | hosts: '',
22 | },
23 | {
24 | id: 2,
25 | name: 'Github Hosts',
26 | active: false,
27 | checked: false,
28 | lock: false,
29 | url: 'https://gitee.com/xueweihan/codes/6g793pm2k1hacwfbyesl464/raw?blob_name=GitHub520.yml',
30 | status: true,
31 | hosts: '',
32 | },
33 | {
34 | id: 3,
35 | name: 'Google Hosts',
36 | active: false,
37 | checked: false,
38 | lock: false,
39 | url: 'https://raw.githubusercontent.com/googlehosts/hosts/master/hosts-files/hosts',
40 | status: true,
41 | hosts: '',
42 | },
43 | ];
44 |
--------------------------------------------------------------------------------
/web_source/src/js/dataAction/index.ts:
--------------------------------------------------------------------------------
1 | import { getSystemHosts, requestHosts, backupLocalHosts, getConfigData, saveConfigData, tipSuccess, tipWarning, tipError } from '../util';
2 | import defaultConfig from './defaultConfig';
3 | // 内部组件
4 | import DATA from './DATA';
5 | import render, { renderTextareaContent } from './render';
6 |
7 | /**
8 | * 数据状态统一控制
9 | * get 函数返回需要的数据信息
10 | * async 返回Promise数据
11 | * 除了render init其余为操作项,都返回this
12 | */
13 |
14 | /**
15 | * 初始化(始终存在默认项本地Hosts)
16 | */
17 | async function init() {
18 | backupLocalHosts(); // 有备份文件时会自动跳过备份 文件名system.bak
19 | const systemHosts = await getSystemHosts();
20 | defaultConfig[0].hosts = typeof systemHosts !== 'boolean' ? systemHosts : '';
21 | defaultConfig[1].hosts = typeof systemHosts !== 'boolean' ? systemHosts : '';
22 | const config = await getConfigData();
23 | if (typeof config === 'boolean' && config === false) return; // 如果读取错误不再继续进行
24 | if (!config) saveConfigData(defaultConfig); // 没有main字段时会返回null
25 | DATA.iniData(config || defaultConfig);
26 | }
27 |
28 | export default {
29 | getData: DATA.getData,
30 |
31 | render,
32 |
33 | init: async function () {
34 | await init();
35 | render();
36 | },
37 |
38 | /**
39 | * 添加新的项
40 | * @param {string} name
41 | */
42 | add: function (name?: string) {
43 | DATA.add(name);
44 | return this;
45 | },
46 |
47 | /**
48 | * 删除指定项参数
49 | * @param {string} id
50 | */
51 | remove: function (id: string) {
52 | DATA.remove(id);
53 | return this;
54 | },
55 |
56 | /**
57 | * 当前项Switch状态切换
58 | * @param {string} id
59 | * @param {boolean} status
60 | */
61 | checked: function (id: string, status: boolean) {
62 | DATA.checked(id, status);
63 | return this;
64 | },
65 |
66 | /**
67 | * 当前项状态激活(其他项切换为关闭)
68 | * @param {string} id
69 | */
70 | active: function (id: string) {
71 | DATA.active(id);
72 | return this;
73 | },
74 |
75 | /**
76 | * 修改指定项参数
77 | * @param {string} id
78 | * @param {Object} params
79 | * @param {function} fn 回调函数
80 | */
81 | modify: function (id: string, params: { name?: string; url?: string; lock?: boolean }, fn?: () => any) {
82 | DATA.modify(id, params);
83 | fn?.();
84 | return this;
85 | },
86 |
87 | /**
88 | * 更新使用中Hosts数据(合并switch项为true的数据)
89 | * @param {function} fn
90 | */
91 | updateUseHosts: DATA.updateUseHosts,
92 |
93 | /**
94 | * 获取指定项的所有信息
95 | * @param {string} id
96 | */
97 | getItemInfo: DATA.getItemInfo,
98 |
99 | /**
100 | * 获取当前激活项的所有信息(如果没激活项则返回第一项)
101 | */
102 | getActionItemInfo: DATA.getActionItemInfo,
103 |
104 | /**
105 | * 获取指定项的Hosts信息
106 | * @param {string} id
107 | */
108 | getItemHosts: DATA.getItemHosts,
109 |
110 | /**
111 | * 更新指定id远程Hosts信息
112 | * @param {string} id
113 | */
114 | asyncUpdateHosts: async function (id: string) {
115 | const curData = DATA.getItemInfo(id);
116 | if (curData.home) {
117 | // 外部修改存到当前数据
118 | const res = await getSystemHosts();
119 | if (typeof res !== 'boolean') {
120 | if (curData.hosts !== res) {
121 | curData.hosts = res;
122 | render();
123 | tipSuccess(`请手动保存外部修改,切换后数据将丢失`);
124 | } else tipSuccess(`${curData.name} 刷新成功`);
125 | } else tipError(`${curData.name} 刷新失败`);
126 | return;
127 | } else if (curData.url) {
128 | const res = await requestHosts(curData.url);
129 | if (res.code) {
130 | tipError(`"${curData.name}" 更新错误: ${res.message}`);
131 | curData.status = false; // 获取失败设置false
132 | } else {
133 | curData.hosts = res.html;
134 | curData.status = true;
135 | DATA.updateUseHosts();
136 | tipSuccess(`"${curData.name}" 更新成功`);
137 | if (curData.active) renderTextareaContent(curData.id.toString());
138 | }
139 | }
140 | },
141 |
142 | /**
143 | * 更新所有远程Hosts信息
144 | */
145 | asyncUpdateAllHosts: async function () {
146 | DATA.modifyEvery(
147 | (item) => !!item.url, // 更新的条件
148 | async (curData, index) => {
149 | const res = await requestHosts(curData.url);
150 | if (res.code) {
151 | setTimeout((e) => {
152 | tipError(`"${curData.name}"更新错误: ${res.message}`);
153 | }, index * 360);
154 | curData.status = false; // 获取失败设置false
155 | } else {
156 | curData.hosts = res.html;
157 | curData.status = true;
158 | tipSuccess(`"${curData.name}"更新成功`);
159 | if (curData.active) renderTextareaContent(curData.id.toString());
160 | }
161 | },
162 | DATA.updateUseHosts // 完成后需执行 更新Hosts函数
163 | );
164 | },
165 |
166 | /**
167 | * 比较指定id hosts数据与传入数据是否有差异
168 | * @param {string} id
169 | * @param {string} diffText
170 | */
171 | inconformity: function (id: string, diffText: string) {
172 | const curData = DATA.getItemInfo(id);
173 | if (curData.home) return false;
174 | return !(curData.hosts === diffText);
175 | },
176 | };
177 |
--------------------------------------------------------------------------------
/web_source/src/js/dataAction/render.ts:
--------------------------------------------------------------------------------
1 | import DATA from './DATA';
2 | import BUS from '../bus';
3 | import CodeMirror from '../CodeMirror';
4 | import template from '~/lib/template';
5 | import { modifyTextareaLockState, modifyTextareaType, modifyTextareaAttrId } from '../textarea';
6 |
7 | function inferType(item) {
8 | let curType = 'local';
9 | if (item.home) curType = 'home';
10 | if (item.url) curType = 'net';
11 | return curType;
12 | }
13 |
14 | /**
15 | * 渲染数据
16 | */
17 | export function render() {
18 | if (process.env.NODE_ENV !== 'production') console.log('render data', DATA.getData());
19 |
20 | const activeData = DATA.getActionItemInfo();
21 | // 判断当前项种类
22 | modifyTextareaType(inferType(activeData));
23 | modifyTextareaLockState(activeData.lock);
24 | modifyTextareaAttrId(activeData.id.toString());
25 |
26 | const html = template('template_nav', DATA.getData());
27 | BUS.nav.innerHTML = html; // 将渲染好的html插入到nav中
28 | // 设置nav高度 过长时能显示滚动条
29 | BUS.nav.style.height = `${document.body.clientHeight - 5}px`;
30 |
31 | renderTextareaContent(activeData.id.toString());
32 | return true;
33 | }
34 |
35 | /**
36 | * 修改文本框内容
37 | * @param {string} id
38 | */
39 | export async function renderTextareaContent(id: string) {
40 | const itemData = DATA.getItemInfo(id);
41 | CodeMirror.setValue(itemData.hosts, itemData.lock);
42 | }
43 |
44 | export default render;
45 |
--------------------------------------------------------------------------------
/web_source/src/js/dialog.ts:
--------------------------------------------------------------------------------
1 | import BUS from './bus';
2 | import dataAction from './dataAction';
3 | import { tipSuccess } from './util';
4 |
5 | /* dialog相关 */
6 | export function openDialog() {
7 | BUS.dialog.style.display = 'block';
8 | BUS.shade.style.display = 'block';
9 | }
10 |
11 | export function closeDialog() {
12 | BUS.dialog.style.display = 'none';
13 | BUS.shade.style.display = 'none';
14 | clearDialog();
15 | }
16 |
17 | export function setDialog(text: string, url?: string) {
18 | (BUS.inputName as HTMLInputElement).value = text ?? '';
19 | (BUS.inputUrl as HTMLInputElement).value = url ?? '';
20 | }
21 | export function clearDialog() {
22 | BUS.inputName.removeAttribute('data-id');
23 | (BUS.inputName as HTMLInputElement).value = '';
24 | (BUS.inputUrl as HTMLInputElement).value = '';
25 | }
26 |
27 | export function dialogApply() {
28 | if (BUS.mouseTargetId) {
29 | const name = (BUS.inputName as HTMLInputElement).value;
30 | const url = (BUS.inputUrl as HTMLInputElement).value;
31 | dataAction.modify(BUS.mouseTargetId, { name, url }).render();
32 | BUS.mouseTargetId = null;
33 | tipSuccess('操作成功');
34 | closeDialog();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/web_source/src/js/init.ts:
--------------------------------------------------------------------------------
1 | import BUS from './bus';
2 | import codeMirror from './CodeMirror';
3 | import dataAction from './dataAction';
4 | import contextMenu from './contextMenu';
5 | import { eventTextareaUpdate, eventTextareaSave } from './textarea';
6 |
7 | export default function init() {
8 | initBusData();
9 | initBind();
10 | // 初始化导航栏
11 | dataAction.init();
12 | // 初始化编辑器
13 | codeMirror.init();
14 | }
15 |
16 | /* 初始化DOM对象相关数据 */
17 | function initBusData() {
18 | BUS.nav = document.querySelector('#nav ul'); // 导航栏
19 | BUS.textarea = document.querySelector('.textarea'); // 文本组件框
20 | BUS.dialog = document.querySelector('.dialog-item'); // 弹出窗
21 | BUS.inputName = document.querySelector('.item-body input[name=name]'); // 弹出窗输入框name
22 | BUS.inputUrl = document.querySelector('.item-body input[name=url]'); // 弹出窗输入框url
23 | BUS.shade = document.querySelector('.shade'); // 遮罩层
24 | BUS.contextMenu = document.querySelector('.context-menu'); // 右键菜单
25 | }
26 |
27 | /* 禁用影响使用的快捷键 */
28 | function disabledKeyboardShortcuts() {
29 | window.onkeydown = window.onkeyup = window.onkeypress = function (event: KeyboardEvent) {
30 | //禁止ctrl+u
31 | if (event.ctrlKey && event.key.toLowerCase() == 'u') return false;
32 | //禁止ctrl+s
33 | if (event.ctrlKey && event.key.toLowerCase() == 's') return false;
34 | //禁止ctrl+shift+i
35 | if (event.ctrlKey && event.shiftKey && event.key.toLowerCase() == 'i') return false;
36 | //禁止 F5
37 | if (event.key == 'F5') return false;
38 | //禁止 F12
39 | if (event.key == 'F12') return false;
40 | };
41 | }
42 |
43 | /* 初始化绑定 */
44 | function initBind() {
45 | if (process.env.NODE_ENV === 'production') disabledKeyboardShortcuts();
46 | // 鼠标右键绑定
47 | contextMenu.init();
48 | // nav上的 option与switch响应
49 | BUS.nav.addEventListener('click', function (e) {
50 | // 开启关闭切换
51 | if ((e.target as HTMLElement).tagName === 'INPUT') {
52 | const checked = (e.target as HTMLInputElement).checked;
53 | const id = (e as any).path[2].dataset.id;
54 | dataAction.checked(id, checked).render();
55 | }
56 | // 切换active
57 | if ((e.target as HTMLElement).tagName === 'LI') {
58 | const id = (e.target as HTMLElement).dataset.id;
59 | dataAction.active(id).render();
60 | }
61 | });
62 | // 文本框中刷新按钮
63 | document.querySelector('.textarea .operation .refresh').addEventListener('click', eventTextareaUpdate);
64 | // 文本框中更新按钮
65 | document.querySelector('.textarea .operation .net').addEventListener('click', eventTextareaUpdate);
66 | // 文本框中保存按钮
67 | document.querySelector('.textarea .operation .save').addEventListener('click', eventTextareaSave);
68 | }
69 |
--------------------------------------------------------------------------------
/web_source/src/js/main.ts:
--------------------------------------------------------------------------------
1 | import 'normalize.css';
2 | import 'lu2/theme/pure/css/common/ui';
3 | import '~/styles/index.less';
4 | import init from './init';
5 |
6 | if (process.env.NODE_ENV !== 'production') {
7 | console.log('▄▄▌ ▐ ▄ ▐ ▄ ▄▄· ▄▄· ');
8 | console.log('██• •█▌▐█•█▌▐█▐█ ▌▪▪ ▐█ ▌▪▪ ');
9 | console.log('██▪ ▐█▐▐▌▐█▐▐▌██ ▄▄ ▄█▀▄ ██ ▄▄ ▄█▀▄ ');
10 | console.log('▐█▌▐▌██▐█▌██▐█▌▐███▌▐█▌.▐▌▐███▌▐█▌.▐▌');
11 | console.log('.▀▀▀ ▀▀ █▪▀▀ █▪·▀▀▀ ▀█▄▀▪·▀▀▀ ▀█▄▀▪ ', '调试模式', process.env.NODE_ENV);
12 | }
13 |
14 | window.onload = init;
15 |
--------------------------------------------------------------------------------
/web_source/src/js/textarea.ts:
--------------------------------------------------------------------------------
1 | import BUS from './bus';
2 | import codeMirror from './CodeMirror';
3 | import dataAction from './dataAction';
4 | import { tipSuccess, tipError } from './util';
5 |
6 | /**
7 | * 增删的类名
8 | * @param {string} content
9 | */
10 | export function modifyTextareaAttrId(content: string) {
11 | return (BUS.textarea.dataset.id = content);
12 | }
13 |
14 | /**
15 | * 增删的类名
16 | * @param {string} className
17 | */
18 | export function modifyTextareaClass(type: string, className: string) {
19 | if (type === 'add') return BUS.textarea.classList.add(className);
20 | if (type === 'remove') return BUS.textarea.classList.remove(className);
21 | }
22 |
23 | /**
24 | * 修改文本框的锁定非锁定类名
25 | * @param {boolean} classState
26 | */
27 | export function modifyTextareaLockState(classState: boolean) {
28 | if (classState) return modifyTextareaClass('add', 'lock');
29 | return modifyTextareaClass('remove', 'lock');
30 | }
31 |
32 | /**
33 | * 标注当前激活项的类型 home url local 三种
34 | * 提供相应按钮和背景
35 | * @param {string} className
36 | */
37 | export function modifyTextareaType(className: string) {
38 | const list = ['local', 'home', 'net'];
39 | list.forEach((item) => {
40 | if (item === className) modifyTextareaClass('add', item);
41 | else modifyTextareaClass('remove', item);
42 | });
43 | }
44 |
45 | /**
46 | * CodeMirror事件文本内容被修改时浮现保存按钮
47 | * @param {Object} CodeMirror 文本编辑器对象
48 | */
49 | export function changeTextareaTip(CodeMirror: any) {
50 | const id = BUS.textarea.dataset.id;
51 | const flag = dataAction.inconformity(id, CodeMirror.getValue());
52 | if (flag) modifyTextareaClass('add', 'save');
53 | else modifyTextareaClass('remove', 'save');
54 | return;
55 | }
56 |
57 | /**
58 | * 更新文本框内容
59 | */
60 | export function eventTextareaUpdate() {
61 | const id = BUS.textarea.dataset.id;
62 | dataAction.asyncUpdateHosts(id);
63 | }
64 |
65 | /**
66 | * 保存文本框内容
67 | */
68 | export function eventTextareaSave() {
69 | const id = BUS.textarea.dataset.id;
70 | const curItem = dataAction.getItemInfo(id);
71 | curItem.hosts = codeMirror.getValue();
72 | dataAction.updateUseHosts(() => {
73 | modifyTextareaClass('remove', 'save');
74 | tipSuccess('保存成功'); // 假提示 实际保存失败会再次提醒
75 | });
76 | }
77 |
--------------------------------------------------------------------------------
/web_source/src/js/types/aardio.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare namespace aardio {
4 | interface IProxy {
5 | ua?: string;
6 | host: string;
7 | user?: string;
8 | password?: string;
9 | }
10 |
11 | interface External {
12 | /** 获取系统HOSTS文本 */
13 | getSystemHosts(): Promise;
14 | /** 保存到系统HOSTS文本 */
15 | saveSystemHosts(content: string): Promise;
16 | /** 清理DNS缓存 */
17 | clearDnsCache(): Promise;
18 | /** 发送请求获取网络HOSTS */
19 | requestHosts(url: string, proxy?: IProxy): Promise<{ code: string; message: string; html?: string }>;
20 | /** 备份系统文件 */
21 | backupLocalHosts(coverage?: boolean): Promise;
22 | /** 读取全部数据 */
23 | getConfigData(): Promise;
24 | /** 保存全部数据 */
25 | saveConfigData(data: Object): Promise;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/web_source/src/js/util.ts:
--------------------------------------------------------------------------------
1 | import LightTip from 'lu2/theme/pure/js/common/ui/LightTip';
2 | import * as aardio from 'aardio';
3 |
4 | /* 工具函数 */
5 |
6 | /**
7 | * 成功tip
8 | * @param {string} text
9 | * @param {number} duration 毫秒
10 | */
11 | export function tipSuccess(text: string, duration?: number) {
12 | new LightTip(text ?? '操作成功', {
13 | type: 'success',
14 | duration: duration ?? 3000,
15 | });
16 | }
17 |
18 | /**
19 | * 异常tip
20 | * @param {string} text
21 | */
22 | export function tipWarning(text: string) {
23 | new LightTip(text ?? '操作异常', { type: 'warning', duration: 3000 });
24 | }
25 |
26 | /**
27 | * 失败tip
28 | * @param {string} text
29 | * @param {number} duration 毫秒
30 | */
31 | export function tipError(text: string, duration?: number) {
32 | new LightTip(text ?? '操作失败', {
33 | type: 'error',
34 | duration: duration ?? 3000,
35 | });
36 | }
37 |
38 | /* aardio相关操作 */
39 |
40 | /**
41 | * 获取本地Hosts
42 | */
43 | export function getSystemHosts() {
44 | return aardio.getSystemHosts().catch((e) => {
45 | tipError(e);
46 | return false;
47 | });
48 | }
49 |
50 | /**
51 | * 保存到本地Hosts
52 | * @param {string} content
53 | */
54 | export function saveSystemHosts(content: string) {
55 | return aardio
56 | .saveSystemHosts(content)
57 | .then((res) => {
58 | if (!res) tipError('写入失败,请用管理员权限启动再重试');
59 | return res;
60 | })
61 | .catch((e) => tipError(e));
62 | }
63 |
64 | /**
65 | * 发送请求获取网络Hosts
66 | * @param {string} url
67 | * @param {aardio.IProxy} proxy
68 | */
69 | export function requestHosts(url: string, proxy?: aardio.IProxy) {
70 | return aardio.requestHosts(url, proxy).catch((e) => ({ code: 9, message: '程序调用错误', html: '' }));
71 | }
72 |
73 | /**
74 | * 刷新DNS缓存
75 | */
76 | export function refreshDnsCache() {
77 | aardio
78 | .clearDnsCache()
79 | .then((res) => {
80 | console.log('ers', res);
81 | })
82 | .catch((e) => tipError('程序调用错误'));
83 | tipSuccess('刷新完成');
84 | return;
85 | }
86 |
87 | /**
88 | * 备份系统文件
89 | * @param {boolean} coverage
90 | */
91 | export function backupLocalHosts(coverage?: boolean) {
92 | return aardio.backupLocalHosts(coverage).catch((e) => ({ code: 9, message: e }));
93 | }
94 |
95 | /**
96 | * 读取全部数据
97 | */
98 | export function getConfigData() {
99 | return aardio
100 | .getConfigData()
101 | .then((res) => {
102 | if (res.main) return res.main;
103 | else return null;
104 | })
105 | .catch((e) => {
106 | tipError('读取数据失败');
107 | return false;
108 | });
109 | }
110 |
111 | /**
112 | * 保存全部数据
113 | */
114 | export function saveConfigData(data: Object) {
115 | return aardio.saveConfigData(data).catch((e) => {
116 | tipError('保存数据失败');
117 | return false;
118 | });
119 | }
120 |
--------------------------------------------------------------------------------
/web_source/src/lib/template.js:
--------------------------------------------------------------------------------
1 | /*! art-template@4.13.1 for browser | https://github.com/aui/art-template */
2 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.template=t():e.template=t()}("undefined"!=typeof self?self:this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,t),i.l=!0,i.exports}var n={};return t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e["default"]}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=4)}([function(e,t,n){"use strict";var r=n(6),i=n(2),o=n(22),s=function(e,t){t.onerror(e,t);var n=function(){return"{Template Error}"};return n.mappings=[],n.sourcesContent=[],n},a=function u(e){var t=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{};"string"!=typeof e?t=e:t.source=e,t=i.$extend(t),e=t.source,!0===t.debug&&(t.cache=!1,t.minimize=!1,t.compileDebug=!0),t.compileDebug&&(t.minimize=!1),t.filename&&(t.filename=t.resolveFilename(t.filename,t));var n=t.filename,a=t.cache,c=t.caches;if(a&&n){var l=c.get(n);if(l)return l}if(!e)try{e=t.loader(n,t),t.source=e}catch(m){var f=new o({name:"CompileError",path:n,message:"template not found: "+m.message,stack:m.stack});if(t.bail)throw f;return s(f,t)}var p=void 0,h=new r(t);try{p=h.build()}catch(f){if(f=new o(f),t.bail)throw f;return s(f,t)}var d=function(e,n){try{return p(e,n)}catch(f){if(!t.compileDebug)return t.cache=!1,t.compileDebug=!0,u(t)(e,n);if(f=new o(f),t.bail)throw f;return s(f,t)()}};return d.mappings=p.mappings,d.sourcesContent=p.sourcesContent,d.toString=function(){return p.toString()},a&&n&&c.set(n,d),d};a.Compiler=r,e.exports=a},function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t["default"]=/((['"])(?:(?!\2|\\).|\\(?:\r\n|[\s\S]))*(\2)?|`(?:[^`\\$]|\\[\s\S]|\$(?!\{)|\$\{(?:[^{}]|\{[^}]*\}?)*\}?)*(`)?)|(\/\/.*)|(\/\*(?:[^*]|\*(?!\/))*(\*\/)?)|(\/(?!\*)(?:\[(?:(?![\]\\]).|\\.)*\]|(?![\/\]\\]).|\\.)+\/(?:(?!\s*(?:\b|[\u0080-\uFFFF$\\'"~({]|[+\-!](?!=)|\.?\d))|[gmiyu]{1,5}\b(?![\u0080-\uFFFF$\\]|\s*(?:[+\-*%&|^<>!=?({]|\/(?![\/*])))))|(0[xX][\da-fA-F]+|0[oO][0-7]+|0[bB][01]+|(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?)|((?!\d)(?:(?!\s)[$\w\u0080-\uFFFF]|\\u[\da-fA-F]{4}|\\u\{[\da-fA-F]+\})+)|(--|\+\+|&&|\|\||=>|\.{3}|(?:[+\-\/%&|^]|\*{1,2}|<{1,2}|>{1,3}|!=?|={1,2})=?|[?~.,:;[\](){}])|(\s+)|(^$|[\s\S])/g,t.matchToToken=function(e){var t={type:"invalid",value:e[0]};return e[1]?(t.type="string",t.closed=!(!e[3]&&!e[4])):e[5]?t.type="comment":e[6]?(t.type="comment",t.closed=!!e[7]):e[8]?t.type="regex":e[9]?t.type="number":e[10]?t.type="name":e[11]?t.type="punctuator":e[12]&&(t.type="whitespace"),t}},function(e,t,n){"use strict";function r(){this.$extend=function(e){return e=e||{},o(e,e instanceof r?e:this)}}var i=n(10),o=n(12),s=n(13),a=n(14),u=n(15),c=n(16),l=n(17),f=n(18),p=n(19),h=n(21),d="undefined"==typeof window,m={source:null,filename:null,rules:[f,l],escape:!0,debug:!!d&&"production"!==process.env.NODE_ENV,bail:!0,cache:!0,minimize:!0,compileDebug:!1,resolveFilename:h,include:s,htmlMinifier:p,htmlMinifierOptions:{collapseWhitespace:!0,minifyCSS:!0,minifyJS:!0,ignoreCustomFragments:[]},onerror:a,loader:c,caches:u,root:"/",extname:".art",ignore:[],imports:i};r.prototype=m,e.exports=new r},function(e,t){},function(e,t,n){"use strict";var r=n(5),i=n(0),o=n(23),s=function(e,t){return t instanceof Object?r({filename:e},t):i({filename:e,source:t})};s.render=r,s.compile=i,s.defaults=o,e.exports=s},function(e,t,n){"use strict";var r=n(0),i=function(e,t,n){return r(e,n)(t)};e.exports=i},function(e,t,n){"use strict";function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t|\([\w\W]*?\))\s*{[\s;]*$)/,"$1})"],[/(^[\w\W]*?\([\w\W]*?\)\s*{[\s;]*$)/,"$1}"]],n=0;n2&&arguments[2]!==undefined?arguments[2]:{},o=[new i("string",e)],s=0;sd&&(p=new i("string",v.slice(d,h.index),p),m.push(p)),p=new i("expression",h[0],p),h[0]=r(p),p.script=a.use.apply(n,h),m.push(p),d=h.index+h[0].length;d]/;o.$escape=function(e){return r(n(e))},o.$each=function(e,t){if(Array.isArray(e))for(var n=0,r=e.length;n {{"+n+"}}")};switch("#"===t&&h("#value","@value"),p){case"set":i="var "+u.join("").trim();break;case"if":i="if("+u.join("").trim()+"){";break;case"else":var d=u.indexOf("if");~d?(u.splice(0,d+1),i="}else if("+u.join("").trim()+"){"):i="}else{";break;case"/if":i="}";break;case"each":l=r._split(a),l.shift(),"as"===l[1]&&(h("each object as value index","each object value index"),l.splice(1,1));i="$each("+(l[0]||"$data")+",function("+(l[1]||"$value")+","+(l[2]||"$index")+"){";break;case"/each":i="})";break;case"block":l=r._split(a),l.shift(),i="block("+l.join(",").trim()+",function(){";break;case"/block":i="})";break;case"echo":p="print",h("echo value","value");case"print":case"include":case"extend":if(0!==u.join("").trim().indexOf("(")){l=r._split(a),l.shift(),i=p+"("+l.join(",")+")";break}default:if(~u.indexOf("|")){var m=a.reduce(function(e,t){var n=t.value,r=t.type;return"|"===n?e.push([]):"whitespace"!==r&&"comment"!==r&&(e.length||e.push([]),":"===n&&1===e[e.length-1].length?h("value | filter: argv","value | filter argv"):e[e.length-1].push(t)),e},[]).map(function(e){return r._split(e)});i=m.reduce(function(e,t){var n=t.shift();return t.unshift(e),"$imports."+n+"("+t.join(",")+")"},m.shift().join(" ").trim())}f=f||"escape"}return c.code=i,c.output=f,c},_split:function(e){e=e.filter(function(e){var t=e.type;return"whitespace"!==t&&"comment"!==t});for(var t=0,n=e.shift(),r=/\]|\)/,i=[[n]];t/,use:function(e,t,n,r){return n={"-":"raw","=":"escape","":!1,"==":"raw","=#":"raw"}[n],t&&(r="/*"+r+"*/",n=!1),{code:r,output:n}}};e.exports=r},function(e,t,n){"use strict";function r(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t> ":" ")+n+"| "+e}).join("\n");return(r||"anonymous")+":"+i+":"+o+"\n"+f+"\n\n"+t+": "+a+(s?"\n generated: "+s:"")}var a=function(e){function t(e){r(this,t);var n=i(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e.message));return n.name="TemplateError",n.message=s(e),Error.captureStackTrace&&Error.captureStackTrace(n,n.constructor),n}return o(t,e),t}(Error);e.exports=a},function(e,t,n){"use strict";e.exports=n(2)}])});
--------------------------------------------------------------------------------
/web_source/src/styles/base.less:
--------------------------------------------------------------------------------
1 | ul {
2 | margin: 0;
3 | padding: 0;
4 | }
5 | li {
6 | list-style-type: none;
7 | }
8 | html,
9 | body,
10 | nav,
11 | nav ul {
12 | height: 100%;
13 | overflow: hidden;
14 | }
15 | body {
16 | background-color: #eef0f5;
17 | }
18 |
19 | .clearfix {
20 | *zoom: 1;
21 | &:before,
22 | &:after {
23 | display: table;
24 | content: "";
25 | }
26 | &:after {
27 | clear: both;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/web_source/src/styles/index.less:
--------------------------------------------------------------------------------
1 | @import './base.less';
2 | @import './ui.less';
3 |
4 | .container {
5 | height: 100%;
6 | width: 100%;
7 | position: relative;
8 | display: flex;
9 | justify-content: space-between;
10 | }
11 |
12 | /* 导航栏 */
13 | .container nav {
14 | flex: 0 0 200px;
15 | height: auto;
16 | width: 200px;
17 | position: relative;
18 |
19 | ul {
20 | padding: 4px 2px 2px 0;
21 | overflow: auto;
22 | &::-webkit-scrollbar {
23 | width: 3px;
24 | }
25 | &::-webkit-scrollbar-thumb {
26 | border-radius: 10px;
27 | background: #bcd;
28 | }
29 | &::-webkit-scrollbar-track {
30 | border-radius: 0;
31 | background: #eef0f5;
32 | }
33 | }
34 |
35 | li {
36 | cursor: default;
37 | font-size: 13px;
38 | height: 40px;
39 | line-height: 40px;
40 | padding-left: 32px;
41 | position: relative;
42 | background: url('~/asset/record.svg') no-repeat 10px;
43 | background-size: 15px;
44 | color: #666666;
45 | border-left: 3px solid #eef0f5;
46 | opacity: 0.6;
47 |
48 | &.home {
49 | background: url('~/asset/home.svg') no-repeat 10px;
50 | background-size: 15px;
51 | }
52 | &.net {
53 | background: url('~/asset/recordFind.svg') no-repeat 10px;
54 | background-size: 15px;
55 | }
56 | &.mouse {
57 | background-color: #bedff7;
58 | border-left: 3px solid #51b0f5;
59 | }
60 |
61 | &.active,
62 | &:hover {
63 | background-color: #bedff7;
64 | border-left: 3px solid #51b0f5;
65 | opacity: 1;
66 | border-radius: 2px;
67 | }
68 |
69 | &:hover .option {
70 | cursor: pointer;
71 | position: absolute;
72 | width: 18px;
73 | height: 40px;
74 | right: 43px;
75 | top: 0px;
76 | background: url('~/asset/edit.svg') no-repeat center;
77 | background-size: 15px;
78 | opacity: 0.5;
79 | &:hover {
80 | opacity: 0.9;
81 | }
82 | }
83 |
84 | .onoff {
85 | position: absolute;
86 | right: 10px;
87 | top: 4px;
88 | }
89 | }
90 |
91 | &::after {
92 | content: 'LnnCoCo';
93 | position: absolute;
94 | bottom: 2px;
95 | left: 2px;
96 | color: rgba(204, 204, 204, 0.6);
97 | font-size: 12px;
98 | transform: scale(0.9);
99 | cursor: default;
100 | }
101 | }
102 |
103 | /* 内容框 */
104 | .container main {
105 | flex: 1;
106 |
107 | .textarea {
108 | background-color: #fff;
109 | border: 1px solid #ddd;
110 | border-radius: 3px;
111 | height: 98%;
112 | margin-top: 3px;
113 | margin-right: 3px;
114 | box-shadow: 0 0px 1px rgba(0, 0, 0, 0.1);
115 |
116 | textarea {
117 | height: 100%;
118 | width: 100%;
119 | border: none;
120 | outline: none;
121 | font-size: 15px;
122 | }
123 |
124 | // 背景
125 | .background-image-layer() {
126 | content: '';
127 | position: absolute;
128 | width: 50%;
129 | height: 62%;
130 | right: 5px;
131 | bottom: 5px;
132 | opacity: 0.06;
133 | }
134 | &.local .CodeMirror::before {
135 | .background-image-layer();
136 | background: url('~/asset/record.svg') no-repeat right bottom;
137 | background-size: contain;
138 | }
139 | &.net .CodeMirror::before {
140 | .background-image-layer();
141 | background: url('~/asset/recordFind.svg') no-repeat right bottom;
142 | background-size: contain;
143 | }
144 | &.lock .CodeMirror::before {
145 | .background-image-layer();
146 | background: url('~/asset/lock.svg') no-repeat right bottom;
147 | background-size: contain;
148 | }
149 | &.home .CodeMirror::before {
150 | .background-image-layer();
151 | background: url('~/asset/home.svg') no-repeat right bottom;
152 | background-size: contain;
153 | }
154 |
155 | // 操作按钮
156 | &.home .operation {
157 | display: block;
158 | .refresh {
159 | display: inline-block;
160 | }
161 | }
162 | &.net .operation {
163 | display: block;
164 | .net {
165 | display: inline-block;
166 | }
167 | }
168 | &.save .operation {
169 | display: block;
170 | .save {
171 | display: inline-block;
172 | }
173 | }
174 |
175 | .operation {
176 | display: none;
177 | position: absolute;
178 | z-index: 110;
179 | top: 17px;
180 | right: 22px;
181 | background-color: rgba(189, 189, 189, 0.226);
182 | height: 20px;
183 | padding: 2px 13px;
184 | border-radius: 16px;
185 | transition: all 0.2s;
186 |
187 | .operation-icon() {
188 | display: none;
189 | width: 17px;
190 | height: 20px;
191 | opacity: 0.2;
192 | margin: 0 3px;
193 | &:hover {
194 | cursor: pointer;
195 | opacity: 0.6;
196 | }
197 | }
198 |
199 | .refresh {
200 | .operation-icon();
201 | background: url('~/asset/refresh.svg') no-repeat center;
202 | background-size: contain;
203 | }
204 |
205 | .net {
206 | .operation-icon();
207 | background: url('~/asset/download.svg') no-repeat center;
208 | background-size: contain;
209 | }
210 |
211 | .save {
212 | .operation-icon();
213 | background: url('~/asset/save.svg') no-repeat center;
214 | background-size: contain;
215 | }
216 | }
217 | }
218 | }
219 |
220 | /* 弹窗样式 */
221 | .dialog-item {
222 | position: absolute;
223 | left: 50%;
224 | top: 50%;
225 | transform: translate(-50%, -50%);
226 | width: 360px;
227 | background-color: #fff;
228 | border: 1px solid #ccc;
229 | border-radius: 3px;
230 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
231 | display: none;
232 | z-index: 100;
233 |
234 | &.active {
235 | display: block;
236 | }
237 | .item-hader {
238 | height: 40px;
239 | line-height: 40px;
240 | padding-left: 15px;
241 | border-bottom: 1px solid #e8e8e8;
242 | font-size: 15px;
243 | }
244 | .item-operation {
245 | overflow: hidden;
246 | padding: 10px;
247 | border-top: 1px solid #e8e8e8;
248 |
249 | .ui-button {
250 | float: right;
251 | & + .ui-button {
252 | margin-right: 10px;
253 | }
254 | }
255 | }
256 | .item-body {
257 | padding: 20px 40px;
258 | font-size: 14px;
259 | .form-item + .form-item {
260 | margin-top: 15px;
261 | }
262 | input {
263 | width: 200px;
264 | font-size: 13px;
265 | }
266 | }
267 | }
268 |
269 | .shade {
270 | display: none;
271 | position: absolute;
272 | top: 0;
273 | left: 0;
274 | width: 100%;
275 | height: 100%;
276 | background: rgba(0, 0, 0, 0.3);
277 | z-index: 99;
278 | }
279 |
280 | /* 右键菜单 */
281 | .context-menu {
282 | position: absolute;
283 | width: 111px;
284 | padding: 5px 0;
285 | background-color: #fff;
286 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
287 | border: 1px solid #d0d0d5;
288 | border-radius: 2px;
289 | font-size: 14px;
290 | animation: fadeIn 0.2s;
291 | z-index: 200;
292 | left: -99999px;
293 | top: -99999px;
294 | .menu-item {
295 | display: block;
296 | line-height: 20px;
297 | padding: 8px 10px 7px;
298 | color: #4c5161;
299 | text-decoration: none;
300 | cursor: pointer;
301 | white-space: nowrap;
302 | text-overflow: ellipsis;
303 | overflow: hidden;
304 | &:hover {
305 | background-color: #f0f7ff;
306 | }
307 | &.disabled {
308 | cursor: default;
309 | pointer-events: none;
310 | opacity: 0.4;
311 | &:hover {
312 | background-color: transparent;
313 | }
314 | }
315 | .icon {
316 | display: inline-block;
317 | width: 13px;
318 | height: 13px;
319 | transform: translateY(2px);
320 | img {
321 | width: 100%;
322 | height: 100%;
323 | }
324 | }
325 | }
326 | .menu-line {
327 | border: 0;
328 | border-bottom: 1px solid #d0d0d5;
329 | margin: 7px 11px;
330 | opacity: 0.4;
331 | }
332 | .menu-target {
333 | cursor: default;
334 | padding-top: 2px;
335 | padding-bottom: 6px;
336 | font-size: 12px;
337 | color: #b9b9b9b0;
338 | height: 12px;
339 | line-height: 12px;
340 | transform: scale(0.8);
341 | }
342 | }
343 |
--------------------------------------------------------------------------------
/web_source/src/styles/ui.less:
--------------------------------------------------------------------------------
1 | /* 控制Input大小 */
2 | body {
3 | input.ui-input,
4 | .ui-input > input {
5 | height: 30px;
6 | }
7 | }
8 |
9 | /* 控制Button大小 */
10 | body {
11 | .ui-button {
12 | min-width: 65px;
13 | line-height: 10px;
14 | }
15 | input.ui-button,
16 | button.ui-button {
17 | height: 30px;
18 | }
19 | }
20 |
21 | /* 控制switch大小 */
22 | .container {
23 | .ui-switch {
24 | width: 26px;
25 | height: 16px;
26 | }
27 | :checked + .ui-switch::before {
28 | margin-left: 10px;
29 | }
30 | .ui-switch::before {
31 | width: 12px;
32 | height: 12px;
33 | }
34 | }
35 |
36 | /* 控制lightip大小 */
37 | body {
38 | .ui-lightip {
39 | width: 20em;
40 | padding: 10px 1em;
41 | opacity: 0.8;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/web_source/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "baseUrl": ".",
5 | "paths": {
6 | "~*": ["./src/*"]
7 | },
8 | "lib": [
9 | "es2016",
10 | "dom"
11 | ]
12 | }
13 | }
--------------------------------------------------------------------------------