aboutsummaryrefslogtreecommitdiffstats
path: root/gaming/collection/_lib.php
diff options
context:
space:
mode:
authorsillylaird <sillyfanboy@gmail.com>2026-09-03 00:33:59 +0000
committersillylaird <sillyfanboy@gmail.com>2026-09-03 00:33:59 +0000
commit898b52edcb47bcb3e9d6106e74ca73e74ea01e70 (patch)
tree85c6ee5ad58b860144551184d4cf86b560c62b91 /gaming/collection/_lib.php
downloadwww-main.tar.gz
www-main.zip
import live www.sillylaird.ca webrootHEADmain
Diffstat (limited to '')
-rw-r--r--gaming/collection/_lib.php366
1 files changed, 366 insertions, 0 deletions
diff --git a/gaming/collection/_lib.php b/gaming/collection/_lib.php
new file mode 100644
index 0000000..6208465
--- /dev/null
+++ b/gaming/collection/_lib.php
@@ -0,0 +1,366 @@
+<?php
+// Shared helpers for /gaming/collection/ catalog pages.
+// Structure inspired by small-web collection catalogs; content & style are SillyLaird's.
+
+require_once __DIR__ . '/../../partials/i18n.php';
+
+function collection_lang(): string {
+ return _i18n_lang();
+}
+
+function collection_h(string $k): string {
+ return htmlspecialchars(t($k), ENT_QUOTES, 'UTF-8');
+}
+
+function collection_he(string $s): string {
+ return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
+}
+
+function collection_load(?string $path = null): array {
+ $path = $path ?? (__DIR__ . '/games.json');
+ $empty = ['consoles' => [], 'hardware' => [], 'systems' => [], 'online' => []];
+ if (!is_file($path)) {
+ return $empty;
+ }
+ $raw = file_get_contents($path);
+ if ($raw === false || trim($raw) === '') {
+ return $empty;
+ }
+ $data = json_decode($raw, true);
+ if (!is_array($data)) {
+ return $empty;
+ }
+ // Prefer consoles; fall back to legacy videogames key.
+ $consoles = $data['consoles'] ?? $data['videogames'] ?? [];
+ return [
+ 'consoles' => is_array($consoles) ? $consoles : [],
+ 'hardware' => is_array($data['hardware'] ?? null) ? $data['hardware'] : [],
+ 'systems' => is_array($data['systems'] ?? null) ? $data['systems'] : [],
+ 'online' => is_array($data['online'] ?? null) ? $data['online'] : [],
+ ];
+}
+
+function collection_image_src(string $image): string {
+ $image = trim($image);
+ if ($image === '') {
+ return '';
+ }
+ if (str_starts_with($image, 'http://') || str_starts_with($image, 'https://') || str_starts_with($image, '/')) {
+ return $image;
+ }
+ if (str_contains($image, '..') || str_contains($image, "\0")) {
+ return '';
+ }
+ return '/gaming/collection/images/' . rawurlencode(basename($image));
+}
+
+function collection_status_class(string $status): string {
+ $slug = strtolower(preg_replace('/[^a-z0-9]+/i', '-', $status) ?? '');
+ $slug = trim($slug, '-');
+ return $slug !== '' ? 'status-' . $slug : 'status-other';
+}
+
+function collection_safe_href(string $url): string {
+ $url = trim($url);
+ if ($url === '') {
+ return '';
+ }
+ if (str_starts_with($url, '/')) {
+ return str_starts_with($url, '//') ? '' : $url;
+ }
+ $scheme = strtolower((string)(parse_url($url, PHP_URL_SCHEME) ?? ''));
+ if (in_array($scheme, ['http', 'https', 'gemini', 'gopher'], true)) {
+ return $url;
+ }
+ return '';
+}
+
+/**
+ * Inventory rows (name :: model) from a flat list (consoles or hardware).
+ * @param list<array<string,mixed>> $items
+ * @return list<array{left:string,right:string,status:string,notes:string,image:string,url:string,specs:array<int,array{label:string,value:string}>}>
+ */
+function collection_inventory_rows(array $items): array {
+ $rows = [];
+ foreach ($items as $item) {
+ if (!is_array($item)) continue;
+ $name = trim((string)($item['name'] ?? ''));
+ $model = trim((string)($item['model'] ?? ''));
+ if ($name === '') continue;
+ if ($model === '') $model = $name;
+ $status = (string)($item['status'] ?? 'Owned');
+ $rows[] = [
+ 'left' => $name,
+ 'right' => $model,
+ 'status' => $status,
+ 'notes' => (string)($item['notes'] ?? ''),
+ 'image' => (string)($item['image'] ?? ''),
+ 'url' => '',
+ 'specs' => [
+ ['label' => t('collection.spec_system'), 'value' => $name],
+ ['label' => t('collection.spec_status'), 'value' => $status],
+ ],
+ ];
+ }
+ return $rows;
+}
+
+/** Consoles inventory. */
+function collection_console_rows(array $data): array {
+ return collection_inventory_rows($data['consoles'] ?? []);
+}
+
+/** Hardware inventory. */
+function collection_hardware_inventory_rows(array $data): array {
+ return collection_inventory_rows($data['hardware'] ?? []);
+}
+
+/** @deprecated use collection_console_rows */
+function collection_videogame_rows(array $data): array {
+ return collection_console_rows($data);
+}
+
+/**
+ * Video games library: titles under systems (system :: title).
+ * @return list<array{left:string,right:string,status:string,notes:string,image:string,url:string,specs:array<int,array{label:string,value:string}>}>
+ */
+function collection_games_library_rows(array $data): array {
+ $rows = [];
+ foreach (($data['systems'] ?? []) as $sys) {
+ if (!is_array($sys)) continue;
+ $left = (string)($sys['name'] ?? '');
+ foreach (($sys['games'] ?? []) as $game) {
+ if (!is_array($game)) continue;
+ $title = trim((string)($game['title'] ?? ''));
+ if ($title === '') continue;
+ $status = (string)($game['status'] ?? 'Owned');
+ $rows[] = [
+ 'left' => $left,
+ 'right' => $title,
+ 'status' => $status,
+ 'notes' => (string)($game['notes'] ?? ''),
+ 'image' => (string)($game['image'] ?? ''),
+ 'url' => '',
+ 'specs' => [
+ ['label' => t('collection.spec_system'), 'value' => $left],
+ ['label' => t('collection.spec_status'), 'value' => $status],
+ ],
+ ];
+ }
+ }
+ return $rows;
+}
+
+/** @deprecated */
+function collection_tech_rows(array $data): array {
+ return collection_games_library_rows($data);
+}
+
+/** @deprecated */
+function collection_hardware_rows(array $data): array {
+ return collection_games_library_rows($data);
+}
+
+/**
+ * Flatten online song packs into catalog rows.
+ * @return list<array{left:string,right:string,status:string,notes:string,image:string,url:string,specs:array<int,array{label:string,value:string}>}>
+ */
+function collection_pack_rows(array $data): array {
+ $rows = [];
+ foreach ($data['online'] as $cat) {
+ if (!is_array($cat)) continue;
+ $left = (string)($cat['name'] ?? '');
+ foreach (($cat['entries'] ?? []) as $entry) {
+ if (!is_array($entry)) continue;
+ $title = trim((string)($entry['title'] ?? ''));
+ if ($title === '') continue;
+ $status = (string)($entry['status'] ?? 'Owned');
+ $url = (string)($entry['url'] ?? '');
+ $specs = [
+ ['label' => t('collection.spec_game'), 'value' => $left],
+ ['label' => t('collection.spec_status'), 'value' => $status],
+ ];
+ if ($url !== '') {
+ $specs[] = ['label' => t('collection.spec_link'), 'value' => t('collection.spec_link_yes')];
+ }
+ $rows[] = [
+ 'left' => $left,
+ 'right' => $title,
+ 'status' => $status,
+ 'notes' => (string)($entry['notes'] ?? ''),
+ 'image' => (string)($entry['image'] ?? ''),
+ 'url' => $url,
+ 'specs' => $specs,
+ ];
+ }
+ }
+ return $rows;
+}
+
+/**
+ * @param list<array{left:string,right:string,status:string,notes:string,image:string,url:string,specs:array}> $rows
+ * @return list<array{left:string,right:string,status:string,notes:string,image:string,url:string,specs:array}>
+ */
+function collection_filter_wishlist(array $rows): array {
+ return array_values(array_filter(
+ $rows,
+ static fn(array $r) => strcasecmp((string)($r['status'] ?? ''), 'Wishlist') === 0
+ ));
+}
+
+/**
+ * Render one catalog row (photo + name + icon-style specs).
+ * @param array{left:string,right:string,status:string,notes:string,image:string,url:string,specs?:array} $row
+ */
+function collection_render_row(array $row): void {
+ $left = (string)($row['left'] ?? '');
+ $right = (string)($row['right'] ?? '');
+ $status = (string)($row['status'] ?? 'Owned');
+ $notes = (string)($row['notes'] ?? '');
+ $img = collection_image_src((string)($row['image'] ?? ''));
+ $href = collection_safe_href((string)($row['url'] ?? ''));
+ $specs = is_array($row['specs'] ?? null) ? $row['specs'] : [];
+ $he = 'collection_he';
+ $h = 'collection_h';
+ ?>
+ <div class="gc-row">
+ <div class="gc-pic">
+ <?php if ($img !== ''): ?>
+ <a href="<?= $he($img) ?>" target="_blank" rel="noopener noreferrer">
+ <img src="<?= $he($img) ?>" alt="" loading="lazy" decoding="async" width="200" height="150" />
+ </a>
+ <?php else: ?>
+ <div class="gc-pic-empty" aria-hidden="true">no photo</div>
+ <?php endif; ?>
+ </div>
+ <div class="gc-body">
+ <p class="gc-name">
+ <?php if ($href !== ''): ?>
+ <a href="<?= $he($href) ?>" target="_blank" rel="noopener noreferrer"><?= $he($left) ?></a>
+ <span class="gc-colon">::</span>
+ <a href="<?= $he($href) ?>" target="_blank" rel="noopener noreferrer"><?= $he($right) ?></a>
+ <?php else: ?>
+ <span class="gc-nick"><?= $he($left) ?></span>
+ <span class="gc-colon">::</span>
+ <span class="gc-model"><?= $he($right) ?></span>
+ <?php endif; ?>
+ </p>
+ <?php if ($notes !== ''): ?>
+ <p class="gc-note"><em><?= nl2br($he($notes)) ?></em></p>
+ <?php endif; ?>
+ <p class="gc-specs">
+ <?php foreach ($specs as $spec):
+ $label = (string)($spec['label'] ?? '');
+ $value = (string)($spec['value'] ?? '');
+ if ($value === '') continue;
+ ?>
+ <span class="gc-spec">
+ <span class="gc-spec-ico" title="<?= $he($label) ?>" aria-hidden="true"><?= $he(mb_substr($label, 0, 1)) ?></span>
+ <span class="gc-spec-val"><?= $he($value) ?></span>
+ </span>
+ <?php endforeach; ?>
+ <?php if ($href !== ''): ?>
+ <span class="gc-spec">
+ <span class="gc-spec-ico" aria-hidden="true">↗</span>
+ <a class="gc-spec-val" href="<?= $he($href) ?>" target="_blank" rel="noopener noreferrer"><?= $h('collection.visit_product') ?></a>
+ </span>
+ <?php endif; ?>
+ </p>
+ </div>
+ </div>
+ <?php
+}
+
+/**
+ * Shared page shell open (head + header + title + menu).
+ * @param 'games'|'consoles'|'hardware'|'packs'|'wishlist' $active
+ */
+function collection_page_open(string $active, string $pageTitle, string $pageDesc, string $baseFile): void {
+ $lang = collection_lang();
+ $langAttr = ['en' => 'en', 'ja' => 'ja', 'zh' => 'zh'][$lang] ?? 'en';
+ $suffix = ['en' => '', 'ja' => '_jp', 'zh' => '_zh'][$lang] ?? '';
+ $selfName = $baseFile . $suffix . '.php';
+ if ($baseFile === 'index' && $lang === 'en') {
+ $selfUrl = 'https://www.sillylaird.ca/gaming/collection/';
+ $enUrl = 'https://www.sillylaird.ca/gaming/collection/';
+ } else {
+ $selfUrl = 'https://www.sillylaird.ca/gaming/collection/' . $selfName;
+ $enUrl = 'https://www.sillylaird.ca/gaming/collection/' . ($baseFile === 'index' ? '' : $baseFile . '.php');
+ if ($baseFile === 'index') {
+ $enUrl = 'https://www.sillylaird.ca/gaming/collection/';
+ }
+ }
+ $jaUrl = 'https://www.sillylaird.ca/gaming/collection/' . $baseFile . '_jp.php';
+ $zhUrl = 'https://www.sillylaird.ca/gaming/collection/' . $baseFile . '_zh.php';
+ if ($baseFile === 'index') {
+ $jaUrl = 'https://www.sillylaird.ca/gaming/collection/index_jp.php';
+ $zhUrl = 'https://www.sillylaird.ca/gaming/collection/index_zh.php';
+ }
+ $h = 'collection_h';
+ // Video Games / Consoles / Hardware / Song Packs / Wishlist
+ if ($lang === 'ja') {
+ $hrefGames = '/gaming/collection/index_jp.php';
+ $hrefCons = '/gaming/collection/consoles_jp.php';
+ $hrefHw = '/gaming/collection/hardware_jp.php';
+ $hrefPk = '/gaming/collection/packs_jp.php';
+ $hrefWl = '/gaming/collection/wishlist_jp.php';
+ } elseif ($lang === 'zh') {
+ $hrefGames = '/gaming/collection/index_zh.php';
+ $hrefCons = '/gaming/collection/consoles_zh.php';
+ $hrefHw = '/gaming/collection/hardware_zh.php';
+ $hrefPk = '/gaming/collection/packs_zh.php';
+ $hrefWl = '/gaming/collection/wishlist_zh.php';
+ } else {
+ $hrefGames = '/gaming/collection/';
+ $hrefCons = '/gaming/collection/consoles.php';
+ $hrefHw = '/gaming/collection/hardware.php';
+ $hrefPk = '/gaming/collection/packs.php';
+ $hrefWl = '/gaming/collection/wishlist.php';
+ }
+ $act = static fn(string $key) => $active === $key ? ' class="gc-menu-active" id="gc-main"' : '';
+ ?>
+<!doctype html>
+<html lang="<?= $langAttr ?>">
+<head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
+ <title><?= collection_he($pageTitle) ?></title>
+ <?php
+ $meta = [
+ 'lang' => $lang,
+ 'title' => $pageTitle,
+ 'desc' => $pageDesc,
+ 'base' => 'gaming/collection/' . $baseFile,
+ 'self_url' => $selfUrl,
+ 'en_url' => $enUrl,
+ 'x_default' => $enUrl,
+ 'ja_url' => $jaUrl,
+ 'zh_url' => $zhUrl,
+ ];
+ include __DIR__ . '/../../partials/head_meta.php';
+ ?>
+ <link rel="stylesheet" href="/assets/css/fonts.css" />
+ <link rel="stylesheet" href="/assets/css/site.css" />
+ <link rel="stylesheet" href="/assets/css/skeleton.css" />
+ <link rel="stylesheet" href="/assets/css/pages/collection.css" />
+</head>
+<body class="gc-page">
+ <a class="skip-link" href="#main"><?= $h('common.skip') ?></a>
+ <?php include __DIR__ . '/../../partials/header.php'; ?>
+
+ <main id="main" class="wrap gc-wrap">
+ <p class="gc-title"><?= $h('collection.h1') ?></p>
+ <nav class="gc-menu" aria-label="<?= $h('collection.jump_nav') ?>">
+ <a href="<?= collection_he($hrefGames) ?>"<?= $act('games') ?>><?= $h('collection.menu_games') ?></a><span class="gc-menu-sep">/</span><a href="<?= collection_he($hrefCons) ?>"<?= $act('consoles') ?>><?= $h('collection.menu_consoles') ?></a><span class="gc-menu-sep">/</span><a href="<?= collection_he($hrefHw) ?>"<?= $act('hardware') ?>><?= $h('collection.menu_hardware') ?></a><span class="gc-menu-sep">/</span><a href="<?= collection_he($hrefPk) ?>"<?= $act('packs') ?>><?= $h('collection.menu_packs') ?></a><span class="gc-menu-sep">/</span><a href="<?= collection_he($hrefWl) ?>"<?= $act('wishlist') ?>><?= $h('collection.menu_wishlist') ?></a>
+ </nav>
+ <?php
+}
+
+function collection_page_close(): void {
+ ?>
+ </main>
+ <?php include __DIR__ . '/../../partials/footer.php'; ?>
+</body>
+</html>
+ <?php
+}