From 2766e23aa05b95b3329ec04d391dce3a63dcf576 Mon Sep 17 00:00:00 2001 From: Peng-YM <1048217874pengym@gmail.com> Date: Sat, 5 Dec 2020 13:39:11 +0800 Subject: [PATCH] =?UTF-8?q?Sub-Store=201.0=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 移除了所有基于关键词的节点操作,统一使用基于正则表达式的节点操作。 2. UI的大量改进。 --- backend/sub-store.js | 82 +- backend/sub-store.min.js | 4 +- web/package-lock.json | 84 +- web/package.json | 11 +- web/public/404.html | 41 + web/src/App.vue | 14 +- web/src/assets/clint-mckoy.jpg | 1 + web/src/assets/css/app.css | 1391 +++++++++++++++ web/src/assets/css/app.scss | 1525 +++++++++++++++++ web/src/assets/lock.jpg | 1 + web/src/assets/login.jpg | 1 + web/src/assets/logo.png | Bin 6849 -> 67 bytes web/src/assets/logo.svg | 11 +- web/src/assets/pricing.jpg | 1 + web/src/assets/register.jpg | 1 + web/src/assets/vuetify.svg | 1 + web/src/components/BottomNav.vue | 9 +- web/src/components/KeywordDeleteOperator.vue | 109 -- web/src/components/KeywordFilter.vue | 130 -- web/src/components/KeywordRenameOperator.vue | 137 -- web/src/components/ProxyList.vue | 10 +- web/src/components/RegexDeleteOperator.vue | 4 +- web/src/components/RegexFilter.vue | 4 +- web/src/components/RegexRenameOperator.vue | 4 +- ...SortOperator.vue => RegexSortOperator.vue} | 47 +- web/src/components/ScriptFilter.vue | 15 +- web/src/components/ScriptOperator.vue | 15 +- web/src/components/SpeedDial.vue | 2 +- web/src/components/TopToolbar.vue | 38 +- web/src/components/base/Card.vue | 9 + web/src/components/base/Item.vue | 69 + web/src/components/base/ItemGroup.vue | 123 ++ web/src/components/base/ItemSubGroup.vue | 25 + web/src/components/base/MaterialAlert.vue | 64 + web/src/components/base/MaterialCard.vue | 168 ++ web/src/components/base/MaterialChartCard.vue | 95 + web/src/components/base/MaterialDropdown.vue | 70 + web/src/components/base/MaterialSnackbar.vue | 66 + web/src/components/base/MaterialStatsCard.vue | 113 ++ web/src/components/base/MaterialTabs.vue | 43 + web/src/components/base/MaterialTestimony.vue | 75 + web/src/components/base/MaterialWizard.vue | 109 ++ web/src/components/base/Subheading.vue | 34 + web/src/components/base/VComponent.vue | 42 + web/src/css/app.css | 48 - web/src/i18n.js | 24 + web/src/locales/ar.json | 44 + web/src/locales/en.json | 44 + web/src/main.js | 7 + web/src/plugins/base.js | 17 + web/src/plugins/chartist.js | 4 + web/src/plugins/vee-validate.js | 5 + web/src/plugins/vue-world-map.js | 4 + web/src/plugins/vuetify.js | 27 +- web/src/views/CollectionEditor.vue | 110 -- web/src/views/Dashboard.vue | 30 - web/src/views/SubEditor.vue | 31 +- web/src/views/Subscription.vue | 6 +- 58 files changed, 4374 insertions(+), 825 deletions(-) create mode 100644 web/public/404.html create mode 100644 web/src/assets/clint-mckoy.jpg create mode 100644 web/src/assets/css/app.css create mode 100644 web/src/assets/css/app.scss create mode 100644 web/src/assets/lock.jpg create mode 100644 web/src/assets/login.jpg create mode 100644 web/src/assets/pricing.jpg create mode 100644 web/src/assets/register.jpg create mode 100644 web/src/assets/vuetify.svg delete mode 100644 web/src/components/KeywordDeleteOperator.vue delete mode 100644 web/src/components/KeywordFilter.vue delete mode 100644 web/src/components/KeywordRenameOperator.vue rename web/src/components/{KeywordSortOperator.vue => RegexSortOperator.vue} (67%) create mode 100644 web/src/components/base/Card.vue create mode 100644 web/src/components/base/Item.vue create mode 100644 web/src/components/base/ItemGroup.vue create mode 100644 web/src/components/base/ItemSubGroup.vue create mode 100644 web/src/components/base/MaterialAlert.vue create mode 100644 web/src/components/base/MaterialCard.vue create mode 100644 web/src/components/base/MaterialChartCard.vue create mode 100644 web/src/components/base/MaterialDropdown.vue create mode 100644 web/src/components/base/MaterialSnackbar.vue create mode 100644 web/src/components/base/MaterialStatsCard.vue create mode 100644 web/src/components/base/MaterialTabs.vue create mode 100644 web/src/components/base/MaterialTestimony.vue create mode 100644 web/src/components/base/MaterialWizard.vue create mode 100644 web/src/components/base/Subheading.vue create mode 100644 web/src/components/base/VComponent.vue delete mode 100644 web/src/css/app.css create mode 100644 web/src/i18n.js create mode 100644 web/src/locales/ar.json create mode 100644 web/src/locales/en.json create mode 100644 web/src/plugins/base.js create mode 100644 web/src/plugins/chartist.js create mode 100644 web/src/plugins/vee-validate.js create mode 100644 web/src/plugins/vue-world-map.js delete mode 100644 web/src/views/CollectionEditor.vue diff --git a/backend/sub-store.js b/backend/sub-store.js index bdd37bf..a45e43e 100644 --- a/backend/sub-store.js +++ b/backend/sub-store.js @@ -54,6 +54,8 @@ function service() { .get(getAllSubscriptions) .post(createSubscription); + $app.get("/api/sub/statistics/:name"); + // collection API $app.route("/api/collection/:name") .get(getCollection) @@ -1584,14 +1586,15 @@ var ProxyUtils = (function () { }; } - // sort by keywords - function KeywordSortOperator(keywords) { + // sort by regex + function RegexSortOperator(expressions) { + expressions = expressions.map(expr => new RegExp(expr)); return { - name: "Keyword Sort Operator", + name: "Regex Sort Operator", func: (proxies) => proxies.sort((a, b) => { - const oA = getKeywordOrder(keywords, a.name); - const oB = getKeywordOrder(keywords, b.name); + const oA = getRegexOrder(expressions, a.name); + const oB = getRegexOrder(expressions, b.name); if (oA && !oB) return -1; if (oB && !oA) return 1; if (oA && oB) return oA < oB ? -1 : 1; @@ -1601,10 +1604,10 @@ var ProxyUtils = (function () { }; } - function getKeywordOrder(keywords, str) { + function getRegexOrder(expressions, str) { let order = null; - for (let i = 0; i < keywords.length; i++) { - if (str.indexOf(keywords[i]) !== -1) { + for (let i = 0; i < expressions.length; i++) { + if (expressions[i].test(str)) { order = i + 1; // plus 1 is important! 0 will be treated as false!!! break; } @@ -1612,22 +1615,6 @@ var ProxyUtils = (function () { return order; } - // rename by keywords - // keywords: [{old: "old", now: "now"}] - function KeywordRenameOperator(keywords) { - return { - name: "Keyword Rename Operator", - func: (proxies) => { - return proxies.map((proxy) => { - for (const {old, now} of keywords) { - proxy.name = proxy.name.replaceAll(old, now).trim(); - } - return proxy; - }); - }, - }; - } - // rename by regex // keywords: [{expr: "string format regex", now: "now"}] function RegexRenameOperator(regex) { @@ -1644,21 +1631,6 @@ var ProxyUtils = (function () { }; } - // delete keywords operator - // keywords: ['a', 'b', 'c'] - function KeywordDeleteOperator(keywords) { - const keywords_ = keywords.map((k) => { - return { - old: k, - now: "", - }; - }); - return { - name: "Keyword Delete Operator", - func: KeywordRenameOperator(keywords_).func, - }; - } - // delete regex operator // regex: ['a', 'b', 'c'] function RegexDeleteOperator(regex) { @@ -1693,16 +1665,10 @@ var ProxyUtils = (function () { (function () { // interface to get internal operators const $get = (name, args) => { - const item = AVAILABLE_OPERATORS[name]; + const item = PROXY_PROCESSORS[name]; return item(args); }; - const $process = (item, proxies) => { - if (item.name.indexOf("Filter") !== -1) { - return ApplyOperator(item, proxies); - } else if (item.name.indexOf("Operator") !== -1) { - return ApplyFilter(item, proxies); - } - }; + const $process = ApplyProcessor; eval(script); output = operator(proxies); })(); @@ -1712,19 +1678,6 @@ var ProxyUtils = (function () { } /**************************** Filters ***************************************/ - // filter by keywords - function KeywordFilter({keywords = [], keep = true}) { - return { - name: "Keyword Filter", - func: (proxies) => { - return proxies.map((proxy) => { - const selected = keywords.some((k) => proxy.name.indexOf(k) !== -1); - return keep ? selected : !selected; - }); - }, - }; - } - // filter useless proxies function UselessFilter() { const KEYWORDS = [ @@ -1738,8 +1691,8 @@ var ProxyUtils = (function () { ]; return { name: "Useless Filter", - func: KeywordFilter({ - keywords: KEYWORDS, + func: RegexFilter({ + regex: KEYWORDS, keep: false, }).func, }; @@ -2031,7 +1984,6 @@ var ProxyUtils = (function () { } return { - "Keyword Filter": KeywordFilter, "Useless Filter": UselessFilter, "Region Filter": RegionFilter, "Regex Filter": RegexFilter, @@ -2041,9 +1993,7 @@ var ProxyUtils = (function () { "Set Property Operator": SetPropertyOperator, "Flag Operator": FlagOperator, "Sort Operator": SortOperator, - "Keyword Sort Operator": KeywordSortOperator, - "Keyword Rename Operator": KeywordRenameOperator, - "Keyword Delete Operator": KeywordDeleteOperator, + "Regex Sort Operator": RegexSortOperator, "Regex Rename Operator": RegexRenameOperator, "Regex Delete Operator": RegexDeleteOperator, "Script Operator": ScriptOperator, diff --git a/backend/sub-store.min.js b/backend/sub-store.min.js index 2d81148..6625c54 100644 --- a/backend/sub-store.min.js +++ b/backend/sub-store.min.js @@ -1,2 +1,2 @@ -// UPDATED AT: 2020年12月 3日 星期四 15时47分16秒 CST -const $=API("sub-store"),Base64=new Base64Code;function service(){console.log("\n┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅\n 𝑺𝒖𝒃-𝑺𝒕𝒐𝒓𝒆 © 𝑷𝒆𝒏𝒈-𝒀𝑴\n┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅\n");const e=express(),t="settings",s="subs",r="collections",n="builtin";function o(e){const t=Object.keys(e);let s="";for(let r of t)if(/USER-AGENT/i.test(r)){s=e[r];break}return-1!==s.indexOf("Quantumult%20X")?"QX":-1!==s.indexOf("Surge")?"Surge":-1!==s.indexOf("Decar")||-1!==s.indexOf("Loon")?"Loon":null}async function a(e,t=!0){const s=HTTP({headers:{"User-Agent":"Quantumult%20X"}}),r="#"+Base64.safeEncode(e),n=$.read(r),o=`#TIME-${Base64.safeEncode(e)}`,a=(new Date).getTime()-$.read(o)>864e5;if(t&&n&&!a)return $.log(`Use cached for url: ${e}`),n;let i="";try{i=(await s.get(e)).body}catch(e){throw new Error(e)}finally{$.write(i,r),$.write((new Date).getTime(),o)}if(0===i.replace(/\s/g,"").length)throw new Error("订阅内容为空!");return i}$.read(s)||$.write({},s),$.read(r)||$.write({},r),$.read(t)||$.write({},t),$.read("rules")||$.write({},"rules"),$.write({rules:getBuiltInRules()},n),e.get("/download/collection/:name",async function(e,t){const{name:n}=e.params,{cache:i}=e.query||"false",p=e.query.target||o(e.headers)||"JSON",l=$.read(r),c=$.read(s),u=l[n];if($.info(`正在下载组合订阅:${n}`),u){const e=u.subscriptions;let s=[];for(let t=0;te!==n);$.write(a,r),t.json({status:"success"})}),e.route("/api/subs").get(function(e,t){const r=$.read(s);t.json({status:"success",data:r})}).post(function(e,t){const r=e.body,n=$.read(s);$.info(`正在创建订阅: ${r.name}`),n[r.name]&&t.status(500).json({status:"failed",message:`订阅${r.name}已存在!`});/^[\w-_]*$/.test(r.name)?(n[r.name]=r,$.write(n,s),t.status(201).json({status:"success",data:r})):t.status(500).json({status:"failed",message:`订阅名称 ${r.name} 中含有非法字符!名称中只能包含英文字母、数字、下划线、横杠。`})}),e.route("/api/collection/:name").get(function(e,t){const{name:s}=e.params,n=$.read(r)[s];n?t.json({status:"success",data:n}):t.status(404).json({status:"failed",message:`未找到订阅集:${s}!`})}).patch(function(e,t){const{name:s}=e.params;let n=e.body;const o=$.read(r);if(o[s]){const e={...o[s],...n};$.info(`正在更新组合订阅:${s}...`),delete o[s],o[n.name||s]=e,$.write(o,r),t.json({status:"success",data:e})}else t.status(500).json({status:"failed",message:`订阅集${s}不存在,无法更新!`})}).delete(function(e,t){const{name:s}=e.params;$.info(`正在删除组合订阅:${s}`);let n=$.read(r);delete n[s],$.write(n,r),t.json({status:"success"})}),e.route("/api/collections").get(function(e,t){const s=$.read(r);t.json({status:"success",data:s})}).post(function(e,t){const s=e.body;$.info(`正在创建组合订阅:${s.name}`);const n=$.read(r);n[s.name]&&t.status(500).json({status:"failed",message:`订阅集${s.name}已存在!`});/^[\w-_]*$/.test(s.name)?(n[s.name]=s,$.write(n,r),t.status(201).json({status:"success",data:s})):t.status(500).json({status:"failed",message:`订阅集名称 ${s.name} 中含有非法字符!名称中只能包含英文字母、数字、下划线、横杠。`})}),e.get("/download/rule/:name",async function(e,t){const{name:s}=e.params,{builtin:r}=e.query,a=e.query.target||o(e.headers)||"Surge";let i;$.info(`正在下载${r?"内置":""}分流订阅:${s}...`),r&&(i=$.read(n).rules[s]);if(i){let e=[];for(let t=0;t{t.json($.read("#sub-store"))}).post((e,t)=>{const s=e.body;$.write(JSON.stringify(s),"#sub-store"),t.end()}),e.route("/api/settings").get(function(e,s){const r=$.read(t);s.json(r)}).patch(function(e,s){const r=e.body,n=$.read(t);$.write({...n,...r},t),s.json({status:"success"})}),e.get("/api/utils/IP_API/:server",async function(e,t){const s=decodeURIComponent(e.params.server),r=await $.http.get(`http://ip-api.com/json/${s}?lang=zh-CN`).then(e=>JSON.parse(e.body));t.json(r)}),e.post("/api/utils/refresh",async function(e,t){const{url:s}=e.body;$.info(`Refreshing cache for URL: ${s}`);try{const e=await a(s,!1);$.write(e,`#${Base64.safeEncode(s)}`),t.json({status:"success"})}catch(e){t.status(500).json({status:"failed",message:`无法刷新资源 ${s}: ${e}`})}}),e.get("/api/utils/env",function(e,t){const{isNode:s,isQX:r,isLoon:n,isSurge:o}=ENV();let a="Node";s&&(a="Node");r&&(a="QX");n&&(a="Loon");o&&(a="Surge");t.json({backend:a})}),e.get("/api/utils/backup",async function(e,s){const{action:r}=e.query,{gistToken:n}=$.read(t);if(n){const e=new Gist("Auto Generated Sub-Store Backup",n);try{let t;switch(r){case"upload":t=$.read("#sub-store"),$.info("上传备份中..."),await e.upload(t);break;case"download":$.info("还原备份中..."),t=await e.download(),$.write(t,"#sub-store")}s.json({status:"success"})}catch(e){const t=`${"upload"===r?"上传":"下载"}备份失败!${e}`;$.error(t),s.status(500).json({status:"failed",message:t})}}else s.status(500).json({status:"failed",message:"未找到Gist备份Token!"})}),e.get("/",async(e,t)=>{t.set("location","https://sub-store.vercel.app/").status(302).end()}),ENV().isQX&&e.options("/",async(e,t)=>{t.status(200).end()}),e.all("/",(e,t)=>{t.send("Hello from sub-store, made with ❤️ by Peng-YM")}),e.start()}service();var ProxyUtils=function(){const PROXY_PREPROCESSORS=function(){return[{name:"HTML",test:e=>/^/.test(e),parse:e=>""},function(){const e=["dm1lc3M","c3NyOi8v","dHJvamFu","c3M6Ly","c3NkOi8v"];return{name:"Base64 Pre-processor",test:function(t){return e.some(e=>-1!==t.indexOf(e))},parse:function(e){return e=Base64.safeDecode(e)}}}(),{name:"Clash Pre-processor",test:function(e){return/proxies/.test(e)},parse:function(e){return-1!==e.indexOf("{")&&(e=e.replace(/ - /g," - ").replace(/:(?!\s)/g,": ").replace(/\,\"/g,', "').replace(/: {/g,": {, ").replace(/, (\"?host|path|tls|mux|skip\"?)/g,", $1").replace(/{name: /g,'{name: "').replace(/, server:/g,'", server:').replace(/{|}/g,"").replace(/,/g,"\n ")),e=-1===(e=e.replace(/ -\n.*name/g," - name").replace(/\$|\`/g,"").split("proxy-providers:")[0].split("proxy-groups:")[0].replace(/\"([\w-]+)\"\s*:/g,"$1:")).indexOf("proxies:")?"proxies:\n"+e:"proxies:"+e.split("proxies:")[1],YAML.eval(e).proxies.map(e=>JSON.stringify(e)).join("\n")}},{name:"SSD Pre-processor",test:function(e){return 0===e.indexOf("ssd://")},parse:function(e){const t=[];let s=JSON.parse(Base64.safeDecode(e.split("ssd://")[1]));s.traffic_used,s.traffic_total,s.expiry,s.airport;let r=s.port,n=s.encryption,o=s.password,a=s.servers;for(let e=0;e{let[t,n]=e.split("=");if(t=t.trim(),n=n.trim(),-1!==r.indexOf(t)){s.type=t;const e=n.split(":");s.server=e[0],s.port=e[1]}else s[t.trim()]=n.trim()}),s}function t(){return{name:"Loon HTTP Parser",test:e=>/^.*=\s*http/i.test(e.split(",")[0])&&5===e.split(",").length&&-1===e.indexOf("username")&&-1===e.indexOf("password"),parse:e=>{const t=e.split("=")[1].split(","),s={name:e.split("=")[0].trim(),type:"http",server:t[1],port:t[2],tls:"443"===t[2]};return t[3]&&(s.username=t[3]),t[4]&&(s.password=t[4]),s.tls&&(s.sni=t["tls-name"]||s.server,s["skip-cert-verify"]=JSON.parse(t["skip-cert-verify"]||"false")),s}}}function s(e){const t={};t.name=e.split("=")[0].trim();const s=e.split(",");t.server=s[1].trim(),t.port=s[2].trim();for(let e=3;e/^ss:\/\//.test(e),parse:e=>{const t={};let s=e.split("ss://")[1];const r={name:decodeURIComponent(e.split("#")[1]),type:"ss",supported:t},n=(s=s.split("#")[0]).match(/@([^\/]*)(\/|$)/)[1],o=n.lastIndexOf(":");r.server=n.substring(0,o),r.port=n.substring(o+1);const a=Base64.safeDecode(s.split("@")[0]).split(":");if(r.cipher=a[0],r.password=a[1],-1!==s.indexOf("?plugin=")){const e=("plugin="+decodeURIComponent(s.split("?plugin=")[1].split("&")[0])).split(";"),n={};for(const t of e){const[e,s]=t.split("=");e&&(n[e]=s||!0)}switch(n.plugin){case"obfs-local":case"simple-obfs":r.plugin="obfs",r["plugin-opts"]={mode:n.obfs,host:n["obfs-host"]};break;case"v2ray-plugin":r.supported={...t,Loon:!1,Surge:!1},r.obfs="v2ray-plugin",r["plugin-opts"]={mode:"websocket",host:n["obfs-host"],path:n.path||"",tls:n.tls||!1};break;default:throw new Error(`Unsupported plugin option: ${n.plugin}`)}}return r}},function(){const e={Surge:!1};return{name:"URI SSR Parser",test:e=>/^ssr:\/\//.test(e),parse:t=>{let s=(t=Base64.safeDecode(t.split("ssr://")[1])).indexOf(":origin");-1===s&&(s=t.indexOf(":auth_"));const r=t.substring(0,s),n=r.substring(0,r.lastIndexOf(":")),o=r.substring(r.lastIndexOf(":")+1);let a=t.substring(s+1).split("/?")[0].split(":"),i={type:"ssr",server:n,port:o,protocol:a[0],cipher:a[1],obfs:a[2],password:Base64.safeDecode(a[3]),supported:e};const p={};if((t=t.split("/?")[1].split("&")).length>1)for(const e of t){const[t,s]=e.split("=");p[t]=s.trim()}return i={...i,name:p.remarks?Base64.safeDecode(p.remarks):i.server,"protocol-param":Base64.safeDecode(p.protoparam||"").replace(/\s/g,""),"obfs-param":Base64.safeDecode(p.obfsparam||"").replace(/\s/g,"")}}}}(),{name:"URI VMess Parser",test:e=>/^vmess:\/\//.test(e),parse:e=>{const t={};e=e.split("vmess://")[1];const s=Base64.safeDecode(e);if(/=\s*vmess/.test(s)){const e=s.split(",").map(e=>e.trim()),t={};for(const s of e)if(-1!==s.indexOf("=")){const[e,r]=s.split("=");t[e.trim()]=r.trim()}const r={name:e[0].split("=")[0].trim(),type:"vmess",server:e[1],port:e[2],cipher:e[3],uuid:e[4].match(/^"(.*)"$/)[1],tls:"over-tls"===t.obfs||"wss"===t.obfs};if(void 0!==t["udp-relay"]&&(r.udp=JSON.parse(t["udp-relay"])),void 0!==t["fast-open"]&&(r.udp=JSON.parse(t["fast-open"])),"ws"===t.obfs||"wss"===t.obfs){r.network="ws",r["ws-path"]=(t["obfs-path"]||'"/"').match(/^"(.*)"$/)[1];let e=t["obfs-header"];e&&-1!==e.indexOf("Host")&&(e=e.match(/Host:\s*([a-zA-Z0-9-.]*)/)[1]),r["ws-headers"]={Host:e||r.server}}return r.tls&&"false"===t['"tls-verification"']&&(r["skip-cert-verify"]=!0),r.tls&&t["obfs-host"]&&(r.sni=t["obfs-host"]),r}{const e=JSON.parse(s),r={name:e.ps,type:"vmess",server:e.add,port:e.port,cipher:"auto",uuid:e.id,alterId:e.aid||0,tls:"tls"===e.tls||!0===e.tls,supported:t};return"ws"===e.net&&(r.network="ws",r["ws-path"]=e.path,r["ws-headers"]={Host:e.host||e.add},r.tls&&e.host&&(r.sni=e.host)),!1===e.verify_cert&&(r["skip-cert-verify"]=!0),r}}},{name:"URI Trojan Parser",test:e=>/^trojan:\/\//.test(e),parse:e=>{if(-1===e.indexOf(":443"))throw new Error("Trojan port should always be 443!");const t=(e=e.split("trojan://")[1]).split("@")[1].split(":443")[0];return{name:decodeURIComponent(e.split("#")[1].trim())||`[Trojan] ${t}`,type:"trojan",server:t,port:443,password:e.split("@")[0],supported:{}}}},{name:"Clash Parser",test:e=>{try{JSON.parse(e)}catch(e){return!1}return!0},parse:e=>JSON.parse(e)},{name:"Surge SS Parser",test:e=>/^.*=\s*ss/.test(e.split(",")[0]),parse:e=>{const t=s(e),r={name:t.name,type:"ss",server:t.server,port:t.port,cipher:t["encrypt-method"],password:t.password,tfo:JSON.parse(t.tfo||"false"),udp:JSON.parse(t["udp-relay"]||"false")};return t.obfs&&(r.plugin="obfs",r["plugin-opts"]={mode:t.obfs,host:t["obfs-host"]}),r}},{name:"Surge VMess Parser",test:e=>/^.*=\s*vmess/.test(e.split(",")[0])&&-1!==e.indexOf("username"),parse:e=>{const t=s(e),r={name:t.name,type:"vmess",server:t.server,port:t.port,uuid:t.username,alterId:0,cipher:"none",tls:JSON.parse(t.tls||"false"),tfo:JSON.parse(t.tfo||"false")};return r.tls&&(void 0!==t["skip-cert-verify"]&&(r["skip-cert-verify"]=!0===t["skip-cert-verify"]||"1"===t["skip-cert-verify"]),r.sni=t.sni||t.server),JSON.parse(t.ws||"false")&&(r.network="ws",r["ws-path"]=t["ws-path"],r["ws-headers"]={Host:t.sni}),r}},{name:"Surge Trojan Parser",test:e=>/^.*=\s*trojan/.test(e.split(",")[0])&&-1!==e.indexOf("sni"),parse:e=>{const t=s(e),r={name:t.name,type:"trojan",server:t.server,port:t.port,password:t.password,sni:t.sni||t.server,tfo:JSON.parse(t.tfo||"false")};return void 0!==t["skip-cert-verify"]&&(r["skip-cert-verify"]=!0===t["skip-cert-verify"]||"1"===t["skip-cert-verify"]),r}},{name:"Surge HTTP Parser",test:e=>/^.*=\s*http/.test(e.split(",")[0])&&!t().test(e),parse:e=>{const t=s(e),r={name:t.name,type:"http",server:t.server,port:t.port,tls:JSON.parse(t.tls||"false"),tfo:JSON.parse(t.tfo||"false")};return r.tls&&(void 0!==t["skip-cert-verify"]&&(r["skip-cert-verify"]=!0===t["skip-cert-verify"]||"1"===t["skip-cert-verify"]),r.sni=t.sni||t.server),t.username&&"none"!==t.username&&(r.username=t.username),t.password&&"none"!==t.password&&(r.password=t.password),r}},{name:"Loon SS Parser",test:e=>"shadowsocks"===e.split(",")[0].split("=")[1].trim().toLowerCase(),parse:e=>{const t=e.split("=")[1].split(","),s={name:e.split("=")[0].trim(),type:"ss",server:t[1],port:t[2],cipher:t[3],password:t[4].replace(/"/g,"")};return t.length>5&&(s.plugin="obfs",s["plugin-opts"]={mode:t[5],host:t[6]}),s}},{name:"Loon SSR Parser",test:e=>"shadowsocksr"===e.split(",")[0].split("=")[1].trim().toLowerCase(),parse:e=>{const t=e.split("=")[1].split(",");return{name:e.split("=")[0].trim(),type:"ssr",server:t[1],port:t[2],cipher:t[3],password:t[4].replace(/"/g,""),protocol:t[5],"protocol-param":t[6].match(/{(.*)}/)[1],supported:{Surge:!1},obfs:t[7],"obfs-param":t[8].match(/{(.*)}/)[1]}}},{name:"Loon VMess Parser",test:e=>/^.*=\s*vmess/i.test(e.split(",")[0])&&-1===e.indexOf("username"),parse:e=>{let t=e.split("=")[1].split(",");const s={name:e.split("=")[0].trim(),type:"vmess",server:t[1],port:t[2],cipher:t[3]||"none",uuid:t[4].replace(/"/g,""),alterId:0};t=t.splice(5);for(const e of t){const[s,r]=e.split(":");t[s]=r}switch(s.tls=JSON.parse(t["over-tls"]||"false"),s.tls&&(s.sni=t["tls-name"]||s.server,s["skip-cert-verify"]=JSON.parse(t["skip-cert-verify"]||"false")),t.transport){case"tcp":break;case"ws":s.network=t.transport,s["ws-path"]=t.path,s["ws-headers"]={Host:t.host}}return s.tls&&(s["skip-cert-verify"]=JSON.parse(t["skip-cert-verify"]||"false")),s}},{name:"Loon Trojan Parser",test:e=>/^.*=\s*trojan/i.test(e.split(",")[0])&&-1===e.indexOf("password"),parse:e=>{const t=e.split("=")[1].split(","),s={name:e.split("=")[0].trim(),type:"trojan",server:t[1],port:t[2],password:t[3].replace(/"/g,""),sni:t[1],"skip-cert-verify":JSON.parse(t["skip-cert-verify"]||"false")};if(t.length>4){const[r,n]=t[4].split(":");if("tls-name"!==r)throw new Error(`Unknown option ${r} for line: \n${e}`);s.sni=n}return s}},t(),{name:"QX SS Parser",test:e=>/^shadowsocks\s*=/.test(e.split(",")[0].trim())&&-1===e.indexOf("ssr-protocol"),parse:t=>{const s=e(t),r={name:s.tag,type:"ss",server:s.server,port:s.port,cipher:s.method,password:s.password,udp:JSON.parse(s["udp-relay"]||"false"),tfo:JSON.parse(s["fast-open"]||"false"),supported:{}};if(s.obfs)switch(r["plugin-opts"]={host:s["obfs-host"]||r.server},s.obfs){case"http":case"tls":r.plugin="obfs",r["plugin-opts"].mode=s.obfs;break;case"ws":case"wss":r["plugin-opts"]={...r["plugin-opts"],mode:"websocket",path:s["obfs-uri"]||"/",tls:"wss"===s.obfs},r["plugin-opts"].tls&&void 0!==s["tls-verification"]&&(r["plugin-opts"]["skip-cert-verify"]=s["tls-verification"]),r.plugin="v2ray-plugin",r.supported.Surge=!1,r.supported.Loon=!1}return r}},{name:"QX SSR Parser",test:e=>/^shadowsocks\s*=/.test(e.split(",")[0].trim())&&-1!==e.indexOf("ssr-protocol"),parse:t=>{const s=e(t),r={name:s.tag,type:"ssr",server:s.server,port:s.port,cipher:s.method,password:s.password,protocol:s["ssr-protocol"],obfs:"plain","protocol-param":s["ssr-protocol-param"],udp:JSON.parse(s["udp-relay"]||"false"),tfo:JSON.parse(s["fast-open"]||"false"),supported:{Surge:!1}};return s.obfs&&(r.obfs=s.obfs,r["obfs-param"]=s["obfs-host"]),r}},{name:"QX VMess Parser",test:e=>/^vmess\s*=/.test(e.split(",")[0].trim()),parse:t=>{const s=e(t),r={type:"vmess",name:s.tag,server:s.server,port:s.port,cipher:s.method||"none",uuid:s.password,alterId:0,tls:"over-tls"===s.obfs||"wss"===s.obfs,udp:JSON.parse(s["udp-relay"]||"false"),tfo:JSON.parse(s["fast-open"]||"false")};return r.tls&&(r.sni=s["obfs-host"]||s.server,r["skip-cert-verify"]=!JSON.parse(s["tls-verification"]||"true")),"ws"!==s.obfs&&"wss"!==s.obfs||(r.network="ws",r["ws-path"]=s["obfs-uri"],r["ws-headers"]={Host:s["obfs-host"]||s.server}),r}},{name:"QX Trojan Parser",test:e=>/^trojan\s*=/.test(e.split(",")[0].trim()),parse:t=>{const s=e(t),r={type:"trojan",name:s.tag,server:s.server,port:s.port,password:s.password,sni:s["tls-host"]||s.server,udp:JSON.parse(s["udp-relay"]||"false"),tfo:JSON.parse(s["fast-open"]||"false")};return r["skip-cert-verify"]=!JSON.parse(s["tls-verification"]||"true"),r}},{name:"QX HTTP Parser",test:e=>/^http\s*=/.test(e.split(",")[0].trim()),parse:t=>{const s=e(t),r={type:"http",name:s.tag,server:s.server,port:s.port,tls:JSON.parse(s["over-tls"]||"false"),udp:JSON.parse(s["udp-relay"]||"false"),tfo:JSON.parse(s["fast-open"]||"false")};return s.username&&"none"!==s.username&&(r.username=s.username),s.password&&"none"!==s.password&&(r.password=s.password),r.tls&&(r.sni=s["tls-host"]||r.server,r["skip-cert-verify"]=!JSON.parse(s["tls-verification"]||"true")),r}}]}(),PROXY_PROCESSORS=function(){function SetPropertyOperator({key:e,value:t}){return{name:"Set Property Operator",func:s=>s.map(s=>(s[e]=t,s))}}function FlagOperator(e=!0){return{name:"Flag Operator",func:t=>t.map(t=>{if(e){const e=getFlag(t.name);t.name=removeFlag(t.name),t.name=e+" "+t.name,t.name=t.name.replace(/🇹🇼/g,"🇨🇳")}else t.name=removeFlag(t.name);return t})}}function SortOperator(e="asc"){return{name:"Sort Operator",func:t=>{switch(e){case"asc":case"desc":return t.sort((t,s)=>{let r=t.name>s.name?1:-1;return r*="desc"===e?-1:1});case"random":return shuffle(t);default:throw new Error("Unknown sort option: "+e)}}}}function KeywordSortOperator(e){return{name:"Keyword Sort Operator",func:t=>t.sort((t,s)=>{const r=getKeywordOrder(e,t.name),n=getKeywordOrder(e,s.name);return r&&!n?-1:n&&!r?1:r&&n?rt.map(t=>{for(const{old:s,now:r}of e)t.name=t.name.replaceAll(s,r).trim();return t})}}function RegexRenameOperator(e){return{name:"Regex Rename Operator",func:t=>t.map(t=>{for(const{expr:s,now:r}of e)t.name=t.name.replace(new RegExp(s,"g"),r).trim();return t})}}function KeywordDeleteOperator(e){return{name:"Keyword Delete Operator",func:KeywordRenameOperator(e.map(e=>({old:e,now:""}))).func}}function RegexDeleteOperator(e){return{name:"Regex Delete Operator",func:RegexRenameOperator(e.map(e=>({expr:e,now:""}))).func}}function ScriptOperator(script){return{name:"Script Operator",func:proxies=>{let output=proxies;return function(){const $get=(e,t)=>{return(0,AVAILABLE_OPERATORS[e])(t)},$process=(e,t)=>-1!==e.name.indexOf("Filter")?ApplyOperator(e,t):-1!==e.name.indexOf("Operator")?ApplyFilter(e,t):void 0;eval(script),output=operator(proxies)}(),output}}}function KeywordFilter({keywords:e=[],keep:t=!0}){return{name:"Keyword Filter",func:s=>s.map(s=>{const r=e.some(e=>-1!==s.name.indexOf(e));return t?r:!r})}}function UselessFilter(){return{name:"Useless Filter",func:KeywordFilter({keywords:["网址","流量","时间","应急","过期","Bandwidth","expire"],keep:!1}).func}}function RegionFilter(e){const t={HK:"🇭🇰",TW:"🇹🇼",US:"🇺🇸",SG:"🇸🇬",JP:"🇯🇵",UK:"🇬🇧"};return{name:"Region Filter",func:s=>s.map(s=>{const r=getFlag(s.name);return e.some(e=>t[e]===r)})}}function RegexFilter({regex:e=[],keep:t=!0}){return{name:"Regex Filter",func:s=>s.map(s=>{const r=e.some(e=>(e=new RegExp(e)).test(s.name));return t?r:!r})}}function TypeFilter(e){return{name:"Type Filter",func:t=>t.map(t=>e.some(e=>t.type===e))}}function ScriptFilter(script){return{name:"Script Filter",func:proxies=>{let output=FULL(proxies.length,!0);return function(){eval(script),output=filter(proxies)}(),output}}}function getFlag(e){const t={"🇦🇨":["AC"],"🇦🇹":["奥地利","维也纳"],"🇦🇺":["AU","Australia","Sydney","澳大利亚","澳洲","墨尔本","悉尼"],"🇧🇪":["BE","比利时"],"🇧🇬":["保加利亚","Bulgaria"],"🇧🇷":["BR","Brazil","巴西","圣保罗"],"🇨🇦":["CA","Canada","Waterloo","加拿大","蒙特利尔","温哥华","楓葉","枫叶","滑铁卢","多伦多"],"🇨🇭":["瑞士","苏黎世","Switzerland"],"🇩🇪":["DE","German","GERMAN","德国","德國","法兰克福"],"🇩🇰":["丹麦"],"🇪🇸":["ES","西班牙","Spain"],"🇪🇺":["EU","欧盟","欧罗巴"],"🇫🇮":["Finland","芬兰","赫尔辛基"],"🇫🇷":["FR","France","法国","法國","巴黎"],"🇬🇧":["UK","GB","England","United Kingdom","英国","伦敦","英"],"🇲🇴":["MO","Macao","澳门","CTM"],"🇭🇺":["匈牙利","Hungary"],"🇭🇰":["HK","Hongkong","Hong Kong","香港","深港","沪港","呼港","HKT","HKBN","HGC","WTT","CMI","穗港","京港","港"],"🇮🇩":["Indonesia","印尼","印度尼西亚","雅加达"],"🇮🇪":["Ireland","爱尔兰","都柏林"],"🇮🇳":["India","印度","孟买","Mumbai"],"🇰🇵":["KP","朝鲜"],"🇰🇷":["KR","Korea","KOR","韩国","首尔","韩","韓"],"🇱🇻":["Latvia","Latvija","拉脱维亚"],"🇲🇽️":["MEX","MX","墨西哥"],"🇲🇾":["MY","Malaysia","马来西亚","吉隆坡"],"🇳🇱":["NL","Netherlands","荷兰","荷蘭","尼德蘭","阿姆斯特丹"],"🇵🇭":["PH","Philippines","菲律宾"],"🇷🇴":["RO","罗马尼亚"],"🇷🇺":["RU","Russia","俄罗斯","俄羅斯","伯力","莫斯科","圣彼得堡","西伯利亚","新西伯利亚","京俄","杭俄"],"🇸🇦":["沙特","迪拜"],"🇸🇪":["SE","Sweden"],"🇸🇬":["SG","Singapore","新加坡","狮城","沪新","京新","泉新","穗新","深新","杭新","广新"],"🇹🇭":["TH","Thailand","泰国","泰國","曼谷"],"🇹🇷":["TR","Turkey","土耳其","伊斯坦布尔"],"🇹🇼":["TW","Taiwan","台湾","台北","台中","新北","彰化","CHT","台","HINET"],"🇺🇸":["US","USA","America","United States","美国","美","京美","波特兰","达拉斯","俄勒冈","凤凰城","费利蒙","硅谷","矽谷","拉斯维加斯","洛杉矶","圣何塞","圣克拉拉","西雅图","芝加哥","沪美","哥伦布","纽约"],"🇻🇳":["VN","越南","胡志明市"],"🇮🇹":["Italy","IT","Nachash","意大利","米兰","義大利"],"🇿🇦":["South Africa","南非"],"🇦🇪":["United Arab Emirates","阿联酋"],"🇯🇵":["JP","Japan","日","日本","东京","大阪","埼玉","沪日","穗日","川日","中日","泉日","杭日","深日","辽日","广日"],"🇦🇷":["AR","阿根廷"],"🇳🇴":["Norway","挪威","NO"],"🇨🇳":["CN","China","回国","中国","江苏","北京","上海","广州","深圳","杭州","徐州","青岛","宁波","镇江","back"],"🏳️‍🌈":["流量","时间","应急","过期","Bandwidth","expire"]};for(let s of Object.keys(t))if(t[s].some(t=>-1!==e.indexOf(t)))return s;return(e.match(/[\uD83C][\uDDE6-\uDDFF][\uD83C][\uDDE6-\uDDFF]/)||[])[0]||"🏴‍☠️"}function removeFlag(e){return e.replace(/[\uD83C][\uDDE6-\uDDFF][\uD83C][\uDDE6-\uDDFF]/g,"").trim()}function shuffle(e){let t,s,r=e.length;for(;0!==r;)s=Math.floor(Math.random()*r),t=e[r-=1],e[r]=e[s],e[s]=t;return e}return{"Keyword Filter":KeywordFilter,"Useless Filter":UselessFilter,"Region Filter":RegionFilter,"Regex Filter":RegexFilter,"Type Filter":TypeFilter,"Script Filter":ScriptFilter,"Set Property Operator":SetPropertyOperator,"Flag Operator":FlagOperator,"Sort Operator":SortOperator,"Keyword Sort Operator":KeywordSortOperator,"Keyword Rename Operator":KeywordRenameOperator,"Keyword Delete Operator":KeywordDeleteOperator,"Regex Rename Operator":RegexRenameOperator,"Regex Delete Operator":RegexDeleteOperator,"Script Operator":ScriptOperator}}(),PROXY_PRODUCERS=function(){return{QX:{produce:e=>{let t,s;switch(e.type){case"ss":if(t="","obfs"===e.plugin){const{host:s,mode:r}=e["plugin-opts"];t=`,obfs=${r}${s?",obfs-host="+s:""}`}if("v2ray-plugin"===e.plugin){const{tls:s,host:r,path:n}=e["plugin-opts"];t=`,obfs=${s?"wss":"ws"}${r?",obfs-host="+r:""}${n?",obfs-uri="+n:""}`}return`shadowsocks=${e.server}:${e.port},method=${e.cipher},password=${e.password}${t}${e.tfo?",fast-open=true":",fast-open=false"}${e.udp?",udp-relay=true":",udp-relay=false"},tag=${e.name}`;case"ssr":return`shadowsocks=${e.server}:${e.port},method=${e.cipher},password=${e.password},ssr-protocol=${e.protocol}${e["protocol-param"]?",ssr-protocol-param="+e["protocol-param"]:""}${e.obfs?",obfs="+e.obfs:""}${e["obfs-param"]?",obfs-host="+e["obfs-param"]:""}${e.tfo?",fast-open=true":",fast-open=false"}${e.udp?",udp-relay=true":",udp-relay=false"},tag=${e.name}`;case"vmess":return t="","ws"===e.network?t=e.tls?`,obfs=wss${e.sni?",obfs-host="+e.sni:""}${e["ws-path"]?",obfs-uri="+e["ws-path"]:""},tls-verification=${e["skip-cert-verify"]?"false":"true"}`:`,obfs=ws${e["ws-headers"].Host?",obfs-host="+e["ws-headers"].Host:""}${e["ws-path"]?",obfs-uri="+e["ws-path"]:""}`:e.tls&&(t=`,obfs=over-tls${e.sni?",obfs-host="+e.sni:""},tls-verification=${e["skip-cert-verify"]?"false":"true"}`),`vmess=${e.server}:${e.port},method=${"auto"===e.cipher?"none":e.cipher},password=${e.uuid}${t}${e.tfo?",fast-open=true":",fast-open=false"}${e.udp?",udp-relay=true":",udp-relay=false"},tag=${e.name}`;case"trojan":return`trojan=${e.server}:${e.port},password=${e.password}${e.sni?",tls-host="+e.sni:""},over-tls=true,tls-verification=${e["skip-cert-verify"]?"false":"true"}${e.tfo?",fast-open=true":",fast-open=false"}${e.udp?",udp-relay=true":",udp-relay=false"},tag=${e.name}`;case"http":return s="",e.tls&&(s=`,over-tls=true,tls-verification=${e["skip-cert-verify"]?"false":"true"}${e.sni?",tls-host="+e.sni:""}`),`http=${e.server}:${e.port},username=${e.username},password=${e.password}${s}${e.tfo?",fast-open=true":",fast-open=false"},tag=${e.name}`}throw new Error(`Platform QX does not support proxy type: ${e.type}`)}},Surge:{produce:e=>{let t,s;switch(e.type){case"ss":if(t="",e.plugin){const{host:s,mode:r}=e["plugin-opts"];if("obfs"!==e.plugin)throw new Error(`Platform Surge does not support obfs option: ${e.obfs}`);t=`,obfs=${r}${s?",obfs-host="+s:""}`}return`${e.name}=ss,${e.server}, ${e.port},encrypt-method=${e.cipher},password=${e.password}${t},tfo=${e.tfo||"false"},udp-relay=${e.udp||"false"}`;case"vmess":s="";let r=`${e.name}=vmess,${e.server},${e.port},username=${e.uuid},tls=${e.tls||"false"},tfo=${e.tfo||"false"}`;if("ws"===e.network){const t=e["ws-path"]||"/",s=e["ws-headers"].Host;r+=`,ws=true${t?",ws-path="+t:""}${s?",ws-headers=HOST:"+s:""}`}return e.tls&&(r+=`${void 0!==e["skip-cert-verify"]?",skip-cert-verify="+e["skip-cert-verify"]:""}`,r+=e.sni?`,sni=${e.sni}`:""),r;case"trojan":return`${e.name}=trojan,${e.server},${e.port},password=${e.password}${void 0!==e["skip-cert-verify"]?",skip-cert-verify="+e["skip-cert-verify"]:""}${e.sni?",sni="+e.sni:""},tfo=${e.tfo||"false"}`;case"http":return s=", tls=false",e.tls&&(s=`,tls=true,skip-cert-verify=${e["skip-cert-verify"]},sni=${e.sni}`),`${e.name}=http, ${e.server}, ${e.port}${e.username?",username="+e.username:""}${e.password?",password="+e.password:""}${s},tfo=${e.tfo||"false"}`}throw new Error(`Platform Surge does not support proxy type: ${e.type}`)}},Loon:{produce:e=>{let t,s;switch(e.type){case"ss":if(t=",,",e.plugin){if("obfs"!==e.plugin)throw new Error(`Platform Loon does not support obfs option: ${e.obfs}`);{const{mode:s,host:r}=e["plugin-opts"];t=`,${s},${r||""}`}}return`${e.name}=shadowsocks,${e.server},${e.port},${e.cipher},"${e.password}"${t}`;case"ssr":return`${e.name}=shadowsocksr,${e.server},${e.port},${e.cipher},"${e.password}",${e.protocol},{${e["protocol-param"]||""}},${e.obfs},{${e["obfs-param"]||""}}`;case"vmess":return t="",t="ws"===e.network?`,transport:ws,host:${e["ws-headers"].Host||e.server},path:${e["ws-path"]||"/"}`:",transport:tcp",e.tls&&(t+=`${e.sni?",tls-name:"+e.sni:""},skip-cert-verify:${e["skip-cert-verify"]||"false"}`),`${e.name}=vmess,${e.server},${e.port},${"auto"===e.cipher?"none":e.cipher},"${e.uuid}",over-tls:${e.tls||"false"}${t}`;case"trojan":return`${e.name}=trojan,${e.server},${e.port},"${e.password}"${e.sni?",tls-name:"+e.sni:""},skip-cert-verify:${e["skip-cert-verify"]||"false"}`;case"http":s="";const r=`${e.name}=${e.tls?"http":"https"},${e.server},${e.port},${e.username||""},${e.password||""}`;return e.tls?r+(s=`${e.sni?",tls-name:"+e.sni:""},skip-cert-verify:${e["skip-cert-verify"]}`):r}throw new Error(`Platform Loon does not support proxy type: ${e.type}`)}},Clash:{type:"ALL",produce:e=>"proxies:\n"+e.map(e=>(delete e.supported," - "+JSON.stringify(e)+"\n")).join("")},URI:{type:"SINGLE",produce:e=>{let t="";switch(e.type){case"ss":const s=`${e.cipher}:${e.password}`;if(t=`ss://${Base64.safeEncode(s)}@${e.server}:${e.port}/`,e.plugin){t+="?plugin=";const s=e["plugin-opts"];switch(e.plugin){case"obfs":t+=encodeURIComponent(`simple-obfs;obfs=${s.mode}${s.host?";obfs-host="+s.host:""}`);break;case"v2ray-plugin":t+=encodeURIComponent(`v2ray-plugin;obfs=${s.mode}${s.host?";obfs-host"+s.host:""}${s.tls?";tls":""}`);break;default:throw new Error(`Unsupported plugin option: ${e.plugin}`)}}t+=`#${encodeURIComponent(e.name)}`;break;case"ssr":t=`${e.server}:${e.port}:${e.protocol}:${e.cipher}:${e.obfs}:${Base64.safeEncode(e.password)}/`,t+=`?remarks=${Base64.safeEncode(e.name)}${e["obfs-param"]?"&obfsparam="+Base64.safeEncode(e["obfs-param"]):""}${e["protocol-param"]?"&protocolparam="+Base64.safeEncode(e["protocol-param"]):""}`,t="ssr://"+Base64.safeEncode(t);break;case"vmess":t={ps:e.name,add:e.server,port:e.port,id:e.uuid,type:"",aid:0,net:e.network||"tcp",tls:e.tls?"tls":""},"ws"===e.network&&(t.path=e["ws-path"]||"/",t.host=e["ws-headers"].Host||e.server),t="vmess://"+Base64.safeEncode(JSON.stringify(t));break;case"trojan":t=`trojan://${e.password}@${e.server}:${e.port}#${encodeURIComponent(e.name)}`;break;default:throw new Error(`Cannot handle proxy type: ${e.type}`)}return t}},JSON:{type:"ALL",produce:e=>JSON.stringify(e,null,2)}}}();function preprocess(e){for(const t of PROXY_PREPROCESSORS)try{if(t.test(e))return $.info(`Pre-processor [${t.name}] activated`),t.parse(e)}catch(e){$.error(`Parser [${t.name}] failed\n Reason: ${e}`)}return e}function safeMatch(e,t){let s;try{s=e.test(t)}catch(e){s=!1}return s}function parse(e){const t=(e=preprocess(e)).split("\n"),s=[];let r;for(let e of t){if(0===(e=e.trim()).length)continue;let t=r&&safeMatch(r,e);if(!t)for(const s of PROXY_PARSERS)if(safeMatch(s,e)){r=s,t=!0,$.info(`Proxy parser: ${s.name} is activated`);break}if(t)try{const t=r.parse(e);t||$.error(`Parser ${r.name} return nothing for \n${e}\n`),s.push(t)}catch(t){$.error(`Failed to parse line: \n ${e}\n Reason: ${t.stack}`)}else $.error(`Failed to find a rule to parse line: \n${e}\n`)}return s}async function process(e,t=[]){for(const s of t){let t,r;if(-1!==s.type.indexOf("Script")){const{mode:e,content:r}=s.args;t="link"===e?await $.http.get(r).then(e=>e.body).catch(e=>{throw new Error(`Error when downloading remote script: ${s.args.content}.\n Reason: ${e}`)}):r}PROXY_PROCESSORS[s.type]?($.info(`Applying "${s.type}" with arguments:\n >>> ${JSON.stringify(s.args,null,2)||"None"}`),e=ApplyProcessor(r=-1!==s.type.indexOf("Script")?PROXY_PROCESSORS[s.type](t):PROXY_PROCESSORS[s.type](s.args),e)):$.error(`Unknown operator: "${s.type}"`)}return e}function produce(e,t){const s=PROXY_PRODUCERS[t];if(!s)throw new Error(`Target platform: ${t} is not supported!`);return e=e.filter(e=>!(e.supported&&!1===e.supported[t])),$.info(`Producing proxies for target: ${t}`),void 0===s.type||"SINGLE"===s.type?e.map(e=>{try{return s.produce(e)}catch(t){return $.error(`Cannot produce proxy: ${JSON.stringify(e,null,2)}\nReason: ${t}`),""}}).filter(e=>e.length>0).join("\n"):"ALL"===s.type?s.produce(e):void 0}return{parse:parse,process:process,produce:produce}}(),RuleUtils=function(){const e=[[/^(DOMAIN|host|HOST)$/,"DOMAIN"],[/^(DOMAIN-KEYWORD|host-keyword|HOST-KEYWORD)$/,"DOMAIN-KEYWORD"],[/^(DOMAIN-SUFFIX|host-suffix|HOST-SUFFIX)$/,"DOMAIN-SUFFIX"],[/^USER-AGENT$/i,"USER-AGENT"],[/^PROCESS-NAME$/,"PROCESS-NAME"],[/^(DEST-PORT|DST-PORT)$/,"DST-PORT"],[/^SRC-IP(-CIDR)?$/,"SRC-IP"],[/^(IN|SRC)-PORT$/,"IN-PORT"],[/^PROTOCOL$/,"PROTOCOL"],[/^IP-CIDR$/i,"IP-CIDR"],[/^(IP-CIDR6|ip6-cidr|IP6-CIDR)$/]],t=function(){return[{name:"HTML",test:e=>/^/.test(e),parse:e=>""},{name:"Clash Provider",test:e=>0===e.indexOf("payload:"),parse:e=>e.replace("payload:","").replace(/^\s*-\s*/gm,"")}]}(),s=function(){return[{name:"Universal Rule Parser",test:()=>!0,parse:t=>{const s=t.split("\n"),r=[];for(let t of s)if(0!==(t=t.trim()).length&&!/\s*#/.test(t))try{const s=t.split(",").map(e=>e.trim());let n=s[0],o=!1;for(const t of e)if(t[0].test(n)){o=!0;const e={type:t[1],content:s[1]};"IP-CIDR"!==e.type&&"IP-CIDR6"!==e.type||(e.options=s.slice(2)),r.push(e)}if(!o)throw new Error("Invalid rule type: "+n)}catch(e){console.error(`Failed to parse line: ${t}\n Reason: ${e}`)}return r}}]}(),r=function(){return{"Regex Filter":function({regex:e=[],keep:t=!0}){return{name:"Regex Filter",func:s=>s.map(s=>{const r=e.some(e=>(e=new RegExp(e)).test(s));return t?r:!r})}},"Remove Duplicate Filter":function(){return{name:"Remove Duplicate Filter",func:e=>{const t=new Set,s=[];return e.forEach(e=>{const r=e.options||[];r.sort();const n=`${e.type},${e.content},${JSON.stringify(r)}`;t.has(n)||(s.push(e),t.add(n))}),s}}},"Type Filter":function(e){return{name:"Type Filter",func:t=>t.map(t=>e.some(e=>t.type===e))}},"Regex Replace Operator":function(e){return{name:"Regex Rename Operator",func:t=>t.map(t=>{for(const{expr:s,now:r}of e)t.content=t.content.replace(new RegExp(s,"g"),r).trim();return t})}}}}(),n=function(){return{QX:{type:"SINGLE",func:e=>-1!==["URL-REGEX","DEST-PORT","SRC-IP","IN-PORT","PROTOCOL"].indexOf(e.type)?null:`${{"DOMAIN-KEYWORD":"HOST-KEYWORD","DOMAIN-SUFFIX":"HOST-SUFFIX",DOMAIN:"HOST","IP-CIDR6":"IP6-CIDR"}[e.type]||e.type},${e.content},SUB-STORE`},Surge:{type:"SINGLE",func:e=>{let t=`${e.type},${e.content}`;return"IP-CIDR"!==e.type&&"IP-CIDR6"!==e.type||(t+=e.options?`,${e.options[0]}`:""),t}},Loon:{type:"SINGLE",func:e=>-1!==["DEST-PORT","SRC-IP","IN-PORT","PROTOCOL"].indexOf(e.type)?null:(e=>{let t=`${e.type},${e.content}`;return"IP-CIDR"!==e.type&&"IP-CIDR6"!==e.type||(t+=e.options?`,${e.options[0]}`:""),t})(e)},Clash:{type:"ALL",func:e=>{const t={"DEST-PORT":"DST-PORT","SRC-IP":"SRC-IP-CIDR","IN-PORT":"SRC-PORT"},s={payload:e.map(e=>{let s=`${t[e.type]||e.type},${e.content}`;return"IP-CIDR"!==e.type&&"IP-CIDR6"!==e.type||(s+=e.options?`,${e.options[0]}`:""),s})};return YAML.stringify(s)}}}}();return{parse:function(e){e=function(e){for(const s of t)try{if(s.test(e))return $.info(`Pre-processor [${s.name}] activated`),s.parse(e)}catch(e){$.error(`Parser [${s.name}] failed\n Reason: ${e}`)}return e}(e);for(const t of s){let s;try{s=t.test(e)}catch{s=!1}if(s)return $.info(`Rule parser [${t.name}] is activated!`),t.parse(e)}},process:async function(e,t){for(const s of t){if(!r[s.type]){console.error(`Unknown operator: ${s.type}!`);continue}const t=r[s.type](s.args);$.info(`Applying "${s.type}" with arguments: \n >>> ${JSON.stringify(s.args)||"None"}`),e=ApplyProcessor(t,e)}return e},produce:function(e,t){const s=n[t];if(!s)throw new Error(`Target platform: ${t} is not supported!`);return void 0===s.type||"SINGLE"===s.type?e.map(e=>{try{return s.func(e)}catch(t){return console.log(`ERROR: cannot produce rule: ${JSON.stringify(e)}\nReason: ${t}`),""}}).filter(e=>e.length>0).join("\n"):"ALL"===s.type?s.func(e):void 0}}}();function getBuiltInRules(){return{AD:{name:"AD",description:"",urls:["https://raw.githubusercontent.com/privacy-protection-tools/anti-AD/master/anti-ad-surge.txt","https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/Providers/BanAD.yaml"]},Global:{name:"Global",description:"",urls:["https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/Providers/ProxyGFWlist.yaml","https://raw.githubusercontent.com/DivineEngine/Profiles/master/Quantumult/Filter/Global.list"]},CN:{name:"CN",description:"",urls:["https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/Providers/ChinaDomain.yaml","https://raw.githubusercontent.com/DivineEngine/Profiles/master/Quantumult/Filter/China.list"]}}}function ApplyProcessor(e,t){return-1!==e.name.indexOf("Filter")?function(e,t){let s=FULL(t.length,!0);try{s=AND(s,e.func(t))}catch(t){console.log(`Cannot apply filter ${e.name}\n Reason: ${t}`)}return t.filter((e,t)=>s[t])}(e,t):-1!==e.name.indexOf("Operator")?function(e,t){let s=clone(t);try{const t=e.func(s);t&&(s=t)}catch(t){console.log(`Cannot apply operator ${e.name}! Reason: ${t}`)}return s}(e,t):void 0}function AND(...e){return e.reduce((e,t)=>e.map((e,s)=>t[s]&&e))}function OR(...e){return e.reduce((e,t)=>e.map((e,s)=>t[s]||e))}function NOT(e){return e.map(e=>!e)}function FULL(e,t){return[...Array(e).keys()].map(()=>t)}function clone(e){return JSON.parse(JSON.stringify(e))}function ENV(){const e="undefined"!=typeof $task,t="undefined"!=typeof $loon,s="undefined"!=typeof $httpClient&&!t,r="function"==typeof require&&"undefined"!=typeof $jsbox;return{isQX:e,isLoon:t,isSurge:s,isNode:"function"==typeof require&&!r,isJSBox:r,isRequest:"undefined"!=typeof $request,isScriptable:"undefined"!=typeof importModule}}function HTTP(e={baseURL:""}){const{isQX:t,isLoon:s,isSurge:r,isScriptable:n,isNode:o}=ENV(),a=/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&\/\/=]*)/;const i={};return["GET","POST","PUT","DELETE","HEAD","OPTIONS","PATCH"].forEach(p=>i[p.toLowerCase()]=(i=>(function(i,p){p="string"==typeof p?{url:p}:p;const l=e.baseURL;l&&!a.test(p.url||"")&&(p.url=l?l+p.url:p.url);const c=(p={...e,...p}).timeout,u={onRequest:()=>{},onResponse:e=>e,onTimeout:()=>{},...p.events};let f,d;if(u.onRequest(i,p),t)f=$task.fetch({method:i,...p});else if(s||r||o)f=new Promise((e,t)=>{(o?require("request"):$httpClient)[i.toLowerCase()](p,(s,r,n)=>{s?t(s):e({statusCode:r.status||r.statusCode,headers:r.headers,body:n})})});else if(n){const e=new Request(p.url);e.method=i,e.headers=p.headers,e.body=p.body,f=new Promise((t,s)=>{e.loadString().then(s=>{t({statusCode:e.response.statusCode,headers:e.response.headers,body:s})}).catch(e=>s(e))})}const h=c?new Promise((e,t)=>{d=setTimeout(()=>(u.onTimeout(),t(`${i} URL: ${p.url} exceeds the timeout ${c} ms`)),c)}):null;return(h?Promise.race([h,f]).then(e=>(clearTimeout(d),e)):f).then(e=>u.onResponse(e))})(p,i))),i}function API(e="untitled",t=!1){const{isQX:s,isLoon:r,isSurge:n,isNode:o,isJSBox:a,isScriptable:i}=ENV();return new class{constructor(e,t){this.name=e,this.debug=t,this.http=HTTP(),this.env=ENV(),this.node=(()=>{if(o){return{fs:require("fs")}}return null})(),this.initCache();Promise.prototype.delay=function(e){return this.then(function(t){return((e,t)=>new Promise(function(s){setTimeout(s.bind(null,t),e)}))(e,t)})}}initCache(){if(s&&(this.cache=JSON.parse($prefs.valueForKey(this.name)||"{}")),(r||n)&&(this.cache=JSON.parse($persistentStore.read(this.name)||"{}")),o){let e="root.json";this.node.fs.existsSync(e)||this.node.fs.writeFileSync(e,JSON.stringify({}),{flag:"wx"},e=>console.log(e)),this.root={},e=`${this.name}.json`,this.node.fs.existsSync(e)?this.cache=JSON.parse(this.node.fs.readFileSync(`${this.name}.json`)):(this.node.fs.writeFileSync(e,JSON.stringify({}),{flag:"wx"},e=>console.log(e)),this.cache={})}}persistCache(){const e=JSON.stringify(this.cache,null,2);s&&$prefs.setValueForKey(e,this.name),(r||n)&&$persistentStore.write(e,this.name),o&&(this.node.fs.writeFileSync(`${this.name}.json`,e,{flag:"w"},e=>console.log(e)),this.node.fs.writeFileSync("root.json",JSON.stringify(this.root,null,2),{flag:"w"},e=>console.log(e)))}write(e,t){if(this.log(`SET ${t}`),-1!==t.indexOf("#")){if(t=t.substr(1),n||r)return $persistentStore.write(e,t);if(s)return $prefs.setValueForKey(e,t);o&&(this.root[t]=e)}else this.cache[t]=e;this.persistCache()}read(e){return this.log(`READ ${e}`),-1===e.indexOf("#")?this.cache[e]:(e=e.substr(1),n||r?$persistentStore.read(e):s?$prefs.valueForKey(e):o?this.root[e]:void 0)}delete(e){if(this.log(`DELETE ${e}`),-1!==e.indexOf("#")){if(e=e.substr(1),n||r)return $persistentStore.write(null,e);if(s)return $prefs.removeValueForKey(e);o&&delete this.root[e]}else delete this.cache[e];this.persistCache()}notify(e,t="",p="",l={}){const c=l["open-url"],u=l["media-url"];if(s&&$notify(e,t,p,l),n&&$notification.post(e,t,p+`${u?"\n多媒体:"+u:""}`,{url:c}),r){let s={};c&&(s.openUrl=c),u&&(s.mediaUrl=u),"{}"===JSON.stringify(s)?$notification.post(e,t,p):$notification.post(e,t,p,s)}if(o||i){const s=p+(c?`\n点击跳转: ${c}`:"")+(u?`\n多媒体: ${u}`:"");if(a){require("push").schedule({title:e,body:(t?t+"\n":"")+s})}else console.log(`${e}\n${t}\n${s}\n\n`)}}log(e){this.debug&&console.log(`[${this.name}] LOG: ${e}`)}info(e){console.log(`[${this.name}] INFO: ${e}`)}error(e){console.log(`[${this.name}] ERROR: ${e}`)}wait(e){return new Promise(t=>setTimeout(t,e))}done(e={}){s||r||n?$done(e):o&&!a&&"undefined"!=typeof $context&&($context.headers=e.headers,$context.statusCode=e.statusCode,$context.body=e.body)}}(e,t)}function Gist(e,t){const s=HTTP({baseURL:"https://api.github.com",headers:{Authorization:`token ${t}`,"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.141 Safari/537.36"},events:{onResponse:e=>/^[45]/.test(String(e.statusCode))?Promise.reject(`ERROR: ${JSON.parse(e.body).message}`):e}});async function r(){return s.get("/gists").then(t=>{const s=JSON.parse(t.body);for(let t of s)if(t.description===e)return t.id;return-1})}this.upload=async function(t){const n=await r(),o={"Sub-Store":{content:t}};return-1===n?s.post({url:"/gists",body:JSON.stringify({description:e,public:!1,files:o})}):s.patch({url:`/gists/${n}`,body:JSON.stringify({files:o})})},this.download=async function(){const e=await r();if(-1===e)return Promise.reject("未找到Gist备份!");try{const{files:t}=await s.get(`/gists/${e}`).then(e=>JSON.parse(e.body)),r=t["Sub-Store"].raw_url;return await s.get(r).then(e=>e.body)}catch(e){return Promise.reject(e)}}}function express({port:e}={port:3e3}){const{isNode:t}=ENV(),s={"Content-Type":"text/plain;charset=UTF-8","Access-Control-Allow-Origin":"*","Access-Control-Allow-Methods":"POST,GET,OPTIONS,PATCH,PUT,DELETE","Access-Control-Allow-Headers":"Origin, X-Requested-With, Content-Type, Accept"};if(t){const t=require("express"),r=require("body-parser"),n=t();return n.use(r.json({verify:i})),n.use(r.urlencoded({verify:i,extended:!0})),n.use(r.raw({verify:i,type:"*/*"})),n.use((e,t,r)=>{t.set(s),r()}),n.start=(()=>{n.listen(e,()=>{$.log(`Express started on port: ${e}`)})}),n}const r=[],n=["GET","POST","PUT","DELETE","PATCH","OPTIONS","HEAD'","ALL"],o=(e,t=0)=>{let{method:s,url:n,headers:a,body:i}=e;/json/i.test(a["Content-Type"])&&(i=JSON.parse(i)),s=s.toUpperCase();const{path:u,query:f}=function(e){const t=(e.match(/https?:\/\/[^\/]+(\/[^?]*)/)||[])[1]||"/",s=e.indexOf("?"),r={};if(-1!==s){let t=e.slice(e.indexOf("?")+1).split("&");for(let e=0;em&&(h=r[d],m=e.split("/").length)}if(h){const e=()=>{o(s,n,d)},t={method:s,url:n,path:u,query:f,params:c(h.pattern,u),headers:a,body:i},r=p(),l=h.callback,m=e=>{r.status(500).json({status:"failed",message:`Internal Server Error: ${e}`})};if("AsyncFunction"===l.constructor.name)l(t,r,e).catch(m);else try{l(t,r,e)}catch(e){m(e)}}else{p().status(404).json({status:"failed",message:"ERROR: 404 not found"})}},a={};return n.forEach(e=>{a[e.toLowerCase()]=((t,s)=>{r.push({method:e,pattern:t,callback:s})})}),a.route=(e=>{const t={};return n.forEach(s=>{t[s.toLowerCase()]=(n=>(r.push({method:s,pattern:e,callback:n}),t))}),t}),a.start=(()=>{o($request)}),a;function i(e,t,s,r){s&&s.length&&(e.rawBody=s.toString(r||"utf8"))}function p(){let e=200;const{isQX:t,isLoon:r,isSurge:n}=ENV(),o=s,a={200:"HTTP/1.1 200 OK",201:"HTTP/1.1 201 Created",302:"HTTP/1.1 302 Found",307:"HTTP/1.1 307 Temporary Redirect",308:"HTTP/1.1 308 Permanent Redirect",404:"HTTP/1.1 404 Not Found",500:"HTTP/1.1 500 Internal Server Error"};return new class{status(t){return e=t,this}send(s=""){const i={status:t?a[e]:e,body:s,headers:o};t?$done(i):(r||n)&&$done({response:i})}end(){this.send()}html(e){this.set("Content-Type","text/html;charset=UTF-8"),this.send(e)}json(e){this.set("Content-Type","application/json;charset=UTF-8"),this.send(JSON.stringify(e))}set(e,t){return o[e]=t,this}}}function l(e,t){if(e instanceof RegExp&&e.test(t))return!0;if("/"===e)return!0;if(-1===e.indexOf(":")){const s=t.split("/"),r=e.split("/");for(let e=0;e>>6)+s(128|63&t):s(224|t>>>12&15)+s(128|t>>>6&63)+s(128|63&t):(t=65536+1024*(e.charCodeAt(0)-55296)+(e.charCodeAt(1)-56320),s(240|t>>>18&7)+s(128|t>>>12&63)+s(128|t>>>6&63)+s(128|63&t))},n=/[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g,o=function(t){const s=[0,2,1][t.length%3],r=t.charCodeAt(0)<<16|(t.length>1?t.charCodeAt(1):0)<<8|(t.length>2?t.charCodeAt(2):0);return[e.charAt(r>>>18),e.charAt(r>>>12&63),s>=2?"=":e.charAt(r>>>6&63),s>=1?"=":e.charAt(63&r)].join("")};this.encode=function(e){return"[object Uint8Array]"===Object.prototype.toString.call(e)?e.toString("base64"):function(e){return e.replace(n,r)}(String(e)).replace(/[\s\S]{1,3}/g,o)};const a=/[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g,i=function(e){switch(e.length){case 4:const t=((7&e.charCodeAt(0))<<18|(63&e.charCodeAt(1))<<12|(63&e.charCodeAt(2))<<6|63&e.charCodeAt(3))-65536;return s(55296+(t>>>10))+s(56320+(1023&t));case 3:return s((15&e.charCodeAt(0))<<12|(63&e.charCodeAt(1))<<6|63&e.charCodeAt(2));default:return s((31&e.charCodeAt(0))<<6|63&e.charCodeAt(1))}},p=function(e){const r=e.length,n=r%4,o=(r>0?t[e.charAt(0)]<<18:0)|(r>1?t[e.charAt(1)]<<12:0)|(r>2?t[e.charAt(2)]<<6:0)|(r>3?t[e.charAt(3)]:0),a=[s(o>>>16),s(o>>>8&255),s(255&o)];return a.length-=[0,0,2,1][n],a.join("")},l=function(e){return e.replace(/\S{1,4}/g,p)},c=function(e){return l(e).replace(a,i)};this.decode=function(e){return c(String(e).replace(/[-_]/g,function(e){return"-"===e?"+":"/"}).replace(/[^A-Za-z0-9\+\/]/g,"")).replace(/>/g,">").replace(/</g,"<")},this.safeEncode=function(e){return this.encode(e.replace(/\+/g,"-").replace(/\//g,"_"))},this.safeDecode=function(e){return this.decode(e.replace(/-/g,"+").replace(/_/g,"/"))}}var YAML=function(){var e=[],t=[],s=0,r={regLevel:new RegExp("^([\\s\\-]+)"),invalidLine:new RegExp("^\\-\\-\\-|^\\.\\.\\.|^\\s*#.*|^\\s*$"),dashesString:new RegExp('^\\s*\\"([^\\"]*)\\"\\s*$'),quotesString:new RegExp("^\\s*\\'([^\\']*)\\'\\s*$"),float:new RegExp("^[+-]?[0-9]+\\.[0-9]+(e[+-]?[0-9]+(\\.[0-9]+)?)?$"),integer:new RegExp("^[+-]?[0-9]+$"),array:new RegExp("\\[\\s*(.*)\\s*\\]"),map:new RegExp("\\{\\s*(.*)\\s*\\}"),key_value:new RegExp("([a-z0-9_-][ a-z0-9_-]*):( .+)","i"),single_key_value:new RegExp("^([a-z0-9_-][ a-z0-9_-]*):( .+?)$","i"),key:new RegExp("([a-z0-9_-][ a-z0-9_-]*):( .+)?","i"),item:new RegExp("^-\\s+"),trim:new RegExp("^\\s+|\\s+$"),comment:new RegExp("([^\\'\\\"#]+([\\'\\\"][^\\'\\\"]*[\\'\\\"])*)*(#.*)?")};function n(e){return{parent:null,length:0,level:e,lines:[],children:[],addChild:function(e){this.children.push(e),e.parent=this,++this.length}}}function o(e){var t=null;if("true"==(e=e.replace(r.trim,"")))return!0;if("false"==e)return!1;if(".NaN"==e)return Number.NaN;if("null"==e)return null;if(".inf"==e)return Number.POSITIVE_INFINITY;if("-.inf"==e)return Number.NEGATIVE_INFINITY;if(t=e.match(r.dashesString))return t[1];if(t=e.match(r.quotesString))return t[1];if(t=e.match(r.float))return parseFloat(t[0]);if(t=e.match(r.integer))return parseInt(t[0]);if(isNaN(t=Date.parse(e))){if(t=e.match(r.single_key_value))return(a={})[t[1]]=o(t[2]),a;if(t=e.match(r.array)){for(var s=0,n=" ",a=[],i="",p=!1,l=0,c=t[1].length;l0&&a.push(o(i)),a}if(t=e.match(r.map)){for(s=0,n=" ",a=[],i="",p=!1,l=0,c=t[1].length;l0&&a.push(i);var u={};for(l=0,c=a.length;l"==v[0]?null!=f?f[S]=a(u.shift()):l[S]=a(u.shift()):null!=f?f[S]=o(v):l[S]=o(v)}else null!=f?f[S]=s(u):l[S]=s(u)}else O.match(/^-\s*$/)?(m&&(m=!1,void 0===l.length&&(l=[])),null!=f&&l.push(f),f={},m=!0):(p=O.match(/^-\s*(.*)/))&&(null!=f?f.push(o(p[1])):(m&&(m=!1,void 0===l.length&&(l=[])),l.push(o(p[1]))))}null!=f&&(m&&(m=!1,void 0===l.length&&(l=[])),l.push(f))}for($=h.length-1;$>=0;--$)n.splice.call(n,h[$],1);return l}(s.children)}return{eval:function(o){e=[],t=[],s=(new Date).getTime();var a=p(function(t){var s,o=r.regLevel,a=r.invalidLine,i=t.split("\n"),p=0,l=0,c=[],u=new n(-1),f=new n(0);u.addChild(f);var d=[],h="";c.push(f),d.push(p);for(var m=0,$=i.length;m<$;++m)if(!(h=i[m]).match(a)){if((p=(s=o.exec(h))?s[1].length:0)>l){var g=f;f=new n(p),g.addChild(f),c.push(f),d.push(p)}else if(p=0;--w)if(d[w]==p){f=new n(p),c.push(f),d.push(p),null!=c[w].parent&&c[w].parent.addChild(f),y=!0;break}if(!y)return void e.push("Error: Invalid indentation at line "+m+": "+h)}f.lines.push(h.replace(r.trim,"")),l=p}return u}(function(e){var t,s=e.split("\n"),n=r.comment;for(var o in s)(t="string"==typeof s[o]&&s[o].match(n))&&void 0!==t[3]&&(s[o]=t[0].substr(0,t[0].length-t[3].length));return s.join("\n")}(o)));return s=(new Date).getTime()-s,a},getErrors:function(){return e},getProcessingTime:function(){return s}}}(); \ No newline at end of file +// UPDATED AT: 2020年12月 5日 星期六 13时17分48秒 CST +const $=API("sub-store"),Base64=new Base64Code;function service(){console.log("\n┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅\n 𝑺𝒖𝒃-𝑺𝒕𝒐𝒓𝒆 © 𝑷𝒆𝒏𝒈-𝒀𝑴\n┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅\n");const e=express(),t="settings",s="subs",r="collections",n="builtin";function o(e){const t=Object.keys(e);let s="";for(let r of t)if(/USER-AGENT/i.test(r)){s=e[r];break}return-1!==s.indexOf("Quantumult%20X")?"QX":-1!==s.indexOf("Surge")?"Surge":-1!==s.indexOf("Decar")||-1!==s.indexOf("Loon")?"Loon":null}async function a(e,t=!0){const s=HTTP({headers:{"User-Agent":"Quantumult%20X"}}),r="#"+Base64.safeEncode(e),n=$.read(r),o=`#TIME-${Base64.safeEncode(e)}`,a=(new Date).getTime()-$.read(o)>864e5;if(t&&n&&!a)return $.log(`Use cached for url: ${e}`),n;let i="";try{i=(await s.get(e)).body}catch(e){throw new Error(e)}finally{$.write(i,r),$.write((new Date).getTime(),o)}if(0===i.replace(/\s/g,"").length)throw new Error("订阅内容为空!");return i}$.read(s)||$.write({},s),$.read(r)||$.write({},r),$.read(t)||$.write({},t),$.read("rules")||$.write({},"rules"),$.write({rules:getBuiltInRules()},n),e.get("/download/collection/:name",async function(e,t){const{name:n}=e.params,{cache:i}=e.query||"false",p=e.query.target||o(e.headers)||"JSON",c=$.read(r),l=$.read(s),u=c[n];if($.info(`正在下载组合订阅:${n}`),u){const e=u.subscriptions;let s=[];for(let t=0;te!==n);$.write(a,r),t.json({status:"success"})}),e.route("/api/subs").get(function(e,t){const r=$.read(s);t.json({status:"success",data:r})}).post(function(e,t){const r=e.body,n=$.read(s);$.info(`正在创建订阅: ${r.name}`),n[r.name]&&t.status(500).json({status:"failed",message:`订阅${r.name}已存在!`});/^[\w-_]*$/.test(r.name)?(n[r.name]=r,$.write(n,s),t.status(201).json({status:"success",data:r})):t.status(500).json({status:"failed",message:`订阅名称 ${r.name} 中含有非法字符!名称中只能包含英文字母、数字、下划线、横杠。`})}),e.get("/api/sub/statistics/:name"),e.route("/api/collection/:name").get(function(e,t){const{name:s}=e.params,n=$.read(r)[s];n?t.json({status:"success",data:n}):t.status(404).json({status:"failed",message:`未找到订阅集:${s}!`})}).patch(function(e,t){const{name:s}=e.params;let n=e.body;const o=$.read(r);if(o[s]){const e={...o[s],...n};$.info(`正在更新组合订阅:${s}...`),delete o[s],o[n.name||s]=e,$.write(o,r),t.json({status:"success",data:e})}else t.status(500).json({status:"failed",message:`订阅集${s}不存在,无法更新!`})}).delete(function(e,t){const{name:s}=e.params;$.info(`正在删除组合订阅:${s}`);let n=$.read(r);delete n[s],$.write(n,r),t.json({status:"success"})}),e.route("/api/collections").get(function(e,t){const s=$.read(r);t.json({status:"success",data:s})}).post(function(e,t){const s=e.body;$.info(`正在创建组合订阅:${s.name}`);const n=$.read(r);n[s.name]&&t.status(500).json({status:"failed",message:`订阅集${s.name}已存在!`});/^[\w-_]*$/.test(s.name)?(n[s.name]=s,$.write(n,r),t.status(201).json({status:"success",data:s})):t.status(500).json({status:"failed",message:`订阅集名称 ${s.name} 中含有非法字符!名称中只能包含英文字母、数字、下划线、横杠。`})}),e.get("/download/rule/:name",async function(e,t){const{name:s}=e.params,{builtin:r}=e.query,a=e.query.target||o(e.headers)||"Surge";let i;$.info(`正在下载${r?"内置":""}分流订阅:${s}...`),r&&(i=$.read(n).rules[s]);if(i){let e=[];for(let t=0;t{t.json($.read("#sub-store"))}).post((e,t)=>{const s=e.body;$.write(JSON.stringify(s),"#sub-store"),t.end()}),e.route("/api/settings").get(function(e,s){const r=$.read(t);s.json(r)}).patch(function(e,s){const r=e.body,n=$.read(t);$.write({...n,...r},t),s.json({status:"success"})}),e.get("/api/utils/IP_API/:server",async function(e,t){const s=decodeURIComponent(e.params.server),r=await $.http.get(`http://ip-api.com/json/${s}?lang=zh-CN`).then(e=>JSON.parse(e.body));t.json(r)}),e.post("/api/utils/refresh",async function(e,t){const{url:s}=e.body;$.info(`Refreshing cache for URL: ${s}`);try{const e=await a(s,!1);$.write(e,`#${Base64.safeEncode(s)}`),t.json({status:"success"})}catch(e){t.status(500).json({status:"failed",message:`无法刷新资源 ${s}: ${e}`})}}),e.get("/api/utils/env",function(e,t){const{isNode:s,isQX:r,isLoon:n,isSurge:o}=ENV();let a="Node";s&&(a="Node");r&&(a="QX");n&&(a="Loon");o&&(a="Surge");t.json({backend:a})}),e.get("/api/utils/backup",async function(e,s){const{action:r}=e.query,{gistToken:n}=$.read(t);if(n){const e=new Gist("Auto Generated Sub-Store Backup",n);try{let t;switch(r){case"upload":t=$.read("#sub-store"),$.info("上传备份中..."),await e.upload(t);break;case"download":$.info("还原备份中..."),t=await e.download(),$.write(t,"#sub-store")}s.json({status:"success"})}catch(e){const t=`${"upload"===r?"上传":"下载"}备份失败!${e}`;$.error(t),s.status(500).json({status:"failed",message:t})}}else s.status(500).json({status:"failed",message:"未找到Gist备份Token!"})}),e.get("/",async(e,t)=>{t.set("location","https://sub-store.vercel.app/").status(302).end()}),ENV().isQX&&e.options("/",async(e,t)=>{t.status(200).end()}),e.all("/",(e,t)=>{t.send("Hello from sub-store, made with ❤️ by Peng-YM")}),e.start()}service();var ProxyUtils=function(){const PROXY_PREPROCESSORS=function(){return[{name:"HTML",test:e=>/^/.test(e),parse:e=>""},function(){const e=["dm1lc3M","c3NyOi8v","dHJvamFu","c3M6Ly","c3NkOi8v"];return{name:"Base64 Pre-processor",test:function(t){return e.some(e=>-1!==t.indexOf(e))},parse:function(e){return e=Base64.safeDecode(e)}}}(),{name:"Clash Pre-processor",test:function(e){return/proxies/.test(e)},parse:function(e){return-1!==e.indexOf("{")&&(e=e.replace(/ - /g," - ").replace(/:(?!\s)/g,": ").replace(/\,\"/g,', "').replace(/: {/g,": {, ").replace(/, (\"?host|path|tls|mux|skip\"?)/g,", $1").replace(/{name: /g,'{name: "').replace(/, server:/g,'", server:').replace(/{|}/g,"").replace(/,/g,"\n ")),e=-1===(e=e.replace(/ -\n.*name/g," - name").replace(/\$|\`/g,"").split("proxy-providers:")[0].split("proxy-groups:")[0].replace(/\"([\w-]+)\"\s*:/g,"$1:")).indexOf("proxies:")?"proxies:\n"+e:"proxies:"+e.split("proxies:")[1],YAML.eval(e).proxies.map(e=>JSON.stringify(e)).join("\n")}},{name:"SSD Pre-processor",test:function(e){return 0===e.indexOf("ssd://")},parse:function(e){const t=[];let s=JSON.parse(Base64.safeDecode(e.split("ssd://")[1]));s.traffic_used,s.traffic_total,s.expiry,s.airport;let r=s.port,n=s.encryption,o=s.password,a=s.servers;for(let e=0;e{let[t,n]=e.split("=");if(t=t.trim(),n=n.trim(),-1!==r.indexOf(t)){s.type=t;const e=n.split(":");s.server=e[0],s.port=e[1]}else s[t.trim()]=n.trim()}),s}function t(){return{name:"Loon HTTP Parser",test:e=>/^.*=\s*http/i.test(e.split(",")[0])&&5===e.split(",").length&&-1===e.indexOf("username")&&-1===e.indexOf("password"),parse:e=>{const t=e.split("=")[1].split(","),s={name:e.split("=")[0].trim(),type:"http",server:t[1],port:t[2],tls:"443"===t[2]};return t[3]&&(s.username=t[3]),t[4]&&(s.password=t[4]),s.tls&&(s.sni=t["tls-name"]||s.server,s["skip-cert-verify"]=JSON.parse(t["skip-cert-verify"]||"false")),s}}}function s(e){const t={};t.name=e.split("=")[0].trim();const s=e.split(",");t.server=s[1].trim(),t.port=s[2].trim();for(let e=3;e/^ss:\/\//.test(e),parse:e=>{const t={};let s=e.split("ss://")[1];const r={name:decodeURIComponent(e.split("#")[1]),type:"ss",supported:t},n=(s=s.split("#")[0]).match(/@([^\/]*)(\/|$)/)[1],o=n.lastIndexOf(":");r.server=n.substring(0,o),r.port=n.substring(o+1);const a=Base64.safeDecode(s.split("@")[0]).split(":");if(r.cipher=a[0],r.password=a[1],-1!==s.indexOf("?plugin=")){const e=("plugin="+decodeURIComponent(s.split("?plugin=")[1].split("&")[0])).split(";"),n={};for(const t of e){const[e,s]=t.split("=");e&&(n[e]=s||!0)}switch(n.plugin){case"obfs-local":case"simple-obfs":r.plugin="obfs",r["plugin-opts"]={mode:n.obfs,host:n["obfs-host"]};break;case"v2ray-plugin":r.supported={...t,Loon:!1,Surge:!1},r.obfs="v2ray-plugin",r["plugin-opts"]={mode:"websocket",host:n["obfs-host"],path:n.path||"",tls:n.tls||!1};break;default:throw new Error(`Unsupported plugin option: ${n.plugin}`)}}return r}},function(){const e={Surge:!1};return{name:"URI SSR Parser",test:e=>/^ssr:\/\//.test(e),parse:t=>{let s=(t=Base64.safeDecode(t.split("ssr://")[1])).indexOf(":origin");-1===s&&(s=t.indexOf(":auth_"));const r=t.substring(0,s),n=r.substring(0,r.lastIndexOf(":")),o=r.substring(r.lastIndexOf(":")+1);let a=t.substring(s+1).split("/?")[0].split(":"),i={type:"ssr",server:n,port:o,protocol:a[0],cipher:a[1],obfs:a[2],password:Base64.safeDecode(a[3]),supported:e};const p={};if((t=t.split("/?")[1].split("&")).length>1)for(const e of t){const[t,s]=e.split("=");p[t]=s.trim()}return i={...i,name:p.remarks?Base64.safeDecode(p.remarks):i.server,"protocol-param":Base64.safeDecode(p.protoparam||"").replace(/\s/g,""),"obfs-param":Base64.safeDecode(p.obfsparam||"").replace(/\s/g,"")}}}}(),{name:"URI VMess Parser",test:e=>/^vmess:\/\//.test(e),parse:e=>{const t={};e=e.split("vmess://")[1];const s=Base64.safeDecode(e);if(/=\s*vmess/.test(s)){const e=s.split(",").map(e=>e.trim()),t={};for(const s of e)if(-1!==s.indexOf("=")){const[e,r]=s.split("=");t[e.trim()]=r.trim()}const r={name:e[0].split("=")[0].trim(),type:"vmess",server:e[1],port:e[2],cipher:e[3],uuid:e[4].match(/^"(.*)"$/)[1],tls:"over-tls"===t.obfs||"wss"===t.obfs};if(void 0!==t["udp-relay"]&&(r.udp=JSON.parse(t["udp-relay"])),void 0!==t["fast-open"]&&(r.udp=JSON.parse(t["fast-open"])),"ws"===t.obfs||"wss"===t.obfs){r.network="ws",r["ws-path"]=(t["obfs-path"]||'"/"').match(/^"(.*)"$/)[1];let e=t["obfs-header"];e&&-1!==e.indexOf("Host")&&(e=e.match(/Host:\s*([a-zA-Z0-9-.]*)/)[1]),r["ws-headers"]={Host:e||r.server}}return r.tls&&"false"===t['"tls-verification"']&&(r["skip-cert-verify"]=!0),r.tls&&t["obfs-host"]&&(r.sni=t["obfs-host"]),r}{const e=JSON.parse(s),r={name:e.ps,type:"vmess",server:e.add,port:e.port,cipher:"auto",uuid:e.id,alterId:e.aid||0,tls:"tls"===e.tls||!0===e.tls,supported:t};return"ws"===e.net&&(r.network="ws",r["ws-path"]=e.path,r["ws-headers"]={Host:e.host||e.add},r.tls&&e.host&&(r.sni=e.host)),!1===e.verify_cert&&(r["skip-cert-verify"]=!0),r}}},{name:"URI Trojan Parser",test:e=>/^trojan:\/\//.test(e),parse:e=>{if(-1===e.indexOf(":443"))throw new Error("Trojan port should always be 443!");const t=(e=e.split("trojan://")[1]).split("@")[1].split(":443")[0];return{name:decodeURIComponent(e.split("#")[1].trim())||`[Trojan] ${t}`,type:"trojan",server:t,port:443,password:e.split("@")[0],supported:{}}}},{name:"Clash Parser",test:e=>{try{JSON.parse(e)}catch(e){return!1}return!0},parse:e=>JSON.parse(e)},{name:"Surge SS Parser",test:e=>/^.*=\s*ss/.test(e.split(",")[0]),parse:e=>{const t=s(e),r={name:t.name,type:"ss",server:t.server,port:t.port,cipher:t["encrypt-method"],password:t.password,tfo:JSON.parse(t.tfo||"false"),udp:JSON.parse(t["udp-relay"]||"false")};return t.obfs&&(r.plugin="obfs",r["plugin-opts"]={mode:t.obfs,host:t["obfs-host"]}),r}},{name:"Surge VMess Parser",test:e=>/^.*=\s*vmess/.test(e.split(",")[0])&&-1!==e.indexOf("username"),parse:e=>{const t=s(e),r={name:t.name,type:"vmess",server:t.server,port:t.port,uuid:t.username,alterId:0,cipher:"none",tls:JSON.parse(t.tls||"false"),tfo:JSON.parse(t.tfo||"false")};return r.tls&&(void 0!==t["skip-cert-verify"]&&(r["skip-cert-verify"]=!0===t["skip-cert-verify"]||"1"===t["skip-cert-verify"]),r.sni=t.sni||t.server),JSON.parse(t.ws||"false")&&(r.network="ws",r["ws-path"]=t["ws-path"],r["ws-headers"]={Host:t.sni}),r}},{name:"Surge Trojan Parser",test:e=>/^.*=\s*trojan/.test(e.split(",")[0])&&-1!==e.indexOf("sni"),parse:e=>{const t=s(e),r={name:t.name,type:"trojan",server:t.server,port:t.port,password:t.password,sni:t.sni||t.server,tfo:JSON.parse(t.tfo||"false")};return void 0!==t["skip-cert-verify"]&&(r["skip-cert-verify"]=!0===t["skip-cert-verify"]||"1"===t["skip-cert-verify"]),r}},{name:"Surge HTTP Parser",test:e=>/^.*=\s*http/.test(e.split(",")[0])&&!t().test(e),parse:e=>{const t=s(e),r={name:t.name,type:"http",server:t.server,port:t.port,tls:JSON.parse(t.tls||"false"),tfo:JSON.parse(t.tfo||"false")};return r.tls&&(void 0!==t["skip-cert-verify"]&&(r["skip-cert-verify"]=!0===t["skip-cert-verify"]||"1"===t["skip-cert-verify"]),r.sni=t.sni||t.server),t.username&&"none"!==t.username&&(r.username=t.username),t.password&&"none"!==t.password&&(r.password=t.password),r}},{name:"Loon SS Parser",test:e=>"shadowsocks"===e.split(",")[0].split("=")[1].trim().toLowerCase(),parse:e=>{const t=e.split("=")[1].split(","),s={name:e.split("=")[0].trim(),type:"ss",server:t[1],port:t[2],cipher:t[3],password:t[4].replace(/"/g,"")};return t.length>5&&(s.plugin="obfs",s["plugin-opts"]={mode:t[5],host:t[6]}),s}},{name:"Loon SSR Parser",test:e=>"shadowsocksr"===e.split(",")[0].split("=")[1].trim().toLowerCase(),parse:e=>{const t=e.split("=")[1].split(",");return{name:e.split("=")[0].trim(),type:"ssr",server:t[1],port:t[2],cipher:t[3],password:t[4].replace(/"/g,""),protocol:t[5],"protocol-param":t[6].match(/{(.*)}/)[1],supported:{Surge:!1},obfs:t[7],"obfs-param":t[8].match(/{(.*)}/)[1]}}},{name:"Loon VMess Parser",test:e=>/^.*=\s*vmess/i.test(e.split(",")[0])&&-1===e.indexOf("username"),parse:e=>{let t=e.split("=")[1].split(",");const s={name:e.split("=")[0].trim(),type:"vmess",server:t[1],port:t[2],cipher:t[3]||"none",uuid:t[4].replace(/"/g,""),alterId:0};t=t.splice(5);for(const e of t){const[s,r]=e.split(":");t[s]=r}switch(s.tls=JSON.parse(t["over-tls"]||"false"),s.tls&&(s.sni=t["tls-name"]||s.server,s["skip-cert-verify"]=JSON.parse(t["skip-cert-verify"]||"false")),t.transport){case"tcp":break;case"ws":s.network=t.transport,s["ws-path"]=t.path,s["ws-headers"]={Host:t.host}}return s.tls&&(s["skip-cert-verify"]=JSON.parse(t["skip-cert-verify"]||"false")),s}},{name:"Loon Trojan Parser",test:e=>/^.*=\s*trojan/i.test(e.split(",")[0])&&-1===e.indexOf("password"),parse:e=>{const t=e.split("=")[1].split(","),s={name:e.split("=")[0].trim(),type:"trojan",server:t[1],port:t[2],password:t[3].replace(/"/g,""),sni:t[1],"skip-cert-verify":JSON.parse(t["skip-cert-verify"]||"false")};if(t.length>4){const[r,n]=t[4].split(":");if("tls-name"!==r)throw new Error(`Unknown option ${r} for line: \n${e}`);s.sni=n}return s}},t(),{name:"QX SS Parser",test:e=>/^shadowsocks\s*=/.test(e.split(",")[0].trim())&&-1===e.indexOf("ssr-protocol"),parse:t=>{const s=e(t),r={name:s.tag,type:"ss",server:s.server,port:s.port,cipher:s.method,password:s.password,udp:JSON.parse(s["udp-relay"]||"false"),tfo:JSON.parse(s["fast-open"]||"false"),supported:{}};if(s.obfs)switch(r["plugin-opts"]={host:s["obfs-host"]||r.server},s.obfs){case"http":case"tls":r.plugin="obfs",r["plugin-opts"].mode=s.obfs;break;case"ws":case"wss":r["plugin-opts"]={...r["plugin-opts"],mode:"websocket",path:s["obfs-uri"]||"/",tls:"wss"===s.obfs},r["plugin-opts"].tls&&void 0!==s["tls-verification"]&&(r["plugin-opts"]["skip-cert-verify"]=s["tls-verification"]),r.plugin="v2ray-plugin",r.supported.Surge=!1,r.supported.Loon=!1}return r}},{name:"QX SSR Parser",test:e=>/^shadowsocks\s*=/.test(e.split(",")[0].trim())&&-1!==e.indexOf("ssr-protocol"),parse:t=>{const s=e(t),r={name:s.tag,type:"ssr",server:s.server,port:s.port,cipher:s.method,password:s.password,protocol:s["ssr-protocol"],obfs:"plain","protocol-param":s["ssr-protocol-param"],udp:JSON.parse(s["udp-relay"]||"false"),tfo:JSON.parse(s["fast-open"]||"false"),supported:{Surge:!1}};return s.obfs&&(r.obfs=s.obfs,r["obfs-param"]=s["obfs-host"]),r}},{name:"QX VMess Parser",test:e=>/^vmess\s*=/.test(e.split(",")[0].trim()),parse:t=>{const s=e(t),r={type:"vmess",name:s.tag,server:s.server,port:s.port,cipher:s.method||"none",uuid:s.password,alterId:0,tls:"over-tls"===s.obfs||"wss"===s.obfs,udp:JSON.parse(s["udp-relay"]||"false"),tfo:JSON.parse(s["fast-open"]||"false")};return r.tls&&(r.sni=s["obfs-host"]||s.server,r["skip-cert-verify"]=!JSON.parse(s["tls-verification"]||"true")),"ws"!==s.obfs&&"wss"!==s.obfs||(r.network="ws",r["ws-path"]=s["obfs-uri"],r["ws-headers"]={Host:s["obfs-host"]||s.server}),r}},{name:"QX Trojan Parser",test:e=>/^trojan\s*=/.test(e.split(",")[0].trim()),parse:t=>{const s=e(t),r={type:"trojan",name:s.tag,server:s.server,port:s.port,password:s.password,sni:s["tls-host"]||s.server,udp:JSON.parse(s["udp-relay"]||"false"),tfo:JSON.parse(s["fast-open"]||"false")};return r["skip-cert-verify"]=!JSON.parse(s["tls-verification"]||"true"),r}},{name:"QX HTTP Parser",test:e=>/^http\s*=/.test(e.split(",")[0].trim()),parse:t=>{const s=e(t),r={type:"http",name:s.tag,server:s.server,port:s.port,tls:JSON.parse(s["over-tls"]||"false"),udp:JSON.parse(s["udp-relay"]||"false"),tfo:JSON.parse(s["fast-open"]||"false")};return s.username&&"none"!==s.username&&(r.username=s.username),s.password&&"none"!==s.password&&(r.password=s.password),r.tls&&(r.sni=s["tls-host"]||r.server,r["skip-cert-verify"]=!JSON.parse(s["tls-verification"]||"true")),r}}]}(),PROXY_PROCESSORS=function(){function SetPropertyOperator({key:e,value:t}){return{name:"Set Property Operator",func:s=>s.map(s=>(s[e]=t,s))}}function FlagOperator(e=!0){return{name:"Flag Operator",func:t=>t.map(t=>{if(e){const e=getFlag(t.name);t.name=removeFlag(t.name),t.name=e+" "+t.name,t.name=t.name.replace(/🇹🇼/g,"🇨🇳")}else t.name=removeFlag(t.name);return t})}}function SortOperator(e="asc"){return{name:"Sort Operator",func:t=>{switch(e){case"asc":case"desc":return t.sort((t,s)=>{let r=t.name>s.name?1:-1;return r*="desc"===e?-1:1});case"random":return shuffle(t);default:throw new Error("Unknown sort option: "+e)}}}}function RegexSortOperator(e){return{name:"Regex Sort Operator",func:t=>t.sort((t,s)=>{const r=getRegexOrder(e,t.name),n=getRegexOrder(e,s.name);return r&&!n?-1:n&&!r?1:r&&n?rt.map(t=>{for(const{expr:s,now:r}of e)t.name=t.name.replace(new RegExp(s,"g"),r).trim();return t})}}function RegexDeleteOperator(e){return{name:"Regex Delete Operator",func:RegexRenameOperator(e.map(e=>({expr:e,now:""}))).func}}function ScriptOperator(script){return{name:"Script Operator",func:proxies=>{let output=proxies;return function(){const $get=(e,t)=>{return(0,PROXY_PROCESSORS[e])(t)},$process=ApplyProcessor;eval(script),output=operator(proxies)}(),output}}}function UselessFilter(){return{name:"Useless Filter",func:RegexFilter({regex:["网址","流量","时间","应急","过期","Bandwidth","expire"],keep:!1}).func}}function RegionFilter(e){const t={HK:"🇭🇰",TW:"🇹🇼",US:"🇺🇸",SG:"🇸🇬",JP:"🇯🇵",UK:"🇬🇧"};return{name:"Region Filter",func:s=>s.map(s=>{const r=getFlag(s.name);return e.some(e=>t[e]===r)})}}function RegexFilter({regex:e=[],keep:t=!0}){return{name:"Regex Filter",func:s=>s.map(s=>{const r=e.some(e=>(e=new RegExp(e)).test(s.name));return t?r:!r})}}function TypeFilter(e){return{name:"Type Filter",func:t=>t.map(t=>e.some(e=>t.type===e))}}function ScriptFilter(script){return{name:"Script Filter",func:proxies=>{let output=FULL(proxies.length,!0);return function(){eval(script),output=filter(proxies)}(),output}}}function getFlag(e){const t={"🇦🇨":["AC"],"🇦🇹":["奥地利","维也纳"],"🇦🇺":["AU","Australia","Sydney","澳大利亚","澳洲","墨尔本","悉尼"],"🇧🇪":["BE","比利时"],"🇧🇬":["保加利亚","Bulgaria"],"🇧🇷":["BR","Brazil","巴西","圣保罗"],"🇨🇦":["CA","Canada","Waterloo","加拿大","蒙特利尔","温哥华","楓葉","枫叶","滑铁卢","多伦多"],"🇨🇭":["瑞士","苏黎世","Switzerland"],"🇩🇪":["DE","German","GERMAN","德国","德國","法兰克福"],"🇩🇰":["丹麦"],"🇪🇸":["ES","西班牙","Spain"],"🇪🇺":["EU","欧盟","欧罗巴"],"🇫🇮":["Finland","芬兰","赫尔辛基"],"🇫🇷":["FR","France","法国","法國","巴黎"],"🇬🇧":["UK","GB","England","United Kingdom","英国","伦敦","英"],"🇲🇴":["MO","Macao","澳门","CTM"],"🇭🇺":["匈牙利","Hungary"],"🇭🇰":["HK","Hongkong","Hong Kong","香港","深港","沪港","呼港","HKT","HKBN","HGC","WTT","CMI","穗港","京港","港"],"🇮🇩":["Indonesia","印尼","印度尼西亚","雅加达"],"🇮🇪":["Ireland","爱尔兰","都柏林"],"🇮🇳":["India","印度","孟买","Mumbai"],"🇰🇵":["KP","朝鲜"],"🇰🇷":["KR","Korea","KOR","韩国","首尔","韩","韓"],"🇱🇻":["Latvia","Latvija","拉脱维亚"],"🇲🇽️":["MEX","MX","墨西哥"],"🇲🇾":["MY","Malaysia","马来西亚","吉隆坡"],"🇳🇱":["NL","Netherlands","荷兰","荷蘭","尼德蘭","阿姆斯特丹"],"🇵🇭":["PH","Philippines","菲律宾"],"🇷🇴":["RO","罗马尼亚"],"🇷🇺":["RU","Russia","俄罗斯","俄羅斯","伯力","莫斯科","圣彼得堡","西伯利亚","新西伯利亚","京俄","杭俄"],"🇸🇦":["沙特","迪拜"],"🇸🇪":["SE","Sweden"],"🇸🇬":["SG","Singapore","新加坡","狮城","沪新","京新","泉新","穗新","深新","杭新","广新"],"🇹🇭":["TH","Thailand","泰国","泰國","曼谷"],"🇹🇷":["TR","Turkey","土耳其","伊斯坦布尔"],"🇹🇼":["TW","Taiwan","台湾","台北","台中","新北","彰化","CHT","台","HINET"],"🇺🇸":["US","USA","America","United States","美国","美","京美","波特兰","达拉斯","俄勒冈","凤凰城","费利蒙","硅谷","矽谷","拉斯维加斯","洛杉矶","圣何塞","圣克拉拉","西雅图","芝加哥","沪美","哥伦布","纽约"],"🇻🇳":["VN","越南","胡志明市"],"🇮🇹":["Italy","IT","Nachash","意大利","米兰","義大利"],"🇿🇦":["South Africa","南非"],"🇦🇪":["United Arab Emirates","阿联酋"],"🇯🇵":["JP","Japan","日","日本","东京","大阪","埼玉","沪日","穗日","川日","中日","泉日","杭日","深日","辽日","广日"],"🇦🇷":["AR","阿根廷"],"🇳🇴":["Norway","挪威","NO"],"🇨🇳":["CN","China","回国","中国","江苏","北京","上海","广州","深圳","杭州","徐州","青岛","宁波","镇江","back"],"🏳️‍🌈":["流量","时间","应急","过期","Bandwidth","expire"]};for(let s of Object.keys(t))if(t[s].some(t=>-1!==e.indexOf(t)))return s;return(e.match(/[\uD83C][\uDDE6-\uDDFF][\uD83C][\uDDE6-\uDDFF]/)||[])[0]||"🏴‍☠️"}function removeFlag(e){return e.replace(/[\uD83C][\uDDE6-\uDDFF][\uD83C][\uDDE6-\uDDFF]/g,"").trim()}function shuffle(e){let t,s,r=e.length;for(;0!==r;)s=Math.floor(Math.random()*r),t=e[r-=1],e[r]=e[s],e[s]=t;return e}return{"Useless Filter":UselessFilter,"Region Filter":RegionFilter,"Regex Filter":RegexFilter,"Type Filter":TypeFilter,"Script Filter":ScriptFilter,"Set Property Operator":SetPropertyOperator,"Flag Operator":FlagOperator,"Sort Operator":SortOperator,"Regex Sort Operator":RegexSortOperator,"Regex Rename Operator":RegexRenameOperator,"Regex Delete Operator":RegexDeleteOperator,"Script Operator":ScriptOperator}}(),PROXY_PRODUCERS=function(){return{QX:{produce:e=>{let t,s;switch(e.type){case"ss":if(t="","obfs"===e.plugin){const{host:s,mode:r}=e["plugin-opts"];t=`,obfs=${r}${s?",obfs-host="+s:""}`}if("v2ray-plugin"===e.plugin){const{tls:s,host:r,path:n}=e["plugin-opts"];t=`,obfs=${s?"wss":"ws"}${r?",obfs-host="+r:""}${n?",obfs-uri="+n:""}`}return`shadowsocks=${e.server}:${e.port},method=${e.cipher},password=${e.password}${t}${e.tfo?",fast-open=true":",fast-open=false"}${e.udp?",udp-relay=true":",udp-relay=false"},tag=${e.name}`;case"ssr":return`shadowsocks=${e.server}:${e.port},method=${e.cipher},password=${e.password},ssr-protocol=${e.protocol}${e["protocol-param"]?",ssr-protocol-param="+e["protocol-param"]:""}${e.obfs?",obfs="+e.obfs:""}${e["obfs-param"]?",obfs-host="+e["obfs-param"]:""}${e.tfo?",fast-open=true":",fast-open=false"}${e.udp?",udp-relay=true":",udp-relay=false"},tag=${e.name}`;case"vmess":return t="","ws"===e.network?t=e.tls?`,obfs=wss${e.sni?",obfs-host="+e.sni:""}${e["ws-path"]?",obfs-uri="+e["ws-path"]:""},tls-verification=${e["skip-cert-verify"]?"false":"true"}`:`,obfs=ws${e["ws-headers"].Host?",obfs-host="+e["ws-headers"].Host:""}${e["ws-path"]?",obfs-uri="+e["ws-path"]:""}`:e.tls&&(t=`,obfs=over-tls${e.sni?",obfs-host="+e.sni:""},tls-verification=${e["skip-cert-verify"]?"false":"true"}`),`vmess=${e.server}:${e.port},method=${"auto"===e.cipher?"none":e.cipher},password=${e.uuid}${t}${e.tfo?",fast-open=true":",fast-open=false"}${e.udp?",udp-relay=true":",udp-relay=false"},tag=${e.name}`;case"trojan":return`trojan=${e.server}:${e.port},password=${e.password}${e.sni?",tls-host="+e.sni:""},over-tls=true,tls-verification=${e["skip-cert-verify"]?"false":"true"}${e.tfo?",fast-open=true":",fast-open=false"}${e.udp?",udp-relay=true":",udp-relay=false"},tag=${e.name}`;case"http":return s="",e.tls&&(s=`,over-tls=true,tls-verification=${e["skip-cert-verify"]?"false":"true"}${e.sni?",tls-host="+e.sni:""}`),`http=${e.server}:${e.port},username=${e.username},password=${e.password}${s}${e.tfo?",fast-open=true":",fast-open=false"},tag=${e.name}`}throw new Error(`Platform QX does not support proxy type: ${e.type}`)}},Surge:{produce:e=>{let t,s;switch(e.type){case"ss":if(t="",e.plugin){const{host:s,mode:r}=e["plugin-opts"];if("obfs"!==e.plugin)throw new Error(`Platform Surge does not support obfs option: ${e.obfs}`);t=`,obfs=${r}${s?",obfs-host="+s:""}`}return`${e.name}=ss,${e.server}, ${e.port},encrypt-method=${e.cipher},password=${e.password}${t},tfo=${e.tfo||"false"},udp-relay=${e.udp||"false"}`;case"vmess":s="";let r=`${e.name}=vmess,${e.server},${e.port},username=${e.uuid},tls=${e.tls||"false"},tfo=${e.tfo||"false"}`;if("ws"===e.network){const t=e["ws-path"]||"/",s=e["ws-headers"].Host;r+=`,ws=true${t?",ws-path="+t:""}${s?",ws-headers=HOST:"+s:""}`}return e.tls&&(r+=`${void 0!==e["skip-cert-verify"]?",skip-cert-verify="+e["skip-cert-verify"]:""}`,r+=e.sni?`,sni=${e.sni}`:""),r;case"trojan":return`${e.name}=trojan,${e.server},${e.port},password=${e.password}${void 0!==e["skip-cert-verify"]?",skip-cert-verify="+e["skip-cert-verify"]:""}${e.sni?",sni="+e.sni:""},tfo=${e.tfo||"false"}`;case"http":return s=", tls=false",e.tls&&(s=`,tls=true,skip-cert-verify=${e["skip-cert-verify"]},sni=${e.sni}`),`${e.name}=http, ${e.server}, ${e.port}${e.username?",username="+e.username:""}${e.password?",password="+e.password:""}${s},tfo=${e.tfo||"false"}`}throw new Error(`Platform Surge does not support proxy type: ${e.type}`)}},Loon:{produce:e=>{let t,s;switch(e.type){case"ss":if(t=",,",e.plugin){if("obfs"!==e.plugin)throw new Error(`Platform Loon does not support obfs option: ${e.obfs}`);{const{mode:s,host:r}=e["plugin-opts"];t=`,${s},${r||""}`}}return`${e.name}=shadowsocks,${e.server},${e.port},${e.cipher},"${e.password}"${t}`;case"ssr":return`${e.name}=shadowsocksr,${e.server},${e.port},${e.cipher},"${e.password}",${e.protocol},{${e["protocol-param"]||""}},${e.obfs},{${e["obfs-param"]||""}}`;case"vmess":return t="",t="ws"===e.network?`,transport:ws,host:${e["ws-headers"].Host||e.server},path:${e["ws-path"]||"/"}`:",transport:tcp",e.tls&&(t+=`${e.sni?",tls-name:"+e.sni:""},skip-cert-verify:${e["skip-cert-verify"]||"false"}`),`${e.name}=vmess,${e.server},${e.port},${"auto"===e.cipher?"none":e.cipher},"${e.uuid}",over-tls:${e.tls||"false"}${t}`;case"trojan":return`${e.name}=trojan,${e.server},${e.port},"${e.password}"${e.sni?",tls-name:"+e.sni:""},skip-cert-verify:${e["skip-cert-verify"]||"false"}`;case"http":s="";const r=`${e.name}=${e.tls?"http":"https"},${e.server},${e.port},${e.username||""},${e.password||""}`;return e.tls?r+(s=`${e.sni?",tls-name:"+e.sni:""},skip-cert-verify:${e["skip-cert-verify"]}`):r}throw new Error(`Platform Loon does not support proxy type: ${e.type}`)}},Clash:{type:"ALL",produce:e=>"proxies:\n"+e.map(e=>(delete e.supported," - "+JSON.stringify(e)+"\n")).join("")},URI:{type:"SINGLE",produce:e=>{let t="";switch(e.type){case"ss":const s=`${e.cipher}:${e.password}`;if(t=`ss://${Base64.safeEncode(s)}@${e.server}:${e.port}/`,e.plugin){t+="?plugin=";const s=e["plugin-opts"];switch(e.plugin){case"obfs":t+=encodeURIComponent(`simple-obfs;obfs=${s.mode}${s.host?";obfs-host="+s.host:""}`);break;case"v2ray-plugin":t+=encodeURIComponent(`v2ray-plugin;obfs=${s.mode}${s.host?";obfs-host"+s.host:""}${s.tls?";tls":""}`);break;default:throw new Error(`Unsupported plugin option: ${e.plugin}`)}}t+=`#${encodeURIComponent(e.name)}`;break;case"ssr":t=`${e.server}:${e.port}:${e.protocol}:${e.cipher}:${e.obfs}:${Base64.safeEncode(e.password)}/`,t+=`?remarks=${Base64.safeEncode(e.name)}${e["obfs-param"]?"&obfsparam="+Base64.safeEncode(e["obfs-param"]):""}${e["protocol-param"]?"&protocolparam="+Base64.safeEncode(e["protocol-param"]):""}`,t="ssr://"+Base64.safeEncode(t);break;case"vmess":t={ps:e.name,add:e.server,port:e.port,id:e.uuid,type:"",aid:0,net:e.network||"tcp",tls:e.tls?"tls":""},"ws"===e.network&&(t.path=e["ws-path"]||"/",t.host=e["ws-headers"].Host||e.server),t="vmess://"+Base64.safeEncode(JSON.stringify(t));break;case"trojan":t=`trojan://${e.password}@${e.server}:${e.port}#${encodeURIComponent(e.name)}`;break;default:throw new Error(`Cannot handle proxy type: ${e.type}`)}return t}},JSON:{type:"ALL",produce:e=>JSON.stringify(e,null,2)}}}();function preprocess(e){for(const t of PROXY_PREPROCESSORS)try{if(t.test(e))return $.info(`Pre-processor [${t.name}] activated`),t.parse(e)}catch(e){$.error(`Parser [${t.name}] failed\n Reason: ${e}`)}return e}function safeMatch(e,t){let s;try{s=e.test(t)}catch(e){s=!1}return s}function parse(e){const t=(e=preprocess(e)).split("\n"),s=[];let r;for(let e of t){if(0===(e=e.trim()).length)continue;let t=r&&safeMatch(r,e);if(!t)for(const s of PROXY_PARSERS)if(safeMatch(s,e)){r=s,t=!0,$.info(`Proxy parser: ${s.name} is activated`);break}if(t)try{const t=r.parse(e);t||$.error(`Parser ${r.name} return nothing for \n${e}\n`),s.push(t)}catch(t){$.error(`Failed to parse line: \n ${e}\n Reason: ${t.stack}`)}else $.error(`Failed to find a rule to parse line: \n${e}\n`)}return s}async function process(e,t=[]){for(const s of t){let t,r;if(-1!==s.type.indexOf("Script")){const{mode:e,content:r}=s.args;t="link"===e?await $.http.get(r).then(e=>e.body).catch(e=>{throw new Error(`Error when downloading remote script: ${s.args.content}.\n Reason: ${e}`)}):r}PROXY_PROCESSORS[s.type]?($.info(`Applying "${s.type}" with arguments:\n >>> ${JSON.stringify(s.args,null,2)||"None"}`),e=ApplyProcessor(r=-1!==s.type.indexOf("Script")?PROXY_PROCESSORS[s.type](t):PROXY_PROCESSORS[s.type](s.args),e)):$.error(`Unknown operator: "${s.type}"`)}return e}function produce(e,t){const s=PROXY_PRODUCERS[t];if(!s)throw new Error(`Target platform: ${t} is not supported!`);return e=e.filter(e=>!(e.supported&&!1===e.supported[t])),$.info(`Producing proxies for target: ${t}`),void 0===s.type||"SINGLE"===s.type?e.map(e=>{try{return s.produce(e)}catch(t){return $.error(`Cannot produce proxy: ${JSON.stringify(e,null,2)}\nReason: ${t}`),""}}).filter(e=>e.length>0).join("\n"):"ALL"===s.type?s.produce(e):void 0}return{parse:parse,process:process,produce:produce}}(),RuleUtils=function(){const e=[[/^(DOMAIN|host|HOST)$/,"DOMAIN"],[/^(DOMAIN-KEYWORD|host-keyword|HOST-KEYWORD)$/,"DOMAIN-KEYWORD"],[/^(DOMAIN-SUFFIX|host-suffix|HOST-SUFFIX)$/,"DOMAIN-SUFFIX"],[/^USER-AGENT$/i,"USER-AGENT"],[/^PROCESS-NAME$/,"PROCESS-NAME"],[/^(DEST-PORT|DST-PORT)$/,"DST-PORT"],[/^SRC-IP(-CIDR)?$/,"SRC-IP"],[/^(IN|SRC)-PORT$/,"IN-PORT"],[/^PROTOCOL$/,"PROTOCOL"],[/^IP-CIDR$/i,"IP-CIDR"],[/^(IP-CIDR6|ip6-cidr|IP6-CIDR)$/]],t=function(){return[{name:"HTML",test:e=>/^/.test(e),parse:e=>""},{name:"Clash Provider",test:e=>0===e.indexOf("payload:"),parse:e=>e.replace("payload:","").replace(/^\s*-\s*/gm,"")}]}(),s=function(){return[{name:"Universal Rule Parser",test:()=>!0,parse:t=>{const s=t.split("\n"),r=[];for(let t of s)if(0!==(t=t.trim()).length&&!/\s*#/.test(t))try{const s=t.split(",").map(e=>e.trim());let n=s[0],o=!1;for(const t of e)if(t[0].test(n)){o=!0;const e={type:t[1],content:s[1]};"IP-CIDR"!==e.type&&"IP-CIDR6"!==e.type||(e.options=s.slice(2)),r.push(e)}if(!o)throw new Error("Invalid rule type: "+n)}catch(e){console.error(`Failed to parse line: ${t}\n Reason: ${e}`)}return r}}]}(),r=function(){return{"Regex Filter":function({regex:e=[],keep:t=!0}){return{name:"Regex Filter",func:s=>s.map(s=>{const r=e.some(e=>(e=new RegExp(e)).test(s));return t?r:!r})}},"Remove Duplicate Filter":function(){return{name:"Remove Duplicate Filter",func:e=>{const t=new Set,s=[];return e.forEach(e=>{const r=e.options||[];r.sort();const n=`${e.type},${e.content},${JSON.stringify(r)}`;t.has(n)||(s.push(e),t.add(n))}),s}}},"Type Filter":function(e){return{name:"Type Filter",func:t=>t.map(t=>e.some(e=>t.type===e))}},"Regex Replace Operator":function(e){return{name:"Regex Rename Operator",func:t=>t.map(t=>{for(const{expr:s,now:r}of e)t.content=t.content.replace(new RegExp(s,"g"),r).trim();return t})}}}}(),n=function(){return{QX:{type:"SINGLE",func:e=>-1!==["URL-REGEX","DEST-PORT","SRC-IP","IN-PORT","PROTOCOL"].indexOf(e.type)?null:`${{"DOMAIN-KEYWORD":"HOST-KEYWORD","DOMAIN-SUFFIX":"HOST-SUFFIX",DOMAIN:"HOST","IP-CIDR6":"IP6-CIDR"}[e.type]||e.type},${e.content},SUB-STORE`},Surge:{type:"SINGLE",func:e=>{let t=`${e.type},${e.content}`;return"IP-CIDR"!==e.type&&"IP-CIDR6"!==e.type||(t+=e.options?`,${e.options[0]}`:""),t}},Loon:{type:"SINGLE",func:e=>-1!==["DEST-PORT","SRC-IP","IN-PORT","PROTOCOL"].indexOf(e.type)?null:(e=>{let t=`${e.type},${e.content}`;return"IP-CIDR"!==e.type&&"IP-CIDR6"!==e.type||(t+=e.options?`,${e.options[0]}`:""),t})(e)},Clash:{type:"ALL",func:e=>{const t={"DEST-PORT":"DST-PORT","SRC-IP":"SRC-IP-CIDR","IN-PORT":"SRC-PORT"},s={payload:e.map(e=>{let s=`${t[e.type]||e.type},${e.content}`;return"IP-CIDR"!==e.type&&"IP-CIDR6"!==e.type||(s+=e.options?`,${e.options[0]}`:""),s})};return YAML.stringify(s)}}}}();return{parse:function(e){e=function(e){for(const s of t)try{if(s.test(e))return $.info(`Pre-processor [${s.name}] activated`),s.parse(e)}catch(e){$.error(`Parser [${s.name}] failed\n Reason: ${e}`)}return e}(e);for(const t of s){let s;try{s=t.test(e)}catch{s=!1}if(s)return $.info(`Rule parser [${t.name}] is activated!`),t.parse(e)}},process:async function(e,t){for(const s of t){if(!r[s.type]){console.error(`Unknown operator: ${s.type}!`);continue}const t=r[s.type](s.args);$.info(`Applying "${s.type}" with arguments: \n >>> ${JSON.stringify(s.args)||"None"}`),e=ApplyProcessor(t,e)}return e},produce:function(e,t){const s=n[t];if(!s)throw new Error(`Target platform: ${t} is not supported!`);return void 0===s.type||"SINGLE"===s.type?e.map(e=>{try{return s.func(e)}catch(t){return console.log(`ERROR: cannot produce rule: ${JSON.stringify(e)}\nReason: ${t}`),""}}).filter(e=>e.length>0).join("\n"):"ALL"===s.type?s.func(e):void 0}}}();function getBuiltInRules(){return{AD:{name:"AD",description:"",urls:["https://raw.githubusercontent.com/privacy-protection-tools/anti-AD/master/anti-ad-surge.txt","https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/Providers/BanAD.yaml"]},Global:{name:"Global",description:"",urls:["https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/Providers/ProxyGFWlist.yaml","https://raw.githubusercontent.com/DivineEngine/Profiles/master/Quantumult/Filter/Global.list"]},CN:{name:"CN",description:"",urls:["https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/Providers/ChinaDomain.yaml","https://raw.githubusercontent.com/DivineEngine/Profiles/master/Quantumult/Filter/China.list"]}}}function ApplyProcessor(e,t){return-1!==e.name.indexOf("Filter")?function(e,t){let s=FULL(t.length,!0);try{s=AND(s,e.func(t))}catch(t){console.log(`Cannot apply filter ${e.name}\n Reason: ${t}`)}return t.filter((e,t)=>s[t])}(e,t):-1!==e.name.indexOf("Operator")?function(e,t){let s=clone(t);try{const t=e.func(s);t&&(s=t)}catch(t){console.log(`Cannot apply operator ${e.name}! Reason: ${t}`)}return s}(e,t):void 0}function AND(...e){return e.reduce((e,t)=>e.map((e,s)=>t[s]&&e))}function OR(...e){return e.reduce((e,t)=>e.map((e,s)=>t[s]||e))}function NOT(e){return e.map(e=>!e)}function FULL(e,t){return[...Array(e).keys()].map(()=>t)}function clone(e){return JSON.parse(JSON.stringify(e))}function ENV(){const e="undefined"!=typeof $task,t="undefined"!=typeof $loon,s="undefined"!=typeof $httpClient&&!t,r="function"==typeof require&&"undefined"!=typeof $jsbox;return{isQX:e,isLoon:t,isSurge:s,isNode:"function"==typeof require&&!r,isJSBox:r,isRequest:"undefined"!=typeof $request,isScriptable:"undefined"!=typeof importModule}}function HTTP(e={baseURL:""}){const{isQX:t,isLoon:s,isSurge:r,isScriptable:n,isNode:o}=ENV(),a=/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&\/\/=]*)/;const i={};return["GET","POST","PUT","DELETE","HEAD","OPTIONS","PATCH"].forEach(p=>i[p.toLowerCase()]=(i=>(function(i,p){p="string"==typeof p?{url:p}:p;const c=e.baseURL;c&&!a.test(p.url||"")&&(p.url=c?c+p.url:p.url);const l=(p={...e,...p}).timeout,u={onRequest:()=>{},onResponse:e=>e,onTimeout:()=>{},...p.events};let f,d;if(u.onRequest(i,p),t)f=$task.fetch({method:i,...p});else if(s||r||o)f=new Promise((e,t)=>{(o?require("request"):$httpClient)[i.toLowerCase()](p,(s,r,n)=>{s?t(s):e({statusCode:r.status||r.statusCode,headers:r.headers,body:n})})});else if(n){const e=new Request(p.url);e.method=i,e.headers=p.headers,e.body=p.body,f=new Promise((t,s)=>{e.loadString().then(s=>{t({statusCode:e.response.statusCode,headers:e.response.headers,body:s})}).catch(e=>s(e))})}const h=l?new Promise((e,t)=>{d=setTimeout(()=>(u.onTimeout(),t(`${i} URL: ${p.url} exceeds the timeout ${l} ms`)),l)}):null;return(h?Promise.race([h,f]).then(e=>(clearTimeout(d),e)):f).then(e=>u.onResponse(e))})(p,i))),i}function API(e="untitled",t=!1){const{isQX:s,isLoon:r,isSurge:n,isNode:o,isJSBox:a,isScriptable:i}=ENV();return new class{constructor(e,t){this.name=e,this.debug=t,this.http=HTTP(),this.env=ENV(),this.node=(()=>{if(o){return{fs:require("fs")}}return null})(),this.initCache();Promise.prototype.delay=function(e){return this.then(function(t){return((e,t)=>new Promise(function(s){setTimeout(s.bind(null,t),e)}))(e,t)})}}initCache(){if(s&&(this.cache=JSON.parse($prefs.valueForKey(this.name)||"{}")),(r||n)&&(this.cache=JSON.parse($persistentStore.read(this.name)||"{}")),o){let e="root.json";this.node.fs.existsSync(e)||this.node.fs.writeFileSync(e,JSON.stringify({}),{flag:"wx"},e=>console.log(e)),this.root={},e=`${this.name}.json`,this.node.fs.existsSync(e)?this.cache=JSON.parse(this.node.fs.readFileSync(`${this.name}.json`)):(this.node.fs.writeFileSync(e,JSON.stringify({}),{flag:"wx"},e=>console.log(e)),this.cache={})}}persistCache(){const e=JSON.stringify(this.cache,null,2);s&&$prefs.setValueForKey(e,this.name),(r||n)&&$persistentStore.write(e,this.name),o&&(this.node.fs.writeFileSync(`${this.name}.json`,e,{flag:"w"},e=>console.log(e)),this.node.fs.writeFileSync("root.json",JSON.stringify(this.root,null,2),{flag:"w"},e=>console.log(e)))}write(e,t){if(this.log(`SET ${t}`),-1!==t.indexOf("#")){if(t=t.substr(1),n||r)return $persistentStore.write(e,t);if(s)return $prefs.setValueForKey(e,t);o&&(this.root[t]=e)}else this.cache[t]=e;this.persistCache()}read(e){return this.log(`READ ${e}`),-1===e.indexOf("#")?this.cache[e]:(e=e.substr(1),n||r?$persistentStore.read(e):s?$prefs.valueForKey(e):o?this.root[e]:void 0)}delete(e){if(this.log(`DELETE ${e}`),-1!==e.indexOf("#")){if(e=e.substr(1),n||r)return $persistentStore.write(null,e);if(s)return $prefs.removeValueForKey(e);o&&delete this.root[e]}else delete this.cache[e];this.persistCache()}notify(e,t="",p="",c={}){const l=c["open-url"],u=c["media-url"];if(s&&$notify(e,t,p,c),n&&$notification.post(e,t,p+`${u?"\n多媒体:"+u:""}`,{url:l}),r){let s={};l&&(s.openUrl=l),u&&(s.mediaUrl=u),"{}"===JSON.stringify(s)?$notification.post(e,t,p):$notification.post(e,t,p,s)}if(o||i){const s=p+(l?`\n点击跳转: ${l}`:"")+(u?`\n多媒体: ${u}`:"");if(a){require("push").schedule({title:e,body:(t?t+"\n":"")+s})}else console.log(`${e}\n${t}\n${s}\n\n`)}}log(e){this.debug&&console.log(`[${this.name}] LOG: ${e}`)}info(e){console.log(`[${this.name}] INFO: ${e}`)}error(e){console.log(`[${this.name}] ERROR: ${e}`)}wait(e){return new Promise(t=>setTimeout(t,e))}done(e={}){s||r||n?$done(e):o&&!a&&"undefined"!=typeof $context&&($context.headers=e.headers,$context.statusCode=e.statusCode,$context.body=e.body)}}(e,t)}function Gist(e,t){const s=HTTP({baseURL:"https://api.github.com",headers:{Authorization:`token ${t}`,"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.141 Safari/537.36"},events:{onResponse:e=>/^[45]/.test(String(e.statusCode))?Promise.reject(`ERROR: ${JSON.parse(e.body).message}`):e}});async function r(){return s.get("/gists").then(t=>{const s=JSON.parse(t.body);for(let t of s)if(t.description===e)return t.id;return-1})}this.upload=async function(t){const n=await r(),o={"Sub-Store":{content:t}};return-1===n?s.post({url:"/gists",body:JSON.stringify({description:e,public:!1,files:o})}):s.patch({url:`/gists/${n}`,body:JSON.stringify({files:o})})},this.download=async function(){const e=await r();if(-1===e)return Promise.reject("未找到Gist备份!");try{const{files:t}=await s.get(`/gists/${e}`).then(e=>JSON.parse(e.body)),r=t["Sub-Store"].raw_url;return await s.get(r).then(e=>e.body)}catch(e){return Promise.reject(e)}}}function express({port:e}={port:3e3}){const{isNode:t}=ENV(),s={"Content-Type":"text/plain;charset=UTF-8","Access-Control-Allow-Origin":"*","Access-Control-Allow-Methods":"POST,GET,OPTIONS,PATCH,PUT,DELETE","Access-Control-Allow-Headers":"Origin, X-Requested-With, Content-Type, Accept"};if(t){const t=require("express"),r=require("body-parser"),n=t();return n.use(r.json({verify:i})),n.use(r.urlencoded({verify:i,extended:!0})),n.use(r.raw({verify:i,type:"*/*"})),n.use((e,t,r)=>{t.set(s),r()}),n.start=(()=>{n.listen(e,()=>{$.log(`Express started on port: ${e}`)})}),n}const r=[],n=["GET","POST","PUT","DELETE","PATCH","OPTIONS","HEAD'","ALL"],o=(e,t=0)=>{let{method:s,url:n,headers:a,body:i}=e;/json/i.test(a["Content-Type"])&&(i=JSON.parse(i)),s=s.toUpperCase();const{path:u,query:f}=function(e){const t=(e.match(/https?:\/\/[^\/]+(\/[^?]*)/)||[])[1]||"/",s=e.indexOf("?"),r={};if(-1!==s){let t=e.slice(e.indexOf("?")+1).split("&");for(let e=0;em&&(h=r[d],m=e.split("/").length)}if(h){const e=()=>{o(s,n,d)},t={method:s,url:n,path:u,query:f,params:l(h.pattern,u),headers:a,body:i},r=p(),c=h.callback,m=e=>{r.status(500).json({status:"failed",message:`Internal Server Error: ${e}`})};if("AsyncFunction"===c.constructor.name)c(t,r,e).catch(m);else try{c(t,r,e)}catch(e){m(e)}}else{p().status(404).json({status:"failed",message:"ERROR: 404 not found"})}},a={};return n.forEach(e=>{a[e.toLowerCase()]=((t,s)=>{r.push({method:e,pattern:t,callback:s})})}),a.route=(e=>{const t={};return n.forEach(s=>{t[s.toLowerCase()]=(n=>(r.push({method:s,pattern:e,callback:n}),t))}),t}),a.start=(()=>{o($request)}),a;function i(e,t,s,r){s&&s.length&&(e.rawBody=s.toString(r||"utf8"))}function p(){let e=200;const{isQX:t,isLoon:r,isSurge:n}=ENV(),o=s,a={200:"HTTP/1.1 200 OK",201:"HTTP/1.1 201 Created",302:"HTTP/1.1 302 Found",307:"HTTP/1.1 307 Temporary Redirect",308:"HTTP/1.1 308 Permanent Redirect",404:"HTTP/1.1 404 Not Found",500:"HTTP/1.1 500 Internal Server Error"};return new class{status(t){return e=t,this}send(s=""){const i={status:t?a[e]:e,body:s,headers:o};t?$done(i):(r||n)&&$done({response:i})}end(){this.send()}html(e){this.set("Content-Type","text/html;charset=UTF-8"),this.send(e)}json(e){this.set("Content-Type","application/json;charset=UTF-8"),this.send(JSON.stringify(e))}set(e,t){return o[e]=t,this}}}function c(e,t){if(e instanceof RegExp&&e.test(t))return!0;if("/"===e)return!0;if(-1===e.indexOf(":")){const s=t.split("/"),r=e.split("/");for(let e=0;e>>6)+s(128|63&t):s(224|t>>>12&15)+s(128|t>>>6&63)+s(128|63&t):(t=65536+1024*(e.charCodeAt(0)-55296)+(e.charCodeAt(1)-56320),s(240|t>>>18&7)+s(128|t>>>12&63)+s(128|t>>>6&63)+s(128|63&t))},n=/[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g,o=function(t){const s=[0,2,1][t.length%3],r=t.charCodeAt(0)<<16|(t.length>1?t.charCodeAt(1):0)<<8|(t.length>2?t.charCodeAt(2):0);return[e.charAt(r>>>18),e.charAt(r>>>12&63),s>=2?"=":e.charAt(r>>>6&63),s>=1?"=":e.charAt(63&r)].join("")};this.encode=function(e){return"[object Uint8Array]"===Object.prototype.toString.call(e)?e.toString("base64"):function(e){return e.replace(n,r)}(String(e)).replace(/[\s\S]{1,3}/g,o)};const a=/[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g,i=function(e){switch(e.length){case 4:const t=((7&e.charCodeAt(0))<<18|(63&e.charCodeAt(1))<<12|(63&e.charCodeAt(2))<<6|63&e.charCodeAt(3))-65536;return s(55296+(t>>>10))+s(56320+(1023&t));case 3:return s((15&e.charCodeAt(0))<<12|(63&e.charCodeAt(1))<<6|63&e.charCodeAt(2));default:return s((31&e.charCodeAt(0))<<6|63&e.charCodeAt(1))}},p=function(e){const r=e.length,n=r%4,o=(r>0?t[e.charAt(0)]<<18:0)|(r>1?t[e.charAt(1)]<<12:0)|(r>2?t[e.charAt(2)]<<6:0)|(r>3?t[e.charAt(3)]:0),a=[s(o>>>16),s(o>>>8&255),s(255&o)];return a.length-=[0,0,2,1][n],a.join("")},c=function(e){return e.replace(/\S{1,4}/g,p)},l=function(e){return c(e).replace(a,i)};this.decode=function(e){return l(String(e).replace(/[-_]/g,function(e){return"-"===e?"+":"/"}).replace(/[^A-Za-z0-9\+\/]/g,"")).replace(/>/g,">").replace(/</g,"<")},this.safeEncode=function(e){return this.encode(e.replace(/\+/g,"-").replace(/\//g,"_"))},this.safeDecode=function(e){return this.decode(e.replace(/-/g,"+").replace(/_/g,"/"))}}var YAML=function(){var e=[],t=[],s=0,r={regLevel:new RegExp("^([\\s\\-]+)"),invalidLine:new RegExp("^\\-\\-\\-|^\\.\\.\\.|^\\s*#.*|^\\s*$"),dashesString:new RegExp('^\\s*\\"([^\\"]*)\\"\\s*$'),quotesString:new RegExp("^\\s*\\'([^\\']*)\\'\\s*$"),float:new RegExp("^[+-]?[0-9]+\\.[0-9]+(e[+-]?[0-9]+(\\.[0-9]+)?)?$"),integer:new RegExp("^[+-]?[0-9]+$"),array:new RegExp("\\[\\s*(.*)\\s*\\]"),map:new RegExp("\\{\\s*(.*)\\s*\\}"),key_value:new RegExp("([a-z0-9_-][ a-z0-9_-]*):( .+)","i"),single_key_value:new RegExp("^([a-z0-9_-][ a-z0-9_-]*):( .+?)$","i"),key:new RegExp("([a-z0-9_-][ a-z0-9_-]*):( .+)?","i"),item:new RegExp("^-\\s+"),trim:new RegExp("^\\s+|\\s+$"),comment:new RegExp("([^\\'\\\"#]+([\\'\\\"][^\\'\\\"]*[\\'\\\"])*)*(#.*)?")};function n(e){return{parent:null,length:0,level:e,lines:[],children:[],addChild:function(e){this.children.push(e),e.parent=this,++this.length}}}function o(e){var t=null;if("true"==(e=e.replace(r.trim,"")))return!0;if("false"==e)return!1;if(".NaN"==e)return Number.NaN;if("null"==e)return null;if(".inf"==e)return Number.POSITIVE_INFINITY;if("-.inf"==e)return Number.NEGATIVE_INFINITY;if(t=e.match(r.dashesString))return t[1];if(t=e.match(r.quotesString))return t[1];if(t=e.match(r.float))return parseFloat(t[0]);if(t=e.match(r.integer))return parseInt(t[0]);if(isNaN(t=Date.parse(e))){if(t=e.match(r.single_key_value))return(a={})[t[1]]=o(t[2]),a;if(t=e.match(r.array)){for(var s=0,n=" ",a=[],i="",p=!1,c=0,l=t[1].length;c0&&a.push(o(i)),a}if(t=e.match(r.map)){for(s=0,n=" ",a=[],i="",p=!1,c=0,l=t[1].length;c0&&a.push(i);var u={};for(c=0,l=a.length;c"==O[0]?null!=f?f[v]=a(u.shift()):c[v]=a(u.shift()):null!=f?f[v]=o(O):c[v]=o(O)}else null!=f?f[v]=s(u):c[v]=s(u)}else S.match(/^-\s*$/)?(m&&(m=!1,void 0===c.length&&(c=[])),null!=f&&c.push(f),f={},m=!0):(p=S.match(/^-\s*(.*)/))&&(null!=f?f.push(o(p[1])):(m&&(m=!1,void 0===c.length&&(c=[])),c.push(o(p[1]))))}null!=f&&(m&&(m=!1,void 0===c.length&&(c=[])),c.push(f))}for($=h.length-1;$>=0;--$)n.splice.call(n,h[$],1);return c}(s.children)}return{eval:function(o){e=[],t=[],s=(new Date).getTime();var a=p(function(t){var s,o=r.regLevel,a=r.invalidLine,i=t.split("\n"),p=0,c=0,l=[],u=new n(-1),f=new n(0);u.addChild(f);var d=[],h="";l.push(f),d.push(p);for(var m=0,$=i.length;m<$;++m)if(!(h=i[m]).match(a)){if((p=(s=o.exec(h))?s[1].length:0)>c){var g=f;f=new n(p),g.addChild(f),l.push(f),d.push(p)}else if(p=0;--w)if(d[w]==p){f=new n(p),l.push(f),d.push(p),null!=l[w].parent&&l[w].parent.addChild(f),y=!0;break}if(!y)return void e.push("Error: Invalid indentation at line "+m+": "+h)}f.lines.push(h.replace(r.trim,"")),c=p}return u}(function(e){var t,s=e.split("\n"),n=r.comment;for(var o in s)(t="string"==typeof s[o]&&s[o].match(n))&&void 0!==t[3]&&(s[o]=t[0].substr(0,t[0].length-t[3].length));return s.join("\n")}(o)));return s=(new Date).getTime()-s,a},getErrors:function(){return e},getProcessingTime:function(){return s}}}(); \ No newline at end of file diff --git a/web/package-lock.json b/web/package-lock.json index 9ca6bc1..430fa73 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,6 +1,6 @@ { "name": "web", - "version": "0.1.0", + "version": "1.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -3291,6 +3291,11 @@ "integrity": "sha1-kAlISfCTfy7twkJdDSip5fDLrZ4=", "dev": true }, + "chartist": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/chartist/-/chartist-0.11.4.tgz", + "integrity": "sha512-H4AimxaUD738/u9Mq8t27J4lh6STsLi4BQHt65nOtpLk3xyrBPaLiLMrHw7/WV9CmsjGA02WihjuL5qpSagLYw==" + }, "check-types": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz", @@ -3354,6 +3359,11 @@ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "dev": true }, + "chroma-js": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-1.4.1.tgz", + "integrity": "sha512-jTwQiT859RTFN/vIf7s+Vl/Z2LcMrvMv3WUFmd/4u76AdlFC0NTNgqEEFPcRiHmAswPsMiQEDZLM8vX8qXpZNQ==" + }, "chrome-trace-event": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", @@ -4719,14 +4729,6 @@ "safer-buffer": "^2.1.0" } }, - "echarts": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/echarts/-/echarts-4.9.0.tgz", - "integrity": "sha512-+ugizgtJ+KmsJyyDPxaw2Br5FqzuBnyOWwcxPKO6y0gc5caYcfnEUIlNStx02necw8jmKmTafmpHhGo4XDtEIA==", - "requires": { - "zrender": "4.3.2" - } - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -7139,7 +7141,8 @@ "lodash": { "version": "4.17.20", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true }, "lodash.defaultsdeep": { "version": "4.6.1", @@ -7542,20 +7545,6 @@ "minimist": "^1.2.5" } }, - "monaco-editor": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.19.3.tgz", - "integrity": "sha512-2n1vJBVQF2Hhi7+r1mMeYsmlf18hjVb6E0v5SoMZyb4aeOmYPKun+CE3gYpiNA1KEvtSdaDHFBqH9d7Wd9vREg==" - }, - "monaco-editor-vue": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/monaco-editor-vue/-/monaco-editor-vue-1.0.10.tgz", - "integrity": "sha512-FO6rioCXkonKmDZYB2WDhDNekXuNJgjxoAPfzwYX+5NTMaxRJcWl3bcUByRpL6LQQ1bTEYLZ+ZdDpoJ+la2trQ==", - "requires": { - "monaco-editor": "^0.19.3", - "vue": "^2.6.11" - } - }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -9511,11 +9500,6 @@ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", "dev": true }, - "resize-detector": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/resize-detector/-/resize-detector-0.1.10.tgz", - "integrity": "sha512-iLcXC8A6Fb0DfA+TRiywrK/0A22bFqkhntjMJMEzXDA4XkcEkfwpNbv7W8iewUiD0xYIaeiXOfiEehTqGKsUFw==" - }, "resolve": { "version": "1.17.0", "resolved": "https://registry.npm.taobao.org/resolve/download/resolve-1.17.0.tgz?cache=0&sync_timestamp=1589682751623&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fresolve%2Fdownload%2Fresolve-1.17.0.tgz", @@ -11245,6 +11229,11 @@ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", "dev": true }, + "vee-validate": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/vee-validate/-/vee-validate-3.4.5.tgz", + "integrity": "sha512-ZEcLqOAZzSkMhDvPcTx0xcwVOijFnMW9J+BA20j+rDmo24T8RCCqVQyRwwrDrcWJZV2dRYl/yYNa2GB6UCoBvg==" + }, "vendors": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", @@ -11273,6 +11262,14 @@ "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.12.tgz", "integrity": "sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg==" }, + "vue-chartist": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/vue-chartist/-/vue-chartist-2.3.1.tgz", + "integrity": "sha512-DNM+eHBA/vebLChmPsWuNzOP0E38yDs6nvBq/VZPLsl+DjMUt9sDiQYAivOHDJ7ss5fCMMYyxRTXd6yZqSt5MQ==", + "requires": { + "chartist": "^0.11.0" + } + }, "vue-cli-plugin-vuetify": { "version": "2.0.7", "resolved": "https://registry.npm.taobao.org/vue-cli-plugin-vuetify/download/vue-cli-plugin-vuetify-2.0.7.tgz", @@ -11292,16 +11289,6 @@ } } }, - "vue-echarts": { - "version": "5.0.0-beta.0", - "resolved": "https://registry.npmjs.org/vue-echarts/-/vue-echarts-5.0.0-beta.0.tgz", - "integrity": "sha512-QZFKGXDAYFQo+F20REpzcdLx79nsl4kOorJRpN+08aYq4YiIlmtWss1Lxadm7Fo+NYyWm8nnT+h4xHv3uqWIDQ==", - "requires": { - "core-js": "^3.4.4", - "lodash": "^4.17.15", - "resize-detector": "^0.1.10" - } - }, "vue-eslint-parser": { "version": "7.1.0", "resolved": "https://registry.npm.taobao.org/vue-eslint-parser/download/vue-eslint-parser-7.1.0.tgz?cache=0&sync_timestamp=1589684321779&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-eslint-parser%2Fdownload%2Fvue-eslint-parser-7.1.0.tgz", @@ -11334,6 +11321,11 @@ "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==", "dev": true }, + "vue-i18n": { + "version": "8.22.2", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-8.22.2.tgz", + "integrity": "sha512-rb569fVJInPUgS/bbCxEQ9DrAoFTntuJvYoK4Fpk2VfNbA09WzdTKk57ppjz3S+ps9hW+p9H+2ASgMvojedkow==" + }, "vue-loader": { "version": "15.9.3", "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.3.tgz", @@ -11403,6 +11395,15 @@ "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", "dev": true }, + "vue-world-map": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/vue-world-map/-/vue-world-map-0.1.1.tgz", + "integrity": "sha1-ehy6G3er5n0JZFzE3TbUeoTEegI=", + "requires": { + "chroma-js": "^1.3.5", + "vue": "^2.5.2" + } + }, "vuetify": { "version": "2.3.10", "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.3.10.tgz", @@ -12289,11 +12290,6 @@ "dev": true } } - }, - "zrender": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/zrender/-/zrender-4.3.2.tgz", - "integrity": "sha512-bIusJLS8c4DkIcdiK+s13HiQ/zjQQVgpNohtd8d94Y2DnJqgM1yjh/jpDb8DoL6hd7r8Awagw8e3qK/oLaWr3g==" } } } diff --git a/web/package.json b/web/package.json index 62a5ae5..cb7ff0e 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "web", - "version": "0.1.0", + "version": "1.0.0", "private": true, "scripts": { "serve": "vue-cli-service serve", @@ -11,16 +11,17 @@ "dependencies": { "@dzangolab/vue-country-flag-icon": "^0.2.0", "axios": "^0.20.0", + "chartist": "^0.11.4", "core-js": "^3.6.5", - "echarts": "^4.9.0", - "lodash": "^4.17.20", "material-design-icons-iconfont": "^5.0.1", - "monaco-editor-vue": "^1.0.10", "v-clipboard": "^2.2.3", + "vee-validate": "^3.4.5", "vue": "^2.6.12", - "vue-echarts": "^5.0.0-beta.0", + "vue-chartist": "^2.3.1", + "vue-i18n": "^8.22.2", "vue-qrcode-component": "^2.1.1", "vue-router": "^3.4.3", + "vue-world-map": "^0.1.1", "vuetify": "^2.3.10", "vuex": "^3.5.1" }, diff --git a/web/public/404.html b/web/public/404.html new file mode 100644 index 0000000..440e288 --- /dev/null +++ b/web/public/404.html @@ -0,0 +1,41 @@ + + + + + Vuetify Md Pro | 404 + + + + + diff --git a/web/src/App.vue b/web/src/App.vue index 05efd15..59e312c 100644 --- a/web/src/App.vue +++ b/web/src/App.vue @@ -1,9 +1,9 @@ - 关键词排序 + 正则排序 - 根据给出的关键词的顺序对节点进行排序,没有出现的关键词将会按照正序排列。 + 根据给出的正则表达式的顺序对节点进行排序,无法匹配的节点将会按照正序排列。 - 关键词 - - {{ keyword }} + {{ item }} @@ -67,41 +68,41 @@ export default { selection: null, currentTag: null, form: { - keyword: "" + item: "" }, - keywords: [] + items: [] } }, created() { if (this.args) { - this.keywords = this.args || []; + this.items = this.args || []; } }, watch: { - keywords() { + items() { this.save(); }, }, methods: { - add(keyword) { - if (keyword) { - this.keywords.push(keyword); - this.form.keyword = ""; + add(item) { + if (item) { + this.items.push(item); + this.form.item = ""; } else { - this.$store.commit("SET_ERROR_MESSAGE", "关键词不能为空!"); + this.$store.commit("SET_ERROR_MESSAGE", "正则表达式不能为空!"); } }, edit(idx) { - this.form.keyword = this.keywords[idx]; + this.form.item = this.items[idx]; this.remove(idx); }, remove(idx) { - this.keywords.splice(idx, 1); + this.items.splice(idx, 1); }, save() { this.$emit("dataChanged", { idx: this.idx, - args: this.keywords + args: this.items }); }, } diff --git a/web/src/components/ScriptFilter.vue b/web/src/components/ScriptFilter.vue index 57246ac..f14ce16 100644 --- a/web/src/components/ScriptFilter.vue +++ b/web/src/components/ScriptFilter.vue @@ -41,14 +41,15 @@ + - diff --git a/web/src/components/ScriptOperator.vue b/web/src/components/ScriptOperator.vue index c7e665e..208eda5 100644 --- a/web/src/components/ScriptOperator.vue +++ b/web/src/components/ScriptOperator.vue @@ -41,14 +41,15 @@ + - diff --git a/web/src/components/SpeedDial.vue b/web/src/components/SpeedDial.vue index 66a2e14..21f5b6b 100644 --- a/web/src/components/SpeedDial.vue +++ b/web/src/components/SpeedDial.vue @@ -15,7 +15,7 @@ fab > mdi-close - apps + mdi-plus
- - - - - settings - - - Settings - - - - - help - - - Help - - - - - - - - {{title}} - +

{{title}}

@@ -50,12 +20,6 @@ export default { }, methods: { - toggleMenu: function () { - this.showMenu = !this.showMenu; - }, - doNothing: function () { - - } }, computed: { diff --git a/web/src/components/base/Card.vue b/web/src/components/base/Card.vue new file mode 100644 index 0000000..9f43013 --- /dev/null +++ b/web/src/components/base/Card.vue @@ -0,0 +1,9 @@ + diff --git a/web/src/components/base/Item.vue b/web/src/components/base/Item.vue new file mode 100644 index 0000000..4113d14 --- /dev/null +++ b/web/src/components/base/Item.vue @@ -0,0 +1,69 @@ + + + diff --git a/web/src/components/base/ItemGroup.vue b/web/src/components/base/ItemGroup.vue new file mode 100644 index 0000000..2b384ec --- /dev/null +++ b/web/src/components/base/ItemGroup.vue @@ -0,0 +1,123 @@ + + + + + diff --git a/web/src/components/base/ItemSubGroup.vue b/web/src/components/base/ItemSubGroup.vue new file mode 100644 index 0000000..dfceb00 --- /dev/null +++ b/web/src/components/base/ItemSubGroup.vue @@ -0,0 +1,25 @@ + + + diff --git a/web/src/components/base/MaterialAlert.vue b/web/src/components/base/MaterialAlert.vue new file mode 100644 index 0000000..f0336ee --- /dev/null +++ b/web/src/components/base/MaterialAlert.vue @@ -0,0 +1,64 @@ + + + diff --git a/web/src/components/base/MaterialCard.vue b/web/src/components/base/MaterialCard.vue new file mode 100644 index 0000000..6380d2d --- /dev/null +++ b/web/src/components/base/MaterialCard.vue @@ -0,0 +1,168 @@ + + + + + diff --git a/web/src/components/base/MaterialChartCard.vue b/web/src/components/base/MaterialChartCard.vue new file mode 100644 index 0000000..25ef3e8 --- /dev/null +++ b/web/src/components/base/MaterialChartCard.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/web/src/components/base/MaterialDropdown.vue b/web/src/components/base/MaterialDropdown.vue new file mode 100644 index 0000000..9be5e8b --- /dev/null +++ b/web/src/components/base/MaterialDropdown.vue @@ -0,0 +1,70 @@ + + + diff --git a/web/src/components/base/MaterialSnackbar.vue b/web/src/components/base/MaterialSnackbar.vue new file mode 100644 index 0000000..25660af --- /dev/null +++ b/web/src/components/base/MaterialSnackbar.vue @@ -0,0 +1,66 @@ + + + + diff --git a/web/src/components/base/MaterialStatsCard.vue b/web/src/components/base/MaterialStatsCard.vue new file mode 100644 index 0000000..b1d919a --- /dev/null +++ b/web/src/components/base/MaterialStatsCard.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/web/src/components/base/MaterialTabs.vue b/web/src/components/base/MaterialTabs.vue new file mode 100644 index 0000000..dc9e4b2 --- /dev/null +++ b/web/src/components/base/MaterialTabs.vue @@ -0,0 +1,43 @@ + + + + + diff --git a/web/src/components/base/MaterialTestimony.vue b/web/src/components/base/MaterialTestimony.vue new file mode 100644 index 0000000..390905e --- /dev/null +++ b/web/src/components/base/MaterialTestimony.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/web/src/components/base/MaterialWizard.vue b/web/src/components/base/MaterialWizard.vue new file mode 100644 index 0000000..fa45adb --- /dev/null +++ b/web/src/components/base/MaterialWizard.vue @@ -0,0 +1,109 @@ + + + + + diff --git a/web/src/components/base/Subheading.vue b/web/src/components/base/Subheading.vue new file mode 100644 index 0000000..5d89cef --- /dev/null +++ b/web/src/components/base/Subheading.vue @@ -0,0 +1,34 @@ + + + diff --git a/web/src/components/base/VComponent.vue b/web/src/components/base/VComponent.vue new file mode 100644 index 0000000..0a1a60a --- /dev/null +++ b/web/src/components/base/VComponent.vue @@ -0,0 +1,42 @@ + + + diff --git a/web/src/css/app.css b/web/src/css/app.css deleted file mode 100644 index 2fbcf4c..0000000 --- a/web/src/css/app.css +++ /dev/null @@ -1,48 +0,0 @@ -[v-cloak] { - display: none; -} -.text-pre-wrap { - white-space: pre-wrap !important; -} -.v-navigation-drawer { - padding-top: constant(safe-area-inset-top) !important; - padding-top: env(safe-area-inset-top) !important; -} -.v-bottom-sheet.v-dialog--fullscreen { - padding-top: constant(safe-area-inset-top) !important; - padding-top: env(safe-area-inset-top) !important; -} -.v-app-bar { - height: auto !important; - padding-top: constant(safe-area-inset-top) !important; - padding-top: env(safe-area-inset-top) !important; -} -.v-toolbar { - height: auto !important; - padding-top: constant(safe-area-inset-top) !important; - padding-top: env(safe-area-inset-top) !important; -} -.v-toolbar__content { - padding-left: 12px !important; - padding-right: 12px !important; -} -.v-main { - margin-top: constant(safe-area-inset-top) !important; - margin-top: env(safe-area-inset-top) !important; - margin-bottom: constant(safe-area-inset-bottom) !important; - margin-bottom: env(safe-area-inset-bottom) !important; -} -.v-main .container { - height: 100%; -} -.v-bottom-navigation, -.v-bottom-sheet { - padding-bottom: constant(safe-area-inset-bottom); - padding-bottom: env(safe-area-inset-bottom); -} -.v-bottom-navigation { - box-sizing: content-box; -} -.v-bottom-navigation button { - box-sizing: border-box; -} \ No newline at end of file diff --git a/web/src/i18n.js b/web/src/i18n.js new file mode 100644 index 0000000..7242d96 --- /dev/null +++ b/web/src/i18n.js @@ -0,0 +1,24 @@ +import Vue from 'vue' +import VueI18n from 'vue-i18n' + +import ar from 'vuetify/lib/locale/ar' +import en from 'vuetify/lib/locale/en' + +Vue.use(VueI18n) + +const messages = { + en: { + ...require('@/locales/en.json'), + $vuetify: en + }, + ar: { + ...require('@/locales/ar.json'), + $vuetify: ar + } +} + +export default new VueI18n({ + locale: process.env.VUE_APP_I18N_LOCALE || 'en', + fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en', + messages +}) diff --git a/web/src/locales/ar.json b/web/src/locales/ar.json new file mode 100644 index 0000000..37bf8e4 --- /dev/null +++ b/web/src/locales/ar.json @@ -0,0 +1,44 @@ +{ + "avatar": "تانيا أندرو", + "buttons": "وصفت", + "calendar": "التقويم", + "charts": "الرسوم البيانية", + "components": "المكونات", + "ct": "CT", + "dashboard": "لوحة القيادة", + "dtables": "جداول البيانات", + "eforms": "أشكال موسعة", + "error": "صفحة الخطأ", + "etables": "الجداول الموسعة", + "example": "مثال", + "forms": "إستمارات", + "fullscreen": "خريطة الشاشة الكاملة", + "google": "خرائط جوجل", + "grid": "نظام الشبكة", + "icons": "الرموز", + "lock": "قفل الشاشة الصفحة", + "login": "صفحة تسجيل الدخول", + "maps": "خرائط", + "multi": "متعدد المستويات انهيار", + "notifications": "إخطارات", + "pages": "صفحات", + "plan": "اختر خطة", + "pricing": "التسعير", + "my-profile": "ملفي", + "edit-profile": "تعديل الملف الشخصي", + "register": "تسجيل الصفحة", + "rforms": "النماذج العادية", + "rtables": "الجداول العادية", + "rtl": "دعم RTL", + "search": "بحث...", + "settings": "الإعدادات", + "tables": "الجداول", + "tabs": "Tabs", + "tim": "تيم الإبداعية", + "timeline": "الجدول الزمني", + "typography": "طباعة", + "user": "ملف تعريفي للمستخدم", + "vforms": "نماذج التحقق من الصحة", + "widgets": "الحاجيات", + "wizard": "ساحر" +} \ No newline at end of file diff --git a/web/src/locales/en.json b/web/src/locales/en.json new file mode 100644 index 0000000..6251f91 --- /dev/null +++ b/web/src/locales/en.json @@ -0,0 +1,44 @@ +{ + "avatar": "Tania Andrew", + "buttons": "Buttons", + "calendar": "Calendar", + "charts": "Charts", + "components": "Components", + "ct": "CT", + "dashboard": "Dashboard", + "dtables": "Data Tables", + "eforms": "Extended Forms", + "error": "Error Page", + "etables": "Extended Tables", + "example": "Example", + "forms": "Forms", + "fullscreen": "Full Screen Map", + "google": "Google Maps", + "grid": "Grid System", + "icons": "Icons", + "lock": "Lock Screen Page", + "login": "Login Page", + "maps": "Maps", + "multi": "Multi Level Collapse", + "notifications": "Notifications", + "pages": "Pages", + "plan": "Choose Plan", + "pricing": "Pricing", + "my-profile": "My Profile", + "edit-profile": "Edit Profile", + "register": "Register Page", + "rforms": "Regular Forms", + "rtables": "Regular Tables", + "rtl": "RTL Support", + "search": "Search", + "settings": "Settings", + "tables": "Tables", + "tabs": "Tabs", + "tim": "Creative Tim", + "timeline": "Timeline", + "typography": "Typography", + "user": "User Profile", + "vforms": "Validation Forms", + "widgets": "Widgets", + "wizard": "Wizard" +} \ No newline at end of file diff --git a/web/src/main.js b/web/src/main.js index 365809e..9bda4d8 100644 --- a/web/src/main.js +++ b/web/src/main.js @@ -1,6 +1,12 @@ import Vue from 'vue' import App from './App.vue' import vuetify from './plugins/vuetify'; +import 'material-design-icons-iconfont/dist/material-design-icons.css' +import './plugins/base'; +import './plugins/chartist'; +// import './plugins/vee-validate'; +import './plugins/vue-world-map'; +import i18n from './i18n'; import router from './router'; import store from './store'; import Clipboard from 'v-clipboard'; @@ -12,5 +18,6 @@ new Vue({ router, store, Clipboard, + i18n, render: h => h(App) }).$mount('#app') diff --git a/web/src/plugins/base.js b/web/src/plugins/base.js new file mode 100644 index 0000000..bec424d --- /dev/null +++ b/web/src/plugins/base.js @@ -0,0 +1,17 @@ +import Vue from 'vue' +import upperFirst from 'lodash/upperFirst' +import camelCase from 'lodash/camelCase' + +const requireComponent = require.context( + '@/components/base', true, /\.vue$/ +) + +requireComponent.keys().forEach(fileName => { + const componentConfig = requireComponent(fileName) + + const componentName = upperFirst( + camelCase(fileName.replace(/^\.\//, '').replace(/\.\w+$/, '')) + ) + + Vue.component(`Base${componentName}`, componentConfig.default || componentConfig) +}) diff --git a/web/src/plugins/chartist.js b/web/src/plugins/chartist.js new file mode 100644 index 0000000..2c8bb2d --- /dev/null +++ b/web/src/plugins/chartist.js @@ -0,0 +1,4 @@ +import Vue from 'vue' +import 'chartist/dist/chartist.min.css' + +Vue.use(require('vue-chartist')) diff --git a/web/src/plugins/vee-validate.js b/web/src/plugins/vee-validate.js new file mode 100644 index 0000000..8c5eab1 --- /dev/null +++ b/web/src/plugins/vee-validate.js @@ -0,0 +1,5 @@ +import Vue from 'vue' +// import * as VeeValidate from 'vee-validate' +import VeeValidate from 'vee-validate' + +Vue.use(VeeValidate) diff --git a/web/src/plugins/vue-world-map.js b/web/src/plugins/vue-world-map.js new file mode 100644 index 0000000..3f5e44c --- /dev/null +++ b/web/src/plugins/vue-world-map.js @@ -0,0 +1,4 @@ +import Vue from 'vue' +import VueWorldMap from 'vue-world-map' + +Vue.component('v-world-map', VueWorldMap) diff --git a/web/src/plugins/vuetify.js b/web/src/plugins/vuetify.js index 00ba4d3..4954830 100644 --- a/web/src/plugins/vuetify.js +++ b/web/src/plugins/vuetify.js @@ -1,7 +1,24 @@ -import Vue from 'vue'; -import Vuetify from 'vuetify/lib'; -import 'material-design-icons-iconfont/dist/material-design-icons.css' +import Vue from 'vue' +import Vuetify from 'vuetify/lib' +import i18n from '@/i18n' -Vue.use(Vuetify); +Vue.use(Vuetify) -export default new Vuetify({}); +const theme = { + primary: '#E91E63', + secondary: '#9C27b0', + accent: '#9C27b0', + info: '#00CAE3' +} + +export default new Vuetify({ + lang: { + t: (key, ...params) => i18n.t(key, params) + }, + theme: { + themes: { + dark: theme, + light: theme + } + } +}) diff --git a/web/src/views/CollectionEditor.vue b/web/src/views/CollectionEditor.vue deleted file mode 100644 index bdce245..0000000 --- a/web/src/views/CollectionEditor.vue +++ /dev/null @@ -1,110 +0,0 @@ - - - - - \ No newline at end of file diff --git a/web/src/views/Dashboard.vue b/web/src/views/Dashboard.vue index 8127c46..5bb0f67 100644 --- a/web/src/views/Dashboard.vue +++ b/web/src/views/Dashboard.vue @@ -1,41 +1,11 @@