Switched templating to Sasae.
This commit is contained in:
parent
ada73c23bb
commit
7bd41f4e93
36 changed files with 587 additions and 777 deletions
|
@ -12,7 +12,7 @@ define('MKI_DIR_LIB', MKI_ROOT . '/lib');
|
|||
define('MKI_DIR_PUB', MKI_ROOT . '/public');
|
||||
define('MKI_DIR_PAGES', MKI_ROOT . '/pages');
|
||||
define('MKI_DIR_CONFIG', MKI_ROOT . '/config');
|
||||
define('MKI_DIR_TEMPLATES', MKI_ROOT . '/tpl');
|
||||
define('MKI_DIR_TEMPLATES', MKI_ROOT . '/templates');
|
||||
|
||||
require_once MKI_ROOT . '/vendor/autoload.php';
|
||||
|
||||
|
|
|
@ -52,14 +52,16 @@ $router = new HttpFx;
|
|||
$router->setDefaultErrorHandler(function($response, $request, $code, $text) use ($ctx) {
|
||||
$path = 'errors/' . $code;
|
||||
$tpl = $ctx->getTemplating();
|
||||
$response->setContent($tpl->render($tpl->exists($path) ? $path : 'errors/master', [
|
||||
$name = is_file(sprintf('%s/errors/%s.twig', MKI_DIR_TEMPLATES, $code)) ? sprintf('errors/%s.twig', $code) : 'errors/master';
|
||||
|
||||
$response->setContent($tpl->render($name, [
|
||||
'http_error_code' => $code,
|
||||
'http_error_title' => $text,
|
||||
]));
|
||||
});
|
||||
|
||||
$router->get('/error/:code', function($response, $request, $code) {
|
||||
return intval($code);
|
||||
$router->get('/error/:code', function($response, $request, string $code) {
|
||||
return (int)$code;
|
||||
});
|
||||
|
||||
foreach(MKI_REDIRS as $source => $target)
|
||||
|
@ -68,7 +70,7 @@ foreach(MKI_REDIRS as $source => $target)
|
|||
});
|
||||
|
||||
$router->use('/', function($response) {
|
||||
$response->setPoweredBy('Makai+Index');
|
||||
$response->setPoweredBy('Makai');
|
||||
});
|
||||
|
||||
$router->get('/header-bgs.json', function() use ($ctx) {
|
||||
|
@ -123,12 +125,23 @@ $router->get('/', function() use ($ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
$db = $ctx->getDatabase();
|
||||
$dbConn = $ctx->getDatabase();
|
||||
|
||||
$projectInfos = (new Projects($dbConn))->getFeatured();
|
||||
$langs = new Languages($dbConn);
|
||||
|
||||
$projects = [];
|
||||
foreach($projectInfos as $projectInfo)
|
||||
$projects[] = [
|
||||
'info' => $projectInfo,
|
||||
'colour' => $projectInfo->hasColour() ? $projectInfo->getColour() : $langs->getProjectColour($projectInfo),
|
||||
];
|
||||
|
||||
$contacts = (new Contacts($dbConn))->getHomePage();
|
||||
|
||||
return $ctx->getTemplating()->render('index', [
|
||||
'projects' => (new Projects($db))->getFeatured(),
|
||||
'languages' => new Languages($db),
|
||||
'contacts' => (new Contacts($db))->getHomePage(),
|
||||
'projects' => $projects,
|
||||
'contacts' => $contacts,
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -139,8 +152,17 @@ $router->get('/contact', function() use ($ctx) {
|
|||
});
|
||||
|
||||
$router->get('/projects', function() use ($ctx) {
|
||||
$db = $ctx->getDatabase();
|
||||
$projects = (new Projects($db))->getAll();
|
||||
$dbConn = $ctx->getDatabase();
|
||||
$langs = new Languages($dbConn);
|
||||
$projectInfos = (new Projects($dbConn))->getAll();
|
||||
|
||||
$projects = [];
|
||||
foreach($projectInfos as $projectInfo)
|
||||
$projects[] = [
|
||||
'info' => $projectInfo,
|
||||
'langs' => $langs->getByProject($projectInfo),
|
||||
'colour' => $projectInfo->hasColour() ? $projectInfo->getColour() : $langs->getProjectColour($projectInfo),
|
||||
];
|
||||
|
||||
$sections = [
|
||||
'projects' => [
|
||||
|
@ -152,12 +174,16 @@ $router->get('/projects', function() use ($ctx) {
|
|||
|
||||
return $ctx->getTemplating()->render('projects', [
|
||||
'sections' => $sections,
|
||||
'languages' => new Languages($db),
|
||||
]);
|
||||
});
|
||||
|
||||
$router->get('/ascii', function() use ($ctx) {
|
||||
return $ctx->getTemplating()->render('ascii');
|
||||
$templating = $ctx->getTemplating();
|
||||
$templating->addFilter('chr', 'chr');
|
||||
$templating->addFilter('decoct', 'decoct');
|
||||
$templating->addFilter('dechex', 'dechex');
|
||||
|
||||
return $templating->render('ascii');
|
||||
});
|
||||
|
||||
$router->get('/rngstr', function($response, $request) {
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
namespace Makai;
|
||||
|
||||
use Index\WString;
|
||||
use Index\Colour\Colour;
|
||||
use Index\Colour\ColourRGB;
|
||||
|
||||
class ContactInfo {
|
||||
private string $name;
|
||||
|
@ -48,12 +50,12 @@ class ContactInfo {
|
|||
return $this->icon;
|
||||
}
|
||||
|
||||
public function getColour(): int {
|
||||
return $this->colour;
|
||||
public function getColour(): Colour {
|
||||
return ColourRGB::fromRawRGB($this->colour);
|
||||
}
|
||||
|
||||
public function getColourHex(): string {
|
||||
return '#' . str_pad(dechex($this->colour), 6, '0', STR_PAD_LEFT);
|
||||
public function getColourRaw(): int {
|
||||
return $this->colour;
|
||||
}
|
||||
|
||||
public function getDisplay(): WString {
|
||||
|
|
24
src/GitInfo.php
Normal file
24
src/GitInfo.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
namespace Makai;
|
||||
|
||||
final class GitInfo {
|
||||
public static function log(string $format, string $args = ''): string {
|
||||
return trim(shell_exec(sprintf('git log --pretty="%s" %s -n1 HEAD', $format, $args)));
|
||||
}
|
||||
|
||||
public static function hash(bool $long = false): string {
|
||||
return self::log($long ? '%H' : '%h');
|
||||
}
|
||||
|
||||
public static function branch(): string {
|
||||
return trim(`git rev-parse --abbrev-ref HEAD`);
|
||||
}
|
||||
|
||||
public static function tag(): string {
|
||||
return trim(`git describe --abbrev=0 --tags`);
|
||||
}
|
||||
|
||||
public static function version(): string {
|
||||
return self::branch() === 'HEAD' ? self::tag() : self::hash();
|
||||
}
|
||||
}
|
|
@ -3,6 +3,8 @@ namespace Makai;
|
|||
|
||||
use Index\AString;
|
||||
use Index\WString;
|
||||
use Index\Colour\Colour;
|
||||
use Index\Colour\ColourRGB;
|
||||
|
||||
class LanguageInfo {
|
||||
private string $id;
|
||||
|
@ -31,7 +33,11 @@ class LanguageInfo {
|
|||
return $this->colour !== null;
|
||||
}
|
||||
|
||||
public function getColour(): ?int {
|
||||
public function getColour(): Colour {
|
||||
return $this->colour === null ? Colour::none() : ColourRGB::fromRawRGB($this->colour);
|
||||
}
|
||||
|
||||
public function getColourRaw(): ?int {
|
||||
return $this->colour;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<?php
|
||||
namespace Makai;
|
||||
|
||||
use Index\Colour\Colour;
|
||||
use Index\Colour\ColourRGB;
|
||||
use Index\Data\DbType;
|
||||
use Index\Data\IDbConnection;
|
||||
use Index\Data\IDbResult;
|
||||
|
@ -31,7 +33,15 @@ class Languages {
|
|||
return $objs;
|
||||
}
|
||||
|
||||
public function getProjectColour(ProjectInfo $project): ?int {
|
||||
public function getProjectColour(ProjectInfo $project): Colour {
|
||||
$raw = $this->getProjectColourRaw($project);
|
||||
if($raw === null)
|
||||
return Colour::none();
|
||||
|
||||
return ColourRGB::fromRawRGB($raw);
|
||||
}
|
||||
|
||||
public function getProjectColourRaw(ProjectInfo $project): ?int {
|
||||
$stmt = $this->conn->prepare(self::QUERY_PROJECT_COLOUR);
|
||||
$stmt->addParameter(1, $project->getId(), DbType::STRING);
|
||||
$stmt->execute();
|
||||
|
@ -40,7 +50,7 @@ class Languages {
|
|||
if(!$result->next())
|
||||
return null;
|
||||
|
||||
return $result->getInteger(0);
|
||||
return $result->isNull(0) ? null : $result->getInteger(0);
|
||||
}
|
||||
|
||||
private static function createObject(IDbResult $result): LanguageInfo {
|
||||
|
|
|
@ -1,30 +1,37 @@
|
|||
<?php
|
||||
namespace Makai;
|
||||
|
||||
use Index\Environment;
|
||||
use Index\Data\IDbConnection;
|
||||
use Sasae\SasaeEnvironment;
|
||||
|
||||
final class MakaiContext {
|
||||
private IDbConnection $db;
|
||||
private ?TemplateContext $tpl = null;
|
||||
private IDbConnection $dbConn;
|
||||
private ?SasaeEnvironment $templating = null;
|
||||
|
||||
public function __construct(IDbConnection $db) {
|
||||
$this->db = $db;
|
||||
public function __construct(IDbConnection $dbConn) {
|
||||
$this->dbConn = $dbConn;
|
||||
}
|
||||
|
||||
public function getDatabase(): IDbConnection {
|
||||
return $this->db;
|
||||
return $this->dbConn;
|
||||
}
|
||||
|
||||
public function getTemplating(): TemplateContext {
|
||||
if($this->tpl === null) {
|
||||
$this->tpl = new TemplateContext(MKI_DIR_TEMPLATES);
|
||||
$this->tpl->setGlobal('makai', $this);
|
||||
$this->tpl->setGlobal('header_nav', $this->getDefaultNavigation());
|
||||
$this->tpl->setGlobal('header_bgs', $this->getDefaultHeaders());
|
||||
$this->tpl->setGlobal('footer_quotes', $this->getFooterQuotes());
|
||||
public function getTemplating(): SasaeEnvironment {
|
||||
if($this->templating === null) {
|
||||
$isDebug = Environment::isDebug();
|
||||
|
||||
$this->templating = new SasaeEnvironment(
|
||||
MKI_DIR_TEMPLATES,
|
||||
cache: $isDebug ? null : ['Makai', GitInfo::hash(true)],
|
||||
debug: $isDebug,
|
||||
);
|
||||
$this->templating->addGlobal('header_nav', $this->getDefaultNavigation());
|
||||
$this->templating->addGlobal('header_bgs', $this->getDefaultHeaders());
|
||||
$this->templating->addGlobal('footer_quotes', $this->getFooterQuotes());
|
||||
}
|
||||
|
||||
return $this->tpl;
|
||||
return $this->templating;
|
||||
}
|
||||
|
||||
public function getDefaultNavigation(): array {
|
||||
|
|
|
@ -4,6 +4,8 @@ namespace Makai;
|
|||
use Index\AString;
|
||||
use Index\WString;
|
||||
use Index\DateTime;
|
||||
use Index\Colour\Colour;
|
||||
use Index\Colour\ColourRGB;
|
||||
|
||||
class ProjectInfo {
|
||||
private string $id;
|
||||
|
@ -86,7 +88,11 @@ class ProjectInfo {
|
|||
return $this->colour !== null;
|
||||
}
|
||||
|
||||
public function getColour(): ?int {
|
||||
public function getColour(): Colour {
|
||||
return $this->colour === null ? Colour::none() : ColourRGB::fromRawRGB($this->colour);
|
||||
}
|
||||
|
||||
public function getColourRaw(): ?int {
|
||||
return $this->colour;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
<?php
|
||||
namespace Makai;
|
||||
|
||||
class Template {
|
||||
public function __construct(
|
||||
private TemplateContext $context,
|
||||
private TemplateVars $vars,
|
||||
private string $path
|
||||
) {}
|
||||
|
||||
public function setVar(string $name, mixed $value): void {
|
||||
$this->vars->setVar($name, $value);
|
||||
}
|
||||
public function removeVar(string $name): void {
|
||||
$this->vars->removeVar($name);
|
||||
}
|
||||
|
||||
public function render(array $vars = [], ?TemplateSelf $self = null): string {
|
||||
if(!is_file($this->path))
|
||||
throw new \RuntimeException('Template file does not exist: ' . $this->path);
|
||||
|
||||
$self = new TemplateSelf($this->context->getFunctions() + $vars + $this->vars->getVars(), $self);
|
||||
$self->tplPath = $this->path;
|
||||
|
||||
$self->var = fn(string $name, mixed $default = null) => $this->vars->getVar($name, $default);
|
||||
$self->setVar = fn(string $name, mixed $value) => $this->vars->setVar($name, $value);
|
||||
$self->remVar = fn(string $name) => $this->vars->removeVar($name);
|
||||
|
||||
$self->block = fn(string $name, mixed $contents) => $this->context->setBlock($name, new TemplateBlock($self, $contents));
|
||||
$self->include = fn(string $name) => $this->context->render($name, [], $self);
|
||||
|
||||
$self->ctx = $this->context;
|
||||
$self->extends = fn(string $name) => $self->extends = $name;
|
||||
|
||||
ob_start();
|
||||
(static function() use ($self) { include $self->tplPath; })();
|
||||
$buffer = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
if(is_string($self->extends)) {
|
||||
if(!empty($buffer))
|
||||
throw new \RuntimeException('You cannot output from templates that extend another.');
|
||||
$buffer = $this->context->render($self->extends, [], $self);
|
||||
}
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
<?php
|
||||
namespace Makai;
|
||||
|
||||
use Closure;
|
||||
|
||||
class TemplateBlock {
|
||||
public function __construct(
|
||||
private object $self,
|
||||
private mixed $body
|
||||
) {}
|
||||
|
||||
public function render(): string {
|
||||
if(is_callable($this->body)) {
|
||||
ob_start();
|
||||
($this->body)($this->self);
|
||||
$body = ob_get_contents();
|
||||
ob_end_clean();
|
||||
} else $body = strval($this->body);
|
||||
|
||||
return $body;
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
<?php
|
||||
namespace Makai;
|
||||
|
||||
use Closure;
|
||||
|
||||
class TemplateContext {
|
||||
private const EXT = '.php';
|
||||
|
||||
private string $pathFormat = '%s' . self::EXT;
|
||||
private TemplateVars $vars;
|
||||
private array $blocks = [];
|
||||
private array $functions = [];
|
||||
|
||||
public function __construct($path = '') {
|
||||
if(!empty($path))
|
||||
$this->pathFormat = rtrim($path, '\\/') . DIRECTORY_SEPARATOR . '%s' . self::EXT;
|
||||
$this->vars = new TemplateVars;
|
||||
$this->defineFunction('global', $this->vars->getVar(...));
|
||||
$this->defineFunction('getBlock', $this->getBlock(...));
|
||||
$this->defineFunction('x', fn(string $string) => htmlspecialchars($string, ENT_QUOTES, 'utf-8', true));
|
||||
}
|
||||
|
||||
public function setGlobal(string $name, mixed $value): void {
|
||||
$this->vars->setVar($name, $value);
|
||||
}
|
||||
public function removeGlobal(string $name): void {
|
||||
$this->vars->removeVar($name);
|
||||
}
|
||||
|
||||
public function defineFunction(string $name, Closure|callable $function): void {
|
||||
$this->functions[$name] = $function;
|
||||
}
|
||||
public function getFunctions(): array {
|
||||
return $this->functions;
|
||||
}
|
||||
|
||||
public function create(string $path): Template {
|
||||
return new Template($this, new TemplateVars($this->vars), sprintf($this->pathFormat, $path));
|
||||
}
|
||||
|
||||
public function exists(string $path): bool {
|
||||
return is_file(sprintf($this->pathFormat, $path));
|
||||
}
|
||||
|
||||
public function render(string $path, array $vars = [], ?TemplateSelf $self = null): string {
|
||||
return $this->create($path)->render($vars, $self);
|
||||
}
|
||||
|
||||
public function getBlock(string $name, mixed $default = ''): string {
|
||||
return ($this->blocks[$name] ?? new TemplateBlock(new \stdClass, $default))->render();
|
||||
}
|
||||
|
||||
public function setBlock(string $name, TemplateBlock $block): void {
|
||||
$this->blocks[$name] = $block;
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
namespace Makai;
|
||||
|
||||
class TemplateSelf {
|
||||
public function __construct(
|
||||
private array $members = [],
|
||||
?TemplateSelf $inherit = null
|
||||
) {
|
||||
if($inherit !== null)
|
||||
$this->members += $inherit->members;
|
||||
}
|
||||
|
||||
public function __get(string $name): mixed {
|
||||
return $this->members[$name];
|
||||
}
|
||||
|
||||
public function __set(string $name, mixed $value): void {
|
||||
$this->members[$name] = $value;
|
||||
}
|
||||
|
||||
public function __isset(string $name): bool {
|
||||
return isset($this->members[$name]);
|
||||
}
|
||||
|
||||
public function __unset(string $name): void {
|
||||
unset($this->members[$name]);
|
||||
}
|
||||
|
||||
public function __call(string $name, array $args): mixed {
|
||||
return ($this->members[$name])(...$args);
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
namespace Makai;
|
||||
|
||||
class TemplateVars {
|
||||
public function __construct(
|
||||
private ?TemplateVars $parent = null,
|
||||
private array $vars = []
|
||||
) {}
|
||||
|
||||
public function getVars(): array {
|
||||
$vars = $this->vars;
|
||||
if($this->parent !== null)
|
||||
$vars += $this->parent->getVars();
|
||||
return $vars;
|
||||
}
|
||||
|
||||
public function getVar(string $name, mixed $default = null): mixed {
|
||||
if(isset($this->vars[$name]))
|
||||
return $this->vars[$name];
|
||||
if($this->parent !== null)
|
||||
return $this->parent->getVar($name, $default);
|
||||
return $default;
|
||||
}
|
||||
|
||||
public function setVar(string $name, mixed $value): void {
|
||||
$this->vars[$name] = $value;
|
||||
}
|
||||
|
||||
public function removeVar(string $name): void {
|
||||
unset($this->vars[$name]);
|
||||
}
|
||||
}
|
257
templates/ascii.twig
Normal file
257
templates/ascii.twig
Normal file
|
@ -0,0 +1,257 @@
|
|||
{% extends 'master.twig' %}
|
||||
|
||||
{% set header_title = 'flash.moe / ascii table' %}
|
||||
{% set header_minimal = true %}
|
||||
|
||||
{% set table = [
|
||||
['Null character', 'NUL'],
|
||||
['Start of heading', 'SOH'],
|
||||
['Start of text', 'STX'],
|
||||
['End of text', 'ETX'],
|
||||
['End of transmission', 'EOT'],
|
||||
['Enquiry', 'ENQ'],
|
||||
['Acknowledgement', 'ACK'],
|
||||
['Bell', 'BEL'],
|
||||
['Backspace', 'BS' ],
|
||||
['Horizontal tab', 'HT' ],
|
||||
['Line feed', 'LF' ],
|
||||
['Vertical tab', 'VT' ],
|
||||
['Form feed', 'FF' ],
|
||||
['Carriage return', 'CR' ],
|
||||
['Shift out/X-On', 'SO' ],
|
||||
['Shift in/X-Off', 'SI' ],
|
||||
['Delta line escape', 'DLE'],
|
||||
['Device control 1 (often XON)', 'DC1'],
|
||||
['Device control 2', 'DC2'],
|
||||
['Device control 3 (often XOFF)', 'DC3'],
|
||||
['Device control 4', 'DC4'],
|
||||
['Negative acknowledgement', 'NAK'],
|
||||
['Synchronous idle', 'SYN'],
|
||||
['End of transmit block', 'ETB'],
|
||||
['Cancel', 'CAN'],
|
||||
['End of medium', 'EM' ],
|
||||
['Substitute', 'SUB'],
|
||||
['Escape', 'ESC'],
|
||||
['File separator', 'FS' ],
|
||||
['Group separator', 'GS' ],
|
||||
['Record separator', 'RS' ],
|
||||
['Unit separator', 'US' ],
|
||||
|
||||
['Space'],
|
||||
['Excalamation mark'],
|
||||
['Double quotes', 'quot'],
|
||||
['Hash'],
|
||||
['Dollar'],
|
||||
['Percent'],
|
||||
['Ampersand', 'amp'],
|
||||
['Single quote'],
|
||||
['Open parenthesis'],
|
||||
['Close parenthesis'],
|
||||
['Asterisk'],
|
||||
['Plus'],
|
||||
['Comma'],
|
||||
['Hyphen'],
|
||||
['Period'],
|
||||
['Slash'],
|
||||
['Zero'],
|
||||
['One'],
|
||||
['Two'],
|
||||
['Three'],
|
||||
['Four'],
|
||||
['Five'],
|
||||
['Six'],
|
||||
['Seven'],
|
||||
['Eight'],
|
||||
['Nine'],
|
||||
['Colon'],
|
||||
['Semicolon'],
|
||||
['Less than', 'lt'],
|
||||
['Equals'],
|
||||
['Greater than', 'gt'],
|
||||
['Question mark'],
|
||||
['At symbol'],
|
||||
['Uppercase A'],
|
||||
['Uppercase B'],
|
||||
['Uppercase C'],
|
||||
['Uppercase D'],
|
||||
['Uppercase E'],
|
||||
['Uppercase F'],
|
||||
['Uppercase G'],
|
||||
['Uppercase H'],
|
||||
['Uppercase I'],
|
||||
['Uppercase J'],
|
||||
['Uppercase K'],
|
||||
['Uppercase L'],
|
||||
['Uppercase M'],
|
||||
['Uppercase N'],
|
||||
['Uppercase O'],
|
||||
['Uppercase P'],
|
||||
['Uppercase Q'],
|
||||
['Uppercase R'],
|
||||
['Uppercase S'],
|
||||
['Uppercase T'],
|
||||
['Uppercase U'],
|
||||
['Uppercase V'],
|
||||
['Uppercase W'],
|
||||
['Uppercase X'],
|
||||
['Uppercase Y'],
|
||||
['Uppercase Z'],
|
||||
['Opening bracket'],
|
||||
['Backslash'],
|
||||
['Closing bracket'],
|
||||
['Caret'],
|
||||
['Underscore'],
|
||||
['Accent grave'],
|
||||
['Lowercase a'],
|
||||
['Lowercase b'],
|
||||
['Lowercase c'],
|
||||
['Lowercase d'],
|
||||
['Lowercase e'],
|
||||
['Lowercase f'],
|
||||
['Lowercase g'],
|
||||
['Lowercase h'],
|
||||
['Lowercase i'],
|
||||
['Lowercase j'],
|
||||
['Lowercase k'],
|
||||
['Lowercase l'],
|
||||
['Lowercase m'],
|
||||
['Lowercase n'],
|
||||
['Lowercase o'],
|
||||
['Lowercase p'],
|
||||
['Lowercase q'],
|
||||
['Lowercase r'],
|
||||
['Lowercase s'],
|
||||
['Lowercase t'],
|
||||
['Lowercase u'],
|
||||
['Lowercase v'],
|
||||
['Lowercase w'],
|
||||
['Lowercase x'],
|
||||
['Lowercase y'],
|
||||
['Lowercase z'],
|
||||
['Opening curly brace'],
|
||||
['Vertical bar'],
|
||||
['Closing curly brace'],
|
||||
['Tilde'],
|
||||
|
||||
['Delete', 'DEL'],
|
||||
] %}
|
||||
|
||||
{% block container %}
|
||||
<div class="ascii-wrap">
|
||||
<div class="ascii-search">
|
||||
<div class="ascii-search-box">
|
||||
<input type="search" id="search" placeholder="Filter..." autocomplete="off">
|
||||
</div>
|
||||
<div class="ascii-search-hint js-invisible-on-scroll">
|
||||
Type <em><code>printable</code></em> for all printable characters, or <em><code>control</code></em> for all control characters.
|
||||
</div>
|
||||
</div>
|
||||
<div class="ascii-chars">
|
||||
{% for code, info in table %}
|
||||
{% set is_printable = code > 31 and code < 127 %}
|
||||
{% set has_html = is_printable and info[1] is defined %}
|
||||
{% set print = is_printable ? code|chr : info[1] %}
|
||||
|
||||
<div class="ascii-char" data-key-code="{{ code }}" data-key-desc="{{ info[0] }}" data-key-print="{{ print }}" data-copy="{{ print }}" {% if has_html %}data-key-html="{{ info[1] }}"{% endif %}>
|
||||
<div class="ascii-char-print">{{ print }}</div>
|
||||
<div class="ascii-char-desc">{{ info[0] }}</div>
|
||||
<div class="ascii-char-misc">
|
||||
<div class="ascii-char-misc-item" data-copy="{{ code }}">
|
||||
<div class="ascii-char-misc-item-head">Decimal</div>
|
||||
<div class="ascii-char-misc-item-value">{{ code }}</div>
|
||||
</div>
|
||||
<div class="ascii-char-misc-item" data-copy="{{ code|decoct }}">
|
||||
<div class="ascii-char-misc-item-head">Octal</div>
|
||||
<div class="ascii-char-misc-item-value">{{ code|decoct }}</div>
|
||||
</div>
|
||||
<div class="ascii-char-misc-item" data-copy="{{ code|dechex }}">
|
||||
<div class="ascii-char-misc-item-head">Hex</div>
|
||||
<div class="ascii-char-misc-item-value">{{ code|dechex }}</div>
|
||||
</div>
|
||||
{% if has_html %}
|
||||
<div class="ascii-char-misc-item" data-copy="&{{ info[1] }};">
|
||||
<div class="ascii-char-misc-item-head">HTML</div>
|
||||
<div class="ascii-char-misc-item-value">&{{ info[1] }};</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var chars = document.getElementsByClassName('ascii-char'),
|
||||
search = document.getElementById('search');
|
||||
|
||||
function charsFilter(filter) {
|
||||
if(!filter) {
|
||||
for(var i = 0; i < chars.length; ++i)
|
||||
chars[i].classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
filter = filter.toLowerCase();
|
||||
|
||||
for(var i = 0; i < chars.length; ++i) {
|
||||
var chr = chars[i],
|
||||
code = (chr.dataset.keyCode || 0).toString().toLowerCase(),
|
||||
print = (chr.dataset.keyPrint || "\0").toString().toLowerCase(),
|
||||
desc = (chr.dataset.keyDesc || '').toString().toLowerCase(),
|
||||
html = (chr.dataset.keyHtml || "\0").toString().toLowerCase(),
|
||||
codeInt = parseInt(code),
|
||||
isMatch = (filter === 'printable' && (codeInt > 31 && codeInt < 127))
|
||||
|| (filter === 'control' && (codeInt < 32 || codeInt === 127))
|
||||
|| code == filter || print == filter
|
||||
|| html == filter || desc.indexOf(filter) >= 0;
|
||||
chr.classList[isMatch ? 'remove' : 'add']('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', function() {
|
||||
var hidden = document.getElementsByClassName('js-hidden-on-scroll'),
|
||||
invisible = document.getElementsByClassName('js-invisible-on-scroll'),
|
||||
atTop = window.scrollY === 0;
|
||||
|
||||
for(var i = 0; i < hidden.length; ++i)
|
||||
hidden[i].classList[atTop ? 'remove' : 'add']('hidden');
|
||||
for(var i = 0; i < invisible.length; ++i)
|
||||
invisible[i].classList[atTop ? 'remove' : 'add']('invisible');
|
||||
});
|
||||
search.addEventListener('keyup', function() {
|
||||
location.hash = search.value.trim();
|
||||
});
|
||||
window.addEventListener('hashchange', function() {
|
||||
charsFilter(decodeURIComponent((location.hash || '#').substring(1)));
|
||||
});
|
||||
if(location.hash.length > 0) {
|
||||
search.value = location.hash.substring(1).trim();
|
||||
charsFilter(search.value);
|
||||
}
|
||||
|
||||
for(var i = 0; i < chars.length; ++i) {
|
||||
chars[i].addEventListener('click', function(ev) {
|
||||
var target = ev.target;
|
||||
|
||||
while(target !== null && typeof target.dataset.copy === 'undefined') {
|
||||
target = target.parentNode || null;
|
||||
|
||||
if(target.classList.contains('char'))
|
||||
break;
|
||||
}
|
||||
|
||||
if(target === null || typeof target.dataset.copy === 'undefined')
|
||||
return;
|
||||
|
||||
var doCopy = function() { navigator.clipboard.writeText(target.dataset.copy); };
|
||||
|
||||
if(typeof window.mozInnerScreenX !== 'undefined')
|
||||
doCopy();
|
||||
else
|
||||
navigator.permissions.query({name: 'clipboard-write'}).then(function(res) {
|
||||
if(res.state === 'granted' || res.state === 'prompt')
|
||||
doCopy();
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
29
templates/contact.twig
Normal file
29
templates/contact.twig
Normal file
|
@ -0,0 +1,29 @@
|
|||
{% extends 'master.twig' %}
|
||||
|
||||
{% set header_title = 'flash.moe / contact' %}
|
||||
|
||||
{% block container %}
|
||||
<div class="section">
|
||||
<div class="section-content">
|
||||
<div class="section-background"></div>
|
||||
<h1>Contact</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="socials">
|
||||
{% for contact in contacts %}
|
||||
<div class="social social-{{ contact.name }}" style="--social-colour: {{ contact.colour }}">
|
||||
{% if contact.hasLink %}
|
||||
<a href="{{ contact.link }}" class="social-background" target="_blank" rel="noopener"></a>
|
||||
{% else %}
|
||||
<div class="social-background" onclick="fm.selectTextInElement(this.parentNode.querySelector('.social-handle')); fm.copySelectedText();"></div>
|
||||
{% endif %}
|
||||
|
||||
<div class="social-icon {{ contact.icon }}"></div>
|
||||
<div class="social-content">
|
||||
<div class="social-name">{{ contact.title }}</div>
|
||||
<div class="social-handle">{{ contact.display }}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
4
templates/errors/403.twig
Normal file
4
templates/errors/403.twig
Normal file
|
@ -0,0 +1,4 @@
|
|||
{% extends 'errors/master.twig' %}
|
||||
|
||||
{% set http_error_image = '/assets/errors/403.jpg' %}
|
||||
{% set http_error_desc = 'You are not supposed to be here.' %}
|
4
templates/errors/404.twig
Normal file
4
templates/errors/404.twig
Normal file
|
@ -0,0 +1,4 @@
|
|||
{% extends 'errors/master.twig' %}
|
||||
|
||||
{% set http_error_image = '/assets/errors/404.jpg' %}
|
||||
{% set http_error_desc = 'Whatever you\'re looking for is no longer here, or might not have been here in the first place.' %}
|
4
templates/errors/405.twig
Normal file
4
templates/errors/405.twig
Normal file
|
@ -0,0 +1,4 @@
|
|||
{% extends 'errors/master.twig' %}
|
||||
|
||||
{% set http_error_image = '/assets/errors/405.jpg' %}
|
||||
{% set http_error_desc = 'You\'re up to something, aren\'t you?' %}
|
11
templates/errors/master.twig
Normal file
11
templates/errors/master.twig
Normal file
|
@ -0,0 +1,11 @@
|
|||
{% extends 'master.twig' %}
|
||||
|
||||
{% block container %}
|
||||
<div class="http-error">
|
||||
<h2 class="http-error-head">{{ http_error_title|default('Unknown Error #' ~ http_error_code) }}</h2>
|
||||
{% if http_error_image is defined %}
|
||||
<img src="{{ http_error_image }}" alt="{{ http_error_image }}" class="http-error-image">
|
||||
{% endif %}
|
||||
<div class="http-error-desc">{{ http_error_desc|default('No additional information is available.') }}</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,12 +1,10 @@
|
|||
<?php
|
||||
$self->extends('home/master');
|
||||
{% extends 'home/master.twig' %}
|
||||
|
||||
$self->header_title = 'flash.moe / homepage';
|
||||
$self->header_full = true;
|
||||
$self->footer_onload = [['fm.initClock'], ['fm.initIndex', 10]];
|
||||
{% set header_title = 'flash.moe / homepage' %}
|
||||
{% set header_full = true %}
|
||||
{% set footer_onload = [['fm.initClock'], ['fm.initIndex', 10]] %}
|
||||
|
||||
$self->block('container', function($self) {
|
||||
?>
|
||||
{% block container %}
|
||||
<div class="php">
|
||||
<div class="php-time">
|
||||
<div class="php-time-analog">
|
||||
|
@ -42,5 +40,4 @@ $self->block('container', function($self) {
|
|||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
});
|
||||
{% endblock %}
|
1
templates/home/master.twig
Normal file
1
templates/home/master.twig
Normal file
|
@ -0,0 +1 @@
|
|||
{% extends 'master.twig' %}
|
73
templates/index.twig
Normal file
73
templates/index.twig
Normal file
|
@ -0,0 +1,73 @@
|
|||
{% extends 'master.twig' %}
|
||||
|
||||
{% set header_title = 'flash.moe' %}
|
||||
{% set header_is_index = true %}
|
||||
{% set footer_onload = [['fm.initIndex']] %}
|
||||
|
||||
{% block container %}
|
||||
<div class="index-menu">
|
||||
{% for link in header_nav|slice(1) %}
|
||||
<a href="{{ link.link }}">{{ link.title }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="index-featured">
|
||||
<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>
|
||||
{% for project in projects %}
|
||||
<div class="index-project" style="background-color: {{ project.colour }};">
|
||||
<a href="/projects#{{ project.info.cleanName }}" class="index-project-anchor"></a>
|
||||
<div class="index-project-content">
|
||||
<div class="index-project-name">{{ project.info.name }}</div>
|
||||
{% if project.info.hasSummary %}
|
||||
<div class="index-project-summary">{{ project.info.summary }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="index-project-links">
|
||||
{% if project.info.hasHomePageUrl %}
|
||||
<a class="index-project-link index-project-link-homepage" href="{{ project.info.homePageUrl }}" rel="noopener" target="_blank">Homepage</a>
|
||||
{% endif %}
|
||||
|
||||
{% if project.info.hasSourceUrl %}
|
||||
<a class="index-project-link index-project-link-repository" href="{{ project.info.sourceUrl }}" rel="noopener" target="_blank">Source</a>
|
||||
{% endif %}
|
||||
|
||||
{% if project.info.hasDiscussionUrl %}
|
||||
<a class="index-project-link index-project-link-forum" href="{{ project.info.discussionUrl }}" rel="noopener" target="_blank">Discussion</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</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">
|
||||
{% for contact in contacts %}
|
||||
<div class="social social-{{ contact.name }}" style="--social-colour: {{ contact.colour }}">
|
||||
{% if contact.hasLink %}
|
||||
<a href="{{ contact.link }}" class="social-background" target="_blank" rel="noopener"></a>
|
||||
{% else %}
|
||||
<div class="social-background" onclick="fm.selectTextInElement(this.parentNode.querySelector('.social-handle')); fm.copySelectedText();"></div>
|
||||
{% endif %}
|
||||
|
||||
<div class="social-icon {{ contact.icon }}"></div>
|
||||
<div class="social-content">
|
||||
<div class="social-name">{{ contact.title }}</div>
|
||||
<div class="social-handle">{{ contact.display }}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -2,21 +2,21 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title><?=($self->header_title ?? 'flash.moe');?></title>
|
||||
<title>{% if title is defined %}{{ title }} // {% endif %}flash.moe</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<link href="/assets/2021.css" type="text/css" rel="stylesheet">
|
||||
<link href="/assets/sprite.css" type="text/css" rel="stylesheet">
|
||||
<link href="/assets/fonts/electrolize/style.css" type="text/css" rel="stylesheet">
|
||||
</head>
|
||||
<body class="<?php if(isset($self->header_is_index)) { echo 'index ';} if(isset($self->header_full)) { echo 'fullscreen-header ';} if(isset($self->header_now_playing)) { echo 'now-playing ';} if(isset($self->header_minimal)) { echo 'header-minimal ';} ?>">
|
||||
<body class="{{ html_classes({ 'index': header_is_index is defined, 'fullscreen-header': header_full is defined, 'now-playing': header_now_playing is defined, 'header-minimal': header_minimal is defined }) }}">
|
||||
<div class="header">
|
||||
<div class="header-background">
|
||||
<img src="<?=($self->header_bgs[array_rand($self->header_bgs)]);?>" alt="">
|
||||
<img src="{{ random(header_bgs) }}" alt="">
|
||||
</div>
|
||||
<div class="header-foreground"<?php if(isset($self->header_offset) && $self->header_offset > 0) echo " style=\"padding-bottom: {$self->header_offset}px\""; ?>>
|
||||
<div class="header-foreground" {% if header_offset is defined and header_offset > 0 %}style="padding-bottom: {{ header_offset }}px"{% endif %}>
|
||||
<a class="header-logo" href="/">
|
||||
<div class="header-flash"><?=($self->header_logo_flash ?? 'flash');?></div>
|
||||
<div class="header-wave"><?=($self->header_logo_wave ?? 'wave');?></div>
|
||||
<div class="header-flash">{{ header_logo_flash|default('flash') }}</div>
|
||||
<div class="header-wave">{{ header_logo_wave|default('wave') }}</div>
|
||||
</a>
|
||||
<div class="header-right">
|
||||
<div class="header-now-playing header-now-playing-hidden">
|
||||
|
@ -32,24 +32,25 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="header-menu">
|
||||
<?php foreach($self->header_nav as $link): ?>
|
||||
<a href="<?=$link['link'];?>"<?php if($link['link'][0] === '/' && substr($link['link'], 0, 2) !== '//') { echo ''; } ?>><?=$link['title'];?></a>
|
||||
<?php endforeach; ?>
|
||||
{% for link in header_nav %}
|
||||
<a href="{{ link.link }}">{{ link.title }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<?=$self->getBlock('container');?>
|
||||
{% block container %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="footer-text">© flashwave <?=($self->footer_copy_start ?? '2010');?>-<?=($self->footer_copy_end ?? date('Y'));?> - <?=($self->footer_quotes[array_rand($self->footer_quotes)]);?></div>
|
||||
<div class="footer-text">© flashwave {{ footer_copy_start|default('2010') }}-{{ footer_copy_end|default(('now'|date('Y'))) }} - {{ random(footer_quotes) }}</div>
|
||||
</div>
|
||||
<?php if(isset($self->footer_onload) && is_array($self->footer_onload)): ?>
|
||||
{% if footer_onload is defined %}
|
||||
<script type="text/javascript">
|
||||
window.fm = { onload: <?=json_encode($self->footer_onload);?> };
|
||||
window.fm = { onload: {{ footer_onload|json_encode|raw }} };
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
{% endif %}
|
||||
<script src="/assets/2021.js" charset="utf-8" type="text/javascript"></script>
|
||||
</body>
|
||||
</html>
|
6
templates/np.twig
Normal file
6
templates/np.twig
Normal file
|
@ -0,0 +1,6 @@
|
|||
{% extends 'master.twig' %}
|
||||
|
||||
{% set header_title = 'flash.moe / now listening' %}
|
||||
{% set header_full = true %}
|
||||
{% set header_now_playing = true %}
|
||||
{% set footer_onload = [['fm.initIndex', 10]] %}
|
54
templates/projects.twig
Normal file
54
templates/projects.twig
Normal file
|
@ -0,0 +1,54 @@
|
|||
{% extends 'master.twig' %}
|
||||
|
||||
{% set header_title = 'flash.moe / projects' %}
|
||||
|
||||
{% block container %}
|
||||
{% for sectionId, section in sections %}
|
||||
<div class="section" id="section-{{ sectionId }}">
|
||||
<div class="section-content">
|
||||
<div class="section-background"></div>
|
||||
<h1>{{ section.title }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% for project in section.items %}
|
||||
<div class="project project-type-{{ sectionId }}" id="{{ project.info.cleanName }}" style="--project-colour: {{ project.colour }};">
|
||||
<div class="project-content">
|
||||
<div class="project-details">
|
||||
<h2>{{ project.info.name }}<div class="project-languages">
|
||||
{% for lang in project.langs %}
|
||||
<div class="project-language" style="--language-colour: {{ lang.colour }};">{{ lang.name }}</div>
|
||||
{% endfor %}</div>
|
||||
</h2>
|
||||
|
||||
{% if project.info.hasSummary %}
|
||||
<p class="project-details-summary">{{ project.info.summary }}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if project.info.hasDescription %}
|
||||
{% set lines = project.info.description|split("\n") %}
|
||||
{% for line in lines %}
|
||||
<p>{{ line|trim }}</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="project-links">
|
||||
{% if project.info.hasHomePageUrl %}
|
||||
<a class="project-link project-link-homepage" href="{{ project.info.homePageUrl }}" rel="noopener" target="_blank">Homepage</a>
|
||||
{% endif %}
|
||||
|
||||
{% if project.info.hasSourceUrl %}
|
||||
<a class="project-link project-link-repository" href="{{ project.info.sourceUrl }}" rel="noopener" target="_blank">Source</a>
|
||||
{% endif %}
|
||||
|
||||
{% if project.info.hasDiscussionUrl %}
|
||||
<a class="project-link project-link-forum" href="{{ project.info.discussionUrl }}" rel="noopener" target="_blank">Discussion</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
|
@ -1,14 +1,12 @@
|
|||
<?php
|
||||
$self->extends('master');
|
||||
{% extends 'master.twig' %}
|
||||
|
||||
$self->header_title = 'flash.moe / whois';
|
||||
$self->header_logo_flash = 'flash.moe ';
|
||||
$self->header_logo_wave = 'whois';
|
||||
$self->header_minimal = true;
|
||||
$self->footer_copy_start = '2013';
|
||||
{% set header_title = 'flash.moe / whois' %}
|
||||
{% set header_logo_flash = 'flash.moe ' %}
|
||||
{% set header_logo_wave = 'whois' %}
|
||||
{% set header_minimal = true %}
|
||||
{% set footer_copy_start = '2013' %}
|
||||
|
||||
$self->block('container', function($self) {
|
||||
?>
|
||||
{% block container %}
|
||||
<div class="whois-container">
|
||||
<form class="whois-lookup-form" method="get" action="">
|
||||
<input class="whois-lookup-form-input" type="text" name="domain" placeholder="Enter a domain or IP address to look up!" value="" id="lookup-input">
|
||||
|
@ -24,5 +22,4 @@ $self->block('container', function($self) {
|
|||
</div>
|
||||
|
||||
<script type="text/javascript" src="/assets/2021whois.js"></script>
|
||||
<?php
|
||||
});
|
||||
{% endblock %}
|
279
tpl/ascii.php
279
tpl/ascii.php
|
@ -1,279 +0,0 @@
|
|||
<?php
|
||||
$self->extends('master');
|
||||
|
||||
$self->header_title = 'flash.moe / ascii table';
|
||||
$self->header_minimal = true;
|
||||
|
||||
$self->block('container', function($self) {
|
||||
$table = [
|
||||
// Control characters
|
||||
['Null character', 'NUL'],
|
||||
['Start of heading', 'SOH'],
|
||||
['Start of text', 'STX'],
|
||||
['End of text', 'ETX'],
|
||||
['End of transmission', 'EOT'],
|
||||
['Enquiry', 'ENQ'],
|
||||
['Acknowledgement', 'ACK'],
|
||||
['Bell', 'BEL'],
|
||||
['Backspace', 'BS' ],
|
||||
['Horizontal tab', 'HT' ],
|
||||
['Line feed', 'LF' ],
|
||||
['Vertical tab', 'VT' ],
|
||||
['Form feed', 'FF' ],
|
||||
['Carriage return', 'CR' ],
|
||||
['Shift out/X-On', 'SO' ],
|
||||
['Shift in/X-Off', 'SI' ],
|
||||
['Delta line escape', 'DLE'],
|
||||
['Device control 1 (often XON)', 'DC1'],
|
||||
['Device control 2', 'DC2'],
|
||||
['Device control 3 (often XOFF)', 'DC3'],
|
||||
['Device control 4', 'DC4'],
|
||||
['Negative acknowledgement', 'NAK'],
|
||||
['Synchronous idle', 'SYN'],
|
||||
['End of transmit block', 'ETB'],
|
||||
['Cancel', 'CAN'],
|
||||
['End of medium', 'EM' ],
|
||||
['Substitute', 'SUB'],
|
||||
['Escape', 'ESC'],
|
||||
['File separator', 'FS' ],
|
||||
['Group separator', 'GS' ],
|
||||
['Record separator', 'RS' ],
|
||||
['Unit separator', 'US' ],
|
||||
|
||||
// Printable characters
|
||||
['Space'],
|
||||
['Excalamation mark'],
|
||||
['Double quotes', 'quot'],
|
||||
['Hash'],
|
||||
['Dollar'],
|
||||
['Percent'],
|
||||
['Ampersand', 'amp'],
|
||||
['Single quote'],
|
||||
['Open parenthesis'],
|
||||
['Close parenthesis'],
|
||||
['Asterisk'],
|
||||
['Plus'],
|
||||
['Comma'],
|
||||
['Hyphen'],
|
||||
['Period'],
|
||||
['Slash'],
|
||||
['Zero'],
|
||||
['One'],
|
||||
['Two'],
|
||||
['Three'],
|
||||
['Four'],
|
||||
['Five'],
|
||||
['Six'],
|
||||
['Seven'],
|
||||
['Eight'],
|
||||
['Nine'],
|
||||
['Colon'],
|
||||
['Semicolon'],
|
||||
['Less than', 'lt'],
|
||||
['Equals'],
|
||||
['Greater than', 'gt'],
|
||||
['Question mark'],
|
||||
['At symbol'],
|
||||
['Uppercase A'],
|
||||
['Uppercase B'],
|
||||
['Uppercase C'],
|
||||
['Uppercase D'],
|
||||
['Uppercase E'],
|
||||
['Uppercase F'],
|
||||
['Uppercase G'],
|
||||
['Uppercase H'],
|
||||
['Uppercase I'],
|
||||
['Uppercase J'],
|
||||
['Uppercase K'],
|
||||
['Uppercase L'],
|
||||
['Uppercase M'],
|
||||
['Uppercase N'],
|
||||
['Uppercase O'],
|
||||
['Uppercase P'],
|
||||
['Uppercase Q'],
|
||||
['Uppercase R'],
|
||||
['Uppercase S'],
|
||||
['Uppercase T'],
|
||||
['Uppercase U'],
|
||||
['Uppercase V'],
|
||||
['Uppercase W'],
|
||||
['Uppercase X'],
|
||||
['Uppercase Y'],
|
||||
['Uppercase Z'],
|
||||
['Opening bracket'],
|
||||
['Backslash'],
|
||||
['Closing bracket'],
|
||||
['Caret'],
|
||||
['Underscore'],
|
||||
['Accent grave'],
|
||||
['Lowercase a'],
|
||||
['Lowercase b'],
|
||||
['Lowercase c'],
|
||||
['Lowercase d'],
|
||||
['Lowercase e'],
|
||||
['Lowercase f'],
|
||||
['Lowercase g'],
|
||||
['Lowercase h'],
|
||||
['Lowercase i'],
|
||||
['Lowercase j'],
|
||||
['Lowercase k'],
|
||||
['Lowercase l'],
|
||||
['Lowercase m'],
|
||||
['Lowercase n'],
|
||||
['Lowercase o'],
|
||||
['Lowercase p'],
|
||||
['Lowercase q'],
|
||||
['Lowercase r'],
|
||||
['Lowercase s'],
|
||||
['Lowercase t'],
|
||||
['Lowercase u'],
|
||||
['Lowercase v'],
|
||||
['Lowercase w'],
|
||||
['Lowercase x'],
|
||||
['Lowercase y'],
|
||||
['Lowercase z'],
|
||||
['Opening curly brace'],
|
||||
['Vertical bar'],
|
||||
['Closing curly brace'],
|
||||
['Tilde'],
|
||||
|
||||
// Delete
|
||||
['Delete', 'DEL'],
|
||||
];
|
||||
?>
|
||||
<div class="ascii-wrap">
|
||||
<div class="ascii-search">
|
||||
<div class="ascii-search-box">
|
||||
<input type="search" id="search" placeholder="Filter..." autocomplete="off">
|
||||
</div>
|
||||
<div class="ascii-search-hint js-invisible-on-scroll">
|
||||
Type <em><code>printable</code></em> for all printable characters, or <em><code>control</code></em> for all control characters.
|
||||
</div>
|
||||
</div>
|
||||
<div class="ascii-chars"><?php
|
||||
foreach($table as $code => $info):
|
||||
$isPrintable = ($code > 31 && $code < 127);
|
||||
$print = $isPrintable ? chr($code) : $info[1];
|
||||
$attrs = ['data-key-code' => $code, 'data-key-desc' => $info[0], 'data-key-print' => $print, 'data-copy' => $print];
|
||||
$attrStr = '';
|
||||
|
||||
if($isPrintable && isset($info[1]))
|
||||
$attrs['data-key-html'] = $info[1];
|
||||
|
||||
foreach($attrs as $name => $value)
|
||||
$attrStr .= $name . '="' . htmlentities($value) . '" ';
|
||||
?><div class="ascii-char" <?=trim($attrStr);?>>
|
||||
<div class="ascii-char-print"><?=$print;?></div>
|
||||
<div class="ascii-char-desc"><?=$info[0];?></div>
|
||||
<div class="ascii-char-misc">
|
||||
<div class="ascii-char-misc-item" data-copy="<?=$code;?>">
|
||||
<div class="ascii-char-misc-item-head">Decimal</div>
|
||||
<div class="ascii-char-misc-item-value"><?=$code;?></div>
|
||||
</div>
|
||||
<div class="ascii-char-misc-item" data-copy="<?=decoct($code);?>">
|
||||
<div class="ascii-char-misc-item-head">Octal</div>
|
||||
<div class="ascii-char-misc-item-value"><?=decoct($code);?></div>
|
||||
</div>
|
||||
<div class="ascii-char-misc-item" data-copy="<?=dechex($code);?>">
|
||||
<div class="ascii-char-misc-item-head">Hex</div>
|
||||
<div class="ascii-char-misc-item-value"><?=dechex($code);?></div>
|
||||
</div>
|
||||
<?php if(isset($attrs['data-key-html'])): ?>
|
||||
<div class="ascii-char-misc-item" data-copy="&<?=$attrs['data-key-html'];?>;">
|
||||
<div class="ascii-char-misc-item-head">HTML</div>
|
||||
<div class="ascii-char-misc-item-value">&<?=$attrs['data-key-html'];?>;</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div><?php endforeach; ?></div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var chars = document.getElementsByClassName('ascii-char'),
|
||||
search = document.getElementById('search');
|
||||
|
||||
function charsFilter(filter) {
|
||||
if(!filter) {
|
||||
for(var i = 0; i < chars.length; ++i)
|
||||
chars[i].classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
filter = filter.toLowerCase();
|
||||
|
||||
for(var i = 0; i < chars.length; ++i) {
|
||||
var chr = chars[i],
|
||||
code = (chr.dataset.keyCode || 0).toString().toLowerCase(),
|
||||
print = (chr.dataset.keyPrint || "\0").toString().toLowerCase(),
|
||||
desc = (chr.dataset.keyDesc || '').toString().toLowerCase(),
|
||||
html = (chr.dataset.keyHtml || "\0").toString().toLowerCase(),
|
||||
codeInt = parseInt(code),
|
||||
isMatch = (filter === 'printable' && (codeInt > 31 && codeInt < 127))
|
||||
|| (filter === 'control' && (codeInt < 32 || codeInt === 127))
|
||||
|| code == filter || print == filter
|
||||
|| html == filter || desc.indexOf(filter) >= 0;
|
||||
chr.classList[isMatch ? 'remove' : 'add']('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', function() {
|
||||
var hidden = document.getElementsByClassName('js-hidden-on-scroll'),
|
||||
invisible = document.getElementsByClassName('js-invisible-on-scroll'),
|
||||
atTop = window.scrollY === 0;
|
||||
|
||||
for(var i = 0; i < hidden.length; ++i)
|
||||
hidden[i].classList[atTop ? 'remove' : 'add']('hidden');
|
||||
for(var i = 0; i < invisible.length; ++i)
|
||||
invisible[i].classList[atTop ? 'remove' : 'add']('invisible');
|
||||
});
|
||||
search.addEventListener('keyup', function() {
|
||||
location.hash = search.value.trim();
|
||||
});
|
||||
window.addEventListener('hashchange', function() {
|
||||
charsFilter(decodeURIComponent((location.hash || '#').substring(1)));
|
||||
});
|
||||
if(location.hash.length > 0) {
|
||||
search.value = location.hash.substring(1).trim();
|
||||
charsFilter(search.value);
|
||||
}
|
||||
|
||||
for(var i = 0; i < chars.length; ++i) {
|
||||
chars[i].addEventListener('click', function(ev) {
|
||||
var target = ev.target;
|
||||
|
||||
while(target !== null && typeof target.dataset.copy === 'undefined') {
|
||||
target = target.parentNode || null;
|
||||
|
||||
if(target.classList.contains('char'))
|
||||
break;
|
||||
}
|
||||
|
||||
if(target === null || typeof target.dataset.copy === 'undefined')
|
||||
return;
|
||||
|
||||
// Clipboard interactions are fucking horrendous
|
||||
/*if(document.execCommand) {
|
||||
/*var clipfriend = document.createElement('input');
|
||||
clipfriend.type = 'text';
|
||||
clipfriend.value = target.dataset.copy;
|
||||
clipfriend.className = 'hidden';
|
||||
document.body.appendChild(clipfriend);
|
||||
clipfriend.select();
|
||||
clipfriend.setSelectionRange(0, clipfriend.value.length);
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(clipfriend);
|
||||
} else {*/
|
||||
var doCopy = function() { navigator.clipboard.writeText(target.dataset.copy); };
|
||||
|
||||
if(typeof window.mozInnerScreenX !== 'undefined')
|
||||
doCopy();
|
||||
else
|
||||
navigator.permissions.query({name: 'clipboard-write'}).then(function(res) {
|
||||
if(res.state === 'granted' || res.state === 'prompt')
|
||||
doCopy();
|
||||
});
|
||||
//}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<?php
|
||||
});
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
$self->extends('master');
|
||||
|
||||
$self->header_title = 'flash.moe / contact';
|
||||
|
||||
$self->block('container', function($self) {
|
||||
?>
|
||||
<div class="section">
|
||||
<div class="section-content">
|
||||
<div class="section-background"></div>
|
||||
<h1>Contact</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="socials">
|
||||
<?php foreach($self->contacts as $contact): ?>
|
||||
<div class="social social-<?=$contact->getName();?>" style="--social-colour: <?=$contact->getColourHex();?>">
|
||||
|
||||
<?php if($contact->hasLink()): ?>
|
||||
<a href="<?=$contact->getLink();?>" 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 <?=$contact->getIcon();?>"></div>
|
||||
<div class="social-content">
|
||||
<div class="social-name"><?=$contact->getTitle();?></div>
|
||||
<div class="social-handle"><?=$contact->getDisplay();?></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php
|
||||
});
|
|
@ -1,5 +0,0 @@
|
|||
<?php
|
||||
$self->extends('errors/master');
|
||||
|
||||
$self->http_error_image = '/assets/errors/403.jpg';
|
||||
$self->http_error_desc = 'You are not supposed to be here.';
|
|
@ -1,5 +0,0 @@
|
|||
<?php
|
||||
$self->extends('errors/master');
|
||||
|
||||
$self->http_error_image = '/assets/errors/404.jpg';
|
||||
$self->http_error_desc = 'Whatever you\'re looking for is no longer here, or might not have been here in the first place.';
|
|
@ -1,5 +0,0 @@
|
|||
<?php
|
||||
$self->extends('errors/master');
|
||||
|
||||
$self->http_error_image = '/assets/errors/405.jpg';
|
||||
$self->http_error_desc = 'You\'re up to something, aren\'t you?';
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
$self->extends('master');
|
||||
|
||||
$self->block('container', function($self) {
|
||||
?>
|
||||
<div class="http-error">
|
||||
<h2 class="http-error-head"><?=($self->http_error_title ?? ('Unknown Error #' . $self->http_error_code));?></h2>
|
||||
<?php if(isset($self->http_error_image)): ?>
|
||||
<img src="<?=$self->http_error_image;?>" alt="<?=$self->http_error_image;?>" class="http-error-image">
|
||||
<?php endif; ?>
|
||||
<div class="http-error-desc"><?=($self->http_error_desc ?? 'No additional information is available.');?></div>
|
||||
</div>
|
||||
<?php
|
||||
});
|
|
@ -1,2 +0,0 @@
|
|||
<?php
|
||||
$self->extends('master');
|
|
@ -1,85 +0,0 @@
|
|||
<?php
|
||||
$self->extends('master');
|
||||
|
||||
$self->header_title = 'flash.moe';
|
||||
$self->header_is_index = true;
|
||||
$self->footer_onload = [['fm.initIndex']];
|
||||
|
||||
$self->block('container', function($self) {
|
||||
?>
|
||||
<div class="index-menu">
|
||||
<?php for($i = 1; $i < count($self->header_nav); ++$i): $link = $self->header_nav[$i]; ?>
|
||||
<a href="<?=$link['link'];?>"><?=$link['title'];?></a>
|
||||
<?php endfor; ?>
|
||||
</div>
|
||||
|
||||
<div class="index-featured">
|
||||
|
||||
<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
|
||||
foreach($self->projects 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()];
|
||||
|
||||
$colour = $project->hasColour() ? $project->getColour() : $self->languages->getProjectColour($project);
|
||||
$colour = str_pad(dechex($colour), 6, '0', STR_PAD_LEFT);
|
||||
?>
|
||||
<div class="index-project" style="background-color: #<?=$colour;?>;">
|
||||
<a href="/projects#<?=$project->getCleanName();?>" class="index-project-anchor"></a>
|
||||
<div class="index-project-content">
|
||||
<div class="index-project-name"><?=$project->getName();?></div>
|
||||
<?php if($project->hasSummary()): ?>
|
||||
<div class="index-project-summary"><?=$project->getSummary();?></div>
|
||||
<?php endif; ?>
|
||||
</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($self->contacts as $contact): ?>
|
||||
<div class="social social-<?=$contact->getName();?>" style="--social-colour: <?=$contact->getColourHex();?>">
|
||||
<?php if($contact->hasLink()): ?>
|
||||
<a href="<?=$contact->getLink();?>" 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 <?=$contact->getIcon();?>"></div>
|
||||
<div class="social-content">
|
||||
<div class="social-name"><?=$contact->getTitle();?></div>
|
||||
<div class="social-handle"><?=$contact->getDisplay();?></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<?php
|
||||
});
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
$self->extends('master');
|
||||
|
||||
$self->header_title = 'flash.moe / now listening';
|
||||
$self->header_full = true;
|
||||
$self->header_now_playing = true;
|
||||
$self->footer_onload = [['fm.initIndex', 10]];
|
|
@ -1,84 +0,0 @@
|
|||
<?php
|
||||
$self->extends('master');
|
||||
|
||||
$self->header_title = 'flash.moe / projects';
|
||||
|
||||
$self->block('container', function($self) {
|
||||
?>
|
||||
<?php foreach($self->sections as $sectionId => $section): ?>
|
||||
<div class="section" id="section-<?=$sectionId;?>">
|
||||
<div class="section-content">
|
||||
<div class="section-background"></div>
|
||||
<h1><?=$section['title'];?></h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
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 = $self->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);
|
||||
?>
|
||||
|
||||
<div class="project project-type-<?=$sectionId;?>" id="<?=$project->getCleanName();?>" style="--project-colour: #<?=$colour;?>;">
|
||||
<div class="project-content">
|
||||
<div class="project-details">
|
||||
<h2><?=$project->getName();?><div class="project-languages">
|
||||
<?php
|
||||
foreach($langs as $lang):
|
||||
$langColour = str_pad(dechex($lang->getColour() ?? 0), 6, '0', STR_PAD_LEFT);
|
||||
?>
|
||||
<div class="project-language" style="--language-colour: #<?=$langColour;?>;"><?=$lang->getName();?></div>
|
||||
<?php endforeach; ?>
|
||||
</div></h2>
|
||||
|
||||
<?php if($project->hasSummary()): ?>
|
||||
<p class="project-details-summary"><?=$project->getSummary();?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php
|
||||
foreach($descLines as $line):
|
||||
$line = $line->trim();
|
||||
if($line->isEmpty())
|
||||
continue;
|
||||
?>
|
||||
<p><?=$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; ?>
|
||||
<?php endforeach; ?>
|
||||
<?php
|
||||
});
|
Loading…
Reference in a new issue