diff --git a/components/footer.php b/components/footer.php index 8658301..3f75eec 100644 --- a/components/footer.php +++ b/components/footer.php @@ -11,7 +11,7 @@ $baseUrl = empty($external) ? '' : '//' . $_SERVER['HTTP_HOST']; window.fm = { onload: }; - + diff --git a/components/header.php b/components/header.php index 77516fa..9d661b3 100644 --- a/components/header.php +++ b/components/header.php @@ -10,7 +10,7 @@ $showNowPlaying = !empty($is_index) || !empty($do_fullscreen_header); <?=($title ?? 'flash.moe');?> - + diff --git a/lib/index b/lib/index index 74c0e6f..960cabb 160000 --- a/lib/index +++ b/lib/index @@ -1 +1 @@ -Subproject commit 74c0e6f17c21af3669345e38598ada1d37675ad4 +Subproject commit 960cabb0567153db037f93d356895a13340aedd4 diff --git a/makai.php b/makai.php index 89aa8db..516f51a 100644 --- a/makai.php +++ b/makai.php @@ -2,6 +2,9 @@ namespace Makai; use Index\Autoloader; +use Index\Data\ConnectionFailedException; +use Index\Data\MariaDB\MariaDBBackend; +use Index\Data\MariaDB\MariaDBConnectionInfo; define('MKI_STARTUP', microtime(true)); define('MKI_ROOT', __DIR__); @@ -9,6 +12,7 @@ define('MKI_DEBUG', is_file(MKI_ROOT . '/.debug')); define('MKI_DIR_SRC', MKI_ROOT . '/src'); define('MKI_DIR_LIB', MKI_ROOT . '/lib'); define('MKI_DIR_PUB', MKI_ROOT . '/public'); +define('MKI_DIR_PAGES', MKI_ROOT . '/pages'); if(MKI_DEBUG) { ini_set('display_errors', 'on'); @@ -21,3 +25,17 @@ if(MKI_DEBUG) { require_once MKI_DIR_LIB . '/index/index.php'; Autoloader::addNamespace(__NAMESPACE__, MKI_DIR_SRC); + +try { + $db = (new MariaDBBackend)->createConnection(MariaDBConnectionInfo::create( + 'unix:/var/run/mysqld/mysqld.sock', + 'website', + 'A3NjVvHRkHAxiYgk8MM4ZrCwrLVyPIYX', + 'website', + 'utf8mb4', + 'SET SESSION time_zone = \'+00:00\', sql_mode = \'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION\'', + )); +} catch(ConnectionFailedException $ex) { + echo '

Unable to connect to database

'; + die($ex->getMessage()); +} diff --git a/pages/blog.php b/pages/blog.php index e7c56a7..ced6595 100644 --- a/pages/blog.php +++ b/pages/blog.php @@ -1,23 +1,94 @@ get('/blog.php', function() { + header('Location: /old-blog/' . (int)filter_input(INPUT_GET, 'p', FILTER_SANITIZE_NUMBER_INT)); + return 302; +}); -if($reqPath === '/blog-assets.json') { - if($reqMethod !== 'GET') - return FM_ERR | 405; +$router->get('/blog-post.php', function() { + header('Location: /old-blog/' . (int)filter_input(INPUT_GET, 'p', FILTER_SANITIZE_NUMBER_INT)); + return 302; +}); - header('Content-Type: application/json; charset=utf-8'); +$router->get('/blog/:id', function(string $id) { + header('Location: /old-blog/' . intval($id)); + return 302; +}); - echo json_encode([ - 'header_backgrounds' => FM_BGS, - 'footer_quotes' => FM_FEET, +$router->get('/old-blog', function() { + $blogInfo = json_decode(file_get_contents(MKI_DIR_PUB . '/old_blog_posts.json')); + + $body = fm_component('header', [ + 'title' => 'flash.moe / old blog posts', ]); - return FM_HIT; -} + $body .= << +
List of old blog posts to enjoy while I Eventually make the new script.
+ +HTML; + + $body .= '
'; + + foreach($blogInfo as $postInfo) { + $preview = trim(explode("\n", $postInfo->post_text)[0]); + $dateCustom = date('Y-m-d @ H:i:s T', $postInfo->post_published); + $dateISO = date('c', $postInfo->post_published); + + $body .= << +

{$postInfo->post_title}

+ +

{$preview}

+ Continue reading +
\r\n +HTML; + } + + $body .= '
'; + $body .= fm_component('footer'); + + return $body; +}); + +$router->get('/old-blog/:id', function(string $id) { + if(!is_numeric($id)) + return 404; + + $postId = intval($id); + $blogInfo = json_decode(file_get_contents(MKI_DIR_PUB . '/old_blog_posts.json')); + + foreach($blogInfo as $postInfo) { + if($postInfo->post_id !== $postId) + continue; + + $dateCustom = date('Y-m-d @ H:i:s T', $postInfo->post_published); + $dateISO = date('c', $postInfo->post_published); + $preview = trim(explode("\n", $postInfo->post_text)[0]); + + $body = fm_component('header', [ + 'title' => 'flash.moe / ' . $postInfo->post_title, + ]); + $body .= '
'; + + if(!empty($postInfo->post_new_url)) + $body .= "post_new_url}\" style=\"display: block; text-align: center; color: #fff; text-decoration: none; font-size: 2em; line-height: 1.5em; padding: 20px 10px; margin: 10px 0; font-weight: bold; background-color: #222; border-radius: 5px\">This post has a new url, please go here instead!"; + + $body .= "

{$postInfo->post_title}

"; + $body .= ""; + + $splitLines = explode("\n", $postInfo->post_text); + + foreach ($splitLines as $paragraph) + if(!empty($paragraph)) + $body .= '

' . trim($paragraph) . '

'; + + $body .= '
'; + $body .= fm_component('footer'); + + return $body; + } + + return 404; +}); diff --git a/pages/contact.php b/pages/contact.php index ab8bd47..7b62996 100644 --- a/pages/contact.php +++ b/pages/contact.php @@ -1,56 +1,13 @@ get('/contact.php', mkiRedirect('/contact')); +$router->get('/contact.html', mkiRedirect('/contact')); -if($reqPath === '/nintendo' || $reqPath === '/nintendo.php') { - if($reqMethod !== 'GET') - return FM_ERR | 405; - header('Location: /contact#gaming'); - return FM_HIT | 302; -} - -if($reqPath === '/now-listening.json') { - if($reqMethod !== 'GET') - return FM_ERR | 405; - header('Content-Type: application/json; charset=utf-8'); - if($reqHead) - return FM_HIT; - - $lfmInfo = cache_output('lastfm', 10, function() { - return json_decode(file_get_contents('https://now.flash.moe/get.php?u=flashwave_')); - }); - - if(empty($lfmInfo[0]->name)) - echo '[]'; - else { - $lfmInfo = $lfmInfo[0]; - echo json_encode([ - 'name' => strval($lfmInfo->name), - 'now_playing' => !empty($lfmInfo->nowplaying), - 'url' => strval($lfmInfo->url), - 'cover' => !empty($lfmInfo->images->large) ? strval($lfmInfo->images->large) : '', - 'artist' => [ - 'name' => !empty($lfmInfo->artist->name) ? strval($lfmInfo->artist->name) : '', - 'url' => explode('/_/', strval($lfmInfo->url))[0], - ], - ]); - } - - return FM_HIT; -} - -if($reqPath === '/contact') { - if($reqMethod !== 'GET') - return FM_ERR | 405; - if($reqHead) - return FM_HIT; +$router->get('/nintendo', mkiRedirect('/contact#gaming')); +$router->get('/nintendo.php', mkiRedirect('/contact#gaming')); +$router->get('/contact', function() { $contact = [ [ 'id' => 'contact', @@ -189,38 +146,45 @@ if($reqPath === '/contact') { ], ]; - fm_component('header', [ + $body = fm_component('header', [ 'title' => 'flash.moe / contact', ]); foreach($contact as $section) { -?> -
+ + $body .= <<
-

+

{$section['title']}

- -
- - - - - - +HTML; + + foreach($section['items'] as $social) { + $body .= '
- -
-'; } - fm_component('footer'); + $body .= fm_component('footer'); - return FM_HIT; -} + return $body; +}); diff --git a/pages/etcetera.php b/pages/etcetera.php index fdd3b4c..fe1753b 100644 --- a/pages/etcetera.php +++ b/pages/etcetera.php @@ -1,20 +1,16 @@ get('/etc.php', mkiRedirect('/etc')); +$router->get('/etc.html', mkiRedirect('/etc')); +$router->get('/etcetera', mkiRedirect('/etc')); +$router->get('/etcetera.html', mkiRedirect('/etc')); +$router->get('/etcetera.php', mkiRedirect('/etc')); +$router->get('/misc', mkiRedirect('/etc')); +$router->get('/misc.html', mkiRedirect('/etc')); +$router->get('/misc.php', mkiRedirect('/etc')); -if($reqPath === '/etc') { - if($reqMethod !== 'GET') - return FM_ERR | 405; - if($reqHead) - return FM_HIT; +$router->get('/etc', function() { $links = [ [ 'title' => 'ASCII Table', @@ -48,28 +44,32 @@ if($reqPath === '/etc') { ], ]; - fm_component('header', [ + $body = fm_component('header', [ 'title' => 'flash.moe / etcetera', ]); -?> -
-
-
-

Etcetera

-
-
- -
-
- -

-

-
-
- +
+
+

Etcetera

+
+ +HTML; - return FM_HIT; -} + foreach($links as $link) { + $body .= << +
+ +

{$link['title']}

+

{$link['desc']}

+
+ +HTML; + } + + $body .= fm_component('footer'); + + return $body; +}); diff --git a/pages/index.php b/pages/index.php index a9832f6..7c649b2 100644 --- a/pages/index.php +++ b/pages/index.php @@ -1,61 +1,69 @@ get('/about', mkiRedirect('/')); +$router->get('/about.html', mkiRedirect('/')); +$router->get('/about.php', mkiRedirect('/')); +$router->get('/index.php', mkiRedirect('/')); +$router->get('/index.html', mkiRedirect('/')); -if($reqPath === '/header-bgs.json') { - if($reqMethod !== 'GET') - return FM_ERR | 405; +$router->get('/365', mkiRedirect('/?fw365')); - header('Content-Type: application/json; charset=utf-8'); - if($reqHead) - return FM_HIT; - echo json_encode(FM_BGS); +$router->get('/donate', mkiRedirect('https://paypal.me/flashwave')); - return FM_HIT; -} - -if($reqPath === '/now-listening') { - if($reqMethod !== 'GET') - return FM_ERR | 405; - if($reqHead) - return FM_HIT; +$router->get('/header-bgs.json', function() { + return json_encode(FM_BGS); +}); +$router->get('/now-listening', function() { $offset = (int)filter_input(INPUT_GET, 'offset', FILTER_SANITIZE_NUMBER_INT); - fm_component('header', [ + $body = fm_component('header', [ 'title' => 'flash.moe / now listening', 'do_fullscreen_header' => true, 'is_now_playing' => true, 'offset' => $offset, ]); - fm_component('footer', [ + + $body .= fm_component('footer', [ 'hide' => true, 'onload' => [ ['fm.initIndex', 10], ], ]); - return FM_HIT; -} + return $body; +}); -if($reqPath === '/home') { - if($reqMethod !== 'GET') - return FM_ERR | 405; - if($reqHead) - return FM_HIT; +$router->get('/now-listening.json', function() { + $lfmInfo = cache_output('lastfm', 10, function() { + return json_decode(file_get_contents('https://now.flash.moe/get.php?u=flashwave_')); + }); - fm_component('header', [ + if(empty($lfmInfo[0]->name)) + return []; + + $lfmInfo = $lfmInfo[0]; + + return [ + 'name' => strval($lfmInfo->name), + 'now_playing' => !empty($lfmInfo->nowplaying), + 'url' => strval($lfmInfo->url), + 'cover' => !empty($lfmInfo->images->large) ? strval($lfmInfo->images->large) : '', + 'artist' => [ + 'name' => !empty($lfmInfo->artist->name) ? strval($lfmInfo->artist->name) : '', + 'url' => explode('/_/', strval($lfmInfo->url))[0], + ], + ]; +}); + +$router->get('/home', function() { + $body = fm_component('header', [ 'title' => 'flash.moe / homepage', 'do_fullscreen_header' => true, ]); -?> + + $body .= <<
@@ -91,8 +99,9 @@ if($reqPath === '/home') {
- true, 'skip_analytics' => true, 'onload' => [ @@ -101,22 +110,10 @@ if($reqPath === '/home') { ], ]); - return FM_HIT; -} - -if($reqPath === '/donate') { - header('Location: https://paypal.me/flashwave'); - return FM_HIT | 302; -} - -if($reqPath === '/' || $reqPath === '/365') { - if($reqMethod !== 'GET') - return FM_ERR | 405; - if($reqHead) - return FM_HIT; - - $is365 = $reqPath === '/365'; + return $body; +}); +$router->get('/', function() use ($db) { $legacyPage = (string)filter_input(INPUT_GET, 'p'); if(!empty($legacyPage)) { $legacyPages = [ @@ -127,20 +124,19 @@ if($reqPath === '/' || $reqPath === '/365') { 'hosted' => '/etc', 'friends' => '/related', ]; + if(isset($legacyPages[$legacyPage])) { header('Location: ' . $legacyPages[$legacyPage]); - return FM_HIT | 302; + return 302; } } - $blogInfo = cache_output('blog', 600, function() { - return json_decode(file_get_contents('https://flash.moe/2020/?blog_dump')); - }); - $projInfo = cache_output('projects-featured', 300, function() { - return json_decode(file_get_contents('https://flash.moe/2020/projects.php?dump_that_shit&rf')); - }); + $is365 = isset($_GET['fw365']); - shuffle($projInfo); + $blogInfo = json_decode(file_get_contents(MKI_DIR_PUB . '/old_blog_posts.json')); + + $projects = (new Projects($db))->getFeatured(); + $languages = new Languages($db); $contact = [ [ @@ -173,108 +169,145 @@ if($reqPath === '/' || $reqPath === '/365') { ], ]; - fm_component('header', [ + $body = fm_component('header', [ 'title' => 'flash.moe', 'is_index' => true, 'is_365' => $is365, ]); -?> -
- - > - -
- '; + + $body .= fm_component('footer', [ 'is_index' => true, 'onload' => [ ['fm.initIndex'], ], ]); - return FM_HIT; -} + return $body; +}); diff --git a/pages/projects.php b/pages/projects.php index 2fc481c..c18c8f7 100644 --- a/pages/projects.php +++ b/pages/projects.php @@ -1,96 +1,126 @@ get('/projects.php', mkiRedirect('/projects')); +$router->get('/projects.html', mkiRedirect('/projects')); +$router->get('/utilities', mkiRedirect('/projects')); +$router->get('/utilities.php', mkiRedirect('/projects')); +$router->get('/utilities.html', mkiRedirect('/projects')); -if($reqPath === '/projects') { - if($reqMethod !== 'GET') - return FM_ERR | 405; - if($reqHead) - return FM_HIT; +$router->get('/projects', function() use ($db) { + $projects = (new Projects($db))->getAll(); + $languages = new Languages($db); - $projInfo = cache_output('projects', 300, function() { - return json_decode(file_get_contents('https://flash.moe/2020/projects.php?dump_that_shit')); - }); - - fm_component('header', [ - 'title' => 'flash.moe / projects', - ]); - - $sectionInfos = [ - 'project' => [ + $sections = [ + 'projects' => [ 'title' => 'Active Projects', 'desc' => 'Projects that I work on on a fairly regular basis.', + 'items' => [], ], - 'tool' => [ + 'tools' => [ 'title' => 'Tools', 'desc' => 'Personal quality of life tools that I update when I need something new from them.', + 'items' => [], ], - 'archive' => [ + 'archives' => [ 'title' => 'Archived Projects', 'desc' => 'Past projects that I no longer work on.', + 'items' => [], ], ]; - foreach($projInfo as $section => $projects): - $sectionInfo = $sectionInfos[$section]; -?> -
-
-
-

-

-
-
-homepage)) - $links[] = ['class' => 'homepage', 'text' => 'Homepage', 'url' => $proj->homepage]; - if(isset($proj->repository)) - $links[] = ['class' => 'repository', 'text' => 'Source', 'url' => $proj->repository]; - if(isset($proj->forum)) - $links[] = ['class' => 'forum', 'text' => 'Discussion', 'url' => $proj->forum]; + foreach($projects as $project) { + if($project->isArchived()) + $sections['archives']['items'][] = $project; + else { + if($project->isTool()) + $sections['tools']['items'][] = $project; + else + $sections['projects']['items'][] = $project; + } + } - $descLines = explode("\n", trim($proj->description ?? '')); -?> -
+ $body = fm_component('header', [ + 'title' => 'flash.moe / projects', + ]); + + foreach($sections as $sectionId => $section) { + $body .= << +
+
+

{$section['title']}

+

{$section['desc']}

+
+
+HTML; + + foreach($section['items'] as $project) { + $links = []; + if($project->hasHomePageUrl()) + $links[] = ['class' => 'homepage', 'text' => 'Homepage', 'url' => $project->getHomePageUrl()]; + if($project->hasSourceUrl()) + $links[] = ['class' => 'repository', 'text' => 'Source', 'url' => $project->getSourceUrl()]; + if($project->hasDiscussionUrl()) + $links[] = ['class' => 'forum', 'text' => 'Discussion', 'url' => $project->getDiscussionUrl()]; + + $descLines = $project->hasDescription() ? $project->getDescription()->trim()->split("\n") : []; + + $langs = $languages->getByProject($project); + + if($project->hasColour()) + $colour = $project->getColour(); + else + foreach($langs as $lang) + if($lang->hasColour()) { + $colour = $lang->getColour(); + break; + } + + $colour = str_pad(dechex($colour), 6, '0', STR_PAD_LEFT); + + $body .= <<
-

name;?>
-languages as $lang): ?> -
- name;?> -
- -

-

summary;?>

- -

- -
- - - -
- -{$project->getName()}
+HTML; - fm_component('footer'); + foreach($langs as $lang) { + $langColour = str_pad(dechex($lang->getColour() ?? 0), 6, '0', STR_PAD_LEFT); + $body .= "
{$lang->getName()}
"; + } - return FM_HIT; -} + $body .= '
'; + + if($project->hasSummary()) + $body .= "

{$project->getSummary()}

"; + + foreach($descLines as $line) { + $line = $line->trim(); + + if($line->isEmpty()) + continue; + + $body .= "

{$line}

"; + } + + $body .= ''; + + if(!empty($links)) { + $body .= ''; + } + + $body .= ''; + } + } + + $body .= fm_component('footer'); + + return $body; +}); diff --git a/pages/related.php b/pages/related.php index baaa3b9..6acac13 100644 --- a/pages/related.php +++ b/pages/related.php @@ -1,20 +1,13 @@ get('/related.php', mkiRedirect('/related')); +$router->get('/related.html', mkiRedirect('/related')); +$router->get('/friends', mkiRedirect('/related')); +$router->get('/friends.php', mkiRedirect('/related')); +$router->get('/friends.html', mkiRedirect('/related')); +$router->get('/related', function() { $related = [ [ 'id' => 'own', @@ -102,31 +95,36 @@ if($reqPath === '/related') { ], ]; - fm_component('header', [ + $body = fm_component('header', [ 'title' => 'flash.moe / related', ]); - foreach($related as $section): -?> -
+ foreach($related as $section) { + $body .= <<
-

+

{$section['name']}

- +HTML; + + foreach($section['sites'] as $site) { + $body .= << - - +

{$site['name']}

+HTML; - fm_component('footer'); + if(isset($site['desc'])) + $body .= "

{$site['desc']}

"; - return FM_HIT; -} + $body .= ''; + } + } + + $body .= fm_component('footer'); + + return $body; +}); diff --git a/public/2020/404.php b/public/2020/404.php deleted file mode 100644 index bbe0e9a..0000000 --- a/public/2020/404.php +++ /dev/null @@ -1,29 +0,0 @@ - - - - - 404 Not Found - - - - -
- -
-

404 - Page not found!

-

You either went to an invalid URL or this content has been deleted.

-
-
- - - - - diff --git a/public/2020/blog.php b/public/2020/blog.php deleted file mode 100644 index 56b8191..0000000 --- a/public/2020/blog.php +++ /dev/null @@ -1,84 +0,0 @@ -prepare(' - SELECT `post_id`, `post_title`, `post_text`, UNIX_TIMESTAMP(`post_published`) AS `post_published`, `post_safe`, `post_new_url` - FROM `fm_blog_posts` - WHERE `post_id` = :id - AND `post_published` IS NOT NULL - AND `post_published` < CURRENT_TIMESTAMP - AND `post_deleted` IS NULL -'); -$getPost->bindValue('id', $postId); -$post = $getPost->execute() ? $getPost->fetch(PDO::FETCH_OBJ) : false; - -if(html_default_mode() === FWH_JVDG && empty($post->post_safe)) - unset($post); - -if(!empty($post)) { - $dateCustom = date('Y-m-d @ H:i:s T', $post->post_published); - $dateISO = date('c', $post->post_published); - $preview = trim(explode("\n", $post->post_text)[0]); - $title = $post->post_title; -} else http_response_code(404); - -if($blogMode) { - fm_component('header', [ - 'title' => $title, - 'styles' => [ - '/css/2020.css' - ], - ]); -} else { -echo html_doctype(); -?> - - - - <?=$title ?? 'flash.moe';?> - - - - - -
- - -
-

Post not found!

-

You either went to an invalid URL or this post has been deleted.

-
- -
-post_new_url)): ?> - This post has a new url, please go here instead! - -

post_title;?>

- - post_text); - - foreach ($splitLines as $paragraph) - if(!empty($paragraph)) - echo '

' . trim($paragraph) . '

'; - ?> -
- -
- - - - - - - diff --git a/public/2020/index.php b/public/2020/index.php deleted file mode 100644 index 690a6f6..0000000 --- a/public/2020/index.php +++ /dev/null @@ -1,106 +0,0 @@ -prepare(" - SELECT `post_id`, `post_title`, `post_text`, UNIX_TIMESTAMP(`post_published`) AS `post_published` - FROM `fm_blog_posts` AS bp - WHERE `post_published` IS NOT NULL - AND `post_published` < CURRENT_TIMESTAMP - AND `post_deleted` IS NULL - ORDER BY `post_published` DESC - "); - $posts = $getPosts->execute() ? $getPosts->fetchAll(PDO::FETCH_OBJ) : false; - $posts = $posts ? $posts : []; -} - -if(isset($_GET['blog_dump'])) { - header('Content-Type: application/json; charset=utf-8'); - echo json_encode($posts ?? []); - return; -} - -if($blogMode) { - fm_component('header', [ - 'title' => 'flash.moe blog', - 'styles' => [ - '/css/2020.css' - ], - ]); -} else { -echo html_doctype(); -?> - - - - <?=$_SERVER['HTTP_HOST'];?> - - - - - - - - - -
-
New blog script is still work in progress, enjoy the old version!
-
- -
- - -
- - -post_text)[0]); - $dateCustom = date('Y-m-d @ H:i:s T', $post->post_published); - $dateISO = date('c', $post->post_published); - - echo << -

{$post->post_title}

- -

{$preview}

- Continue reading -
\r\n -HTML; - } -?> -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/public/2020/nintendo.php b/public/2020/nintendo.php deleted file mode 100644 index 662aa2a..0000000 --- a/public/2020/nintendo.php +++ /dev/null @@ -1,67 +0,0 @@ - - - - - Nintendo Friend Codes - - - - -
- -
-
-

List of my Nintendo friend codes so I don't have to dig them up every time. Please use these codes only if we know each other.

-

If you're adding me on 3DS you'll have to send me your friend code as well, use the contact links in the sidebar to do so.

-
- -
-
-

Nintendo Switch

-
-
- SW-7446-8163-4902 -
-
- -
-
-

Nintendo 3DS

-
-
- 4013-0352-0648 -
-
- -
-
-

Nintendo Wii U

-
-
- flashwave0 -
-
- -
-

Below are friend codes for Nintendo WFC revival services, these will only be useful with modified consoles.

-
- -
-
-

Mario Kart Wii (Wiimmfi)

-
-
- 5202-9182-8404 -
-
-
-
- - - diff --git a/public/2020/projects.php b/public/2020/projects.php deleted file mode 100644 index 0bc4d9c..0000000 --- a/public/2020/projects.php +++ /dev/null @@ -1,177 +0,0 @@ -prepare(' - SELECT `project_id`, `project_name`, COALESCE(`project_name_clean`, REPLACE(LOWER(`project_name`), \' \', \'-\')) AS `project_name_clean`, `project_summary`, `project_description`, `project_featured`, `project_order`, `project_homepage`, `project_repository`, `project_forum`, UNIX_TIMESTAMP(`project_archived`) AS `project_archived`, `project_type`, UNIX_TIMESTAMP(`project_created`) AS `project_created`, `project_colour` - FROM `fm_projects` - WHERE `project_deleted` IS NULL - '. ($randomFeatured ? ' AND `project_featured` <> 0 ORDER BY RAND()' : 'ORDER BY `project_order` DESC') -); -$getProjects->execute(); -$projects = $getProjects->fetchAll(PDO::FETCH_OBJ); - -$activeProjects = []; -$toolProjects = []; -$archivedProjects = []; - -foreach($projects as $project) { - if(!empty($project->project_archived)) - $archivedProjects[] = $project; - elseif($project->project_type === 'Tool') - $toolProjects[] = $project; - else - $activeProjects[] = $project; -} - -$getLanguages = $pdo->prepare(' - SELECT pl.`language_id`, pl.`language_name`, pl.`language_colour` - FROM `fm_proglangs` AS pl - LEFT JOIN `fm_projects_proglangs` AS ppl - ON ppl.`language_id` = pl.`language_id` - WHERE ppl.`project_id` = :project_id - ORDER BY ppl.`priority` -'); - -function fm_project_box(stdClass $project, array $languages): string { - $colour = html_colour(isset($languages[0]->language_colour) ? $languages[0]->language_colour : 0x212121); - - $html = << -
-

{$project->project_name}

-
-
-

{$project->project_summary}

-
\r\n -HTML; - - $indent = ' '; - - foreach($languages as $lang) { - $colour = html_colour($lang->language_colour); - $html .= "{$indent}
{$lang->language_name}
\r\n"; - } - - $html .= "{$indent}
\r\n"; - - $links = ''; - - if(!empty($project->project_homepage)) - $links .= " project_homepage}\">\"\" Homepage\r\n"; - if(!empty($project->project_repository)) - $links .= " project_repository}\">\"\" Source\r\n"; - if(!empty($project->project_forum)) - $links .= " project_forum}\">\"\" Discussion\r\n"; - - if(!empty($links)) { - $html .= "{$indent}
\r\n"; - $html .= "{$indent}{$links}"; - $html .= "{$indent}
\r\n"; - } - - $html .= << -
\r\n -HTML; - - return $html; -} - -if(isset($_GET['dump_that_shit'])) { - $doConvert = !isset($_GET['noconvert']); - $meow = []; - $projects = array_merge($activeProjects, $toolProjects, $archivedProjects); - - foreach($projects as $project) { - if($doConvert) { - $projKeys = array_keys((array)$project); - foreach($projKeys as $key) { - $project->{substr($key, 8)} = $project->{$key}; - unset($project->{$key}); - } - } - - $getLanguages->bindValue('project_id', $project->id ?? $project->project_id); - $getLanguages->execute(); - $project->languages = $getLanguages->fetchAll(PDO::FETCH_OBJ); - if($doConvert) { - $langKeys = array_keys((array)$project->languages[0]); - for($i = 0; $i < count($project->languages); ++$i) - foreach($langKeys as $key) { - if(!isset($project->colour)) - $project->colour = $project->languages[$i]->language_colour; - $project->languages[$i]->{substr($key, 9)} = $project->languages[$i]->{$key}; - unset($project->languages[$i]->{$key}); - } - } - - if($randomFeatured || !$doConvert) - $meow[] = $project; - else { - $key = $project->archived ? 'archive' : strtolower($project->type); - $meow[$key][] = $project; - } - } - - header('Content-Type: application/json; charset=utf-8'); - echo json_encode($meow); - return; -} - -echo html_doctype(); -?> - - - - Projects - - - - -
- -
-

Active Projects

-
-bindValue('project_id', $project->project_id); - $getLanguages->execute(); - $languages = $getLanguages->fetchAll(PDO::FETCH_OBJ); - echo fm_project_box($project, $languages); - } -?> -
-

Tools

-
-bindValue('project_id', $project->project_id); - $getLanguages->execute(); - $languages = $getLanguages->fetchAll(PDO::FETCH_OBJ); - echo fm_project_box($project, $languages); - } -?> -
-

Archived Projects

-
-bindValue('project_id', $project->project_id); - $getLanguages->execute(); - $languages = $getLanguages->fetchAll(PDO::FETCH_OBJ); - echo fm_project_box($project, $languages); - } -?> -
-
-
- - - - - diff --git a/public/404.php b/public/404.php deleted file mode 100644 index d24faec..0000000 --- a/public/404.php +++ /dev/null @@ -1,2 +0,0 @@ - 'Home', 'link' => '/'], - //['title' => 'Blog', 'link' => '//blog.flash.moe'], - ['title' => 'Blog', 'link' => '//flash.moe/2020/?blog=1'], - ['title' => 'Projects', 'link' => '/projects'], - ['title' => 'Contact', 'link' => '/contact'], - ['title' => 'Related', 'link' => '/related'], - ['title' => 'Etcetera', 'link' => '/etc'], - ['title' => 'Forum', 'link' => '//forum.flash.moe'], -]); - -define('FM_BGS', [ - '/assets/headers/krk-000.jpg', '/assets/headers/krk-001.jpg', '/assets/headers/krk-002.jpg', - '/assets/headers/krk-003.jpg', '/assets/headers/krk-004.jpg', '/assets/headers/krk-005.jpg', - '/assets/headers/krk-006.jpg', '/assets/headers/krk-007.jpg', '/assets/headers/krk-008.jpg', - - '/assets/headers/mkt-000.jpg', '/assets/headers/mkt-001.jpg', '/assets/headers/mkt-002.jpg', - '/assets/headers/mkt-003.jpg', '/assets/headers/mkt-004.jpg', '/assets/headers/mkt-005.jpg', - '/assets/headers/mkt-006.jpg', '/assets/headers/mkt-007.jpg', '/assets/headers/mkt-008.jpg', - '/assets/headers/mkt-009.jpg', '/assets/headers/mkt-010.jpg', '/assets/headers/mkt-011.jpg', - '/assets/headers/mkt-012.jpg', '/assets/headers/mkt-013.jpg', '/assets/headers/mkt-014.jpg', - '/assets/headers/mkt-015.jpg', '/assets/headers/mkt-016.jpg', '/assets/headers/mkt-017.jpg', - '/assets/headers/mkt-018.jpg', '/assets/headers/mkt-019.jpg', '/assets/headers/mkt-020.jpg', - '/assets/headers/mkt-021.jpg', '/assets/headers/mkt-022.jpg', '/assets/headers/mkt-023.jpg', - '/assets/headers/mkt-024.jpg', '/assets/headers/mkt-025.jpg', '/assets/headers/mkt-026.jpg', - '/assets/headers/mkt-027.jpg', '/assets/headers/mkt-028.jpg', '/assets/headers/mkt-029.jpg', - '/assets/headers/mkt-030.jpg', '/assets/headers/mkt-031.jpg', '/assets/headers/mkt-032.jpg', - '/assets/headers/mkt-033.jpg', '/assets/headers/mkt-034.jpg', '/assets/headers/mkt-035.jpg', - '/assets/headers/mkt-036.jpg', '/assets/headers/mkt-037.jpg', '/assets/headers/mkt-038.jpg', - '/assets/headers/mkt-039.jpg', '/assets/headers/mkt-040.jpg', '/assets/headers/mkt-041.jpg', - '/assets/headers/mkt-042.jpg', '/assets/headers/mkt-043.jpg', '/assets/headers/mkt-044.jpg', - '/assets/headers/mkt-045.jpg', '/assets/headers/mkt-046.jpg', '/assets/headers/mkt-047.jpg', - '/assets/headers/mkt-048.jpg', '/assets/headers/mkt-049.jpg', '/assets/headers/mkt-050.jpg', - '/assets/headers/mkt-051.jpg', '/assets/headers/mkt-052.jpg', '/assets/headers/mkt-053.jpg', - '/assets/headers/mkt-054.jpg', '/assets/headers/mkt-055.jpg', '/assets/headers/mkt-056.jpg', - '/assets/headers/mkt-057.jpg', '/assets/headers/mkt-058.jpg', '/assets/headers/mkt-059.jpg', - '/assets/headers/mkt-060.jpg', '/assets/headers/mkt-061.jpg', '/assets/headers/mkt-062.jpg', - '/assets/headers/mkt-063.jpg', -]); - -define('FM_FEET', [ - 'if it ain\'t broke, i\'ll break it', -]); - -function fm_component(string $_component_name, array $vars = []) { - extract($vars); - require __DIR__ . '/../../components/' . $_component_name . '.php'; -} diff --git a/public/_v4/html.php b/public/_v4/html.php deleted file mode 100644 index 3b13c2c..0000000 --- a/public/_v4/html.php +++ /dev/null @@ -1,501 +0,0 @@ -\r\n" - : "\r\n"; -} - -function html_charset(string $charset = 'utf-8', ?int $version = null): string { - return html_old_browser() - ? "\r\n" - : "\r\n"; -} - -function html_stylesheet(string $name, array $params = [], ?int $version = null): string { - if(html_modern()) - $cssFormat = '/css/%s'; - else { - $cssFormat = '/css.php?path=%s'; - - if(empty($params['accent'])) - $params['accent'] = html_default_mode() === FWH_JVDG ? '#2d9940' : '#4a3650'; - - if(!empty($params)) { - foreach($params as $key => $val) - $cssFormat .= '&' . rawurlencode($key) . '=' . str_replace('%', '%%', rawurlencode($val)); - } - } - - return "\r\n"; -} - -function html_script(string $path, string $charset = 'utf-8', string $language = 'javascript', ?int $version = null): string { - if($path[0] === '/' && $path[1] !== '/') - $path = html_baseurl() . $path; - - $html = "\r\n"; -} - -function html_meta(?int $version = null): string { - $meta = "\r\n"; - - if(html_default_mode() === FWH_JVDG) - $meta .= " \r\n"; - - return $meta; -} - -function html_open(?int $version = null): string { - return html_doctype($version) . (($version ?? html_default_ver()) < FWH_2020 ? "" : ''); -} - -function html_close(?int $version = null): string { - return ($version ?? html_default_ver()) < FWH_2020 ? "" : ''; -} - -function html_head(string $title, array $vars = [], ?int $version = null): string { - $version = $version ?? html_default_ver(); - $accentColour = $vars['accent_colour'] ?? '#555'; - $html = html_charset(); - - $html .= "{$title}"; - - switch ($version) { - case FWH_2016: - return $html . html_stylesheet('2016.css', ['accent' => $accentColour], $version) . html_meta($version); - - case FWH_2019: - $html .= html_stylesheet('2019.css', ['accent' => $accentColour], $version); - - if (!empty($vars['transitional'])) - $html .= html_stylesheet('2016-lite.css', ['accent' => $accentColour], $version); - - $html .= html_meta($version); - - if(html_modern()) { - $html .= << - :root { - --accent-colour: {$accentColour}; - } - -HTML; - } - - return $html; - } - - return ''; -} - -function html_navigation(array $navigation, array $vars = [], ?int $version = null): string { - $home = html_local_url($navigation[0]['link'] ?? $vars['link'] ?? '/'); - - if(html_old_browser()) { - $title = !empty($vars['title1']) ? ($vars['title1'] . ($vars['title2'] ?? '')) : $vars['title'] ?? 'flashwave'; - $html = ''; - - foreach ($navigation as $item) { - $item['link'] = html_local_url($item['link']); - $html .= ""; - } - - return $html . '

' . $title . '

{$item['title']}
'; - } - - switch ($version ?? html_default_ver()) { - case FWH_2016: - $title = $vars['title'] ?? 'flash.moe'; - - $html = << -
- -
-HTML; - - foreach ($navigation as $item) { - $item['link'] = html_local_url($item['link']); - $html .= sprintf(' - - - %s - - ', $item['link'], $item['mdi-icon'] ?? 'mdi-link-variant', $item['title']); - } - - $html .= << -
- -HTML; - - return $html; - - case FWH_2019: - $title1 = $vars['title1'] ?? 'flash'; - $title2 = $vars['title2'] ?? 'wave'; - - $html = << -
-
- -
-HTML; - - foreach ($navigation as $item) { - $item['link'] = html_local_url($item['link']); - $html .= "{$item['title']}"; - } - - $html .= '
'; - - $html .= << - -HTML; - - return $html; - } - - return ''; -} - -function html_footer(?int $version = null): string { - $currentYear = date('Y'); - - if(html_old_browser()) { - return << - © Flashwave 2010-{$currentYear} -
-HTML; - } - - switch ($version ?? html_default_ver()) { - case FWH_2016: - return << - © Flashwave 2010-{$currentYear} - -HTML; - - case FWH_2019: - return << - - - -HTML; - } - - return ''; -} - -function html_sidebar(?int $version = null, ?array $links = null, ?array $accounts = null): string { - $version = $version ?? html_default_ver(); - if($version < FWH_2020) - return ''; - - $isJvdg = html_default_mode() === FWH_JVDG; - - $links = $links ?? [ - [ - 'title' => 'Home', - 'desc' => 'Index with latest blog posts', - 'dest' => html_local_url('/'), - 'hide' => $isJvdg, - ], - [ - 'title' => 'Projects', - 'desc' => 'List of things I\'ve made or am making', - 'dest' => html_local_url('/projects'), - 'hide' => $isJvdg, - ], - [ - 'title' => 'Forum', - 'desc' => 'Feature requests and bug reports for some projects', - 'dest' => '//forum.flash.moe', - 'hide' => $isJvdg, - ], - [ - 'title' => 'Flashii', - 'desc' => 'Community site I run and develop', - 'dest' => '//flashii.net', - 'hide' => $isJvdg, - ], - [ - 'title' => 'Railgun', - 'desc' => 'Chat server and chat protocols I work on', - 'dest' => '//railgun.sh', - 'hide' => $isJvdg, - ], - ]; - - $accounts = $accounts ?? [ - [ - 'title' => 'E-mail', - 'url' => 'mailto:me+contact@flash.moe', - 'image' => html_baseurl() . '/assets/icons/email.png', - ], - [ - 'title' => 'Flashii', - 'url' => '//flashii.net/profile.php?u=1', - 'image' => html_baseurl() . '/assets/icons/flashii.png', - 'hide' => $isJvdg, - ], - [ - 'title' => 'Github', - 'url' => '//github.com/flashwave', - 'image' => html_baseurl() . '/assets/icons/github.png', - ], - [ - 'title' => 'YouTube', - 'url' => '//youtube.com/c/flashwave', - 'image' => html_baseurl() . '/assets/icons/youtube.png', - 'hide' => $isJvdg, - ], - [ - 'title' => 'Twitch', - 'url' => '//twitch.tv/flashwave0', - 'image' => html_baseurl() . '/assets/icons/twitch.png', - 'hide' => $isJvdg, - ], - [ - 'title' => 'Steam', - 'url' => '//steamcommunity.com/id/flashwave_', - 'image' => html_baseurl() . '/assets/icons/steam.png', - 'hide' => $isJvdg, - ], - [ - 'title' => 'Twitter', - 'url' => 'javascript:confirm(\'Proceed with caution.\') ? location.assign(\'//twitter.com/smugwave\') : void(0);', - 'image' => html_baseurl() . '/assets/icons/twitter.png', - 'hide' => $isJvdg, - ], - [ - 'title' => 'last.fm', - 'url' => '//www.last.fm/user/flashwave_', - 'image' => html_baseurl() . '/assets/icons/lastfm.png', - 'hide' => $isJvdg, - ], - [ - 'title' => 'Nintendo', - 'url' => '/nintendo', - 'image' => html_baseurl() . '/assets/icons/ninswitch.png', - 'hide' => $isJvdg, - ], - [ - 'title' => 'Donate', - 'url' => '//paypal.me/flashwave', - 'image' => html_baseurl() . '/assets/icons/paypal.png', - 'hide' => $isJvdg, - ], - ]; - - $title1 = $isJvdg ? 'julian' : 'flash'; - $title2 = $isJvdg ? 'vdg' : 'wave'; - $avatar = $isJvdg ? '//julianvdg.nl/avatar.php' : '//flashii.net/user-assets.php?m=avatar&u=1&r=120'; - $avatarHTML = $isJvdg ? '' : ''; - - $html = << - \r\n -HTML; - - $name = $isJvdg ? 'Julian van de Groep' : 'Flashwave'; - $year = date('Y'); - if(!$isJvdg) - $html .= << - -
\r\n -HTML; - else $html .= ''; - - return $html; -} diff --git a/public/_v4/includes.php b/public/_v4/includes.php index de81cf7..1bc3357 100644 --- a/public/_v4/includes.php +++ b/public/_v4/includes.php @@ -1,20 +1,13 @@ PDO::CASE_NATURAL, @@ -33,28 +26,3 @@ try { echo '

Unable to connect to database

'; die($ex->getMessage()); } - -define('HEAD_FLASHWAVE', [ - 'accent_colour' => '#4a3650', -]); -define('HEAD_ERROR', [ - 'accent_colour' => '#960018', -]); -define('HEAD_BLOG', [ - 'accent_colour' => '#4a3650', - 'transitional' => true, -]); -define('HEAD_WHOIS', [ - 'accent_colour' => '#555', - 'transitional' => true, -]); - -define('NAV_FLASHWAVE', [ - ['title' => 'Home', 'link' => '/'], - ['title' => 'Projects', 'link' => '/projects.php'], -]); -define('NAV_ERROR', [ - ['title' => 'Retry', 'link' => $_SERVER['REQUEST_URI']], - ['title' => 'Home', 'link' => '/'], -]); -define('NAV_WHOIS', NAV_FLASHWAVE); diff --git a/public/assets/2020v2.js b/public/assets/2021.js similarity index 97% rename from public/assets/2020v2.js rename to public/assets/2021.js index 9a4ecb1..b3f32fb 100644 --- a/public/assets/2020v2.js +++ b/public/assets/2021.js @@ -1,351 +1,351 @@ -Date.prototype.getWeek = function() { - var date = new Date(this.getTime()); - date.setHours(0, 0, 0, 0); - date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7); - var week1 = new Date(date.getFullYear(), 0, 4); - return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000 - 3 + (week1.getDay() + 6) % 7) / 7); -} -Date.prototype.getWeekYear = function() { - var date = new Date(this.getTime()); - date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7); - return date.getFullYear(); -} - -window.fm = (function() { - var fm = this; - this.container = document.querySelector('.container'); - this.headerBackground = null; - this.originalHeaderBackground = null; - this.defaultCoverImage = 'https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png'; - this.indexIsPlaying = false; - this.indexPlayingDefaultCover = false; - this.indexPlayingContainer = null; - this.indexPlayingCover = null; - this.indexPlayingTitle = null; - this.indexPlayingArtist = null; - this.indexLastNp = null; - this.indexPlayingInterval = null; - this.homeInterval = null; - - this.callByName = function(name, args) { - var ref = window, - parent = null, - parts = name.split('.'); - for(var i = 0; i < parts.length; ++i) { - parent = ref; - if(typeof ref[parts[i]] !== 'undefined') - ref = ref[parts[i]]; - else return; - } - if(typeof ref !== 'function') - return; - return ref.apply(parent, args); - }; - - this.runFuncs = function(funcs) { - for(var i = 0; i < funcs.length; ++i) - fm.callByName(funcs[i][0], funcs[i].slice(1)); - }; - - if(this.onload && Array.isArray(this.onload)) - window.addEventListener('load', function() { fm.runFuncs(fm.onload); }); - - if(sessionStorage.getItem('header-bgs') === null - || sessionStorage.getItem('header-bgs-loaded') < Date.now() - 86400000) { - var hXhr = new XMLHttpRequest; - hXhr.onload = function() { - sessionStorage.setItem('header-bgs', hXhr.responseText); - sessionStorage.setItem('header-bgs-loaded', Date.now()); - }; - hXhr.open('GET', '/header-bgs.json'); - hXhr.send(); - } - - this.dynloadCurrent = function() { - return { - components: { - header: { - title: document.title, - is_index: document.body.classList.contains('index'), - do_fullscreen_header: document.body.classList.contains('fullscreen-header'), - is_now_playing: document.body.classList.contains('now-playing'), - }, - footer: { - onload: this.onload || [], - } - }, - raw_html: fm.container.innerHTML, - }; - }; - - this.dynloadApply = function(state) { - if((state.components || {}).header) { - document.title = state.components.header.title || ''; - document.body.classList[state.components.header.is_index ? 'add' : 'remove']('index'); - document.body.classList[state.components.header.do_fullscreen_header ? 'add' : 'remove']('fullscreen-header'); - document.body.classList[state.components.header.is_now_playing ? 'add' : 'remove']('now-playing'); - } - - if(typeof state.raw_html !== 'undefined') - this.container.innerHTML = state.raw_html; - - if((state.components || {}).footer) { - this.runFuncs(state.components.footer.onload || []); - } - - fm.dynloadInit(); - fm.setRandomHeaderBackgroundIfNotPlaying(); - }; - - this.dynloadInit = function(initial) { - if(initial) { - history.replaceState(this.dynloadCurrent(), document.title, location.toString()); - window.addEventListener('popstate', function(ev) { - fm.dynloadApply(ev.state || {}); - }); - } - - var dynload = document.querySelectorAll('[data-fm-dynload]'); - for(var i = 0; i < dynload.length; ++i) - (function(dc){ - var url = new URL(dc.href), - hash = url.hash.substring(1); - url.hash = ''; - dc.removeAttribute('data-fm-dynload'); - dc.addEventListener('click', function(ev) { - ev.stopPropagation(); - ev.preventDefault(); - - var xhr = new XMLHttpRequest; - xhr.onload = function() { - try { - var obj = JSON.parse(xhr.responseText); - } catch(ex) {} - if(!obj) { - location.assign(dc.href); - return; - } - var title = ((obj.components || {}).header || {}).title || ''; - history.pushState(obj, title, dc.href); - fm.dynloadApply(obj); - if(hash) { - var targetEl = document.getElementById(hash); - if(targetEl) { - setTimeout(function() { - window.scrollTo({ - top: targetEl.getBoundingClientRect().top, - behavior: 'smooth' - }); - }, 500); - } - } - }; - xhr.onerror = function(ev) { - location.assign(dc.href); - }; - xhr.open('GET', url); - xhr.setRequestHeader('Accept', 'application/x-fdynload+json'); - xhr.send(); - }, true); - })(dynload[i]); - }; - - this.dynloadInit(true); - - this.selectTextInElement = function(elem) { - // MSIE - if(document.body.createTextRange) { - var range = document.body.createTextRange(); - range.moveToElementText(elem); - range.select(); - return; - } - - // Mozilla - if(window.getSelection) { - var select = window.getSelection(), - range = document.createRange(); - range.selectNodeContents(elem); - select.removeAllRanges(); - select.addRange(range); - return; - } - - console.warn('Unable to select text.'); - }; - - this.copySelectedText = function() { - if(document.execCommand) { - document.execCommand('copy'); - return; - } - - console.warn('Unable to copy text.'); - }; - - this.getNowListening = function(callback) { - if(!callback) - return; - var xhr = new XMLHttpRequest; - xhr.onreadystatechange = function() { - if(xhr.readyState !== 4 || xhr.status !== 200) - return; - callback.call(this, JSON.parse(xhr.responseText)); - }.bind(this); - xhr.open('GET', '/now-listening.json'); - xhr.send(); - }; - - this.updateIndexNowListening = function() { - window.fm.getNowListening(function(info) { - if(this.indexLastNp === null - || this.indexLastNp.url != info.url - || this.indexLastNp.now_playing != info.now_playing) { - if(this.indexLastNp !== null) - this.originalHeaderBackground = this.getRandomHeaderBackground(); - this.indexLastNp = info; - } else return; - - this.indexIsPlaying = info.now_playing; - this.indexPlayingDefaultCover = !info.cover || info.cover === this.defaultCoverImage; - this.indexPlayingContainer.classList[info.now_playing ? 'remove' : 'add']('header-now-playing-hidden'); - this.indexPlayingCover.alt = this.indexPlayingCover.src = (info.cover !== this.defaultCoverImage ? info.cover : '//now.flash.moe/resources/no-cover.png'); - this.indexPlayingTitle.textContent = this.indexPlayingTitle.title = info.name; - this.indexPlayingTitle.href = info.url; - this.indexPlayingArtist.textContent = this.indexPlayingArtist.title = (info.artist || {}).name || ''; - this.indexPlayingArtist.href = (info.artist || {}).url || ''; - this.switchHeaderBackground( - info.now_playing && !this.indexPlayingDefaultCover - ? this.indexPlayingCover.src - : this.originalHeaderBackground - ); - }); - }; - - this.getRandomHeaderBackground = function() { - var set = JSON.parse(sessionStorage.getItem('header-bgs')); - if(!set) - return '/assets/errors/404.jpg'; - return set[parseInt(Math.random() * set.length) - 1]; - }; - - this.setRandomHeaderBackground = function() { - this.switchHeaderBackground(this.getRandomHeaderBackground()); - }; - - this.setRandomHeaderBackgroundIfNotPlaying = function() { - if(!this.indexIsPlaying || this.indexPlayingDefaultCover) - this.setRandomHeaderBackground(); - }; - - this.getCurrentHeaderBackground = function() { - return this.headerBackground.querySelector('img').src; - }; - - this.headerBackgroundIsChanging = false; - this.switchHeaderBackground = function(url) { - if(this.headerBackgroundIsChanging || this.getCurrentHeaderBackground() === url) - return; - this.headerBackgroundIsChanging = true; - var newImg = document.createElement('img'), - oldImg = this.headerBackground.querySelector('img'); - newImg.alt = newImg.src = url; - newImg.style.opacity = '0'; - oldImg.style.zIndex = '-1'; - newImg.style.zIndex = '0'; - this.headerBackground.appendChild(newImg); - newImg.onload = function() { - setTimeout(function() { - newImg.style.opacity = null; - setTimeout(function() { - newImg.style.zIndex = null; - this.headerBackground.removeChild(oldImg); - this.headerBackgroundIsChanging = false; - }.bind(this), 500); - }.bind(this), 50); - }.bind(this); - newImg.onerror = function() { - this.headerBackgroundIsChanging = false; - this.switchHeaderBackground(this.originalHeaderBackground); - }.bind(this); - }; - - this.headerBackground = document.querySelector('.header-background'); - this.originalHeaderBackground = this.headerBackground.querySelector('img').src; - - this.initIndex = function(npInterval) { - if(!this.indexPlayingContainer) { - this.indexPlayingContainer = document.querySelector('.header-now-playing'); - this.indexPlayingCover = window.fm.indexPlayingContainer.querySelector('.header-now-playing-cover img'); - this.indexPlayingCover.onerror = function() { - this.indexPlayingCover.src = '//now.flash.moe/resources/no-cover.png'; - }.bind(this); - this.indexPlayingTitle = window.fm.indexPlayingContainer.querySelector('.header-now-playing-title a'); - this.indexPlayingArtist = window.fm.indexPlayingContainer.querySelector('.header-now-playing-artist a'); - } - - if(this.indexPlayingInterval) { - clearInterval(this.indexPlayingInterval); - this.indexPlayingInterval = null; - } - - this.updateIndexNowListening(); - this.indexPlayingInterval = setInterval(this.updateIndexNowListening, (npInterval || 30) * 1000); - }; - - this.initClock = function() { - if(this.homeInterval) - return; - - var digitalClock = document.querySelector('.php-time-digital'), - analogClock = document.querySelector('.php-time-analog'), - dateZone = document.querySelector('.php-time-date'), - digHours = digitalClock.querySelector('.php-time-digital-hours'), - digSeparator = digitalClock.querySelector('.php-time-digital-separator'), - digMinutes = digitalClock.querySelector('.php-time-digital-minutes'), - angHours = analogClock.querySelector('.clock-hand-hours'), - angMinutes = analogClock.querySelector('.clock-hand-minutes'), - angSeconds = analogClock.querySelector('.clock-hand-seconds') - dateWeek = dateZone.querySelector('.php-date-week'), - dateDay = dateZone.querySelector('.php-date-day'), - dateMonth = dateZone.querySelector('.php-date-month'), - dateYear = dateZone.querySelector('.php-date-year'); - - this.homeInterval = setInterval(function() { - if(!document.body.contains(digitalClock)) { - clearInterval(this.homeInterval); - this.homeInterval = null; - return; - } - - var time = new Date; - - var dHour = time.getHours(), - dMin = time.getMinutes(); - - if(dHour < 10) - dHour = '0' + dHour; - if(dMin < 10) - dMin = '0' + dMin; - - dateWeek.textContent = time.getWeek(); - dateDay.textContent = time.getDate(); - dateMonth.textContent = time.getMonth() + 1; - dateYear.textContent = time.getFullYear(); - - digHours.textContent = dHour; - digMinutes.textContent = dMin; - digSeparator.classList[time.getSeconds() % 2 ? 'add' : 'remove']('php-time-digital-separator-hidden'); - - var rSec = time.getSeconds() / 60, - rMin = (time.getMinutes() + Math.min(.99, rSec)) / 60, - rHour = (time.getHours() + Math.min(.99, rMin)) / 12; - - angHours.style.setProperty('--hand-rotation', (rHour * 360).toString() + 'deg'); - angMinutes.style.setProperty('--hand-rotation', (rMin * 360).toString() + 'deg'); - angSeconds.style.setProperty('--hand-rotation', (rSec * 360).toString() + 'deg'); - }.bind(this), 200); - }; - - return this; -}).call(window.fm || {}); +Date.prototype.getWeek = function() { + var date = new Date(this.getTime()); + date.setHours(0, 0, 0, 0); + date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7); + var week1 = new Date(date.getFullYear(), 0, 4); + return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000 - 3 + (week1.getDay() + 6) % 7) / 7); +} +Date.prototype.getWeekYear = function() { + var date = new Date(this.getTime()); + date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7); + return date.getFullYear(); +} + +window.fm = (function() { + var fm = this; + this.container = document.querySelector('.container'); + this.headerBackground = null; + this.originalHeaderBackground = null; + this.defaultCoverImage = 'https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png'; + this.indexIsPlaying = false; + this.indexPlayingDefaultCover = false; + this.indexPlayingContainer = null; + this.indexPlayingCover = null; + this.indexPlayingTitle = null; + this.indexPlayingArtist = null; + this.indexLastNp = null; + this.indexPlayingInterval = null; + this.homeInterval = null; + + this.callByName = function(name, args) { + var ref = window, + parent = null, + parts = name.split('.'); + for(var i = 0; i < parts.length; ++i) { + parent = ref; + if(typeof ref[parts[i]] !== 'undefined') + ref = ref[parts[i]]; + else return; + } + if(typeof ref !== 'function') + return; + return ref.apply(parent, args); + }; + + this.runFuncs = function(funcs) { + for(var i = 0; i < funcs.length; ++i) + fm.callByName(funcs[i][0], funcs[i].slice(1)); + }; + + if(this.onload && Array.isArray(this.onload)) + window.addEventListener('load', function() { fm.runFuncs(fm.onload); }); + + if(sessionStorage.getItem('header-bgs') === null + || sessionStorage.getItem('header-bgs-loaded') < Date.now() - 86400000) { + var hXhr = new XMLHttpRequest; + hXhr.onload = function() { + sessionStorage.setItem('header-bgs', hXhr.responseText); + sessionStorage.setItem('header-bgs-loaded', Date.now()); + }; + hXhr.open('GET', '/header-bgs.json'); + hXhr.send(); + } + + this.dynloadCurrent = function() { + return { + components: { + header: { + title: document.title, + is_index: document.body.classList.contains('index'), + do_fullscreen_header: document.body.classList.contains('fullscreen-header'), + is_now_playing: document.body.classList.contains('now-playing'), + }, + footer: { + onload: this.onload || [], + } + }, + raw_html: fm.container.innerHTML, + }; + }; + + this.dynloadApply = function(state) { + if((state.components || {}).header) { + document.title = state.components.header.title || ''; + document.body.classList[state.components.header.is_index ? 'add' : 'remove']('index'); + document.body.classList[state.components.header.do_fullscreen_header ? 'add' : 'remove']('fullscreen-header'); + document.body.classList[state.components.header.is_now_playing ? 'add' : 'remove']('now-playing'); + } + + if(typeof state.raw_html !== 'undefined') + this.container.innerHTML = state.raw_html; + + if((state.components || {}).footer) { + this.runFuncs(state.components.footer.onload || []); + } + + fm.dynloadInit(); + fm.setRandomHeaderBackgroundIfNotPlaying(); + }; + + this.dynloadInit = function(initial) { + if(initial) { + history.replaceState(this.dynloadCurrent(), document.title, location.toString()); + window.addEventListener('popstate', function(ev) { + fm.dynloadApply(ev.state || {}); + }); + } + + var dynload = document.querySelectorAll('[data-fm-dynload]'); + for(var i = 0; i < dynload.length; ++i) + (function(dc){ + var url = new URL(dc.href), + hash = url.hash.substring(1); + url.hash = ''; + dc.removeAttribute('data-fm-dynload'); + dc.addEventListener('click', function(ev) { + ev.stopPropagation(); + ev.preventDefault(); + + var xhr = new XMLHttpRequest; + xhr.onload = function() { + try { + var obj = JSON.parse(xhr.responseText); + } catch(ex) {} + if(!obj) { + location.assign(dc.href); + return; + } + var title = ((obj.components || {}).header || {}).title || ''; + history.pushState(obj, title, dc.href); + fm.dynloadApply(obj); + if(hash) { + var targetEl = document.getElementById(hash); + if(targetEl) { + setTimeout(function() { + window.scrollTo({ + top: targetEl.getBoundingClientRect().top, + behavior: 'smooth' + }); + }, 500); + } + } + }; + xhr.onerror = function(ev) { + location.assign(dc.href); + }; + xhr.open('GET', url); + xhr.setRequestHeader('Accept', 'application/x-fdynload+json'); + xhr.send(); + }, true); + })(dynload[i]); + }; + + this.dynloadInit(true); + + this.selectTextInElement = function(elem) { + // MSIE + if(document.body.createTextRange) { + var range = document.body.createTextRange(); + range.moveToElementText(elem); + range.select(); + return; + } + + // Mozilla + if(window.getSelection) { + var select = window.getSelection(), + range = document.createRange(); + range.selectNodeContents(elem); + select.removeAllRanges(); + select.addRange(range); + return; + } + + console.warn('Unable to select text.'); + }; + + this.copySelectedText = function() { + if(document.execCommand) { + document.execCommand('copy'); + return; + } + + console.warn('Unable to copy text.'); + }; + + this.getNowListening = function(callback) { + if(!callback) + return; + var xhr = new XMLHttpRequest; + xhr.onreadystatechange = function() { + if(xhr.readyState !== 4 || xhr.status !== 200) + return; + callback.call(this, JSON.parse(xhr.responseText)); + }.bind(this); + xhr.open('GET', '/now-listening.json'); + xhr.send(); + }; + + this.updateIndexNowListening = function() { + window.fm.getNowListening(function(info) { + if(this.indexLastNp === null + || this.indexLastNp.url != info.url + || this.indexLastNp.now_playing != info.now_playing) { + if(this.indexLastNp !== null) + this.originalHeaderBackground = this.getRandomHeaderBackground(); + this.indexLastNp = info; + } else return; + + this.indexIsPlaying = info.now_playing; + this.indexPlayingDefaultCover = !info.cover || info.cover === this.defaultCoverImage; + this.indexPlayingContainer.classList[info.now_playing ? 'remove' : 'add']('header-now-playing-hidden'); + this.indexPlayingCover.alt = this.indexPlayingCover.src = (info.cover !== this.defaultCoverImage ? info.cover : '//now.flash.moe/resources/no-cover.png'); + this.indexPlayingTitle.textContent = this.indexPlayingTitle.title = info.name; + this.indexPlayingTitle.href = info.url; + this.indexPlayingArtist.textContent = this.indexPlayingArtist.title = (info.artist || {}).name || ''; + this.indexPlayingArtist.href = (info.artist || {}).url || ''; + this.switchHeaderBackground( + info.now_playing && !this.indexPlayingDefaultCover + ? this.indexPlayingCover.src + : this.originalHeaderBackground + ); + }); + }; + + this.getRandomHeaderBackground = function() { + var set = JSON.parse(sessionStorage.getItem('header-bgs')); + if(!set) + return '/assets/errors/404.jpg'; + return set[parseInt(Math.random() * set.length) - 1]; + }; + + this.setRandomHeaderBackground = function() { + this.switchHeaderBackground(this.getRandomHeaderBackground()); + }; + + this.setRandomHeaderBackgroundIfNotPlaying = function() { + if(!this.indexIsPlaying || this.indexPlayingDefaultCover) + this.setRandomHeaderBackground(); + }; + + this.getCurrentHeaderBackground = function() { + return this.headerBackground.querySelector('img').src; + }; + + this.headerBackgroundIsChanging = false; + this.switchHeaderBackground = function(url) { + if(this.headerBackgroundIsChanging || this.getCurrentHeaderBackground() === url) + return; + this.headerBackgroundIsChanging = true; + var newImg = document.createElement('img'), + oldImg = this.headerBackground.querySelector('img'); + newImg.alt = newImg.src = url; + newImg.style.opacity = '0'; + oldImg.style.zIndex = '-1'; + newImg.style.zIndex = '0'; + this.headerBackground.appendChild(newImg); + newImg.onload = function() { + setTimeout(function() { + newImg.style.opacity = null; + setTimeout(function() { + newImg.style.zIndex = null; + this.headerBackground.removeChild(oldImg); + this.headerBackgroundIsChanging = false; + }.bind(this), 500); + }.bind(this), 50); + }.bind(this); + newImg.onerror = function() { + this.headerBackgroundIsChanging = false; + this.switchHeaderBackground(this.originalHeaderBackground); + }.bind(this); + }; + + this.headerBackground = document.querySelector('.header-background'); + this.originalHeaderBackground = this.headerBackground.querySelector('img').src; + + this.initIndex = function(npInterval) { + if(!this.indexPlayingContainer) { + this.indexPlayingContainer = document.querySelector('.header-now-playing'); + this.indexPlayingCover = window.fm.indexPlayingContainer.querySelector('.header-now-playing-cover img'); + this.indexPlayingCover.onerror = function() { + this.indexPlayingCover.src = '//now.flash.moe/resources/no-cover.png'; + }.bind(this); + this.indexPlayingTitle = window.fm.indexPlayingContainer.querySelector('.header-now-playing-title a'); + this.indexPlayingArtist = window.fm.indexPlayingContainer.querySelector('.header-now-playing-artist a'); + } + + if(this.indexPlayingInterval) { + clearInterval(this.indexPlayingInterval); + this.indexPlayingInterval = null; + } + + this.updateIndexNowListening(); + this.indexPlayingInterval = setInterval(this.updateIndexNowListening, (npInterval || 30) * 1000); + }; + + this.initClock = function() { + if(this.homeInterval) + return; + + var digitalClock = document.querySelector('.php-time-digital'), + analogClock = document.querySelector('.php-time-analog'), + dateZone = document.querySelector('.php-time-date'), + digHours = digitalClock.querySelector('.php-time-digital-hours'), + digSeparator = digitalClock.querySelector('.php-time-digital-separator'), + digMinutes = digitalClock.querySelector('.php-time-digital-minutes'), + angHours = analogClock.querySelector('.clock-hand-hours'), + angMinutes = analogClock.querySelector('.clock-hand-minutes'), + angSeconds = analogClock.querySelector('.clock-hand-seconds') + dateWeek = dateZone.querySelector('.php-date-week'), + dateDay = dateZone.querySelector('.php-date-day'), + dateMonth = dateZone.querySelector('.php-date-month'), + dateYear = dateZone.querySelector('.php-date-year'); + + this.homeInterval = setInterval(function() { + if(!document.body.contains(digitalClock)) { + clearInterval(this.homeInterval); + this.homeInterval = null; + return; + } + + var time = new Date; + + var dHour = time.getHours(), + dMin = time.getMinutes(); + + if(dHour < 10) + dHour = '0' + dHour; + if(dMin < 10) + dMin = '0' + dMin; + + dateWeek.textContent = time.getWeek(); + dateDay.textContent = time.getDate(); + dateMonth.textContent = time.getMonth() + 1; + dateYear.textContent = time.getFullYear(); + + digHours.textContent = dHour; + digMinutes.textContent = dMin; + digSeparator.classList[time.getSeconds() % 2 ? 'add' : 'remove']('php-time-digital-separator-hidden'); + + var rSec = time.getSeconds() / 60, + rMin = (time.getMinutes() + Math.min(.99, rSec)) / 60, + rHour = (time.getHours() + Math.min(.99, rMin)) / 12; + + angHours.style.setProperty('--hand-rotation', (rHour * 360).toString() + 'deg'); + angMinutes.style.setProperty('--hand-rotation', (rMin * 360).toString() + 'deg'); + angSeconds.style.setProperty('--hand-rotation', (rSec * 360).toString() + 'deg'); + }.bind(this), 200); + }; + + return this; +}).call(window.fm || {}); diff --git a/public/assets/2020v2.css b/public/css/2021.css similarity index 94% rename from public/assets/2020v2.css rename to public/css/2021.css index 2730565..6928ae1 100644 --- a/public/assets/2020v2.css +++ b/public/css/2021.css @@ -1,953 +1,1024 @@ -* { - margin: 0; - padding: 0; - box-sizing: border-box; - position: relative; - outline-style: none; - user-select: none; -} - -html, body { - width: 100%; - height: 100%; -} - -html { - scrollbar-color: #4a3650 #111; -} - -.hidden { - display: none !important; -} - -/* an attempt to replicate scrollbar-color for chromosome */ -::-webkit-scrollbar { - width: 6px; - background-color: #111; -} -::-webkit-scrollbar-thumb { - background-color: #4a3650; -} -::-webkit-scrollbar-thumb:hover { - background-color: #5b4761; -} -::-webkit-scrollbar-thumb:active { - background-color: #6c5872; -} - -body { - background-color: #111; - color: #fff; - font: 12px/20px Tahoma, Geneva, 'Dejavu Sans', Arial, Helvetica, sans-serif; - display: flex; - flex-direction: column; -} - -.header, .footer { flex: 0 0 auto; } -.container { flex: 1 1 auto; } - -.header { - width: 100%; - height: 200px; - overflow: hidden; - transition: height .5s; -} -@media (max-width: 700px) { - .header { height: auto; } -} -.index .header { - height: 50vh; -} -.fullscreen-header .header { - height: 100%; -} -.header-background { - position: absolute; - top: 0; - bottom: 0; - right: 0; - left: 0; - overflow: hidden; -} -.header-background img { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - object-fit: cover; - filter: blur(20px) brightness(80%); - transform: scale(1.2); - opacity: 1; - transition: opacity .5s; -} -.header-foreground { - width: 100%; - height: 100%; - display: flex; - max-width: 1200px; - margin: 0 auto; - justify-content: space-between; - align-items: flex-end; - padding: 10px; -} -@media (max-width: 700px) { - .header-foreground { - justify-content: center; - align-items: center; - flex-direction: column; - } -} -.index .header-foreground { - align-items: center; - justify-content: flex-end; - flex-direction: column; -} -.header-logo { - font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; - font-size: 3em; - line-height: 1.2em; - filter: drop-shadow(0 1px 5px #000); - color: transparent; - text-decoration: none; - display: flex; - flex: 0 0 auto; -} -.index .header-logo { - font-size: 5em; -} -.header-flash { - background-image: linear-gradient(180deg, #eee 0%, #ddd 50%, #ccc 50%, #aaa 100%); - background-clip: text; - -webkit-background-clip: text; -} -.header-wave { - background-image: linear-gradient(0deg, #281430 0%, #392540 50%, #4a3650 50%, #6c5871 100%); - -webkit-background-clip: text; -} -.header-right { - display: flex; - flex-direction: column; - align-items: flex-end; -} -.header-menu { - display: flex; - justify-content: center; - flex: 0 0 auto; - margin-top: 10px; -} -.index .header-menu, -.fullscreen-header .header-menu, -.now-playing .header-menu { - display: none; -} -.header-menu a { - display: block; - color: #fff; - text-decoration: none; - font-size: 1.5em; - line-height: 1.2em; - margin: 0 2px; - padding: 5px 10px; - border-radius: 5px; - text-shadow: 0 1px 5px #000; - transition: background-color .1s; -} -.header-menu a:hover, -.header-menu a:focus { - background-color: rgba(255, 255, 255, .1); -} -.header-menu a:active { - background-color: rgba(127, 127, 127, .1); -} -@media (max-width: 800px) { - .header-menu { - display: grid; - grid-template-columns: 1fr 1fr 1fr; - } - .header-menu a { - text-align: center; - min-width: 150px; - margin: 2px; - padding: 10px 0; - } - .header-menu :first-child { - display: none; - } -} -@media (max-width: 480px) { - .header-menu { grid-template-columns: 1fr 1fr; } -} -@media (max-width: 320px) { - .header-menu { grid-template-columns: 1fr; } -} - -.footer {} -.index .footer, -.fullscreen-header .footer, -.now-playing .footer { - display: none; -} -.footer-text { - opacity: .2; - font-size: .9em; - text-align: center; - max-width: 1200px; - margin: 5px auto; -} - -.index { - display: flex; - flex-direction: column; - height: 100%; - max-height: 100vh; - overflow: hidden; -} -@media(max-width: 900px) { .index { - max-height: initial; - overflow: auto; -} } - -.index-menu { - width: 100%; - display: flex; - justify-content: center; - background-image: linear-gradient(0deg, #111 0%, #222 50%, #333 50%, #555 100%); - flex: 0 0 auto; - box-shadow: 0 0 1em rgba(0, 0, 0, .8); -} -.index-menu a { - display: block; - color: #fff; - text-decoration: none; - font-size: 1.5em; - line-height: 1.2em; - padding: 5px 10px; - transition: background-color .1s; -} -.index-menu a:hover, -.index-menu a:focus { - background-color: rgba(255, 255, 255, .1); -} -.index-menu a:active { - background-color: rgba(127, 127, 127, .1); -} -@media (max-width: 600px) { - .index-menu { - flex-wrap: wrap; - } - .index-menu a { - min-width: 200px; - margin: 2px; - padding: 10px; - text-align: center; - } -} - -.socials { - max-width: 1100px; - width: 100%; - margin: 10px auto; - padding: 0 15px; - display: grid; - grid-template-columns: 1fr 1fr 1fr; - justify-content: space-evenly; - column-gap: 10px; -} -@media(max-width: 980px) { .socials { grid-template-columns: 1fr 1fr; } } -@media(max-width: 640px) { .socials { grid-template-columns: 1fr; } } -.social { - width: 100%; - margin: 5px 0; - filter: drop-shadow(0 1px 5px #000); - cursor: pointer; - display: flex; - align-items: center; - padding: 5px; -} -.index-contact .social { - margin: 10px 0; -} -.social-background { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-image: linear-gradient(0deg, #1118 0%, #2228 50%, #3338 50%, #5558 100%); - transform: skew(-15deg); -} -.social:active .social-background { - background-image: linear-gradient(0deg, #1118 0%, #2228 50%, #3338 50%, #3338 100%); -} -.social-icon { - width: 25px; - height: 25px; - margin: 3px 4px 2px 8px; - pointer-events: none; -} -.social-content { - margin: 2px 5px; - pointer-events: none; -} -.social-name { - font-size: .9em; - line-height: 1.5em; -} -.social-handle { - font-size: 1.5em; - line-height: 1.3em; - font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; - user-select: text; -} -.social-twitter .social-background { background-color: #1da1f2; } -.social-youtube .social-background { background-color: #ff0000; } -.social-flashii .social-background { background-color: #8559a5; } -.social-github .social-background { background-color: #222222; } -.social-twitch .social-background { background-color: #6441a4; } -.social-steam .social-background { background-color: #2c2e35; } -.social-osu .social-background { background-color: #ff66aa; } -.social-tetrio .social-background { background-color: #40c045; } -.social-email .social-background { background-color: #4a3650; } -.social-lastfm .social-background { background-color: #ba0000; } -.social-nin-sw .social-background { background-color: #e60012; } -.social-nin-3ds .social-background { background-color: #ce171f; } -.social-nin-wiiu .social-background { background-color: #00acca; } -.social-paypal .social-background { background-color: #009cde; } -.social-patreon .social-background { background-color: #f86754; } - -.index-featured { - display: grid; - grid-template-columns: 1fr 1fr 1fr; - flex: 1 1 auto; - overflow: hidden; - margin: 5px auto; - padding: 0 10px; - margin-bottom: 0; - column-gap: 10px; - row-gap: 10px; - width: 100%; - max-width: 1500px; -} -@media(max-width: 900px) { .index-featured { grid-template-columns: 1fr; overflow: visible; } } -.index-feature { - overflow: auto; - scrollbar-width: thin; -} -.index-feature-header { - border-bottom: 1px solid #333; - display: flex; - align-items: center; -} -.index-feature-header-link { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; -} -.index-feature-header-title { - font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; - font-size: 2em; - line-height: 1.5em; - pointer-events: none; - flex: 1 1 auto; -} -.index-feature-header-more { - pointer-events: none; - flex: 0 0 auto; - margin: 0 5px; - padding: 2px 8px; - border-radius: 2em; - font-size: .9em; - line-height: 1.5em; - background-color: #1a1a1a; - transition: background-color .2s; -} -.index-feature-header-more::after { - content: " »"; -} -.index-feature-header:focus-within .index-feature-header-more, -.index-feature-header:hover .index-feature-header-more, -.index-feature-header-more:focus { - background-color: #2a2a2a; -} -.index-feature-header:active .index-feature-header-more, -.index-feature-header-more:active { - background-color: #222; -} - -.index-contact { - padding: 5px 15px; - overflow: auto; -} - -.index-blog {} -.index-blog-post { - margin: 5px; - padding: 2px 5px; - background: #222; - box-shadow: 0 2px 5px #000; -} -.index-blog-post-link { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; -} -.index-blog-post-header { - display: flex; - align-items: center; - pointer-events: none; - border-bottom: 1px solid #333; -} -.index-blog-post-title { - font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; - flex: 1 1 auto; - font-size: 1.5em; - line-height: 1.5em; -} -.index-blog-post-published { - flex: 0 1 auto; - font-size: .9em; - line-height: 1.5em; - opacity: .5; - padding-bottom: 2px; -} -.index-blog-post-content { - pointer-events: none; - max-height: 70px; - overflow: hidden; -} -.index-blog-post-content a { - pointer-events: initial; - color: inherit; - text-decoration: underline; -} - -.header-now-playing { - max-height: 60px; - min-width: 300px; - max-width: 500px; - height: 100%; - background-image: linear-gradient(0deg, #111c 0%, #222c 50%, #333c 50%, #555c 100%); - box-shadow: 0 2px 1em #000; - border-radius: 5px; - overflow: hidden; - align-items: center; - bottom: 0; - padding: 5px; - display: grid; - grid-template-columns: 25px 50px 1fr; - column-gap: 5px; - transition: bottom .5s, width .2s, max-height .5s, padding .2s; -} -.header-now-playing-hidden { - bottom: -80px; - max-height: 0; - padding: 0; -} -.header-now-playing-icon img { - vertical-align: middle; -} -.header-now-playing-cover { - width: 50px; - height: 50px; -} -.header-now-playing-cover img { - width: 100%; - height: 100%; - vertical-align: middle; - object-fit: cover; -} -.header-now-playing-details { - overflow: hidden; - white-space: nowrap; -} -.header-now-playing-details a { - color: #fff; - text-decoration: none; - transition: width .2s; -} -.header-now-playing-details a:hover, -.header-now-playing-details a:focus { - text-decoration: underline; -} -.header-now-playing-title { - font-size: 1.2em; -} -.header-now-playing-title, -.header-now-playing-artist { - overflow: hidden; - text-overflow: ellipsis; -} - -.index-project { - margin: 5px; - background-image: linear-gradient(180deg, #555c 0, #333c 32px, #222c 32px, #111c 100%); - box-shadow: 0 2px 5px #000; - border-radius: 5px; - overflow: hidden; -} -.index-project-anchor { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; -} -.index-project-content { - margin: 5px 8px; - margin-bottom: 0; - pointer-events: none; -} -.index-project-name { - font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; - flex: 1 1 auto; - font-size: 1.5em; - line-height: 1.5em; -} -.index-project-summary { - margin-top: 2px; -} -.index-project-links { - display: flex; - pointer-events: none; - margin: 0 3px; -} -.index-project-link { - pointer-events: initial; - margin: 4px 1px; - color: #fff; - text-decoration: none; - min-width: 100px; - padding: 2px 8px; - border-radius: 4px; - transition: background-color .2s; -} -.index-project-link:hover, -.index-project-link:focus { - background-color: #fff2; -} -.index-project-link:active { - background-color: #fff1; -} - - -.section { - padding: 0 15px; -} -.section:not(:first-child) { - margin-top: 30px; -} -.section-content { - max-width: 1100px; - margin: 10px auto; - padding: 10px 20px; - filter: drop-shadow(0 1px 5px #000); -} -.section-background { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-image: linear-gradient(0deg, #111 0%, #222 50%, #333 50%, #555 100%); - transform: skew(-15deg); -} -.section-content h1 { - font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; - font-size: 2em; - line-height: 1em; - font-weight: 400; -} - -.project { - padding: 0 10px; - /*background-color: var(--project-colour); - background-image: linear-gradient(#111e, #111e); - overflow: hidden;*/ -} -.project-content { - max-width: 1100px; - width: 100%; - margin: 1em auto; - overflow: hidden; - background-color: var(--project-colour); - background-image: linear-gradient(180deg, #555c 0, #333c 38px, #222c 38px, #111c 100%); - box-shadow: 0 2px 1em #000; - border-radius: 5px; -} -.project-languages { - font-size: 0; - line-height: 0; - display: inline-block; - font-family: Tahoma, Geneva, 'Dejavu Sans', Arial, Helvetica, sans-serif; - margin-left: 4px; -} -.project-language { - font-size: 11px; - line-height: 18px; - display: inline-block; - border-left: 4px solid var(--language-colour); - background-color: var(--language-colour); - background-image: linear-gradient(90deg, #1118, #111a); - border-radius: 4px; - overflow: hidden; - padding: 0 4px; - margin: 0 4px; - box-shadow: 0 0 1px var(--language-colour); -} -.project-details { - margin: 10px; - margin-bottom: 0; -} -.project-details h2 { - font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; - font-size: 2em; - line-height: 1em; - font-weight: 400; - margin-bottom: 5px; -} -.project-details p { - font-size: .9em; - line-height: 1.8em; -} -.project-details .project-details-summary { - font-size: 1.2em; - line-height: 1.5em; -} -.project-links { - display: flex; - margin: 0 3px; -} -.project-link { - margin: 4px 1px; - color: #fff; - text-decoration: none; - min-width: 100px; - padding: 2px 8px; - border-radius: 4px; - transition: background-color .2s; -} -.project-link:hover, -.project-link:active { - background-color: #fff2; -} -.project-link:focus { - background-color: #fff1; -} - -.etcetera-item { - max-width: 1100px; - margin: 10px auto; - padding: 0 15px; - filter: drop-shadow(0 1px 5px #000); -} -.etcetera-item-link { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: #393939; - background-image: linear-gradient(0deg, #1118 0%, #2228 50%, #3338 50%, #5558 100%); - border-radius: 5px; - overflow: hidden; - transition: background-color .2s; -} -.etcetera-item-link:hover, -.etcetera-item-link:focus { - background-color: #555; -} -.etcetera-item-link:active { - background-color: #222; -} -.etcetera-item-content { - max-width: 1100px; - margin: 0 auto; - padding: 10px 12px; -} -.etcetera-item-content h2 { - font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; - font-size: 1.5em; - line-height: 1.2em; - font-weight: 400; - pointer-events: none; -} -.etcetera-item-content p { - font-size: .9em; - line-height: 1.4em; - pointer-events: none; -} - -.http-error { - text-align: center; -} -.http-error-head { - font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; - font-size: 2.5em; - line-height: 2em; - font-weight: 400; -} - -.related-site { - max-width: 1100px; - margin: 10px auto; - padding: 0 15px; - filter: drop-shadow(0 1px 5px #000); -} -.related-site-link { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: #393939; - background-image: linear-gradient(0deg, #1118 0%, #2228 50%, #3338 50%, #5558 100%); - border-radius: 5px; - overflow: hidden; - transition: background-color .2s; -} -.related-site-link:hover, -.related-site-link:focus { - background-color: #555; -} -.related-site-link:active { - background-color: #222; -} -.related-site-content { - max-width: 1100px; - margin: 0 auto; - padding: 10px 12px; -} -.related-site-content h2 { - font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; - font-size: 1.5em; - line-height: 1.2em; - font-weight: 400; - pointer-events: none; -} -.related-site-content p { - font-size: .9em; - line-height: 1.4em; - pointer-events: none; -} - -.php { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - width: 100%; - height: 100%; - z-index: 177; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - pointer-events: none; -} -.php-search { - max-width: 600px; - width: 100%; - pointer-events: initial; -} -.php-search-input { - border: 1px solid #999; - background-color: #fff; - opacity: .5; - box-shadow: 0 2px .5em #000a, inset 0 1px 2px #000a; - transition: opacity .5s, box-shadow .5s; -} -.php-search-input:hover, -.php-search-input:focus, -.php-search-input:focus-within { - opacity: 1; - box-shadow: 0 2px 1em #000, inset 0 1px 2px #000a; -} -.php-search-input input { - border-width: 0; - background-color: transparent; - color: #000; - font-family: Tahoma, Geneva, 'Dejavu Sans', Arial, Helvetica, sans-serif; - font-size: 24px; - line-height: 35px; - width: 100%; - height: 100%; - padding: 2px 5px; -} -.php-time { - display: flex; - justify-content: center; - align-items: center; - margin: 20px auto; - width: 100%; -} -.php-time-analog { - flex: 0 0 auto; -} -.php-time-alter { - flex: 0 0 auto; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - width: 300px; - font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; - filter: drop-shadow(0 1px 5px #000); - color: transparent; -} -.php-time-digital { - font-size: 7em; - line-height: 1.5em; - display: flex; - flex: 0 0 auto; - max-width: 300px; - width: 100%; - align-items: center; - justify-content: center; -} -.php-time-date { - font-size: 2em; -} -.php-time-digital-separator { - margin-top: -4px; -} -.php-time-digital-separator-hidden { - visibility: hidden; -} -.php-time-digital-hours, -.php-time-digital-separator, -.php-date-label { - background-image: linear-gradient(180deg, #eee 0%, #ddd 50%, #ccc 50%, #aaa 100%); - background-clip: text; - -webkit-background-clip: text; -} -.php-time-digital-minutes, -.php-date-week, -.php-date-day, -.php-date-month, -.php-date-year { - background-image: linear-gradient(0deg, #281430 0%, #392540 50%, #4a3650 50%, #6c5871 100%); - background-clip: text; - -webkit-background-clip: text; -} - -.clock { - width: 200px; - height: 200px; - border-radius: 100%; - border: 4px solid #c0c0c0; - overflow: hidden; - box-sizing: content-box; - filter: drop-shadow(0 1px 5px #000); -} -.clock-background { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: #fffd; - filter: blur(20px); -} -.clock-center { - position: absolute; - top: 96px; - left: 96px; - width: 8px; - height: 8px; - background-color: #000; - border-radius: 100%; - z-index: 1000; -} -.clock-hand { - --hand-rotation: 0deg; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - transform: rotate(var(--hand-rotation)); - z-index: 900; -} -.clock-hand-display { - position: absolute; - width: 2px; - left: 99px; - height: 99px; - background-color: #000; -} -.clock-hand-hours { - z-index: 910; -} -.clock-hand-minutes { - z-index: 920; -} -.clock-hand-seconds { - z-index: 930; -} -.clock-hand-hours .clock-hand-display { - height: 70px; - margin-top: 30px; - filter: drop-shadow(0 1px 2px #000); -} -.clock-hand-minutes .clock-hand-display { - height: 80px; - margin-top: 20px; - filter: drop-shadow(0 1px 3px #000); -} -.clock-hand-seconds .clock-hand-display { - height: 100px; - margin-top: 15px; - opacity: .6; - background-color: #f00; - filter: drop-shadow(0 1px 4px #000); -} -.clock-number { - position: absolute; - top: 0; - left: 0; - z-index: 500; - width: 100px; - height: 100px; -} -.clock-number-display { - position: absolute; - width: 2px; - height: 10px; - background-color: #000; -} -.clock-number-9 .clock-number-display, -.clock-number-3 .clock-number-display { - width: 10px; - height: 2px; -} -.clock-number-6 .clock-number-display { - bottom: 0; -} -.clock-number-12 .clock-number-display { - right: 0; - height: 15px; -} -.clock-number-3 .clock-number-display { - right: 0; - bottom: 0; -} -.clock-number-12 { - left: 1px; -} -.clock-number-9 { - top: 99px; -} -.clock-number-6 { - top: 100px; - left: 99px; -} -.clock-number-3 { - top: 1px; - left: 100px; -} +* { + margin: 0; + padding: 0; + box-sizing: border-box; + position: relative; + outline-style: none; + user-select: none; +} + +html, body { + width: 100%; + height: 100%; +} + +html { + scrollbar-color: #4a3650 #111; +} + +.hidden { + display: none !important; +} + +/* an attempt to replicate scrollbar-color for chromosome */ +::-webkit-scrollbar { + width: 6px; + background-color: #111; +} +::-webkit-scrollbar-thumb { + background-color: #4a3650; +} +::-webkit-scrollbar-thumb:hover { + background-color: #5b4761; +} +::-webkit-scrollbar-thumb:active { + background-color: #6c5872; +} + +body { + background-color: #111; + color: #fff; + font: 12px/20px Tahoma, Geneva, 'Dejavu Sans', Arial, Helvetica, sans-serif; + display: flex; + flex-direction: column; +} + +.header, .footer { flex: 0 0 auto; } +.container { flex: 1 1 auto; } + +.header { + width: 100%; + height: 200px; + overflow: hidden; + transition: height .5s; +} +@media (max-width: 700px) { + .header { height: auto; } +} +.index .header { + height: 50vh; +} +.fullscreen-header .header { + height: 100%; +} +.header-background { + position: absolute; + top: 0; + bottom: 0; + right: 0; + left: 0; + overflow: hidden; +} +.header-background img { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + object-fit: cover; + filter: blur(20px) brightness(80%); + transform: scale(1.2); + opacity: 1; + transition: opacity .5s; +} +.header-foreground { + width: 100%; + height: 100%; + display: flex; + max-width: 1200px; + margin: 0 auto; + justify-content: space-between; + align-items: flex-end; + padding: 10px; +} +@media (max-width: 700px) { + .header-foreground { + justify-content: center; + align-items: center; + flex-direction: column; + } +} +.index .header-foreground { + align-items: center; + justify-content: flex-end; + flex-direction: column; +} +.header-logo { + font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; + font-size: 3em; + line-height: 1.2em; + filter: drop-shadow(0 1px 5px #000); + color: transparent; + text-decoration: none; + display: flex; + flex: 0 0 auto; +} +.index .header-logo { + font-size: 5em; +} +.header-flash { + background-image: linear-gradient(180deg, #eee 0%, #ddd 50%, #ccc 50%, #aaa 100%); + background-clip: text; + -webkit-background-clip: text; +} +.header-wave { + background-image: linear-gradient(0deg, #281430 0%, #392540 50%, #4a3650 50%, #6c5871 100%); + -webkit-background-clip: text; +} +.header-right { + display: flex; + flex-direction: column; + align-items: flex-end; +} +.header-menu { + display: flex; + justify-content: center; + flex: 0 0 auto; + margin-top: 10px; +} +.index .header-menu, +.fullscreen-header .header-menu, +.now-playing .header-menu { + display: none; +} +.header-menu a { + display: block; + color: #fff; + text-decoration: none; + font-size: 1.5em; + line-height: 1.2em; + margin: 0 2px; + padding: 5px 10px; + border-radius: 5px; + text-shadow: 0 1px 5px #000; + transition: background-color .1s; +} +.header-menu a:hover, +.header-menu a:focus { + background-color: rgba(255, 255, 255, .1); +} +.header-menu a:active { + background-color: rgba(127, 127, 127, .1); +} +@media (max-width: 800px) { + .header-menu { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + } + .header-menu a { + text-align: center; + min-width: 150px; + margin: 2px; + padding: 10px 0; + } + .header-menu :first-child { + display: none; + } +} +@media (max-width: 480px) { + .header-menu { grid-template-columns: 1fr 1fr; } +} +@media (max-width: 320px) { + .header-menu { grid-template-columns: 1fr; } +} + +.footer {} +.index .footer, +.fullscreen-header .footer, +.now-playing .footer { + display: none; +} +.footer-text { + opacity: .2; + font-size: .9em; + text-align: center; + max-width: 1200px; + margin: 5px auto; +} + +.index { + display: flex; + flex-direction: column; + height: 100%; + max-height: 100vh; + overflow: hidden; +} +@media(max-width: 900px) { .index { + max-height: initial; + overflow: auto; +} } + +.index-menu { + width: 100%; + display: flex; + justify-content: center; + background-image: linear-gradient(0deg, #111 0%, #222 50%, #333 50%, #555 100%); + flex: 0 0 auto; + box-shadow: 0 0 1em rgba(0, 0, 0, .8); +} +.index-menu a { + display: block; + color: #fff; + text-decoration: none; + font-size: 1.5em; + line-height: 1.2em; + padding: 5px 10px; + transition: background-color .1s; +} +.index-menu a:hover, +.index-menu a:focus { + background-color: rgba(255, 255, 255, .1); +} +.index-menu a:active { + background-color: rgba(127, 127, 127, .1); +} +@media (max-width: 600px) { + .index-menu { + flex-wrap: wrap; + } + .index-menu a { + min-width: 200px; + margin: 2px; + padding: 10px; + text-align: center; + } +} + +.socials { + max-width: 1100px; + width: 100%; + margin: 10px auto; + padding: 0 15px; + display: grid; + grid-template-columns: 1fr 1fr 1fr; + justify-content: space-evenly; + column-gap: 10px; +} +@media(max-width: 980px) { .socials { grid-template-columns: 1fr 1fr; } } +@media(max-width: 640px) { .socials { grid-template-columns: 1fr; } } +.social { + width: 100%; + margin: 5px 0; + filter: drop-shadow(0 1px 5px #000); + cursor: pointer; + display: flex; + align-items: center; + padding: 5px; +} +.index-contact .social { + margin: 10px 0; +} +.social-background { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-image: linear-gradient(0deg, #1118 0%, #2228 50%, #3338 50%, #5558 100%); + transform: skew(-15deg); +} +.social:active .social-background { + background-image: linear-gradient(0deg, #1118 0%, #2228 50%, #3338 50%, #3338 100%); +} +.social-icon { + width: 25px; + height: 25px; + margin: 3px 4px 2px 8px; + pointer-events: none; +} +.social-content { + margin: 2px 5px; + pointer-events: none; +} +.social-name { + font-size: .9em; + line-height: 1.5em; +} +.social-handle { + font-size: 1.5em; + line-height: 1.3em; + font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; + user-select: text; +} +.social-twitter .social-background { background-color: #1da1f2; } +.social-youtube .social-background { background-color: #ff0000; } +.social-flashii .social-background { background-color: #8559a5; } +.social-github .social-background { background-color: #222222; } +.social-twitch .social-background { background-color: #6441a4; } +.social-steam .social-background { background-color: #2c2e35; } +.social-osu .social-background { background-color: #ff66aa; } +.social-tetrio .social-background { background-color: #40c045; } +.social-email .social-background { background-color: #4a3650; } +.social-lastfm .social-background { background-color: #ba0000; } +.social-nin-sw .social-background { background-color: #e60012; } +.social-nin-3ds .social-background { background-color: #ce171f; } +.social-nin-wiiu .social-background { background-color: #00acca; } +.social-paypal .social-background { background-color: #009cde; } +.social-patreon .social-background { background-color: #f86754; } + +.index-featured { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + flex: 1 1 auto; + overflow: hidden; + margin: 5px auto; + padding: 0 10px; + margin-bottom: 0; + column-gap: 10px; + row-gap: 10px; + width: 100%; + max-width: 1500px; +} +@media(max-width: 900px) { .index-featured { grid-template-columns: 1fr; overflow: visible; } } +.index-feature { + overflow: auto; + scrollbar-width: thin; +} +.index-feature-header { + border-bottom: 1px solid #333; + display: flex; + align-items: center; +} +.index-feature-header-link { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.index-feature-header-title { + font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; + font-size: 2em; + line-height: 1.5em; + pointer-events: none; + flex: 1 1 auto; +} +.index-feature-header-more { + pointer-events: none; + flex: 0 0 auto; + margin: 0 5px; + padding: 2px 8px; + border-radius: 2em; + font-size: .9em; + line-height: 1.5em; + background-color: #1a1a1a; + transition: background-color .2s; +} +.index-feature-header-more::after { + content: " »"; +} +.index-feature-header:focus-within .index-feature-header-more, +.index-feature-header:hover .index-feature-header-more, +.index-feature-header-more:focus { + background-color: #2a2a2a; +} +.index-feature-header:active .index-feature-header-more, +.index-feature-header-more:active { + background-color: #222; +} + +.index-contact { + padding: 5px 15px; + overflow: auto; +} + +.index-blog {} +.index-blog-post { + margin: 5px; + padding: 2px 5px; + background: #222; + box-shadow: 0 2px 5px #000; +} +.index-blog-post-link { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.index-blog-post-header { + display: flex; + align-items: center; + pointer-events: none; + border-bottom: 1px solid #333; +} +.index-blog-post-title { + font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; + flex: 1 1 auto; + font-size: 1.5em; + line-height: 1.5em; +} +.index-blog-post-published { + flex: 0 1 auto; + font-size: .9em; + line-height: 1.5em; + opacity: .5; + padding-bottom: 2px; +} +.index-blog-post-content { + pointer-events: none; + max-height: 70px; + overflow: hidden; +} +.index-blog-post-content a { + pointer-events: initial; + color: inherit; + text-decoration: underline; +} + +.header-now-playing { + max-height: 60px; + min-width: 300px; + max-width: 500px; + height: 100%; + background-image: linear-gradient(0deg, #111c 0%, #222c 50%, #333c 50%, #555c 100%); + box-shadow: 0 2px 1em #000; + border-radius: 5px; + overflow: hidden; + align-items: center; + bottom: 0; + padding: 5px; + display: grid; + grid-template-columns: 25px 50px 1fr; + column-gap: 5px; + transition: bottom .5s, width .2s, max-height .5s, padding .2s; +} +.header-now-playing-hidden { + bottom: -80px; + max-height: 0; + padding: 0; +} +.header-now-playing-icon img { + vertical-align: middle; +} +.header-now-playing-cover { + width: 50px; + height: 50px; +} +.header-now-playing-cover img { + width: 100%; + height: 100%; + vertical-align: middle; + object-fit: cover; +} +.header-now-playing-details { + overflow: hidden; + white-space: nowrap; +} +.header-now-playing-details a { + color: #fff; + text-decoration: none; + transition: width .2s; +} +.header-now-playing-details a:hover, +.header-now-playing-details a:focus { + text-decoration: underline; +} +.header-now-playing-title { + font-size: 1.2em; +} +.header-now-playing-title, +.header-now-playing-artist { + overflow: hidden; + text-overflow: ellipsis; +} + +.index-project { + margin: 5px; + background-image: linear-gradient(180deg, #555c 0, #333c 32px, #222c 32px, #111c 100%); + box-shadow: 0 2px 5px #000; + border-radius: 5px; + overflow: hidden; +} +.index-project-anchor { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.index-project-content { + margin: 5px 8px; + margin-bottom: 0; + pointer-events: none; +} +.index-project-name { + font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; + flex: 1 1 auto; + font-size: 1.5em; + line-height: 1.5em; +} +.index-project-summary { + margin-top: 2px; +} +.index-project-links { + display: flex; + pointer-events: none; + margin: 0 3px; +} +.index-project-link { + pointer-events: initial; + margin: 4px 1px; + color: #fff; + text-decoration: none; + min-width: 100px; + padding: 2px 8px; + border-radius: 4px; + transition: background-color .2s; +} +.index-project-link:hover, +.index-project-link:focus { + background-color: #fff2; +} +.index-project-link:active { + background-color: #fff1; +} + + +.section { + padding: 0 15px; +} +.section:not(:first-child) { + margin-top: 30px; +} +.section-content { + max-width: 1100px; + margin: 10px auto; + padding: 10px 20px; + filter: drop-shadow(0 1px 5px #000); +} +.section-background { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-image: linear-gradient(0deg, #111 0%, #222 50%, #333 50%, #555 100%); + transform: skew(-15deg); +} +.section-content h1 { + font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; + font-size: 2em; + line-height: 1em; + font-weight: 400; +} + +.project { + padding: 0 10px; + /*background-color: var(--project-colour); + background-image: linear-gradient(#111e, #111e); + overflow: hidden;*/ +} +.project-content { + max-width: 1100px; + width: 100%; + margin: 1em auto; + overflow: hidden; + background-color: var(--project-colour); + background-image: linear-gradient(180deg, #555c 0, #333c 38px, #222c 38px, #111c 100%); + box-shadow: 0 2px 1em #000; + border-radius: 5px; +} +.project-languages { + font-size: 0; + line-height: 0; + display: inline-block; + font-family: Tahoma, Geneva, 'Dejavu Sans', Arial, Helvetica, sans-serif; + margin-left: 4px; +} +.project-language { + font-size: 11px; + line-height: 18px; + display: inline-block; + border-left: 4px solid var(--language-colour); + background-color: var(--language-colour); + background-image: linear-gradient(90deg, #1118, #111a); + border-radius: 4px; + overflow: hidden; + padding: 0 4px; + margin: 0 4px; + box-shadow: 0 0 1px var(--language-colour); +} +.project-details { + margin: 10px; + margin-bottom: 0; +} +.project-details h2 { + font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; + font-size: 2em; + line-height: 1em; + font-weight: 400; + margin-bottom: 5px; +} +.project-details p { + font-size: .9em; + line-height: 1.8em; +} +.project-details .project-details-summary { + font-size: 1.2em; + line-height: 1.5em; +} +.project-links { + display: flex; + margin: 0 3px; +} +.project-link { + margin: 4px 1px; + color: #fff; + text-decoration: none; + min-width: 100px; + padding: 2px 8px; + border-radius: 4px; + transition: background-color .2s; +} +.project-link:hover, +.project-link:active { + background-color: #fff2; +} +.project-link:focus { + background-color: #fff1; +} + +.etcetera-item { + max-width: 1100px; + margin: 10px auto; + padding: 0 15px; + filter: drop-shadow(0 1px 5px #000); +} +.etcetera-item-link { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: #393939; + background-image: linear-gradient(0deg, #1118 0%, #2228 50%, #3338 50%, #5558 100%); + border-radius: 5px; + overflow: hidden; + transition: background-color .2s; +} +.etcetera-item-link:hover, +.etcetera-item-link:focus { + background-color: #555; +} +.etcetera-item-link:active { + background-color: #222; +} +.etcetera-item-content { + max-width: 1100px; + margin: 0 auto; + padding: 10px 12px; +} +.etcetera-item-content h2 { + font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; + font-size: 1.5em; + line-height: 1.2em; + font-weight: 400; + pointer-events: none; +} +.etcetera-item-content p { + font-size: .9em; + line-height: 1.4em; + pointer-events: none; +} + +.http-error { + text-align: center; +} +.http-error-head { + font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; + font-size: 2.5em; + line-height: 2em; + font-weight: 400; +} + +.related-site { + max-width: 1100px; + margin: 10px auto; + padding: 0 15px; + filter: drop-shadow(0 1px 5px #000); +} +.related-site-link { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: #393939; + background-image: linear-gradient(0deg, #1118 0%, #2228 50%, #3338 50%, #5558 100%); + border-radius: 5px; + overflow: hidden; + transition: background-color .2s; +} +.related-site-link:hover, +.related-site-link:focus { + background-color: #555; +} +.related-site-link:active { + background-color: #222; +} +.related-site-content { + max-width: 1100px; + margin: 0 auto; + padding: 10px 12px; +} +.related-site-content h2 { + font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; + font-size: 1.5em; + line-height: 1.2em; + font-weight: 400; + pointer-events: none; +} +.related-site-content p { + font-size: .9em; + line-height: 1.4em; + pointer-events: none; +} + +.php { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + width: 100%; + height: 100%; + z-index: 177; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + pointer-events: none; +} +.php-search { + max-width: 600px; + width: 100%; + pointer-events: initial; +} +.php-search-input { + border: 1px solid #999; + background-color: #fff; + opacity: .5; + box-shadow: 0 2px .5em #000a, inset 0 1px 2px #000a; + transition: opacity .5s, box-shadow .5s; +} +.php-search-input:hover, +.php-search-input:focus, +.php-search-input:focus-within { + opacity: 1; + box-shadow: 0 2px 1em #000, inset 0 1px 2px #000a; +} +.php-search-input input { + border-width: 0; + background-color: transparent; + color: #000; + font-family: Tahoma, Geneva, 'Dejavu Sans', Arial, Helvetica, sans-serif; + font-size: 24px; + line-height: 35px; + width: 100%; + height: 100%; + padding: 2px 5px; +} +.php-time { + display: flex; + justify-content: center; + align-items: center; + margin: 20px auto; + width: 100%; +} +.php-time-analog { + flex: 0 0 auto; +} +.php-time-alter { + flex: 0 0 auto; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + width: 300px; + font-family: 'Electrolize', Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; + filter: drop-shadow(0 1px 5px #000); + color: transparent; +} +.php-time-digital { + font-size: 7em; + line-height: 1.5em; + display: flex; + flex: 0 0 auto; + max-width: 300px; + width: 100%; + align-items: center; + justify-content: center; +} +.php-time-date { + font-size: 2em; +} +.php-time-digital-separator { + margin-top: -4px; +} +.php-time-digital-separator-hidden { + visibility: hidden; +} +.php-time-digital-hours, +.php-time-digital-separator, +.php-date-label { + background-image: linear-gradient(180deg, #eee 0%, #ddd 50%, #ccc 50%, #aaa 100%); + background-clip: text; + -webkit-background-clip: text; +} +.php-time-digital-minutes, +.php-date-week, +.php-date-day, +.php-date-month, +.php-date-year { + background-image: linear-gradient(0deg, #281430 0%, #392540 50%, #4a3650 50%, #6c5871 100%); + background-clip: text; + -webkit-background-clip: text; +} + +.clock { + width: 200px; + height: 200px; + border-radius: 100%; + border: 4px solid #c0c0c0; + overflow: hidden; + box-sizing: content-box; + filter: drop-shadow(0 1px 5px #000); +} +.clock-background { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: #fffd; + filter: blur(20px); +} +.clock-center { + position: absolute; + top: 96px; + left: 96px; + width: 8px; + height: 8px; + background-color: #000; + border-radius: 100%; + z-index: 1000; +} +.clock-hand { + --hand-rotation: 0deg; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + transform: rotate(var(--hand-rotation)); + z-index: 900; +} +.clock-hand-display { + position: absolute; + width: 2px; + left: 99px; + height: 99px; + background-color: #000; +} +.clock-hand-hours { + z-index: 910; +} +.clock-hand-minutes { + z-index: 920; +} +.clock-hand-seconds { + z-index: 930; +} +.clock-hand-hours .clock-hand-display { + height: 70px; + margin-top: 30px; + filter: drop-shadow(0 1px 2px #000); +} +.clock-hand-minutes .clock-hand-display { + height: 80px; + margin-top: 20px; + filter: drop-shadow(0 1px 3px #000); +} +.clock-hand-seconds .clock-hand-display { + height: 100px; + margin-top: 15px; + opacity: .6; + background-color: #f00; + filter: drop-shadow(0 1px 4px #000); +} +.clock-number { + position: absolute; + top: 0; + left: 0; + z-index: 500; + width: 100px; + height: 100px; +} +.clock-number-display { + position: absolute; + width: 2px; + height: 10px; + background-color: #000; +} +.clock-number-9 .clock-number-display, +.clock-number-3 .clock-number-display { + width: 10px; + height: 2px; +} +.clock-number-6 .clock-number-display { + bottom: 0; +} +.clock-number-12 .clock-number-display { + right: 0; + height: 15px; +} +.clock-number-3 .clock-number-display { + right: 0; + bottom: 0; +} +.clock-number-12 { + left: 1px; +} +.clock-number-9 { + top: 99px; +} +.clock-number-6 { + top: 100px; + left: 99px; +} +.clock-number-3 { + top: 1px; + left: 100px; +} + +.ob-wrapper h1 { + font-family: Verdana, 'Dejavu Sans', Arial, Helvetica, sans-serif; + font-size: 20px; + line-height: 24px; +} + +.ob-wrapper { + padding-top: 1px; + max-width: 1000px; + margin: 0 auto; + display: flex; +} +@media(max-width: 700px) { + .ob-wrapper { flex-direction: column; } +} +.ob-blog { + margin: 10px; +} +.ob-blog * { + user-select: text; +} +.ob-blog h1 a { + color: #fff; + text-decoration: none; +} +.ob-blog p { + font-size: 14px; + line-height: 22px; +} +.ob-blog p a { + color: #55c; + text-decoration: none; +} +.ob-blog p a:hover, +.ob-blog p a:focus { + text-decoration: underline; +} +.ob-blog p a:active { + color: #c22; +} +.ob-blog time { + font-size: 11px; +} +.ob-blog.ob-preview p { + color: #ccc; + margin: .2em 0; + font-size: 12px; + line-height: 20px; +} +.ob-blogs .ob-blog:not(:last-child) { + margin-bottom: 1.5em; +} +.ob-blog .ob-continue { + display: block; + text-align: center; + color: #fff; + text-decoration: none; + padding: 5px; + margin: 3px 0; + background-color: #161616; + border-radius: 5px; + transition: background-color .2s; +} +.ob-blog .ob-continue:hover, +.ob-blog .ob-continue:focus { + background-color: #1f1f1f; +} +.ob-blog .ob-continue:active { + background-color: #191919; +} diff --git a/public/index.php b/public/index.php index 0743444..5532ff7 100644 --- a/public/index.php +++ b/public/index.php @@ -2,16 +2,16 @@ namespace Makai; use Index\DateTime; +use Index\Routing\Router; +use Index\Routing\RoutePathNotFoundException; +use Index\Routing\RouteMethodNotSupportedException; require_once __DIR__ . '/../makai.php'; -define('FM_HIT', 0x01000000); -define('FM_ERR', 0x02000000); - define('FM_NAV', [ ['title' => 'Home', 'link' => '/'], //['title' => 'Blog', 'link' => '//blog.flash.moe'], - ['title' => 'Blog', 'link' => '//flash.moe/2020/?blog=1'], + ['title' => 'Blog', 'link' => '/old-blog'], ['title' => 'Projects', 'link' => '/projects'], ['title' => 'Contact', 'link' => '/contact'], ['title' => 'Related', 'link' => '/related'], @@ -75,17 +75,23 @@ define('FM_ERRS' , [ define('FM_DYNLOAD', filter_input(INPUT_SERVER, 'HTTP_ACCEPT') === 'application/x-fdynload+json'); -function fm_component(string $_component_name, array $vars = []) { +function fm_component(string $_component_name, array $vars = []): string { if(FM_DYNLOAD) { global $fmComponentVars; if(!isset($fmComponentVars)) $fmComponentVars = []; $fmComponentVars[$_component_name] = $vars; - return; + return ''; } extract($vars); + + ob_start(); require __DIR__ . '/../components/' . $_component_name . '.php'; + $meow = ob_get_contents(); + ob_clean(); + + return $meow; } function first_paragraph(string $text, string $delimiter = "\n"): string { @@ -130,8 +136,6 @@ function cache_output(string $name, int $lifetime, callable $callable) { return unserialize(file_get_contents($path)); } -ob_start(); - $reqMethod = (string)filter_input(INPUT_SERVER, 'REQUEST_METHOD'); $reqPath = '/' . trim(parse_url((string)filter_input(INPUT_SERVER, 'REQUEST_URI'), PHP_URL_PATH), '/'); $reqHead = false; @@ -141,55 +145,86 @@ if($reqMethod == 'HEAD') { $reqHead = true; } -if(FM_DYNLOAD) - ob_start(); +$router = new Router; -if(substr($reqPath, 0, 7) === '/error/') { - $statusCode = intval(substr($reqPath, 7, 3)); -} else { - foreach(glob(__DIR__ . '/../pages/*.php') as $page) { - $result = include_once $page; - $statusCode = $result & 0xFFF; - if(($result & FM_HIT) === FM_HIT) { - if($statusCode >= 100 && $statusCode <= 999) - http_response_code($statusCode); - else - $statusCode = 200; - break; - } - if(($result & FM_ERR) === FM_ERR) - break; - } - - // Make sure we have a valid error code - if($statusCode < 100) - $statusCode = 404; -} - -if($statusCode >= 400 && $statusCode <= 599 && ob_get_length() < 1) { - $errorInfo = FM_ERRS[$statusCode ?? 404] ?? FM_ERRS[404]; +$router->get('/error/:code', function(string $code) use ($reqHead) { + $errorInfo = FM_ERRS[$code ?? 404] ?? FM_ERRS[404]; http_response_code($errorInfo['code']); if(!$reqHead) { - fm_component('header', ['title' => $errorInfo['title']]); -?> + $body = fm_component('header', ['title' => $errorInfo['title']]); + + $body .= << -

- <?=$errorInfo['image'];?> -
+

{$errorInfo['title']}

+ {$errorInfo['image']} +
{$errorInfo['desc']}
-resolve($reqMethod, $reqPath); +} catch(RoutePathNotFoundException $ex) { + $handlers = $router->resolve('GET', '/error/404'); +} catch(RouteMethodNotSupportedException $ex) { + $handlers = $router->resolve('GET', '/error/405'); +} + +$result = $handlers->run(); + +if(is_int($result)) + $result = $router->resolve('GET', "/error/{$result}")->run(); + +if($result !== null && !is_bool($result)) { + if(is_int($result)) { + http_response_code($result); + header('Content-Type: text/plain; charset=us-ascii'); + echo $result; + } elseif(is_array($result) + || $result instanceof \stdClass + || $result instanceof \JsonSerializable) { + http_response_code(200); + header('Content-Type: application/json; charset=utf-8'); + echo json_encode($result); + } else { + $result = (string)$result; + + if(strtolower(substr($result, 0, 14)) === ' $fmComponentVars ?? [], + 'raw_html' => $result, + ]); + } else { + header('Content-Type: text/plain; charset=us-ascii'); + echo $result; + } + } } } - -if(FM_DYNLOAD) { - $contents = ob_get_contents(); - ob_end_clean(); - - header('Content-Type: application/x-fdynload+json'); - echo json_encode([ - 'components' => $fmComponentVars ?? [], - 'raw_html' => $contents, - ]); -} diff --git a/public/old_blog_posts.json b/public/old_blog_posts.json new file mode 100644 index 0000000..de48c84 --- /dev/null +++ b/public/old_blog_posts.json @@ -0,0 +1 @@ +[{"post_id":8,"post_title":"So long, WordPress!","post_text":"Once again in my endless consistency, I've decided to get rid of WordPress in favour of a custom system. This is a continuation to stuff I described in the \"Personal\" website<\/a> post. I'm working on a redesign of the website, this is only a temporary slate to facilitate the removal of WordPress.\r\n\r\nAs mentioned in the linked post, major old links will still work for a while. Comments have also been backported even though there are few, there's just nothing to display them yet since this current layout is only temporary.","post_published":1550614196,"post_new_url":"https://flash.moe"},{"post_id":4,"post_title":"\"Personal\" website","post_text":"This is going to be a short post, but it's better than nothing (oops).\r\n\r\nEarlier this week I was thinking back on the earlier days of Flashii, back when that qualified as my personal site, and how I'd do something new with it at least every other week. This also made me realise that flash.moe has been stagnant for nearly almost over two years even though there's a number of projects that I could be posting stuff about.\r\n\r\nThis is not intended as an announcement or anything, but I might bring out a redesign sometime soon. I don't think I'll open source it since I might work with basic PHP\/HTML files and stuff since there really isn't a need for informational personal websites to run on elaborate MVVM\/MVC frameworks and designs. Especially if it means they barely get updated since updating the design accordingly is a pain.\r\n\r\nIf I do end up pushing this through I'll make sure that old links will remain working.\r\n","post_published":1517761750,"post_new_url":null},{"post_id":3,"post_title":"Make backups, kids","post_text":"So yesterday I had that biggest mindfart ever, while doing some maintenance to Testii, Flashii's public test preview, I deleted every file in the www folder.\r\n\r\nNow you're probably wondering; \u201cHow does that even happen?\u201d Well I wanted to purge the uploads folder so I did a rm -rf *, this was my first mistake since I should\u2019ve gone up a directory and rm -rf'd just the uploads dir or left the r flag out since the uploads dir doesn\u2019t have subs had it not been for the r flag only a single experimental file would\u2019ve been lost. But that still doesn\u2019t explain how I managed to delete everything which is where the real kicker comes in, I absentmindedly entered cd .. twice before executing the command... Yeah it just keeps getting better...\r\n\r\nI could blame rm for not having a confirmation, especially when using the r and f flags, but the fact of the matter is that I just fucked up and I\u2019ve accepted that. I should\u2019ve made backups and I should\u2019ve been more cautious before using a command that forcibly and recursively destroys everything in its path...\r\n\r\nAnyway, I guess I\u2019ll lay out the losses also. All databases were unaffected by all this so stuff I kept on git was back up within a 10-minute timeframe. A lot of archived stuff was lost, none of it was really important but it\u2019s mainly just sad that it\u2019s gone now. data.flashii.net is pretty much entirely gone which is kind of inconvenient since it was still used for some assets but fixing that is as easy as updating references to assets.flashii.net. I\u2019m also taking this opportunity to kill oniichan, since like everything on it is gone anyway, in favour of an archive section on flash.moe, despite the domain being really cute it costs me nearly \u20ac20 a year and it literally goes towards nothing.\r\n\r\nSo yeah, the morals of this story; don\u2019t use flags you don\u2019t actually need on commands and make backups... or just don\u2019t be me, that should work too.\r\n","post_published":1480955714,"post_new_url":null},{"post_id":2,"post_title":"Electron","post_text":"Something that's become popular in the past few years is Electron. If you don't know what Electron is it basically allows you to wrap a web application in a Chrome based window and have it run \"natively\" on Windows, Mac OS X and GNU\/Linux distributions, if you want to know more go read up on it yourself<\/a>.\r\n\r\nNow you're probably wondering where I'm going with this. Well I think this is by far one of the worst things to have happened to systems development, despite being marketed as native it really should be \"native\". Yes it wraps around to give you access to native APIs such as Windows' notification system and such but at heart all Electron is is a Google Chrome window without a navigation bar, you could literally just launch chrome with the --app directive<\/abbr>.\r\n\r\nI can kind of get why you'd want to use HTML and CSS to make UIs for desktop programs (believe me, XAML is cute but can be a bitch to debug), it's relatively easy to work with, does a lot for you and doesn't overcomplicate. Despite that you should be challenging yourself by learning something new instead of just shoving stuff you do already into an environment it shouldn't be applied in. A new challenge and the knowledge that comes with it is never bad!\r\n\r\nNot to mention the ridiculous amount of memory you'd need to run multiple Electron based applications at once, people are using the excuse of 'People have a lot of memory nowadays anyway' as an excuse to not memory manage their software properly which by far is the most dumb thing of all. Just because there's a lot of space doesn't mean all of it should be used, you need space to move around as well. I used to frequent a Slack organisation but basically just gave up on going to it because the application made my computer completely kill itself, and my computer is pretty decent<\/a>. Considering that writing this same program in C#, or any compiled language really, it would run a million times better I just said 'fuck it' and stopped bothering. Now lets get the --app directive of Chrome back into this, if you'd use this instead of the \"native\" electron application and conserve memory space by using the already active instance(s) as far as that's possible with Chrome anyway<\/a>.\r\n\r\nNow to hop onto a more controversial related side topic. The current situation with Chrome's dominance feels a lot to me like the situation with Internet Explorer back in the 90s, granted I wasn't actually there for that but you can make a few links to this and ActiveX which was finally fully deprecated and removed with the dawn of Microsoft Edge. Of course Electron isn't half as retarded as ActiveX was but certain links are there. That's how I feel about it anyway, feel free to call me retarded in the comments.\r\n\r\nWith that you can probably interchange Electron with Node.js for most of this post although Node is a tad less ridiculous, still would never seriously use either though which means my resources wouldn't be the ones being wasted in the end. I still don't really get why people would voluntarily choose to write Javascript when they have the countless other options.\r\n\r\nAnd alternatively you could just use your browser because most people have one open all the time regardless...\r\n","post_published":1463335056,"post_new_url":null}] diff --git a/src/LanguageInfo.php b/src/LanguageInfo.php new file mode 100644 index 0000000..eeefc3a --- /dev/null +++ b/src/LanguageInfo.php @@ -0,0 +1,37 @@ +id = $id; + $this->name = $name; + $this->colour = $colour; + } + + public function getId(): AString { + return $this->id; + } + + public function getName(): WString { + return $this->name; + } + + public function hasColour(): bool { + return $this->colour !== null; + } + + public function getColour(): ?int { + return $this->colour; + } +} diff --git a/src/Languages.php b/src/Languages.php new file mode 100644 index 0000000..34b7409 --- /dev/null +++ b/src/Languages.php @@ -0,0 +1,53 @@ +conn = $conn; + } + + public function getByProject(ProjectInfo $project): array { + $stmt = $this->conn->prepare(self::QUERY_PROJECT); + $stmt->addParameter(1, $project->getId(), DbType::STRING); + $stmt->execute(); + $result = $stmt->getResult(); + $objs = []; + + while($result->next()) + $objs[] = self::createObject($result); + + return $objs; + } + + public function getProjectColour(ProjectInfo $project): ?int { + $stmt = $this->conn->prepare(self::QUERY_PROJECT_COLOUR); + $stmt->addParameter(1, $project->getId(), DbType::STRING); + $stmt->execute(); + $result = $stmt->getResult(); + + if(!$result->next()) + return null; + + return $result->getInteger(0); + } + + private static function createObject(IDatabaseResult $result): LanguageInfo { + return new LanguageInfo( + $result->getAString(0), // id + $result->getWString(1, 'utf-8'), // name + $result->isNull(2) ? null : $result->getInteger(2) // colour + ); + } +} diff --git a/src/ProjectInfo.php b/src/ProjectInfo.php new file mode 100644 index 0000000..d1dfa9d --- /dev/null +++ b/src/ProjectInfo.php @@ -0,0 +1,144 @@ +id = $id; + $this->name = $name; + $this->nameClean = $nameClean; + $this->summary = $summary; + $this->description = $description; + $this->featured = $featured; + $this->order = $order; + $this->colour = $colour; + $this->type = $type; + $this->homepage = $homepage; + $this->source = $source; + $this->discussion = $discussion; + $this->createdAt = DateTime::fromUnixTimeSeconds($createdAt); + $this->archivedAt = $archivedAt === null ? null : DateTime::fromUnixTimeSeconds($archivedAt); + $this->deletedAt = $deletedAt === null ? null : DateTime::fromUnixTimeSeconds($deletedAt); + } + + public function getId(): string { + return $this->id; + } + + public function getName(): WString { + return $this->name; + } + + public function getCleanName(): AString { + return $this->nameClean; + } + + public function hasSummary(): bool { + return $this->summary !== null; + } + + public function getSummary(): ?WString { + return $this->summary; + } + + public function hasDescription(): bool { + return $this->description !== null; + } + + public function getDescription(): ?WString { + return $this->description; + } + + public function isFeatured(): bool { + return $this->featured; + } + + public function getOrder(): int { + return $this->order; + } + + public function hasColour(): bool { + return $this->colour !== null; + } + + public function getColour(): ?int { + return $this->colour; + } + + public function getProjectType(): string { + return $this->type; + } + + public function isTool(): bool { + return $this->getProjectType() === 'Tool'; + } + + public function hasHomePageUrl(): bool { + return $this->homepage !== null; + } + + public function getHomePageUrl(): ?AString { + return $this->homepage; + } + + public function hasSourceUrl(): bool { + return $this->source !== null; + } + + public function getSourceUrl(): ?AString { + return $this->source; + } + + public function hasDiscussionUrl(): bool { + return $this->discussion !== null; + } + + public function getDiscussionUrl(): ?AString { + return $this->discussion; + } + + public function getCreatedAt(): DateTime { + return $this->createdAt; + } + + public function getArchivedAt(): ?DateTime { + return $this->archivedAt; + } + + public function isArchived(): bool { + return $this->archivedAt !== null; + } + + public function getDeletedAt(): ?DateTime { + return $this->deletedAt; + } + + public function isDeleted(): bool { + return $this->deletedAt !== null; + } +} diff --git a/src/Projects.php b/src/Projects.php new file mode 100644 index 0000000..a89fa78 --- /dev/null +++ b/src/Projects.php @@ -0,0 +1,62 @@ + 0 ORDER BY RAND() LIMIT 3'; + + private IDatabaseConnection $conn; + + public function __construct(IDatabaseConnection $conn) { + $this->conn = $conn; + } + + public function getAll(): array { + $stmt = $this->conn->prepare(self::QUERY_ALL); + $stmt->execute(); + $result = $stmt->getResult(); + $objs = []; + + while($result->next()) + $objs[] = self::createObject($result); + + return $objs; + } + + public function getFeatured(): array { + $stmt = $this->conn->prepare(self::QUERY_FEATURED); + $stmt->execute(); + $result = $stmt->getResult(); + $objs = []; + + while($result->next()) + $objs[] = self::createObject($result); + + return $objs; + } + + private static function createObject(IDatabaseResult $result): ProjectInfo { + return new ProjectInfo( + $result->getString(0), // id + $result->getWString(1, 'utf-8'), // name + $result->getAString(2), // clean name + $result->isNull(3) ? null : $result->getWString(3, 'utf-8'), // summary + $result->isNull(4) ? null : $result->getWString(4, 'utf-8'), // description + $result->getInteger(5) !== 0, // featured + $result->getInteger(6), // order + $result->isNull(13) ? null : $result->getInteger(13), // colour + $result->getString(11), // type + $result->isNull(7) ? null : $result->getAString(7), // homepage + $result->isNull(8) ? null : $result->getAString(8), // repository + $result->isNull(9) ? null : $result->getAString(9), // forum + $result->getInteger(12), // created + $result->isNull(10) ? null : $result->getInteger(10), // archived + null // deleted + ); + } +}