This commit is contained in:
foxster-mp4
2023-07-17 19:16:44 -07:00
parent 8eef026fa8
commit 8ed7aa4bc1
23 changed files with 546 additions and 357 deletions

View File

@@ -1,11 +1,18 @@
<!--
app.html
altsource-viewer (https://github.com/therealFoxster/altsource-viewer)
Copyright (c) 2023 Foxster.
MIT License.
-->
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/shared.css"> <link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/uibanner.css">
<link rel="stylesheet" href="css/app.css"> <link rel="stylesheet" href="css/app.css">
</head> </head>
<body class="loading"> <body class="loading">
@@ -113,8 +120,6 @@
</a> </a>
</div> </div>
</div> </div>
<script src="js/shared.js"></script> <script src="js/app.js" type="module"></script>
<script src="js/app.js"></script>
<script src="js/main.js"></script>
</body> </body>
</html> </html>

View File

@@ -1,11 +1,18 @@
<!--
apps.html
altsource-viewer (https://github.com/therealFoxster/altsource-viewer)
Copyright (c) 2023 Foxster.
MIT License.
-->
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/shared.css"> <link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/uibanner.css">
<link rel="stylesheet" href="css/apps.css"> <link rel="stylesheet" href="css/apps.css">
</head> </head>
<body class="loading"> <body class="loading">
@@ -18,8 +25,6 @@
<div id="main"> <div id="main">
<div id="apps"></div> <div id="apps"></div>
</div> </div>
<script src="js/shared.js"></script> <script src="js/apps.js" type="module"></script>
<script src="js/apps.js"></script>
<script src="js/main.js"></script>
</body> </body>
</html> </html>

View File

@@ -1,3 +1,11 @@
/*
app.css
altsource-viewer (https://github.com/therealFoxster/altsource-viewer)
Copyright (c) 2023 Foxster.
MIT License.
*/
a { a {
color: var(--app-tint-color) color: var(--app-tint-color)
} }

View File

@@ -1,3 +1,11 @@
/*
apps.css
altsource-viewer (https://github.com/therealFoxster/altsource-viewer)
Copyright (c) 2023 Foxster.
MIT License.
*/
#main { #main {
padding-top: 7rem; padding-top: 7rem;
} }

View File

@@ -1,3 +1,11 @@
/*
news.css
altsource-viewer (https://github.com/therealFoxster/altsource-viewer)
Copyright (c) 2023 Foxster.
MIT License.
*/
#main { #main {
padding-top: 8rem; padding-top: 8rem;
} }

View File

@@ -1,3 +1,11 @@
/*
search.css
altsource-viewer (https://github.com/therealFoxster/altsource-viewer)
Copyright (c) 2023 Foxster.
MIT License.
*/
#main { #main {
padding-top: 2rem; padding-top: 2rem;
} }

View File

