mirror of
https://github.com/driftywinds/driftywinds.github.io.git
synced 2025-12-19 19:13:33 +00:00
Initial commit
This commit is contained in:
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Temporary files
|
||||||
|
*~
|
||||||
|
~$*.doc*
|
||||||
|
~$*.xls*
|
||||||
|
~$*.ppt*
|
||||||
|
*.xlk
|
||||||
|
*.pdf
|
||||||
|
|
||||||
|
# .DS_Store files
|
||||||
|
*.DS_Store
|
||||||
|
|
||||||
|
# Preference files
|
||||||
|
.vscode/
|
||||||
105
app.html
Normal file
105
app.html
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="css/main.css">
|
||||||
|
<link rel="stylesheet" href="css/app.css">
|
||||||
|
<link rel="stylesheet" href="css/uibanner.css">
|
||||||
|
</head>
|
||||||
|
<body class="loading">
|
||||||
|
<div id="loading">
|
||||||
|
<img src="img/loading.gif" alt="loading">
|
||||||
|
<p>Loading</p>
|
||||||
|
</div>
|
||||||
|
<div id="main">
|
||||||
|
<div id="top">
|
||||||
|
<!-- Navigation bar -->
|
||||||
|
<div id="nav-bar">
|
||||||
|
<button id="back" type="button">
|
||||||
|
<i class="bi bi-chevron-left"></i>
|
||||||
|
Back
|
||||||
|
</button>
|
||||||
|
<div id="title" class="hidden">
|
||||||
|
<img src="https://raw.githubusercontent.com/therealFoxster/AltSource/master/img/icons/generic_app.jpeg" alt="">
|
||||||
|
<p>AltSource</p>
|
||||||
|
</div>
|
||||||
|
<a href="https://example.com" class="install hidden">
|
||||||
|
<button class="uibutton">Free</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Content -->
|
||||||
|
<div class="item">
|
||||||
|
<div class="app-header">
|
||||||
|
<div class="content">
|
||||||
|
<img src="https://raw.githubusercontent.com/therealFoxster/AltSource/master/img/icons/generic_app.jpeg" alt="">
|
||||||
|
<div class="right">
|
||||||
|
<div class="text">
|
||||||
|
<p class="title">AltSource</p>
|
||||||
|
<p class="subtitle">therealFoxster</p>
|
||||||
|
</div>
|
||||||
|
<div class="ipa">
|
||||||
|
<a href="https://example.com" class="install">
|
||||||
|
<button class="uibutton">Free</button>
|
||||||
|
</a>
|
||||||
|
<a href="http://example.com" id="download">
|
||||||
|
Download IPA
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="background"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="preview" class="section">
|
||||||
|
<p id="subtitle">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<div class="header">
|
||||||
|
<h2>Preview</h2>
|
||||||
|
</div>
|
||||||
|
<div id="screenshots"></div>
|
||||||
|
<p id="description">
|
||||||
|
The quick brown fox jumps over the lazy dog.
|
||||||
|
The quick brown fox jumps over the lazy dog.
|
||||||
|
The quick brown fox jumps over the lazy dog.
|
||||||
|
The quick brown fox jumps over the lazy dog.
|
||||||
|
The quick brown fox jumps over the lazy dog.
|
||||||
|
The quick brown fox jumps over the lazy dog.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div id="whats-new" class="section">
|
||||||
|
<div class="header">
|
||||||
|
<h2>What's New</h2>
|
||||||
|
<p id="version-date">Apr 10, 2023</p>
|
||||||
|
</div>
|
||||||
|
<div class="header">
|
||||||
|
<p id="version">Version 2.0</p>
|
||||||
|
<p id="version-size">0 KB</p>
|
||||||
|
</div>
|
||||||
|
<p id="version-description">
|
||||||
|
The quick brown fox jumps over the lazy dog.
|
||||||
|
The quick brown fox jumps over the lazy dog.
|
||||||
|
The quick brown fox jumps over the lazy dog.
|
||||||
|
The quick brown fox jumps over the lazy dog.
|
||||||
|
The quick brown fox jumps over the lazy dog.
|
||||||
|
The quick brown fox jumps over the lazy dog.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div id="permissions" class="section">
|
||||||
|
<div class="header">
|
||||||
|
<h2>Permissions</h2>
|
||||||
|
</div>
|
||||||
|
<div class="permission">
|
||||||
|
<i class="bi-person-fill-check"></i>
|
||||||
|
<div class="text">
|
||||||
|
<p class="title">None</p>
|
||||||
|
<p class="description">The developer has not specified any permission required by this app.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="js/main.js"></script>
|
||||||
|
<script src="js/app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
24
apps.html
Normal file
24
apps.html
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="css/main.css">
|
||||||
|
<link rel="stylesheet" href="css/apps.css">
|
||||||
|
<link rel="stylesheet" href="css/uibanner.css">
|
||||||
|
</head>
|
||||||
|
<body class="loading">
|
||||||
|
<div id="loading">
|
||||||
|
<img src="img/loading.gif" alt="loading">
|
||||||
|
<p>Loading</p>
|
||||||
|
</div>
|
||||||
|
<!-- Add to AltStore banner & navigation bar -->
|
||||||
|
<div id="top"></div>
|
||||||
|
<div id="main">
|
||||||
|
<div id="apps"></div>
|
||||||
|
</div>
|
||||||
|
<script src="js/main.js"></script>
|
||||||
|
<script src="js/apps.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
156
css/app.css
Normal file
156
css/app.css
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
a {
|
||||||
|
color: var(--app-tint-color)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main */
|
||||||
|
|
||||||
|
#main {
|
||||||
|
padding: unset;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main .item {
|
||||||
|
margin-top: 30%;
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-bar #title,
|
||||||
|
#nav-bar .install {
|
||||||
|
transition: opacity 0.25s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IPA install & download */
|
||||||
|
|
||||||
|
.ipa {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ipa #download {
|
||||||
|
position: absolute;
|
||||||
|
margin-top: 36px; /* 32px button height + 4px gap */
|
||||||
|
font-size: 0.65em;
|
||||||
|
font-weight: 450;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Preview */
|
||||||
|
|
||||||
|
#preview {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preview #subtitle {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.9em;
|
||||||
|
margin: 0 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preview .header {
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preview #screenshots {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
overflow: scroll;
|
||||||
|
padding-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preview #screenshots>img {
|
||||||
|
max-width: 70%;
|
||||||
|
margin-left: 1em;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#preview #description {
|
||||||
|
margin: 1em;
|
||||||
|
-webkit-line-clamp: 5;
|
||||||
|
line-clamp: 5;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
word-wrap: break-word
|
||||||
|
}
|
||||||
|
|
||||||
|
#more button {
|
||||||
|
min-width: 0 !important;
|
||||||
|
margin-left: 2px !important;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-transform: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: none;
|
||||||
|
background: unset;
|
||||||
|
color: var(--accent-color);
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
padding-left: 36px;
|
||||||
|
background: linear-gradient(to right, transparent, var(--color-bg) 35%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (hover:hover) {
|
||||||
|
#more button:hover {
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* What's new */
|
||||||
|
|
||||||
|
#whats-new,
|
||||||
|
#permissions {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#whats-new .header,
|
||||||
|
#permissions .header {
|
||||||
|
margin: 0.35em 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#whats-new .header>p:first-of-type {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#whats-new .header>p {
|
||||||
|
opacity: 0.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
#whats-new #version-description {
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
line-clamp: 3;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
word-wrap: break-word
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Permissions */
|
||||||
|
|
||||||
|
#permissions .permission {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75em;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#permissions .permission i {
|
||||||
|
font-size: 1.55em;
|
||||||
|
color: var(--app-tint-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#permissions .permission .title {
|
||||||
|
font-weight: 550;
|
||||||
|
}
|
||||||
|
|
||||||
|
#permissions .permission .description {
|
||||||
|
font-size: 0.9em;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
27
css/apps.css
Normal file
27
css/apps.css
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#main {
|
||||||
|
padding-top: 7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#apps .app-container {
|
||||||
|
margin: 1em 1rem 3.25em 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#apps .app-container:last-of-type {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screenshots {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screenshots:last-of-type {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screenshot {
|
||||||
|
max-width: 48%;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
45
css/index.css
Normal file
45
css/index.css
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#main {
|
||||||
|
padding-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main #title {
|
||||||
|
margin-bottom: 0.35rem;
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main input {
|
||||||
|
width: 95%;
|
||||||
|
font-size: 1em;
|
||||||
|
padding: 0.5rem 0.8rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.07);
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestion {
|
||||||
|
padding: 1rem 1rem 1rem 0;
|
||||||
|
margin-left: 1rem;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
border-bottom: 0.1px solid var(--color-separator);
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestion .bi {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
#main input {
|
||||||
|
background-color: rgba(255, 255, 255, 0.07);
|
||||||
|
}
|
||||||
|
.suggestion {
|
||||||
|
border-bottom: 0.1px solid var(--color-separator-dark) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#main .textfield {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
389
css/main.css
Normal file
389
css/main.css
Normal file
@@ -0,0 +1,389 @@
|
|||||||
|
@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.4/font/bootstrap-icons.css");
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--accent-color: #018084;
|
||||||
|
--app-tint-color: #018084;
|
||||||
|
|
||||||
|
--color-bg: rgba(255, 255, 255, 255);
|
||||||
|
--color-separator: rgba(0, 0, 0, 0.15);
|
||||||
|
--color-primary: rgba(0, 122, 254, 255);
|
||||||
|
--color-transparent: rgba(255, 255, 255, 0.75);
|
||||||
|
|
||||||
|
--color-bg-dark: rgb(26, 25, 27);
|
||||||
|
--color-separator-dark: rgba(255, 255, 255, 0.15);
|
||||||
|
--color-primary-dark: rgba(11, 132, 254, 255);
|
||||||
|
--color-transparent-dark: rgba(26, 25, 27, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark appearance */
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
|
||||||
|
body,
|
||||||
|
.uibanner,
|
||||||
|
#loading {
|
||||||
|
background-color: var(--color-bg-dark) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
h1,
|
||||||
|
h2 {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-bar {
|
||||||
|
background-color: var(--color-transparent-dark) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uibanner,
|
||||||
|
#nav-bar {
|
||||||
|
border-color: var(--color-separator-dark) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-header .background {
|
||||||
|
opacity: 0.35 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#more button {
|
||||||
|
background: linear-gradient(to right, transparent, var(--color-bg-dark) 35%) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body.loading {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0 auto;
|
||||||
|
z-index: 4;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 4px;
|
||||||
|
background: var(--color-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading img {
|
||||||
|
width: 24px;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading p {
|
||||||
|
font-size: 0.75em;
|
||||||
|
font-weight: 500;
|
||||||
|
text-transform: uppercase;
|
||||||
|
opacity: 0.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* h1#title {
|
||||||
|
padding-left: 1rem;
|
||||||
|
} */
|
||||||
|
|
||||||
|
body {
|
||||||
|
width: 100%;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-device-width: 767px) {
|
||||||
|
body {
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 414px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#top {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 5;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#title {
|
||||||
|
margin-bottom: 0.35em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main>p:first-of-type {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.9em;
|
||||||
|
opacity: 0.5;
|
||||||
|
padding-left: 1rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Navigation bar */
|
||||||
|
|
||||||
|
#nav-bar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 0.1px solid var(--color-separator);
|
||||||
|
background-color: var(--color-transparent);
|
||||||
|
-webkit-backdrop-filter: saturate(100%) blur(30px);
|
||||||
|
backdrop-filter: saturate(100%) blur(20px);
|
||||||
|
max-width: 414px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0.25em 0.75em;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-bar #title {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
min-height: 2.5em;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-bar #title>p {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-bar #title>img {
|
||||||
|
max-height: 2em;
|
||||||
|
border-radius: 0.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-bar #back {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-bar #back .bi {
|
||||||
|
margin-left: -8px;
|
||||||
|
-webkit-text-stroke: 1px;
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main */
|
||||||
|
|
||||||
|
#main {
|
||||||
|
/* padding: 1em; */
|
||||||
|
padding-top: 3.55rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Section header */
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* News */
|
||||||
|
|
||||||
|
#news-items {
|
||||||
|
display: grid;
|
||||||
|
height: 100%;
|
||||||
|
gap: 1em;
|
||||||
|
grid-auto-columns: 95%;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
overflow-x: scroll;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#news-items.one {
|
||||||
|
grid-auto-columns: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#news .item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 1.5em;
|
||||||
|
overflow: hidden;
|
||||||
|
min-width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#news .item>.text {
|
||||||
|
margin: 1.5em;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#news .item>.text>p {
|
||||||
|
opacity: 0.75;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#news .item .image-wrapper {
|
||||||
|
max-height: 15.15em;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#news img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* App header */
|
||||||
|
|
||||||
|
#apps.section .item {
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-header {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-header>.content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-header img {
|
||||||
|
display: block;
|
||||||
|
border-radius: 13.5px;
|
||||||
|
max-width: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-header .background {
|
||||||
|
position: absolute;
|
||||||
|
background-color: var(--accent-color);
|
||||||
|
opacity: 0.2;
|
||||||
|
border-radius: 1.5em;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-header>.content>.right {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
margin-left: 0.65em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-header>.content .text>.title {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-header>.content .text>.subtitle {
|
||||||
|
opacity: 0.5;
|
||||||
|
font-size: 0.85em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* About */
|
||||||
|
|
||||||
|
#about a:visited {
|
||||||
|
color: var(--accent-color)
|
||||||
|
}
|
||||||
|
|
||||||
|
#about.section .item {
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons */
|
||||||
|
|
||||||
|
button.uibutton {
|
||||||
|
min-width: 78px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 12px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
border: none;
|
||||||
|
border-radius: 20px;
|
||||||
|
color: white;
|
||||||
|
background-color: var(--accent-color);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.uibutton:active {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Links */
|
||||||
|
|
||||||
|
a {
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--accent-color)
|
||||||
|
}
|
||||||
|
|
||||||
|
a>button {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (hover:hover) {
|
||||||
|
a:hover {
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header a,
|
||||||
|
#nav-bar #back {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 1.05em;
|
||||||
|
font-weight: 450;
|
||||||
|
border: none;
|
||||||
|
background: unset;
|
||||||
|
padding: 0;
|
||||||
|
color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Badges */
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
margin-left: 0.05rem !important;
|
||||||
|
margin-right: 0.05rem !important;
|
||||||
|
padding: 0.5px 6.5px;
|
||||||
|
font-size: 10px;
|
||||||
|
line-height: 1.3333733333;
|
||||||
|
font-weight: 700;
|
||||||
|
border-radius: 12px;
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
box-sizing: border-box;
|
||||||
|
white-space: nowrap;
|
||||||
|
display: inline-block;
|
||||||
|
background: transparent;
|
||||||
|
text-transform: uppercase;
|
||||||
|
vertical-align: middle;
|
||||||
|
color: white;
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beta.badge {
|
||||||
|
margin-bottom: 2px;
|
||||||
|
background-color: #f5a10d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beta.badge::before {
|
||||||
|
content: "Beta";
|
||||||
|
}
|
||||||
|
|
||||||
|
.small.badge {
|
||||||
|
font-size: 8px;
|
||||||
|
}
|
||||||
19
css/news.css
Normal file
19
css/news.css
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#main {
|
||||||
|
padding-top: 8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#news {
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.news-item-wrapper {
|
||||||
|
margin-bottom: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.news-item-wrapper:last-of-type {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.news-item-wrapper .item {
|
||||||
|
margin-bottom: 1rem !important;
|
||||||
|
}
|
||||||
68
css/uibanner.css
Normal file
68
css/uibanner.css
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
.uibanner .icon {
|
||||||
|
max-width: 30px;
|
||||||
|
border-radius: 7px;
|
||||||
|
border: 0.75px solid rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.uibanner {
|
||||||
|
z-index: 5;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-bottom: 1px;
|
||||||
|
border-bottom: 0.1px solid var(--color-separator);
|
||||||
|
}
|
||||||
|
|
||||||
|
.uibanner {
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uibanner>.icon {
|
||||||
|
margin-left: 14px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uibanner>.content {
|
||||||
|
min-height: 28px;
|
||||||
|
width: 100%;
|
||||||
|
margin-left: 8px;
|
||||||
|
padding: 8px 16px 8px 0px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uibanner>.content>.text-container>.title-text {
|
||||||
|
padding-bottom: 0 !important;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 12.95px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uibanner>.content>.text-container>.detail-text {
|
||||||
|
padding-top: 0 !important;
|
||||||
|
font-size: 11px !important;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 12px !important;
|
||||||
|
opacity: 0.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uibanner>.content button {
|
||||||
|
min-width: 68px !important;
|
||||||
|
padding: 5px 13px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
border: none;
|
||||||
|
border-radius: 20px;
|
||||||
|
color: white;
|
||||||
|
background-color: rgb(0, 122, 254);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uibanner>.content a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: unset;
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
48
home.html
Normal file
48
home.html
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="css/uibanner.css">
|
||||||
|
<link rel="stylesheet" href="css/main.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="loading">
|
||||||
|
<div id="loading">
|
||||||
|
<img src="img/loading.gif" alt="loading">
|
||||||
|
<p>Loading</p>
|
||||||
|
</div>
|
||||||
|
<!-- Add to AltStore banner -->
|
||||||
|
<div id="top"></div>
|
||||||
|
<div id="main">
|
||||||
|
<p>Now viewing</p>
|
||||||
|
<div class="header">
|
||||||
|
<h1 id="title">altsource-v2</h1>
|
||||||
|
<a href="index.html"><i class="bi bi-pencil-square"></i></a>
|
||||||
|
</div>
|
||||||
|
<div id="news" class="section">
|
||||||
|
<div class="header">
|
||||||
|
<h2>News</h2>
|
||||||
|
<a href="index.html">View All</a>
|
||||||
|
</div>
|
||||||
|
<div id="news-items"></div>
|
||||||
|
</div>
|
||||||
|
<div id="apps" class="section">
|
||||||
|
<div class="header">
|
||||||
|
<h2>Featured Apps</h2>
|
||||||
|
<a href="index.html">View All Apps</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="about" class="section">
|
||||||
|
<div class="header">
|
||||||
|
<h2>About</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="js/main.js"></script>
|
||||||
|
<script src="js/home.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
61
index.html
Normal file
61
index.html
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="css/uibanner.css">
|
||||||
|
<link rel="stylesheet" href="css/main.css">
|
||||||
|
<link rel="stylesheet" href="css/index.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="main">
|
||||||
|
<h1 id="title">AltSource Viewer</h1>
|
||||||
|
<div class="textfield">
|
||||||
|
<input type="text" placeholder="Source URL">
|
||||||
|
</div>
|
||||||
|
<div id="suggestions" class="section">
|
||||||
|
<div class="header">
|
||||||
|
<h2>Suggested</h2>
|
||||||
|
</div>
|
||||||
|
<a href="home.html?source=https://therealfoxster.github.io/altsource/data/apps.json">
|
||||||
|
<div class="suggestion">
|
||||||
|
<i class="bi bi-search"></i>
|
||||||
|
Foxster's AltSource
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="home.html?source=https://qnblackcat.github.io/AltStore/apps.json">
|
||||||
|
<div class="suggestion">
|
||||||
|
<i class="bi bi-search"></i>
|
||||||
|
Qn_'s AltStore Repo
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="home.html?source=https://alt.getutm.app">
|
||||||
|
<div class="suggestion">
|
||||||
|
<i class="bi bi-search"></i>
|
||||||
|
UTM Repository
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="home.html?source=https://flyinghead.github.io/flycast-builds/altstore.json">
|
||||||
|
<div class="suggestion">
|
||||||
|
<i class="bi bi-search"></i>
|
||||||
|
Flyinghead
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="home.html?source=https://provenance-emu.com/apps.json">
|
||||||
|
<div class="suggestion">
|
||||||
|
<i class="bi bi-search"></i>
|
||||||
|
Provenance EMU
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="https://therealfoxster.github.io/altsource-viewer" target="_blank">
|
||||||
|
<div class="suggestion">
|
||||||
|
<i class="bi bi-code-slash"></i>
|
||||||
|
Source Code
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="js/index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
216
js/app.js
Normal file
216
js/app.js
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
const bundleId = urlSearchParams.get('id');
|
||||||
|
if (!urlSearchParams.has('id') || !bundleId) nope();
|
||||||
|
|
||||||
|
// Hide/show navigation bar title & install button
|
||||||
|
let hidden = false;
|
||||||
|
window.onscroll = function (e) {
|
||||||
|
const appName = document.querySelector(".app-header .text>.title");
|
||||||
|
const title = document.getElementById("title");
|
||||||
|
const button = document.querySelector("#nav-bar .install");
|
||||||
|
|
||||||
|
if (hidden && appName.getBoundingClientRect().y >= 72) { // App name not visible
|
||||||
|
hidden = false;
|
||||||
|
title.classList.add("hidden");
|
||||||
|
button.classList.add("hidden");
|
||||||
|
button.disaled = true;
|
||||||
|
} else if (!hidden && appName.getBoundingClientRect().y < 72) {
|
||||||
|
hidden = true;
|
||||||
|
title.classList.remove("hidden");
|
||||||
|
button.classList.remove("hidden");
|
||||||
|
button.disaled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(sourceURL)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(json => {
|
||||||
|
const apps = json.apps.filter(app => app.bundleIdentifier === bundleId);
|
||||||
|
const app = apps[0];
|
||||||
|
if (!app) {
|
||||||
|
alert(`Unable to find app matching bundle identifier "${bundleId}".\nYou will now be redirected to the home page.`);
|
||||||
|
nope();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.title = `${app.name} - ${json.name}`;
|
||||||
|
|
||||||
|
const tintColor = `#${app.tintColor}`;
|
||||||
|
|
||||||
|
if (tintColor)
|
||||||
|
document.querySelector(':root').style.setProperty("--app-tint-color", `${tintColor}`);
|
||||||
|
|
||||||
|
// Tint back button
|
||||||
|
const backButton = document.getElementById("back");
|
||||||
|
backButton.style.color = tintColor;
|
||||||
|
|
||||||
|
const installButtons = document.querySelectorAll("a.install");
|
||||||
|
installButtons.forEach(button => {
|
||||||
|
button.href = `altstore://install?url=${app.downloadURL}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const downloadButton = document.getElementById("download");
|
||||||
|
downloadButton.href = app.downloadURL;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Navigation bar
|
||||||
|
//
|
||||||
|
const navBar = document.getElementById("nav-bar");
|
||||||
|
const navBarIcon = navBar.querySelector("#title>img");
|
||||||
|
const navBarTitle = navBar.querySelector("#title>p");
|
||||||
|
const navBarInstallButton = navBar.querySelector(".uibutton");
|
||||||
|
|
||||||
|
navBarTitle.textContent = app.name;
|
||||||
|
navBarIcon.src = app.iconURL;
|
||||||
|
navBarInstallButton.style.backgroundColor = `${tintColor}`;
|
||||||
|
|
||||||
|
//
|
||||||
|
// App header
|
||||||
|
//
|
||||||
|
const appHeader = document.querySelector("#main .app-header");
|
||||||
|
const appHeaderIcon = appHeader.querySelector("img");
|
||||||
|
const appHeaderTitle = appHeader.querySelector(".title");
|
||||||
|
const appHeaderSubtitle = appHeader.querySelector(".subtitle");
|
||||||
|
const appHeaderInstallButton = appHeader.querySelector(".uibutton");
|
||||||
|
const appHeaderBackground = appHeader.querySelector(".background");
|
||||||
|
|
||||||
|
appHeaderIcon.src = app.iconURL;
|
||||||
|
appHeaderTitle.textContent = app.name;
|
||||||
|
appHeaderSubtitle.textContent = app.developerName;
|
||||||
|
appHeaderInstallButton.style.backgroundColor = tintColor;
|
||||||
|
appHeaderBackground.style.backgroundColor = tintColor;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Preview
|
||||||
|
//
|
||||||
|
const preview = document.getElementById("preview");
|
||||||
|
const previewSubtitle = preview.querySelector("#subtitle");
|
||||||
|
const previewScreenshots = preview.querySelector("#screenshots");
|
||||||
|
const previewDescription = preview.querySelector("#description");
|
||||||
|
|
||||||
|
previewSubtitle.textContent = app.subtitle;
|
||||||
|
app.screenshotURLs.forEach(url => {
|
||||||
|
previewScreenshots.insertAdjacentHTML("beforeend", `<img src="${url}" alt="">`);
|
||||||
|
});
|
||||||
|
|
||||||
|
let localizedDescription = app.localizedDescription;
|
||||||
|
|
||||||
|
const perviewDescriptionURLs = [...new Set(localizedDescription.match(urlRegex))]; // Creating set from array to remove duplicates
|
||||||
|
|
||||||
|
perviewDescriptionURLs.forEach(url => {
|
||||||
|
localizedDescription = localizedDescription.replaceAll(url, `<a href="${url}">${url}</a>`)
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
previewDescription.innerHTML = localizedDescription.replaceAll("\n", "<br>");
|
||||||
|
|
||||||
|
const more = `
|
||||||
|
<a id="more" onclick="revealTruncatedText(this);">
|
||||||
|
<button style="color: ${tintColor};">more</button>
|
||||||
|
</a>`;
|
||||||
|
|
||||||
|
if (previewDescription.scrollHeight > previewDescription.clientHeight)
|
||||||
|
previewDescription.insertAdjacentHTML("beforeend", more);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Version info
|
||||||
|
//
|
||||||
|
const versionDate = document.getElementById("version-date");
|
||||||
|
const version = document.getElementById("version");
|
||||||
|
const versionSize = document.getElementById("version-size");
|
||||||
|
const versionDescription = document.getElementById("version-description");
|
||||||
|
|
||||||
|
// Version date
|
||||||
|
const versionDateObject = new Date(app.versionDate),
|
||||||
|
month = versionDateObject.toUTCString().split(" ")[2],
|
||||||
|
date = versionDateObject.getDate(),
|
||||||
|
dateString = `${month} ${date}, ${versionDateObject.getFullYear()}`;
|
||||||
|
const today = new Date();
|
||||||
|
const msPerDay = 60 * 60 * 24 * 1000;
|
||||||
|
const msDifference = today.valueOf() - versionDateObject.valueOf();
|
||||||
|
versionDate.textContent = dateString;
|
||||||
|
if (msDifference <= msPerDay) // Today
|
||||||
|
versionDate.textContent = "Today";
|
||||||
|
else if (msDifference <= msPerDay * 2) // Yesterday
|
||||||
|
versionDate.textContent = "Yesterday";
|
||||||
|
|
||||||
|
// Version number
|
||||||
|
version.textContent = `Version ${app.version}`;
|
||||||
|
|
||||||
|
// Version size
|
||||||
|
const units = ["B", "KB", "MB", "GB"];
|
||||||
|
var appSize = app.size, c = 0;
|
||||||
|
while (appSize > 1024) {
|
||||||
|
appSize = parseFloat(appSize/1024).toFixed(1);
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
versionSize.textContent = `${appSize} ${units[c]}`;
|
||||||
|
|
||||||
|
// Version description
|
||||||
|
var appVersionDescription = app.versionDescription;
|
||||||
|
const urls = [...new Set(appVersionDescription.match(urlRegex))]; // Creating set from array to remove duplicates
|
||||||
|
|
||||||
|
urls.forEach(url =>
|
||||||
|
appVersionDescription = appVersionDescription.replaceAll(url, `<a href="${url}">${url}</a>`)
|
||||||
|
);
|
||||||
|
|
||||||
|
versionDescription.innerHTML = appVersionDescription.replaceAll("\n", "<br>");
|
||||||
|
if (versionDescription.scrollHeight > versionDescription.clientHeight)
|
||||||
|
versionDescription.insertAdjacentHTML("beforeend", more);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Permissions
|
||||||
|
//
|
||||||
|
const permissions = document.getElementById("permissions");
|
||||||
|
|
||||||
|
if (app.permissions)
|
||||||
|
permissions.querySelector(".permission").remove();
|
||||||
|
|
||||||
|
app.permissions?.forEach(permission => {
|
||||||
|
let permissionType = "Unknown", icon = "gear-wide-connected";
|
||||||
|
switch (permission.type) {
|
||||||
|
case "background-audio":
|
||||||
|
permissionType = "Background Audio";
|
||||||
|
// icon = "audio";
|
||||||
|
icon = "volume-up-fill";
|
||||||
|
break;
|
||||||
|
case "background-fetch":
|
||||||
|
permissionType = "Background Fetch";
|
||||||
|
// icon = "fetch";
|
||||||
|
icon = "arrow-repeat"
|
||||||
|
break;
|
||||||
|
case "photos":
|
||||||
|
permissionType = "Photos"
|
||||||
|
// icon = "photos";
|
||||||
|
icon = "image-fill";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const html = `
|
||||||
|
<div class="permission">
|
||||||
|
<i class="bi-${icon}" style="color: ${tintColor};"></i>
|
||||||
|
<div class="text">
|
||||||
|
<p class="title">${permissionType}</p>
|
||||||
|
<p class="description">${permission.usageDescription ?? "No description provided."}</p>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
permissions.insertAdjacentHTML("beforeend", html);
|
||||||
|
});
|
||||||
|
|
||||||
|
waitForAllImagesToLoad();
|
||||||
|
});
|
||||||
|
|
||||||
|
function revealTruncatedText(moreButton) {
|
||||||
|
const textId = moreButton.parentNode.id;
|
||||||
|
const text = document.getElementById(textId);
|
||||||
|
text.style.display = "block";
|
||||||
|
text.style.overflow = "auto";
|
||||||
|
text.style.webkitLineClamp = "none";
|
||||||
|
text.style.lineClamp = "none";
|
||||||
|
text.removeChild(moreButton)
|
||||||
|
}
|
||||||
|
|
||||||
|
function nope() {
|
||||||
|
window.location.replace("index.html");
|
||||||
|
}
|
||||||
37
js/apps.js
Normal file
37
js/apps.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
addNavigationBar("All Apps");
|
||||||
|
|
||||||
|
fetch(sourceURL)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(json => {
|
||||||
|
if (json.tintColor) setTintColor(json.tintColor)
|
||||||
|
|
||||||
|
document.title = `Apps - ${json.name}`;
|
||||||
|
|
||||||
|
json.apps.sort((a, b) => (new Date(b.versionDate)).valueOf() - (new Date(a.versionDate)).valueOf());
|
||||||
|
json.apps.forEach(app => {
|
||||||
|
if (app.beta) return; // Ignore beta apps
|
||||||
|
|
||||||
|
const urls = app.screenshotURLs;
|
||||||
|
|
||||||
|
let html = `
|
||||||
|
<div class="app-container">`;
|
||||||
|
html +=
|
||||||
|
appHeaderHTML(app);
|
||||||
|
html += `
|
||||||
|
<p style="text-align: center; font-size: 0.9em;">${app.subtitle ?? ""}</p>`;
|
||||||
|
if (urls) {
|
||||||
|
html += `
|
||||||
|
<div class="screenshots">`;
|
||||||
|
for (let i = 0; i < urls.length, i < 2; i++) html += `
|
||||||
|
<img src="${urls[i]}" class="screenshot">`;
|
||||||
|
html += `
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
html += `
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
document.getElementById("apps").insertAdjacentHTML("beforeend", html);
|
||||||
|
});
|
||||||
|
|
||||||
|
waitForAllImagesToLoad();
|
||||||
|
});
|
||||||
72
js/home.js
Normal file
72
js/home.js
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
fetch(sourceURL)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(json => {
|
||||||
|
document.querySelector("#news a").href = `news.html?source=${sourceURL}`;
|
||||||
|
document.querySelector("#apps a").href = `apps.html?source=${sourceURL}`;
|
||||||
|
|
||||||
|
if (json.tintColor) setTintColor(json.tintColor)
|
||||||
|
|
||||||
|
document.title = json.name;
|
||||||
|
document.getElementById("title").innerText = json.name;
|
||||||
|
|
||||||
|
// Sort apps in descending order
|
||||||
|
json.apps.sort((a, b) => {
|
||||||
|
// If b < a
|
||||||
|
return (new Date(b.versionDate)).valueOf() - (new Date(a.versionDate)).valueOf();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (json.news && json.news.length >= 1) {
|
||||||
|
// Sort news in decending order (latest first)
|
||||||
|
json.news.sort((a, b) => (new Date(b.date)).valueOf() - (new Date(a.date)).valueOf());
|
||||||
|
|
||||||
|
// News
|
||||||
|
if (json.news.length == 1) {
|
||||||
|
document.getElementById("news-items").insertAdjacentHTML("beforeend", newsItemHTML(json.news[0], json.apps, true));
|
||||||
|
document.getElementById("news-items").classList.add("one");
|
||||||
|
} else if (json.news.length > 1) {
|
||||||
|
for (let i = 0; i < 5 && i < json.news.length; i++) {
|
||||||
|
document.getElementById("news-items").insertAdjacentHTML("beforeend", newsItemHTML(json.news[i], json.apps, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
document.getElementById("news").remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apps
|
||||||
|
let count = 1;
|
||||||
|
json.apps.forEach(app => {
|
||||||
|
// Max: 3 featured apps if not specified
|
||||||
|
if (count > 3) return;
|
||||||
|
|
||||||
|
// Ignore beta apps
|
||||||
|
if (app.beta) return;
|
||||||
|
|
||||||
|
// If there are featured apps, ignore non-featured apps
|
||||||
|
if (json.featuredApps && !json.featuredApps.includes(app.bundleIdentifier)) return;
|
||||||
|
|
||||||
|
document.getElementById("apps").insertAdjacentHTML("beforeend", appHeaderHTML(app));
|
||||||
|
|
||||||
|
count++;
|
||||||
|
});
|
||||||
|
|
||||||
|
var description = json.description;
|
||||||
|
|
||||||
|
if (description) {
|
||||||
|
const urls = [...new Set(description.match(urlRegex))]; // Creating set from array to remove duplicates
|
||||||
|
|
||||||
|
urls.forEach(url =>
|
||||||
|
description = description.replaceAll(url, `<a href="${url}">${url}</a>`)
|
||||||
|
);
|
||||||
|
|
||||||
|
document.getElementById("about").insertAdjacentHTML("beforeend", `
|
||||||
|
<div class="item">
|
||||||
|
<p>${description.replaceAll("\n", "<br>")}</p>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
} else {
|
||||||
|
document.getElementById("about").remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
waitForAllImagesToLoad();
|
||||||
|
});
|
||||||
16
js/index.js
Normal file
16
js/index.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
const textField = document.querySelector("input");
|
||||||
|
|
||||||
|
textField.addEventListener("keypress", function (event) {
|
||||||
|
// If the user presses the "Enter" key on the keyboard
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const url = textField.value;
|
||||||
|
const urlRegex = /(https?:\/\/[^ ]*)/g;
|
||||||
|
|
||||||
|
if (!url.match(urlRegex))
|
||||||
|
alert("Invalid URL.");
|
||||||
|
else
|
||||||
|
window.location.replace(`home.html?source=${url}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
132
js/main.js
Normal file
132
js/main.js
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
const urlSearchParams = new URLSearchParams(window.location.search);
|
||||||
|
const sourceURL = urlSearchParams.get('source');
|
||||||
|
|
||||||
|
// If no source
|
||||||
|
if (!urlSearchParams.has('source') || !sourceURL) {
|
||||||
|
alert(`No source provided.`);
|
||||||
|
window.location.replace("index.html");
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/31760088
|
||||||
|
const urlRegex = /(https?:\/\/[^ ]*)/g; // "g": global flag; without this, match() returns only the first matching result
|
||||||
|
|
||||||
|
// If source is not a URL
|
||||||
|
if (!sourceURL.match(urlRegex)) {
|
||||||
|
alert("Invalid URL.");
|
||||||
|
window.location.replace("index.html");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Back button
|
||||||
|
document.getElementById("back")?.addEventListener("click", () => history.back(1));
|
||||||
|
|
||||||
|
// Add to AltStore banner
|
||||||
|
document.getElementById("top")?.insertAdjacentHTML("afterbegin", `
|
||||||
|
<div class="uibanner">
|
||||||
|
<img src="https://user-images.githubusercontent.com/705880/65270980-1eb96f80-dad1-11e9-9367-78ccd25ceb02.png" alt="altstore-icon" class="icon">
|
||||||
|
<div class="content">
|
||||||
|
<div class="text-container">
|
||||||
|
<p class="title-text">AltStore <span class="small beta badge"></span></p>
|
||||||
|
<p class="detail-text">
|
||||||
|
Add this source to AltStore to receive app updates (requires AltStore beta)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<a href="altstore://source?url=${sourceURL}">
|
||||||
|
<button>Add</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>`);
|
||||||
|
|
||||||
|
function waitForAllImagesToLoad() {
|
||||||
|
const allImages = document.querySelectorAll("img");
|
||||||
|
var count = 0;
|
||||||
|
|
||||||
|
allImages.forEach(image => {
|
||||||
|
// New img element that won't be rendered to the DOM
|
||||||
|
var newImage = document.createElement("img");
|
||||||
|
// Attach load listener
|
||||||
|
newImage.addEventListener("load", imageLoaded);
|
||||||
|
// Set src
|
||||||
|
newImage.src = image.src;
|
||||||
|
})
|
||||||
|
|
||||||
|
function imageLoaded() {
|
||||||
|
if (++count == allImages.length) {
|
||||||
|
document.querySelector("body").classList.remove("loading");
|
||||||
|
document.getElementById("loading").remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTintColor(color) {
|
||||||
|
document.querySelector(':root').style.setProperty("--accent-color", `#${color}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addNavigationBar(title) {
|
||||||
|
document.getElementById("top").insertAdjacentHTML("beforeend", `
|
||||||
|
<div id="nav-bar">
|
||||||
|
<button id="back" type="button">
|
||||||
|
<i class="bi bi-chevron-left"></i>
|
||||||
|
Back
|
||||||
|
</button>
|
||||||
|
<div id="title">
|
||||||
|
<p>${title ?? ""}</p>
|
||||||
|
</div>
|
||||||
|
<button id="back" class="hidden">
|
||||||
|
<i class="bi bi-chevron-left"></i>
|
||||||
|
Back
|
||||||
|
</button>
|
||||||
|
</div>`);
|
||||||
|
document.getElementById("back")?.addEventListener("click", () => history.back(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
function newsItemHTML(news, apps, minimal) {
|
||||||
|
let html = `
|
||||||
|
<div class="news-item-wrapper">`;
|
||||||
|
|
||||||
|
if (news.url) html += `
|
||||||
|
<a href="${news.url}">`;
|
||||||
|
html += `
|
||||||
|
<div class="item" style="background-color: #${news.tintColor};">
|
||||||
|
<div class="text">
|
||||||
|
<h3>${news.title}</h3>
|
||||||
|
<p>${news.caption}</p>
|
||||||
|
</div>`;
|
||||||
|
if (news.imageURL && !minimal) html += `
|
||||||
|
<div class="image-wrapper">
|
||||||
|
<img src="${news.imageURL}">
|
||||||
|
</div>`;
|
||||||
|
html += `
|
||||||
|
</div>`;
|
||||||
|
if (news.url) html +=
|
||||||
|
"</a>";
|
||||||
|
|
||||||
|
if (news.appID && !minimal) {
|
||||||
|
const app = apps.find(app => app.bundleIdentifier == news.appID);
|
||||||
|
if (app) html += appHeaderHTML(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
html += "</div>";
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function appHeaderHTML(app) {
|
||||||
|
return `
|
||||||
|
<div class="item">
|
||||||
|
<div class="app-header">
|
||||||
|
<div class="content">
|
||||||
|
<img src="${app.iconURL}" alt="">
|
||||||
|
<div class="right">
|
||||||
|
<div class="text">
|
||||||
|
<p class="title">${app.name}</p>
|
||||||
|
<p class="subtitle">${app.developerName}</p>
|
||||||
|
</div>
|
||||||
|
<a href="app.html?source=${sourceURL}&id=${app.bundleIdentifier}">
|
||||||
|
<button class="uibutton" style="background-color: #${app.tintColor};">View</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="background" style="background-color: #${app.tintColor};"></div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
16
js/news.js
Normal file
16
js/news.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
addNavigationBar("All News");
|
||||||
|
|
||||||
|
fetch(sourceURL)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(json => {
|
||||||
|
if (json.tintColor) setTintColor(json.tintColor)
|
||||||
|
|
||||||
|
document.title = `News - ${json.name}`;
|
||||||
|
|
||||||
|
json.news.sort((a, b) => (new Date(b.date)).valueOf() - (new Date(a.date)).valueOf());
|
||||||
|
json.news.forEach(news =>
|
||||||
|
document.getElementById("news").insertAdjacentHTML("beforeend", newsItemHTML(news, json.apps))
|
||||||
|
);
|
||||||
|
|
||||||
|
waitForAllImagesToLoad();
|
||||||
|
});
|
||||||
24
news.html
Normal file
24
news.html
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="css/main.css">
|
||||||
|
<link rel="stylesheet" href="css/news.css">
|
||||||
|
<link rel="stylesheet" href="css/uibanner.css">
|
||||||
|
</head>
|
||||||
|
<body class="loading">
|
||||||
|
<div id="loading">
|
||||||
|
<img src="img/loading.gif" alt="loading">
|
||||||
|
<p>Loading</p>
|
||||||
|
</div>
|
||||||
|
<!-- Add to AltStore banner & navigation bar -->
|
||||||
|
<div id="top"></div>
|
||||||
|
<div id="main">
|
||||||
|
<div id="news"></div>
|
||||||
|
</div>
|
||||||
|
<script src="js/main.js"></script>
|
||||||
|
<script src="js/news.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user