diff --git a/app.js b/app.js index 3934702..9c8e77b 100644 --- a/app.js +++ b/app.js @@ -11,6 +11,30 @@ let currentView = "discover"; let currentApp = null; + function escapeHtml(str) { + if (typeof str !== "string") return ""; + return str + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + } + + function sanitizeUrl(url) { + if (typeof url !== "string") return "#"; + const trimmed = url.trim(); + if (!trimmed) return "#"; + try { + const parsed = new URL(trimmed); + const protocol = parsed.protocol.toLowerCase(); + if (protocol === "http:" || protocol === "https:") return parsed.href; + } catch (e) { + // invalid URL + } + return "#"; + } + const $ = (sel, ctx = document) => ctx.querySelector(sel); const $$ = (sel, ctx = document) => [...ctx.querySelectorAll(sel)]; @@ -85,6 +109,16 @@ return `${formatNumber(stars)}`; } + function platformIcon(platform) { + if (platform === "ios") { + return ``; + } + if (platform === "android") { + return ``; + } + return ``; + } + function isPaidApp(app) { return app.price && app.price !== "Free" && app.price !== "free"; } @@ -95,6 +129,8 @@ function getButtonLabel(app) { if (isPaidApp(app)) return app.price; + if (app.links && app.links.length > 0) return "Get"; + if (app.category && (app.category.includes("games") || app.category.includes("entertainment"))) return "Get"; if (app.brew || app.downloadUrl || app.installCommand) return "Get"; return "View"; } @@ -323,12 +359,27 @@