@@ -1,3 +1,11 @@
/*
style.css
altsource-viewer (https://github.com/therealFoxster/altsource-viewer)
Copyright (c) 2023 Foxster.
MIT License.
*/
@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.4/font/bootstrap-icons.css"); @import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.4/font/bootstrap-icons.css");
:root { :root {
@@ -422,3 +430,75 @@ a>button {
.small.badge { .small.badge {
font-size: 8px; font-size: 8px;
} }
/* uibanner */
.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 {
margin-left: 6px;
text-decoration: none;
color: unset;
font-weight: 600;
color: white;
}

View File

@@ -1,69 +0,0 @@
.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 {
margin-left: 6px;
text-decoration: none;
color: unset;
font-weight: 600;
color: white;
}

View File

@@ -1,11 +1,18 @@
<!--
index.html
altsource-viewer (https://github.com/therealFoxster/altsource-viewer)
Copyright (c) 2023 Foxster.
MIT License.
-->
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/shared.css"> <link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/uibanner.css">
</head> </head>
<body class="loading"> <body class="loading">
<div id="loading"> <div id="loading">
@@ -39,8 +46,6 @@
</div> </div>
</div> </div>
</div> </div>
<script src="js/shared.js"></script> <script src="js/index.js" type="module"></script>
<script src="js/index.js"></script>
<script src="js/main.js"></script>
</body> </body>
</html> </html>

154
js/app.js
View File

@@ -1,3 +1,14 @@
//
// app.js
// altsource-viewer (https://github.com/therealFoxster/altsource-viewer)
//
// Copyright (c) 2023 Foxster.
// MIT License.
//
import { urlSearchParams, formatString, sourceURL } from "./utilities.js";
import { main } from "./main.js";
if (!urlSearchParams.has('id')) exit(); if (!urlSearchParams.has('id')) exit();
const bundleId = urlSearchParams.get('id'); const bundleId = urlSearchParams.get('id');
@@ -23,7 +34,7 @@ const bundleId = urlSearchParams.get('id');
} }
})(); })();
function main(json) { main((json) => {
const app = getAppWithBundleId(bundleId); const app = getAppWithBundleId(bundleId);
if (!app) exit(); if (!app) exit();
@@ -74,7 +85,7 @@ function main(json) {
<button style="color: ${tintColor};">more</button> <button style="color: ${tintColor};">more</button>
</a>`; </a>`;
this.revealTruncatedText = moreButton => { window.revealTruncatedText = moreButton => {
const textId = moreButton.parentNode.id; const textId = moreButton.parentNode.id;
const text = document.getElementById(textId); const text = document.getElementById(textId);
text.style.display = "block"; text.style.display = "block";
@@ -125,7 +136,8 @@ function main(json) {
// Version size // Version size
const units = ["B", "KB", "MB", "GB"]; const units = ["B", "KB", "MB", "GB"];
var appSize = app.size, i = 0; var appSize = app.size, i = 0;
while (appSize > 1024) { i++; while (appSize > 1024) {
i++;
appSize = parseFloat(appSize / 1024).toFixed(1); appSize = parseFloat(appSize / 1024).toFixed(1);
} }
versionSizeElement.textContent = `${appSize} ${units[i]}`; versionSizeElement.textContent = `${appSize} ${units[i]}`;
@@ -147,73 +159,73 @@ function main(json) {
app.permissions?.forEach(permission => { app.permissions?.forEach(permission => {
var permissionType, icon; var permissionType, icon;
switch (permission.type) { switch (permission.type) {
// AltStore-supported permissions // AltStore-supported permissions
case "background-audio": case "background-audio":
permissionType = "Background Audio"; permissionType = "Background Audio";
icon = "volume-up-fill"; icon = "volume-up-fill";
break; break;
case "background-fetch": case "background-fetch":
permissionType = "Background Fetch"; permissionType = "Background Fetch";
icon = "arrow-repeat" icon = "arrow-repeat"
break; break;
case "photos": case "photos":
permissionType = "Photos" permissionType = "Photos"
icon = "image-fill"; icon = "image-fill";
break; break;
// Additional permissions // Additional permissions
case "camera": case "camera":
permissionType = "Camera" permissionType = "Camera"
icon = "camera-fill"; icon = "camera-fill";
break; break;
case "music": case "music":
permissionType = "Music Library" permissionType = "Music Library"
icon = "music-note-list"; icon = "music-note-list";
break; break;
case "location": case "location":
permissionType = "Location" permissionType = "Location"
icon = "geo-alt-fill"; icon = "geo-alt-fill";
break; break;
case "microphone": case "microphone":
permissionType = "Microphone" permissionType = "Microphone"
icon = "mic-fill"; icon = "mic-fill";
break; break;
case "contacts": case "contacts":
permissionType = "Contacts" permissionType = "Contacts"
icon = "people-fill"; icon = "people-fill";
break; break;
case "bluetooth": case "bluetooth":
permissionType = "Bluetooth" permissionType = "Bluetooth"
icon = "bluetooth"; icon = "bluetooth";
break; break;
case "faceid": case "faceid":
permissionType = "Face ID" permissionType = "Face ID"
icon = "person-bounding-box"; icon = "person-bounding-box";
break; break;
case "network": case "network":
permissionType = "Network" permissionType = "Network"
icon = "wifi"; icon = "wifi";
break; break;
case "calendar": case "calendar":
case "calendars": case "calendars":
permissionType = "Calendar" permissionType = "Calendar"
icon = "calendar-date"; icon = "calendar-date";
break; break;
case "reminders": case "reminders":
permissionType = "Reminders" permissionType = "Reminders"
icon = "list-ul"; icon = "list-ul";
break; break;
case "siri": case "siri":
permissionType = "Siri" permissionType = "Siri"
icon = "gear-wide-connected"; icon = "gear-wide-connected";
break; break;
case "speech-recognition": case "speech-recognition":
permissionType = "Speech Recognition" permissionType = "Speech Recognition"
icon = "soundwave"; icon = "soundwave";
break; break;
default: default:
permissionType = permission.type.replaceAll("-", " "); permissionType = permission.type.replaceAll("-", " ");
icon = "gear-wide-connected"; icon = "gear-wide-connected";
break; break;
} }
permissions.insertAdjacentHTML("beforeend", ` permissions.insertAdjacentHTML("beforeend", `
<div class="permission"> <div class="permission">
@@ -235,7 +247,7 @@ function main(json) {
sourceTitle.innerText = json.name; sourceTitle.innerText = json.name;
sourceContainer.href = `index.html?source=${sourceURL}`; sourceContainer.href = `index.html?source=${sourceURL}`;
sourceSubtitle.innerText = json.description ?? "Tap to get started"; sourceSubtitle.innerText = json.description ?? "Tap to get started";
} });
function exit() { function exit() {
window.location.replace(`index.html?source=${sourceURL}`); window.location.replace(`index.html?source=${sourceURL}`);

View File

@@ -1,6 +1,18 @@
//
// apps.js
// altsource-viewer (https://github.com/therealFoxster/altsource-viewer)
//
// Copyright (c) 2023 Foxster.
// MIT License.
//
import { insertNavigationBar } from "./utilities.js";
import { AppHeader } from "./components/AppHeader.js";
import { main } from "./main.js";
insertNavigationBar("All Apps"); insertNavigationBar("All Apps");
function main(json) { main((json) => {
// Set tab title // Set tab title
document.title = `Apps - ${json.name}`; document.title = `Apps - ${json.name}`;
@@ -13,7 +25,7 @@ function main(json) {
let html = ` let html = `
<div class="app-container"> <div class="app-container">
${appHeaderHTML(app) } ${AppHeader(app)}
<p style="text-align: center; font-size: 0.9em;">${app.subtitle ?? ""}</p>`; <p style="text-align: center; font-size: 0.9em;">${app.subtitle ?? ""}</p>`;
if (app.screenshotURLs) { if (app.screenshotURLs) {
html += ` html += `
@@ -28,4 +40,4 @@ function main(json) {
document.getElementById("apps").insertAdjacentHTML("beforeend", html); document.getElementById("apps").insertAdjacentHTML("beforeend", html);
}); });
} });

View File

@@ -0,0 +1,25 @@
//
// AltStoreBanner.js
// altsource-viewer (https://github.com/therealFoxster/altsource-viewer)
//
// Copyright (c) 2023 Foxster.
// MIT License.
//
import { sourceURL } from "../utilities.js";
export const AltStoreBanner = (sourceName) => `
<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 ${sourceName ? "\"" + sourceName + "\"" : "this source"} to AltStore to receive app updates
</p>
</div>
<a href="altstore://source?url=${sourceURL}">
<button>Add</button>
</a>
</div>
</div>`;

View File

@@ -0,0 +1,28 @@
//
// AppHeader.js
// altsource-viewer (https://github.com/therealFoxster/altsource-viewer)
//
// Copyright (c) 2023 Foxster.
// MIT License.
//
import { sourceURL } from "../utilities.js";
export const AppHeader = app => app ? `
<div class="item">
<div class="app-header">
<div class="content">
<img id="app-icon" 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.replaceAll("#", "")};">View</button>
</a>
</div>
</div>
<div class="background" style="background-color: #${app.tintColor.replaceAll("#", "")};"></div>
</div>
</div>` : undefined;

View File

@@ -0,0 +1,22 @@
//
// NavigationBar.js
// altsource-viewer (https://github.com/therealFoxster/altsource-viewer)
//
// Copyright (c) 2023 Foxster.
// MIT License.
//
export const NavigationBar = (title) => `
<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>`;

25
js/components/NewsItem.js Normal file
View File

@@ -0,0 +1,25 @@
//
// NewsItem.js
// altsource-viewer (https://github.com/therealFoxster/altsource-viewer)
//
// Copyright (c) 2023 Foxster.
// MIT License.
//
import { AppHeader } from "./AppHeader.js";
export const NewsItem = (news, minimal = false) => `
<div class="news-item-wrapper"> ${news.url ?
"<a href='" + news.url + "'>" : ""}
<div class="item" style="background-color: #${news.tintColor.replaceAll("#", "")};">
<div class="text">
<h3>${news.title}</h3>
<p>${news.caption}</p>
</div>${news.imageURL && !minimal ?
"<div class='image-wrapper'>" +
"<img src='" + news.imageURL + "'>" +
"</div>" : ""}
</div> ${news.url ?
"</a>" : ""} ${news.appID && !minimal ?
AppHeader(getAppWithBundleId(news.appID)) ?? "" : ""}
</div>`;

View File

@@ -1,4 +1,17 @@
function main(json) { //
// index.js
// altsource-viewer (https://github.com/therealFoxster/altsource-viewer)
//
// Copyright (c) 2023 Foxster.
// MIT License.
//
import { sourceURL, formatString } from "./utilities.js";
import { NewsItem } from "./components/NewsItem.js";
import { AppHeader } from "./components/AppHeader.js";
import { main } from "./main.js";
main((json) => {
// Set "View All News" link // Set "View All News" link
document.querySelector("#news a").href = `news.html?source=${sourceURL}`; document.querySelector("#news a").href = `news.html?source=${sourceURL}`;
// Set "View All Apps" link // Set "View All Apps" link
@@ -17,10 +30,10 @@ function main(json) {
(new Date(b.date)).valueOf() - (new Date(a.date)).valueOf()); (new Date(b.date)).valueOf() - (new Date(a.date)).valueOf());
if (json.news.length == 1) { if (json.news.length == 1) {
document.getElementById("news-items").insertAdjacentHTML("beforeend", newsItemHTML(json.news[0], true)); document.getElementById("news-items").insertAdjacentHTML("beforeend", NewsItem(json.news[0], true));
document.getElementById("news-items").classList.add("one"); document.getElementById("news-items").classList.add("one");
} else for (let i = 0; i < 5 && i < json.news.length; i++) } else for (let i = 0; i < 5 && i < json.news.length; i++)
document.getElementById("news-items").insertAdjacentHTML("beforeend", newsItemHTML(json.news[i], true)); document.getElementById("news-items").insertAdjacentHTML("beforeend", NewsItem(json.news[i], true));
} else document.getElementById("news").remove(); } else document.getElementById("news").remove();
// Sort apps in descending order of version date // Sort apps in descending order of version date
@@ -39,7 +52,7 @@ function main(json) {
// If there are featured apps, ignore non-featured apps // If there are featured apps, ignore non-featured apps
if (json.featuredApps && !json.featuredApps.includes(app.bundleIdentifier)) return; if (json.featuredApps && !json.featuredApps.includes(app.bundleIdentifier)) return;
document.getElementById("apps").insertAdjacentHTML("beforeend", appHeaderHTML(app)); document.getElementById("apps").insertAdjacentHTML("beforeend", AppHeader(app));
count++; count++;
}); });
@@ -53,4 +66,4 @@ function main(json) {
</div> </div>
`); `);
else document.getElementById("about").remove(); else document.getElementById("about").remove();
} });

View File

@@ -1,54 +1,75 @@
(function () { //
// If no source // main.js
if (!urlSearchParams.has('source')) // altsource-viewer (https://github.com/therealFoxster/altsource-viewer)
search(); //
// If source is not a valid HTTP URL // Copyright (c) 2023 Foxster.
else if (!isValidHTTPURL(sourceURL)) { // MIT License.
alert("Invalid HTTP URL."); //
search();
}
})();
fetch(sourceURL) import { urlSearchParams, sourceURL, search, isValidHTTPURL, setTintColor, insertAltStoreBanner, setUpBackButton } from "./utilities.js";
.then(response => response.json())
.then(json => {
// Set tint color
const tintColor = json.tintColor?.replaceAll("#", "");
if (tintColor) setTintColor(tintColor);
insertAddToAltStoreBanner(json.name); export function main(callback) {
(() => {
// If no source
if (!urlSearchParams.has('source'))
search();
// If source is not a valid HTTP URL
else if (!isValidHTTPURL(sourceURL)) {
alert("Invalid HTTP URL.");
search();
}
setApps(json.apps); var apps;
main(json); window.setApps = array =>
waitForAllImagesToLoad(); apps = array;
}) window.getAppWithBundleId = bundleId =>
.catch(error => console.error("An error occurred.", error)); apps?.find(app => app.bundleIdentifier == bundleId) ?? undefined;
function waitForAllImagesToLoad() { setUpBackButton();
const allImages = document.querySelectorAll("img"); })();
var count = 0;
allImages.forEach(image => { fetch(sourceURL)
// New img element that won't be rendered to the DOM .then(response => response.json())
var newImage = document.createElement("img"); .then(json => {
// Attach load listener // Set tint color
newImage.addEventListener("load", loaded); const tintColor = json.tintColor?.replaceAll("#", "");
// Set src if (tintColor) setTintColor(tintColor);
newImage.src = image.src;
// Unable to load image insertAltStoreBanner(json.name);
image.addEventListener("error", (event) => {
if (event.target.id == "app-icon") { setApps(json.apps);
event.target.src = "img/generic_app.jpeg"; // main(json);
} else event.target.remove() callback(json);
loaded(); waitForAllImagesToLoad();
})
.catch(error => console.error("An error occurred.", error));
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", loaded);
// Set src
newImage.src = image.src;
// Unable to load image
image.addEventListener("error", (event) => {
if (event.target.id == "app-icon") {
event.target.src = "img/generic_app.jpeg";
} else event.target.remove()
loaded();
});
}); });
});
function loaded() { function loaded() {
if (++count == allImages.length) { if (++count == allImages.length) {
document.querySelector("body").classList.remove("loading"); document.querySelector("body").classList.remove("loading");
document.getElementById("loading").remove(); document.getElementById("loading").remove();
}
} }
} }
} }

View File

@@ -1,6 +1,18 @@
//
// news.js
// altsource-viewer (https://github.com/therealFoxster/altsource-viewer)
//
// Copyright (c) 2023 Foxster.
// MIT License.
//
import { insertNavigationBar } from "./utilities.js";
import { NewsItem } from "./components/NewsItem.js";
import { main } from "./main.js";
insertNavigationBar("All News"); insertNavigationBar("All News");
function main(json) { main((json) => {
// Set tab title // Set tab title
document.title = `News - ${json.name}`; document.title = `News - ${json.name}`;
@@ -8,5 +20,5 @@ function main(json) {
json.news.sort((a, b) => (new Date(b.date)).valueOf() - (new Date(a.date)).valueOf()); json.news.sort((a, b) => (new Date(b.date)).valueOf() - (new Date(a.date)).valueOf());
// Create & insert news items // Create & insert news items
json.news.forEach(news => document.getElementById("news").insertAdjacentHTML("beforeend", newsItemHTML(news))); json.news.forEach(news => document.getElementById("news").insertAdjacentHTML("beforeend", NewsItem(news)));
} });

View File

@@ -1,3 +1,13 @@
//
// search.js
// altsource-viewer (https://github.com/therealFoxster/altsource-viewer)
//
// Copyright (c) 2023 Foxster.
// MIT License.
//
import { urlSearchParams, sourceURL } from "./utilities.js";
(function main() { (function main() {
const success = url => window.location.replace(`index.html?source=${url}`); const success = url => window.location.replace(`index.html?source=${url}`);

View File

@@ -1,135 +0,0 @@
const urlSearchParams = new URLSearchParams(window.location.search);
const sourceURL = urlSearchParams.get('source')?.replaceAll("+", "%2B");
// https://stackoverflow.com/a/8943487
const urlRegex = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
(function (global) {
var apps;
global.setApps = array =>
apps = array;
global.getAppWithBundleId = bundleId =>
apps?.find(app => app.bundleIdentifier == bundleId) ?? undefined;
setUpBackButton();
})(this);
const newsItemHTML = (news, minimal = false) => `
<div class="news-item-wrapper"> ${news.url ?
"<a href='" + news.url + "'>" : ""}
<div class="item" style="background-color: #${news.tintColor.replaceAll("#", "")};">
<div class="text">
<h3>${news.title}</h3>
<p>${news.caption}</p>
</div>${news.imageURL && !minimal ?
"<div class='image-wrapper'>" +
"<img src='" + news.imageURL + "'>" +
"</div>" : ""}
</div> ${news.url ?
"</a>" : ""} ${news.appID && !minimal ?
appHeaderHTML(getAppWithBundleId(news.appID)) ?? "" : ""}
</div>`;
const appHeaderHTML = app => app ? `
<div class="item">
<div class="app-header">
<div class="content">
<img id="app-icon" 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.replaceAll("#", "")};">View</button>
</a>
</div>
</div>
<div class="background" style="background-color: #${app.tintColor.replaceAll("#", "")};"></div>
</div>
</div>` : undefined;
function insertAddToAltStoreBanner(source) {
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 ${source ? "\"" + source + "\"" : "this source"} to AltStore to receive app updates
</p>
</div>
<a href="altstore://source?url=${sourceURL}">
<button>Add</button>
</a>
</div>
</div>`);
}
function insertNavigationBar(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>`);
setUpBackButton();
}
// https://stackoverflow.com/a/43467144/19227228
function isValidHTTPURL(string) {
var url;
try {
url = new URL(string);
} catch (error) {
console.error("An error occurred.", error);
return false;
}
return url.protocol == "http:" || url.protocol == "https:";
}
function formatString(string) {
if (!string) return undefined;
// URLs
const urlArray = string.match(urlRegex);
// const urlSet = [...new Set(urlArray)]; // Converting to set to remove duplicates
var result = "";
urlArray?.forEach(url => {
string = string.replace(url, `<a href="${url}">${url}</a>`)
// Remove formatted substring so it won't get formatted again (prevents <a> tag within the href attribute another <a> tag)
let endIndexOfClosingTag = string.indexOf("</a>") + 4;
let formattedSubstring = string.substring(0, endIndexOfClosingTag);
result += formattedSubstring;
string = string.replace(formattedSubstring, "");
});
result += string;
// New lines
return result.replaceAll("\n", "<br>");
}
function setTintColor(color) {
document.querySelector(':root')?.style.setProperty("--accent-color", `#${color}`);
}
function setUpBackButton() {
document.getElementById("back")?.addEventListener("click", () => history.back(1));
}
function search() {
window.location.replace("search.html");
}
const $ = selector => selector.startsWith("#") && !selector.includes(".") && !selector.includes(" ")
? document.getElementById(selector.substring(1))
: document.querySelectorAll(selector);

74
js/utilities.js Normal file
View File

@@ -0,0 +1,74 @@
//
// utilities.js
// altsource-viewer (https://github.com/therealFoxster/altsource-viewer)
//
// Copyright (c) 2023 Foxster.
// MIT License.
//
import { AltStoreBanner } from "./components/AltStoreBanner.js";
import { NavigationBar } from "./components/NavigationBar.js";
export const urlSearchParams = new URLSearchParams(window.location.search);
export const sourceURL = urlSearchParams.get('source')?.replaceAll("+", "%2B");
// https://stackoverflow.com/a/8943487
export const urlRegex = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
export function insertAltStoreBanner(sourceName) {
document.getElementById("top")?.insertAdjacentHTML("afterbegin", AltStoreBanner(sourceName));
}
export function insertNavigationBar(title) {
document.getElementById("top")?.insertAdjacentHTML("beforeend", NavigationBar(title));
setUpBackButton();
}
// https://stackoverflow.com/a/43467144/19227228
export function isValidHTTPURL(string) {
var url;
try {
url = new URL(string);
} catch (error) {
console.error("An error occurred.", error);
return false;
}
return url.protocol == "http:" || url.protocol == "https:";
}
export function formatString(string) {
if (!string) return undefined;
// URLs
const urlArray = string.match(urlRegex);
// const urlSet = [...new Set(urlArray)]; // Converting to set to remove duplicates
var result = "";
urlArray?.forEach(url => {
string = string.replace(url, `<a href="${url}">${url}</a>`)
// Remove formatted substring so it won't get formatted again (prevents <a> tag within the href attribute another <a> tag)
let endIndexOfClosingTag = string.indexOf("</a>") + 4;
let formattedSubstring = string.substring(0, endIndexOfClosingTag);
result += formattedSubstring;
string = string.replace(formattedSubstring, "");
});
result += string;
// New lines
return result.replaceAll("\n", "<br>");
}
export function setTintColor(color) {
document.querySelector(':root')?.style.setProperty("--accent-color", `#${color}`);
}
export function setUpBackButton() {
document.getElementById("back")?.addEventListener("click", () => history.back(1));
}
export function search() {
window.location.replace("search.html");
}
const $ = selector => selector.startsWith("#") && !selector.includes(".") && !selector.includes(" ")
? document.getElementById(selector.substring(1))
: document.querySelectorAll(selector);

View File

@@ -1,11 +1,18 @@
<!--
news.html
altsource-viewer (https://github.com/therealFoxster/altsource-viewer)
Copyright (c) 2023 Foxster.
MIT License.
-->
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/shared.css"> <link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/uibanner.css">
<link rel="stylesheet" href="css/news.css"> <link rel="stylesheet" href="css/news.css">
</head> </head>
<body class="loading"> <body class="loading">
@@ -18,8 +25,6 @@
<div id="main"> <div id="main">
<div id="news"></div> <div id="news"></div>
</div> </div>
<script src="js/shared.js"></script> <script src="js/news.js" type="module"></script>
<script src="js/news.js"></script>
<script src="js/main.js"></script>
</body> </body>
</html> </html>

View File

@@ -1,10 +1,18 @@
<!--
search.html
altsource-viewer (https://github.com/therealFoxster/altsource-viewer)
Copyright (c) 2023 Foxster.
MIT License.
-->
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/shared.css"> <link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/search.css"> <link rel="stylesheet" href="css/search.css">
<title>AltSource Viewer</title> <title>AltSource Viewer</title>
</head> </head>
@@ -68,7 +76,6 @@
</a> </a>
</div> </div>
</div> </div>
<script src="js/shared.js"></script> <script src="js/search.js" type="module"></script>
<script src="js/search.js"></script>
</body> </body>
</html> </html>