├── CNAME ├── LICENSE ├── README.md ├── appid.js ├── brew.sh ├── brook.js ├── brook.sh ├── build.sh ├── bundle.js ├── china.js ├── lib.js ├── nami.sh └── static ├── m.css ├── r.js └── readme.md /CNAME: -------------------------------------------------------------------------------- 1 | bash.ooo -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 TxThinking 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [bash.ooo](https://github.com/txthinking/bash) 2 | 3 | ## [nami](https://github.com/txthinking/nami) 4 | 5 | ``` 6 | bash <(curl https://bash.ooo/nami.sh) 7 | ``` 8 | 9 | ## [brook](https://github.com/txthinking/brook) 10 | 11 | ``` 12 | bash <(curl https://bash.ooo/brook.sh) 13 | ``` 14 | 15 | ## china.js 16 | 17 | > `nami install brook bun bun.plus` 18 | 19 | [自动生成自用的中国域名直连模块](https://www.txthinking.com/talks/articles/china-list.article) 20 | 21 | ``` 22 | bunu https://bash.ooo/china.js 23 | ``` 24 | 25 | ## appid.js 26 | 27 | > `nami install bun bun.plus` 28 | 29 | Get appid from macOS app 30 | 31 | ``` 32 | bunu https://bash.ooo/appid.js 33 | ``` 34 | 35 | ## [brew](https://brew.sh) 36 | 37 | ``` 38 | bash <(curl https://bash.ooo/brew.sh) 39 | ``` 40 | 41 | -------------------------------------------------------------------------------- /appid.js: -------------------------------------------------------------------------------- 1 | import * as fs from 'node:fs/promises'; 2 | import { $ } from 'bun'; 3 | 4 | if (process.argv.length != 3) { 5 | console.log() 6 | console.log(`Get appid from macOS app:`) 7 | console.log() 8 | console.log(` $ bunu https://bash.ooo/appid.js /Applications/Safari.app`) 9 | console.log() 10 | process.exit() 11 | } 12 | 13 | var l = await fs.readdir(process.argv[2], { recursive: true }) 14 | l = l.filter(v => v.endsWith('Info.plist')) 15 | var l1= [] 16 | for(var i=0;i (i18n[s] ? i18n[s][language] ?? s : s); 82 | 83 | var ip4 = (await $`curl -s -4 http3.ooo`.text().catch((e) => "")).trim(); 84 | var ip6 = (await $`curl -s -6 http3.ooo`.text().catch((e) => "")).trim(); 85 | if (!ip4) { 86 | console.log("Can not find your server public IPv4"); 87 | process.exit(1); 88 | } 89 | if (!ip6) { 90 | console.log("Can not find your server public IPv6"); 91 | process.exit(1); 92 | } 93 | 94 | var letsgo = async () => { 95 | await lib.select(lang("Choose what you want to do: "), [ 96 | { 97 | anwser: lang("I want to run brook server/wsserver/wssserver/socks5"), 98 | action: async () => { 99 | await lib.select(lang("Choose which you want to run: "), [ 100 | { 101 | anwser: lang("I want to run brook server"), 102 | action: async () => { 103 | var port = await lib.question(lang("Type a port, such as 9999: "), /\d+/); 104 | if ((await $`lsof -i:${port}`.text().catch((e) => "")).trim()) { 105 | console.log(lang("This port is occupied!")); 106 | process.exit(1); 107 | } 108 | var password = await lib.question(lang("Type a password, such as mypassword: "), /.+/); 109 | await $`joker brook server --listen :${port} --password "${password}"` 110 | await Bun.sleep(2000); 111 | await $`joker list` 112 | await $`joker log $(joker last)` 113 | if (ip4) { 114 | await $`brook link -s ${joinhostport(ip4, port)} -p "${password}" --udpovertcp` 115 | } 116 | if (ip6) { 117 | await $`brook link -s ${joinhostport(ip6, port)} -p "${password}" --udpovertcp` 118 | } 119 | console.log(`${lang("Tip: if there is a firewall, remember to open TCP and UDP")} ${port}`); 120 | }, 121 | }, 122 | { 123 | anwser: lang("I want to run brook wsserver"), 124 | action: async () => { 125 | var port = await lib.question(lang("Type a port, such as 9999: "), /\d+/); 126 | if ((await $`lsof -i:${port}`.text().catch((e) => "")).trim()) { 127 | console.log(lang("This port is occupied!")); 128 | process.exit(1); 129 | } 130 | var password = await lib.question(lang("Type a password, such as mypassword: "), /.+/); 131 | await $`joker brook wsserver --listen :${port} --password "${password}"` 132 | await Bun.sleep(2000); 133 | await $`joker list` 134 | await $`joker log $(joker last)` 135 | if (ip4) { 136 | await $`brook link -s ws://${joinhostport(ip4, port)} -p "${password}"` 137 | } 138 | if (ip6) { 139 | await $`brook link -s ws://${joinhostport(ip6, port)} -p "${password}"` 140 | } 141 | console.log(`${lang("Tip: if there is a firewall, remember to open TCP")} ${port}`); 142 | }, 143 | }, 144 | { 145 | anwser: lang("I want to run brook wssserver"), 146 | action: async () => { 147 | if ((await $`lsof -i:80`.text().catch((e) => "")).trim()) { 148 | console.log(lang("Port 80 is occupied!")); 149 | process.exit(1); 150 | } 151 | var domain = await lib.question(lang("Type your domain, such as hello.com: "), /([1-9]|[a-z]|-|\.)+/); 152 | if ((await $`dig +short -t A ${domain}`.text()).trim() != ip4 && (await $`dig +short -t AAAA ${domain}`.text()).trim() != ip6) { 153 | console.log(lang("Please resolve your domain to your server's IP")); 154 | process.exit(1); 155 | } 156 | var port = await lib.question(lang("Type a port, such as 9999: "), /\d+/); 157 | if ((await $`lsof -i:${port}`.text().catch((e) => "")).trim()) { 158 | console.log(lang("This port is occupied!")); 159 | process.exit(1); 160 | } 161 | var password = await lib.question(lang("Type a password, such as mypassword: "), /.+/); 162 | await $`joker brook wssserver --domainaddress ${domain}:${port} --password "${password}"` 163 | await Bun.sleep(2000); 164 | await $`joker list` 165 | await $`joker log $(joker last)` 166 | await $`brook link -s wss://${joinhostport(domain, port)} -p "${password}"` 167 | console.log(`${lang("Tip: if there is a firewall, remember to open TCP 80 and")} ${port}`); 168 | }, 169 | }, 170 | { 171 | anwser: lang("I want to run brook wssserver withoutBrookProtocol"), 172 | action: async () => { 173 | if ((await $`lsof -i:80`.text().catch((e) => "")).trim()) { 174 | console.log(lang("Port 80 is occupied!")); 175 | process.exit(1); 176 | } 177 | var domain = await lib.question(lang("Type your domain, such as hello.com: "), /([1-9]|[a-z]|-|\.)+/); 178 | if ((await $`dig +short -t A ${domain}`.text()).trim() != ip4 && (await $`dig +short -t AAAA ${domain}`.text()).trim() != ip6) { 179 | console.log(lang("Please resolve your domain to your server's IP")); 180 | process.exit(1); 181 | } 182 | var port = await lib.question(lang("Type a port, such as 9999: "), /\d+/); 183 | if ((await $`lsof -i:${port}`.text().catch((e) => "")).trim()) { 184 | console.log(lang("This port is occupied!")); 185 | process.exit(1); 186 | } 187 | var password = await lib.question(lang("Type a password, such as mypassword: "), /.+/); 188 | await $`joker brook wssserver --domainaddress ${domain}:${port} --password "${password}" --withoutBrookProtocol` 189 | await Bun.sleep(2000); 190 | await $`joker list` 191 | await $`joker log $(joker last)` 192 | await $`brook link -s wss://${joinhostport(domain, port)} -p "${password}" --withoutBrookProtocol` 193 | console.log(`${lang("Tip: if there is a firewall, remember to open TCP 80 and")} ${port}`); 194 | }, 195 | }, 196 | { 197 | anwser: lang("I want to run brook quicserver"), 198 | action: async () => { 199 | if ((await $`lsof -i:80`.text().catch((e) => "")).trim()) { 200 | console.log(lang("Port 80 is occupied!")); 201 | process.exit(1); 202 | } 203 | var domain = await lib.question(lang("Type your domain, such as hello.com: "), /([1-9]|[a-z]|-|\.)+/); 204 | if ((await $`dig +short -t A ${domain}`.text()).trim() != ip4 && (await $`dig +short -t AAAA ${domain}`.text()).trim() != ip6) { 205 | console.log(lang("Please resolve your domain to your server's IP")); 206 | process.exit(1); 207 | } 208 | var port = await lib.question(lang("Type a port, such as 9999: "), /\d+/); 209 | if ((await $`lsof -i:${port}`.text().catch((e) => "")).trim()) { 210 | console.log(lang("This port is occupied!")); 211 | process.exit(1); 212 | } 213 | var password = await lib.question(lang("Type a password, such as mypassword: "), /.+/); 214 | await $`joker brook quicserver --domainaddress ${domain}:${port} --password "${password}"` 215 | await Bun.sleep(2000); 216 | await $`joker list` 217 | await $`joker log $(joker last)` 218 | await $`brook link -s quic://${joinhostport(domain, port)} -p "${password}" --udpoverstream` 219 | console.log(`${lang("Tip: if there is a firewall, remember to open TCP 80 and")} ${port}`); 220 | }, 221 | }, 222 | { 223 | anwser: lang("I want to run brook quicserver withoutBrookProtocol"), 224 | action: async () => { 225 | if ((await $`lsof -i:80`.text().catch((e) => "")).trim()) { 226 | console.log(lang("Port 80 is occupied!")); 227 | process.exit(1); 228 | } 229 | var domain = await lib.question(lang("Type your domain, such as hello.com: "), /([1-9]|[a-z]|-|\.)+/); 230 | if ((await $`dig +short -t A ${domain}`.text()).trim() != ip4 && (await $`dig +short -t AAAA ${domain}`.text()).trim() != ip6) { 231 | console.log(lang("Please resolve your domain to your server's IP")); 232 | process.exit(1); 233 | } 234 | var port = await lib.question(lang("Type a port, such as 9999: "), /\d+/); 235 | if ((await $`lsof -i:${port}`.text().catch((e) => "")).trim()) { 236 | console.log(lang("This port is occupied!")); 237 | process.exit(1); 238 | } 239 | var password = await lib.question(lang("Type a password, such as mypassword: "), /.+/); 240 | await $`joker brook quicserver --domainaddress ${domain}:${port} --password "${password}" --withoutBrookProtocol` 241 | await Bun.sleep(2000); 242 | await $`joker list` 243 | await $`joker log $(joker last)` 244 | await $`brook link -s quic://${joinhostport(domain, port)} -p "${password}" --udpoverstream --withoutBrookProtocol` 245 | console.log(`${lang("Tip: if there is a firewall, remember to open TCP 80 and")} ${port}`); 246 | }, 247 | }, 248 | { 249 | anwser: lang("I want to run brook socks5 without username and password"), 250 | action: async () => { 251 | var ip = ip4 ? ip4 : ip6; 252 | var port = await lib.question(lang("Type a port, such as 9999: "), /\d+/); 253 | if ((await $`lsof -i:${port}`.text().catch((e) => "")).trim()) { 254 | console.log(lang("This port is occupied!")); 255 | process.exit(1); 256 | } 257 | await $`joker brook socks5 -listen :${port} --socks5ServerIP ${ip}` 258 | await Bun.sleep(2000); 259 | await $`joker list` 260 | await $`joker log $(joker last)` 261 | await $`brook link -s socks5://${joinhostport(ip, port)}` 262 | console.log(`${lang("Tip: if there is a firewall, remember to open TCP and UDP")} ${port}`); 263 | }, 264 | }, 265 | { 266 | anwser: lang("I want to run brook socks5 with username and password"), 267 | action: async () => { 268 | var ip = ip4 ? ip4 : ip6; 269 | var port = await lib.question(lang("Type a port, such as 9999: "), /\d+/); 270 | if ((await $`lsof -i:${port}`.text().catch((e) => "")).trim()) { 271 | console.log(lang("This port is occupied!")); 272 | process.exit(1); 273 | } 274 | var username = await lib.question(lang("Type a username, such as myusername: "), /.+/); 275 | var password = await lib.question(lang("Type a password, such as mypassword: "), /.+/); 276 | await $`joker brook socks5 -listen :${port} --socks5ServerIP ${ip} --username "${username}" --password "${password}"` 277 | await Bun.sleep(2000); 278 | await $`joker list` 279 | await $`joker log $(joker last)` 280 | await $`brook link -s socks5://${joinhostport(ip, port)} --username "${username}" --password "${password}"` 281 | console.log(`${lang("Tip: if there is a firewall, remember to open TCP and UDP")} ${port}`); 282 | }, 283 | }, 284 | ]); 285 | }, 286 | }, 287 | { 288 | anwser: lang("I want to see running brook command"), 289 | action: async () => { 290 | await $`joker list` 291 | }, 292 | }, 293 | { 294 | anwser: lang("I want to stop a brook command"), 295 | action: async () => { 296 | await $`joker list` 297 | var id = await lib.question(lang("Choose a PID your want to stop: "), /\d+/); 298 | await $`joker stop ${id}` 299 | }, 300 | }, 301 | ]); 302 | }; 303 | 304 | await lib.select("Language: ", [ 305 | { 306 | anwser: "English", 307 | action: async () => { 308 | language = "en"; 309 | await letsgo(); 310 | }, 311 | }, 312 | { 313 | anwser: "Chinese", 314 | action: async () => { 315 | language = "zh"; 316 | await letsgo(); 317 | }, 318 | }, 319 | ]); 320 | -------------------------------------------------------------------------------- /brook.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$(uname -s)" != "Linux" ];then 4 | echo "This script only support Linux"; 5 | exit; 6 | fi 7 | 8 | restartsh="" 9 | if [ ! -f "$HOME/.nami/bin/nami" ] || [ ! -f "$HOME/.nami/bin/joker" ] || [ ! -f "$HOME/.nami/bin/brook" ] || [ ! -f "$HOME/.nami/bin/7z" ] || [ ! -f "$HOME/.nami/bin/bun" ] || [ ! -f "$HOME/.nami/bin/bunu" ] || [ `echo $PATH | grep $HOME/.nami/bin | wc -l` -eq 0 ];then 10 | mkdir -p $HOME/.nami/bin 11 | curl -L -o $HOME/.nami/bin/nami "https://github.com/txthinking/nami/releases/latest/download/nami_linux_amd64" 12 | chmod +x $HOME/.nami/bin/nami 13 | echo 'export PATH=$HOME/.nami/bin:$PATH' >> $HOME/.bashrc 14 | echo 'export PATH=$HOME/.nami/bin:$PATH' >> $HOME/.bash_profile 15 | echo 'export PATH=$HOME/.nami/bin:$PATH' >> $HOME/.zshenv 16 | export PATH=$HOME/.nami/bin:$PATH 17 | nami install joker 18 | nami install brook 19 | nami install 7z 20 | nami install bun 21 | nami install bun.plus 22 | restartsh="todo" 23 | fi 24 | 25 | bunu https://bash.ooo/brook.js 26 | 27 | if [ "$restartsh" = "todo" ];then 28 | exec -l $SHELL 29 | fi 30 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $# -ne 1 ]; then 4 | echo "./build.sh version" 5 | exit 6 | fi 7 | 8 | mkdir _ 9 | 10 | deno compile -A -r --unstable --target x86_64-unknown-linux-gnu -o _/brookscript_linux_amd64 https://raw.githubusercontent.com/txthinking/bash/master/brook.js 11 | 12 | nami release github.com/txthinking/bash $1 _ 13 | 14 | rm -rf _ 15 | -------------------------------------------------------------------------------- /bundle.js: -------------------------------------------------------------------------------- 1 | import * as fs from 'node:fs/promises' 2 | import path from "node:path"; 3 | 4 | if (process.argv.length < 4) { 5 | console.log("$ bunu https://bash.ooo/bundle.js /path/to/directory bundled.js") 6 | console.log("```") 7 | console.log("import readfile from './bundled.js'") 8 | console.log("new TextDecoder().decode(readfile('public/to/file'))") 9 | console.log("```") 10 | process.exit(1) 11 | } 12 | 13 | var dir = process.argv[2].endsWith("/") ? process.argv[2] : process.argv[2] + '/' 14 | var output = process.argv[3] 15 | 16 | var f = await fs.open(output, 'w+'); 17 | await fs.write(f.fd, new TextEncoder().encode(`var m = {};\n`)); 18 | var l = await fs.readdir(dir, { recursive: true, withFileTypes: true }) 19 | for (var v of l) { 20 | if (v.isFile()) { 21 | var s = v.path + "/" + v.name 22 | var k = s.replace(dir, path.basename(dir) + "/") 23 | await fs.write(f.fd, new TextEncoder().encode(`m["${k}"] = new Uint8Array([`)); 24 | var b = new Uint8Array(await Bun.file(s).arrayBuffer()); 25 | for (var j = 0; j < b.length; j++) { 26 | await fs.write(f.fd, new TextEncoder().encode(`${b[j]},`)); 27 | } 28 | await fs.write(f.fd, new TextEncoder().encode(`]);\n`)); 29 | } 30 | } 31 | await fs.write(f.fd, new TextEncoder().encode(`export default function(k){if(m[k]) return m[k]; throw 'NotFound';}\n`)); 32 | await fs.close(f.fd) 33 | -------------------------------------------------------------------------------- /china.js: -------------------------------------------------------------------------------- 1 | import os from 'node:os'; 2 | import lib from 'https://bash.ooo/lib.js'; 3 | import { $ } from 'bun'; 4 | import { Database } from "bun:sqlite"; 5 | import * as fs from 'node:fs/promises'; 6 | import path from 'node:path' 7 | 8 | const { program } = require('commander') 9 | program 10 | .name('bunu https://bash.ooo/china.js') 11 | .description('https://www.txthinking.com/talks/articles/china-list.article') 12 | .option('--source ', "gui: 自动查找 GUI 日志; /path/to/log: 服务端或客户端日志路径。[与 --how 一起使用]", '') 13 | .option('--how ', 'A: 从海外 IP 向海外 DNS 发起查询, 比如开启 GUI 的情况下或在服务器端运行, 缺点是如果域名同时有国内和海外 IP 则会被认为是海外域名; B: 从国内 IP 向阿里 DNS 发起查询, 比如在本地运行, 开启 GUI 情况下也没事,GUI 默认 bypass 了阿里 DNS, 缺点是如果返回的污染 IP 是国内的 IP 就会错乱,但历史经验不会, 还有一个缺点是 Google 有一些域名有国内的 IP。[与 --source 一起使用]', '') 14 | .option('--table', '打印整个表。[独立使用]', false) 15 | .option('--china ', '弥补 A 和 B 方案的不足,手动调整某个域名为国内域名。[独立使用]', '') 16 | .option('--global ', '弥补 A 和 B 方案的不足,手动调整某个域名为国际域名。[独立使用]', '') 17 | .option('--delete ', '移除某个域名. 如果想删除所有, 直接删除 rm -rf ~/.china.db。[独立使用]', '') 18 | .option('--modulea', '生成 module, 让中国域名走 bypass DNS 来解析出 A 记录,然后直接 bypass。[独立使用]', false) 19 | program.parse(); 20 | const options = program.opts(); 21 | 22 | if (!options.china && !options.global && !options.delete && !options.table && !options.modulea && (!options.source || !options.how)) { 23 | program.help() 24 | } 25 | 26 | var db = new Database(os.homedir() + "/.china.db", { create: true }); 27 | var l = db.query(`SELECT name FROM sqlite_master WHERE type='table'`).all(); 28 | if (!l.find(v => v.name == 'cn')) { 29 | db.query(` 30 | create table cn( 31 | id INTEGER PRIMARY KEY AUTOINCREMENT, 32 | domain text not null UNIQUE, 33 | iscn INTEGER not null default 0 34 | ) 35 | `).run(); 36 | // 内置部分主流域名 37 | db.query('insert into cn(domain, iscn) values(?, ?)').run('cn', 1) 38 | db.query('insert into cn(domain, iscn) values(?, ?)').run("wxcloudrun.com", 1) 39 | db.query('insert into cn(domain, iscn) values(?, ?)').run("alibabadns.com", 1) 40 | db.query('insert into cn(domain, iscn) values(?, ?)').run("10010.com", 1) 41 | db.query('insert into cn(domain, iscn) values(?, ?)').run("115.com", 1) 42 | db.query('insert into cn(domain, iscn) values(?, ?)').run("126.net", 1) 43 | db.query('insert into cn(domain, iscn) values(?, ?)').run("127.net", 1) 44 | db.query('insert into cn(domain, iscn) values(?, ?)').run("163.com", 1) 45 | db.query('insert into cn(domain, iscn) values(?, ?)').run("163jiasu.com", 1) 46 | db.query('insert into cn(domain, iscn) values(?, ?)').run("163yun.com", 1) 47 | db.query('insert into cn(domain, iscn) values(?, ?)').run("1905.com", 1) 48 | db.query('insert into cn(domain, iscn) values(?, ?)').run("21cn.com", 1) 49 | db.query('insert into cn(domain, iscn) values(?, ?)').run("300hu.com", 1) 50 | db.query('insert into cn(domain, iscn) values(?, ?)').run("321fenx.com", 1) 51 | db.query('insert into cn(domain, iscn) values(?, ?)').run("360buyimg.com", 1) 52 | db.query('insert into cn(domain, iscn) values(?, ?)').run("365dmp.com", 1) 53 | db.query('insert into cn(domain, iscn) values(?, ?)').run("71edge.com", 1) 54 | db.query('insert into cn(domain, iscn) values(?, ?)').run("95516.com", 1) 55 | db.query('insert into cn(domain, iscn) values(?, ?)').run("adkwai.com", 1) 56 | db.query('insert into cn(domain, iscn) values(?, ?)').run("adukwai.com", 1) 57 | db.query('insert into cn(domain, iscn) values(?, ?)').run("aggrx.com", 1) 58 | db.query('insert into cn(domain, iscn) values(?, ?)').run("ali-health.com", 1) 59 | db.query('insert into cn(domain, iscn) values(?, ?)').run("aliapp.org", 1) 60 | db.query('insert into cn(domain, iscn) values(?, ?)').run("alibaba-inc.com", 1) 61 | db.query('insert into cn(domain, iscn) values(?, ?)').run("alibaba.com", 1) 62 | db.query('insert into cn(domain, iscn) values(?, ?)').run("alibabausercontent.com", 1) 63 | db.query('insert into cn(domain, iscn) values(?, ?)').run("alicdn.com", 1) 64 | db.query('insert into cn(domain, iscn) values(?, ?)').run("alipay.com", 1) 65 | db.query('insert into cn(domain, iscn) values(?, ?)').run("alipayobjects.com", 1) 66 | db.query('insert into cn(domain, iscn) values(?, ?)').run("aliyun.com", 1) 67 | db.query('insert into cn(domain, iscn) values(?, ?)').run("aliyuncs.com", 1) 68 | db.query('insert into cn(domain, iscn) values(?, ?)').run("amap.com", 1) 69 | db.query('insert into cn(domain, iscn) values(?, ?)').run("amemv.com", 1) 70 | db.query('insert into cn(domain, iscn) values(?, ?)').run("baidu.com", 1) 71 | db.query('insert into cn(domain, iscn) values(?, ?)').run("baidubce.com", 1) 72 | db.query('insert into cn(domain, iscn) values(?, ?)').run("baidupcs.com", 1) 73 | db.query('insert into cn(domain, iscn) values(?, ?)').run("baidustatic.com", 1) 74 | db.query('insert into cn(domain, iscn) values(?, ?)').run("baifubao.com", 1) 75 | db.query('insert into cn(domain, iscn) values(?, ?)').run("baishan.com", 1) 76 | db.query('insert into cn(domain, iscn) values(?, ?)').run("baizhanlive.com", 1) 77 | db.query('insert into cn(domain, iscn) values(?, ?)').run("bcebos.com", 1) 78 | db.query('insert into cn(domain, iscn) values(?, ?)').run("bcelive.com", 1) 79 | db.query('insert into cn(domain, iscn) values(?, ?)').run("bdimg.com", 1) 80 | db.query('insert into cn(domain, iscn) values(?, ?)').run("bdstatic.com", 1) 81 | db.query('insert into cn(domain, iscn) values(?, ?)').run("bdurl.net", 1) 82 | db.query('insert into cn(domain, iscn) values(?, ?)').run("bdxiguastatic.com", 1) 83 | db.query('insert into cn(domain, iscn) values(?, ?)').run("bdxiguavod.com", 1) 84 | db.query('insert into cn(domain, iscn) values(?, ?)').run("bigda.com", 1) 85 | db.query('insert into cn(domain, iscn) values(?, ?)').run("biliapi.com", 1) 86 | db.query('insert into cn(domain, iscn) values(?, ?)').run("biliapi.net", 1) 87 | db.query('insert into cn(domain, iscn) values(?, ?)').run("bilibili.com", 1) 88 | db.query('insert into cn(domain, iscn) values(?, ?)').run("biligame.com", 1) 89 | db.query('insert into cn(domain, iscn) values(?, ?)').run("biligame.net", 1) 90 | db.query('insert into cn(domain, iscn) values(?, ?)').run("bilivideo.com", 1) 91 | db.query('insert into cn(domain, iscn) values(?, ?)').run("bjshcw.com", 1) 92 | db.query('insert into cn(domain, iscn) values(?, ?)').run("bosszhipin.com", 1) 93 | db.query('insert into cn(domain, iscn) values(?, ?)').run("bytedance.com", 1) 94 | db.query('insert into cn(domain, iscn) values(?, ?)').run("byteeffecttos.com", 1) 95 | db.query('insert into cn(domain, iscn) values(?, ?)').run("bytegecko.com", 1) 96 | db.query('insert into cn(domain, iscn) values(?, ?)').run("bytegoofy.com", 1) 97 | db.query('insert into cn(domain, iscn) values(?, ?)').run("byteimg.com", 1) 98 | db.query('insert into cn(domain, iscn) values(?, ?)').run("bytemaimg.com", 1) 99 | db.query('insert into cn(domain, iscn) values(?, ?)').run("bytemastatic.com", 1) 100 | db.query('insert into cn(domain, iscn) values(?, ?)').run("bytescm.com", 1) 101 | db.query('insert into cn(domain, iscn) values(?, ?)').run("bytetos.com", 1) 102 | db.query('insert into cn(domain, iscn) values(?, ?)').run("c-ctrip.com", 1) 103 | db.query('insert into cn(domain, iscn) values(?, ?)').run("calorietech.com", 1) 104 | db.query('insert into cn(domain, iscn) values(?, ?)').run("cdnhwc2.com", 1) 105 | db.query('insert into cn(domain, iscn) values(?, ?)').run("cdntips.net", 1) 106 | db.query('insert into cn(domain, iscn) values(?, ?)').run("chinanetcenter.com", 1) 107 | db.query('insert into cn(domain, iscn) values(?, ?)').run("cibntv.net", 1) 108 | db.query('insert into cn(domain, iscn) values(?, ?)').run("cl2009.com", 1) 109 | db.query('insert into cn(domain, iscn) values(?, ?)').run("cmbchina.com", 1) 110 | db.query('insert into cn(domain, iscn) values(?, ?)').run("cmbimg.com", 1) 111 | db.query('insert into cn(domain, iscn) values(?, ?)').run("cmpassport.com", 1) 112 | db.query('insert into cn(domain, iscn) values(?, ?)').run("cnzz.com", 1) 113 | db.query('insert into cn(domain, iscn) values(?, ?)').run("cpatrk.net", 1) 114 | db.query('insert into cn(domain, iscn) values(?, ?)').run("ctfile.com", 1) 115 | db.query('insert into cn(domain, iscn) values(?, ?)').run("ctobsnssdk.com", 1) 116 | db.query('insert into cn(domain, iscn) values(?, ?)').run("dbankcloud.com", 1) 117 | db.query('insert into cn(domain, iscn) values(?, ?)').run("dewu.com", 1) 118 | db.query('insert into cn(domain, iscn) values(?, ?)').run("dewucdn.com", 1) 119 | db.query('insert into cn(domain, iscn) values(?, ?)').run("dianping.com", 1) 120 | db.query('insert into cn(domain, iscn) values(?, ?)').run("douyincdn.com", 1) 121 | db.query('insert into cn(domain, iscn) values(?, ?)').run("douyinliving.com", 1) 122 | db.query('insert into cn(domain, iscn) values(?, ?)').run("douyinstatic.com", 1) 123 | db.query('insert into cn(domain, iscn) values(?, ?)').run("douyinvod.com", 1) 124 | db.query('insert into cn(domain, iscn) values(?, ?)').run("douyu.com", 1) 125 | db.query('insert into cn(domain, iscn) values(?, ?)').run("dpfile.com", 1) 126 | db.query('insert into cn(domain, iscn) values(?, ?)').run("dutils.com", 1) 127 | db.query('insert into cn(domain, iscn) values(?, ?)').run("duxiaoman.com", 1) 128 | db.query('insert into cn(domain, iscn) values(?, ?)').run("duxiaomanfintech.com", 1) 129 | db.query('insert into cn(domain, iscn) values(?, ?)').run("dxmpay.com", 1) 130 | db.query('insert into cn(domain, iscn) values(?, ?)').run("easytomessage.com", 1) 131 | db.query('insert into cn(domain, iscn) values(?, ?)').run("eckwai.com", 1) 132 | db.query('insert into cn(domain, iscn) values(?, ?)').run("ecukwai.com", 1) 133 | db.query('insert into cn(domain, iscn) values(?, ?)').run("effirst.com", 1) 134 | db.query('insert into cn(domain, iscn) values(?, ?)').run("etoote.com", 1) 135 | db.query('insert into cn(domain, iscn) values(?, ?)').run("fengkongcloud.com", 1) 136 | db.query('insert into cn(domain, iscn) values(?, ?)').run("fun.tv", 1) 137 | db.query('insert into cn(domain, iscn) values(?, ?)').run("funshion.com", 1) 138 | db.query('insert into cn(domain, iscn) values(?, ?)').run("funshion.net", 1) 139 | db.query('insert into cn(domain, iscn) values(?, ?)').run("gdtimg.com", 1) 140 | db.query('insert into cn(domain, iscn) values(?, ?)').run("geetest.com", 1) 141 | db.query('insert into cn(domain, iscn) values(?, ?)').run("gepush.com", 1) 142 | db.query('insert into cn(domain, iscn) values(?, ?)').run("getui.com", 1) 143 | db.query('insert into cn(domain, iscn) values(?, ?)').run("getui.net", 1) 144 | db.query('insert into cn(domain, iscn) values(?, ?)').run("gifshow.com", 1) 145 | db.query('insert into cn(domain, iscn) values(?, ?)').run("gotokeep.com", 1) 146 | db.query('insert into cn(domain, iscn) values(?, ?)').run("gridsumdissector.com", 1) 147 | db.query('insert into cn(domain, iscn) values(?, ?)').run("gtimg.com", 1) 148 | db.query('insert into cn(domain, iscn) values(?, ?)').run("hc-cdn.com", 1) 149 | db.query('insert into cn(domain, iscn) values(?, ?)').run("hdslb.com", 1) 150 | db.query('insert into cn(domain, iscn) values(?, ?)').run("hicloud.com", 1) 151 | db.query('insert into cn(domain, iscn) values(?, ?)').run("hitv.com", 1) 152 | db.query('insert into cn(domain, iscn) values(?, ?)').run("httpdns.pro", 1) 153 | db.query('insert into cn(domain, iscn) values(?, ?)').run("huanqiu.com", 1) 154 | db.query('insert into cn(domain, iscn) values(?, ?)').run("huaweicloud.com", 1) 155 | db.query('insert into cn(domain, iscn) values(?, ?)').run("hunantv.com", 1) 156 | db.query('insert into cn(domain, iscn) values(?, ?)').run("huoshan.com", 1) 157 | db.query('insert into cn(domain, iscn) values(?, ?)').run("huoshanlive.com", 1) 158 | db.query('insert into cn(domain, iscn) values(?, ?)').run("huoshanstatic.com", 1) 159 | db.query('insert into cn(domain, iscn) values(?, ?)').run("huoshanvod.com", 1) 160 | db.query('insert into cn(domain, iscn) values(?, ?)').run("id6.me", 1) 161 | db.query('insert into cn(domain, iscn) values(?, ?)').run("idqqimg.com", 1) 162 | db.query('insert into cn(domain, iscn) values(?, ?)').run("igexin.com", 1) 163 | db.query('insert into cn(domain, iscn) values(?, ?)').run("ihuoshanlive.com", 1) 164 | db.query('insert into cn(domain, iscn) values(?, ?)').run("imtmp.net", 1) 165 | db.query('insert into cn(domain, iscn) values(?, ?)').run("inkuai.com", 1) 166 | db.query('insert into cn(domain, iscn) values(?, ?)').run("ip6.arpa", 1) 167 | db.query('insert into cn(domain, iscn) values(?, ?)').run("ipaddr.host", 1) 168 | db.query('insert into cn(domain, iscn) values(?, ?)').run("ipv4only.arpa", 1) 169 | db.query('insert into cn(domain, iscn) values(?, ?)').run("iqiyi.com", 1) 170 | db.query('insert into cn(domain, iscn) values(?, ?)').run("iqiyipic.com", 1) 171 | db.query('insert into cn(domain, iscn) values(?, ?)').run("irs01.com", 1) 172 | db.query('insert into cn(domain, iscn) values(?, ?)').run("itoutiaostatic.com", 1) 173 | db.query('insert into cn(domain, iscn) values(?, ?)').run("ixigua.com", 1) 174 | db.query('insert into cn(domain, iscn) values(?, ?)').run("jd.com", 1) 175 | db.query('insert into cn(domain, iscn) values(?, ?)').run("jdcloud.com", 1) 176 | db.query('insert into cn(domain, iscn) values(?, ?)').run("jinhuahuolong.com", 1) 177 | db.query('insert into cn(domain, iscn) values(?, ?)').run("jomoxc.com", 1) 178 | db.query('insert into cn(domain, iscn) values(?, ?)').run("joying.com", 1) 179 | db.query('insert into cn(domain, iscn) values(?, ?)').run("jpush.io", 1) 180 | db.query('insert into cn(domain, iscn) values(?, ?)').run("keepcdn.com", 1) 181 | db.query('insert into cn(domain, iscn) values(?, ?)').run("ksapisrv.com", 1) 182 | db.query('insert into cn(domain, iscn) values(?, ?)').run("kskwai.com", 1) 183 | db.query('insert into cn(domain, iscn) values(?, ?)').run("ksord.com", 1) 184 | db.query('insert into cn(domain, iscn) values(?, ?)').run("ksosoft.com", 1) 185 | db.query('insert into cn(domain, iscn) values(?, ?)').run("kspkg.com", 1) 186 | db.query('insert into cn(domain, iscn) values(?, ?)').run("ksyun.com", 1) 187 | db.query('insert into cn(domain, iscn) values(?, ?)').run("ksyungslb.com", 1) 188 | db.query('insert into cn(domain, iscn) values(?, ?)').run("kuaishou.com", 1) 189 | db.query('insert into cn(domain, iscn) values(?, ?)').run("kuaishouzt.com", 1) 190 | db.query('insert into cn(domain, iscn) values(?, ?)').run("kuiniuca.com", 1) 191 | db.query('insert into cn(domain, iscn) values(?, ?)').run("kwai.com", 1) 192 | db.query('insert into cn(domain, iscn) values(?, ?)').run("kwaicdn.com", 1) 193 | db.query('insert into cn(domain, iscn) values(?, ?)').run("kwaizt.com", 1) 194 | db.query('insert into cn(domain, iscn) values(?, ?)').run("kwimgs.com", 1) 195 | db.query('insert into cn(domain, iscn) values(?, ?)').run("laiqukankan.com", 1) 196 | db.query('insert into cn(domain, iscn) values(?, ?)').run("le.com", 1) 197 | db.query('insert into cn(domain, iscn) values(?, ?)').run("letv.com", 1) 198 | db.query('insert into cn(domain, iscn) values(?, ?)').run("letvimg.com", 1) 199 | db.query('insert into cn(domain, iscn) values(?, ?)').run("leyingtt.com", 1) 200 | db.query('insert into cn(domain, iscn) values(?, ?)').run("lnk0.com", 1) 201 | db.query('insert into cn(domain, iscn) values(?, ?)').run("m1905.com", 1) 202 | db.query('insert into cn(domain, iscn) values(?, ?)').run("maoyan.com", 1) 203 | db.query('insert into cn(domain, iscn) values(?, ?)').run("meipai.com", 1) 204 | db.query('insert into cn(domain, iscn) values(?, ?)').run("meitu.com", 1) 205 | db.query('insert into cn(domain, iscn) values(?, ?)').run("meituan.com", 1) 206 | db.query('insert into cn(domain, iscn) values(?, ?)').run("meituan.net", 1) 207 | db.query('insert into cn(domain, iscn) values(?, ?)').run("meitudata.com", 1) 208 | db.query('insert into cn(domain, iscn) values(?, ?)').run("meitustat.com", 1) 209 | db.query('insert into cn(domain, iscn) values(?, ?)').run("meizu.com", 1) 210 | db.query('insert into cn(domain, iscn) values(?, ?)').run("mgtv.com", 1) 211 | db.query('insert into cn(domain, iscn) values(?, ?)').run("miaozhen.com", 1) 212 | db.query('insert into cn(domain, iscn) values(?, ?)').run("migucloud.com", 1) 213 | db.query('insert into cn(domain, iscn) values(?, ?)').run("miguvideo.com", 1) 214 | db.query('insert into cn(domain, iscn) values(?, ?)').run("mmstat.com", 1) 215 | db.query('insert into cn(domain, iscn) values(?, ?)').run("mob.com", 1) 216 | db.query('insert into cn(domain, iscn) values(?, ?)').run("myapp.com", 1) 217 | db.query('insert into cn(domain, iscn) values(?, ?)').run("myqcloud.com", 1) 218 | db.query('insert into cn(domain, iscn) values(?, ?)').run("myzhiniu.com", 1) 219 | db.query('insert into cn(domain, iscn) values(?, ?)').run("mzstatic.com", 1) 220 | db.query('insert into cn(domain, iscn) values(?, ?)').run("netease.com", 1) 221 | db.query('insert into cn(domain, iscn) values(?, ?)').run("netease.im", 1) 222 | db.query('insert into cn(domain, iscn) values(?, ?)').run("nintyinc.com", 1) 223 | db.query('insert into cn(domain, iscn) values(?, ?)').run("novelfm.com", 1) 224 | db.query('insert into cn(domain, iscn) values(?, ?)').run("novelfmstatic.com", 1) 225 | db.query('insert into cn(domain, iscn) values(?, ?)').run("onethingpcs.com", 1) 226 | db.query('insert into cn(domain, iscn) values(?, ?)').run("onewsvod.com", 1) 227 | db.query('insert into cn(domain, iscn) values(?, ?)').run("oskwai.com", 1) 228 | db.query('insert into cn(domain, iscn) values(?, ?)').run("pangolin-dsp-toutiao.com", 1) 229 | db.query('insert into cn(domain, iscn) values(?, ?)').run("pangolin-sdk-toutiao-b.com", 1) 230 | db.query('insert into cn(domain, iscn) values(?, ?)').run("pangolin-sdk-toutiao.com", 1) 231 | db.query('insert into cn(domain, iscn) values(?, ?)').run("pddpic.com", 1) 232 | db.query('insert into cn(domain, iscn) values(?, ?)').run("pddugc.com", 1) 233 | db.query('insert into cn(domain, iscn) values(?, ?)').run("pglstatp-toutiao.com", 1) 234 | db.query('insert into cn(domain, iscn) values(?, ?)').run("pinduoduo.com", 1) 235 | db.query('insert into cn(domain, iscn) values(?, ?)').run("pinduoduo.net", 1) 236 | db.query('insert into cn(domain, iscn) values(?, ?)').run("poizon.com", 1) 237 | db.query('insert into cn(domain, iscn) values(?, ?)').run("ppsimg.com", 1) 238 | db.query('insert into cn(domain, iscn) values(?, ?)').run("pstatp.com", 1) 239 | db.query('insert into cn(domain, iscn) values(?, ?)').run("qcloud.com", 1) 240 | db.query('insert into cn(domain, iscn) values(?, ?)').run("qingting.fm", 1) 241 | db.query('insert into cn(domain, iscn) values(?, ?)').run("qiniup.com", 1) 242 | db.query('insert into cn(domain, iscn) values(?, ?)').run("qiyi.com", 1) 243 | db.query('insert into cn(domain, iscn) values(?, ?)').run("qiyukf.com", 1) 244 | db.query('insert into cn(domain, iscn) values(?, ?)').run("qmail.com", 1) 245 | db.query('insert into cn(domain, iscn) values(?, ?)').run("qnqcdn.net", 1) 246 | db.query('insert into cn(domain, iscn) values(?, ?)').run("qq.com", 1) 247 | db.query('insert into cn(domain, iscn) values(?, ?)').run("qqmail.com", 1) 248 | db.query('insert into cn(domain, iscn) values(?, ?)').run("qy.net", 1) 249 | db.query('insert into cn(domain, iscn) values(?, ?)').run("rr.tv", 1) 250 | db.query('insert into cn(domain, iscn) values(?, ?)').run("sankuai.com", 1) 251 | db.query('insert into cn(domain, iscn) values(?, ?)').run("servicewechat.com", 1) 252 | db.query('insert into cn(domain, iscn) values(?, ?)').run("shuqireader.com", 1) 253 | db.query('insert into cn(domain, iscn) values(?, ?)').run("smtcdns.net", 1) 254 | db.query('insert into cn(domain, iscn) values(?, ?)').run("snssdk.com", 1) 255 | db.query('insert into cn(domain, iscn) values(?, ?)').run("sohu.com", 1) 256 | db.query('insert into cn(domain, iscn) values(?, ?)').run("sohucs.com", 1) 257 | db.query('insert into cn(domain, iscn) values(?, ?)').run("szbdyd.com", 1) 258 | db.query('insert into cn(domain, iscn) values(?, ?)').run("tamaegis.com", 1) 259 | db.query('insert into cn(domain, iscn) values(?, ?)').run("tanx.com", 1) 260 | db.query('insert into cn(domain, iscn) values(?, ?)').run("taobao.com", 1) 261 | db.query('insert into cn(domain, iscn) values(?, ?)').run("tdatamaster.com", 1) 262 | db.query('insert into cn(domain, iscn) values(?, ?)').run("tencent-cloud.com", 1) 263 | db.query('insert into cn(domain, iscn) values(?, ?)').run("tencent-cloud.net", 1) 264 | db.query('insert into cn(domain, iscn) values(?, ?)').run("tencent.com", 1) 265 | db.query('insert into cn(domain, iscn) values(?, ?)').run("tencentmusic.com", 1) 266 | db.query('insert into cn(domain, iscn) values(?, ?)').run("tenpay.com", 1) 267 | db.query('insert into cn(domain, iscn) values(?, ?)').run("tfogc.com", 1) 268 | db.query('insert into cn(domain, iscn) values(?, ?)').run("tingyun.com", 1) 269 | db.query('insert into cn(domain, iscn) values(?, ?)').run("tmall.com", 1) 270 | db.query('insert into cn(domain, iscn) values(?, ?)').run("toutiao.com", 1) 271 | db.query('insert into cn(domain, iscn) values(?, ?)').run("toutiaoapi.com", 1) 272 | db.query('insert into cn(domain, iscn) values(?, ?)').run("toutiaostatic.com", 1) 273 | db.query('insert into cn(domain, iscn) values(?, ?)').run("toutiaovod.com", 1) 274 | db.query('insert into cn(domain, iscn) values(?, ?)').run("tudou.com", 1) 275 | db.query('insert into cn(domain, iscn) values(?, ?)').run("tv002.com", 1) 276 | db.query('insert into cn(domain, iscn) values(?, ?)').run("ucweb.com", 1) 277 | db.query('insert into cn(domain, iscn) values(?, ?)').run("ugdtimg.com", 1) 278 | db.query('insert into cn(domain, iscn) values(?, ?)').run("ulikecam.com", 1) 279 | db.query('insert into cn(domain, iscn) values(?, ?)').run("umeng.com", 1) 280 | db.query('insert into cn(domain, iscn) values(?, ?)').run("umengcloud.com", 1) 281 | db.query('insert into cn(domain, iscn) values(?, ?)').run("umsns.com", 1) 282 | db.query('insert into cn(domain, iscn) values(?, ?)').run("unionpay.com", 1) 283 | db.query('insert into cn(domain, iscn) values(?, ?)').run("upqzfile.com", 1) 284 | db.query('insert into cn(domain, iscn) values(?, ?)').run("vemarsdev.com", 1) 285 | db.query('insert into cn(domain, iscn) values(?, ?)').run("vemarsstatic.com", 1) 286 | db.query('insert into cn(domain, iscn) values(?, ?)').run("vlabvod.com", 1) 287 | db.query('insert into cn(domain, iscn) values(?, ?)').run("volceapplog.com", 1) 288 | db.query('insert into cn(domain, iscn) values(?, ?)').run("volces.com", 1) 289 | db.query('insert into cn(domain, iscn) values(?, ?)').run("vzuu.com", 1) 290 | db.query('insert into cn(domain, iscn) values(?, ?)').run("wanzjhb.com", 1) 291 | db.query('insert into cn(domain, iscn) values(?, ?)').run("weibo.com", 1) 292 | db.query('insert into cn(domain, iscn) values(?, ?)').run("weibocdn.com", 1) 293 | db.query('insert into cn(domain, iscn) values(?, ?)').run("weiyun.com", 1) 294 | db.query('insert into cn(domain, iscn) values(?, ?)').run("wnsqzonebk.com", 1) 295 | db.query('insert into cn(domain, iscn) values(?, ?)').run("xhscdn.com", 1) 296 | db.query('insert into cn(domain, iscn) values(?, ?)').run("xiaodutv.com", 1) 297 | db.query('insert into cn(domain, iscn) values(?, ?)').run("xiaohongshu.com", 1) 298 | db.query('insert into cn(domain, iscn) values(?, ?)').run("xiaomi.com", 1) 299 | db.query('insert into cn(domain, iscn) values(?, ?)').run("xiaomi.net", 1) 300 | db.query('insert into cn(domain, iscn) values(?, ?)').run("ximalaya.com", 1) 301 | db.query('insert into cn(domain, iscn) values(?, ?)').run("xiuxiustatic.com", 1) 302 | db.query('insert into cn(domain, iscn) values(?, ?)').run("xmcdn.com", 1) 303 | db.query('insert into cn(domain, iscn) values(?, ?)').run("xxpkg.com", 1) 304 | db.query('insert into cn(domain, iscn) values(?, ?)').run("xycdn.com", 1) 305 | db.query('insert into cn(domain, iscn) values(?, ?)').run("yangkeduo.com", 1) 306 | db.query('insert into cn(domain, iscn) values(?, ?)').run("ykimg.com", 1) 307 | db.query('insert into cn(domain, iscn) values(?, ?)').run("youku.com", 1) 308 | db.query('insert into cn(domain, iscn) values(?, ?)').run("yqkk.link", 1) 309 | db.query('insert into cn(domain, iscn) values(?, ?)').run("yximgs.com", 1) 310 | db.query('insert into cn(domain, iscn) values(?, ?)').run("yy.com", 1) 311 | db.query('insert into cn(domain, iscn) values(?, ?)').run("yystatic.com", 1) 312 | db.query('insert into cn(domain, iscn) values(?, ?)').run("zhihu.com", 1) 313 | db.query('insert into cn(domain, iscn) values(?, ?)').run("zhimg.com", 1) 314 | db.query('insert into cn(domain, iscn) values(?, ?)').run("zhipin.com", 1) 315 | db.query('insert into cn(domain, iscn) values(?, ?)').run("zhuanzfx.com", 1) 316 | db.query('insert into cn(domain, iscn) values(?, ?)').run("zijieapi.com", 1) 317 | } 318 | 319 | if (options.china) { 320 | var l1 = options.china.split('.') 321 | var a = l1.pop() 322 | var b = l1.pop() 323 | var d = b + '.' + a 324 | var r = db.query('select * from cn where domain=?').get(d); 325 | if (r) { 326 | db.query('update cn set iscn=1 where domain=?').run(d); 327 | } else { 328 | db.query('insert into cn(domain, iscn) values(?, ?)').run(d, 1) 329 | } 330 | process.exit() 331 | } 332 | 333 | if (options.global) { 334 | var l1 = options.global.split('.') 335 | var a = l1.pop() 336 | var b = l1.pop() 337 | var d = b + '.' + a 338 | var r = db.query('select * from cn where domain=?').get(d); 339 | if (r) { 340 | db.query('update cn set iscn=2 where domain=?').run(d); 341 | } else { 342 | db.query('insert into cn(domain, iscn) values(?, ?)').run(d, 2) 343 | } 344 | process.exit() 345 | } 346 | 347 | if (options.delete) { 348 | db.query('delete from cn where domain=?').run(options.delete); 349 | process.exit() 350 | } 351 | 352 | if (options.table) { 353 | var r = db.query('select * from cn').all(); 354 | r.sort((a, b) => a.domain > b.domain) 355 | const { printTable } = require('console-table-printer'); 356 | printTable(r) 357 | process.exit() 358 | } 359 | 360 | if (options.modulea) { 361 | var r = db.query('select * from cn where iscn=1').all(); 362 | var i = 0 363 | var s = "" 364 | var l = [] 365 | r.map(v => v.domain).forEach(v => { 366 | l.push(`"${v}": true,`) 367 | i++ 368 | if (i == 200) { 369 | s += ` 370 | l = { 371 | ${l.join("\n").slice(0, -1)} 372 | } 373 | r = f(hp.host, l) 374 | if r != undefined { 375 | return r 376 | } 377 | ` 378 | i = 0 379 | l = [] 380 | } 381 | }) 382 | if (l.length) { 383 | s += ` 384 | l = { 385 | ${l.join("\n").slice(0, -1)} 386 | } 387 | r = f(hp.host, l) 388 | if r != undefined { 389 | return r 390 | } 391 | ` 392 | } 393 | s = ` 394 | modules = append(modules, { 395 | address: func(m) { 396 | if m.domainaddress { 397 | brook := import("brook") 398 | hp := brook.splithostport(m.domainaddress) 399 | if is_error(hp) { 400 | return hp 401 | } 402 | text := import("text") 403 | f := func(domain, l){ 404 | ss := text.split(text.to_lower(domain), ".") 405 | s := "" 406 | for i := len(ss) - 1; i >= 0; i-- { 407 | if s == "" { 408 | s = ss[i] 409 | } else { 410 | s = ss[i] + "." + s 411 | } 412 | if l[s] { 413 | return { ipaddressfrombypassdns: "A", bypass: true } 414 | } 415 | } 416 | } 417 | l := undefined 418 | r := undefined 419 | ${s} 420 | } 421 | } 422 | }) 423 | ` 424 | console.log(s) 425 | process.exit() 426 | } 427 | 428 | function get_domain(addr) { 429 | if (addr.indexOf(':') == -1) { 430 | return addr 431 | } 432 | if (addr.startsWith('[')) { 433 | return 434 | } 435 | var i = addr.lastIndexOf(':') 436 | var s = addr.slice(0, i) 437 | if (/^[\d\.]+$/.test(s)) { 438 | return 439 | } 440 | return s 441 | } 442 | 443 | async function exists(file) { 444 | try { 445 | await fs.access(file, fs.constants.F_OK) 446 | return true 447 | } catch { 448 | return false 449 | } 450 | } 451 | 452 | async function get_todo() { 453 | var l = [] 454 | if (options.source == 'gui') { 455 | if (await exists(os.homedir() + "/Library/Group Containers/group.com.txthinking.brook.onemacos/b.log")) { 456 | var s = await fs.readFile(os.homedir() + "/Library/Group Containers/group.com.txthinking.brook.onemacos/b.log", { encoding: 'utf8' }) 457 | if (s && s.trim()) { 458 | l = l.concat(s.trim().split("\n").map(v => JSON.parse(v)).filter(v => v.action == "PROXY").map(v => get_domain(v.content)).filter(v => v)) 459 | } 460 | } 461 | if (await exists(os.homedir() + "/Library/Group Containers/group.com.txthinking.brookmacos/b.log")) { 462 | var s = await fs.readFile(os.homedir() + "/Library/Group Containers/group.com.txthinking.brookmacos/b.log", { encoding: 'utf8' }) 463 | if (s && s.trim()) { 464 | l = l.concat(s.trim().split("\n").map(v => JSON.parse(v)).filter(v => v.action == "PROXY").map(v => get_domain(v.content)).filter(v => v)) 465 | } 466 | } 467 | if (await exists(os.homedir() + "/.b.log")) { 468 | var s = await fs.readFile(os.homedir() + "/.b.log", { encoding: 'utf8' }) 469 | if (s && s.trim()) { 470 | l = l.concat(s.trim().split("\n").map(v => JSON.parse(v)).filter(v => v.action == "PROXY").map(v => get_domain(v.content)).filter(v => v)) 471 | } 472 | } 473 | if (await exists(path.join(process.env.AppData ?? '', ".b.log"))) { 474 | var s = await fs.readFile(path.join(process.env.AppData ?? '', ".b.log"), { encoding: 'utf8' }) 475 | if (s && s.trim()) { 476 | l = l.concat(s.trim().split("\n").map(v => JSON.parse(v)).filter(v => v.action == "PROXY").map(v => get_domain(v.content)).filter(v => v)) 477 | } 478 | } 479 | } else { 480 | var s = await fs.readFile(options.source, { encoding: 'utf8' }) 481 | if (s && s.trim()) { 482 | l = l.concat(s.trim().split("\n").map(v => JSON.parse(v)).filter(v => v.action == "PROXY").map(v => get_domain(v.content)).filter(v => v)) 483 | l = l.concat(s.trim().split("\n").map(v => JSON.parse(v)).filter(v => v.dst).map(v => get_domain(v.dst)).filter(v => v)) 484 | l = l.concat(s.trim().split("\n").map(v => JSON.parse(v)).filter(v => v.dns).map(v => get_domain(v.domain)).filter(v => v)) 485 | } 486 | } 487 | if (!l.length) { 488 | console.log('没有发现域名') 489 | process.exit(1) 490 | } 491 | return [...new Set(l)] 492 | } 493 | 494 | async function get_cn_domain_with_global_dns(domain) { 495 | var s = await $`brook dohclient -t A --short -d ${domain}`.text() 496 | if (s) { 497 | if ((await $`brook ipcountry --ip ${s.trim()}`.text()).trim() != 'CN') { 498 | return 499 | } 500 | s = await $`brook dohclient -t A -d ${domain}`.text() 501 | var l = s.trim().split('\n').map(v => v.split(/\s+/)).filter(v => v.length == 5 && v[3] == 'CNAME').map(v => v[4].slice(0, -1)) 502 | l.push(domain) 503 | return l.map(v => { 504 | var l1 = v.split('.') 505 | var a = l1.pop() 506 | var b = l1.pop() 507 | return b + '.' + a 508 | }) 509 | } 510 | var s = await $`brook dohclient -t AAAA --short -d ${domain}`.text() 511 | if (s) { 512 | if ((await $`brook ipcountry --ip ${s.trim()}`.text()).trim() != 'CN') { 513 | return 514 | } 515 | s = await $`brook dohclient -t AAAA -d ${domain}`.text() 516 | var l = s.trim().split('\n').map(v => v.split(/\s+/)).filter(v => v.length == 5 && v[3] == 'CNAME').map(v => v[4].slice(0, -1)) 517 | l.push(domain) 518 | return l.map(v => { 519 | var l1 = v.split('.') 520 | var a = l1.pop() 521 | var b = l1.pop() 522 | return b + '.' + a 523 | }) 524 | } 525 | throw `unknown ${domain}` 526 | } 527 | 528 | async function get_cn_domain_with_china_dns(domain) { 529 | var s = await $`brook dohclient -s 'https://dns.alidns.com/dns-query?address=223.5.5.5:443' -t A --short -d ${domain}`.text() 530 | if (s) { 531 | if ((await $`brook ipcountry --ip ${s.trim()}`.text()).trim() != 'CN') { 532 | return 533 | } 534 | s = await $`brook dohclient -s 'https://dns.alidns.com/dns-query?address=223.5.5.5:443' -t A -d ${domain}`.text() 535 | var l = s.trim().split('\n').map(v => v.split(/\s+/)).filter(v => v.length == 5 && v[3] == 'CNAME').map(v => v[4].slice(0, -1)) 536 | l.push(domain) 537 | return l.map(v => { 538 | var l1 = v.split('.') 539 | var a = l1.pop() 540 | var b = l1.pop() 541 | return b + '.' + a 542 | }) 543 | } 544 | var s = await $`brook dohclient -s 'https://dns.alidns.com/dns-query?address=223.5.5.5:443' -t AAAA --short -d ${domain}`.text() 545 | if (s) { 546 | if ((await $`brook ipcountry --ip ${s.trim()}`.text()).trim() != 'CN') { 547 | return 548 | } 549 | s = await $`brook dohclient -s 'https://dns.alidns.com/dns-query?address=223.5.5.5:443' -t AAAA -d ${domain}`.trim() 550 | var l = s.trim().split('\n').map(v => v.split(/\s+/)).filter(v => v.length == 5 && v[3] == 'CNAME').map(v => v[4].slice(0, -1)) 551 | l.push(domain) 552 | return l.map(v => { 553 | var l1 = v.split('.') 554 | var a = l1.pop() 555 | var b = l1.pop() 556 | return b + '.' + a 557 | }) 558 | } 559 | throw `unknown ${domain}` 560 | } 561 | 562 | var l = await get_todo() 563 | console.log('todo', l.length) 564 | for (var i = 0; i < l.length; i++) { 565 | var d = l[i].toLowerCase() 566 | if (d.endsWith('.cn')) { 567 | continue 568 | } 569 | var l1 = d.split('.') 570 | var a = l1.pop() 571 | var b = l1.pop() 572 | var s0 = b + '.' + a 573 | var r = db.query('select * from cn where domain=? or domain like ?').get(s0, "%." + s0); 574 | if (r) { 575 | continue 576 | } 577 | try { 578 | var l1 = await lib.retry(async () => options.how == 'A' ? await get_cn_domain_with_global_dns(d) : await get_cn_domain_with_china_dns(d), 1000, 2) 579 | if (l1) { 580 | l1.forEach(v => { 581 | var r = db.query('select * from cn where domain=?').get(v); 582 | if (r) { 583 | return 584 | } 585 | db.query('insert into cn(domain, iscn) values(?, ?)').run(v, 1) 586 | }) 587 | } else { 588 | db.query('insert into cn(domain, iscn) values(?, ?)').run(s0, 2) 589 | } 590 | console.log(`${parseInt((i + 1) / l.length * 100)}%`) 591 | } catch (e) { 592 | console.log(d, e.toString()) 593 | } 594 | } 595 | -------------------------------------------------------------------------------- /lib.js: -------------------------------------------------------------------------------- 1 | import { Database } from "bun:sqlite"; 2 | import * as fs from 'node:fs/promises'; 3 | import { $ } from "bun"; 4 | 5 | var db = (sdb) => { 6 | return { 7 | c: (table, o) => { 8 | var l = []; 9 | var l1 = []; 10 | var params = {}; 11 | for (var k in o) { 12 | if (k == 'id') { 13 | continue; 14 | } 15 | l.push(k); 16 | l1.push(`$${k}`); 17 | params[`$${k}`] = o[k]; 18 | } 19 | var r = sdb.query(`insert into ${table}(${l.join(', ')}) values(${l1.join(', ')})`).run(params); 20 | return sdb.query(`select * from ${table} where id=?`).get(r.lastInsertRowid); 21 | }, 22 | u: (table, o) => { 23 | if (!o.id) { 24 | throw new Error('u needs object has id'); 25 | } 26 | var l = []; 27 | var params = {}; 28 | for (var k in o) { 29 | if (k == 'id') { 30 | continue; 31 | } 32 | l.push(`${k}=$${k}`); 33 | params[`$${k}`] = o[k]; 34 | } 35 | params[`$id`] = o.id; 36 | sdb.query(`update ${table} set ${l.join(', ')} where id=$id`).run(params); 37 | return sdb.query(`select * from ${table} where id=?`).get(o.id); 38 | }, 39 | r: (table, id) => { 40 | return sdb.query(`select * from ${table} where id=?`).get(id) 41 | }, 42 | d: (table, id) => { 43 | sdb.query(`delete from ${table} where id=?`).run(id) 44 | }, 45 | query: (...args) => { 46 | return sdb.query(...args); 47 | }, 48 | transaction: (f) => { 49 | return sdb.transaction(f)(); 50 | }, 51 | close: () => { 52 | sdb.close(); 53 | }, 54 | }; 55 | }; 56 | 57 | export default { 58 | 59 | video_second: async function(vora) { 60 | var a = await $`ffprobe -i ${vora} -show_entries format=duration -v quiet -of csv="p=0"`.text() 61 | return parseInt(parseFloat(a)); 62 | }, 63 | 64 | second_to_clock: function(s) { 65 | var i = parseInt(s / 60 / 60); 66 | var h = i >= 10 ? `${i}` : `0${i}`; 67 | s -= i * 60 * 60; 68 | var i = parseInt(s / 60); 69 | var m = i >= 10 ? `${i}` : `0${i}`; 70 | s -= i * 60; 71 | var ss = s >= 10 ? `${s}` : `0${s}`; 72 | return `${h}:${m}:${ss}`; 73 | }, 74 | 75 | clock_to_second: function(s) { 76 | var l = s.split(":") 77 | return parseInt(l[0]) * 60 * 60 + parseInt(l[1]) * 60 + parseInt(l[2]) 78 | }, 79 | 80 | country_to_emoji: function (country_code) { 81 | if (country_code === "UK") { 82 | country_code = "GB"; 83 | } 84 | const flag = country_code.replace(/[A-Z]/g, (match) => { 85 | const cc = match.charCodeAt(0); 86 | return String.fromCodePoint(cc + 127397); 87 | }); 88 | return flag; 89 | }, 90 | 91 | file_exists: async function(s) { 92 | try { 93 | await fs.access(s, fs.constants.F_OK) 94 | return true 95 | } catch { 96 | return false 97 | } 98 | }, 99 | 100 | sqlite: function(path, options) { 101 | if (options) options = {} 102 | var s = new Database(path, { 103 | create: options.readonly ? false : true, 104 | readonly: options.readonly ? true : false, 105 | }); 106 | if (options.wal) { 107 | s.exec("PRAGMA journal_mode = WAL;"); 108 | } 109 | return db(s); 110 | }, 111 | 112 | migrate: function(sqlite) { 113 | var f = (id, sql) => { 114 | var r = sqlite.query(`select * from migration where id=?`).get(id); 115 | if (r) { 116 | return 117 | } 118 | sqlite.query(sql).run(); 119 | sqlite.query(`insert into migration(id) values(?)`).run(id); 120 | }; 121 | var l = sqlite.query(`SELECT name FROM sqlite_master WHERE type='table'`).all(); 122 | if (!l.find(v => v.name == 'migration')) { 123 | sqlite.query(` 124 | create table migration( 125 | id text not null UNIQUE 126 | ) 127 | `).run(); 128 | } 129 | return f; 130 | }, 131 | 132 | now: function() { 133 | return parseInt(Date.now() / 1000); 134 | }, 135 | 136 | question: async function(q, v) { 137 | if (!v) { 138 | return prompt(q); 139 | } 140 | for (; ;) { 141 | var s = prompt(q); 142 | if (!s) { 143 | continue; 144 | } 145 | if (typeof v === "function") { 146 | if (await v(s)) { 147 | return s; 148 | } 149 | continue; 150 | } 151 | if (v.test(s)) { 152 | return s; 153 | } 154 | } 155 | }, 156 | 157 | stdin: async function() { 158 | var reader = (await Bun.stdin.stream()).getReader(); 159 | var l = []; 160 | for (; true;) { 161 | var { done, value } = await reader.read(); 162 | if (value) { 163 | l.push(value); 164 | } 165 | if (done) { 166 | break; 167 | } 168 | } 169 | var b = new Uint8Array(l.reduce((v, a) => a.length + v, 0)); 170 | var i = 0; 171 | l.forEach(v => { 172 | b.set(v, i); 173 | i += v.length; 174 | }); 175 | return new TextDecoder().decode(b); 176 | }, 177 | 178 | retry: async function(f, delay, times) { 179 | var i = 0; 180 | for (; true; i++) { 181 | try { 182 | return await f(); 183 | } catch (e) { 184 | if (i < times) { 185 | if (delay) { 186 | await Bun.sleep(delay); 187 | } 188 | continue; 189 | } 190 | throw e; 191 | } 192 | } 193 | }, 194 | 195 | select: async function(question, anwseraction) { 196 | for (; ;) { 197 | var i = prompt(anwseraction.map((v, i) => `${i + 1}: ${v.anwser}`).join("\n") + `\n${question}`); 198 | i = parseInt(i); 199 | if (isNaN(i) || i < 1 || i > anwseraction.length) { 200 | continue; 201 | } 202 | break; 203 | } 204 | await anwseraction[i - 1].action(); 205 | }, 206 | 207 | go: function(js, args) { 208 | return new Promise((resolve, reject) => { 209 | var blob = new Blob( 210 | [ 211 | js, 212 | ], 213 | { 214 | type: "application/javascript", 215 | }, 216 | ); 217 | var url = URL.createObjectURL(blob); 218 | var worker = new Worker(url); 219 | worker.postMessage(args); 220 | worker.onmessage = event => { 221 | worker.terminate(); 222 | resolve(event.data) 223 | }; 224 | }); 225 | }, 226 | 227 | setImmediatePromise: function() { 228 | return new Promise((resolve) => { 229 | setImmediate(() => resolve()); 230 | }); 231 | }, 232 | 233 | // async lock, js lock, js mutex, js sync, js queue 234 | // var sync = new Sync(); 235 | // await sync.atomic(async () => {}); 236 | Sync: function() { 237 | var p = Promise.resolve(); 238 | this.atomic = (f) => { 239 | p = new Promise((resolve, reject) => { 240 | p.then(() => { 241 | f() 242 | .then((v) => resolve(v)) 243 | .catch((v) => reject(v)); 244 | }).catch((e) => { 245 | f() 246 | .then((v) => resolve(v)) 247 | .catch((v) => reject(v)); 248 | }); 249 | }); 250 | return p; 251 | }; 252 | }, 253 | 254 | read_url: async function(url) { 255 | var res = await fetch(url); 256 | if (!res.ok) { 257 | throw res.status + ": " + (await res.text()); 258 | } 259 | return await res.text(); 260 | }, 261 | 262 | } 263 | -------------------------------------------------------------------------------- /nami.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | os="" 4 | arch="" 5 | 6 | if [ $(uname -s) = "Darwin" ]; then 7 | os="darwin" 8 | fi 9 | if [ $(uname -s) = "Linux" ]; then 10 | os="linux" 11 | if [ `cat /etc/*elease 2>/dev/null | grep 'CentOS Linux 7' | wc -l` -eq 1 ]; then 12 | echo "Requires CentOS version >= 8" 13 | exit; 14 | fi 15 | fi 16 | if [ $(uname -s | grep "MINGW" | wc -l) -eq 1 ]; then 17 | os="windows" 18 | fi 19 | 20 | if [ $(uname -m) = "x86_64" ]; then 21 | arch="amd64" 22 | fi 23 | if [ $(uname -m) = "arm64" ]; then 24 | arch="arm64" 25 | fi 26 | if [ $(uname -m) = "aarch64" ]; then 27 | arch="arm64" 28 | fi 29 | 30 | if [ "$os" = "" -o "$arch" = "" ]; then 31 | echo "Nami does not support your OS/ARCH yet. Please submit issue or PR to https://github.com/txthinking/nami" 32 | exit 33 | fi 34 | 35 | sfx="" 36 | if [ $os = "windows" ]; then 37 | sfx=".exe" 38 | fi 39 | 40 | mkdir -p $HOME/.nami/bin 41 | curl -L -o $HOME/.nami/bin/nami$sfx "https://github.com/txthinking/nami/releases/latest/download/nami_${os}_${arch}$sfx" 42 | chmod +x $HOME/.nami/bin/nami 43 | echo 'export PATH=$HOME/.nami/bin:$PATH' >> $HOME/.bashrc 44 | echo 'export PATH=$HOME/.nami/bin:$PATH' >> $HOME/.bash_profile 45 | echo 'export PATH=$HOME/.nami/bin:$PATH' >> $HOME/.zshenv 46 | exec -l $SHELL 47 | -------------------------------------------------------------------------------- /static/m.css: -------------------------------------------------------------------------------- 1 | @media (prefers-color-scheme:dark){.markdown-body,[data-theme=dark]{color-scheme:dark;--color-prettylights-syntax-comment:#8b949e;--color-prettylights-syntax-constant:#79c0ff;--color-prettylights-syntax-entity:#d2a8ff;--color-prettylights-syntax-storage-modifier-import:#c9d1d9;--color-prettylights-syntax-entity-tag:#7ee787;--color-prettylights-syntax-keyword:#ff7b72;--color-prettylights-syntax-string:#a5d6ff;--color-prettylights-syntax-variable:#ffa657;--color-prettylights-syntax-brackethighlighter-unmatched:#f85149;--color-prettylights-syntax-invalid-illegal-text:#f0f6fc;--color-prettylights-syntax-invalid-illegal-bg:#8e1519;--color-prettylights-syntax-carriage-return-text:#f0f6fc;--color-prettylights-syntax-carriage-return-bg:#b62324;--color-prettylights-syntax-string-regexp:#7ee787;--color-prettylights-syntax-markup-list:#f2cc60;--color-prettylights-syntax-markup-heading:#1f6feb;--color-prettylights-syntax-markup-italic:#c9d1d9;--color-prettylights-syntax-markup-bold:#c9d1d9;--color-prettylights-syntax-markup-deleted-text:#ffdcd7;--color-prettylights-syntax-markup-deleted-bg:#67060c;--color-prettylights-syntax-markup-inserted-text:#aff5b4;--color-prettylights-syntax-markup-inserted-bg:#033a16;--color-prettylights-syntax-markup-changed-text:#ffdfb6;--color-prettylights-syntax-markup-changed-bg:#5a1e02;--color-prettylights-syntax-markup-ignored-text:#c9d1d9;--color-prettylights-syntax-markup-ignored-bg:#1158c7;--color-prettylights-syntax-meta-diff-range:#d2a8ff;--color-prettylights-syntax-brackethighlighter-angle:#8b949e;--color-prettylights-syntax-sublimelinter-gutter-mark:#484f58;--color-prettylights-syntax-constant-other-reference-link:#a5d6ff;--color-fg-default:#e6edf3;--color-fg-muted:#7d8590;--color-fg-subtle:#6e7681;--color-canvas-default:#0d1117;--color-canvas-subtle:#161b22;--color-border-default:#30363d;--color-border-muted:#21262d;--color-neutral-muted:rgba(110,118,129,0.4);--color-accent-fg:#2f81f7;--color-accent-emphasis:#1f6feb;--color-attention-fg:#d29922;--color-attention-subtle:rgba(187,128,9,0.15);--color-danger-fg:#f85149;--color-done-fg:#a371f7}}@media (prefers-color-scheme:light){.markdown-body,[data-theme=light]{color-scheme:light;--color-prettylights-syntax-comment:#6e7781;--color-prettylights-syntax-constant:#0550ae;--color-prettylights-syntax-entity:#6639ba;--color-prettylights-syntax-storage-modifier-import:#24292f;--color-prettylights-syntax-entity-tag:#116329;--color-prettylights-syntax-keyword:#cf222e;--color-prettylights-syntax-string:#0a3069;--color-prettylights-syntax-variable:#953800;--color-prettylights-syntax-brackethighlighter-unmatched:#82071e;--color-prettylights-syntax-invalid-illegal-text:#f6f8fa;--color-prettylights-syntax-invalid-illegal-bg:#82071e;--color-prettylights-syntax-carriage-return-text:#f6f8fa;--color-prettylights-syntax-carriage-return-bg:#cf222e;--color-prettylights-syntax-string-regexp:#116329;--color-prettylights-syntax-markup-list:#3b2300;--color-prettylights-syntax-markup-heading:#0550ae;--color-prettylights-syntax-markup-italic:#24292f;--color-prettylights-syntax-markup-bold:#24292f;--color-prettylights-syntax-markup-deleted-text:#82071e;--color-prettylights-syntax-markup-deleted-bg:#ffebe9;--color-prettylights-syntax-markup-inserted-text:#116329;--color-prettylights-syntax-markup-inserted-bg:#dafbe1;--color-prettylights-syntax-markup-changed-text:#953800;--color-prettylights-syntax-markup-changed-bg:#ffd8b5;--color-prettylights-syntax-markup-ignored-text:#eaeef2;--color-prettylights-syntax-markup-ignored-bg:#0550ae;--color-prettylights-syntax-meta-diff-range:#8250df;--color-prettylights-syntax-brackethighlighter-angle:#57606a;--color-prettylights-syntax-sublimelinter-gutter-mark:#8c959f;--color-prettylights-syntax-constant-other-reference-link:#0a3069;--color-fg-default:#1F2328;--color-fg-muted:#656d76;--color-fg-subtle:#6e7781;--color-canvas-default:#ffffff;--color-canvas-subtle:#f6f8fa;--color-border-default:#d0d7de;--color-border-muted:hsla(210,18%,87%,1);--color-neutral-muted:rgba(175,184,193,0.2);--color-accent-fg:#0969da;--color-accent-emphasis:#0969da;--color-attention-fg:#9a6700;--color-attention-subtle:#fff8c5;--color-danger-fg:#d1242f;--color-done-fg:#8250df;--color-prettylights-syntax-comment:#6e7781;--color-prettylights-syntax-constant:#0550ae;--color-prettylights-syntax-entity:#6639ba;--color-prettylights-syntax-storage-modifier-import:#24292f;--color-prettylights-syntax-entity-tag:#116329;--color-prettylights-syntax-keyword:#cf222e;--color-prettylights-syntax-string:#0a3069;--color-prettylights-syntax-variable:#953800;--color-prettylights-syntax-brackethighlighter-unmatched:#82071e;--color-prettylights-syntax-invalid-illegal-text:#f6f8fa;--color-prettylights-syntax-invalid-illegal-bg:#82071e;--color-prettylights-syntax-carriage-return-text:#f6f8fa;--color-prettylights-syntax-carriage-return-bg:#cf222e;--color-prettylights-syntax-string-regexp:#116329;--color-prettylights-syntax-markup-list:#3b2300;--color-prettylights-syntax-markup-heading:#0550ae;--color-prettylights-syntax-markup-italic:#24292f;--color-prettylights-syntax-markup-bold:#24292f;--color-prettylights-syntax-markup-deleted-text:#82071e;--color-prettylights-syntax-markup-deleted-bg:#ffebe9;--color-prettylights-syntax-markup-inserted-text:#116329;--color-prettylights-syntax-markup-inserted-bg:#dafbe1;--color-prettylights-syntax-markup-changed-text:#953800;--color-prettylights-syntax-markup-changed-bg:#ffd8b5;--color-prettylights-syntax-markup-ignored-text:#eaeef2;--color-prettylights-syntax-markup-ignored-bg:#0550ae;--color-prettylights-syntax-meta-diff-range:#8250df;--color-prettylights-syntax-brackethighlighter-angle:#57606a;--color-prettylights-syntax-sublimelinter-gutter-mark:#8c959f;--color-prettylights-syntax-constant-other-reference-link:#0a3069;--color-fg-default:#1F2328;--color-fg-muted:#656d76;--color-fg-subtle:#6e7781;--color-canvas-default:#ffffff;--color-canvas-subtle:#f6f8fa;--color-border-default:#d0d7de;--color-border-muted:hsla(210,18%,87%,1);--color-neutral-muted:rgba(175,184,193,0.2);--color-accent-fg:#0969da;--color-accent-emphasis:#0969da;--color-attention-fg:#9a6700;--color-attention-subtle:#fff8c5;--color-danger-fg:#d1242f;--color-done-fg:#8250df}}.markdown-body{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;margin:0;color:var(--color-fg-default);background-color:var(--color-canvas-default);font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";font-size:16px;line-height:1.5;word-wrap:break-word}.markdown-body .octicon{display:inline-block;fill:currentColor;vertical-align:text-bottom}.markdown-body h1:hover .anchor .octicon-link:before,.markdown-body h2:hover .anchor .octicon-link:before,.markdown-body h3:hover .anchor .octicon-link:before,.markdown-body h4:hover .anchor .octicon-link:before,.markdown-body h5:hover .anchor .octicon-link:before,.markdown-body h6:hover .anchor .octicon-link:before{width:16px;height:16px;content:' ';display:inline-block;background-color:currentColor;-webkit-mask-image:url("data:image/svg+xml,");mask-image:url("data:image/svg+xml,")}.markdown-body details,.markdown-body figcaption,.markdown-body figure{display:block}.markdown-body summary{display:list-item}.markdown-body [hidden]{display:none!important}.markdown-body a{background-color:transparent;color:var(--color-accent-fg);text-decoration:none}.markdown-body abbr[title]{border-bottom:none;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.markdown-body b,.markdown-body strong{font-weight:var(--base-text-weight-semibold,600)}.markdown-body dfn{font-style:italic}.markdown-body h1{margin:.67em 0;font-weight:var(--base-text-weight-semibold,600);padding-bottom:.3em;font-size:2em;border-bottom:1px solid var(--color-border-muted)}.markdown-body mark{background-color:var(--color-attention-subtle);color:var(--color-fg-default)}.markdown-body small{font-size:90%}.markdown-body sub,.markdown-body sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}.markdown-body sub{bottom:-.25em}.markdown-body sup{top:-.5em}.markdown-body img{border-style:none;max-width:100%;box-sizing:content-box;background-color:var(--color-canvas-default)}.markdown-body code,.markdown-body kbd,.markdown-body pre,.markdown-body samp{font-family:monospace;font-size:1em}.markdown-body figure{margin:1em 40px}.markdown-body hr{box-sizing:content-box;overflow:hidden;background:0 0;border-bottom:1px solid var(--color-border-muted);height:.25em;padding:0;margin:24px 0;background-color:var(--color-border-default);border:0}.markdown-body input{font:inherit;margin:0;overflow:visible;font-family:inherit;font-size:inherit;line-height:inherit}.markdown-body [type=button],.markdown-body [type=reset],.markdown-body [type=submit]{-webkit-appearance:button}.markdown-body [type=checkbox],.markdown-body [type=radio]{box-sizing:border-box;padding:0}.markdown-body [type=number]::-webkit-inner-spin-button,.markdown-body [type=number]::-webkit-outer-spin-button{height:auto}.markdown-body [type=search]::-webkit-search-cancel-button,.markdown-body [type=search]::-webkit-search-decoration{-webkit-appearance:none}.markdown-body ::-webkit-input-placeholder{color:inherit;opacity:.54}.markdown-body ::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}.markdown-body a:hover{text-decoration:underline}.markdown-body ::placeholder{color:var(--color-fg-subtle);opacity:1}.markdown-body hr::before{display:table;content:""}.markdown-body hr::after{display:table;clear:both;content:""}.markdown-body table{border-spacing:0;border-collapse:collapse;display:block;width:max-content;max-width:100%;overflow:auto}.markdown-body td,.markdown-body th{padding:0}.markdown-body details summary{cursor:pointer}.markdown-body details:not([open])>:not(summary){display:none!important}.markdown-body [role=button]:focus,.markdown-body a:focus,.markdown-body input[type=checkbox]:focus,.markdown-body input[type=radio]:focus{outline:2px solid var(--color-accent-fg);outline-offset:-2px;box-shadow:none}.markdown-body [role=button]:focus:not(:focus-visible),.markdown-body a:focus:not(:focus-visible),.markdown-body input[type=checkbox]:focus:not(:focus-visible),.markdown-body input[type=radio]:focus:not(:focus-visible){outline:solid 1px transparent}.markdown-body [role=button]:focus-visible,.markdown-body a:focus-visible,.markdown-body input[type=checkbox]:focus-visible,.markdown-body input[type=radio]:focus-visible{outline:2px solid var(--color-accent-fg);outline-offset:-2px;box-shadow:none}.markdown-body a:not([class]):focus,.markdown-body a:not([class]):focus-visible,.markdown-body input[type=checkbox]:focus,.markdown-body input[type=checkbox]:focus-visible,.markdown-body input[type=radio]:focus,.markdown-body input[type=radio]:focus-visible{outline-offset:0}.markdown-body kbd{display:inline-block;padding:3px 5px;font:11px ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;line-height:10px;color:var(--color-fg-default);vertical-align:middle;background-color:var(--color-canvas-subtle);border:solid 1px var(--color-neutral-muted);border-bottom-color:var(--color-neutral-muted);border-radius:6px;box-shadow:inset 0 -1px 0 var(--color-neutral-muted)}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{margin-top:24px;margin-bottom:16px;font-weight:var(--base-text-weight-semibold,600);line-height:1.25}.markdown-body h2{font-weight:var(--base-text-weight-semibold,600);padding-bottom:.3em;font-size:1.5em;border-bottom:1px solid var(--color-border-muted)}.markdown-body h3{font-weight:var(--base-text-weight-semibold,600);font-size:1.25em}.markdown-body h4{font-weight:var(--base-text-weight-semibold,600);font-size:1em}.markdown-body h5{font-weight:var(--base-text-weight-semibold,600);font-size:.875em}.markdown-body h6{font-weight:var(--base-text-weight-semibold,600);font-size:.85em;color:var(--color-fg-muted)}.markdown-body p{margin-top:0;margin-bottom:10px}.markdown-body blockquote{margin:0;padding:0 1em;color:var(--color-fg-muted);border-left:.25em solid var(--color-border-default)}.markdown-body ol,.markdown-body ul{margin-top:0;margin-bottom:0;padding-left:2em}.markdown-body ol ol,.markdown-body ul ol{list-style-type:lower-roman}.markdown-body ol ol ol,.markdown-body ol ul ol,.markdown-body ul ol ol,.markdown-body ul ul ol{list-style-type:lower-alpha}.markdown-body dd{margin-left:0}.markdown-body code,.markdown-body samp,.markdown-body tt{font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;font-size:12px}.markdown-body pre{margin-top:0;margin-bottom:0;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;font-size:12px;word-wrap:normal}.markdown-body .octicon{display:inline-block;overflow:visible!important;vertical-align:text-bottom;fill:currentColor}.markdown-body input::-webkit-inner-spin-button,.markdown-body input::-webkit-outer-spin-button{margin:0;-webkit-appearance:none;appearance:none}.markdown-body .color-fg-accent{color:var(--color-accent-fg)!important}.markdown-body .color-fg-attention{color:var(--color-attention-fg)!important}.markdown-body .color-fg-done{color:var(--color-done-fg)!important}.markdown-body .flex-items-center{align-items:center!important}.markdown-body .mb-1{margin-bottom:var(--base-size-4,4px)!important}.markdown-body .text-semibold{font-weight:var(--base-text-weight-medium,500)!important}.markdown-body .d-inline-flex{display:inline-flex!important}.markdown-body::before{display:table;content:""}.markdown-body::after{display:table;clear:both;content:""}.markdown-body>:first-child{margin-top:0!important}.markdown-body>:last-child{margin-bottom:0!important}.markdown-body a:not([href]){color:inherit;text-decoration:none}.markdown-body .absent{color:var(--color-danger-fg)}.markdown-body .anchor{float:left;padding-right:4px;margin-left:-20px;line-height:1}.markdown-body .anchor:focus{outline:0}.markdown-body blockquote,.markdown-body details,.markdown-body dl,.markdown-body ol,.markdown-body p,.markdown-body pre,.markdown-body table,.markdown-body ul{margin-top:0;margin-bottom:16px}.markdown-body blockquote>:first-child{margin-top:0}.markdown-body blockquote>:last-child{margin-bottom:0}.markdown-body h1 .octicon-link,.markdown-body h2 .octicon-link,.markdown-body h3 .octicon-link,.markdown-body h4 .octicon-link,.markdown-body h5 .octicon-link,.markdown-body h6 .octicon-link{color:var(--color-fg-default);vertical-align:middle;visibility:hidden}.markdown-body h1:hover .anchor,.markdown-body h2:hover .anchor,.markdown-body h3:hover .anchor,.markdown-body h4:hover .anchor,.markdown-body h5:hover .anchor,.markdown-body h6:hover .anchor{text-decoration:none}.markdown-body h1:hover .anchor .octicon-link,.markdown-body h2:hover .anchor .octicon-link,.markdown-body h3:hover .anchor .octicon-link,.markdown-body h4:hover .anchor .octicon-link,.markdown-body h5:hover .anchor .octicon-link,.markdown-body h6:hover .anchor .octicon-link{visibility:visible}.markdown-body h1 code,.markdown-body h1 tt,.markdown-body h2 code,.markdown-body h2 tt,.markdown-body h3 code,.markdown-body h3 tt,.markdown-body h4 code,.markdown-body h4 tt,.markdown-body h5 code,.markdown-body h5 tt,.markdown-body h6 code,.markdown-body h6 tt{padding:0 .2em;font-size:inherit}.markdown-body summary h1,.markdown-body summary h2,.markdown-body summary h3,.markdown-body summary h4,.markdown-body summary h5,.markdown-body summary h6{display:inline-block}.markdown-body summary h1 .anchor,.markdown-body summary h2 .anchor,.markdown-body summary h3 .anchor,.markdown-body summary h4 .anchor,.markdown-body summary h5 .anchor,.markdown-body summary h6 .anchor{margin-left:-40px}.markdown-body summary h1,.markdown-body summary h2{padding-bottom:0;border-bottom:0}.markdown-body ol.no-list,.markdown-body ul.no-list{padding:0;list-style-type:none}.markdown-body ol[type="a s"]{list-style-type:lower-alpha}.markdown-body ol[type="A s"]{list-style-type:upper-alpha}.markdown-body ol[type="i s"]{list-style-type:lower-roman}.markdown-body ol[type="I s"]{list-style-type:upper-roman}.markdown-body ol[type="1"]{list-style-type:decimal}.markdown-body div>ol:not([type]){list-style-type:decimal}.markdown-body ol ol,.markdown-body ol ul,.markdown-body ul ol,.markdown-body ul ul{margin-top:0;margin-bottom:0}.markdown-body li>p{margin-top:16px}.markdown-body li+li{margin-top:.25em}.markdown-body dl{padding:0}.markdown-body dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:var(--base-text-weight-semibold,600)}.markdown-body dl dd{padding:0 16px;margin-bottom:16px}.markdown-body table th{font-weight:var(--base-text-weight-semibold,600)}.markdown-body table td,.markdown-body table th{padding:6px 13px;border:1px solid var(--color-border-default)}.markdown-body table td>:last-child{margin-bottom:0}.markdown-body table tr{background-color:var(--color-canvas-default);border-top:1px solid var(--color-border-muted)}.markdown-body table tr:nth-child(2n){background-color:var(--color-canvas-subtle)}.markdown-body table img{background-color:transparent}.markdown-body img[align=right]{padding-left:20px}.markdown-body img[align=left]{padding-right:20px}.markdown-body .emoji{max-width:none;vertical-align:text-top;background-color:transparent}.markdown-body span.frame{display:block;overflow:hidden}.markdown-body span.frame>span{display:block;float:left;width:auto;padding:7px;margin:13px 0 0;overflow:hidden;border:1px solid var(--color-border-default)}.markdown-body span.frame span img{display:block;float:left}.markdown-body span.frame span span{display:block;padding:5px 0 0;clear:both;color:var(--color-fg-default)}.markdown-body span.align-center{display:block;overflow:hidden;clear:both}.markdown-body span.align-center>span{display:block;margin:13px auto 0;overflow:hidden;text-align:center}.markdown-body span.align-center span img{margin:0 auto;text-align:center}.markdown-body span.align-right{display:block;overflow:hidden;clear:both}.markdown-body span.align-right>span{display:block;margin:13px 0 0;overflow:hidden;text-align:right}.markdown-body span.align-right span img{margin:0;text-align:right}.markdown-body span.float-left{display:block;float:left;margin-right:13px;overflow:hidden}.markdown-body span.float-left span{margin:13px 0 0}.markdown-body span.float-right{display:block;float:right;margin-left:13px;overflow:hidden}.markdown-body span.float-right>span{display:block;margin:13px auto 0;overflow:hidden;text-align:right}.markdown-body code,.markdown-body tt{padding:.2em .4em;margin:0;font-size:85%;white-space:break-spaces;background-color:var(--color-neutral-muted);border-radius:6px}.markdown-body code br,.markdown-body tt br{display:none}.markdown-body del code{text-decoration:inherit}.markdown-body samp{font-size:85%}.markdown-body pre code{font-size:100%}.markdown-body pre>code{padding:0;margin:0;word-break:normal;white-space:pre;background:0 0;border:0}.markdown-body .highlight{margin-bottom:16px}.markdown-body .highlight pre{margin-bottom:0;word-break:normal}.markdown-body .highlight pre,.markdown-body pre{padding:16px;overflow:auto;font-size:85%;line-height:1.45;color:var(--color-fg-default);background-color:var(--color-canvas-subtle);border-radius:6px}.markdown-body pre code,.markdown-body pre tt{display:inline;max-width:auto;padding:0;margin:0;overflow:visible;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}.markdown-body .csv-data td,.markdown-body .csv-data th{padding:5px;overflow:hidden;font-size:12px;line-height:1;text-align:left;white-space:nowrap}.markdown-body .csv-data .blob-num{padding:10px 8px 9px;text-align:right;background:var(--color-canvas-default);border:0}.markdown-body .csv-data tr{border-top:0}.markdown-body .csv-data th{font-weight:var(--base-text-weight-semibold,600);background:var(--color-canvas-subtle);border-top:0}.markdown-body [data-footnote-ref]::before{content:"["}.markdown-body [data-footnote-ref]::after{content:"]"}.markdown-body .footnotes{font-size:12px;color:var(--color-fg-muted);border-top:1px solid var(--color-border-default)}.markdown-body .footnotes ol{padding-left:16px}.markdown-body .footnotes ol ul{display:inline-block;padding-left:16px;margin-top:16px}.markdown-body .footnotes li{position:relative}.markdown-body .footnotes li:target::before{position:absolute;top:-8px;right:-8px;bottom:-8px;left:-24px;pointer-events:none;content:"";border:2px solid var(--color-accent-emphasis);border-radius:6px}.markdown-body .footnotes li:target{color:var(--color-fg-default)}.markdown-body .footnotes .data-footnote-backref g-emoji{font-family:monospace}.markdown-body .pl-c{color:var(--color-prettylights-syntax-comment)}.markdown-body .pl-c1,.markdown-body .pl-s .pl-v{color:var(--color-prettylights-syntax-constant)}.markdown-body .pl-e,.markdown-body .pl-en{color:var(--color-prettylights-syntax-entity)}.markdown-body .pl-s .pl-s1,.markdown-body .pl-smi{color:var(--color-prettylights-syntax-storage-modifier-import)}.markdown-body .pl-ent{color:var(--color-prettylights-syntax-entity-tag)}.markdown-body .pl-k{color:var(--color-prettylights-syntax-keyword)}.markdown-body .pl-pds,.markdown-body .pl-s,.markdown-body .pl-s .pl-pse .pl-s1,.markdown-body .pl-sr,.markdown-body .pl-sr .pl-cce,.markdown-body .pl-sr .pl-sra,.markdown-body .pl-sr .pl-sre{color:var(--color-prettylights-syntax-string)}.markdown-body .pl-smw,.markdown-body .pl-v{color:var(--color-prettylights-syntax-variable)}.markdown-body .pl-bu{color:var(--color-prettylights-syntax-brackethighlighter-unmatched)}.markdown-body .pl-ii{color:var(--color-prettylights-syntax-invalid-illegal-text);background-color:var(--color-prettylights-syntax-invalid-illegal-bg)}.markdown-body .pl-c2{color:var(--color-prettylights-syntax-carriage-return-text);background-color:var(--color-prettylights-syntax-carriage-return-bg)}.markdown-body .pl-sr .pl-cce{font-weight:700;color:var(--color-prettylights-syntax-string-regexp)}.markdown-body .pl-ml{color:var(--color-prettylights-syntax-markup-list)}.markdown-body .pl-mh,.markdown-body .pl-mh .pl-en,.markdown-body .pl-ms{font-weight:700;color:var(--color-prettylights-syntax-markup-heading)}.markdown-body .pl-mi{font-style:italic;color:var(--color-prettylights-syntax-markup-italic)}.markdown-body .pl-mb{font-weight:700;color:var(--color-prettylights-syntax-markup-bold)}.markdown-body .pl-md{color:var(--color-prettylights-syntax-markup-deleted-text);background-color:var(--color-prettylights-syntax-markup-deleted-bg)}.markdown-body .pl-mi1{color:var(--color-prettylights-syntax-markup-inserted-text);background-color:var(--color-prettylights-syntax-markup-inserted-bg)}.markdown-body .pl-mc{color:var(--color-prettylights-syntax-markup-changed-text);background-color:var(--color-prettylights-syntax-markup-changed-bg)}.markdown-body .pl-mi2{color:var(--color-prettylights-syntax-markup-ignored-text);background-color:var(--color-prettylights-syntax-markup-ignored-bg)}.markdown-body .pl-mdr{font-weight:700;color:var(--color-prettylights-syntax-meta-diff-range)}.markdown-body .pl-ba{color:var(--color-prettylights-syntax-brackethighlighter-angle)}.markdown-body .pl-sg{color:var(--color-prettylights-syntax-sublimelinter-gutter-mark)}.markdown-body .pl-corl{text-decoration:underline;color:var(--color-prettylights-syntax-constant-other-reference-link)}.markdown-body g-emoji{display:inline-block;min-width:1ch;font-family:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:1em;font-style:normal!important;font-weight:var(--base-text-weight-normal,400);line-height:1;vertical-align:-.075em}.markdown-body g-emoji img{width:1em;height:1em}.markdown-body .task-list-item{list-style-type:none}.markdown-body .task-list-item label{font-weight:var(--base-text-weight-normal,400)}.markdown-body .task-list-item.enabled label{cursor:pointer}.markdown-body .task-list-item+.task-list-item{margin-top:4px}.markdown-body .task-list-item .handle{display:none}.markdown-body .task-list-item-checkbox{margin:0 .2em .25em -1.4em;vertical-align:middle}.markdown-body .contains-task-list:dir(rtl) .task-list-item-checkbox{margin:0 -1.6em .25em .2em}.markdown-body .contains-task-list{position:relative}.markdown-body .contains-task-list:focus-within .task-list-item-convert-container,.markdown-body .contains-task-list:hover .task-list-item-convert-container{display:block;width:auto;height:24px;overflow:visible;clip:auto}.markdown-body .QueryBuilder .qb-entity{color:var(--color-prettylights-syntax-entity)}.markdown-body .QueryBuilder .qb-constant{color:var(--color-prettylights-syntax-constant)}.markdown-body ::-webkit-calendar-picker-indicator{filter:invert(50%)}.markdown-body .markdown-alert{padding:0 1em;margin-bottom:16px;color:inherit;border-left:.25em solid var(--color-border-default)}.markdown-body .markdown-alert>:first-child{margin-top:0}.markdown-body .markdown-alert>:last-child{margin-bottom:0}.markdown-body .markdown-alert.markdown-alert-note{border-left-color:var(--color-accent-fg)}.markdown-body .markdown-alert.markdown-alert-important{border-left-color:var(--color-done-fg)}.markdown-body .markdown-alert.markdown-alert-warning{border-left-color:var(--color-attention-fg)} -------------------------------------------------------------------------------- /static/r.js: -------------------------------------------------------------------------------- 1 | function R(e,t){if(t&&t.documentElement)e=t,t=arguments[2];else if(!e||!e.documentElement)throw new Error("First argument to R constructor should be a document object.");if(t=t||{},this._doc=e,this._docJSDOMParser=this._doc.firstChild.__JSDOMParser__,this._articleTitle=null,this._articleByline=null,this._articleDir=null,this._articleSiteName=null,this._attempts=[],this._debug=!!t.debug,this._maxElemsToParse=t.maxElemsToParse||this.DEFAULT_MAX_ELEMS_TO_PARSE,this._nbTopCandidates=t.nbTopCandidates||this.DEFAULT_N_TOP_CANDIDATES,this._charThreshold=t.charThreshold||this.DEFAULT_CHAR_THRESHOLD,this._classesToPreserve=this.CLASSES_TO_PRESERVE.concat(t.classesToPreserve||[]),this._keepClasses=!!t.keepClasses,this._serializer=t.serializer||function(e){return e.innerHTML},this._disableJSONLD=!!t.disableJSONLD,this._allowedVideoRegex=t.allowedVideoRegex||this.REGEXPS.videos,this._flags=this.FLAG_STRIP_UNLIKELYS|this.FLAG_WEIGHT_CLASSES|this.FLAG_CLEAN_CONDITIONALLY,this._debug){let e=function(e){if(e.nodeType==e.TEXT_NODE)return`${e.nodeName} ("${e.textContent}")`;let t=Array.from(e.attributes||[],function(e){return`${e.name}="${e.value}"`}).join(" ");return`<${e.localName} ${t}>`};this.log=function(){if(typeof console!="undefined"){let t=Array.from(arguments,t=>t&&t.nodeType==this.ELEMENT_NODE?e(t):t);t.unshift("Reader: (R)"),console.log.apply(console,t)}else if(typeof dump!="undefined"){var t=Array.prototype.map.call(arguments,function(t){return t&&t.nodeName?e(t):t}).join(" ");dump("Reader: (R) "+t+` 2 | `)}}}else this.log=function(){}}R.prototype={FLAG_STRIP_UNLIKELYS:1,FLAG_WEIGHT_CLASSES:2,FLAG_CLEAN_CONDITIONALLY:4,ELEMENT_NODE:1,TEXT_NODE:3,DEFAULT_MAX_ELEMS_TO_PARSE:0,DEFAULT_N_TOP_CANDIDATES:5,DEFAULT_TAGS_TO_SCORE:"section,h2,h3,h4,h5,h6,p,td,pre".toUpperCase().split(","),DEFAULT_CHAR_THRESHOLD:500,REGEXPS:{unlikelyCandidates:/-ad-|ai2html|banner|breadcrumbs|combx|comment|community|cover-wrap|disqus|extra|footer|gdpr|header|legends|menu|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote/i,okMaybeItsACandidate:/and|article|body|column|content|main|shadow/i,positive:/article|body|content|entry|hentry|h-entry|main|page|pagination|post|text|blog|story/i,negative:/-ad-|hidden|^hid$| hid$| hid |^hid |banner|combx|comment|com-|contact|foot|footer|footnote|gdpr|masthead|media|meta|outbrain|promo|related|scroll|share|shoutbox|sidebar|skyscraper|sponsor|shopping|tags|tool|widget/i,extraneous:/print|archive|comment|discuss|e[-]?mail|share|reply|all|login|sign|single|utility/i,byline:/byline|author|dateline|writtenby|p-author/i,replaceFonts:/<(\/?)font[^>]*>/gi,normalize:/\s{2,}/g,videos:/\/\/(www\.)?((dailymotion|youtube|youtube-nocookie|player\.vimeo|v\.qq)\.com|(archive|upload\.wikimedia)\.org|player\.twitch\.tv)/i,shareElements:/(\b|_)(share|sharedaddy)(\b|_)/i,nextLink:/(next|weiter|continue|>([^|]|$)|»([^|]|$))/i,prevLink:/(prev|earl|old|new|<|«)/i,tokenize:/\W+/g,whitespace:/^\s*$/,hasContent:/\S$/,hashUrl:/^#.+/,srcsetUrl:/(\S+)(\s+[\d.]+[xw])?(\s*(?:,|$))/g,b64DataUrl:/^data:\s*([^\s;,]+)\s*;\s*base64\s*,/i,jsonLdArticleTypes:/^Article|AdvertiserContentArticle|NewsArticle|AnalysisNewsArticle|AskPublicNewsArticle|BackgroundNewsArticle|OpinionNewsArticle|ReportageNewsArticle|ReviewNewsArticle|Report|SatiricalArticle|ScholarlyArticle|MedicalScholarlyArticle|SocialMediaPosting|BlogPosting|LiveBlogPosting|DiscussionForumPosting|TechArticle|APIReference$/},UNLIKELY_ROLES:["menu","menubar","complementary","navigation","alert","alertdialog","dialog"],DIV_TO_P_ELEMS:new Set(["BLOCKQUOTE","DL","DIV","IMG","OL","P","PRE","TABLE","UL"]),ALTER_TO_DIV_EXCEPTIONS:["DIV","ARTICLE","SECTION","P"],PRESENTATIONAL_ATTRIBUTES:["align","background","bgcolor","border","cellpadding","cellspacing","frame","hspace","rules","style","valign","vspace"],DEPRECATED_SIZE_ATTRIBUTE_ELEMS:["TABLE","TH","TD","HR","PRE"],PHRASING_ELEMS:["ABBR","AUDIO","B","BDO","BR","BUTTON","CITE","CODE","DATA","DATALIST","DFN","EM","EMBED","I","IMG","INPUT","KBD","LABEL","MARK","MATH","METER","NOSCRIPT","OBJECT","OUTPUT","PROGRESS","Q","RUBY","SAMP","SCRIPT","SELECT","SMALL","SPAN","STRONG","SUB","SUP","TEXTAREA","TIME","VAR","WBR"],CLASSES_TO_PRESERVE:["page"],HTML_ESCAPE_MAP:{lt:"<",gt:">",amp:"&",quot:'"',apos:"'"},_postProcessContent:function(e){this._fixRelativeUris(e),this._simplifyNestedElements(e),this._keepClasses||this._cleanClasses(e)},_removeNodes:function(e,t){if(this._docJSDOMParser&&e._isLiveNodeList)throw new Error("Do not pass live node lists to _removeNodes");for(var s,o,n=e.length-1;n>=0;n--)s=e[n],o=s.parentNode,o&&(!t||t.call(this,s,n,e))&&o.removeChild(s)},_replaceNodeTags:function(e,t){if(this._docJSDOMParser&&e._isLiveNodeList)throw new Error("Do not pass live node lists to _replaceNodeTags");for(const n of e)this._setNodeTag(n,t)},_forEachNode:function(e,t){Array.prototype.forEach.call(e,t,this)},_findNode:function(e,t){return Array.prototype.find.call(e,t,this)},_someNode:function(e,t){return Array.prototype.some.call(e,t,this)},_everyNode:function(e,t){return Array.prototype.every.call(e,t,this)},_concatNodeLists:function(){var e=Array.prototype.slice,t=e.call(arguments),n=t.map(function(t){return e.call(t)});return Array.prototype.concat.apply([],n)},_getAllNodesWithTag:function(e,t){return e.querySelectorAll?e.querySelectorAll(t.join(",")):[].concat.apply([],t.map(function(t){var n=e.getElementsByTagName(t);return Array.isArray(n)?n:Array.from(n)}))},_cleanClasses:function(e){var n=this._classesToPreserve,t=(e.getAttribute("class")||"").split(/\s+/).filter(function(e){return n.indexOf(e)!=-1}).join(" ");t?e.setAttribute("class",t):e.removeAttribute("class");for(e=e.firstElementChild;e;e=e.nextElementSibling)this._cleanClasses(e)},_fixRelativeUris:function(e){var s,o,n=this._doc.baseURI,i=this._doc.documentURI;function t(e){if(n==i&&e.charAt(0)=="#")return e;try{return new URL(e,n).href}catch{}return e}s=this._getAllNodesWithTag(e,["a"]),this._forEachNode(s,function(e){var s,o,n=e.getAttribute("href");if(n)if(n.indexOf("javascript:")===0)if(e.childNodes.length===1&&e.childNodes[0].nodeType===this.TEXT_NODE)o=this._doc.createTextNode(e.textContent),e.parentNode.replaceChild(o,e);else{for(s=this._doc.createElement("span");e.firstChild;)s.appendChild(e.firstChild);e.parentNode.replaceChild(s,e)}else e.setAttribute("href",t(n))}),o=this._getAllNodesWithTag(e,["img","picture","figure","video","audio","source"]),this._forEachNode(o,function(e){var i,n=e.getAttribute("src"),s=e.getAttribute("poster"),o=e.getAttribute("srcset");n&&e.setAttribute("src",t(n)),s&&e.setAttribute("poster",t(s)),o&&(i=o.replace(this.REGEXPS.srcsetUrl,function(e,n,s,o){return t(n)+(s||"")+o}),e.setAttribute("srcset",i))})},_simplifyNestedElements:function(e){for(var n,s,t=e;t;){if(t.parentNode&&["DIV","SECTION"].includes(t.tagName)&&!(t.id&&t.id.startsWith("readability"))){if(this._isElementWithoutContent(t)){t=this._removeAndGetNext(t);continue}if(this._hasSingleTagInsideElement(t,"DIV")||this._hasSingleTagInsideElement(t,"SECTION")){for(s=t.children[0],n=0;n»] /.test(e)?(o=/ [\\/>»] /.test(e),e=t.replace(/(.*)[|\-\\/>»] .*/gi,"$1"),s(e)<3&&(e=t.replace(/[^|\-\\/>»]*[|\-\\/>»](.*)/gi,"$1"))):e.indexOf(": ")!==-1?(r=this._concatNodeLists(n.getElementsByTagName("h1"),n.getElementsByTagName("h2")),c=e.trim(),l=this._someNode(r,function(e){return e.textContent.trim()===c}),l||(e=t.substring(t.lastIndexOf(":")+1),s(e)<3?e=t.substring(t.indexOf(":")+1):s(t.substr(0,t.indexOf(":")))>5&&(e=t))):(e.length>150||e.length<15)&&(i=n.getElementsByTagName("h1"),i.length===1&&(e=this._getInnerText(i[0]))),e=e.trim().replace(this.REGEXPS.normalize," "),a=s(e),a<=4&&(!o||a!=s(t.replace(/[|\-\\/>»]+/g,""))-1)&&(e=t),e},_prepDocument:function(){var e=this._doc;this._removeNodes(this._getAllNodesWithTag(e,["style"])),e.body&&this._replaceBrs(e.body),this._replaceNodeTags(this._getAllNodesWithTag(e,["font"]),"SPAN")},_nextNode:function(e){for(var t=e;t&&t.nodeType!=this.ELEMENT_NODE&&this.REGEXPS.whitespace.test(t.textContent);)t=t.nextSibling;return t},_replaceBrs:function(e){this._forEachNode(this._getAllNodesWithTag(e,["br"]),function(e){for(var n,s,i,a,t=e.nextSibling,o=!1;(t=this._nextNode(t))&&t.tagName=="BR";)o=!0,i=t.nextSibling,t.parentNode.removeChild(t),t=i;if(o){for(n=this._doc.createElement("p"),e.parentNode.replaceChild(n,e),t=n.nextSibling;t;){if(t.tagName=="BR"&&(s=this._nextNode(t.nextSibling),s&&s.tagName=="BR"))break;if(!this._isPhrasingContent(t))break;a=t.nextSibling,n.appendChild(t),t=a}for(;n.lastChild&&this._isWhitespace(n.lastChild);)n.removeChild(n.lastChild);n.parentNode.tagName==="P"&&this._setNodeTag(n.parentNode,"DIV")}})},_setNodeTag:function(e,t){if(this.log("_setNodeTag",e,t),this._docJSDOMParser)return e.localName=t.toLowerCase(),e.tagName=t.toUpperCase(),e;for(var s,n=e.ownerDocument.createElement(t);e.firstChild;)n.appendChild(e.firstChild);e.parentNode.replaceChild(n,e),e.readability&&(n.readability=e.readability);for(s=0;s!s.includes(e)),i=o.join(" ").length/n.join(" ").length,1-i)},_checkByline:function(e,t){if(this._articleByline)return!1;if(e.getAttribute!==void 0)var s=e.getAttribute("rel"),n=e.getAttribute("itemprop");return!(!(s==="author"||n&&n.indexOf("author")!==-1||this.REGEXPS.byline.test(t))||!this._isValidByline(e.textContent))&&(this._articleByline=e.textContent.trim(),!0)},_getNodeAncestors:function(e,t){t=t||0;for(var s=0,n=[];e.parentNode;){if(n.push(e.parentNode),t&&++s===t)break;e=e.parentNode}return n},_grabArticle:function(e){if(this.log("**** grabArticle ****"),f=this._doc,$=e!==null,e=e||this._doc.body,!e)return this.log("No body found in document. Abort."),null;for(P=e.innerHTML;!0;){this.log("Starting grabArticle loop"),H=this._flagIsActive(this.FLAG_STRIP_UNLIKELYS),v=[],t=this._doc.documentElement;let K=!0;for(;t;){if(t.tagName==="HTML"&&(this._articleLang=t.getAttribute("lang")),l=t.className+" "+t.id,!this._isProbablyVisible(t)){this.log("Removing hidden node - "+l),t=this._removeAndGetNext(t);continue}if(t.getAttribute("aria-modal")=="true"&&t.getAttribute("role")=="dialog"){t=this._removeAndGetNext(t);continue}if(this._checkByline(t,l)){t=this._removeAndGetNext(t);continue}if(K&&this._headerDuplicatesTitle(t)){this.log("Removing header: ",t.textContent.trim(),this._articleTitle.trim()),K=!1,t=this._removeAndGetNext(t);continue}if(H){if(this.REGEXPS.unlikelyCandidates.test(l)&&!this.REGEXPS.okMaybeItsACandidate.test(l)&&!this._hasAncestorTag(t,"table")&&!this._hasAncestorTag(t,"code")&&t.tagName!=="BODY"&&t.tagName!=="A"){this.log("Removing unlikely candidate - "+l),t=this._removeAndGetNext(t);continue}if(this.UNLIKELY_ROLES.includes(t.getAttribute("role"))){this.log("Removing content with role "+t.getAttribute("role")+" - "+l),t=this._removeAndGetNext(t);continue}}if((t.tagName==="DIV"||t.tagName==="SECTION"||t.tagName==="HEADER"||t.tagName==="H1"||t.tagName==="H2"||t.tagName==="H3"||t.tagName==="H4"||t.tagName==="H5"||t.tagName==="H6")&&this._isElementWithoutContent(t)){t=this._removeAndGetNext(t);continue}if(this.DEFAULT_TAGS_TO_SCORE.indexOf(t.tagName)!==-1&&v.push(t),t.tagName==="DIV"){for(a=null,c=t.firstChild;c;){if(I=c.nextSibling,this._isPhrasingContent(c))a!==null?a.appendChild(c):this._isWhitespace(c)||(a=f.createElement("p"),t.replaceChild(a,c),a.appendChild(c));else if(a!==null){for(;a.lastChild&&this._isWhitespace(a.lastChild);)a.removeChild(a.lastChild);a=null}c=I}this._hasSingleTagInsideElement(t,"P")&&this._getLinkDensity(t)<.25?(T=t.children[0],t.parentNode.replaceChild(T,t),t=T,v.push(t)):this._hasChildBlockElement(t)||(t=this._setNodeTag(t,"P"),v.push(t))}t=this._getNextNode(t)}O=[],this._forEachNode(v,function(e){if(!e.parentNode||typeof e.parentNode.tagName=="undefined")return;var t,s,n=this._getInnerText(e);if(n.length<25)return;if(s=this._getNodeAncestors(e,5),s.length===0)return;t=0,t+=1,t+=n.split(",").length,t+=Math.min(Math.floor(n.length/100),3),this._forEachNode(s,function(e,n){if(!e.tagName||!e.parentNode||typeof e.parentNode.tagName=="undefined")return;if(typeof e.readability=="undefined"&&(this._initializeNode(e),O.push(e)),n===0)var s=1;else n===1?s=2:s=n*3;e.readability.contentScore+=t/s})});for(var t,n,s,o,i,a,c,l,d,u,h,m,f,p,g,v,b,j,y,_,w,O,x,C,E,k,A,S,F,T,z,D,N,L,R,P,H,I,B,V,$,W,r=[],M=0,U=O.length;MF.readability.contentScore){r.splice(b,0,h),r.length>this._nbTopCandidates&&r.pop();break}}if(n=r[0]||null,D=!1,n===null||n.tagName==="BODY"){for(n=f.createElement("DIV"),D=!0;e.firstChild;)this.log("Moving child out:",e.firstChild),n.appendChild(e.firstChild);e.appendChild(n),this._initializeNode(n)}else if(n){for(g=[],p=1;p=.75&&g.push(this._getNodeAncestors(r[p]));if(C=3,g.length>=C)for(s=n.parentNode;s.tagName!=="BODY";){for(E=0,k=0;k=C){n=s;break}s=s.parentNode}for(n.readability||this._initializeNode(n),s=n.parentNode,A=n.readability.contentScore,V=A/3;s.tagName!=="BODY";){if(!s.readability){s=s.parentNode;continue}if(R=s.readability.contentScore,RA){n=s;break}A=s.readability.contentScore,s=s.parentNode}for(s=n.parentNode;s.tagName!="BODY"&&s.children.length==1;)n=s,s=n.parentNode;n.readability||this._initializeNode(n)}i=f.createElement("DIV"),$&&(i.id="readability-content");for(B=Math.max(10,n.readability.contentScore*.2),s=n.parentNode,w=s.children,m=0,N=w.length;m=B?u=!0:o.nodeName==="P"&&(L=this._getLinkDensity(o),z=this._getInnerText(o),_=z.length,_>80&&L<.25?u=!0:_<80&&_>0&&L===0&&z.search(/\.( |$)/)!==-1&&(u=!0))),u&&(this.log("Appending node:",o),this.ALTER_TO_DIV_EXCEPTIONS.indexOf(o.nodeName)===-1&&(this.log("Altering sibling:",o,"to div."),o=this._setNodeTag(o,"DIV")),i.appendChild(o),w=s.children,m-=1,N-=1);if(this._debug&&this.log("Article content pre-prep: "+i.innerHTML),this._prepArticle(i),this._debug&&this.log("Article content post-prep: "+i.innerHTML),D)n.id="readability-page-1",n.className="page";else{for(j=f.createElement("DIV"),j.id="readability-page-1",j.className="page";i.firstChild;)j.appendChild(i.firstChild);i.appendChild(j)}if(this._debug&&this.log("Article content after paging: "+i.innerHTML),x=!0,d=this._getInnerText(i,!0).length,d0&&e.length<100)},_unescapeHtmlEntities:function(e){if(!e)return e;var t=this.HTML_ESCAPE_MAP;return e.replace(/&(quot|amp|apos|lt|gt);/g,function(e,n){return t[n]}).replace(/&#(?:x([0-9a-z]{1,4})|([0-9]{1,4}));/gi,function(e,t,n){var s=parseInt(t||n,t?16:10);return String.fromCharCode(s)})},_getJSONLD:function(e){var t,n=this._getAllNodesWithTag(e,["script"]);return this._forEachNode(n,function(e){if(!t&&e.getAttribute("type")==="application/ld+json")try{var s,o,i,a=e.textContent.replace(/^\s*\s*$/g,""),n=JSON.parse(a);if(!n["@context"]||!n["@context"].match(/^https?:\/\/schema\.org$/))return;if(!n["@type"]&&Array.isArray(n["@graph"])&&(n=n["@graph"].find(function(e){return(e["@type"]||"").match(this.REGEXPS.jsonLdArticleTypes)})),!n||!n["@type"]||!n["@type"].match(this.REGEXPS.jsonLdArticleTypes))return;t={},typeof n.name=="string"&&typeof n.headline=="string"&&n.name!==n.headline?(s=this._getArticleTitle(),o=this._textSimilarity(n.name,s)>.75,i=this._textSimilarity(n.headline,s)>.75,i&&!o?t.title=n.headline:t.title=n.name):typeof n.name=="string"?t.title=n.name.trim():typeof n.headline=="string"&&(t.title=n.headline.trim()),n.author&&(typeof n.author.name=="string"?t.byline=n.author.name.trim():Array.isArray(n.author)&&n.author[0]&&typeof n.author[0].name=="string"&&(t.byline=n.author.filter(function(e){return e&&typeof e.name=="string"}).map(function(e){return e.name.trim()}).join(", "))),typeof n.description=="string"&&(t.excerpt=n.description.trim()),n.publisher&&typeof n.publisher.name=="string"&&(t.siteName=n.publisher.name.trim());return}catch(e){this.log(e.message)}}),t||{}},_getArticleMetadata:function(e){var n={},t={},s=this._doc.getElementsByTagName("meta"),o=/\s*(dc|dcterm|og|twitter)\s*:\s*(author|creator|description|title|site_name)\s*/gi,i=/^\s*(?:(dc|dcterm|og|twitter|weibo:(article|webpage))\s*[.:]\s*)?(author|creator|description|title|site_name)\s*$/i;return this._forEachNode(s,function(e){var n,s,r=e.getAttribute("name"),c=e.getAttribute("property"),a=e.getAttribute("content");if(!a)return;s=null,n=null,c&&(s=c.match(o),s&&(n=s[0].toLowerCase().replace(/\s/g,""),t[n]=a.trim())),!s&&r&&i.test(r)&&(n=r,a&&(n=n.toLowerCase().replace(/\s/g,"").replace(/\./g,":"),t[n]=a.trim()))}),n.title=e.title||t["dc:title"]||t["dcterm:title"]||t["og:title"]||t["weibo:article:title"]||t["weibo:webpage:title"]||t.title||t["twitter:title"],n.title||(n.title=this._getArticleTitle()),n.byline=e.byline||t["dc:creator"]||t["dcterm:creator"]||t.author,n.excerpt=e.excerpt||t["dc:description"]||t["dcterm:description"]||t["og:description"]||t["weibo:article:description"]||t["weibo:webpage:description"]||t.description||t["twitter:description"],n.siteName=e.siteName||t["og:site_name"],n.title=this._unescapeHtmlEntities(n.title),n.byline=this._unescapeHtmlEntities(n.byline),n.excerpt=this._unescapeHtmlEntities(n.excerpt),n.siteName=this._unescapeHtmlEntities(n.siteName),n},_isSingleImage:function(e){return e.tagName==="IMG"||e.children.length===1&&e.textContent.trim()===""&&this._isSingleImage(e.children[0])},_unwrapNoscriptImages:function(e){var t,n=Array.from(e.getElementsByTagName("img"));this._forEachNode(n,function(e){for(var n,t=0;t0&&o>n)return!1;if(e.parentNode.tagName===t&&(!s||s(e.parentNode)))return!0;e=e.parentNode,o++}return!1},_getRowAndColumnCount:function(e){for(var t,n,o,r,c,l=0,i=0,a=e.getElementsByTagName("tr"),s=0;s0){t._readabilityDataTable=!0;continue}if(l=["col","colgroup","tfoot","thead","th"],d=function(e){return!!t.getElementsByTagName(e)[0]},l.some(d)){this.log("Data table because found data-y descendant"),t._readabilityDataTable=!0;continue}if(t.getElementsByTagName("table")[0]){t._readabilityDataTable=!1;continue}if(n=this._getRowAndColumnCount(t),n.rows>=10||n.columns>4){t._readabilityDataTable=!0;continue}t._readabilityDataTable=n.rows*n.columns>10}},_fixLazyImages:function(e){this._forEachNode(this._getAllNodesWithTag(e,["img","picture","figure"]),function(e){if(e.src&&this.REGEXPS.b64DataUrl.test(e.src)){var t,n,s,o,i,a,r,c,l=this.REGEXPS.b64DataUrl.exec(e.src);if(l[1]==="image/svg+xml")return;for(i=!1,s=0;sn+=this._getInnerText(e,!0).length),n/s)},_cleanConditionally:function(e,t){if(!this._flagIsActive(this.FLAG_CLEAN_CONDITIONALLY))return;this._removeNodes(this._getAllNodesWithTag(e,[t]),function(e){var n,s,i,a,r,c,l,d,u,h,m,f,p,g,v,b,j,y=function(e){return e._readabilityDataTable},o=t==="ul"||t==="ol";if(o||(h=0,v=this._getAllNodesWithTag(e,["ul","ol"]),this._forEachNode(v,e=>h+=this._getInnerText(e).length),o=h/this._getInnerText(e).length>.9),t==="table"&&y(e))return!1;if(this._hasAncestorTag(e,"table",-1,y))return!1;if(this._hasAncestorTag(e,"code"))return!1;if(a=this._getClassWeight(e),this.log("Cleaning Conditionally",e),b=0,a+b<0)return!0;if(this._getCharCount(e,",")<10){for(c=e.getElementsByTagName("p").length,s=e.getElementsByTagName("img").length,j=e.getElementsByTagName("li").length-100,g=e.getElementsByTagName("input").length,p=this._getTextDensity(e,["h1","h2","h3","h4","h5","h6"]),u=0,i=this._getAllNodesWithTag(e,["object","embed","iframe"]),n=0;n1&&c/s<.5&&!this._hasAncestorTag(e,"figure")||!o&&j>c||g>Math.floor(c/3)||!o&&p<.9&&f<25&&(s===0||s>2)&&!this._hasAncestorTag(e,"figure")||!o&&a<25&&m>.2||a>=25&&m>.5||u===1&&f<75||u>1,o&&d){for(l=0;l1)return d}let t=e.getElementsByTagName("li").length;if(s==t)return!1}return d}return!1})},_cleanMatchedNodes:function(e,t){for(var s=this._getNextNode(e,!0),n=this._getNextNode(e);n&&n!=s;)t.call(this,n,n.className+" "+n.id)?n=this._removeAndGetNext(n):n=this._getNextNode(n)},_cleanHeaders:function(e){let t=this._getAllNodesWithTag(e,["h1","h2"]);this._removeNodes(t,function(e){let t=this._getClassWeight(e)<0;return t&&this.log("Removing header with low class weight:",e),t})},_headerDuplicatesTitle:function(e){if(e.tagName!="H1"&&e.tagName!="H2")return!1;var t=this._getInnerText(e,!1);return this.log("Evaluating similarity of header:",t,this._articleTitle),this._textSimilarity(this._articleTitle,t)>.75},_flagIsActive:function(e){return(this._flags&e)>0},_removeFlag:function(e){this._flags=this._flags&~e},_isProbablyVisible:function(e){return(!e.style||e.style.display!="none")&&!e.hasAttribute("hidden")&&(!e.hasAttribute("aria-hidden")||e.getAttribute("aria-hidden")!="true"||e.className&&e.className.indexOf&&e.className.indexOf("fallback-image")!==-1)},parse:function(){if(this._maxElemsToParse>0){var e,t,n,s,i,o=this._doc.getElementsByTagName("*").length;if(o>this._maxElemsToParse)throw new Error("Aborting parsing document; "+o+" elements found")}return this._unwrapNoscriptImages(this._doc),i=this._disableJSONLD?{}:this._getJSONLD(this._doc),this._removeScripts(this._doc),this._prepDocument(),e=this._getArticleMetadata(i),this._articleTitle=e.title,t=this._grabArticle(),t?(this.log("Grabbed: "+t.innerHTML),this._postProcessContent(t),e.excerpt||(n=t.getElementsByTagName("p"),n.length>0&&(e.excerpt=n[0].textContent.trim())),s=t.textContent,{title:this._articleTitle,byline:e.byline||this._articleByline,dir:this._articleDir,lang:this._articleLang,content:this._serializer(t),textContent:s,length:s.length,excerpt:e.excerpt,siteName:e.siteName||this._articleSiteName}):null}},typeof module=="object"&&(module.exports=R) -------------------------------------------------------------------------------- /static/readme.md: -------------------------------------------------------------------------------- 1 | [brook.app](https://brook.app) 2 | --------------------------------------------------------------------------------