ui stuff, proxy?, and whatever else
This commit is contained in:
11
apps.html
11
apps.html
@ -40,7 +40,13 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
<div class="gamesToolbar">
|
||||||
<input type="text" class="searchbar" id="gamesearch" placeholder="Type here to search.." />
|
<input type="text" class="searchbar" id="gamesearch" placeholder="Type here to search.." />
|
||||||
|
<button id="filtersToggle" type="button">filters</button>
|
||||||
|
<button id="randomGame">random app</button>
|
||||||
|
</div>
|
||||||
|
<div id="tagFilters">
|
||||||
|
<p class="filterLabel">sorting</p>
|
||||||
<select id="gamesort">
|
<select id="gamesort">
|
||||||
<option value="az">a -> z</option>
|
<option value="az">a -> z</option>
|
||||||
<option value="za">z -> a</option>
|
<option value="za">z -> a</option>
|
||||||
@ -48,9 +54,14 @@
|
|||||||
<option value="played">your top apps</option>
|
<option value="played">your top apps</option>
|
||||||
<option value="global">global top apps</option>
|
<option value="global">global top apps</option>
|
||||||
</select>
|
</select>
|
||||||
|
<p class="filterLabel" id="tagChipsLabel">game genres</p>
|
||||||
|
<div id="tagChips"></div>
|
||||||
|
</div>
|
||||||
<p id="gameCount">xx apps loaded..</p>
|
<p id="gameCount">xx apps loaded..</p>
|
||||||
<p id="starredHeader">starred apps</p>
|
<p id="starredHeader">starred apps</p>
|
||||||
<div id="starredgames"></div>
|
<div id="starredgames"></div>
|
||||||
|
<p id="recentHeader">recently played</p>
|
||||||
|
<div id="recentgames"></div>
|
||||||
<p id="allHeader" class="title">all apps</p>
|
<p id="allHeader" class="title">all apps</p>
|
||||||
<div id="games">
|
<div id="games">
|
||||||
<p id="loadingMsg">apps loading..</p>
|
<p id="loadingMsg">apps loading..</p>
|
||||||
|
|||||||
@ -50,7 +50,7 @@
|
|||||||
transform: scale(1.1);
|
transform: scale(1.1);
|
||||||
filter: drop-shadow(0 0 5px var(--color-1))
|
filter: drop-shadow(0 0 5px var(--color-1))
|
||||||
}
|
}
|
||||||
#games, #starredgames, #topGames {
|
#games, #starredgames, #topGames, #recentgames {
|
||||||
width: 90vw;
|
width: 90vw;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@ -64,16 +64,107 @@
|
|||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
color: var(--color-1);
|
color: var(--color-1);
|
||||||
}
|
}
|
||||||
#starredHeader, #allHeader, #topHeader {
|
#starredHeader, #allHeader, #topHeader, #recentHeader {
|
||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
color: var(--color-1);
|
color: var(--color-1);
|
||||||
}
|
}
|
||||||
#noResults, #starredHeader, #allHeader, #topHeader {
|
#noResults, #starredHeader, #allHeader, #topHeader, #recentHeader {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
.gamesToolbar {
|
||||||
|
width: 90vw;
|
||||||
|
max-width: 700px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin: 10px 0 4px;
|
||||||
|
}
|
||||||
|
.gamesToolbar .searchbar {
|
||||||
|
flex: 2 1 320px;
|
||||||
|
width: auto;
|
||||||
|
margin: 0;
|
||||||
|
padding: 16px 20px;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
.gamesToolbar #gamesort,
|
||||||
|
.gamesToolbar #randomGame,
|
||||||
|
.gamesToolbar #filtersToggle {
|
||||||
|
margin: 0;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
.gamesToolbar #randomGame:hover,
|
||||||
|
.gamesToolbar #filtersToggle:hover {
|
||||||
|
filter: brightness(1.1);
|
||||||
|
box-shadow: none;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
.gamesToolbar #filtersToggle.hasActive {
|
||||||
|
background-color: var(--color-1);
|
||||||
|
border-color: var(--color-1);
|
||||||
|
color: var(--bg-2);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.gamesToolbar #filtersToggle.open {
|
||||||
|
box-shadow: inset 0 0 0 2px var(--text-color);
|
||||||
|
}
|
||||||
|
#tagFilters {
|
||||||
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
width: 90vw;
|
||||||
|
max-width: 700px;
|
||||||
|
margin: 4px 0 10px;
|
||||||
|
}
|
||||||
|
#tagFilters.open {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
#tagFilters #gamesort {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.filterLabel {
|
||||||
|
font-size: 16px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
color: var(--text-color);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
#tagChipsLabel {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
#tagChips {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.tagChip {
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 2px;
|
||||||
|
padding: 6px 14px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background-color: color-mix(in srgb, var(--color-2) 40%, #00000000 60%);
|
||||||
|
border-color: var(--color-3);
|
||||||
|
color: var(--text-color);
|
||||||
|
outline: none;
|
||||||
|
transition-duration: 0.25s;
|
||||||
|
font-size: 13px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.tagChip:hover {
|
||||||
|
filter: brightness(1.15);
|
||||||
|
}
|
||||||
|
.tagChip.active {
|
||||||
|
background-color: var(--color-1);
|
||||||
|
border-color: var(--color-1);
|
||||||
|
color: var(--bg-2);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
#gamesort {
|
#gamesort {
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: 2px;
|
border-width: 2px;
|
||||||
|
|||||||
50
css/home.css
Normal file
50
css/home.css
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
.homeNav {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 16px;
|
||||||
|
max-width: 700px;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
.navCard {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
width: 110px;
|
||||||
|
height: 100px;
|
||||||
|
border-radius: 16px;
|
||||||
|
background-color: color-mix(in srgb, var(--color-2) 40%, #00000000 60%);
|
||||||
|
box-shadow: 0 0 10px var(--color-2);
|
||||||
|
backdrop-filter: blur(2px);
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--text-color);
|
||||||
|
transition-duration: 0.25s;
|
||||||
|
}
|
||||||
|
.navCard:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
.navCard img {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.navCard span {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
body[fast] .navCard {
|
||||||
|
background-color: color-mix(in srgb, var(--color-2) 70%, #000000 30%);
|
||||||
|
backdrop-filter: blur(0);
|
||||||
|
}
|
||||||
|
@-moz-document url-prefix() {
|
||||||
|
.navCard {
|
||||||
|
backdrop-filter: blur(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 500px) {
|
||||||
|
.navCard {
|
||||||
|
width: 90px;
|
||||||
|
height: 85px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -36,7 +36,7 @@ body {
|
|||||||
#pjs {
|
#pjs {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
position: absolute;
|
position: fixed;
|
||||||
z-index: -1000;
|
z-index: -1000;
|
||||||
border: none;
|
border: none;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
|
|||||||
165
css/proxy.css
Normal file
165
css/proxy.css
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
body {
|
||||||
|
overflow: hidden;
|
||||||
|
align-items: stretch;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
#proxyBar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 10px;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
background-color: var(--bg-2);
|
||||||
|
box-shadow: 0 0 10px var(--color-2);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
#proxyBar a {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
#proxyBar a img {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
#sj-form {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
#sj-address {
|
||||||
|
flex: 1;
|
||||||
|
width: auto;
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px 16px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
#proxyBar button {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
#proxyFullscreen {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition-duration: 0.25s;
|
||||||
|
filter: brightness(0.9);
|
||||||
|
}
|
||||||
|
#proxyFullscreen:hover {
|
||||||
|
filter: brightness(1);
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
#proxyFullscreen:active {
|
||||||
|
filter: brightness(1.2);
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
#proxyError {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
padding: 0 10px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
#proxyError p {
|
||||||
|
color: #ff8a8a;
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
#proxyError pre {
|
||||||
|
color: color-mix(in srgb, var(--text-color) 60%, #00000000 40%);
|
||||||
|
font-size: 12px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
margin: 0 0 6px;
|
||||||
|
}
|
||||||
|
#proxyFrameHolder {
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
#sj-frame {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: none;
|
||||||
|
background-color: black;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
#proxyShortcuts {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 18px;
|
||||||
|
padding: 24px 16px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
.filterLabel {
|
||||||
|
font-size: 16px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
color: var(--text-color);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
#proxyShortcutsGrid {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0;
|
||||||
|
width: 90vw;
|
||||||
|
max-width: 1000px;
|
||||||
|
}
|
||||||
|
.proxyShortcut {
|
||||||
|
width: 20vw;
|
||||||
|
min-width: 180px;
|
||||||
|
height: 9vh;
|
||||||
|
min-height: 64px;
|
||||||
|
background-color: color-mix(in srgb, var(--color-2) 40%, #00000000 60%);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 20px;
|
||||||
|
margin: 15px;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
backdrop-filter: blur(2px);
|
||||||
|
box-shadow: 0 0 10px var(--color-2);
|
||||||
|
color: var(--text-color);
|
||||||
|
outline: none;
|
||||||
|
transition-duration: 0.25s;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
.proxyShortcut:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
.proxyShortcut img {
|
||||||
|
height: calc(100% - 8px);
|
||||||
|
border-radius: 15px;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
margin: 4px;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
.proxyShortcutHolder {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.proxyShortcutHolder h1 {
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 15px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
body[fast] .proxyShortcut {
|
||||||
|
background-color: color-mix(in srgb, var(--color-2) 70%, #000000 30%);
|
||||||
|
backdrop-filter: blur(0);
|
||||||
|
}
|
||||||
|
@-moz-document url-prefix() {
|
||||||
|
.proxyShortcut {
|
||||||
|
backdrop-filter: blur(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
182
css/settings.css
182
css/settings.css
@ -1,6 +1,10 @@
|
|||||||
|
body {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
section {
|
section {
|
||||||
min-width: 20%;
|
min-width: 20%;
|
||||||
height: 420px;
|
min-height: 420px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
margin: 16px;
|
margin: 16px;
|
||||||
background-color: color-mix(in srgb, var(--color-2) 40%, #00000000 60%);
|
background-color: color-mix(in srgb, var(--color-2) 40%, #00000000 60%);
|
||||||
@ -27,14 +31,132 @@ sections {
|
|||||||
margin: 6px;
|
margin: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.feedback {
|
||||||
|
min-height: 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 6px 4px 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback.error {
|
||||||
|
color: #ff8a8a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback.success {
|
||||||
|
color: #7cffb2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggleRow {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 6px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggleRow label {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggleRow .switch {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
flex: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggleRow .switch input[type=checkbox] {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggleRow .switchTrack {
|
||||||
|
display: inline-block;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 42px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background-color: color-mix(in srgb, var(--color-4) 60%, #00000000 40%);
|
||||||
|
border: 2px solid var(--color-3);
|
||||||
|
position: relative;
|
||||||
|
transition-duration: 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggleRow .switchTrack::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
left: 2px;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: var(--text-color);
|
||||||
|
transition-duration: 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggleRow .switch:hover .switchTrack {
|
||||||
|
filter: brightness(1.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggleRow .switch input[type=checkbox]:checked ~ .switchTrack {
|
||||||
|
background-color: var(--color-1);
|
||||||
|
border-color: var(--color-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggleRow .switch input[type=checkbox]:checked ~ .switchTrack::after {
|
||||||
|
transform: translateX(18px);
|
||||||
|
background-color: var(--bg-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggleRow .switch input[type=checkbox]:focus-visible ~ .switchTrack {
|
||||||
|
outline: 2px solid var(--color-1);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#hiddenGamesSection {
|
||||||
|
width: min(640px, 90vw);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hiddenGamesRow {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 16px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hiddenGamesControls {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
flex: 1 1 220px;
|
||||||
|
}
|
||||||
|
|
||||||
#hiddenGamesList {
|
#hiddenGamesList {
|
||||||
width: min(280px, 90%);
|
width: auto;
|
||||||
height: 200px;
|
flex: 1 1 260px;
|
||||||
|
align-self: flex-start;
|
||||||
|
height: 340px;
|
||||||
|
min-height: 0;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
contain: layout;
|
||||||
border: 1px solid color-mix(in srgb, var(--color-1) 50%, #00000000 50%);
|
border: 1px solid color-mix(in srgb, var(--color-1) 50%, #00000000 50%);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
margin: 8px 0;
|
margin: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hidden-game-row {
|
.hidden-game-row {
|
||||||
@ -45,6 +167,58 @@ sections {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hidden-game-row input[type=checkbox] {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden-game-row .checkboxBox {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
min-width: 18px;
|
||||||
|
border: 2px solid var(--color-1);
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: color-mix(in srgb, var(--color-4) 40%, #00000000 60%);
|
||||||
|
cursor: pointer;
|
||||||
|
transition-duration: 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden-game-row:hover .checkboxBox {
|
||||||
|
border-color: var(--text-color);
|
||||||
|
background-color: color-mix(in srgb, var(--color-3) 50%, #00000000 50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden-game-row input[type=checkbox]:checked ~ .checkboxBox {
|
||||||
|
background-color: var(--color-1);
|
||||||
|
border-color: var(--color-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden-game-row input[type=checkbox]:checked ~ .checkboxBox::after {
|
||||||
|
content: "";
|
||||||
|
width: 4px;
|
||||||
|
height: 8px;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
border: solid var(--bg-2);
|
||||||
|
border-width: 0 2px 2px 0;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden-game-row input[type=checkbox]:focus-visible ~ .checkboxBox {
|
||||||
|
outline: 2px solid var(--color-1);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
#hiddenGamesSearch {
|
#hiddenGamesSearch {
|
||||||
width: min(280px, 90%);
|
width: min(280px, 90%);
|
||||||
}
|
}
|
||||||
|
|||||||
64
home.html
64
home.html
@ -6,7 +6,7 @@
|
|||||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-J0Y3T4HN4N"></script>
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-J0Y3T4HN4N"></script>
|
||||||
<script>
|
<script>
|
||||||
window.dataLayer = window.dataLayer || [];
|
window.dataLayer = window.dataLayer || [];
|
||||||
function gtag(){dataLayer.push(arguments);}
|
function gtag() { dataLayer.push(arguments); }
|
||||||
gtag('js', new Date());
|
gtag('js', new Date());
|
||||||
|
|
||||||
gtag('config', 'G-J0Y3T4HN4N');
|
gtag('config', 'G-J0Y3T4HN4N');
|
||||||
@ -37,6 +37,7 @@
|
|||||||
<!-- initialize my stuff -->
|
<!-- initialize my stuff -->
|
||||||
<link rel="stylesheet" href="/css/main.css" />
|
<link rel="stylesheet" href="/css/main.css" />
|
||||||
<link rel="stylesheet" href="/css/pages.css" />
|
<link rel="stylesheet" href="/css/pages.css" />
|
||||||
|
<link rel="stylesheet" href="/css/home.css" />
|
||||||
<link rel="preload" href="https://fonts.googleapis.com/css2?family=Mulish&display=swap" as="style"
|
<link rel="preload" href="https://fonts.googleapis.com/css2?family=Mulish&display=swap" as="style"
|
||||||
onload="this.onload=null;this.rel='stylesheet'" />
|
onload="this.onload=null;this.rel='stylesheet'" />
|
||||||
<script src="/js/main.js" defer></script>
|
<script src="/js/main.js" defer></script>
|
||||||
@ -59,6 +60,24 @@
|
|||||||
document.getElementById("randomquote").innerHTML = randomQuote;
|
document.getElementById("randomquote").innerHTML = randomQuote;
|
||||||
})
|
})
|
||||||
.catch((error) => console.error("Error loading quotes:", error));
|
.catch((error) => console.error("Error loading quotes:", error));
|
||||||
|
|
||||||
|
fetch("/api/proxy/status")
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
if (data.unlocked) document.getElementById("proxyNavCard").style.display = "";
|
||||||
|
})
|
||||||
|
.catch(() => { });
|
||||||
|
|
||||||
|
document.getElementById("openblank").addEventListener("click", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
let win = window.open();
|
||||||
|
win.document.body.style.margin = "0";
|
||||||
|
win.document.body.style.height = "100vh";
|
||||||
|
let html = `<style>*{margin:0;padding:0;border:none;height:100vh;width:100vw}</style><iframe src=index.html></iframe>`;
|
||||||
|
win.document.querySelector("html").innerHTML = html;
|
||||||
|
location.href = "https://google.com";
|
||||||
|
window.close();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<link rel="manifest" href="/manifest.json" />
|
<link rel="manifest" href="/manifest.json" />
|
||||||
@ -74,16 +93,39 @@
|
|||||||
<h1 class="title">selenite.</h1>
|
<h1 class="title">selenite.</h1>
|
||||||
<noscript>enable javascript if you want the games to actually load</noscript>
|
<noscript>enable javascript if you want the games to actually load</noscript>
|
||||||
<p id="randomquote">better thank me for this unblocked website</p>
|
<p id="randomquote">better thank me for this unblocked website</p>
|
||||||
<div class="samerow">
|
<div class="homeNav">
|
||||||
<div class="img-container">
|
<a class="navCard" href="projects.html">
|
||||||
<a href="/users"><img src="img/user_index.svg" /></a>
|
<img src="img/games.svg" />
|
||||||
</div>
|
<span>games</span>
|
||||||
<div class="img-container">
|
</a>
|
||||||
<a href="projects.html"><img src="img/games.svg" /></a>
|
<a class="navCard" href="apps.html">
|
||||||
</div>
|
<img src="img/apps.svg" />
|
||||||
<div class="img-container">
|
<span>apps</span>
|
||||||
<a target="_blank" href="https://discord.gg/7jyufnwJNf"><img src="img/discord.svg" /></a>
|
</a>
|
||||||
</div>
|
<a class="navCard" href="proxy.html" id="proxyNavCard" style="display: none;">
|
||||||
|
<img src="img/addlink.svg" />
|
||||||
|
<span>proxy</span>
|
||||||
|
</a>
|
||||||
|
<a class="navCard" href="bookmarklets.html">
|
||||||
|
<img src="img/bookmarklets.svg" />
|
||||||
|
<span>bookmarklets</span>
|
||||||
|
</a>
|
||||||
|
<a class="navCard" href="/users">
|
||||||
|
<img src="img/user_index.svg" />
|
||||||
|
<span>profile</span>
|
||||||
|
</a>
|
||||||
|
<a class="navCard" href="settings.html">
|
||||||
|
<img src="img/settings.svg" />
|
||||||
|
<span>settings</span>
|
||||||
|
</a>
|
||||||
|
<a class="navCard" href="#" id="openblank">
|
||||||
|
<img src="img/open.svg" />
|
||||||
|
<span>open about:blank</span>
|
||||||
|
</a>
|
||||||
|
<a class="navCard" target="_blank" href="https://discord.gg/7jyufnwJNf">
|
||||||
|
<img src="img/discord.svg" />
|
||||||
|
<span>discord</span>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M680-160v-120H560v-80h120v-120h80v120h120v80H760v120h-80ZM440-280H280q-83 0-141.5-58.5T80-480q0-83 58.5-141.5T280-680h160v80H280q-50 0-85 35t-35 85q0 50 35 85t85 35h160v80ZM320-440v-80h320v80H320Zm560-40h-80q0-50-35-85t-85-35H520v-80h160q83 0 141.5 58.5T880-480Z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24" fill="#FFFFFF"><path d="M680-160v-120H560v-80h120v-120h80v120h120v80H760v120h-80ZM440-280H280q-83 0-141.5-58.5T80-480q0-83 58.5-141.5T280-680h160v80H280q-50 0-85 35t-35 85q0 50 35 85t85 35h160v80ZM320-440v-80h320v80H320Zm560-40h-80q0-50-35-85t-85-35H520v-80h160q83 0 141.5 58.5T880-480Z"/></svg>
|
||||||
|
Before Width: | Height: | Size: 368 B After Width: | Height: | Size: 383 B |
25
index.html
25
index.html
@ -6,7 +6,7 @@
|
|||||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-J0Y3T4HN4N"></script>
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-J0Y3T4HN4N"></script>
|
||||||
<script>
|
<script>
|
||||||
window.dataLayer = window.dataLayer || [];
|
window.dataLayer = window.dataLayer || [];
|
||||||
function gtag(){dataLayer.push(arguments);}
|
function gtag() { dataLayer.push(arguments); }
|
||||||
gtag('js', new Date());
|
gtag('js', new Date());
|
||||||
|
|
||||||
gtag('config', 'G-J0Y3T4HN4N');
|
gtag('config', 'G-J0Y3T4HN4N');
|
||||||
@ -144,9 +144,7 @@
|
|||||||
let screenIDs = ["zero", "one", "two", "three", "four"];
|
let screenIDs = ["zero", "one", "two", "three", "four"];
|
||||||
let screens = screenIDs.map((e) => document.getElementById(e));
|
let screens = screenIDs.map((e) => document.getElementById(e));
|
||||||
let index = 0;
|
let index = 0;
|
||||||
|
|
||||||
let welcome = document.querySelector("welcome");
|
let welcome = document.querySelector("welcome");
|
||||||
// let blur = document.getElementById("blur");
|
|
||||||
|
|
||||||
// TODO: finish
|
// TODO: finish
|
||||||
hideAll();
|
hideAll();
|
||||||
@ -238,24 +236,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 500);
|
}, 500);
|
||||||
document.addEventListener("keydown", (e) => {
|
function handlePanicKeydown(e) {
|
||||||
if (currentPanic.key.length > 0 && currentPanic.url.length > 0) {
|
if (currentPanic.key.length > 0 && currentPanic.url.length > 0 && e.key.toLowerCase() === currentPanic.key.toLowerCase()) {
|
||||||
if (e.key = currentPanic.key) {
|
|
||||||
location.href = currentPanic.url;
|
location.href = currentPanic.url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
document.addEventListener("keydown", handlePanicKeydown);
|
||||||
document.getElementById("iframe").contentWindow.document.addEventListener("keydown", (e) => {
|
|
||||||
if (currentPanic.key.length > 0 && currentPanic.url.length > 0) {
|
|
||||||
if (e.key = currentPanic.key) {
|
|
||||||
location.href = currentPanic.url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
document.getElementById("iframe").addEventListener("load", () => {
|
document.getElementById("iframe").addEventListener("load", () => {
|
||||||
|
try {
|
||||||
|
document.getElementById("iframe").contentWindow.document.addEventListener("keydown", handlePanicKeydown);
|
||||||
|
} catch { }
|
||||||
|
try {
|
||||||
document.getElementById("iframe").contentWindow.addEventListener("beforeunload", () => {
|
document.getElementById("iframe").contentWindow.addEventListener("beforeunload", () => {
|
||||||
alert("unloading! show loading bar")
|
alert("unloading! show loading bar")
|
||||||
})
|
})
|
||||||
|
} catch { }
|
||||||
});
|
});
|
||||||
document.querySelectorAll(".sidebar-item").forEach((item) => {
|
document.querySelectorAll(".sidebar-item").forEach((item) => {
|
||||||
if (item.childNodes[0].target) {
|
if (item.childNodes[0].target) {
|
||||||
@ -353,7 +348,7 @@
|
|||||||
</div> <!-- open blank -->
|
</div> <!-- open blank -->
|
||||||
<div class="sidebar-item"><a href="#" id="fullscreen"><img src="/img/fullscreen.svg" /></a>
|
<div class="sidebar-item"><a href="#" id="fullscreen"><img src="/img/fullscreen.svg" /></a>
|
||||||
<div class="sidebar-item-descriptor">fullscreen</div>
|
<div class="sidebar-item-descriptor">fullscreen</div>
|
||||||
</div> <!-- open blank -->
|
</div> <!-- fullscreen -->
|
||||||
<div class="sidebar-divider"></div>
|
<div class="sidebar-divider"></div>
|
||||||
<div class="sidebar-item"><a href="#" target="/u/"><img src="/img/user.svg" /></a>
|
<div class="sidebar-item"><a href="#" target="/u/"><img src="/img/user.svg" /></a>
|
||||||
<div class="sidebar-item-descriptor">user profile</div>
|
<div class="sidebar-item-descriptor">user profile</div>
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-J0Y3T4HN4N"></script>
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-J0Y3T4HN4N"></script>
|
||||||
<script>
|
<script>
|
||||||
window.dataLayer = window.dataLayer || [];
|
window.dataLayer = window.dataLayer || [];
|
||||||
function gtag(){dataLayer.push(arguments);}
|
function gtag() { dataLayer.push(arguments); }
|
||||||
gtag('js', new Date());
|
gtag('js', new Date());
|
||||||
|
|
||||||
gtag('config', 'G-J0Y3T4HN4N');
|
gtag('config', 'G-J0Y3T4HN4N');
|
||||||
@ -66,6 +66,7 @@
|
|||||||
<h2>credits</h2>
|
<h2>credits</h2>
|
||||||
<p>whilst the majority of the work has been done by the creator, sky, selenite would not be possible without some
|
<p>whilst the majority of the work has been done by the creator, sky, selenite would not be possible without some
|
||||||
key sources and people.</p>
|
key sources and people.</p>
|
||||||
|
<p><a href="https://discordapp.com/users/1406787010131398706">firstlynx</a> (continuation and new owner)</p>
|
||||||
<p><a href="https://github.com/a456pur/seraph">seraph</a> by <a href="https://a456pur.dev">a456pur</a> (games)</p>
|
<p><a href="https://github.com/a456pur/seraph">seraph</a> by <a href="https://a456pur.dev">a456pur</a> (games)</p>
|
||||||
<p><a href="https://gitlab.com/3kh0/3kh0-assets">3kh0 assets</a> by <a href="https://3kh0.com">3kh0</a> (games)</p>
|
<p><a href="https://gitlab.com/3kh0/3kh0-assets">3kh0 assets</a> by <a href="https://3kh0.com">3kh0</a> (games)</p>
|
||||||
<p><a href="https://github.com/genizy/web-port">web ports</a> by <a href="https://gn-math.github.io/">gn-math</a>
|
<p><a href="https://github.com/genizy/web-port">web ports</a> by <a href="https://gn-math.github.io/">gn-math</a>
|
||||||
|
|||||||
144
js/loader.js
144
js/loader.js
@ -5,7 +5,9 @@ let topElements = [];
|
|||||||
let isDev = [];
|
let isDev = [];
|
||||||
let pageData;
|
let pageData;
|
||||||
let starredGames = [];
|
let starredGames = [];
|
||||||
|
let recentGames = [];
|
||||||
let globalPlays = null;
|
let globalPlays = null;
|
||||||
|
let selectedTags = new Set();
|
||||||
|
|
||||||
function getHiddenGames() {
|
function getHiddenGames() {
|
||||||
try {
|
try {
|
||||||
@ -16,12 +18,22 @@ function getHiddenGames() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getRecentlyPlayed() {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(localStorage.getItem("selenite.recentlyPlayed") || "[]");
|
||||||
|
return parsed;
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function loadGames() {
|
async function loadGames() {
|
||||||
if (type == "g") pageData = { path: "/resources/games.json", prefix: "semag", type: "g" };
|
if (type == "g") pageData = { path: "/resources/games.json", prefix: "semag", type: "g" };
|
||||||
else if (type == "a") pageData = { path: "/resources/apps.json", prefix: "sppa", type: "a" };
|
else if (type == "a") pageData = { path: "/resources/apps.json", prefix: "sppa", type: "a" };
|
||||||
else { sAlert("this is not valid"); return; }
|
else { sAlert("this is not valid"); return; }
|
||||||
|
|
||||||
let data = await (await fetch(pageData.path)).json();
|
let data = await (await fetch(pageData.path)).json();
|
||||||
|
const topGamesDisabled = localStorage.getItem("selenite.disableTopGames") == "true";
|
||||||
|
|
||||||
data.forEach((g, i) => { g._key = g.name.toUpperCase(); g._index = i; });
|
data.forEach((g, i) => { g._key = g.name.toUpperCase(); g._index = i; });
|
||||||
data.sort((a, b) => a._key < b._key ? -1 : a._key > b._key ? 1 : 0);
|
data.sort((a, b) => a._key < b._key ? -1 : a._key > b._key ? 1 : 0);
|
||||||
@ -50,6 +62,7 @@ async function loadGames() {
|
|||||||
newElement.dataset.name = element._key;
|
newElement.dataset.name = element._key;
|
||||||
newElement.dataset.top = element.tags && element.tags.includes("top") ? "1" : "";
|
newElement.dataset.top = element.tags && element.tags.includes("top") ? "1" : "";
|
||||||
newElement.dataset.index = element._index;
|
newElement.dataset.index = element._index;
|
||||||
|
newElement.dataset.tags = element.tags ? element.tags.join(",") : "";
|
||||||
newElement.className = "game";
|
newElement.className = "game";
|
||||||
newElement.href = `/loader.html?title=${encodeURIComponent(element.name)}&dir=${element.directory}&img=${element.image}&type=${pageData.type}`;
|
newElement.href = `/loader.html?title=${encodeURIComponent(element.name)}&dir=${element.directory}&img=${element.image}&type=${pageData.type}`;
|
||||||
|
|
||||||
@ -73,6 +86,7 @@ async function loadGames() {
|
|||||||
newElement.appendChild(image);
|
newElement.appendChild(image);
|
||||||
newElement.appendChild(holder);
|
newElement.appendChild(holder);
|
||||||
newElement.appendChild(warnings);
|
newElement.appendChild(warnings);
|
||||||
|
holder.appendChild(star);
|
||||||
|
|
||||||
if (element.tags) {
|
if (element.tags) {
|
||||||
[["18+", "thirteen", "18+"], ["13+", "thirteen", "13+"],
|
[["18+", "thirteen", "18+"], ["13+", "thirteen", "13+"],
|
||||||
@ -91,18 +105,16 @@ async function loadGames() {
|
|||||||
w.className = "thirteen";
|
w.className = "thirteen";
|
||||||
warnings.appendChild(w);
|
warnings.appendChild(w);
|
||||||
}
|
}
|
||||||
if (element.tags.includes("top")) {
|
if (element.tags.includes("top") && !topGamesDisabled) {
|
||||||
topFragment.appendChild(newElement);
|
topFragment.appendChild(newElement);
|
||||||
topElements.push(newElement);
|
topElements.push(newElement);
|
||||||
} else {
|
} else {
|
||||||
gamesFragment.appendChild(newElement);
|
gamesFragment.appendChild(newElement);
|
||||||
gameElements.push(newElement);
|
gameElements.push(newElement);
|
||||||
holder.appendChild(star);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
gamesFragment.appendChild(newElement);
|
gamesFragment.appendChild(newElement);
|
||||||
gameElements.push(newElement);
|
gameElements.push(newElement);
|
||||||
holder.appendChild(star);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
elements.push(newElement);
|
elements.push(newElement);
|
||||||
@ -117,9 +129,10 @@ async function loadGames() {
|
|||||||
if (topGElement) topGElement.appendChild(topFragment);
|
if (topGElement) topGElement.appendChild(topFragment);
|
||||||
const visibleGameCount = elements.length;
|
const visibleGameCount = elements.length;
|
||||||
const hiddenCount = hiddenGames.length;
|
const hiddenCount = hiddenGames.length;
|
||||||
|
const itemLabel = pageData.type === "a" ? "apps" : "games";
|
||||||
document.getElementById("gameCount").innerText = hiddenCount > 0
|
document.getElementById("gameCount").innerText = hiddenCount > 0
|
||||||
? `${visibleGameCount} games loaded (${hiddenCount} hidden)`
|
? `${visibleGameCount} ${itemLabel} loaded (${hiddenCount} hidden)`
|
||||||
: `${visibleGameCount} games loaded!`;
|
: `${visibleGameCount} ${itemLabel} loaded!`;
|
||||||
document.getElementById("loadingMsg").style.display = "none";
|
document.getElementById("loadingMsg").style.display = "none";
|
||||||
document.getElementById("allHeader").style.display = "block";
|
document.getElementById("allHeader").style.display = "block";
|
||||||
const topHeader = document.getElementById("topHeader");
|
const topHeader = document.getElementById("topHeader");
|
||||||
@ -134,17 +147,87 @@ async function loadGames() {
|
|||||||
e.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); starEvent(e); });
|
e.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); starEvent(e); });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const recentHeader = document.getElementById("recentHeader");
|
||||||
|
const recentGamesEl = document.getElementById("recentgames");
|
||||||
|
if (recentHeader && recentGamesEl && localStorage.getItem("selenite.disableRecentlyPlayed") != "true") {
|
||||||
|
const pageDirs = new Set(elements.map((el) => el.dataset.target));
|
||||||
|
recentGames = getRecentlyPlayed().filter((dir) => !hiddenSet.has(dir) && pageDirs.has(dir));
|
||||||
|
if (recentGames.length > 0) {
|
||||||
|
recentHeader.style.display = "block";
|
||||||
|
_rebuildRecent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tagSet = new Set();
|
||||||
|
data.forEach((element) => {
|
||||||
|
if (hiddenSet.has(element.directory)) return;
|
||||||
|
(element.tags || []).forEach((t) => { if (t !== "top") tagSet.add(t); });
|
||||||
|
});
|
||||||
|
renderTagFilters([...tagSet].sort());
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTagFilters(tags) {
|
||||||
|
const chipsContainer = document.getElementById("tagChips");
|
||||||
|
const chipsLabel = document.getElementById("tagChipsLabel");
|
||||||
|
if (!chipsContainer) return;
|
||||||
|
if (tags.length === 0) {
|
||||||
|
chipsContainer.style.display = "none";
|
||||||
|
if (chipsLabel) chipsLabel.style.display = "none";
|
||||||
|
updateFiltersToggleLabel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
chipsContainer.innerHTML = "";
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
tags.forEach((tag) => {
|
||||||
|
const chip = document.createElement("button");
|
||||||
|
chip.type = "button";
|
||||||
|
chip.className = "tagChip";
|
||||||
|
chip.innerText = tag;
|
||||||
|
chip.dataset.tag = tag;
|
||||||
|
chip.addEventListener("click", () => {
|
||||||
|
if (selectedTags.has(tag)) {
|
||||||
|
selectedTags.delete(tag);
|
||||||
|
chip.classList.remove("active");
|
||||||
|
} else {
|
||||||
|
selectedTags.add(tag);
|
||||||
|
chip.classList.add("active");
|
||||||
|
}
|
||||||
|
updateFiltersToggleLabel();
|
||||||
|
applyFilters();
|
||||||
|
});
|
||||||
|
fragment.appendChild(chip);
|
||||||
|
});
|
||||||
|
chipsContainer.appendChild(fragment);
|
||||||
|
updateFiltersToggleLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateFiltersToggleLabel() {
|
||||||
|
const toggleBtn = document.getElementById("filtersToggle");
|
||||||
|
if (!toggleBtn) return;
|
||||||
|
toggleBtn.innerText = selectedTags.size > 0 ? `filters (${selectedTags.size})` : "filters";
|
||||||
|
toggleBtn.classList.toggle("hasActive", selectedTags.size > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let searchRaf = null;
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
document.getElementById("gamesearch").addEventListener("input", () => {
|
const toggleBtn = document.getElementById("filtersToggle");
|
||||||
if (searchRaf) cancelAnimationFrame(searchRaf);
|
const panel = document.getElementById("tagFilters");
|
||||||
searchRaf = requestAnimationFrame(() => {
|
if (!toggleBtn || !panel) return;
|
||||||
const input = document.getElementById("gamesearch").value.toUpperCase();
|
toggleBtn.addEventListener("click", () => {
|
||||||
|
const isOpen = panel.classList.toggle("open");
|
||||||
|
toggleBtn.classList.toggle("open", isOpen);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function applyFilters() {
|
||||||
|
const searchInput = document.getElementById("gamesearch");
|
||||||
|
const input = (searchInput ? searchInput.value : "").toUpperCase();
|
||||||
let hidden = 0;
|
let hidden = 0;
|
||||||
elements.forEach((el) => {
|
elements.forEach((el) => {
|
||||||
if (el.dataset.name.includes(input)) {
|
const matchesSearch = el.dataset.name.includes(input);
|
||||||
|
const elTags = el.dataset.tags ? el.dataset.tags.split(",") : [];
|
||||||
|
const matchesTags = selectedTags.size === 0 || elTags.some((t) => selectedTags.has(t));
|
||||||
|
if (matchesSearch && matchesTags) {
|
||||||
el.style.display = "flex";
|
el.style.display = "flex";
|
||||||
} else {
|
} else {
|
||||||
el.style.display = "none";
|
el.style.display = "none";
|
||||||
@ -152,6 +235,14 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
document.getElementById("noResults").style.display = hidden >= elements.length ? "flex" : "none";
|
document.getElementById("noResults").style.display = hidden >= elements.length ? "flex" : "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
let searchRaf = null;
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
document.getElementById("gamesearch").addEventListener("input", () => {
|
||||||
|
if (searchRaf) cancelAnimationFrame(searchRaf);
|
||||||
|
searchRaf = requestAnimationFrame(() => {
|
||||||
|
applyFilters();
|
||||||
searchRaf = null;
|
searchRaf = null;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -160,15 +251,32 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
function _rebuildStarred() {
|
function _rebuildStarred() {
|
||||||
const container = document.getElementById("starredgames");
|
const container = document.getElementById("starredgames");
|
||||||
const gamesEl = document.getElementById("games");
|
const gamesEl = document.getElementById("games");
|
||||||
|
const topGEl = document.getElementById("topGames");
|
||||||
container.innerHTML = "";
|
container.innerHTML = "";
|
||||||
const fragment = document.createDocumentFragment();
|
const fragment = document.createDocumentFragment();
|
||||||
starredGames.forEach((dir) => {
|
starredGames.forEach((dir) => {
|
||||||
const el = gamesEl.querySelector(`a.game[data-target='${dir}']`);
|
const el = gamesEl.querySelector(`a.game[data-target='${dir}']`) || (topGEl && topGEl.querySelector(`a.game[data-target='${dir}']`));
|
||||||
if (el) fragment.appendChild(el.cloneNode(true));
|
if (el) fragment.appendChild(el.cloneNode(true));
|
||||||
});
|
});
|
||||||
container.appendChild(fragment);
|
container.appendChild(fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _rebuildRecent() {
|
||||||
|
const container = document.getElementById("recentgames");
|
||||||
|
const gamesEl = document.getElementById("games");
|
||||||
|
const topGEl = document.getElementById("topGames");
|
||||||
|
container.innerHTML = "";
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
recentGames.slice(0, 4).forEach((dir) => {
|
||||||
|
const el = gamesEl.querySelector(`a.game[data-target='${dir}']`) || (topGEl && topGEl.querySelector(`a.game[data-target='${dir}']`));
|
||||||
|
if (el) fragment.appendChild(el.cloneNode(true));
|
||||||
|
});
|
||||||
|
container.appendChild(fragment);
|
||||||
|
container.querySelectorAll("#star").forEach((e) => {
|
||||||
|
e.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); starEvent(e); });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function getTotalPlayTime(directory) {
|
function getTotalPlayTime(directory) {
|
||||||
const playTime = JSON.parse(localStorage.getItem("selenite.playTime") || "{}");
|
const playTime = JSON.parse(localStorage.getItem("selenite.playTime") || "{}");
|
||||||
return Object.values(playTime).reduce((total, day) => total + (day[directory] || 0), 0);
|
return Object.values(playTime).reduce((total, day) => total + (day[directory] || 0), 0);
|
||||||
@ -227,6 +335,18 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const randomBtn = document.getElementById("randomGame");
|
||||||
|
if (!randomBtn) return;
|
||||||
|
randomBtn.addEventListener("click", () => {
|
||||||
|
if (elements.length === 0) return;
|
||||||
|
const visible = elements.filter((el) => el.style.display !== "none");
|
||||||
|
const pool = visible.length > 0 ? visible : elements;
|
||||||
|
const random = pool[Math.floor(Math.random() * pool.length)];
|
||||||
|
location.href = random.href;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
function starEvent(e) {
|
function starEvent(e) {
|
||||||
const game = e.target.parentNode.parentNode.getAttribute("data-target");
|
const game = e.target.parentNode.parentNode.getAttribute("data-target");
|
||||||
starredGames = JSON.parse(localStorage.getItem("selenite.starred") || "[]");
|
starredGames = JSON.parse(localStorage.getItem("selenite.starred") || "[]");
|
||||||
|
|||||||
22
js/main.js
22
js/main.js
@ -98,16 +98,18 @@ document.addEventListener("DOMContentLoaded", async ()=>{
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function handlePanicKeydown(e) {
|
||||||
let scrollRaf = null;
|
let panic;
|
||||||
document.addEventListener("scroll", () => {
|
try {
|
||||||
if (scrollRaf) return;
|
panic = JSON.parse(localStorage.getItem("selenite.panic-mode") || "{}");
|
||||||
scrollRaf = requestAnimationFrame(() => {
|
} catch {
|
||||||
const pjs = document.getElementById("pjs");
|
return;
|
||||||
if (pjs) pjs.style.top = window.scrollY + "px";
|
}
|
||||||
scrollRaf = null;
|
if (panic.key && panic.url && e.key.toLowerCase() === panic.key.toLowerCase()) {
|
||||||
});
|
location.href = panic.url;
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener("keydown", handlePanicKeydown);
|
||||||
|
|
||||||
if ("serviceWorker" in navigator) {
|
if ("serviceWorker" in navigator) {
|
||||||
navigator.serviceWorker.addEventListener('message', (event) => {
|
navigator.serviceWorker.addEventListener('message', (event) => {
|
||||||
|
|||||||
33
loader.html
33
loader.html
@ -20,6 +20,31 @@
|
|||||||
<script src="/js/all.min.js" defer></script>
|
<script src="/js/all.min.js" defer></script>
|
||||||
<script defer>
|
<script defer>
|
||||||
let gameData = {};
|
let gameData = {};
|
||||||
|
function recordRecentlyPlayed(directory) {
|
||||||
|
if (!directory) return;
|
||||||
|
let recent = [];
|
||||||
|
try {
|
||||||
|
recent = JSON.parse(localStorage.getItem("selenite.recentlyPlayed") || "[]");
|
||||||
|
} catch {
|
||||||
|
recent = [];
|
||||||
|
}
|
||||||
|
recent = recent.filter((dir) => dir !== directory);
|
||||||
|
recent.unshift(directory);
|
||||||
|
if (recent.length > 20) recent.length = 20;
|
||||||
|
localStorage.setItem("selenite.recentlyPlayed", JSON.stringify(recent));
|
||||||
|
}
|
||||||
|
function handlePanicKeydown(e) {
|
||||||
|
let panic;
|
||||||
|
try {
|
||||||
|
panic = JSON.parse(localStorage.getItem("selenite.panic-mode") || "{}");
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (panic.key && panic.url && e.key.toLowerCase() === panic.key.toLowerCase()) {
|
||||||
|
location.href = panic.url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener("keydown", handlePanicKeydown);
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
const gameFrame = document.getElementById("gameFrame");
|
const gameFrame = document.getElementById("gameFrame");
|
||||||
const gameImage = document.getElementById("gameImage");
|
const gameImage = document.getElementById("gameImage");
|
||||||
@ -35,6 +60,7 @@
|
|||||||
gameFrame.src = `/resources/${prefix}/${gameData.path}/index.html`;
|
gameFrame.src = `/resources/${prefix}/${gameData.path}/index.html`;
|
||||||
gameImage.src = `/resources/${prefix}/${gameData.path}/${params.get("img")}`;
|
gameImage.src = `/resources/${prefix}/${gameData.path}/${params.get("img")}`;
|
||||||
gameName.innerText = decodeURIComponent(params.get("title"));
|
gameName.innerText = decodeURIComponent(params.get("title"));
|
||||||
|
recordRecentlyPlayed(gameData.path);
|
||||||
} else if (localStorage.getItem("selenite.lastGame")) {
|
} else if (localStorage.getItem("selenite.lastGame")) {
|
||||||
const game = JSON.parse(decodeURIComponent(atob(localStorage.getItem("selenite.lastGame"))));
|
const game = JSON.parse(decodeURIComponent(atob(localStorage.getItem("selenite.lastGame"))));
|
||||||
gameData.type = game[3];
|
gameData.type = game[3];
|
||||||
@ -43,8 +69,15 @@
|
|||||||
gameFrame.src = `/resources/${prefix}/${gameData.path}/index.html`;
|
gameFrame.src = `/resources/${prefix}/${gameData.path}/index.html`;
|
||||||
gameImage.src = `/resources/${prefix}/${gameData.path}/${decodeURIComponent(game[1])}`;
|
gameImage.src = `/resources/${prefix}/${gameData.path}/${decodeURIComponent(game[1])}`;
|
||||||
gameName.innerText = game[2];
|
gameName.innerText = game[2];
|
||||||
|
recordRecentlyPlayed(gameData.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gameFrame.addEventListener("load", () => {
|
||||||
|
try {
|
||||||
|
gameFrame.contentWindow.document.addEventListener("keydown", handlePanicKeydown);
|
||||||
|
} catch { }
|
||||||
|
});
|
||||||
|
|
||||||
document.getElementById("fullscreen").addEventListener("click", () => gameFrame.requestFullscreen());
|
document.getElementById("fullscreen").addEventListener("click", () => gameFrame.requestFullscreen());
|
||||||
document.getElementById("reload").addEventListener("click", () => { gameFrame.src = gameFrame.src; });
|
document.getElementById("reload").addEventListener("click", () => { gameFrame.src = gameFrame.src; });
|
||||||
document.getElementById("open").addEventListener("click", () => window.open(gameFrame.src));
|
document.getElementById("open").addEventListener("click", () => window.open(gameFrame.src));
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-J0Y3T4HN4N"></script>
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-J0Y3T4HN4N"></script>
|
||||||
<script>
|
<script>
|
||||||
window.dataLayer = window.dataLayer || [];
|
window.dataLayer = window.dataLayer || [];
|
||||||
function gtag(){dataLayer.push(arguments);}
|
function gtag() { dataLayer.push(arguments); }
|
||||||
gtag('js', new Date());
|
gtag('js', new Date());
|
||||||
|
|
||||||
gtag('config', 'G-J0Y3T4HN4N');
|
gtag('config', 'G-J0Y3T4HN4N');
|
||||||
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<!-- seo + other things -->
|
<!-- seo + other things -->
|
||||||
<title>Projects | Selenite</title>
|
<title>Selenite</title>
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||||
@ -40,7 +40,13 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
<div class="gamesToolbar">
|
||||||
<input type="text" class="searchbar" id="gamesearch" placeholder="Type here to search.." />
|
<input type="text" class="searchbar" id="gamesearch" placeholder="Type here to search.." />
|
||||||
|
<button id="filtersToggle" type="button">filters</button>
|
||||||
|
<button id="randomGame">random game</button>
|
||||||
|
</div>
|
||||||
|
<div id="tagFilters">
|
||||||
|
<p class="filterLabel">sorting</p>
|
||||||
<select id="gamesort">
|
<select id="gamesort">
|
||||||
<option value="az">a -> z</option>
|
<option value="az">a -> z</option>
|
||||||
<option value="za">z -> a</option>
|
<option value="za">z -> a</option>
|
||||||
@ -48,9 +54,14 @@
|
|||||||
<option value="played">your top games</option>
|
<option value="played">your top games</option>
|
||||||
<option value="global">global top games</option>
|
<option value="global">global top games</option>
|
||||||
</select>
|
</select>
|
||||||
|
<p class="filterLabel" id="tagChipsLabel">game genres</p>
|
||||||
|
<div id="tagChips"></div>
|
||||||
|
</div>
|
||||||
<p id="gameCount">xx games loaded..</p>
|
<p id="gameCount">xx games loaded..</p>
|
||||||
<p id="starredHeader">starred games</p>
|
<p id="starredHeader">starred games</p>
|
||||||
<div id="starredgames"></div>
|
<div id="starredgames"></div>
|
||||||
|
<p id="recentHeader">recently played</p>
|
||||||
|
<div id="recentgames"></div>
|
||||||
<p id="topHeader" class="title">top games</p>
|
<p id="topHeader" class="title">top games</p>
|
||||||
<div id="topGames"></div>
|
<div id="topGames"></div>
|
||||||
<p id="allHeader" class="title">all games</p>
|
<p id="allHeader" class="title">all games</p>
|
||||||
|
|||||||
28
proxy-locked.html
Normal file
28
proxy-locked.html
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-J0Y3T4HN4N"></script>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
gtag('js', new Date());
|
||||||
|
|
||||||
|
gtag('config', 'G-J0Y3T4HN4N');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script src="/js/main.js" defer></script>
|
||||||
|
<link rel="stylesheet" href="/css/main.css" />
|
||||||
|
<link rel="stylesheet" href="/css/pages.css" />
|
||||||
|
<link rel="manifest" href="/manifest.json" />
|
||||||
|
<title>Selenite</title>
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1 class="title">locked</h1>
|
||||||
|
<p>this is locked behind a secret password.</p>
|
||||||
|
<p>enter it in <a href="/settings.html">settings</a> to unlock it.</p>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
201
proxy.html
Normal file
201
proxy.html
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<!-- Google tag (gtag.js) -->
|
||||||
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-J0Y3T4HN4N"></script>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag() { dataLayer.push(arguments); }
|
||||||
|
gtag('js', new Date());
|
||||||
|
|
||||||
|
gtag('config', 'G-J0Y3T4HN4N');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": "Selenite",
|
||||||
|
"alternateName": "selenite.cc",
|
||||||
|
"url": "https://selenite.cc",
|
||||||
|
"logo": "https://selenite.cc/favicon.png",
|
||||||
|
"sameAs": [
|
||||||
|
"https://github.com/selenite-cc",
|
||||||
|
"https://youtube.com/@selenitecc",
|
||||||
|
"https://tiktok.com/@selenitecc",
|
||||||
|
"https://selenite.cc",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/css/main.css" />
|
||||||
|
<link rel="stylesheet" href="/css/pages.css" />
|
||||||
|
<link rel="stylesheet" href="/css/proxy.css" />
|
||||||
|
<link rel="manifest" href="/manifest.json" />
|
||||||
|
<title>Selenite</title>
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
|
||||||
|
<script src="/scram-assets/scramjet.all.js"></script>
|
||||||
|
<script src="/scram-assets/baremux/index.js"></script>
|
||||||
|
<script defer>
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const swAllowedHostnames = ["localhost", "127.0.0.1"];
|
||||||
|
|
||||||
|
async function registerProxySW() {
|
||||||
|
if (!navigator.serviceWorker) {
|
||||||
|
throw new Error("your browser doesn't support service workers.");
|
||||||
|
}
|
||||||
|
if (location.protocol !== "https:" && !swAllowedHostnames.includes(location.hostname)) {
|
||||||
|
throw new Error("service workers cannot be registered without https.");
|
||||||
|
}
|
||||||
|
const registration = await navigator.serviceWorker.register("/scram-sw.js", { scope: "/scram/" });
|
||||||
|
const worker = registration.installing || registration.waiting || registration.active;
|
||||||
|
if (worker && worker.state !== "activated") {
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
worker.addEventListener("statechange", () => {
|
||||||
|
if (worker.state === "activated") resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderProxyShortcuts(onSelect) {
|
||||||
|
const grid = document.getElementById("proxyShortcutsGrid");
|
||||||
|
if (!grid) return;
|
||||||
|
try {
|
||||||
|
const sites = await (await fetch("/resources/proxysites.json")).json();
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
sites.forEach((site) => {
|
||||||
|
const button = document.createElement("button");
|
||||||
|
button.type = "button";
|
||||||
|
button.className = "proxyShortcut";
|
||||||
|
button.dataset.url = site.url;
|
||||||
|
|
||||||
|
const icon = document.createElement("img");
|
||||||
|
icon.src = site.icon || `https://s2.googleusercontent.com/s2/favicons?domain_url=${encodeURIComponent(site.url)}&sz=256`;
|
||||||
|
if (site.iconBg) {
|
||||||
|
icon.style.backgroundColor = site.iconBg;
|
||||||
|
icon.style.padding = "10px";
|
||||||
|
icon.style.boxSizing = "border-box";
|
||||||
|
}
|
||||||
|
|
||||||
|
const holder = document.createElement("div");
|
||||||
|
holder.className = "proxyShortcutHolder";
|
||||||
|
const title = document.createElement("h1");
|
||||||
|
title.innerText = site.name;
|
||||||
|
holder.appendChild(title);
|
||||||
|
|
||||||
|
button.appendChild(icon);
|
||||||
|
button.appendChild(holder);
|
||||||
|
button.addEventListener("click", () => onSelect(site.url));
|
||||||
|
|
||||||
|
fragment.appendChild(button);
|
||||||
|
});
|
||||||
|
grid.appendChild(fragment);
|
||||||
|
} catch {
|
||||||
|
const shortcuts = document.getElementById("proxyShortcuts");
|
||||||
|
if (shortcuts) shortcuts.style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveAddress(input) {
|
||||||
|
try {
|
||||||
|
return new URL(input).toString();
|
||||||
|
} catch { }
|
||||||
|
try {
|
||||||
|
const url = new URL(`http://${input}`);
|
||||||
|
if (url.hostname.includes(".")) return url.toString();
|
||||||
|
} catch { }
|
||||||
|
return `https://www.google.com/search?q=${encodeURIComponent(input)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const form = document.getElementById("sj-form");
|
||||||
|
const address = document.getElementById("sj-address");
|
||||||
|
const error = document.getElementById("sj-error");
|
||||||
|
const errorCode = document.getElementById("sj-error-code");
|
||||||
|
const frameHolder = document.getElementById("proxyFrameHolder");
|
||||||
|
|
||||||
|
const { ScramjetController } = $scramjetLoadController();
|
||||||
|
const scramjet = new ScramjetController({
|
||||||
|
prefix: "/scram/",
|
||||||
|
files: {
|
||||||
|
wasm: "/scram-assets/scramjet.wasm.wasm",
|
||||||
|
all: "/scram-assets/scramjet.all.js",
|
||||||
|
sync: "/scram-assets/scramjet.sync.js",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
scramjet.init();
|
||||||
|
|
||||||
|
const connection = new BareMux.BareMuxConnection("/scram-assets/baremux/worker.js");
|
||||||
|
|
||||||
|
form.addEventListener("submit", async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
error.textContent = "";
|
||||||
|
errorCode.textContent = "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
await registerProxySW();
|
||||||
|
} catch (err) {
|
||||||
|
error.textContent = "failed to register service worker.";
|
||||||
|
errorCode.textContent = err.toString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = resolveAddress(address.value);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const wispUrl = (location.protocol === "https:" ? "wss" : "ws") + "://" + location.host + "/wisp/";
|
||||||
|
if ((await connection.getTransport()) !== "/scram-assets/libcurl/index.mjs") {
|
||||||
|
await connection.setTransport("/scram-assets/libcurl/index.mjs", [{ websocket: wispUrl }]);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
error.textContent = "failed to set up the proxy connection.";
|
||||||
|
errorCode.textContent = err.toString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
frameHolder.innerHTML = "";
|
||||||
|
const frame = scramjet.createFrame();
|
||||||
|
frame.frame.id = "sj-frame";
|
||||||
|
frameHolder.appendChild(frame.frame);
|
||||||
|
frame.go(url);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("proxyFullscreen").addEventListener("click", () => {
|
||||||
|
const frame = document.getElementById("sj-frame");
|
||||||
|
if (frame) frame.requestFullscreen();
|
||||||
|
});
|
||||||
|
|
||||||
|
renderProxyShortcuts((url) => {
|
||||||
|
address.value = url;
|
||||||
|
form.requestSubmit();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="proxyBar">
|
||||||
|
<a href="/home.html" title="back to home"><img src="/img/home.svg" /></a>
|
||||||
|
<form id="sj-form">
|
||||||
|
<input id="sj-address" type="text" placeholder="search or enter a url" autocomplete="off" />
|
||||||
|
<button type="submit">go</button>
|
||||||
|
</form>
|
||||||
|
<img id="proxyFullscreen" src="/img/fullscreen.svg" title="fullscreen" />
|
||||||
|
</div>
|
||||||
|
<div id="proxyError">
|
||||||
|
<p id="sj-error"></p>
|
||||||
|
<pre id="sj-error-code"></pre>
|
||||||
|
</div>
|
||||||
|
<div id="proxyFrameHolder">
|
||||||
|
<div id="proxyShortcuts">
|
||||||
|
<p class="filterLabel">recommended sites</p>
|
||||||
|
<div id="proxyShortcutsGrid"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
16
scram-sw.js
Normal file
16
scram-sw.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
importScripts("/scram-assets/scramjet.all.js");
|
||||||
|
|
||||||
|
const { ScramjetServiceWorker } = $scramjetLoadWorker();
|
||||||
|
const scramjet = new ScramjetServiceWorker();
|
||||||
|
|
||||||
|
async function handleRequest(event) {
|
||||||
|
await scramjet.loadConfig();
|
||||||
|
if (scramjet.route(event)) {
|
||||||
|
return scramjet.fetch(event);
|
||||||
|
}
|
||||||
|
return fetch(event.request);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addEventListener("fetch", (event) => {
|
||||||
|
event.respondWith(handleRequest(event));
|
||||||
|
});
|
||||||
263
settings.html
263
settings.html
@ -6,7 +6,7 @@
|
|||||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-J0Y3T4HN4N"></script>
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-J0Y3T4HN4N"></script>
|
||||||
<script>
|
<script>
|
||||||
window.dataLayer = window.dataLayer || [];
|
window.dataLayer = window.dataLayer || [];
|
||||||
function gtag(){dataLayer.push(arguments);}
|
function gtag() { dataLayer.push(arguments); }
|
||||||
gtag('js', new Date());
|
gtag('js', new Date());
|
||||||
|
|
||||||
gtag('config', 'G-J0Y3T4HN4N');
|
gtag('config', 'G-J0Y3T4HN4N');
|
||||||
@ -84,9 +84,19 @@
|
|||||||
if (localStorage.getItem("selenite.super-fast-mode") == 'true') {
|
if (localStorage.getItem("selenite.super-fast-mode") == 'true') {
|
||||||
document.getElementById("superFastMode").setAttribute("checked", "")
|
document.getElementById("superFastMode").setAttribute("checked", "")
|
||||||
};
|
};
|
||||||
if (localStorage.getItem("selenite.disableTopGames") == 'false') {
|
if (localStorage.getItem("selenite.disableTopGames") == 'true') {
|
||||||
document.getElementById("topGamesToggle").setAttribute("checked", "")
|
document.getElementById("topGamesToggle").setAttribute("checked", "")
|
||||||
};
|
};
|
||||||
|
if (localStorage.getItem("selenite.disableRecentlyPlayed") == 'true') {
|
||||||
|
document.getElementById("recentlyPlayedToggle").setAttribute("checked", "")
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const savedPanic = JSON.parse(localStorage.getItem("selenite.panic-mode") || "null");
|
||||||
|
if (savedPanic && savedPanic.key && savedPanic.url) {
|
||||||
|
document.getElementById("panicKey").value = savedPanic.key;
|
||||||
|
document.getElementById("panicURL").value = savedPanic.url;
|
||||||
|
}
|
||||||
|
} catch { }
|
||||||
document.getElementById("themes").addEventListener("input", (e) => {
|
document.getElementById("themes").addEventListener("input", (e) => {
|
||||||
localStorage.setItem("selenite.theme", e.target.value);
|
localStorage.setItem("selenite.theme", e.target.value);
|
||||||
document.body.setAttribute("theme", e.target.value);
|
document.body.setAttribute("theme", e.target.value);
|
||||||
@ -114,26 +124,93 @@
|
|||||||
location.reload();
|
location.reload();
|
||||||
});
|
});
|
||||||
document.getElementById("topGamesToggle").addEventListener("click", (e) => {
|
document.getElementById("topGamesToggle").addEventListener("click", (e) => {
|
||||||
localStorage.setItem("selenite.disableTopGames", (e.target.checked == false));
|
localStorage.setItem("selenite.disableTopGames", e.target.checked ? "true" : "false");
|
||||||
});
|
});
|
||||||
document.getElementById("submitCloak").addEventListener("click", (e) => {
|
document.getElementById("recentlyPlayedToggle").addEventListener("click", (e) => {
|
||||||
if (document.getElementById("cloaks").value) {
|
localStorage.setItem("selenite.disableRecentlyPlayed", e.target.checked ? "true" : "false");
|
||||||
localStorage.setItem("selenite.tab-cloak", JSON.stringify({
|
});
|
||||||
"name": cloaks[document.getElementById("cloaks").value][0],
|
document.getElementById("submitCloak").addEventListener("click", () => {
|
||||||
"icon": cloaks[document.getElementById("cloaks").value][1]
|
const feedback = document.getElementById("cloakFeedback");
|
||||||
}));
|
const presetValue = document.getElementById("cloaks").value;
|
||||||
|
let name, icon;
|
||||||
|
if (presetValue) {
|
||||||
|
name = cloaks[presetValue][0];
|
||||||
|
icon = cloaks[presetValue][1];
|
||||||
} else {
|
} else {
|
||||||
localStorage.setItem("selenite.tab-cloak", JSON.stringify({
|
name = document.getElementById("cloakName").value.trim();
|
||||||
"name": document.getElementById("cloakName").value || "",
|
icon = document.getElementById("cloakIcon").value.trim();
|
||||||
"icon": document.getElementById("cloakIcon").value || ""
|
}
|
||||||
}));
|
localStorage.setItem("selenite.tab-cloak", JSON.stringify({ name, icon }));
|
||||||
|
if (!name && !icon) {
|
||||||
|
feedback.textContent = "tab cloak reset to default.";
|
||||||
|
feedback.className = "feedback";
|
||||||
|
} else {
|
||||||
|
feedback.textContent = `tab cloak set to "${name || "Selenite"}"!`;
|
||||||
|
feedback.className = "feedback success";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
document.getElementById("submitPanic").addEventListener("click", (e) => {
|
document.getElementById("submitPanic").addEventListener("click", () => {
|
||||||
localStorage.setItem("selenite.panic-mode", JSON.stringify({
|
const feedback = document.getElementById("panicFeedback");
|
||||||
"key": document.getElementById("panicKey").value || "",
|
const key = document.getElementById("panicKey").value.trim();
|
||||||
"url": document.getElementById("panicURL").value || ""
|
let url = document.getElementById("panicURL").value.trim();
|
||||||
}));
|
|
||||||
|
if (!key) {
|
||||||
|
feedback.textContent = "enter a key to trigger panic mode.";
|
||||||
|
feedback.className = "feedback error";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!url) {
|
||||||
|
feedback.textContent = "enter a website to redirect to.";
|
||||||
|
feedback.className = "feedback error";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!/^https?:\/\//i.test(url)) {
|
||||||
|
url = "https://" + url;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
new URL(url);
|
||||||
|
} catch {
|
||||||
|
feedback.textContent = "that doesn't look like a valid website url.";
|
||||||
|
feedback.className = "feedback error";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem("selenite.panic-mode", JSON.stringify({ key, url }));
|
||||||
|
document.getElementById("panicURL").value = url;
|
||||||
|
feedback.textContent = `panic mode saved! press "${key}" to instantly leave.`;
|
||||||
|
feedback.className = "feedback success";
|
||||||
|
});
|
||||||
|
document.getElementById("submitProxyUnlock").addEventListener("click", () => {
|
||||||
|
const feedback = document.getElementById("proxyUnlockFeedback");
|
||||||
|
const passwordInput = document.getElementById("proxyPassword");
|
||||||
|
const password = passwordInput.value;
|
||||||
|
|
||||||
|
if (!password) {
|
||||||
|
feedback.textContent = "enter a password.";
|
||||||
|
feedback.className = "feedback error";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch("/api/proxy/unlock", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ password }),
|
||||||
|
})
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
if (data.success) {
|
||||||
|
feedback.textContent = "beta access granted!";
|
||||||
|
feedback.className = "feedback success";
|
||||||
|
passwordInput.value = "";
|
||||||
|
} else {
|
||||||
|
feedback.textContent = data.message || "incorrect password.";
|
||||||
|
feedback.className = "feedback error";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
feedback.textContent = "failed to reach the server.";
|
||||||
|
feedback.className = "feedback error";
|
||||||
|
});
|
||||||
});
|
});
|
||||||
document.getElementById("toggleCDN").addEventListener("change", () => {
|
document.getElementById("toggleCDN").addEventListener("change", () => {
|
||||||
let toggle = document.getElementById("toggleCDN");
|
let toggle = document.getElementById("toggleCDN");
|
||||||
@ -207,10 +284,14 @@
|
|||||||
updateHiddenGamesCount(games.length);
|
updateHiddenGamesCount(games.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const box = document.createElement("span");
|
||||||
|
box.className = "checkboxBox";
|
||||||
|
|
||||||
const text = document.createElement("span");
|
const text = document.createElement("span");
|
||||||
text.innerText = game.name;
|
text.innerText = game.name;
|
||||||
|
|
||||||
row.appendChild(checkbox);
|
row.appendChild(checkbox);
|
||||||
|
row.appendChild(box);
|
||||||
row.appendChild(text);
|
row.appendChild(text);
|
||||||
list.appendChild(row);
|
list.appendChild(row);
|
||||||
});
|
});
|
||||||
@ -234,6 +315,27 @@
|
|||||||
searchInput.addEventListener("input", () => {
|
searchInput.addEventListener("input", () => {
|
||||||
renderHiddenGameList(games, new Set(getHiddenGames()), searchInput.value);
|
renderHiddenGameList(games, new Set(getHiddenGames()), searchInput.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.getElementById("unhideAllGames").addEventListener("click", () => {
|
||||||
|
const feedback = document.getElementById("hiddenGamesFeedback");
|
||||||
|
const previousCount = getHiddenGames().length;
|
||||||
|
setHiddenGames([]);
|
||||||
|
renderHiddenGameList(games, new Set(), searchInput.value);
|
||||||
|
updateHiddenGamesCount(games.length);
|
||||||
|
if (feedback) {
|
||||||
|
if (previousCount > 0) {
|
||||||
|
feedback.textContent = `unhid ${previousCount} game${previousCount === 1 ? "" : "s"}!`;
|
||||||
|
feedback.className = "feedback success";
|
||||||
|
} else {
|
||||||
|
feedback.textContent = "no games were hidden.";
|
||||||
|
feedback.className = "feedback";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearRecentlyPlayed() {
|
||||||
|
localStorage.removeItem("selenite.recentlyPlayed");
|
||||||
}
|
}
|
||||||
|
|
||||||
function wipeData() {
|
function wipeData() {
|
||||||
@ -244,38 +346,85 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function setDataFeedback(message, type) {
|
||||||
|
const el = document.getElementById("dataFeedback");
|
||||||
|
if (!el) return;
|
||||||
|
el.textContent = message;
|
||||||
|
el.className = "feedback" + (type ? " " + type : "");
|
||||||
|
}
|
||||||
function downloadData() {
|
function downloadData() {
|
||||||
let storage = JSON.stringify(localStorage);
|
try {
|
||||||
let cookies = document.cookie;
|
const data = JSON.stringify({
|
||||||
let data = btoa(JSON.stringify({ localStorage: storage, cookies: cookies }));
|
localStorage: { ...localStorage },
|
||||||
|
cookies: document.cookie
|
||||||
let file = new Blob([data], { type: "text" });
|
});
|
||||||
let a = document.createElement("a");
|
const file = new Blob([data], { type: "application/json" });
|
||||||
a.href = URL.createObjectURL(file);
|
const url = URL.createObjectURL(file);
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = url;
|
||||||
a.download = "selenite.dat";
|
a.download = "selenite.dat";
|
||||||
a.click();
|
a.click();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
setDataFeedback("data downloaded!", "success");
|
||||||
|
} catch (err) {
|
||||||
|
setDataFeedback("failed to download data: " + err.message, "error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function parseBackup(text) {
|
||||||
|
let data;
|
||||||
|
try {
|
||||||
|
data = JSON.parse(text);
|
||||||
|
} catch {
|
||||||
|
// fall back to the old base64 + double-stringified-localStorage format
|
||||||
|
data = JSON.parse(atob(text));
|
||||||
|
}
|
||||||
|
if (!data || typeof data.cookies !== "string") {
|
||||||
|
throw new Error("this file doesn't look like a selenite backup.");
|
||||||
|
}
|
||||||
|
let local = data.localStorage;
|
||||||
|
if (typeof local === "string") local = JSON.parse(local);
|
||||||
|
if (!local || typeof local !== "object") {
|
||||||
|
throw new Error("this file doesn't look like a selenite backup.");
|
||||||
|
}
|
||||||
|
return { localStorage: local, cookies: data.cookies };
|
||||||
}
|
}
|
||||||
function uploadData() {
|
function uploadData() {
|
||||||
let input = document.createElement("input");
|
const input = document.createElement("input");
|
||||||
input.type = "file";
|
input.type = "file";
|
||||||
input.accept = "dat";
|
input.accept = ".dat,application/json";
|
||||||
input.click();
|
|
||||||
input.addEventListener("change", e => {
|
input.addEventListener("change", e => {
|
||||||
if (prompt("Are you sure you want to upload data?\nThis may overwrite any previous data you had stored.\n\nType \"i am sure\" to continue.") == "i am sure") {
|
const file = e.target.files[0];
|
||||||
let fileReader = new FileReader();
|
if (!file) return;
|
||||||
fileReader.addEventListener("load", e => {
|
if (prompt("Are you sure you want to upload data?\nThis may overwrite any previous data you had stored.\n\nType \"i am sure\" to continue.") != "i am sure") return;
|
||||||
let data = JSON.parse(atob(e.target.result));
|
|
||||||
let local = JSON.parse(data["localStorage"]);
|
|
||||||
let cookies = data["cookies"];
|
|
||||||
|
|
||||||
document.cookie = cookies;
|
const fileReader = new FileReader();
|
||||||
Object.keys(local).forEach(e => {
|
fileReader.addEventListener("load", e => {
|
||||||
localStorage.setItem(e, local[e]);
|
try {
|
||||||
})
|
const { localStorage: local, cookies } = parseBackup(e.target.result);
|
||||||
|
|
||||||
|
document.cookie.split(";").forEach(c => {
|
||||||
|
const name = c.split("=")[0].trim();
|
||||||
|
if (name) document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT";
|
||||||
});
|
});
|
||||||
fileReader.readAsText(e.target.files[0]);
|
cookies.split(";").forEach(c => {
|
||||||
|
c = c.trim();
|
||||||
|
if (c) document.cookie = c;
|
||||||
|
});
|
||||||
|
Object.keys(local).forEach(key => {
|
||||||
|
localStorage.setItem(key, local[key]);
|
||||||
|
});
|
||||||
|
|
||||||
|
setDataFeedback("data uploaded! reload the page to see changes.", "success");
|
||||||
|
} catch (err) {
|
||||||
|
setDataFeedback("failed to upload data: " + err.message, "error");
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
fileReader.addEventListener("error", () => {
|
||||||
|
setDataFeedback("failed to read that file.", "error");
|
||||||
|
});
|
||||||
|
fileReader.readAsText(file);
|
||||||
|
});
|
||||||
|
input.click();
|
||||||
};
|
};
|
||||||
async function deleteAllCaches() {
|
async function deleteAllCaches() {
|
||||||
try {
|
try {
|
||||||
@ -308,12 +457,18 @@
|
|||||||
</select>
|
</select>
|
||||||
<input type="color" id="colorPicker" style="display: none">
|
<input type="color" id="colorPicker" style="display: none">
|
||||||
<p style="font-size:20px;margin:6px;">toggles</p>
|
<p style="font-size:20px;margin:6px;">toggles</p>
|
||||||
<div class="samerow"><input type="checkbox" id="fastMode"><label for="fastMode"
|
<div class="toggleRow"><label class="switch"><input type="checkbox" id="fastMode"><span
|
||||||
|
class="switchTrack"></span></label><label for="fastMode"
|
||||||
title="disables particles, animated background, & other misc effects">fast mode</label></div>
|
title="disables particles, animated background, & other misc effects">fast mode</label></div>
|
||||||
<div class="samerow"><input type="checkbox" id="superFastMode"><label for="superFastMode"
|
<div class="toggleRow"><label class="switch"><input type="checkbox" id="superFastMode"><span
|
||||||
|
class="switchTrack"></span></label><label for="superFastMode"
|
||||||
title="disables all blurs and transparency">super fast mode</label></div>
|
title="disables all blurs and transparency">super fast mode</label></div>
|
||||||
<div class="samerow"><input type="checkbox" id="topGamesToggle"><label for="topGamesToggle">disable top
|
<div class="toggleRow"><label class="switch"><input type="checkbox" id="topGamesToggle"><span
|
||||||
|
class="switchTrack"></span></label><label for="topGamesToggle">disable top
|
||||||
games</label></div>
|
games</label></div>
|
||||||
|
<div class="toggleRow"><label class="switch"><input type="checkbox" id="recentlyPlayedToggle"><span
|
||||||
|
class="switchTrack"></span></label><label for="recentlyPlayedToggle">disable
|
||||||
|
recently played</label></div>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h2>tab cloak</h2>
|
<h2>tab cloak</h2>
|
||||||
@ -332,6 +487,7 @@
|
|||||||
</select>
|
</select>
|
||||||
|
|
||||||
<button id="submitCloak">set tab cloak</button>
|
<button id="submitCloak">set tab cloak</button>
|
||||||
|
<p id="cloakFeedback" class="feedback"></p>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h2>panic mode</h2>
|
<h2>panic mode</h2>
|
||||||
@ -340,6 +496,7 @@
|
|||||||
<p title="the url itll direct you to">website</p>
|
<p title="the url itll direct you to">website</p>
|
||||||
<input type="text" id="panicURL">
|
<input type="text" id="panicURL">
|
||||||
<button id="submitPanic">set panic mode</button>
|
<button id="submitPanic">set panic mode</button>
|
||||||
|
<p id="panicFeedback" class="feedback"></p>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h2>manage data</h2>
|
<h2>manage data</h2>
|
||||||
@ -347,13 +504,29 @@
|
|||||||
<button onclick="uploadData()">upload</button>
|
<button onclick="uploadData()">upload</button>
|
||||||
<button onclick="wipeData()">wipe (erases all game data)</button>
|
<button onclick="wipeData()">wipe (erases all game data)</button>
|
||||||
<button onclick="deleteAllCaches()">wipe all cache</button>
|
<button onclick="deleteAllCaches()">wipe all cache</button>
|
||||||
<input id="toggleCDN" name="toggleCDN" type="checkbox"></inpuit> <label for="toggleCDN">cdn enabled</label>
|
<button onclick="clearRecentlyPlayed()">clear recently played</button>
|
||||||
|
<div class="toggleRow"><label class="switch"><input id="toggleCDN" name="toggleCDN" type="checkbox"><span
|
||||||
|
class="switchTrack"></span></label><label for="toggleCDN">cdn enabled</label></div>
|
||||||
|
<p id="dataFeedback" class="feedback"></p>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section id="hiddenGamesSection">
|
||||||
<h2>hidden games</h2>
|
<h2>hidden games</h2>
|
||||||
<p id="hiddenGamesCount">0 hidden</p>
|
<p id="hiddenGamesCount">0 hidden</p>
|
||||||
|
<div class="hiddenGamesRow">
|
||||||
|
<div class="hiddenGamesControls">
|
||||||
<input type="text" id="hiddenGamesSearch" placeholder="search games">
|
<input type="text" id="hiddenGamesSearch" placeholder="search games">
|
||||||
|
<button id="unhideAllGames">unhide all</button>
|
||||||
|
<p id="hiddenGamesFeedback" class="feedback"></p>
|
||||||
|
</div>
|
||||||
<div id="hiddenGamesList"></div>
|
<div id="hiddenGamesList"></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2>beta access</h2>
|
||||||
|
<p title="the secret password for beta access">password</p>
|
||||||
|
<input type="password" id="proxyPassword" autocomplete="off">
|
||||||
|
<button id="submitProxyUnlock">unlock</button>
|
||||||
|
<p id="proxyUnlockFeedback" class="feedback"></p>
|
||||||
</section>
|
</section>
|
||||||
</sections>
|
</sections>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user