v7
This commit is contained in:
parent
ef15e8819d
commit
cd9737ddb7
29 changed files with 84099 additions and 37 deletions
74
app.js
74
app.js
|
|
@ -12,54 +12,54 @@ const fakeServe = new nodeStatic.Server('fakeStatic/');
|
||||||
const server = https.createServer();
|
const server = https.createServer();
|
||||||
|
|
||||||
fs.readdir('/etc/letsencrypt/live', { withFileTypes: true }, (err, files) => {
|
fs.readdir('/etc/letsencrypt/live', { withFileTypes: true }, (err, files) => {
|
||||||
if (!err)
|
if (!err)
|
||||||
files
|
files
|
||||||
.filter(file => file.isDirectory())
|
.filter(file => file.isDirectory())
|
||||||
.map(folder => folder.name)
|
.map(folder => folder.name)
|
||||||
.forEach(dir => {
|
.forEach(dir => {
|
||||||
server.addContext(dir, {
|
server.addContext(dir, {
|
||||||
key: fs.readFileSync(`/etc/letsencrypt/live/${dir}/privkey.pem`),
|
key: fs.readFileSync(`/etc/letsencrypt/live/${dir}/privkey.pem`),
|
||||||
cert: fs.readFileSync(`/etc/letsencrypt/live/${dir}/fullchain.pem`)
|
cert: fs.readFileSync(`/etc/letsencrypt/live/${dir}/fullchain.pem`)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
server.on('request', (request, response) => {
|
server.on('request', (request, response) => {
|
||||||
const ip = request.headers['x-forwarded-for'] || request.connection.remoteAddress;
|
const ip = request.headers['x-forwarded-for'] || request.connection.remoteAddress;
|
||||||
|
|
||||||
const isLS = ip.startsWith('34.216.110') || ip.startsWith('54.244.51') || ip.startsWith('54.172.60') || ip.startsWith('34.203.250') || ip.startsWith('34.203.254') || ['18.237.145.219', '34.213.241.18', '54.184.142.71', '34.219.54.89', '52.13.31.12', '52.89.157.185', '34.208.60.206', '3.80.101.141', '54.90.242.158', '54.172.185.65', '3.83.250.144', '18.209.180.25', '54.167.181.168', '54.166.136.197', '52.207.207.52', '54.252.242.153', '3.104.121.59', '34.253.198.121', '63.33.56.11', '34.250.114.219', '54.171.251.199'].includes(ip);
|
const isLS = ip.startsWith('34.216.110') || ip.startsWith('54.244.51') || ip.startsWith('54.172.60') || ip.startsWith('34.203.250') || ip.startsWith('34.203.254') || ['18.237.145.219', '34.213.241.18', '54.184.142.71', '34.219.54.89', '52.13.31.12', '52.89.157.185', '34.208.60.206', '3.80.101.141', '54.90.242.158', '54.172.185.65', '3.83.250.144', '18.209.180.25', '54.167.181.168', '54.166.136.197', '52.207.207.52', '54.252.242.153', '3.104.121.59', '34.253.198.121', '63.33.56.11', '34.250.114.219', '54.171.251.199'].includes(ip);
|
||||||
|
|
||||||
const unlockNow = request.url === '/?unlock';
|
const unlockNow = request.url === '/?unlock';
|
||||||
if (unlockNow)
|
if (unlockNow)
|
||||||
response.setHeader('Set-Cookie', ['key=standard; expires=Sun, 1 Jan 2023 00:00:00 UTC; path=/']);
|
response.setHeader('Set-Cookie', ['key=standard; expires=Sun, 1 Jan 2023 00:00:00 UTC; path=/']);
|
||||||
const unlockPatronNow = request.url === '/?unlockPatron';
|
const unlockPatronNow = request.url === '/?unlockPatron';
|
||||||
if (unlockPatronNow)
|
if (unlockPatronNow)
|
||||||
response.setHeader('Set-Cookie', ['key=patron; expires=Sun, 1 Jan 2023 00:00:00 UTC; path=/']);
|
response.setHeader('Set-Cookie', ['key=patron; expires=Sun, 1 Jan 2023 00:00:00 UTC; path=/']);
|
||||||
|
|
||||||
const unlocked = request.headers['cookie'] === 'key=standard' || unlockNow;
|
const unlocked = request.headers['cookie'] === 'key=standard' || unlockNow;
|
||||||
const patronUnlocked = request.headers['cookie'] === 'key=patron' || unlockPatronNow;
|
const patronUnlocked = request.headers['cookie'] === 'key=patron' || unlockPatronNow;
|
||||||
|
|
||||||
if (bare.route_request(request, response))
|
if (bare.route_request(request, response))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!(unlocked || patronUnlocked) && (isLS || request.headers.host === 'nebulaproxy.nebula.bio' && !request.headers['user-agent'].match(/CrOS/)))
|
if (!(unlocked || patronUnlocked) && (isLS || request.headers.host === 'nebulaproxy.nebula.bio' && !request.headers['user-agent'].match(/CrOS/)))
|
||||||
fakeServe.serve(request, response);
|
fakeServe.serve(request, response);
|
||||||
else {
|
else {
|
||||||
if (bare.route_request(request, response))
|
if (bare.route_request(request, response))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (patronUnlocked)
|
if (patronUnlocked)
|
||||||
patronServe.serve(request, response);
|
patronServe.serve(request, response);
|
||||||
else
|
else
|
||||||
serve.serve(request, response);
|
serve.serve(request, response);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
server.on('upgrade', (req, socket, head) => {
|
server.on('upgrade', (req, socket, head) => {
|
||||||
if (bare.route_upgrade(req, socket, head))
|
if (bare.route_upgrade(req, socket, head))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
socket.end();
|
socket.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
server.listen(443);
|
server.listen(443);
|
||||||
BIN
static/images/cur.gif
Normal file
BIN
static/images/cur.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
BIN
static/images/cursor.cur
Normal file
BIN
static/images/cursor.cur
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2 KiB |
BIN
static/images/logo.png
Normal file
BIN
static/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
65
static/index.html
Normal file
65
static/index.html
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Nebula</title>
|
||||||
|
<link rel="icon" type="image/x-icon" href="/images/logo.png">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<!-- Stylesheets -->
|
||||||
|
<link rel="stylesheet" href="style/master.css">
|
||||||
|
<link rel="stylesheet" href="style/main.css">
|
||||||
|
<!-- Embed -->
|
||||||
|
<meta name="theme-color" content="#eb6f92">
|
||||||
|
<meta property="og:title" content="Nebula">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:image" content="/images/logo.png">
|
||||||
|
<meta property="og:description" content="">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<!-- Scripts -->
|
||||||
|
<script src="uv/uv.bundle.js"></script>
|
||||||
|
<script src="uv/uv.config.js"></script>
|
||||||
|
<div id="navbar">
|
||||||
|
<img src='/images/logo.png' id="thumbImg" style='height: 90%; margin-left: 5%; border-radius: 5px; '>
|
||||||
|
<h1 style='margin-left: .5%; font-family: "Roboto"; color: #4de0fa'>nebula</h1>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<!--
|
||||||
|
<li>
|
||||||
|
<a onclick="href = '/go/' + __uv$config.encodeUrl('https://discord.gg/neb')">C <ubr>hat </a>
|
||||||
|
</li>
|
||||||
|
-->
|
||||||
|
<li>
|
||||||
|
<a href="options">Settings</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a href="https://discord.gg/bZQxg6SkVp">Need Support?</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="resources/form.js"></script>
|
||||||
|
<script src="resources/deviceHandler.js"></script>
|
||||||
|
<div id="content">
|
||||||
|
<form class="url" action="" method="POST" id="unblocker-form">
|
||||||
|
<input type="text" id="url" name="url" autocomplete="off" placeholder="Explore the web freely">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- Stamp -->
|
||||||
|
<p class="stamp"> © Nebula Services. All rights reserved. </p>
|
||||||
|
<!-- Panelbear -->
|
||||||
|
<script async src="https://cdn.panelbear.com/analytics.js?site=AZa75ZyiRRZ"></script>
|
||||||
|
<script>
|
||||||
|
window.panelbear = window.panelbear || function() {
|
||||||
|
(window.panelbear.q = window.panelbear.q || []).push(arguments);
|
||||||
|
};
|
||||||
|
panelbear('config', {
|
||||||
|
site: 'AZa75ZyiRRZ'
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
BIN
static/mobile/.DS_Store
vendored
Normal file
BIN
static/mobile/.DS_Store
vendored
Normal file
Binary file not shown.
49
static/mobile/index.html
Normal file
49
static/mobile/index.html
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Nebula</title>
|
||||||
|
<link rel="icon" type="image/x-icon" href="/images/logo.png">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<!-- Stylesheets -->
|
||||||
|
<link rel="stylesheet" href="style/master.css">
|
||||||
|
<link rel="stylesheet" href="style/main.css">
|
||||||
|
<!-- Embed -->
|
||||||
|
<meta name="theme-color" content="#eb6f92">
|
||||||
|
<meta property="og:title" content="Nebula">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:image" content="/images/logo.png">
|
||||||
|
<meta property="og:description" content="">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<!-- Scripts -->
|
||||||
|
<script src="uv/uv.bundle.js"></script>
|
||||||
|
<script src="uv/uv.config.js"></script>
|
||||||
|
<div id="navbar">
|
||||||
|
<img src='/images/logo.png' style='height: 90%; margin-left: 5%; border-radius: 5px; '>
|
||||||
|
<h1 style='margin-left: .5%; font-family: "Roboto"; color: #4de0fa'>nebula</h1>
|
||||||
|
|
||||||
|
<h1 style='margin-left: .5%; font-family: "Roboto"; color: #838282; font-size: 15px; bottom: 0; padding-top: 15px;'>mobile</h1>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<script src="resources/form.js"></script>
|
||||||
|
<div id="content">
|
||||||
|
<form class="url" action="" method="POST" id="unblocker-form">
|
||||||
|
<input type="text" id="url" name="url" autofocus="" autocomplete="off" placeholder="Explore the web freely">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<p class="stamp"> © Nebula Services. All rights reserved. |</p>
|
||||||
|
<!-- Panelbear -->
|
||||||
|
<script async src="https://cdn.panelbear.com/analytics.js?site=AZa75ZyiRRZ"></script>
|
||||||
|
<script>
|
||||||
|
window.panelbear = window.panelbear || function() {
|
||||||
|
(window.panelbear.q = window.panelbear.q || []).push(arguments);
|
||||||
|
};
|
||||||
|
panelbear('config', {
|
||||||
|
site: 'AZa75ZyiRRZ'
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
1
static/mobile/resources/deviceHandler.js
Normal file
1
static/mobile/resources/deviceHandler.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
(function(a,b){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))window.location=b})(navigator.userAgent||navigator.vendor||window.opera,'/mobile');
|
||||||
160
static/mobile/resources/form.js
Normal file
160
static/mobile/resources/form.js
Normal file
File diff suppressed because one or more lines are too long
197
static/mobile/style/main.css
Normal file
197
static/mobile/style/main.css
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
:root {
|
||||||
|
--background-primary: #191724;
|
||||||
|
--navbar-color: #26233a;
|
||||||
|
--navbar-height: 4em;
|
||||||
|
--navbar-text-color: #e0def4;
|
||||||
|
--input-text-color: #e0def4;
|
||||||
|
--input-placeholder-color: #6e6a86;
|
||||||
|
--input-background-color: #1f1d2e;
|
||||||
|
--input-border-color: #eb6f92;
|
||||||
|
--input-border-size: 0.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-input-placeholder {
|
||||||
|
text-align: center;
|
||||||
|
font-family: 'Roboto';
|
||||||
|
}
|
||||||
|
|
||||||
|
:-moz-placeholder {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar {
|
||||||
|
height: 60px;
|
||||||
|
text-align: center;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: var(--navbar-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: white;
|
||||||
|
text-decoration: none !important;
|
||||||
|
font-family: 'Roboto';
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: grey;
|
||||||
|
transition: 0.5s;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.down {
|
||||||
|
background-color: rgb(90, 24, 154);
|
||||||
|
left: inherit !important;
|
||||||
|
font-family: 'Helvetica';
|
||||||
|
background-color: var(--navbar-color);
|
||||||
|
/* box-shadow: 2px 2px rgb(0 0 0 / 20%); */
|
||||||
|
color: white;
|
||||||
|
display: none;
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
position: fixed;
|
||||||
|
border-bottom-right-radius: 10px;
|
||||||
|
border-bottom-left-radius: 10px;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar ul:not(.down) {
|
||||||
|
font-family: 'Helvetica';
|
||||||
|
background-color: var(--navbar-color);
|
||||||
|
/* box-shadow: 2px 2px rgb(0 0 0 / 20%); */
|
||||||
|
color: white;
|
||||||
|
margin-left: auto;
|
||||||
|
list-style-type: none;
|
||||||
|
float: right !important;
|
||||||
|
margin-right: 5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar ul li {
|
||||||
|
float: left;
|
||||||
|
padding-top: 1em;
|
||||||
|
padding-bottom: 1em;
|
||||||
|
padding-right: 1em;
|
||||||
|
padding-left: 1em;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
::placeholder,
|
||||||
|
input[type='text'] {
|
||||||
|
color: white;
|
||||||
|
font-size: 20px;
|
||||||
|
text-align: center;
|
||||||
|
font-family: 'Roboto';
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar ul p,
|
||||||
|
ul a {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
#navbar ul li p, ul li a {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#navbar ul li ul {
|
||||||
|
border-bottom-right-radius: 10px;
|
||||||
|
border-bottom-left-radius: 10px;
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
margin-top: 1rem;
|
||||||
|
left: 0;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar ul li:hover ul,
|
||||||
|
ul li ul:hover {
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar ul li p {
|
||||||
|
color: white;
|
||||||
|
font-family: 'Calibri';
|
||||||
|
z-index: 3 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar ul li ul li {
|
||||||
|
clear: both;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 98%;
|
||||||
|
color: white;
|
||||||
|
flex-direction: column;
|
||||||
|
font-family: 'Roboto';
|
||||||
|
}
|
||||||
|
|
||||||
|
#content h1 {
|
||||||
|
padding-bottom: 0.5em;
|
||||||
|
font-weight: 100;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content img {
|
||||||
|
padding-left: .5em;
|
||||||
|
filter: brightness(0) invert(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#content input {
|
||||||
|
font-size: 20px;
|
||||||
|
text-align: center;
|
||||||
|
font-family: 'Calibri';
|
||||||
|
border-style: solid !important;
|
||||||
|
border: 1.3px solid var(--input-border-color);
|
||||||
|
border-width: 1px;
|
||||||
|
border-radius: 15px;
|
||||||
|
background-color: var(--input-background-color);
|
||||||
|
color: var(--input-text-color);
|
||||||
|
width: 300px;
|
||||||
|
height: 50px;
|
||||||
|
box-shadow: none !important;
|
||||||
|
outline: none;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
font-family: 'Roboto';
|
||||||
|
animation-name: inputwide;
|
||||||
|
animation-duration: 2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
--input-text-color: #e0def4;
|
||||||
|
--input-placeholder-color: #6e6a86;
|
||||||
|
--input-background-color: #1f1d2e;
|
||||||
|
--input-border-color: #eb6f92;
|
||||||
|
--input-border-size: 0.1em;
|
||||||
|
*/
|
||||||
|
|
||||||
|
#content input:focus {
|
||||||
|
/* outline: 1.3px solid #eb6f9; !important;*/
|
||||||
|
outline: none;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes inputwide {
|
||||||
|
0% {
|
||||||
|
width: 0px;
|
||||||
|
transition-duration: 0.5s;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
width: 300px;
|
||||||
|
transition-duration: 0.5s;
|
||||||
|
}
|
||||||
|
}
|
||||||
72
static/mobile/style/master.css
Normal file
72
static/mobile/style/master.css
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Dongle&family=Roboto:wght@100&display=swap");
|
||||||
|
:root {
|
||||||
|
--background-primary: #191724;
|
||||||
|
--text-color-primary: #31748f;
|
||||||
|
--text-color-secondary: #9ccfd8;
|
||||||
|
--header-color: #26233a;
|
||||||
|
--header-height: 4em;
|
||||||
|
--header-text-color: #e0def4;
|
||||||
|
--input-text-color: white;
|
||||||
|
--input-placeholder-color: #6e6a86;
|
||||||
|
--input-background-color: #1f1d2e;
|
||||||
|
--input-border-color: #eb6f92;
|
||||||
|
--input-border-size: 0.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--background-primary);
|
||||||
|
color: var(--text-color-primary);
|
||||||
|
animation: fadeInAnimation ease 3s;
|
||||||
|
animation-iteration-count: 1;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInAnimation {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus::placeholder {
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar #thumbImg {
|
||||||
|
transition: width 2s, height 2s, transform 2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar #thumbImg:hover {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@1,100&display=swap');
|
||||||
|
.stamp {
|
||||||
|
text-align: left;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
font-family: 'Montserrat', sans-serif;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: lighter;
|
||||||
|
color: whitesmoke;
|
||||||
|
opacity: 38%;
|
||||||
|
user-select: none;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
230
static/mobile/style/options.css
Normal file
230
static/mobile/style/options.css
Normal file
|
|
@ -0,0 +1,230 @@
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Work+Sans:wght@300&display=swap");
|
||||||
|
:root {
|
||||||
|
--background-primary: #191724;
|
||||||
|
--sidebar-color: #191724;
|
||||||
|
--sidebar-text-color: #e0def4;
|
||||||
|
--text-color-primary: #e0def4;
|
||||||
|
--text-color-secondary: #6e6a86;
|
||||||
|
--focus-color: #eb6f92;
|
||||||
|
--header-height: 10vh;
|
||||||
|
--section-font-size: 20pt;
|
||||||
|
--section-font: 'Calibri';
|
||||||
|
--section-padding: 0.5em;
|
||||||
|
--setting-distance-from-sidebar: 1em;
|
||||||
|
--setting-distance-from-right: 1em;
|
||||||
|
--setting-name-font: 'Calibri';
|
||||||
|
--setting-desc-font: 'Calibri';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
body {
|
||||||
|
background-color: var(--background-primary);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
#sidebar {
|
||||||
|
animation: fadeIn 750ms ease-in 300ms forwards;
|
||||||
|
-webkit-animation: fadeIn 750ms ease-in 300ms forwards;
|
||||||
|
position: absolute;
|
||||||
|
top: var(--header-height);
|
||||||
|
left: 0;
|
||||||
|
background-color: var(--sidebar-color);
|
||||||
|
transition: width 0.5s;
|
||||||
|
width: var(--sidebar-width);
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting li a {
|
||||||
|
background-color: #2e2828;
|
||||||
|
padding: 10px 16px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-div li a ul {
|
||||||
|
border-bottom-right-radius: 10px;
|
||||||
|
border-bottom-left-radius: 10px;
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
margin-top: 10px;
|
||||||
|
left: 0 !important;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-div li a:hover ul,
|
||||||
|
ul li:hover {
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
.settings-div ul li p{
|
||||||
|
color: white;
|
||||||
|
font-family: 'Calibri';
|
||||||
|
z-index: 3 !important;
|
||||||
|
|
||||||
|
}*/
|
||||||
|
|
||||||
|
li {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-div li a ul li {
|
||||||
|
clear: both;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--sidebar-text-color);
|
||||||
|
font-size: var(--section-font-size);
|
||||||
|
font-family: var(--section-font);
|
||||||
|
width: 100%;
|
||||||
|
transition: background-color 0.5s;
|
||||||
|
padding-top: var(--section-padding);
|
||||||
|
padding-bottom: var(--section-padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section:hover {
|
||||||
|
background-color: #ffffff20;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-div {
|
||||||
|
position: absolute;
|
||||||
|
left: calc(var(--sidebar-width) + var(--setting-distance-from-sidebar));
|
||||||
|
top: 0;
|
||||||
|
width: calc(100vw - var(--sidebar-width) - var(--setting-distance-from-sidebar));
|
||||||
|
padding-top: 5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
color: var(--text-color-primary);
|
||||||
|
font-size: 2vmax;
|
||||||
|
margin: 0;
|
||||||
|
font-family: var(--setting-name-font);
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
color: var(--text-color-secondary);
|
||||||
|
margin: 0;
|
||||||
|
font-size: 2vmax;
|
||||||
|
font-family: var(--setting-desc-font);
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-input {
|
||||||
|
/* left: 100%; */
|
||||||
|
color: black !important;
|
||||||
|
position: relative;
|
||||||
|
/* right: 0; */
|
||||||
|
transform: translateY(-1.5em);
|
||||||
|
float: right;
|
||||||
|
margin-right: 5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul li {
|
||||||
|
float: left;
|
||||||
|
padding-top: 1em;
|
||||||
|
padding-bottom: 1em;
|
||||||
|
padding-right: 1em;
|
||||||
|
padding-left: 1em;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
transform: translateY(-30px);
|
||||||
|
justify-self: end;
|
||||||
|
font-family: var(--setting-desc-font);
|
||||||
|
align-self: flex-end;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes fadeIn {
|
||||||
|
0% {
|
||||||
|
opacity: 0.01;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
0% {
|
||||||
|
opacity: 0.01;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
justify-content: left;
|
||||||
|
align-items: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rectangle {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
position: relative;
|
||||||
|
width: 50px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-top: auto;
|
||||||
|
height: 50px;
|
||||||
|
background: #380848;
|
||||||
|
transform: scale(0);
|
||||||
|
border-radius: 50%;
|
||||||
|
color: white;
|
||||||
|
opacity: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
animation: scale-in 0.3s ease-out forwards, expand 0.35s 0.25s ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-text {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 16px;
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
animation: fade-in 0.65s ease-in forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes scale-in {
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes expand {
|
||||||
|
50% {
|
||||||
|
width: 350px;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
width: 300px;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, .2), 0px 1px 1px 0px rgba(0, 0, 0, .14), 0px 3px 3px -1px rgba(0, 0, 0, .12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stamp {
|
||||||
|
text-align: left;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
5
static/mobile/sw.js
Normal file
5
static/mobile/sw.js
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
importScripts('./uv/uv.sw.js');
|
||||||
|
|
||||||
|
const sw = new UVServiceWorker();
|
||||||
|
|
||||||
|
self.addEventListener('fetch', event => event.respondWith(sw.fetch(event)));
|
||||||
39304
static/mobile/uv/uv.bundle.js
Normal file
39304
static/mobile/uv/uv.bundle.js
Normal file
File diff suppressed because one or more lines are too long
10
static/mobile/uv/uv.config.js
Normal file
10
static/mobile/uv/uv.config.js
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
self.__uv$config = {
|
||||||
|
prefix: '/mobile/go/',
|
||||||
|
bare: '/bare/',
|
||||||
|
encodeUrl: Ultraviolet.codec.xor.encode,
|
||||||
|
decodeUrl: Ultraviolet.codec.xor.decode,
|
||||||
|
handler: '/uv/uv.handler.js',
|
||||||
|
bundle: '/uv/uv.bundle.js',
|
||||||
|
config: '/uv/uv.config.js',
|
||||||
|
sw: '/uv/uv.sw.js',
|
||||||
|
};
|
||||||
1129
static/mobile/uv/uv.handler.js
Normal file
1129
static/mobile/uv/uv.handler.js
Normal file
File diff suppressed because it is too large
Load diff
789
static/mobile/uv/uv.sw.js
Normal file
789
static/mobile/uv/uv.sw.js
Normal file
|
|
@ -0,0 +1,789 @@
|
||||||
|
importScripts('/uv/uv.bundle.js');
|
||||||
|
importScripts('/uv/uv.config.js');
|
||||||
|
|
||||||
|
class UVServiceWorker extends EventEmitter {
|
||||||
|
constructor(config = __uv$config) {
|
||||||
|
super();
|
||||||
|
if (!config.bare) config.bare = '/bare/';
|
||||||
|
this.addresses = typeof config.bare === 'string' ? [ new URL(config.bare, location) ] : config.bare.map(str => new URL(str, location));
|
||||||
|
this.headers = {
|
||||||
|
csp: [
|
||||||
|
'cross-origin-embedder-policy',
|
||||||
|
'cross-origin-opener-policy',
|
||||||
|
'cross-origin-resource-policy',
|
||||||
|
'content-security-policy',
|
||||||
|
'content-security-policy-report-only',
|
||||||
|
'expect-ct',
|
||||||
|
'feature-policy',
|
||||||
|
'origin-isolation',
|
||||||
|
'strict-transport-security',
|
||||||
|
'upgrade-insecure-requests',
|
||||||
|
'x-content-type-options',
|
||||||
|
'x-download-options',
|
||||||
|
'x-frame-options',
|
||||||
|
'x-permitted-cross-domain-policies',
|
||||||
|
'x-powered-by',
|
||||||
|
'x-xss-protection',
|
||||||
|
],
|
||||||
|
forward: [
|
||||||
|
'accept-encoding',
|
||||||
|
'connection',
|
||||||
|
'content-length',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
this.method = {
|
||||||
|
empty: [
|
||||||
|
'GET',
|
||||||
|
'HEAD'
|
||||||
|
]
|
||||||
|
};
|
||||||
|
this.statusCode = {
|
||||||
|
empty: [
|
||||||
|
204,
|
||||||
|
304,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
this.config = config;
|
||||||
|
this.browser = Ultraviolet.Bowser.getParser(self.navigator.userAgent).getBrowserName();
|
||||||
|
|
||||||
|
if (this.browser === 'Firefox') {
|
||||||
|
this.headers.forward.push('user-agent');
|
||||||
|
this.headers.forward.push('content-type');
|
||||||
|
};
|
||||||
|
};
|
||||||
|
async fetch({ request }) {
|
||||||
|
if (!request.url.startsWith(location.origin + (this.config.prefix || '/service/'))) {
|
||||||
|
return fetch(request);
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
|
||||||
|
const ultraviolet = new Ultraviolet(this.config);
|
||||||
|
|
||||||
|
if (typeof this.config.construct === 'function') {
|
||||||
|
this.config.construct(ultraviolet, 'service');
|
||||||
|
};
|
||||||
|
|
||||||
|
const db = await ultraviolet.cookie.db();
|
||||||
|
|
||||||
|
ultraviolet.meta.origin = location.origin;
|
||||||
|
ultraviolet.meta.base = ultraviolet.meta.url = new URL(ultraviolet.sourceUrl(request.url));
|
||||||
|
|
||||||
|
const requestCtx = new RequestContext(
|
||||||
|
request,
|
||||||
|
this,
|
||||||
|
ultraviolet,
|
||||||
|
!this.method.empty.includes(request.method.toUpperCase()) ? await request.blob() : null
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ultraviolet.meta.url.protocol === 'blob:') {
|
||||||
|
requestCtx.blob = true;
|
||||||
|
requestCtx.base = requestCtx.url = new URL(requestCtx.url.pathname);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (request.referrer && request.referrer.startsWith(location.origin)) {
|
||||||
|
const referer = new URL(ultraviolet.sourceUrl(request.referrer));
|
||||||
|
|
||||||
|
if (requestCtx.headers.origin || ultraviolet.meta.url.origin !== referer.origin && request.mode === 'cors') {
|
||||||
|
requestCtx.headers.origin = referer.origin;
|
||||||
|
};
|
||||||
|
|
||||||
|
requestCtx.headers.referer = referer.href;
|
||||||
|
};
|
||||||
|
|
||||||
|
const cookies = await ultraviolet.cookie.getCookies(db) || [];
|
||||||
|
const cookieStr = ultraviolet.cookie.serialize(cookies, ultraviolet.meta, false);
|
||||||
|
|
||||||
|
if (this.browser === 'Firefox' && !(request.destination === 'iframe' || request.destination === 'document')) {
|
||||||
|
requestCtx.forward.shift();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (cookieStr) requestCtx.headers.cookie = cookieStr;
|
||||||
|
requestCtx.headers.Host = requestCtx.url.host;
|
||||||
|
|
||||||
|
|
||||||
|
const reqEvent = new HookEvent(requestCtx, null, null);
|
||||||
|
this.emit('request', reqEvent);
|
||||||
|
|
||||||
|
if (reqEvent.intercepted) return reqEvent.returnValue;
|
||||||
|
|
||||||
|
const response = await fetch(requestCtx.send);
|
||||||
|
|
||||||
|
if (response.status === 500) {
|
||||||
|
return Promise.reject('');
|
||||||
|
};
|
||||||
|
|
||||||
|
const responseCtx = new ResponseContext(requestCtx, response, this);
|
||||||
|
const resEvent = new HookEvent(responseCtx, null, null);
|
||||||
|
|
||||||
|
this.emit('beforemod', resEvent);
|
||||||
|
if (resEvent.intercepted) return resEvent.returnValue;
|
||||||
|
|
||||||
|
for (const name of this.headers.csp) {
|
||||||
|
if (responseCtx.headers[name]) delete responseCtx.headers[name];
|
||||||
|
};
|
||||||
|
|
||||||
|
if (responseCtx.headers.location) {
|
||||||
|
responseCtx.headers.location = ultraviolet.rewriteUrl(responseCtx.headers.location);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (responseCtx.headers['set-cookie']) {
|
||||||
|
Promise.resolve(ultraviolet.cookie.setCookies(responseCtx.headers['set-cookie'], db, ultraviolet.meta)).then(() => {
|
||||||
|
self.clients.matchAll().then(function (clients){
|
||||||
|
clients.forEach(function(client){
|
||||||
|
client.postMessage({
|
||||||
|
msg: 'updateCookies',
|
||||||
|
url: ultraviolet.meta.url.href,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
delete responseCtx.headers['set-cookie'];
|
||||||
|
};
|
||||||
|
|
||||||
|
if (responseCtx.body) {
|
||||||
|
switch(request.destination) {
|
||||||
|
case 'script':
|
||||||
|
case 'worker':
|
||||||
|
responseCtx.body = `if (!self.__uv && self.importScripts) importScripts('${__uv$config.bundle}', '${__uv$config.config}', '${__uv$config.handler}');\n`;
|
||||||
|
responseCtx.body += ultraviolet.js.rewrite(
|
||||||
|
await response.text()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'style':
|
||||||
|
responseCtx.body = ultraviolet.rewriteCSS(
|
||||||
|
await response.text()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'iframe':
|
||||||
|
case 'document':
|
||||||
|
if (isHtml(ultraviolet.meta.url, (responseCtx.headers['content-type'] || ''))) {
|
||||||
|
responseCtx.body = ultraviolet.rewriteHtml(
|
||||||
|
await response.text(),
|
||||||
|
{
|
||||||
|
document: true ,
|
||||||
|
injectHead: ultraviolet.createHtmlInject(
|
||||||
|
this.config.handler,
|
||||||
|
this.config.bundle,
|
||||||
|
this.config.config,
|
||||||
|
ultraviolet.cookie.serialize(cookies, ultraviolet.meta, true),
|
||||||
|
request.referrer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
if (requestCtx.headers.accept === 'text/event-stream') {
|
||||||
|
responseCtx.headers['content-type'] = 'text/event-stream';
|
||||||
|
};
|
||||||
|
|
||||||
|
this.emit('response', resEvent);
|
||||||
|
if (resEvent.intercepted) return resEvent.returnValue;
|
||||||
|
|
||||||
|
return new Response(responseCtx.body, {
|
||||||
|
headers: responseCtx.headers,
|
||||||
|
status: responseCtx.status,
|
||||||
|
statusText: responseCtx.statusText,
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch(err) {
|
||||||
|
return new Response(err.toString(), {
|
||||||
|
status: 500,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
getBarerResponse(response) {
|
||||||
|
const headers = {};
|
||||||
|
const raw = JSON.parse(response.headers.get('x-bare-headers'));
|
||||||
|
|
||||||
|
for (const key in raw) {
|
||||||
|
headers[key.toLowerCase()] = raw[key];
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
headers,
|
||||||
|
status: +response.headers.get('x-bare-status'),
|
||||||
|
statusText: response.headers.get('x-bare-status-text'),
|
||||||
|
body: !this.statusCode.empty.includes(+response.headers.get('x-bare-status')) ? response.body : null,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
get address() {
|
||||||
|
return this.addresses[Math.floor(Math.random() * this.addresses.length)];
|
||||||
|
};
|
||||||
|
static Ultraviolet = Ultraviolet;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.UVServiceWorker = UVServiceWorker;
|
||||||
|
|
||||||
|
|
||||||
|
class ResponseContext {
|
||||||
|
constructor(request, response, worker) {
|
||||||
|
const { headers, status, statusText, body } = !request.blob ? worker.getBarerResponse(response) : {
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
headers: Object.fromEntries([...response.headers.entries()]),
|
||||||
|
body: response.body,
|
||||||
|
};
|
||||||
|
this.request = request;
|
||||||
|
this.raw = response;
|
||||||
|
this.ultraviolet = request.ultraviolet;
|
||||||
|
this.headers = headers;
|
||||||
|
this.status = status;
|
||||||
|
this.statusText = statusText;
|
||||||
|
this.body = body;
|
||||||
|
};
|
||||||
|
get url() {
|
||||||
|
return this.request.url;
|
||||||
|
}
|
||||||
|
get base() {
|
||||||
|
return this.request.base;
|
||||||
|
};
|
||||||
|
set base(val) {
|
||||||
|
this.request.base = val;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class RequestContext {
|
||||||
|
constructor(request, worker, ultraviolet, body = null) {
|
||||||
|
this.ultraviolet = ultraviolet;
|
||||||
|
this.request = request;
|
||||||
|
this.headers = Object.fromEntries([...request.headers.entries()]);
|
||||||
|
this.method = request.method;
|
||||||
|
this.forward = [...worker.headers.forward];
|
||||||
|
this.address = worker.address;
|
||||||
|
this.body = body || null;
|
||||||
|
this.redirect = request.redirect;
|
||||||
|
this.credentials = 'omit';
|
||||||
|
this.mode = request.mode === 'cors' ? request.mode : 'same-origin';
|
||||||
|
this.blob = false;
|
||||||
|
};
|
||||||
|
get send() {
|
||||||
|
return new Request((!this.blob ? this.address.href + 'v1/' : 'blob:' + location.origin + this.url.pathname), {
|
||||||
|
method: this.method,
|
||||||
|
headers: {
|
||||||
|
'x-bare-protocol': this.url.protocol,
|
||||||
|
'x-bare-host': this.url.hostname,
|
||||||
|
'x-bare-path': this.url.pathname + this.url.search,
|
||||||
|
'x-bare-port': this.url.port || (this.url.protocol === 'https:' ? '443' : '80'),
|
||||||
|
'x-bare-headers': JSON.stringify(this.headers),
|
||||||
|
'x-bare-forward-headers': JSON.stringify(this.forward),
|
||||||
|
},
|
||||||
|
redirect: this.redirect,
|
||||||
|
credentials: this.credentials,
|
||||||
|
mode: location.origin !== this.address.origin ? 'cors' : this.mode,
|
||||||
|
body: this.body
|
||||||
|
});
|
||||||
|
};
|
||||||
|
get url() {
|
||||||
|
return this.ultraviolet.meta.url;
|
||||||
|
};
|
||||||
|
set url(val) {
|
||||||
|
this.ultraviolet.meta.url = val;
|
||||||
|
};
|
||||||
|
get base() {
|
||||||
|
return this.ultraviolet.meta.base;
|
||||||
|
};
|
||||||
|
set base(val) {
|
||||||
|
this.ultraviolet.meta.base = val;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function isHtml(url, contentType = '') {
|
||||||
|
return (Ultraviolet.mime.contentType((contentType || url.pathname)) || 'text/html').split(';')[0] === 'text/html';
|
||||||
|
};
|
||||||
|
|
||||||
|
class HookEvent {
|
||||||
|
#intercepted;
|
||||||
|
#returnValue;
|
||||||
|
constructor(data = {}, target = null, that = null) {
|
||||||
|
this.#intercepted = false;
|
||||||
|
this.#returnValue = null;
|
||||||
|
this.data = data;
|
||||||
|
this.target = target;
|
||||||
|
this.that = that;
|
||||||
|
};
|
||||||
|
get intercepted() {
|
||||||
|
return this.#intercepted;
|
||||||
|
};
|
||||||
|
get returnValue() {
|
||||||
|
return this.#returnValue;
|
||||||
|
};
|
||||||
|
respondWith(input) {
|
||||||
|
this.#returnValue = input;
|
||||||
|
this.#intercepted = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var R = typeof Reflect === 'object' ? Reflect : null
|
||||||
|
var ReflectApply = R && typeof R.apply === 'function'
|
||||||
|
? R.apply
|
||||||
|
: function ReflectApply(target, receiver, args) {
|
||||||
|
return Function.prototype.apply.call(target, receiver, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
var ReflectOwnKeys
|
||||||
|
if (R && typeof R.ownKeys === 'function') {
|
||||||
|
ReflectOwnKeys = R.ownKeys
|
||||||
|
} else if (Object.getOwnPropertySymbols) {
|
||||||
|
ReflectOwnKeys = function ReflectOwnKeys(target) {
|
||||||
|
return Object.getOwnPropertyNames(target)
|
||||||
|
.concat(Object.getOwnPropertySymbols(target));
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
ReflectOwnKeys = function ReflectOwnKeys(target) {
|
||||||
|
return Object.getOwnPropertyNames(target);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function ProcessEmitWarning(warning) {
|
||||||
|
if (console && console.warn) console.warn(warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {
|
||||||
|
return value !== value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function EventEmitter() {
|
||||||
|
EventEmitter.init.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backwards-compat with node 0.10.x
|
||||||
|
EventEmitter.EventEmitter = EventEmitter;
|
||||||
|
|
||||||
|
EventEmitter.prototype._events = undefined;
|
||||||
|
EventEmitter.prototype._eventsCount = 0;
|
||||||
|
EventEmitter.prototype._maxListeners = undefined;
|
||||||
|
|
||||||
|
// By default EventEmitters will print a warning if more than 10 listeners are
|
||||||
|
// added to it. This is a useful default which helps finding memory leaks.
|
||||||
|
var defaultMaxListeners = 10;
|
||||||
|
|
||||||
|
function checkListener(listener) {
|
||||||
|
if (typeof listener !== 'function') {
|
||||||
|
throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
|
||||||
|
enumerable: true,
|
||||||
|
get: function() {
|
||||||
|
return defaultMaxListeners;
|
||||||
|
},
|
||||||
|
set: function(arg) {
|
||||||
|
if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
|
||||||
|
throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.');
|
||||||
|
}
|
||||||
|
defaultMaxListeners = arg;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
EventEmitter.init = function() {
|
||||||
|
|
||||||
|
if (this._events === undefined ||
|
||||||
|
this._events === Object.getPrototypeOf(this)._events) {
|
||||||
|
this._events = Object.create(null);
|
||||||
|
this._eventsCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._maxListeners = this._maxListeners || undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Obviously not all Emitters should be limited to 10. This function allows
|
||||||
|
// that to be increased. Set to zero for unlimited.
|
||||||
|
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
|
||||||
|
if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {
|
||||||
|
throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.');
|
||||||
|
}
|
||||||
|
this._maxListeners = n;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
function _getMaxListeners(that) {
|
||||||
|
if (that._maxListeners === undefined)
|
||||||
|
return EventEmitter.defaultMaxListeners;
|
||||||
|
return that._maxListeners;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
|
||||||
|
return _getMaxListeners(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
EventEmitter.prototype.emit = function emit(type) {
|
||||||
|
var args = [];
|
||||||
|
for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
|
||||||
|
var doError = (type === 'error');
|
||||||
|
|
||||||
|
var events = this._events;
|
||||||
|
if (events !== undefined)
|
||||||
|
doError = (doError && events.error === undefined);
|
||||||
|
else if (!doError)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If there is no 'error' event listener then throw.
|
||||||
|
if (doError) {
|
||||||
|
var er;
|
||||||
|
if (args.length > 0)
|
||||||
|
er = args[0];
|
||||||
|
if (er instanceof Error) {
|
||||||
|
// Note: The comments on the `throw` lines are intentional, they show
|
||||||
|
// up in Node's output if this results in an unhandled exception.
|
||||||
|
throw er; // Unhandled 'error' event
|
||||||
|
}
|
||||||
|
// At least give some kind of context to the user
|
||||||
|
var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));
|
||||||
|
err.context = er;
|
||||||
|
throw err; // Unhandled 'error' event
|
||||||
|
}
|
||||||
|
|
||||||
|
var handler = events[type];
|
||||||
|
|
||||||
|
if (handler === undefined)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (typeof handler === 'function') {
|
||||||
|
ReflectApply(handler, this, args);
|
||||||
|
} else {
|
||||||
|
var len = handler.length;
|
||||||
|
var listeners = arrayClone(handler, len);
|
||||||
|
for (var i = 0; i < len; ++i)
|
||||||
|
ReflectApply(listeners[i], this, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
function _addListener(target, type, listener, prepend) {
|
||||||
|
var m;
|
||||||
|
var events;
|
||||||
|
var existing;
|
||||||
|
|
||||||
|
checkListener(listener);
|
||||||
|
|
||||||
|
events = target._events;
|
||||||
|
if (events === undefined) {
|
||||||
|
events = target._events = Object.create(null);
|
||||||
|
target._eventsCount = 0;
|
||||||
|
} else {
|
||||||
|
// To avoid recursion in the case that type === "newListener"! Before
|
||||||
|
// adding it to the listeners, first emit "newListener".
|
||||||
|
if (events.newListener !== undefined) {
|
||||||
|
target.emit('newListener', type,
|
||||||
|
listener.listener ? listener.listener : listener);
|
||||||
|
|
||||||
|
// Re-assign `events` because a newListener handler could have caused the
|
||||||
|
// this._events to be assigned to a new object
|
||||||
|
events = target._events;
|
||||||
|
}
|
||||||
|
existing = events[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existing === undefined) {
|
||||||
|
// Optimize the case of one listener. Don't need the extra array object.
|
||||||
|
existing = events[type] = listener;
|
||||||
|
++target._eventsCount;
|
||||||
|
} else {
|
||||||
|
if (typeof existing === 'function') {
|
||||||
|
// Adding the second element, need to change to array.
|
||||||
|
existing = events[type] =
|
||||||
|
prepend ? [listener, existing] : [existing, listener];
|
||||||
|
// If we've already got an array, just append.
|
||||||
|
} else if (prepend) {
|
||||||
|
existing.unshift(listener);
|
||||||
|
} else {
|
||||||
|
existing.push(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for listener leak
|
||||||
|
m = _getMaxListeners(target);
|
||||||
|
if (m > 0 && existing.length > m && !existing.warned) {
|
||||||
|
existing.warned = true;
|
||||||
|
// No error code for this since it is a Warning
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
var w = new Error('Possible EventEmitter memory leak detected. ' +
|
||||||
|
existing.length + ' ' + String(type) + ' listeners ' +
|
||||||
|
'added. Use emitter.setMaxListeners() to ' +
|
||||||
|
'increase limit');
|
||||||
|
w.name = 'MaxListenersExceededWarning';
|
||||||
|
w.emitter = target;
|
||||||
|
w.type = type;
|
||||||
|
w.count = existing.length;
|
||||||
|
ProcessEmitWarning(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventEmitter.prototype.addListener = function addListener(type, listener) {
|
||||||
|
return _addListener(this, type, listener, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
|
||||||
|
|
||||||
|
EventEmitter.prototype.prependListener =
|
||||||
|
function prependListener(type, listener) {
|
||||||
|
return _addListener(this, type, listener, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
function onceWrapper() {
|
||||||
|
if (!this.fired) {
|
||||||
|
this.target.removeListener(this.type, this.wrapFn);
|
||||||
|
this.fired = true;
|
||||||
|
if (arguments.length === 0)
|
||||||
|
return this.listener.call(this.target);
|
||||||
|
return this.listener.apply(this.target, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _onceWrap(target, type, listener) {
|
||||||
|
var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
|
||||||
|
var wrapped = onceWrapper.bind(state);
|
||||||
|
wrapped.listener = listener;
|
||||||
|
state.wrapFn = wrapped;
|
||||||
|
return wrapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventEmitter.prototype.once = function once(type, listener) {
|
||||||
|
checkListener(listener);
|
||||||
|
this.on(type, _onceWrap(this, type, listener));
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
EventEmitter.prototype.prependOnceListener =
|
||||||
|
function prependOnceListener(type, listener) {
|
||||||
|
checkListener(listener);
|
||||||
|
this.prependListener(type, _onceWrap(this, type, listener));
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Emits a 'removeListener' event if and only if the listener was removed.
|
||||||
|
EventEmitter.prototype.removeListener =
|
||||||
|
function removeListener(type, listener) {
|
||||||
|
var list, events, position, i, originalListener;
|
||||||
|
|
||||||
|
checkListener(listener);
|
||||||
|
|
||||||
|
events = this._events;
|
||||||
|
if (events === undefined)
|
||||||
|
return this;
|
||||||
|
|
||||||
|
list = events[type];
|
||||||
|
if (list === undefined)
|
||||||
|
return this;
|
||||||
|
|
||||||
|
if (list === listener || list.listener === listener) {
|
||||||
|
if (--this._eventsCount === 0)
|
||||||
|
this._events = Object.create(null);
|
||||||
|
else {
|
||||||
|
delete events[type];
|
||||||
|
if (events.removeListener)
|
||||||
|
this.emit('removeListener', type, list.listener || listener);
|
||||||
|
}
|
||||||
|
} else if (typeof list !== 'function') {
|
||||||
|
position = -1;
|
||||||
|
|
||||||
|
for (i = list.length - 1; i >= 0; i--) {
|
||||||
|
if (list[i] === listener || list[i].listener === listener) {
|
||||||
|
originalListener = list[i].listener;
|
||||||
|
position = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position < 0)
|
||||||
|
return this;
|
||||||
|
|
||||||
|
if (position === 0)
|
||||||
|
list.shift();
|
||||||
|
else {
|
||||||
|
spliceOne(list, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.length === 1)
|
||||||
|
events[type] = list[0];
|
||||||
|
|
||||||
|
if (events.removeListener !== undefined)
|
||||||
|
this.emit('removeListener', type, originalListener || listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
|
||||||
|
|
||||||
|
EventEmitter.prototype.removeAllListeners =
|
||||||
|
function removeAllListeners(type) {
|
||||||
|
var listeners, events, i;
|
||||||
|
|
||||||
|
events = this._events;
|
||||||
|
if (events === undefined)
|
||||||
|
return this;
|
||||||
|
|
||||||
|
// not listening for removeListener, no need to emit
|
||||||
|
if (events.removeListener === undefined) {
|
||||||
|
if (arguments.length === 0) {
|
||||||
|
this._events = Object.create(null);
|
||||||
|
this._eventsCount = 0;
|
||||||
|
} else if (events[type] !== undefined) {
|
||||||
|
if (--this._eventsCount === 0)
|
||||||
|
this._events = Object.create(null);
|
||||||
|
else
|
||||||
|
delete events[type];
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// emit removeListener for all listeners on all events
|
||||||
|
if (arguments.length === 0) {
|
||||||
|
var keys = Object.keys(events);
|
||||||
|
var key;
|
||||||
|
for (i = 0; i < keys.length; ++i) {
|
||||||
|
key = keys[i];
|
||||||
|
if (key === 'removeListener') continue;
|
||||||
|
this.removeAllListeners(key);
|
||||||
|
}
|
||||||
|
this.removeAllListeners('removeListener');
|
||||||
|
this._events = Object.create(null);
|
||||||
|
this._eventsCount = 0;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
listeners = events[type];
|
||||||
|
|
||||||
|
if (typeof listeners === 'function') {
|
||||||
|
this.removeListener(type, listeners);
|
||||||
|
} else if (listeners !== undefined) {
|
||||||
|
// LIFO order
|
||||||
|
for (i = listeners.length - 1; i >= 0; i--) {
|
||||||
|
this.removeListener(type, listeners[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
function _listeners(target, type, unwrap) {
|
||||||
|
var events = target._events;
|
||||||
|
|
||||||
|
if (events === undefined)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
var evlistener = events[type];
|
||||||
|
if (evlistener === undefined)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
if (typeof evlistener === 'function')
|
||||||
|
return unwrap ? [evlistener.listener || evlistener] : [evlistener];
|
||||||
|
|
||||||
|
return unwrap ?
|
||||||
|
unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
EventEmitter.prototype.listeners = function listeners(type) {
|
||||||
|
return _listeners(this, type, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
EventEmitter.prototype.rawListeners = function rawListeners(type) {
|
||||||
|
return _listeners(this, type, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
EventEmitter.listenerCount = function(emitter, type) {
|
||||||
|
if (typeof emitter.listenerCount === 'function') {
|
||||||
|
return emitter.listenerCount(type);
|
||||||
|
} else {
|
||||||
|
return listenerCount.call(emitter, type);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
EventEmitter.prototype.listenerCount = listenerCount;
|
||||||
|
function listenerCount(type) {
|
||||||
|
var events = this._events;
|
||||||
|
|
||||||
|
if (events !== undefined) {
|
||||||
|
var evlistener = events[type];
|
||||||
|
|
||||||
|
if (typeof evlistener === 'function') {
|
||||||
|
return 1;
|
||||||
|
} else if (evlistener !== undefined) {
|
||||||
|
return evlistener.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventEmitter.prototype.eventNames = function eventNames() {
|
||||||
|
return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
|
||||||
|
};
|
||||||
|
|
||||||
|
function arrayClone(arr, n) {
|
||||||
|
var copy = new Array(n);
|
||||||
|
for (var i = 0; i < n; ++i)
|
||||||
|
copy[i] = arr[i];
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
function spliceOne(list, index) {
|
||||||
|
for (; index + 1 < list.length; index++)
|
||||||
|
list[index] = list[index + 1];
|
||||||
|
list.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
function unwrapListeners(arr) {
|
||||||
|
var ret = new Array(arr.length);
|
||||||
|
for (var i = 0; i < ret.length; ++i) {
|
||||||
|
ret[i] = arr[i].listener || arr[i];
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function once(emitter, name) {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
function errorListener(err) {
|
||||||
|
emitter.removeListener(name, resolver);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolver() {
|
||||||
|
if (typeof emitter.removeListener === 'function') {
|
||||||
|
emitter.removeListener('error', errorListener);
|
||||||
|
}
|
||||||
|
resolve([].slice.call(arguments));
|
||||||
|
};
|
||||||
|
|
||||||
|
eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });
|
||||||
|
if (name !== 'error') {
|
||||||
|
addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addErrorHandlerIfEventEmitter(emitter, handler, flags) {
|
||||||
|
if (typeof emitter.on === 'function') {
|
||||||
|
eventTargetAgnosticAddListener(emitter, 'error', handler, flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
|
||||||
|
if (typeof emitter.on === 'function') {
|
||||||
|
if (flags.once) {
|
||||||
|
emitter.once(name, listener);
|
||||||
|
} else {
|
||||||
|
emitter.on(name, listener);
|
||||||
|
}
|
||||||
|
} else if (typeof emitter.addEventListener === 'function') {
|
||||||
|
// EventTarget does not have `error` event semantics like Node
|
||||||
|
// EventEmitters, we do not listen for `error` events here.
|
||||||
|
emitter.addEventListener(name, function wrapListener(arg) {
|
||||||
|
// IE does not have builtin `{ once: true }` support so we
|
||||||
|
// have to do it manually.
|
||||||
|
if (flags.once) {
|
||||||
|
emitter.removeEventListener(name, wrapListener);
|
||||||
|
}
|
||||||
|
listener(arg);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter);
|
||||||
|
}
|
||||||
|
}
|
||||||
85
static/options/index.html
Normal file
85
static/options/index.html
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Nebula</title>
|
||||||
|
|
||||||
|
<link rel="icon" type="image/x-icon" href="../images/fav.png">
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<!-- Stylesheets -->
|
||||||
|
<link rel="stylesheet" href="../style/master.css">
|
||||||
|
<link rel="stylesheet" href="../style/main.css">
|
||||||
|
<link rel="stylesheet" href="../style/options.css">
|
||||||
|
<!-- Embed -->
|
||||||
|
<meta name="theme-color" content="#5a189a">
|
||||||
|
<meta property=og:title content=Nebula>
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:url" content="https://thatonewebsite.mathhelpbeta.gq/images/fav.png" />
|
||||||
|
<meta property="og:image" content="https://thatonewebsite.mathhelpbeta.gq/images/fav.png" />
|
||||||
|
<meta property="og:description" content="Welcome To Nebula, Explore the Web. Freely." />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<!-- Scripts -->
|
||||||
|
<script src="../uv/uv.bundle.js"></script>
|
||||||
|
<script src="../uv/uv.config.js"></script>
|
||||||
|
<div>
|
||||||
|
</div>
|
||||||
|
<div id="navbar" style='background: none;'>
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting">
|
||||||
|
<div onclick="window.location ='/';" class="bk-btn">
|
||||||
|
<div onclick="window.location ='/';" class="bk-btn-triangle"></div>
|
||||||
|
<div onclick="window.location ='/';" class="bk-btn-bar"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-cont">
|
||||||
|
<p class="name"> Stealth Mode <b style="font-size:19px; color: rgb(226, 68, 68);"> NEW </b> </p>
|
||||||
|
<p class="description"> Prevents teachers, administrators and analytic collection extensions from gathering information, viewing your screen via Browser extension, or browser recording (history) </p>
|
||||||
|
|
||||||
|
<input class="setting-input" id="undefined" type="checkbox" onclick="saveIc();toggleNoGG()">
|
||||||
|
<label for="undefined" class="toogle-button"></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="settings-cont" style="display: inline-block;">
|
||||||
|
<p class="name"> Proxy <b style="font-size:19px; color: rgb(226, 68, 68);"> NEW </b> </p>
|
||||||
|
<p class="description"> Choose the proxy that fits you! </p>
|
||||||
|
|
||||||
|
<input class="setting-input" type="checkbox" onclick="saveIc();">
|
||||||
|
<label for="undefined" class="toogle-button"></label>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div id='sidebar'>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="stamp"> Version 7.0.4 (Public Release) | © Nebula Services 2021 - Present</p>
|
||||||
|
|
||||||
|
<!-- Panelbear -->
|
||||||
|
<script async src="https://cdn.panelbear.com/analytics.js?site=AZa75ZyiRRZ">
|
||||||
|
</script>
|
||||||
|
<script src="../resources/options.js">
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
window.panelbear = window.panelbear || function() {
|
||||||
|
(window.panelbear.q = window.panelbear.q || []).push(arguments);
|
||||||
|
};
|
||||||
|
panelbear('config', {
|
||||||
|
site: 'AZa75ZyiRRZ'
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
1
static/resources/deviceHandler.js
Normal file
1
static/resources/deviceHandler.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
(function(a,b){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))window.location=b})(navigator.userAgent||navigator.vendor||window.opera,'/mobile');
|
||||||
77
static/resources/form.js
Normal file
77
static/resources/form.js
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
var option = localStorage.getItem('nogg');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
|
||||||
|
|
||||||
|
function isUrl(val = '') {
|
||||||
|
if (/^http(s?):\/\//.test(val) || val.includes('.') && val.substr(0, 1) !== ' ') return true;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOGG
|
||||||
|
const useNoGG = false;
|
||||||
|
const form = document.querySelector('form');
|
||||||
|
form.addEventListener('submit', event => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (typeof navigator.serviceWorker === 'undefined')
|
||||||
|
alert('Your browser does not support service workers or you are in private browsing!');
|
||||||
|
|
||||||
|
navigator.serviceWorker.register('./sw.js', {
|
||||||
|
scope: __uv$config.prefix
|
||||||
|
}).then(() => {
|
||||||
|
const value = event.target.firstElementChild.value;
|
||||||
|
|
||||||
|
let url = value.trim();
|
||||||
|
if (!isUrl(url))
|
||||||
|
url = 'https://www.google.com/search?q=' + url;
|
||||||
|
else
|
||||||
|
if (!(url.startsWith('https://') || url.startsWith('http://'))) url = 'http://' + url;
|
||||||
|
const redirectTo = __uv$config.prefix + __uv$config.encodeUrl(url);
|
||||||
|
const option = localStorage.getItem('nogg');
|
||||||
|
if (option === 'on') {
|
||||||
|
stealthEngine(redirectTo);
|
||||||
|
} else location.href = redirectTo;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// NoGG Engine
|
||||||
|
function stealthEngine(encodedURL){
|
||||||
|
// The URL must be encoded ^
|
||||||
|
let inFrame
|
||||||
|
|
||||||
|
try {
|
||||||
|
inFrame = window !== top
|
||||||
|
} catch (e) {
|
||||||
|
inFrame = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inFrame && !navigator.userAgent.includes("Firefox")) {
|
||||||
|
const popup = open("about:blank", "_blank")
|
||||||
|
if (!popup || popup.closed) {
|
||||||
|
alert("Popups are disabled!")
|
||||||
|
} else {
|
||||||
|
const doc = popup.document
|
||||||
|
const iframe = doc.createElement("iframe")
|
||||||
|
const style = iframe.style
|
||||||
|
const img = doc.createElement("link")
|
||||||
|
const link = location.href
|
||||||
|
img.rel = "icon"
|
||||||
|
img.href = "https://ssl.gstatic.com/images/branding/product/1x/drive_2020q4_32dp.png"
|
||||||
|
doc.title = "Google Drive"
|
||||||
|
|
||||||
|
var currentLink = link.slice(0, link.length - 1);
|
||||||
|
|
||||||
|
iframe.src = currentLink + encodedURL
|
||||||
|
|
||||||
|
style.position = "fixed"
|
||||||
|
style.top = style.bottom = style.left = style.right = 0
|
||||||
|
style.border = style.outline = "none"
|
||||||
|
style.width = style.height = "100%"
|
||||||
|
|
||||||
|
doc.body.appendChild(iframe)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}});
|
||||||
52
static/resources/options.js
Normal file
52
static/resources/options.js
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
|
||||||
|
// Set the option
|
||||||
|
var option = localStorage.getItem('nogg')
|
||||||
|
function toggleNoGG() {
|
||||||
|
if (option === 'on') {
|
||||||
|
|
||||||
|
option = 'off';
|
||||||
|
localStorage.setItem('nogg', 'off');
|
||||||
|
} else {
|
||||||
|
|
||||||
|
option = 'on';
|
||||||
|
localStorage.setItem('nogg', 'on');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(localStorage.getItem('nogg'))
|
||||||
|
|
||||||
|
|
||||||
|
// Notification Banner
|
||||||
|
function saveIc() {
|
||||||
|
console.log("Checked")
|
||||||
|
var notification = `
|
||||||
|
<div class="notification-container" id="notification-container">
|
||||||
|
<div class="notification notification-success">
|
||||||
|
<strong>Success:</strong> Your settings have been saved!
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
document.getElementById('navbar').innerHTML = notification
|
||||||
|
setTimeout(() => {
|
||||||
|
var NotificationOBJ = document.getElementById('notif')
|
||||||
|
|
||||||
|
}, 2000);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Update the CheckBox to match the settings
|
||||||
|
window.onload = function() {
|
||||||
|
console.log("Current Settings: ")
|
||||||
|
console.log("NoGG = ", localStorage.getItem('nogg'))
|
||||||
|
|
||||||
|
if (localStorage.getItem('nogg') == 'on') {
|
||||||
|
setTimeout(() => {
|
||||||
|
var item = document.getElementById("undefined");
|
||||||
|
document.getElementById("undefined").checked = true;}, 600);
|
||||||
|
|
||||||
|
}};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
197
static/style/main.css
Normal file
197
static/style/main.css
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
:root {
|
||||||
|
--background-primary: #191724;
|
||||||
|
--navbar-color: #26233a;
|
||||||
|
--navbar-height: 4em;
|
||||||
|
--navbar-text-color: #e0def4;
|
||||||
|
--input-text-color: #e0def4;
|
||||||
|
--input-placeholder-color: #6e6a86;
|
||||||
|
--input-background-color: #1f1d2e;
|
||||||
|
--input-border-color: #eb6f92;
|
||||||
|
--input-border-size: 0.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-input-placeholder {
|
||||||
|
text-align: center;
|
||||||
|
font-family: 'Roboto';
|
||||||
|
}
|
||||||
|
|
||||||
|
:-moz-placeholder {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar {
|
||||||
|
height: 60px;
|
||||||
|
text-align: center;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: var(--navbar-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: white;
|
||||||
|
text-decoration: none !important;
|
||||||
|
font-family: 'Roboto';
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: grey;
|
||||||
|
transition: 0.5s;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.down {
|
||||||
|
background-color: rgb(90, 24, 154);
|
||||||
|
left: inherit !important;
|
||||||
|
font-family: 'Helvetica';
|
||||||
|
background-color: var(--navbar-color);
|
||||||
|
/* box-shadow: 2px 2px rgb(0 0 0 / 20%); */
|
||||||
|
color: white;
|
||||||
|
display: none;
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
position: fixed;
|
||||||
|
border-bottom-right-radius: 10px;
|
||||||
|
border-bottom-left-radius: 10px;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar ul:not(.down) {
|
||||||
|
font-family: 'Helvetica';
|
||||||
|
background-color: var(--navbar-color);
|
||||||
|
/* box-shadow: 2px 2px rgb(0 0 0 / 20%); */
|
||||||
|
color: white;
|
||||||
|
margin-left: auto;
|
||||||
|
list-style-type: none;
|
||||||
|
float: right !important;
|
||||||
|
margin-right: 5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar ul li {
|
||||||
|
float: left;
|
||||||
|
padding-top: 1em;
|
||||||
|
padding-bottom: 1em;
|
||||||
|
padding-right: 1em;
|
||||||
|
padding-left: 1em;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
::placeholder,
|
||||||
|
input[type='text'] {
|
||||||
|
color: white;
|
||||||
|
font-size: 20px;
|
||||||
|
text-align: center;
|
||||||
|
font-family: 'Roboto';
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar ul p,
|
||||||
|
ul a {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
#navbar ul li p, ul li a {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#navbar ul li ul {
|
||||||
|
border-bottom-right-radius: 10px;
|
||||||
|
border-bottom-left-radius: 10px;
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
margin-top: 1rem;
|
||||||
|
left: 0;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar ul li:hover ul,
|
||||||
|
ul li ul:hover {
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar ul li p {
|
||||||
|
color: white;
|
||||||
|
font-family: 'Calibri';
|
||||||
|
z-index: 3 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar ul li ul li {
|
||||||
|
clear: both;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 98%;
|
||||||
|
color: white;
|
||||||
|
flex-direction: column;
|
||||||
|
font-family: 'Roboto';
|
||||||
|
}
|
||||||
|
|
||||||
|
#content h1 {
|
||||||
|
padding-bottom: 0.5em;
|
||||||
|
font-weight: 100;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content img {
|
||||||
|
padding-left: .5em;
|
||||||
|
filter: brightness(0) invert(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#content input {
|
||||||
|
font-size: 20px;
|
||||||
|
text-align: center;
|
||||||
|
font-family: 'Calibri';
|
||||||
|
border-style: solid !important;
|
||||||
|
border: 1.3px solid var(--input-border-color);
|
||||||
|
border-width: 1px;
|
||||||
|
border-radius: 15px;
|
||||||
|
background-color: var(--input-background-color);
|
||||||
|
color: var(--input-text-color);
|
||||||
|
width: 300px;
|
||||||
|
height: 50px;
|
||||||
|
box-shadow: none !important;
|
||||||
|
outline: none;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
font-family: 'Roboto';
|
||||||
|
animation-name: inputwide;
|
||||||
|
animation-duration: 2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
--input-text-color: #e0def4;
|
||||||
|
--input-placeholder-color: #6e6a86;
|
||||||
|
--input-background-color: #1f1d2e;
|
||||||
|
--input-border-color: #eb6f92;
|
||||||
|
--input-border-size: 0.1em;
|
||||||
|
*/
|
||||||
|
|
||||||
|
#content input:focus {
|
||||||
|
/* outline: 1.3px solid #eb6f9; !important;*/
|
||||||
|
outline: none;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes inputwide {
|
||||||
|
0% {
|
||||||
|
width: 0px;
|
||||||
|
transition-duration: 0.5s;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
width: 300px;
|
||||||
|
transition-duration: 0.5s;
|
||||||
|
}
|
||||||
|
}
|
||||||
74
static/style/master.css
Normal file
74
static/style/master.css
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Dongle&family=Roboto:wght@100&display=swap");
|
||||||
|
:root {
|
||||||
|
--background-primary: #191724;
|
||||||
|
--text-color-primary: #31748f;
|
||||||
|
--text-color-secondary: #9ccfd8;
|
||||||
|
--header-color: #26233a;
|
||||||
|
--header-height: 4em;
|
||||||
|
--header-text-color: #e0def4;
|
||||||
|
--input-text-color: white;
|
||||||
|
--input-placeholder-color: #6e6a86;
|
||||||
|
--input-background-color: #1f1d2e;
|
||||||
|
--input-border-color: #eb6f92;
|
||||||
|
--input-border-size: 0.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
cursor: url("../images/cur.gif"), url("../images/cursor.cur"), default;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--background-primary);
|
||||||
|
color: var(--text-color-primary);
|
||||||
|
animation: fadeInAnimation ease 3s;
|
||||||
|
animation-iteration-count: 1;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInAnimation {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus::placeholder {
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar #thumbImg {
|
||||||
|
transition: width 2s, height 2s, transform 2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar #thumbImg:hover {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@1,100&display=swap');
|
||||||
|
.stamp {
|
||||||
|
text-align: left;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
font-family: 'Montserrat', sans-serif;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: lighter;
|
||||||
|
color: whitesmoke;
|
||||||
|
opacity: 38%;
|
||||||
|
user-select: none;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
328
static/style/options.css
Normal file
328
static/style/options.css
Normal file
|
|
@ -0,0 +1,328 @@
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Work+Sans:wght@300&display=swap");
|
||||||
|
:root {
|
||||||
|
--background-primary: #191724;
|
||||||
|
--sidebar-color: #191724;
|
||||||
|
--sidebar-text-color: #e0def4;
|
||||||
|
--text-color-primary: #e0def4;
|
||||||
|
--text-color-secondary: #6e6a86;
|
||||||
|
--focus-color: #eb6f92;
|
||||||
|
--header-height: 10vh;
|
||||||
|
--section-font-size: 20pt;
|
||||||
|
--section-font: 'Calibri';
|
||||||
|
--section-padding: 0.5em;
|
||||||
|
--setting-distance-from-sidebar: 1em;
|
||||||
|
--setting-distance-from-right: 1em;
|
||||||
|
--setting-name-font: 'Calibri';
|
||||||
|
--setting-desc-font: 'Calibri';
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
user-select: none;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
body {
|
||||||
|
background-color: var(--background-primary);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
#sidebar {
|
||||||
|
animation: fadeIn 750ms ease-in 300ms forwards;
|
||||||
|
-webkit-animation: fadeIn 750ms ease-in 300ms forwards;
|
||||||
|
position: absolute;
|
||||||
|
top: var(--header-height);
|
||||||
|
left: 0;
|
||||||
|
background-color: var(--sidebar-color);
|
||||||
|
transition: width 0.5s;
|
||||||
|
width: var(--sidebar-width);
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting li a {
|
||||||
|
background-color: #2e2828;
|
||||||
|
padding: 10px 16px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-div li a ul {
|
||||||
|
border-bottom-right-radius: 10px;
|
||||||
|
border-bottom-left-radius: 10px;
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
margin-top: 10px;
|
||||||
|
left: 0 !important;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-div li a:hover ul,
|
||||||
|
ul li:hover {
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
.settings-div ul li p{
|
||||||
|
color: white;
|
||||||
|
font-family: 'Calibri';
|
||||||
|
z-index: 3 !important;
|
||||||
|
|
||||||
|
}*/
|
||||||
|
|
||||||
|
li {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-div li a ul li {
|
||||||
|
clear: both;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--sidebar-text-color);
|
||||||
|
font-size: var(--section-font-size);
|
||||||
|
font-family: var(--section-font);
|
||||||
|
width: 100%;
|
||||||
|
transition: background-color 0.5s;
|
||||||
|
padding-top: var(--section-padding);
|
||||||
|
padding-bottom: var(--section-padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section:hover {
|
||||||
|
background-color: #ffffff20;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-div {
|
||||||
|
position: absolute;
|
||||||
|
left: calc(var(--sidebar-width) + var(--setting-distance-from-sidebar));
|
||||||
|
top: 0;
|
||||||
|
width: calc(100vw - var(--sidebar-width) - var(--setting-distance-from-sidebar));
|
||||||
|
padding-top: 5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-input {
|
||||||
|
/* left: 100%; */
|
||||||
|
color: black !important;
|
||||||
|
position: relative;
|
||||||
|
/* right: 0; */
|
||||||
|
transform: translateY(-1.5em);
|
||||||
|
float: right;
|
||||||
|
margin-right: 5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Mulish:wght@300&display=swap');
|
||||||
|
|
||||||
|
.toogle-button {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 10PX;
|
||||||
|
display: inline-block;
|
||||||
|
width: 75px;
|
||||||
|
height: 35px;
|
||||||
|
background-color: #dfddf3;
|
||||||
|
border-radius: 30px;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.toogle-button::after {
|
||||||
|
content: 'Off';
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
color: #E7E2CD;
|
||||||
|
background-color: #e14343;
|
||||||
|
border: 2px solid #E7E2CD;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: 0 0 5px rgb(0 0 0 / 25%);
|
||||||
|
position: absolute;
|
||||||
|
top: -3px;
|
||||||
|
left: 0;
|
||||||
|
line-height: 0;
|
||||||
|
display: grid;
|
||||||
|
place-content: center;
|
||||||
|
transition: all .5s;
|
||||||
|
transform: 1s ease-in;
|
||||||
|
font-family: 'Mulish', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-input:checked + .toogle-button::after {
|
||||||
|
content: 'On';
|
||||||
|
background-color: #53b357;
|
||||||
|
transform: translateX(35px) rotate(360deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul li {
|
||||||
|
float: left;
|
||||||
|
padding-top: 1em;
|
||||||
|
padding-bottom: 1em;
|
||||||
|
padding-right: 1em;
|
||||||
|
padding-left: 1em;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
transform: translateY(-30px);
|
||||||
|
justify-self: end;
|
||||||
|
font-family: var(--setting-desc-font);
|
||||||
|
align-self: flex-end;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes fadeIn {
|
||||||
|
0% {
|
||||||
|
opacity: 0.01;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
0% {
|
||||||
|
opacity: 0.01;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@import url('https://fonts.googleapis.com/css?family=Roboto:400,500&display=swap');
|
||||||
|
|
||||||
|
.notification-container {
|
||||||
|
position: fixed;
|
||||||
|
top: 15px;
|
||||||
|
right: 15px;
|
||||||
|
width: 500px;
|
||||||
|
max-width: calc(100% - 30px);
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 15px 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
animation: grow 0.5s ease-in forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes grow {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.8);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification.hide {
|
||||||
|
animation: shrink 0.3s ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shrink {
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification strong {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 20px;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-info {
|
||||||
|
background-color: #00cae3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-success {
|
||||||
|
background-color: #55b559;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-warning {
|
||||||
|
background-color: #ff9e0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-danger {
|
||||||
|
background-color: #f55145;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@keyframes fade-in {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stamp {
|
||||||
|
text-align: left;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
.bk-btn {
|
||||||
|
height: 52px;
|
||||||
|
width: 52px;
|
||||||
|
background-color: black;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
.bk-btn .bk-btn-triangle {
|
||||||
|
position: relative;
|
||||||
|
top: 13px;
|
||||||
|
left: 10.4px;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-top: 13px solid transparent;
|
||||||
|
border-bottom: 13px solid transparent;
|
||||||
|
border-right: 13px solid white;
|
||||||
|
}
|
||||||
|
.bk-btn .bk-btn-bar {
|
||||||
|
position: relative;
|
||||||
|
background-color: white;
|
||||||
|
height: 7.8px;
|
||||||
|
width: 13px;
|
||||||
|
top: -3.64px;
|
||||||
|
left: 22.88px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-cont {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 300px;
|
||||||
|
height: 246px;
|
||||||
|
padding: 30px;
|
||||||
|
border: 2px solid rgb(121 103 221);
|
||||||
|
border-radius: 9px;
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
color: var(--text-color-primary);
|
||||||
|
font-size: 2vmax;
|
||||||
|
margin: 0;
|
||||||
|
font-family: var(--setting-name-font);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
color: var(--text-color-secondary);
|
||||||
|
margin: 0;
|
||||||
|
font-size: 17px;
|
||||||
|
font-family: var(--setting-desc-font);
|
||||||
|
}
|
||||||
5
static/sw.js
Normal file
5
static/sw.js
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
importScripts('./uv/uv.sw.js');
|
||||||
|
|
||||||
|
const sw = new UVServiceWorker();
|
||||||
|
|
||||||
|
self.addEventListener('fetch', event => event.respondWith(sw.fetch(event)));
|
||||||
39304
static/uv/uv.bundle.js
Normal file
39304
static/uv/uv.bundle.js
Normal file
File diff suppressed because one or more lines are too long
10
static/uv/uv.config.js
Normal file
10
static/uv/uv.config.js
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
self.__uv$config = {
|
||||||
|
prefix: '/go/',
|
||||||
|
bare: '/bare/',
|
||||||
|
encodeUrl: Ultraviolet.codec.xor.encode,
|
||||||
|
decodeUrl: Ultraviolet.codec.xor.decode,
|
||||||
|
handler: '/uv/uv.handler.js',
|
||||||
|
bundle: '/uv/uv.bundle.js',
|
||||||
|
config: '/uv/uv.config.js',
|
||||||
|
sw: '/uv/uv.sw.js',
|
||||||
|
};
|
||||||
1129
static/uv/uv.handler.js
Normal file
1129
static/uv/uv.handler.js
Normal file
File diff suppressed because it is too large
Load diff
789
static/uv/uv.sw.js
Normal file
789
static/uv/uv.sw.js
Normal file
|
|
@ -0,0 +1,789 @@
|
||||||
|
importScripts('/uv/uv.bundle.js');
|
||||||
|
importScripts('/uv/uv.config.js');
|
||||||
|
|
||||||
|
class UVServiceWorker extends EventEmitter {
|
||||||
|
constructor(config = __uv$config) {
|
||||||
|
super();
|
||||||
|
if (!config.bare) config.bare = '/bare/';
|
||||||
|
this.addresses = typeof config.bare === 'string' ? [ new URL(config.bare, location) ] : config.bare.map(str => new URL(str, location));
|
||||||
|
this.headers = {
|
||||||
|
csp: [
|
||||||
|
'cross-origin-embedder-policy',
|
||||||
|
'cross-origin-opener-policy',
|
||||||
|
'cross-origin-resource-policy',
|
||||||
|
'content-security-policy',
|
||||||
|
'content-security-policy-report-only',
|
||||||
|
'expect-ct',
|
||||||
|
'feature-policy',
|
||||||
|
'origin-isolation',
|
||||||
|
'strict-transport-security',
|
||||||
|
'upgrade-insecure-requests',
|
||||||
|
'x-content-type-options',
|
||||||
|
'x-download-options',
|
||||||
|
'x-frame-options',
|
||||||
|
'x-permitted-cross-domain-policies',
|
||||||
|
'x-powered-by',
|
||||||
|
'x-xss-protection',
|
||||||
|
],
|
||||||
|
forward: [
|
||||||
|
'accept-encoding',
|
||||||
|
'connection',
|
||||||
|
'content-length',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
this.method = {
|
||||||
|
empty: [
|
||||||
|
'GET',
|
||||||
|
'HEAD'
|
||||||
|
]
|
||||||
|
};
|
||||||
|
this.statusCode = {
|
||||||
|
empty: [
|
||||||
|
204,
|
||||||
|
304,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
this.config = config;
|
||||||
|
this.browser = Ultraviolet.Bowser.getParser(self.navigator.userAgent).getBrowserName();
|
||||||
|
|
||||||
|
if (this.browser === 'Firefox') {
|
||||||
|
this.headers.forward.push('user-agent');
|
||||||
|
this.headers.forward.push('content-type');
|
||||||
|
};
|
||||||
|
};
|
||||||
|
async fetch({ request }) {
|
||||||
|
if (!request.url.startsWith(location.origin + (this.config.prefix || '/service/'))) {
|
||||||
|
return fetch(request);
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
|
||||||
|
const ultraviolet = new Ultraviolet(this.config);
|
||||||
|
|
||||||
|
if (typeof this.config.construct === 'function') {
|
||||||
|
this.config.construct(ultraviolet, 'service');
|
||||||
|
};
|
||||||
|
|
||||||
|
const db = await ultraviolet.cookie.db();
|
||||||
|
|
||||||
|
ultraviolet.meta.origin = location.origin;
|
||||||
|
ultraviolet.meta.base = ultraviolet.meta.url = new URL(ultraviolet.sourceUrl(request.url));
|
||||||
|
|
||||||
|
const requestCtx = new RequestContext(
|
||||||
|
request,
|
||||||
|
this,
|
||||||
|
ultraviolet,
|
||||||
|
!this.method.empty.includes(request.method.toUpperCase()) ? await request.blob() : null
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ultraviolet.meta.url.protocol === 'blob:') {
|
||||||
|
requestCtx.blob = true;
|
||||||
|
requestCtx.base = requestCtx.url = new URL(requestCtx.url.pathname);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (request.referrer && request.referrer.startsWith(location.origin)) {
|
||||||
|
const referer = new URL(ultraviolet.sourceUrl(request.referrer));
|
||||||
|
|
||||||
|
if (requestCtx.headers.origin || ultraviolet.meta.url.origin !== referer.origin && request.mode === 'cors') {
|
||||||
|
requestCtx.headers.origin = referer.origin;
|
||||||
|
};
|
||||||
|
|
||||||
|
requestCtx.headers.referer = referer.href;
|
||||||
|
};
|
||||||
|
|
||||||
|
const cookies = await ultraviolet.cookie.getCookies(db) || [];
|
||||||
|
const cookieStr = ultraviolet.cookie.serialize(cookies, ultraviolet.meta, false);
|
||||||
|
|
||||||
|
if (this.browser === 'Firefox' && !(request.destination === 'iframe' || request.destination === 'document')) {
|
||||||
|
requestCtx.forward.shift();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (cookieStr) requestCtx.headers.cookie = cookieStr;
|
||||||
|
requestCtx.headers.Host = requestCtx.url.host;
|
||||||
|
|
||||||
|
|
||||||
|
const reqEvent = new HookEvent(requestCtx, null, null);
|
||||||
|
this.emit('request', reqEvent);
|
||||||
|
|
||||||
|
if (reqEvent.intercepted) return reqEvent.returnValue;
|
||||||
|
|
||||||
|
const response = await fetch(requestCtx.send);
|
||||||
|
|
||||||
|
if (response.status === 500) {
|
||||||
|
return Promise.reject('');
|
||||||
|
};
|
||||||
|
|
||||||
|
const responseCtx = new ResponseContext(requestCtx, response, this);
|
||||||
|
const resEvent = new HookEvent(responseCtx, null, null);
|
||||||
|
|
||||||
|
this.emit('beforemod', resEvent);
|
||||||
|
if (resEvent.intercepted) return resEvent.returnValue;
|
||||||
|
|
||||||
|
for (const name of this.headers.csp) {
|
||||||
|
if (responseCtx.headers[name]) delete responseCtx.headers[name];
|
||||||
|
};
|
||||||
|
|
||||||
|
if (responseCtx.headers.location) {
|
||||||
|
responseCtx.headers.location = ultraviolet.rewriteUrl(responseCtx.headers.location);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (responseCtx.headers['set-cookie']) {
|
||||||
|
Promise.resolve(ultraviolet.cookie.setCookies(responseCtx.headers['set-cookie'], db, ultraviolet.meta)).then(() => {
|
||||||
|
self.clients.matchAll().then(function (clients){
|
||||||
|
clients.forEach(function(client){
|
||||||
|
client.postMessage({
|
||||||
|
msg: 'updateCookies',
|
||||||
|
url: ultraviolet.meta.url.href,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
delete responseCtx.headers['set-cookie'];
|
||||||
|
};
|
||||||
|
|
||||||
|
if (responseCtx.body) {
|
||||||
|
switch(request.destination) {
|
||||||
|
case 'script':
|
||||||
|
case 'worker':
|
||||||
|
responseCtx.body = `if (!self.__uv && self.importScripts) importScripts('${__uv$config.bundle}', '${__uv$config.config}', '${__uv$config.handler}');\n`;
|
||||||
|
responseCtx.body += ultraviolet.js.rewrite(
|
||||||
|
await response.text()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'style':
|
||||||
|
responseCtx.body = ultraviolet.rewriteCSS(
|
||||||
|
await response.text()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'iframe':
|
||||||
|
case 'document':
|
||||||
|
if (isHtml(ultraviolet.meta.url, (responseCtx.headers['content-type'] || ''))) {
|
||||||
|
responseCtx.body = ultraviolet.rewriteHtml(
|
||||||
|
await response.text(),
|
||||||
|
{
|
||||||
|
document: true ,
|
||||||
|
injectHead: ultraviolet.createHtmlInject(
|
||||||
|
this.config.handler,
|
||||||
|
this.config.bundle,
|
||||||
|
this.config.config,
|
||||||
|
ultraviolet.cookie.serialize(cookies, ultraviolet.meta, true),
|
||||||
|
request.referrer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
if (requestCtx.headers.accept === 'text/event-stream') {
|
||||||
|
responseCtx.headers['content-type'] = 'text/event-stream';
|
||||||
|
};
|
||||||
|
|
||||||
|
this.emit('response', resEvent);
|
||||||
|
if (resEvent.intercepted) return resEvent.returnValue;
|
||||||
|
|
||||||
|
return new Response(responseCtx.body, {
|
||||||
|
headers: responseCtx.headers,
|
||||||
|
status: responseCtx.status,
|
||||||
|
statusText: responseCtx.statusText,
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch(err) {
|
||||||
|
return new Response(err.toString(), {
|
||||||
|
status: 500,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
getBarerResponse(response) {
|
||||||
|
const headers = {};
|
||||||
|
const raw = JSON.parse(response.headers.get('x-bare-headers'));
|
||||||
|
|
||||||
|
for (const key in raw) {
|
||||||
|
headers[key.toLowerCase()] = raw[key];
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
headers,
|
||||||
|
status: +response.headers.get('x-bare-status'),
|
||||||
|
statusText: response.headers.get('x-bare-status-text'),
|
||||||
|
body: !this.statusCode.empty.includes(+response.headers.get('x-bare-status')) ? response.body : null,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
get address() {
|
||||||
|
return this.addresses[Math.floor(Math.random() * this.addresses.length)];
|
||||||
|
};
|
||||||
|
static Ultraviolet = Ultraviolet;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.UVServiceWorker = UVServiceWorker;
|
||||||
|
|
||||||
|
|
||||||
|
class ResponseContext {
|
||||||
|
constructor(request, response, worker) {
|
||||||
|
const { headers, status, statusText, body } = !request.blob ? worker.getBarerResponse(response) : {
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
headers: Object.fromEntries([...response.headers.entries()]),
|
||||||
|
body: response.body,
|
||||||
|
};
|
||||||
|
this.request = request;
|
||||||
|
this.raw = response;
|
||||||
|
this.ultraviolet = request.ultraviolet;
|
||||||
|
this.headers = headers;
|
||||||
|
this.status = status;
|
||||||
|
this.statusText = statusText;
|
||||||
|
this.body = body;
|
||||||
|
};
|
||||||
|
get url() {
|
||||||
|
return this.request.url;
|
||||||
|
}
|
||||||
|
get base() {
|
||||||
|
return this.request.base;
|
||||||
|
};
|
||||||
|
set base(val) {
|
||||||
|
this.request.base = val;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class RequestContext {
|
||||||
|
constructor(request, worker, ultraviolet, body = null) {
|
||||||
|
this.ultraviolet = ultraviolet;
|
||||||
|
this.request = request;
|
||||||
|
this.headers = Object.fromEntries([...request.headers.entries()]);
|
||||||
|
this.method = request.method;
|
||||||
|
this.forward = [...worker.headers.forward];
|
||||||
|
this.address = worker.address;
|
||||||
|
this.body = body || null;
|
||||||
|
this.redirect = request.redirect;
|
||||||
|
this.credentials = 'omit';
|
||||||
|
this.mode = request.mode === 'cors' ? request.mode : 'same-origin';
|
||||||
|
this.blob = false;
|
||||||
|
};
|
||||||
|
get send() {
|
||||||
|
return new Request((!this.blob ? this.address.href + 'v1/' : 'blob:' + location.origin + this.url.pathname), {
|
||||||
|
method: this.method,
|
||||||
|
headers: {
|
||||||
|
'x-bare-protocol': this.url.protocol,
|
||||||
|
'x-bare-host': this.url.hostname,
|
||||||
|
'x-bare-path': this.url.pathname + this.url.search,
|
||||||
|
'x-bare-port': this.url.port || (this.url.protocol === 'https:' ? '443' : '80'),
|
||||||
|
'x-bare-headers': JSON.stringify(this.headers),
|
||||||
|
'x-bare-forward-headers': JSON.stringify(this.forward),
|
||||||
|
},
|
||||||
|
redirect: this.redirect,
|
||||||
|
credentials: this.credentials,
|
||||||
|
mode: location.origin !== this.address.origin ? 'cors' : this.mode,
|
||||||
|
body: this.body
|
||||||
|
});
|
||||||
|
};
|
||||||
|
get url() {
|
||||||
|
return this.ultraviolet.meta.url;
|
||||||
|
};
|
||||||
|
set url(val) {
|
||||||
|
this.ultraviolet.meta.url = val;
|
||||||
|
};
|
||||||
|
get base() {
|
||||||
|
return this.ultraviolet.meta.base;
|
||||||
|
};
|
||||||
|
set base(val) {
|
||||||
|
this.ultraviolet.meta.base = val;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function isHtml(url, contentType = '') {
|
||||||
|
return (Ultraviolet.mime.contentType((contentType || url.pathname)) || 'text/html').split(';')[0] === 'text/html';
|
||||||
|
};
|
||||||
|
|
||||||
|
class HookEvent {
|
||||||
|
#intercepted;
|
||||||
|
#returnValue;
|
||||||
|
constructor(data = {}, target = null, that = null) {
|
||||||
|
this.#intercepted = false;
|
||||||
|
this.#returnValue = null;
|
||||||
|
this.data = data;
|
||||||
|
this.target = target;
|
||||||
|
this.that = that;
|
||||||
|
};
|
||||||
|
get intercepted() {
|
||||||
|
return this.#intercepted;
|
||||||
|
};
|
||||||
|
get returnValue() {
|
||||||
|
return this.#returnValue;
|
||||||
|
};
|
||||||
|
respondWith(input) {
|
||||||
|
this.#returnValue = input;
|
||||||
|
this.#intercepted = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var R = typeof Reflect === 'object' ? Reflect : null
|
||||||
|
var ReflectApply = R && typeof R.apply === 'function'
|
||||||
|
? R.apply
|
||||||
|
: function ReflectApply(target, receiver, args) {
|
||||||
|
return Function.prototype.apply.call(target, receiver, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
var ReflectOwnKeys
|
||||||
|
if (R && typeof R.ownKeys === 'function') {
|
||||||
|
ReflectOwnKeys = R.ownKeys
|
||||||
|
} else if (Object.getOwnPropertySymbols) {
|
||||||
|
ReflectOwnKeys = function ReflectOwnKeys(target) {
|
||||||
|
return Object.getOwnPropertyNames(target)
|
||||||
|
.concat(Object.getOwnPropertySymbols(target));
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
ReflectOwnKeys = function ReflectOwnKeys(target) {
|
||||||
|
return Object.getOwnPropertyNames(target);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function ProcessEmitWarning(warning) {
|
||||||
|
if (console && console.warn) console.warn(warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {
|
||||||
|
return value !== value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function EventEmitter() {
|
||||||
|
EventEmitter.init.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backwards-compat with node 0.10.x
|
||||||
|
EventEmitter.EventEmitter = EventEmitter;
|
||||||
|
|
||||||
|
EventEmitter.prototype._events = undefined;
|
||||||
|
EventEmitter.prototype._eventsCount = 0;
|
||||||
|
EventEmitter.prototype._maxListeners = undefined;
|
||||||
|
|
||||||
|
// By default EventEmitters will print a warning if more than 10 listeners are
|
||||||
|
// added to it. This is a useful default which helps finding memory leaks.
|
||||||
|
var defaultMaxListeners = 10;
|
||||||
|
|
||||||
|
function checkListener(listener) {
|
||||||
|
if (typeof listener !== 'function') {
|
||||||
|
throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
|
||||||
|
enumerable: true,
|
||||||
|
get: function() {
|
||||||
|
return defaultMaxListeners;
|
||||||
|
},
|
||||||
|
set: function(arg) {
|
||||||
|
if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
|
||||||
|
throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.');
|
||||||
|
}
|
||||||
|
defaultMaxListeners = arg;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
EventEmitter.init = function() {
|
||||||
|
|
||||||
|
if (this._events === undefined ||
|
||||||
|
this._events === Object.getPrototypeOf(this)._events) {
|
||||||
|
this._events = Object.create(null);
|
||||||
|
this._eventsCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._maxListeners = this._maxListeners || undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Obviously not all Emitters should be limited to 10. This function allows
|
||||||
|
// that to be increased. Set to zero for unlimited.
|
||||||
|
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
|
||||||
|
if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {
|
||||||
|
throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.');
|
||||||
|
}
|
||||||
|
this._maxListeners = n;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
function _getMaxListeners(that) {
|
||||||
|
if (that._maxListeners === undefined)
|
||||||
|
return EventEmitter.defaultMaxListeners;
|
||||||
|
return that._maxListeners;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
|
||||||
|
return _getMaxListeners(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
EventEmitter.prototype.emit = function emit(type) {
|
||||||
|
var args = [];
|
||||||
|
for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
|
||||||
|
var doError = (type === 'error');
|
||||||
|
|
||||||
|
var events = this._events;
|
||||||
|
if (events !== undefined)
|
||||||
|
doError = (doError && events.error === undefined);
|
||||||
|
else if (!doError)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If there is no 'error' event listener then throw.
|
||||||
|
if (doError) {
|
||||||
|
var er;
|
||||||
|
if (args.length > 0)
|
||||||
|
er = args[0];
|
||||||
|
if (er instanceof Error) {
|
||||||
|
// Note: The comments on the `throw` lines are intentional, they show
|
||||||
|
// up in Node's output if this results in an unhandled exception.
|
||||||
|
throw er; // Unhandled 'error' event
|
||||||
|
}
|
||||||
|
// At least give some kind of context to the user
|
||||||
|
var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));
|
||||||
|
err.context = er;
|
||||||
|
throw err; // Unhandled 'error' event
|
||||||
|
}
|
||||||
|
|
||||||
|
var handler = events[type];
|
||||||
|
|
||||||
|
if (handler === undefined)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (typeof handler === 'function') {
|
||||||
|
ReflectApply(handler, this, args);
|
||||||
|
} else {
|
||||||
|
var len = handler.length;
|
||||||
|
var listeners = arrayClone(handler, len);
|
||||||
|
for (var i = 0; i < len; ++i)
|
||||||
|
ReflectApply(listeners[i], this, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
function _addListener(target, type, listener, prepend) {
|
||||||
|
var m;
|
||||||
|
var events;
|
||||||
|
var existing;
|
||||||
|
|
||||||
|
checkListener(listener);
|
||||||
|
|
||||||
|
events = target._events;
|
||||||
|
if (events === undefined) {
|
||||||
|
events = target._events = Object.create(null);
|
||||||
|
target._eventsCount = 0;
|
||||||
|
} else {
|
||||||
|
// To avoid recursion in the case that type === "newListener"! Before
|
||||||
|
// adding it to the listeners, first emit "newListener".
|
||||||
|
if (events.newListener !== undefined) {
|
||||||
|
target.emit('newListener', type,
|
||||||
|
listener.listener ? listener.listener : listener);
|
||||||
|
|
||||||
|
// Re-assign `events` because a newListener handler could have caused the
|
||||||
|
// this._events to be assigned to a new object
|
||||||
|
events = target._events;
|
||||||
|
}
|
||||||
|
existing = events[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existing === undefined) {
|
||||||
|
// Optimize the case of one listener. Don't need the extra array object.
|
||||||
|
existing = events[type] = listener;
|
||||||
|
++target._eventsCount;
|
||||||
|
} else {
|
||||||
|
if (typeof existing === 'function') {
|
||||||
|
// Adding the second element, need to change to array.
|
||||||
|
existing = events[type] =
|
||||||
|
prepend ? [listener, existing] : [existing, listener];
|
||||||
|
// If we've already got an array, just append.
|
||||||
|
} else if (prepend) {
|
||||||
|
existing.unshift(listener);
|
||||||
|
} else {
|
||||||
|
existing.push(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for listener leak
|
||||||
|
m = _getMaxListeners(target);
|
||||||
|
if (m > 0 && existing.length > m && !existing.warned) {
|
||||||
|
existing.warned = true;
|
||||||
|
// No error code for this since it is a Warning
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
var w = new Error('Possible EventEmitter memory leak detected. ' +
|
||||||
|
existing.length + ' ' + String(type) + ' listeners ' +
|
||||||
|
'added. Use emitter.setMaxListeners() to ' +
|
||||||
|
'increase limit');
|
||||||
|
w.name = 'MaxListenersExceededWarning';
|
||||||
|
w.emitter = target;
|
||||||
|
w.type = type;
|
||||||
|
w.count = existing.length;
|
||||||
|
ProcessEmitWarning(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventEmitter.prototype.addListener = function addListener(type, listener) {
|
||||||
|
return _addListener(this, type, listener, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
|
||||||
|
|
||||||
|
EventEmitter.prototype.prependListener =
|
||||||
|
function prependListener(type, listener) {
|
||||||
|
return _addListener(this, type, listener, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
function onceWrapper() {
|
||||||
|
if (!this.fired) {
|
||||||
|
this.target.removeListener(this.type, this.wrapFn);
|
||||||
|
this.fired = true;
|
||||||
|
if (arguments.length === 0)
|
||||||
|
return this.listener.call(this.target);
|
||||||
|
return this.listener.apply(this.target, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _onceWrap(target, type, listener) {
|
||||||
|
var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
|
||||||
|
var wrapped = onceWrapper.bind(state);
|
||||||
|
wrapped.listener = listener;
|
||||||
|
state.wrapFn = wrapped;
|
||||||
|
return wrapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventEmitter.prototype.once = function once(type, listener) {
|
||||||
|
checkListener(listener);
|
||||||
|
this.on(type, _onceWrap(this, type, listener));
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
EventEmitter.prototype.prependOnceListener =
|
||||||
|
function prependOnceListener(type, listener) {
|
||||||
|
checkListener(listener);
|
||||||
|
this.prependListener(type, _onceWrap(this, type, listener));
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Emits a 'removeListener' event if and only if the listener was removed.
|
||||||
|
EventEmitter.prototype.removeListener =
|
||||||
|
function removeListener(type, listener) {
|
||||||
|
var list, events, position, i, originalListener;
|
||||||
|
|
||||||
|
checkListener(listener);
|
||||||
|
|
||||||
|
events = this._events;
|
||||||
|
if (events === undefined)
|
||||||
|
return this;
|
||||||
|
|
||||||
|
list = events[type];
|
||||||
|
if (list === undefined)
|
||||||
|
return this;
|
||||||
|
|
||||||
|
if (list === listener || list.listener === listener) {
|
||||||
|
if (--this._eventsCount === 0)
|
||||||
|
this._events = Object.create(null);
|
||||||
|
else {
|
||||||
|
delete events[type];
|
||||||
|
if (events.removeListener)
|
||||||
|
this.emit('removeListener', type, list.listener || listener);
|
||||||
|
}
|
||||||
|
} else if (typeof list !== 'function') {
|
||||||
|
position = -1;
|
||||||
|
|
||||||
|
for (i = list.length - 1; i >= 0; i--) {
|
||||||
|
if (list[i] === listener || list[i].listener === listener) {
|
||||||
|
originalListener = list[i].listener;
|
||||||
|
position = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position < 0)
|
||||||
|
return this;
|
||||||
|
|
||||||
|
if (position === 0)
|
||||||
|
list.shift();
|
||||||
|
else {
|
||||||
|
spliceOne(list, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.length === 1)
|
||||||
|
events[type] = list[0];
|
||||||
|
|
||||||
|
if (events.removeListener !== undefined)
|
||||||
|
this.emit('removeListener', type, originalListener || listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
|
||||||
|
|
||||||
|
EventEmitter.prototype.removeAllListeners =
|
||||||
|
function removeAllListeners(type) {
|
||||||
|
var listeners, events, i;
|
||||||
|
|
||||||
|
events = this._events;
|
||||||
|
if (events === undefined)
|
||||||
|
return this;
|
||||||
|
|
||||||
|
// not listening for removeListener, no need to emit
|
||||||
|
if (events.removeListener === undefined) {
|
||||||
|
if (arguments.length === 0) {
|
||||||
|
this._events = Object.create(null);
|
||||||
|
this._eventsCount = 0;
|
||||||
|
} else if (events[type] !== undefined) {
|
||||||
|
if (--this._eventsCount === 0)
|
||||||
|
this._events = Object.create(null);
|
||||||
|
else
|
||||||
|
delete events[type];
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// emit removeListener for all listeners on all events
|
||||||
|
if (arguments.length === 0) {
|
||||||
|
var keys = Object.keys(events);
|
||||||
|
var key;
|
||||||
|
for (i = 0; i < keys.length; ++i) {
|
||||||
|
key = keys[i];
|
||||||
|
if (key === 'removeListener') continue;
|
||||||
|
this.removeAllListeners(key);
|
||||||
|
}
|
||||||
|
this.removeAllListeners('removeListener');
|
||||||
|
this._events = Object.create(null);
|
||||||
|
this._eventsCount = 0;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
listeners = events[type];
|
||||||
|
|
||||||
|
if (typeof listeners === 'function') {
|
||||||
|
this.removeListener(type, listeners);
|
||||||
|
} else if (listeners !== undefined) {
|
||||||
|
// LIFO order
|
||||||
|
for (i = listeners.length - 1; i >= 0; i--) {
|
||||||
|
this.removeListener(type, listeners[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
function _listeners(target, type, unwrap) {
|
||||||
|
var events = target._events;
|
||||||
|
|
||||||
|
if (events === undefined)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
var evlistener = events[type];
|
||||||
|
if (evlistener === undefined)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
if (typeof evlistener === 'function')
|
||||||
|
return unwrap ? [evlistener.listener || evlistener] : [evlistener];
|
||||||
|
|
||||||
|
return unwrap ?
|
||||||
|
unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
EventEmitter.prototype.listeners = function listeners(type) {
|
||||||
|
return _listeners(this, type, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
EventEmitter.prototype.rawListeners = function rawListeners(type) {
|
||||||
|
return _listeners(this, type, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
EventEmitter.listenerCount = function(emitter, type) {
|
||||||
|
if (typeof emitter.listenerCount === 'function') {
|
||||||
|
return emitter.listenerCount(type);
|
||||||
|
} else {
|
||||||
|
return listenerCount.call(emitter, type);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
EventEmitter.prototype.listenerCount = listenerCount;
|
||||||
|
function listenerCount(type) {
|
||||||
|
var events = this._events;
|
||||||
|
|
||||||
|
if (events !== undefined) {
|
||||||
|
var evlistener = events[type];
|
||||||
|
|
||||||
|
if (typeof evlistener === 'function') {
|
||||||
|
return 1;
|
||||||
|
} else if (evlistener !== undefined) {
|
||||||
|
return evlistener.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventEmitter.prototype.eventNames = function eventNames() {
|
||||||
|
return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
|
||||||
|
};
|
||||||
|
|
||||||
|
function arrayClone(arr, n) {
|
||||||
|
var copy = new Array(n);
|
||||||
|
for (var i = 0; i < n; ++i)
|
||||||
|
copy[i] = arr[i];
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
function spliceOne(list, index) {
|
||||||
|
for (; index + 1 < list.length; index++)
|
||||||
|
list[index] = list[index + 1];
|
||||||
|
list.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
function unwrapListeners(arr) {
|
||||||
|
var ret = new Array(arr.length);
|
||||||
|
for (var i = 0; i < ret.length; ++i) {
|
||||||
|
ret[i] = arr[i].listener || arr[i];
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function once(emitter, name) {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
function errorListener(err) {
|
||||||
|
emitter.removeListener(name, resolver);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolver() {
|
||||||
|
if (typeof emitter.removeListener === 'function') {
|
||||||
|
emitter.removeListener('error', errorListener);
|
||||||
|
}
|
||||||
|
resolve([].slice.call(arguments));
|
||||||
|
};
|
||||||
|
|
||||||
|
eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });
|
||||||
|
if (name !== 'error') {
|
||||||
|
addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addErrorHandlerIfEventEmitter(emitter, handler, flags) {
|
||||||
|
if (typeof emitter.on === 'function') {
|
||||||
|
eventTargetAgnosticAddListener(emitter, 'error', handler, flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
|
||||||
|
if (typeof emitter.on === 'function') {
|
||||||
|
if (flags.once) {
|
||||||
|
emitter.once(name, listener);
|
||||||
|
} else {
|
||||||
|
emitter.on(name, listener);
|
||||||
|
}
|
||||||
|
} else if (typeof emitter.addEventListener === 'function') {
|
||||||
|
// EventTarget does not have `error` event semantics like Node
|
||||||
|
// EventEmitters, we do not listen for `error` events here.
|
||||||
|
emitter.addEventListener(name, function wrapListener(arg) {
|
||||||
|
// IE does not have builtin `{ once: true }` support so we
|
||||||
|
// have to do it manually.
|
||||||
|
if (flags.once) {
|
||||||
|
emitter.removeEventListener(name, wrapListener);
|
||||||
|
}
|
||||||
|
listener(arg);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue