import
This commit is contained in:
commit
1405d65126
15 changed files with 1850 additions and 0 deletions
18
components/footer.php
Normal file
18
components/footer.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
$quotes = isset($quotes) && is_array($quotes) ? $quotes : FM_FEET;
|
||||
$baseUrl = empty($external) ? '' : '//' . $_SERVER['HTTP_HOST'];
|
||||
|
||||
if(empty($is_index)):
|
||||
?>
|
||||
</div>
|
||||
<?php endif; if(empty($hide)): ?>
|
||||
<div class="footer">
|
||||
<div class="footer-text">© flashwave 2010-<?=date('Y');?> - <?=($quotes[array_rand($quotes)]);?></div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<script src="<?=$baseUrl;?>/assets/2020v2.js" charset="utf-8" type="text/javascript"></script>
|
||||
<?php if(isset($scripts) && is_array($scripts)) foreach($scripts as $script): ?>
|
||||
<script src="<?=$script;?>" charset="utf-8" type="text/javascript"></script>
|
||||
<?php endforeach;?>
|
||||
</body>
|
||||
</html>
|
52
components/header.php
Normal file
52
components/header.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
$menu = isset($menu) && is_array($menu) ? $menu : FM_NAV;
|
||||
$backgrounds = isset($backgrounds) && is_array($backgrounds) ? $backgrounds : FM_BGS;
|
||||
$baseUrl = empty($external) ? '' : '//' . $_SERVER['HTTP_HOST'];
|
||||
?>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title><?=($title ?? 'flash.moe');?></title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="<?=$baseUrl;?>/assets/2020v2.css" type="text/css" rel="stylesheet"/>
|
||||
<link href="//flash.moe/css/electrolize/style.css" type="text/css" rel="stylesheet"/>
|
||||
<?php if(isset($styles) && is_array($styles)) foreach($styles as $style): ?>
|
||||
<link href="<?=$style;?>" type="text/css" rel="stylesheet"/>
|
||||
<?php endforeach;?>
|
||||
</head>
|
||||
<body class="<?php if(isset($is_index)) { echo 'index';} if(isset($do_fullscreen_header)) { echo 'fullscreen-header';} ?>">
|
||||
<div class="header">
|
||||
<div class="header-background">
|
||||
<img src="<?=($backgrounds[array_rand($backgrounds)]);?>" alt=""/>
|
||||
</div>
|
||||
<div class="header-foreground"<?php if(isset($offset) && $offset > 0) echo " style=\"padding-bottom: {$offset}px\""; ?>>
|
||||
<a class="header-logo" href="/">
|
||||
<div class="header-flash">flash</div>
|
||||
<div class="header-wave">wave</div>
|
||||
</a>
|
||||
<?php if(!empty($is_index) || !empty($do_fullscreen_header)): ?>
|
||||
<div class="header-now-playing header-now-playing-hidden">
|
||||
<div class="header-now-playing-icon" title="Now listening to...">
|
||||
<a href="/now-listening"><img src="https://mikoto.misaka.nl/i/1594148841-7yAS44p.png" alt=""/></a>
|
||||
</div>
|
||||
<div class="header-now-playing-cover">
|
||||
<img src="//now.flash.moe/resources/no-cover.png" alt=""/>
|
||||
</div>
|
||||
<div class="header-now-playing-details">
|
||||
<div class="header-now-playing-title"><a href="#" target="_blank" rel="noopener"></a></div>
|
||||
<div class="header-now-playing-artist"><a href="#" target="_blank" rel="noopener"></a></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="header-menu">
|
||||
<?php foreach($menu as $link): ?>
|
||||
<a href="<?=$link['link'];?>"><?=$link['title'];?></a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php if(empty($is_index) && empty($do_fullscreen_header)): ?>
|
||||
<div class="container">
|
||||
<?php endif; ?>
|
28
lib/fmcolour.php
Normal file
28
lib/fmcolour.php
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
class FmColour {
|
||||
private $raw = null;
|
||||
|
||||
public function __construct(?int $raw) {
|
||||
$this->raw = $raw;
|
||||
}
|
||||
|
||||
public function hasColour(): bool {
|
||||
return $this->raw !== null;
|
||||
}
|
||||
|
||||
public function getRaw(): int {
|
||||
return $this->raw ?? 0;
|
||||
}
|
||||
|
||||
public function getRed(): int { return ($this->getRaw() >> 16) & 0xFF; }
|
||||
public function getGreen(): int { return ($this->getRaw() >> 8) & 0xFF; }
|
||||
public function getBlue(): int { return $this->getRaw() & 0xFF; }
|
||||
|
||||
public function getHex(): string { return str_pad(dechex($this->getRaw()), 6, '0'); }
|
||||
|
||||
public function getCSS(): string {
|
||||
if(!$this->hasColour())
|
||||
return 'inherit';
|
||||
return '#' . $this->getHex();
|
||||
}
|
||||
}
|
14
lib/fmlanguage.php
Normal file
14
lib/fmlanguage.php
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
class FmLanguage {
|
||||
private $language_id = -1;
|
||||
private $language_name = '';
|
||||
private $language_colour = null;
|
||||
|
||||
public static function fromStdClass(stdClass $lang) {
|
||||
$langRaw = (array)$lang;
|
||||
$lang = new static;
|
||||
foreach($langRaw as $prop => $val)
|
||||
$lang->{$prop} = $val;
|
||||
return $lang;
|
||||
}
|
||||
}
|
170
lib/fmproject.php
Normal file
170
lib/fmproject.php
Normal file
|
@ -0,0 +1,170 @@
|
|||
<?php
|
||||
class FmProject {
|
||||
public const TYPE_PROJECT = 'Project';
|
||||
public const TYPE_TOOL = 'Tool';
|
||||
|
||||
private $project_id = -1;
|
||||
private $project_name = '';
|
||||
private $project_name_clean = null;
|
||||
private $project_summary = null;
|
||||
private $project_description = null;
|
||||
private $project_order = 0;
|
||||
private $project_type = self::TYPE_PROJECT;
|
||||
private $project_featured = 0;
|
||||
private $project_colour = null;
|
||||
private $project_homepage = null;
|
||||
private $project_repository = null;
|
||||
private $project_forum = null;
|
||||
private $project_created = null;
|
||||
private $project_deleted = null;
|
||||
private $project_archived = null;
|
||||
|
||||
private $languages = null;
|
||||
|
||||
public function getId(): int {
|
||||
return $this->project_id < 1 ? -1 : $this->project_id;
|
||||
}
|
||||
|
||||
public function getName(): string {
|
||||
return $this->project_name;
|
||||
}
|
||||
public function getCleanName(): string {
|
||||
return $this->project_name_clean ?? str_replace(' ', '-', trim(strtolower($this->getName())));
|
||||
}
|
||||
|
||||
public function getSummary(): string {
|
||||
return $this->project_summary ?? '';
|
||||
}
|
||||
public function getDescription(): string {
|
||||
return $this->project_description ?? '';
|
||||
}
|
||||
public function getDescriptionLines(): array {
|
||||
$linesRaw = array_reverse(explode("\n", $this->getDescription()));
|
||||
$lines = [];
|
||||
while(($line = array_pop($linesRaw)) !== false) {
|
||||
$line = trim($line);
|
||||
if(!empty($line))
|
||||
$lines[] = $line;
|
||||
}
|
||||
return $lines;
|
||||
}
|
||||
|
||||
public function getOrder(): int {
|
||||
return $this->project_order;
|
||||
}
|
||||
public function getType(): string {
|
||||
return $this->project_type;
|
||||
}
|
||||
public function isProject(): bool {
|
||||
return $this->getType() === self::TYPE_PROJECT;
|
||||
}
|
||||
public function isTool(): bool {
|
||||
return $this->getType() === self::TYPE_TOOL;
|
||||
}
|
||||
|
||||
public function isFeatured(): bool {
|
||||
return boolval($this->project_featured);
|
||||
}
|
||||
|
||||
// TODO: INHERIT LANGUAGE COLOUR
|
||||
public function hasColour(): bool {
|
||||
return $this->project_colour !== null;
|
||||
}
|
||||
public function getColour(): FmColour {
|
||||
return new FmColour($this->project_colour);
|
||||
}
|
||||
|
||||
public function hasHomepage(): bool {
|
||||
return !empty($this->project_homepage);
|
||||
}
|
||||
public function getHomepage(): string {
|
||||
return $this->project_homepage ?? '';
|
||||
}
|
||||
|
||||
public function hasRepository(): bool {
|
||||
return !empty($this->project_repository);
|
||||
}
|
||||
public function getRepository(): string {
|
||||
return $this->project_repository ?? '';
|
||||
}
|
||||
|
||||
public function hasForum(): bool {
|
||||
return !empty($this->project_forum);
|
||||
}
|
||||
public function getForum(): string {
|
||||
return $this->project_forum ?? '';
|
||||
}
|
||||
|
||||
public function getCreatedTime(): DateTime {
|
||||
return new DateTime('@' . ($this->project_created ?? 0));
|
||||
}
|
||||
|
||||
public function isDeleted(): bool {
|
||||
return $this->project_deleted !== null;
|
||||
}
|
||||
public function getDeletedTime(): DateTime {
|
||||
return new DateTime('@' . ($this->project_deleted ?? 0));
|
||||
}
|
||||
|
||||
public function isArchived(): bool {
|
||||
return $this->project_archived !== null;
|
||||
}
|
||||
public function getArchivedTime(): DateTime {
|
||||
return new DateTime('@' . ($this->project_archived ?? 0));
|
||||
}
|
||||
|
||||
public function getLinks(): array {
|
||||
$links = [];
|
||||
if($this->hasHomepage())
|
||||
$links[] = FmProjectLink::create('homepage', 'Homepage', $this->getHomepage());
|
||||
if($this->hasRepository())
|
||||
$links[] = FmProjectLink::create('repository', 'Source', $this->getRepository());
|
||||
if($this->hasForum())
|
||||
$links[] = FmProjectLink::create('forum', 'Discussion', $this->getForum());
|
||||
return $links;
|
||||
}
|
||||
|
||||
public function getLanguages(): array {
|
||||
return $this->languages ?? [];
|
||||
}
|
||||
|
||||
private function transformLanguages(): void {
|
||||
if(empty($this->languages))
|
||||
return;
|
||||
foreach($this->languages as &$lang)
|
||||
if($lang instanceof stdClass)
|
||||
$lang = FmLanguage::fromStdClass($lang);
|
||||
}
|
||||
|
||||
public static function byFeatured(): array {
|
||||
return cache_output('projects-featured-noconvert', 300, function() {
|
||||
$projects = json_decode(file_get_contents('https://flash.moe/2020/projects.php?dump_that_shit&noconvert&rf'));
|
||||
|
||||
foreach($projects as &$project) {
|
||||
$projRaw = (array)$project;
|
||||
$project = new static;
|
||||
foreach($projRaw as $prop => $val)
|
||||
$project->{$prop} = $val;
|
||||
$project->transformLanguages();
|
||||
}
|
||||
|
||||
return $projects;
|
||||
});
|
||||
}
|
||||
|
||||
public static function all(): array {
|
||||
return cache_output('projects-noconvert', 300, function() {
|
||||
$projects = json_decode(file_get_contents('https://flash.moe/2020/projects.php?dump_that_shit&noconvert'));
|
||||
|
||||
foreach($projects as &$project) {
|
||||
$projRaw = (array)$project;
|
||||
$project = new static;
|
||||
foreach($projRaw as $prop => $val)
|
||||
$project->{$prop} = $val;
|
||||
$project->transformLanguages();
|
||||
}
|
||||
|
||||
return $projects;
|
||||
});
|
||||
}
|
||||
}
|
24
lib/fmprojectlink.php
Normal file
24
lib/fmprojectlink.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
class FmProjectLink {
|
||||
private $className;
|
||||
private $text;
|
||||
private $url;
|
||||
|
||||
public function getClass(): string {
|
||||
return $this->className;
|
||||
}
|
||||
public function getText(): string {
|
||||
return $this->text;
|
||||
}
|
||||
public function getUrl(): string {
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
public static function create(string $class, string $text, string $url): self {
|
||||
$link = new static;
|
||||
$link->className = $class;
|
||||
$this->text = $text;
|
||||
$this->url = $url;
|
||||
return $link;
|
||||
}
|
||||
}
|
42
pages/blog.php
Normal file
42
pages/blog.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
if(preg_match('#^/blog/([0-9]+)$#', $reqPath, $matches) || $reqPath === '/blog.php' || $reqPath === '/blog-post.php') {
|
||||
if($reqMethod !== 'GET')
|
||||
return FM_ERR | 405;
|
||||
header('Location: //flash.moe/2020/blog.php?p=' . ($matches[1] ?? filter_input(INPUT_GET, 'p', FILTER_SANITIZE_NUMBER_INT) ?? ''));
|
||||
return FM_HIT | 302;
|
||||
}
|
||||
|
||||
if($reqPath === '/blog-elements.json') {
|
||||
if($reqMethod !== 'GET')
|
||||
return FM_ERR | 405;
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
$title = filter_input(INPUT_GET, 'title', FILTER_SANITIZE_STRING);
|
||||
|
||||
ob_start();
|
||||
fm_component('header', [
|
||||
'title' => $title ?? '%title%',
|
||||
'external' => true,
|
||||
'styles' => [
|
||||
'//blog.flash.moe/assets/blog.css',
|
||||
],
|
||||
]);
|
||||
$header = ob_get_contents();
|
||||
ob_clean();
|
||||
fm_component('footer', [
|
||||
'external' => true,
|
||||
'scripts' => [
|
||||
'//blog.flash.moe/assets/blog.js',
|
||||
],
|
||||
]);
|
||||
$footer = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
echo json_encode([
|
||||
'header' => $header,
|
||||
'footer' => $footer,
|
||||
]);
|
||||
|
||||
return FM_HIT;
|
||||
}
|
193
pages/contact.php
Normal file
193
pages/contact.php
Normal file
|
@ -0,0 +1,193 @@
|
|||
<?php
|
||||
if($reqPath === '/contact.php' || $reqPath === '/contact.html') {
|
||||
if($reqMethod !== 'GET')
|
||||
return FM_ERR | 405;
|
||||
header('Location: /contact');
|
||||
return FM_HIT | 302;
|
||||
}
|
||||
|
||||
if($reqPath === '/nintendo') {
|
||||
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');
|
||||
|
||||
$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;
|
||||
|
||||
$contact = [
|
||||
[
|
||||
'id' => 'contact',
|
||||
'title' => 'Direct Contact',
|
||||
'items' => [
|
||||
[
|
||||
'id' => 'email',
|
||||
'name' => 'E-mail',
|
||||
'display' => 'me@flash.moe',
|
||||
'link' => 'mailto:me+contact@flash.moe',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
[
|
||||
'id' => 'communities',
|
||||
'title' => 'Communities & Social Media',
|
||||
'items' => [
|
||||
[
|
||||
'id' => 'flashii',
|
||||
'name' => 'Flashii',
|
||||
'display' => 'flash',
|
||||
'link' => '//flashii.net/profile.php?u=1',
|
||||
],
|
||||
[
|
||||
'id' => 'twitter',
|
||||
'name' => 'Twitter',
|
||||
'display' => '@smugwave',
|
||||
'link' => '//twitter.com/smugwave',
|
||||
],
|
||||
[
|
||||
'id' => 'youtube',
|
||||
'name' => 'YouTube',
|
||||
'display' => 'flashwave',
|
||||
'link' => '//youtube.com/c/flashwave',
|
||||
],
|
||||
[
|
||||
'id' => 'github',
|
||||
'name' => 'Github',
|
||||
'display' => 'flashwave',
|
||||
'link' => '//github.com/flashwave',
|
||||
],
|
||||
[
|
||||
'id' => 'twitch',
|
||||
'name' => 'Twitch.tv',
|
||||
'display' => 'flashwave0',
|
||||
'link' => '//twitch.tv/flashwave0',
|
||||
],
|
||||
[
|
||||
'id' => 'lastfm',
|
||||
'name' => 'Last.fm',
|
||||
'display' => 'flashwave_',
|
||||
'link' => '//last.fm/user/flashwave_',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
[
|
||||
'id' => 'gaming',
|
||||
'title' => 'Gaming',
|
||||
'items' => [
|
||||
[
|
||||
'id' => 'steam',
|
||||
'name' => 'Steam',
|
||||
'display' => 'flashwave_',
|
||||
'link' => '//steamcommunity.com/id/flashwave_',
|
||||
],
|
||||
[
|
||||
'id' => 'nin-sw',
|
||||
'name' => 'Nintendo Switch',
|
||||
'display' => 'SW-7446-8163-4902',
|
||||
],
|
||||
[
|
||||
'id' => 'nin-3ds',
|
||||
'name' => 'Nintendo 3DS',
|
||||
'display' => '4013-0352-0648',
|
||||
],
|
||||
[
|
||||
'id' => 'nin-wiiu',
|
||||
'name' => 'Wii U / NNID',
|
||||
'display' => 'flashwave0',
|
||||
],
|
||||
[
|
||||
'id' => 'osu',
|
||||
'name' => 'osu!',
|
||||
'display' => 'flash',
|
||||
'link' => '//osu.ppy.sh/u/flash',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
[
|
||||
'id' => 'support',
|
||||
'title' => 'Support me',
|
||||
'items' => [
|
||||
[
|
||||
'id' => 'paypal',
|
||||
'name' => 'Paypal Donation',
|
||||
'display' => 'flashwave',
|
||||
'link' => '//paypal.me/flashwave',
|
||||
],
|
||||
[
|
||||
'id' => 'patreon',
|
||||
'name' => 'Patreon',
|
||||
'display' => 'flashwave',
|
||||
'link' => '//patreon.com/flashwave',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
fm_component('header', [
|
||||
'title' => 'flash.moe / contact',
|
||||
]);
|
||||
|
||||
foreach($contact as $section) {
|
||||
?>
|
||||
<div class="section" id="section-<?=$section;?>">
|
||||
<div class="section-content">
|
||||
<div class="section-background"></div>
|
||||
<h1><?=$section['title'];?></h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="socials">
|
||||
<?php foreach($section['items'] as $social): ?>
|
||||
<div class="social social-<?=$social['id'];?>">
|
||||
<?php if(isset($social['link'])): ?>
|
||||
<a href="<?=$social['link'];?>" class="social-background" target="_blank" rel="noopener"></a>
|
||||
<?php else: ?>
|
||||
<div class="social-background" onclick="fm.selectTextInElement(this.parentNode.querySelector('.social-handle')); fm.copySelectedText();"></div>
|
||||
<?php endif; ?>
|
||||
<div class="social-icon"></div>
|
||||
<div class="social-content">
|
||||
<div class="social-name"><?=$social['name'];?></div>
|
||||
<div class="social-handle"><?=$social['display'];?></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
fm_component('footer');
|
||||
|
||||
return FM_HIT;
|
||||
}
|
59
pages/etcetera.php
Normal file
59
pages/etcetera.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
if($reqPath === '/etc.php' || $reqPath === 'etc.html'
|
||||
|| $reqPath === '/etcetera' || $reqPath === '/etcetera.html' || $reqPath === '/etcetera.php'
|
||||
|| $reqPath === '/misc' || $reqPath === '/misc.html' || $reqPath === '/misc.php') {
|
||||
if($reqMethod !== 'GET')
|
||||
return FM_ERR | 405;
|
||||
header('Location: /etc');
|
||||
return FM_HIT | 302;
|
||||
}
|
||||
|
||||
if($reqPath === '/etc') {
|
||||
$links = [
|
||||
[
|
||||
'title' => 'ASCII Table',
|
||||
'desc' => 'ASCII character set table with minor filtering functionality.',
|
||||
'link' => '//flash.moe/ascii.php',
|
||||
],
|
||||
[
|
||||
'title' => 'MIDI Collection',
|
||||
'desc' => 'My collection of MIDI and SF2 files.',
|
||||
'link' => '//flash.moe/midis',
|
||||
],
|
||||
[
|
||||
'title' => 'Randomised Keys',
|
||||
'desc' => 'Generates a number of random keys at various lengths using a secure random number generator.',
|
||||
'link' => '//flash.moe/key.php',
|
||||
],
|
||||
[
|
||||
'title' => 'The Abyss',
|
||||
'desc' => 'Accumulated collection of historical trash. Beware that the things here are unaltered products of their time.',
|
||||
'link' => '//abyss.flash.moe',
|
||||
],
|
||||
];
|
||||
|
||||
fm_component('header', [
|
||||
'title' => 'flash.moe / etcetera',
|
||||
]);
|
||||
?>
|
||||
<div class="section">
|
||||
<div class="section-content">
|
||||
<div class="section-background"></div>
|
||||
<h1>Etcetera</h1>
|
||||
</div>
|
||||
</div>
|
||||
<?php foreach($links as $link): ?>
|
||||
<div class="etcetera-item">
|
||||
<div class="etcetera-item-content">
|
||||
<a href="<?=$link['link'];?>" class="etcetera-item-link" rel="noopener" target="_blank"></a>
|
||||
<h2><?=$link['title'];?></h2>
|
||||
<p><?=$link['desc'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
endforeach;
|
||||
|
||||
fm_component('footer');
|
||||
|
||||
return FM_HIT;
|
||||
}
|
199
pages/index.php
Normal file
199
pages/index.php
Normal file
|
@ -0,0 +1,199 @@
|
|||
<?php
|
||||
if($reqPath === '/about' || $reqPath === '/about.html' || $reqPath === '/about.php'
|
||||
|| $reqPath === '/index.php' || $reqPath === '/index.html') {
|
||||
if($reqMethod !== 'GET')
|
||||
return FM_ERR | 405;
|
||||
header('Location: /');
|
||||
return FM_HIT | 302;
|
||||
}
|
||||
|
||||
if($reqPath === '/now-listening') {
|
||||
if($reqMethod !== 'GET')
|
||||
return FM_ERR | 405;
|
||||
|
||||
$offset = (int)filter_input(INPUT_GET, 'offset', FILTER_SANITIZE_NUMBER_INT);
|
||||
|
||||
fm_component('header', [
|
||||
'title' => 'flash.moe / now listening',
|
||||
'do_fullscreen_header' => true,
|
||||
'offset' => $offset,
|
||||
]);
|
||||
?>
|
||||
<script>window.addEventListener('load', function() { window.fm.initIndex(10); });</script>
|
||||
<?php
|
||||
fm_component('footer', [
|
||||
'hide' => true,
|
||||
]);
|
||||
|
||||
return FM_HIT;
|
||||
}
|
||||
|
||||
if($reqPath === '/test') {
|
||||
if($reqMethod !== 'GET')
|
||||
return FM_ERR | 405;
|
||||
|
||||
header('Content-Type: text/plain');
|
||||
|
||||
var_dump(FmProject::byFeatured());
|
||||
|
||||
return FM_HIT;
|
||||
}
|
||||
|
||||
if($reqPath === '/') {
|
||||
if($reqMethod !== 'GET')
|
||||
return FM_ERR | 405;
|
||||
|
||||
$legacyPage = filter_input(INPUT_GET, 'p', FILTER_SANITIZE_STRING);
|
||||
if(!empty($legacyPage)) {
|
||||
$legacyPages = [
|
||||
'projects' => '/projects',
|
||||
'contact' => '/contact',
|
||||
'about' => '/',
|
||||
'etc' => '/etc',
|
||||
'hosted' => '/etc',
|
||||
'friends' => '/related',
|
||||
];
|
||||
if(isset($legacyPages[$legacyPage])) {
|
||||
header('Location: ' . $legacyPages[$legacyPage]);
|
||||
return FM_HIT | 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'));
|
||||
});
|
||||
|
||||
shuffle($projInfo);
|
||||
|
||||
$contact = [
|
||||
[
|
||||
'id' => 'email',
|
||||
'name' => 'E-mail',
|
||||
'display' => 'me@flash.moe',
|
||||
'link' => 'mailto:me+contact@flash.moe',
|
||||
],
|
||||
[
|
||||
'id' => 'flashii',
|
||||
'name' => 'Flashii',
|
||||
'display' => 'flash',
|
||||
'link' => '//flashii.net/profile.php?u=1',
|
||||
],
|
||||
[
|
||||
'id' => 'twitter',
|
||||
'name' => 'Twitter',
|
||||
'display' => '@smugwave',
|
||||
'link' => '//twitter.com/smugwave',
|
||||
],
|
||||
[
|
||||
'id' => 'github',
|
||||
'name' => 'Github',
|
||||
'display' => 'flashwave',
|
||||
'link' => '//github.com/flashwave',
|
||||
],
|
||||
];
|
||||
|
||||
fm_component('header', [
|
||||
'title' => 'flash.moe',
|
||||
'is_index' => true,
|
||||
]);
|
||||
?>
|
||||
<div class="index-menu">
|
||||
<?php for($i = 1; $i < count(FM_NAV); ++$i): ?>
|
||||
<a href="<?=(FM_NAV[$i]['link']);?>"><?=(FM_NAV[$i]['title']);?></a>
|
||||
<?php endfor; ?>
|
||||
</div>
|
||||
<div class="index-featured">
|
||||
<div class="index-feature">
|
||||
<div class="index-feature-header">
|
||||
<a href="//blog.flash.moe" class="index-feature-header-link"></a>
|
||||
<div class="index-feature-header-title">Blog</div>
|
||||
<div class="index-feature-header-more">More</div>
|
||||
</div>
|
||||
<div class="index-blog">
|
||||
<?php $blogCount = 0; foreach($blogInfo as $blogPost): if($blogCount++ >= 5) break; ?>
|
||||
<div class="index-blog-post">
|
||||
<a href="//flash.moe/blog/<?=$blogPost->post_id;?>" class="index-blog-post-link"></a>
|
||||
<div class="index-blog-post-header">
|
||||
<div class="index-blog-post-title"><?=$blogPost->post_title;?></div>
|
||||
<div class="index-blog-post-published"><time datetime="<?=date('c', $blogPost->post_published);?>" title="<?=date('r', $blogPost->post_published);?>"><?=time_elapsed($blogPost->post_published);?></time></div>
|
||||
</div>
|
||||
<div class="index-blog-post-content">
|
||||
<?=first_paragraph($blogPost->post_text);?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="index-feature">
|
||||
<div class="index-feature-header">
|
||||
<a href="/projects" class="index-feature-header-link"></a>
|
||||
<div class="index-feature-header-title">Projects</div>
|
||||
<div class="index-feature-header-more">More</div>
|
||||
</div>
|
||||
<?php
|
||||
$projCount = 0;
|
||||
foreach($projInfo as $proj):
|
||||
if($projCount++ >= 3)
|
||||
break;
|
||||
|
||||
$links = [];
|
||||
if(isset($proj->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];
|
||||
?>
|
||||
<div class="index-project" style="background-color: #<?=str_pad(dechex($proj->colour), 6, '0', STR_PAD_LEFT);?>;">
|
||||
<a href="/projects#<?=$proj->name_clean;?>" class="index-project-anchor"></a>
|
||||
<div class="index-project-content">
|
||||
<div class="index-project-name"><?=$proj->name;?></div>
|
||||
<div class="index-project-summary"><?=$proj->summary;?></div>
|
||||
</div>
|
||||
<?php if(!empty($links)): ?>
|
||||
<div class="index-project-links">
|
||||
<?php foreach($links as $link): ?>
|
||||
<a class="index-project-link index-project-link-<?=$link['class'];?>" href="<?=$link['url'];?>" rel="noopener" target="_blank">
|
||||
<?=$link['text'];?>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<div class="index-feature">
|
||||
<div class="index-feature-header">
|
||||
<a href="/contact" class="index-feature-header-link"></a>
|
||||
<div class="index-feature-header-title">Contact</div>
|
||||
<div class="index-feature-header-more">More</div>
|
||||
</div>
|
||||
<div class="index-contact">
|
||||
<?php foreach($contact as $social): ?>
|
||||
<div class="social social-<?=$social['id'];?>">
|
||||
<?php if(isset($social['link'])): ?>
|
||||
<a href="<?=$social['link'];?>" class="social-background" target="_blank" rel="noopener"></a>
|
||||
<?php else: ?>
|
||||
<div class="social-background" onclick="fm.selectTextInElement(this.parentNode.querySelector('.social-handle')); fm.copySelectedText();"></div>
|
||||
<?php endif; ?>
|
||||
<div class="social-icon"></div>
|
||||
<div class="social-content">
|
||||
<div class="social-name"><?=$social['name'];?></div>
|
||||
<div class="social-handle"><?=$social['display'];?></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>window.addEventListener('load', function() { window.fm.initIndex(); });</script>
|
||||
<?php
|
||||
fm_component('footer', [
|
||||
'is_index' => true,
|
||||
]);
|
||||
|
||||
return FM_HIT;
|
||||
}
|
91
pages/projects.php
Normal file
91
pages/projects.php
Normal file
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
if($reqPath === '/projects.php' || $reqPath === '/projects.html') {
|
||||
if($reqMethod !== 'GET')
|
||||
return FM_ERR | 405;
|
||||
header('Location: /projects');
|
||||
return FM_HIT | 302;
|
||||
}
|
||||
|
||||
if($reqPath === '/projects') {
|
||||
if($reqMethod !== 'GET')
|
||||
return FM_ERR | 405;
|
||||
|
||||
$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' => [
|
||||
'title' => 'Active Projects',
|
||||
'desc' => 'Projects that I work on on a fairly regular basis.',
|
||||
],
|
||||
'tool' => [
|
||||
'title' => 'Tools',
|
||||
'desc' => 'Personal quality of life tools that I update when I need something new from them.',
|
||||
],
|
||||
'archive' => [
|
||||
'title' => 'Archived Projects',
|
||||
'desc' => 'Past projects that I no longer work on.',
|
||||
],
|
||||
];
|
||||
|
||||
foreach($projInfo as $section => $projects):
|
||||
$sectionInfo = $sectionInfos[$section];
|
||||
?>
|
||||
<div class="section" id="section-<?=$section;?>">
|
||||
<div class="section-content">
|
||||
<div class="section-background"></div>
|
||||
<h1><?=$sectionInfo['title'];?></h1>
|
||||
<p><?=$sectionInfo['desc'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
foreach($projects as $proj):
|
||||
$links = [];
|
||||
if(isset($proj->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];
|
||||
|
||||
$descLines = explode("\n", trim($proj->description ?? ''));
|
||||
?>
|
||||
<div class="project project-type-<?=$section;?>" id="<?=$proj->name_clean;?>" style="--project-colour: #<?=str_pad(dechex($proj->colour), 6, '0', STR_PAD_LEFT);?>;">
|
||||
<div class="project-content">
|
||||
<div class="project-details">
|
||||
<h2><?=$proj->name;?><div class="project-languages">
|
||||
<?php foreach($proj->languages as $lang): ?>
|
||||
<div class="project-language" style="--language-colour: #<?=str_pad(dechex($lang->colour), 6, '0', STR_PAD_LEFT);?>;">
|
||||
<?=$lang->name;?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div></h2>
|
||||
<p class="project-details-summary"><?=$proj->summary;?></p>
|
||||
<?php foreach($descLines as $line): if(empty($line)) continue; ?>
|
||||
<p><?=trim($line);?></p>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php if(!empty($links)): ?>
|
||||
<div class="project-links">
|
||||
<?php foreach($links as $link): ?>
|
||||
<a class="project-link project-link-<?=$link['class'];?>" href="<?=$link['url'];?>" rel="noopener" target="_blank">
|
||||
<?=$link['text'];?>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
endforeach;
|
||||
endforeach;
|
||||
|
||||
fm_component('footer');
|
||||
|
||||
return FM_HIT;
|
||||
}
|
44
pages/related.php
Normal file
44
pages/related.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
if($reqPath === '/related.php' || $reqPath === '/related.html'
|
||||
|| $reqPath === '/friends' || $reqPath === '/friends.html' || $reqPath === '/friends.php') {
|
||||
if($reqMethod !== 'GET')
|
||||
return FM_ERR | 405;
|
||||
header('Location: /related');
|
||||
return FM_HIT | 302;
|
||||
}
|
||||
|
||||
if($reqPath === '/related') {
|
||||
if($reqMethod !== 'GET')
|
||||
return FM_ERR | 405;
|
||||
|
||||
fm_component('header', [
|
||||
'title' => 'flash.moe / related',
|
||||
]);
|
||||
?>
|
||||
<div class="section">
|
||||
<div class="section-content">
|
||||
<div class="section-background"></div>
|
||||
<h1>Related Sites</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="related-sites">
|
||||
<div class="related-site">
|
||||
<a href="https://wart.host/" class="related-site-link"></a>
|
||||
<div class="related-site-image">
|
||||
<img src="//wart.host/Vintage/assets/cat.gif" alt=""/>
|
||||
</div>
|
||||
<div class="related-site-content">
|
||||
<div class="related-site-title">
|
||||
wart host
|
||||
</div>
|
||||
<div class="related-site-desc">
|
||||
screenshot palace of my momther
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
fm_component('footer');
|
||||
|
||||
return FM_HIT;
|
||||
}
|
676
public/assets/2020v2.css
Normal file
676
public/assets/2020v2.css
Normal file
|
@ -0,0 +1,676 @@
|
|||
* {
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
@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-menu {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.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 {}
|
||||
.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;
|
||||
background-size: 25px 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-icon { background-image: url('//flash.moe/assets/icons/twitter.png'); }
|
||||
.social-twitter .social-background { background-color: #1da1f2; }
|
||||
.social-youtube .social-icon { background-image: url('//flash.moe/assets/icons/youtube.png'); }
|
||||
.social-youtube .social-background { background-color: #ff0000; }
|
||||
.social-flashii .social-icon { background-image: url('//flash.moe/assets/icons/flashii.png'); }
|
||||
.social-flashii .social-background { background-color: #8559a5; }
|
||||
.social-github .social-icon { background-image: url('//flash.moe/assets/icons/github.png'); }
|
||||
.social-github .social-background { background-color: #222222; }
|
||||
.social-twitch .social-icon { background-image: url('//flash.moe/assets/icons/twitch.png'); }
|
||||
.social-twitch .social-background { background-color: #6441a4; }
|
||||
.social-steam .social-icon { background-image: url('//flash.moe/assets/icons/steam.png'); }
|
||||
.social-steam .social-background { background-color: #2c2e35; }
|
||||
.social-osu .social-icon { background-image: url('//flash.moe/assets/icons/steam.png'); }
|
||||
.social-osu .social-background { background-color: #ff66aa; }
|
||||
.social-email .social-icon { background-image: url('//flash.moe/assets/icons/email.png'); }
|
||||
.social-email .social-background { background-color: #4a3650; }
|
||||
.social-lastfm .social-icon { background-image: url('//flash.moe/assets/icons/lastfm.png'); }
|
||||
.social-lastfm .social-background { background-color: #ba0000; }
|
||||
.social-nin-sw .social-icon { background-image: url('//flash.moe/assets/icons/ninswitch.png'); }
|
||||
.social-nin-sw .social-background { background-color: #e60012; }
|
||||
.social-nin-3ds .social-icon { background-image: url('//flash.moe/assets/icons/ninswitch.png'); }
|
||||
.social-nin-3ds .social-background { background-color: #ce171f; }
|
||||
.social-nin-wiiu .social-icon { background-image: url('//flash.moe/assets/icons/ninswitch.png'); }
|
||||
.social-nin-wiiu .social-background { background-color: #00acca; }
|
||||
.social-paypal .social-icon { background-image: url('//flash.moe/assets/icons/paypal.png'); }
|
||||
.social-paypal .social-background { background-color: #009cde; }
|
||||
.social-patreon .social-icon { background-image: url('//flash.moe/assets/icons/paypal.png'); }
|
||||
.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;
|
||||
}
|
113
public/assets/2020v2.js
Normal file
113
public/assets/2020v2.js
Normal file
|
@ -0,0 +1,113 @@
|
|||
window.fm = (function() {
|
||||
this.headerBackground = null;
|
||||
this.originalHeaderBackground = null;
|
||||
this.defaultCoverImage = 'https://lastfm.freetls.fastly.net/i/u/174s/2a96cbd8b46e442fc41c2b86b821562f.png';
|
||||
this.indexPlayingContainer = null;
|
||||
this.indexPlayingCover = null;
|
||||
this.indexPlayingTitle = null;
|
||||
this.indexPlayingArtist = null;
|
||||
|
||||
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) {
|
||||
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 && info.cover !== this.defaultCoverImage
|
||||
? this.indexPlayingCover.src
|
||||
: this.originalHeaderBackground
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
this.getCurrentHeaderBackground = function() {
|
||||
return this.headerBackground.querySelector('img').src;
|
||||
};
|
||||
|
||||
this.switchHeaderBackground = function(url) {
|
||||
if(this.getCurrentHeaderBackground() === url)
|
||||
return;
|
||||
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);
|
||||
}.bind(this), 500);
|
||||
}.bind(this), 50);
|
||||
}.bind(this);
|
||||
newImg.onerror = function() {
|
||||
this.switchHeaderBackground(this.originalHeaderBackground);
|
||||
}.bind(this);
|
||||
};
|
||||
|
||||
this.initIndex = function(npInterval) {
|
||||
this.headerBackground = document.querySelector('.header-background');
|
||||
this.originalHeaderBackground = this.headerBackground.querySelector('img').src;
|
||||
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');
|
||||
this.updateIndexNowListening();
|
||||
setInterval(this.updateIndexNowListening, (npInterval || 30) * 1000);
|
||||
};
|
||||
|
||||
return this;
|
||||
}).call(window.fm || {});
|
127
public/index.php
Normal file
127
public/index.php
Normal file
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
// TODO FOR LAUNCH
|
||||
// - Make a new list of headers, make sure they're small in file size
|
||||
// - Make icons
|
||||
|
||||
// TODO deferred
|
||||
// - Make related page
|
||||
|
||||
// Todo ultra-deferred
|
||||
// - Make blog script
|
||||
|
||||
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' => 'Projects', 'link' => '/projects'],
|
||||
['title' => 'Contact', 'link' => '/contact'],
|
||||
//['title' => 'Related', 'link' => '/related'],
|
||||
['title' => 'Etcetera', 'link' => '/etc'],
|
||||
['title' => 'Forum', 'link' => '//forum.flash.moe'],
|
||||
]);
|
||||
|
||||
define('FM_BGS', [
|
||||
'//mikoto.misaka.nl/i/Konachan.com%20-%20164484%20blush%20brown_eyes%20brown_hair%20drink%20misaka_mikoto%20seifuku%20short_hair%20skirt%20socks%20to_aru_kagaku_no_railgun%20to_aru_majutsu_no_index.png',
|
||||
'//mikoto.misaka.nl/i/misaka%202.png',
|
||||
'//mikoto.misaka.nl/i/misaka.jpg',
|
||||
'//mikoto.misaka.nl/i/Drl8DEPWwAAPniH.jpg',
|
||||
'//mikoto.misaka.nl/i/65776322_p2_master1200.jpg',
|
||||
'//mikoto.misaka.nl/i/52666925_p0.jpg',
|
||||
'//mikoto.misaka.nl/i/44161767_p0.jpg',
|
||||
'//mikoto.misaka.nl/i/32809110_p0.jpg',
|
||||
'//mikoto.misaka.nl/i/1551886307-ZEgXIk2.jpg',
|
||||
'//mikoto.misaka.nl/i/1551886971-MgnvSNG.jpg',
|
||||
'//mikoto.misaka.nl/i/1551881563-wR2Dcx8.jpg',
|
||||
'//mikoto.misaka.nl/i/1552084409-BsTLlKg.jpg',
|
||||
]);
|
||||
|
||||
define('FM_FEET', [
|
||||
'if it ain\'t broke, i\'ll break it',
|
||||
]);
|
||||
|
||||
define('FM_ERRS' , [
|
||||
403 => ['code' => 403, 'title' => 'Access Denied'],
|
||||
404 => ['code' => 404, 'title' => 'Not Found'],
|
||||
405 => ['code' => 405, 'title' => 'Method Not Supported'],
|
||||
]);
|
||||
|
||||
function fm_component(string $name, array $vars = []) {
|
||||
extract($vars);
|
||||
require __DIR__ . '/../components/' . $name . '.php';
|
||||
}
|
||||
|
||||
function first_paragraph(string $text, string $delimiter = "\n"): string {
|
||||
$index = mb_strpos($text, $delimiter);
|
||||
return $index === false ? $text : mb_substr($text, 0, $index);
|
||||
}
|
||||
|
||||
function time_elapsed(int $since, bool $full = false): string {
|
||||
$now = new DateTime;
|
||||
$since = new DateTime(date('c', $since));
|
||||
$diff = $now->diff($since);
|
||||
|
||||
$diff->w = floor($diff->d / 7);
|
||||
$diff->d -= $diff->w * 7;
|
||||
|
||||
$string = [
|
||||
'y' => 'year',
|
||||
'm' => 'month',
|
||||
'w' => 'week',
|
||||
'd' => 'day',
|
||||
'h' => 'hour',
|
||||
'i' => 'minute',
|
||||
's' => 'second',
|
||||
];
|
||||
|
||||
foreach($string as $key => &$value) {
|
||||
if($diff->{$key})
|
||||
$value = $diff->{$key} . ' ' . $value . ($diff->{$key} > 1 ? 's' : '');
|
||||
else
|
||||
unset($string[$key]);
|
||||
}
|
||||
|
||||
if(!$full)
|
||||
$string = array_slice($string, 0, 1);
|
||||
return $string ? implode(', ', $string) . ' ago' : 'just now';
|
||||
}
|
||||
|
||||
function cache_output(string $name, int $lifetime, callable $callable) {
|
||||
$path = sys_get_temp_dir() . '/fm-' . $name . '.cache';
|
||||
if(!is_file($path) || (filemtime($path) + $lifetime) < time())
|
||||
file_put_contents($path, serialize($callable()));
|
||||
return unserialize(file_get_contents($path));
|
||||
}
|
||||
|
||||
set_include_path(realpath(__DIR__ . '/../lib/') . PATH_SEPARATOR . get_include_path());
|
||||
spl_autoload_extensions('.php');
|
||||
spl_autoload_register();
|
||||
|
||||
ob_start();
|
||||
|
||||
$reqMethod = filter_input(INPUT_SERVER, 'REQUEST_METHOD', FILTER_SANITIZE_STRING);
|
||||
$reqPath = '/' . trim(parse_url(filter_input(INPUT_SERVER, 'REQUEST_URI', FILTER_SANITIZE_STRING), PHP_URL_PATH), '/');
|
||||
|
||||
if(substr($reqPath, 0, 7) === '/error/') {
|
||||
$statusCode = intval(substr($reqPath, 8, 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);
|
||||
return;
|
||||
}
|
||||
if(($result & FM_ERR) === FM_ERR)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$errorInfo = FM_ERRS[$statusCode ?? 404] ?? FM_ERRS[404];
|
||||
http_response_code($errorInfo['code']);
|
||||
fm_component('header', ['title' => $errorInfo['title']]);
|
||||
printf(" <h2>%s</h2>\r\n", $errorInfo['title']);
|
||||
fm_component('footer');
|
Loading…
Reference in a new issue