commit 5f7c62f8a7de19d121b481deb4e229204e9ff089 Author: Ty Clifford Date: Wed Jun 24 09:19:08 2026 -0400 v3.1.1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ca19963 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +/blog/ +/bg/ +/embed/ +/inspect/ +/media/ +/onboard/ +/index_config/api_keys.txt +/index_config/updates.json +db.class.php diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3835195 --- /dev/null +++ b/LICENSE @@ -0,0 +1,23 @@ +# License +MIT License + +Copyright (c) 2023 Ty Clifford +Copyright (c) 2018 Painted Sky Studios + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..10b81d3 --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +# Landing page + +This repository is what's under the hood at tyclifford.com. + +# /i/ +`i` directory is a simple blog system. Content is produced via Markdown. + +Place content in date format in `/content/`. + +## To-do +``` +- Media page +``` + +# License +MIT License + +Copyright (c) 2023-2024 Ty Clifford + +Copyright (c) 2018 Painted Sky Studios + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/admanager/ads.json b/admanager/ads.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/admanager/ads.json @@ -0,0 +1 @@ +[] diff --git a/admanager/config.php b/admanager/config.php new file mode 100644 index 0000000..a80a1c6 --- /dev/null +++ b/admanager/config.php @@ -0,0 +1,98 @@ + 'Above stream', + 'below' => 'Below stream', +])); + +// ── Standard ad sizes ───────────────────────────────────────────────────────── +// Each size has: +// key — internal identifier (used as CSS class suffix) +// label — human-readable name shown in the manager +// width — px width of the creative +// height — px height of the creative +// min_screen — minimum viewport width (px) at which this size is shown +// max_screen — maximum viewport width (px) at which this size is shown +// (null = no upper limit) +// +// An ad can have multiple sizes. The live page renders ALL size variants +// for each ad and uses CSS media queries to show exactly one at a time. +define('ADS_SIZES', serialize([ + [ + 'key' => 'mobile-banner', + 'label' => 'Mobile Banner (320×50)', + 'width' => 320, + 'height' => 50, + 'min_screen' => 0, + 'max_screen' => 479, + ], + [ + 'key' => 'mobile-rect', + 'label' => 'Mobile Rectangle (300×250)', + 'width' => 300, + 'height' => 250, + 'min_screen' => 0, + 'max_screen' => 599, + ], + [ + 'key' => 'tablet-banner', + 'label' => 'Tablet Banner (468×60)', + 'width' => 468, + 'height' => 60, + 'min_screen' => 480, + 'max_screen' => 767, + ], + [ + 'key' => 'leaderboard', + 'label' => 'Leaderboard (728×90)', + 'width' => 728, + 'height' => 90, + 'min_screen' => 768, + 'max_screen' => 1023, + ], + [ + 'key' => 'billboard', + 'label' => 'Billboard (970×90)', + 'width' => 970, + 'height' => 90, + 'min_screen' => 1024, + 'max_screen' => null, + ], + [ + 'key' => 'large-rect', + 'label' => 'Large Rectangle (336×280)', + 'width' => 336, + 'height' => 280, + 'min_screen' => 600, + 'max_screen' => null, + ], + [ + 'key' => 'custom', + 'label' => 'Custom size', + 'width' => null, + 'height' => null, + 'min_screen' => 0, + 'max_screen' => null, + ], +])); diff --git a/admanager/index.php b/admanager/index.php new file mode 100644 index 0000000..7dfd625 --- /dev/null +++ b/admanager/index.php @@ -0,0 +1,684 @@ + + + + + + + <?= htmlspecialchars(ADS_MANAGER_TITLE) ?> — <?= htmlspecialchars(ADS_SITE_NAME) ?> + + + + + + + + + +
+ + +
+
+
+ Ad Manager + +
+ +
+ +
+ + + +
+
+
+ + +
+
+ New Ad +
+
+
+ +
+
+ + +
+
+ + +
+
+ +
+ + + Where clicking the ad takes the visitor. Leave blank for no link. +
+ +
+ + +
+ +
Size variants
+ Add one entry per screen size. The live page shows the best-fitting variant. + +
Creative sizes
+
+ + +
+ + +
+
+
+ + +
+
+ All Ads + + ad + +
+
    + +
  • No ads yet — create one above.
  • + + +
  • + +
    +
    +
    + +
    + +
    +
    + + +
    +
    +
    + + + + + 0): ?> + size + + +
    + +
    + + + + + + + +
    + + + +
    +
    +
    + + +
    +
    + + +
    +
    +
    + + +
    +
    + + +
    + +
    Size variants
    +
    Creative sizes
    +
    + $s): ?> + +
    +
    + + +
    +
    +
    + + +
    +
    + + +
    +
    +
    + + + Paste a full URL. For local files: /media/filename.jpg +
    +
    +
    + + +
    +
    + + +
    +
    +
    + +
    + + +
    + + +
    +
    +
    +
  • + + +
+
+ +
+ + + + + diff --git a/admanager/save.php b/admanager/save.php new file mode 100644 index 0000000..f28bc3e --- /dev/null +++ b/admanager/save.php @@ -0,0 +1,184 @@ + body: partial ad fields → update + * DELETE save.php?id= → delete + * POST save.php?action=reorder body: {ids:[…]} → reorder + * POST save.php?action=toggle body: {id,active} → toggle active + */ + +require_once __DIR__ . '/config.php'; + +header('Content-Type: application/json; charset=utf-8'); +header('X-Content-Type-Options: nosniff'); + +function ad_ok(mixed $data, int $code = 200): never { + http_response_code($code); + echo json_encode(['ok' => true, 'data' => $data], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); + exit; +} +function ad_err(string $msg, int $code = 400): never { + http_response_code($code); + echo json_encode(['ok' => false, 'error' => $msg]); + exit; +} + +// ── Auth ────────────────────────────────────────────────────────────────────── +$supplied = trim($_SERVER['HTTP_X_AD_KEY'] ?? $_SERVER['HTTP_X_Ad_Key'] ?? ($_GET['ad_key'] ?? '')); +if ($supplied === '') ad_err('Missing X-Ad-Key header.', 401); +if (!hash_equals(ADS_ADMIN_KEY, $supplied)) ad_err('Invalid key.', 403); + +// ── Data helpers ────────────────────────────────────────────────────────────── +function load_ads(): array { + if (!file_exists(ADS_JSON_PATH)) return []; + $raw = json_decode(file_get_contents(ADS_JSON_PATH), true); + return is_array($raw) ? $raw : []; +} + +function save_ads(array $ads): void { + $json = json_encode(array_values($ads), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); + $tmp = ADS_JSON_PATH . '.tmp.' . getmypid(); + if (file_put_contents($tmp, $json, LOCK_EX) === false) ad_err('Write failed.', 500); + if (!rename($tmp, ADS_JSON_PATH)) { @unlink($tmp); ad_err('Rename failed.', 500); } +} + +function find_ad(array $ads, string $id): int|false { + foreach ($ads as $i => $a) { if (($a['id'] ?? '') === $id) return $i; } + return false; +} + +function new_id(array $ads): string { + $existing = array_column($ads, 'id'); + do { $id = 'ad_' . strtolower(bin2hex(random_bytes(4))); } while (in_array($id, $existing)); + return $id; +} + +function read_body(): array { + $raw = file_get_contents('php://input'); + if (!$raw) return []; + $d = json_decode($raw, true); + if (!is_array($d)) ad_err('Body must be valid JSON.'); + return $d; +} + +function sanitise_ad(array $in, bool $require_fields = true): array { + $errors = []; + if ($require_fields) { + if (trim($in['title'] ?? '') === '') $errors[] = '"title" is required.'; + if (trim($in['placement'] ?? '') === '') $errors[] = '"placement" is required.'; + $valid_placements = array_keys(unserialize(ADS_PLACEMENTS)); + if (!in_array($in['placement'] ?? '', $valid_placements)) $errors[] = 'Invalid placement.'; + } + if ($errors) ad_err(implode(' ', $errors)); + + $out = []; + if (isset($in['title'])) $out['title'] = trim((string)$in['title']); + if (isset($in['placement'])) $out['placement'] = trim((string)$in['placement']); + if (isset($in['click_url'])) $out['click_url'] = trim((string)$in['click_url']); + if (isset($in['active'])) $out['active'] = (bool)$in['active']; + if (isset($in['note'])) $out['note'] = trim((string)$in['note']); + + // sizes: array of {size_key, media_url, media_type, custom_w, custom_h} + if (isset($in['sizes']) && is_array($in['sizes'])) { + $clean_sizes = []; + foreach ($in['sizes'] as $s) { + if (empty($s['size_key']) || empty($s['media_url'])) continue; + $clean_sizes[] = [ + 'size_key' => trim((string)$s['size_key']), + 'media_url' => trim((string)$s['media_url']), + 'media_type' => in_array($s['media_type'] ?? '', ['image','video']) ? $s['media_type'] : 'image', + 'custom_w' => isset($s['custom_w']) ? (int)$s['custom_w'] : null, + 'custom_h' => isset($s['custom_h']) ? (int)$s['custom_h'] : null, + ]; + } + $out['sizes'] = $clean_sizes; + } + return $out; +} + +// ── Routing ─────────────────────────────────────────────────────────────────── +$method = $_SERVER['REQUEST_METHOD']; +$id = trim($_GET['id'] ?? ''); +$action = trim($_GET['action'] ?? ''); + +// Toggle active +if ($method === 'POST' && $action === 'toggle') { + $body = read_body(); + $tid = trim((string)($body['id'] ?? '')); + $ads = load_ads(); + $idx = find_ad($ads, $tid); + if ($idx === false) ad_err("Ad \"$tid\" not found.", 404); + $ads[$idx]['active'] = (bool)($body['active'] ?? !$ads[$idx]['active']); + save_ads($ads); + ad_ok($ads[$idx]); +} + +// Reorder +if ($method === 'POST' && $action === 'reorder') { + $body = read_body(); + if (!isset($body['ids']) || !is_array($body['ids'])) ad_err('"ids" required.'); + $ads = load_ads(); + $indexed = []; + foreach ($ads as $a) $indexed[$a['id']] = $a; + $reordered = []; + foreach ($body['ids'] as $rid) { + if (!isset($indexed[$rid])) ad_err("Unknown id \"$rid\"."); + $reordered[] = $indexed[$rid]; + unset($indexed[$rid]); + } + foreach ($indexed as $a) $reordered[] = $a; + save_ads($reordered); + ad_ok(['reordered' => count($reordered)]); +} + +// Create +if ($method === 'POST') { + $body = read_body(); + $clean = sanitise_ad($body, true); + $ads = load_ads(); + $ad = array_merge([ + 'id' => new_id($ads), + 'title' => '', + 'placement' => 'above', + 'click_url' => '', + 'active' => true, + 'note' => '', + 'sizes' => [], + 'created' => (new DateTime('now', new DateTimeZone('UTC')))->format(DateTime::ATOM), + ], $clean); + array_unshift($ads, $ad); + save_ads($ads); + ad_ok($ad, 201); +} + +// Update +if ($method === 'PUT') { + if ($id === '') ad_err('?id= required for PUT.'); + $ads = load_ads(); + $idx = find_ad($ads, $id); + if ($idx === false) ad_err("Ad \"$id\" not found.", 404); + $body = read_body(); + $clean = sanitise_ad($body, false); + $ads[$idx] = array_merge($ads[$idx], $clean); + $ads[$idx]['id'] = $id; + save_ads($ads); + ad_ok($ads[$idx]); +} + +// Delete +if ($method === 'DELETE') { + if ($id === '') ad_err('?id= required for DELETE.'); + $ads = load_ads(); + $idx = find_ad($ads, $id); + if ($idx === false) ad_err("Ad \"$id\" not found.", 404); + $deleted = $ads[$idx]; + array_splice($ads, $idx, 1); + save_ads($ads); + ad_ok($deleted); +} + +ad_err('Method not allowed.', 405); diff --git a/ads.txt b/ads.txt new file mode 100644 index 0000000..fef243e --- /dev/null +++ b/ads.txt @@ -0,0 +1 @@ +google.com, pub-3329298426058645, DIRECT, f08c47fec0942fa0 diff --git a/api.php b/api.php new file mode 100644 index 0000000..a5eeaef --- /dev/null +++ b/api.php @@ -0,0 +1,358 @@ + Get one post + * POST api.php Create post (JSON body) + * PUT api.php?id= Update post (JSON body, partial) + * DELETE api.php?id= Delete post + * POST api.php?action=reorder Reorder (JSON body: {"ids":["003","001",…]}) + * POST api.php?action=bulk_delete Bulk delete (JSON body: {"ids":["001","002"]}) + * + * Post schema + * id string auto-generated if omitted on create + * text string required + * date string ISO-8601; defaults to now() on create + * tags string[] optional + * link string optional URL + * link_label string optional + */ + +// ══════════════════════════════════════════════════════════════════════════════ +// CONFIG — edit this block only +// ══════════════════════════════════════════════════════════════════════════════ + +// Path to updates.json (relative to this file or absolute) +define('API_JSON_PATH', __DIR__ . '/index_config/updates.json'); + +// Path to api_key.txt — one key per line; lines starting with # are ignored. +// Generate a key: php -r "echo bin2hex(random_bytes(32));" +define('API_KEY_FILE', __DIR__ . '/index_config/api_keys.txt'); + +// Allowed CORS origin. '*' opens to all; set to 'https://tyclifford.com' to lock. +define('API_CORS_ORIGIN', 'https:/tyclifford.com'); + +// Maximum posts returned by a single list request (hard ceiling). +define('API_MAX_LIMIT', 100); + +// ══════════════════════════════════════════════════════════════════════════════ +// BOOTSTRAP +// ══════════════════════════════════════════════════════════════════════════════ + +header('Content-Type: application/json; charset=utf-8'); +header('X-Content-Type-Options: nosniff'); +if (API_CORS_ORIGIN !== '') { + header('Access-Control-Allow-Origin: ' . API_CORS_ORIGIN); + header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS'); + header('Access-Control-Allow-Headers: X-API-Key, Content-Type'); +} + +// Handle CORS preflight +if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { + http_response_code(204); + exit; +} + +// ── Helpers ─────────────────────────────────────────────────────────────────── + +function api_ok(mixed $data, int $code = 200): never { + http_response_code($code); + echo json_encode(['ok' => true, 'data' => $data], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); + exit; +} + +function api_err(string $message, int $code = 400): never { + http_response_code($code); + echo json_encode(['ok' => false, 'error' => $message], JSON_UNESCAPED_UNICODE); + exit; +} + +// ── Auth ────────────────────────────────────────────────────────────────────── + +function api_auth(): void { + if (!file_exists(API_KEY_FILE)) { + api_err('API key file not configured. Create index_config/api_keys.txt with at least one key.', 503); + } + + $supplied = trim( + $_SERVER['HTTP_X_API_KEY'] + ?? $_SERVER['HTTP_X_Api_Key'] + ?? ($_GET['api_key'] ?? '') + ); + + if ($supplied === '') { + api_err('Missing API key. Supply X-API-Key header or ?api_key= parameter.', 401); + } + + $valid_keys = array_filter( + array_map('trim', file(API_KEY_FILE)), + fn($line) => $line !== '' && !str_starts_with($line, '#') + ); + + foreach ($valid_keys as $key) { + if (hash_equals($key, $supplied)) return; // constant-time compare + } + + api_err('Invalid API key.', 403); +} + +// ── JSON store ──────────────────────────────────────────────────────────────── + +function load_posts(): array { + if (!file_exists(API_JSON_PATH)) return []; + $raw = file_get_contents(API_JSON_PATH); + $data = json_decode($raw, true); + return is_array($data) ? $data : []; +} + +function save_posts(array $posts): void { + $dir = dirname(API_JSON_PATH); + if (!is_dir($dir)) { + if (!mkdir($dir, 0755, true)) { + api_err('Cannot create data directory.', 500); + } + } + + $json = json_encode( + array_values($posts), // reindex + JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT + ); + + // Atomic write with exclusive lock + $tmp = API_JSON_PATH . '.tmp.' . getmypid(); + if (file_put_contents($tmp, $json, LOCK_EX) === false) { + api_err('Failed to write data file.', 500); + } + if (!rename($tmp, API_JSON_PATH)) { + @unlink($tmp); + api_err('Failed to replace data file.', 500); + } +} + +function find_post(array $posts, string $id): int|false { + foreach ($posts as $i => $p) { + if (($p['id'] ?? '') === $id) return $i; + } + return false; +} + +function generate_id(array $posts): string { + $existing = array_column($posts, 'id'); + do { + $id = strtolower(bin2hex(random_bytes(4))); + } while (in_array($id, $existing, true)); + return $id; +} + +function validate_post(array $input, bool $require_text = true): array { + $errors = []; + + if ($require_text && trim($input['text'] ?? '') === '') { + $errors[] = '"text" is required and cannot be empty.'; + } + if (isset($input['text']) && mb_strlen($input['text']) > 4000) { + $errors[] = '"text" exceeds 4000 characters.'; + } + if (isset($input['date'])) { + $dt = DateTime::createFromFormat(DateTime::ATOM, $input['date']) + ?: DateTime::createFromFormat('Y-m-d\TH:i:s\Z', $input['date']) + ?: DateTime::createFromFormat('Y-m-d', $input['date']); + if ($dt === false) { + $errors[] = '"date" must be ISO-8601 (e.g. 2025-08-14T21:30:00Z).'; + } + } + if (isset($input['tags'])) { + if (!is_array($input['tags'])) { + $errors[] = '"tags" must be an array of strings.'; + } else { + foreach ($input['tags'] as $t) { + if (!is_string($t) || mb_strlen($t) > 50) { + $errors[] = 'Each tag must be a string ≤ 50 characters.'; + break; + } + } + } + } + if (isset($input['link']) && $input['link'] !== '') { + if (!filter_var($input['link'], FILTER_VALIDATE_URL)) { + $errors[] = '"link" must be a valid URL or empty string.'; + } + } + + return $errors; +} + +function sanitise_post(array $input): array { + $out = []; + if (isset($input['text'])) $out['text'] = trim((string)$input['text']); + if (isset($input['date'])) $out['date'] = trim((string)$input['date']); + if (isset($input['tags'])) $out['tags'] = array_values(array_map('trim', (array)$input['tags'])); + if (array_key_exists('link', $input)) $out['link'] = trim((string)$input['link']); + if (array_key_exists('link_label', $input)) $out['link_label'] = trim((string)$input['link_label']); + return $out; +} + +// ── Request body ────────────────────────────────────────────────────────────── + +function read_body(): array { + $raw = file_get_contents('php://input'); + if ($raw === '' || $raw === false) return []; + $data = json_decode($raw, true); + if (!is_array($data)) api_err('Request body must be valid JSON.', 400); + return $data; +} + +// ══════════════════════════════════════════════════════════════════════════════ +// ROUTING +// ══════════════════════════════════════════════════════════════════════════════ + +api_auth(); + +$method = $_SERVER['REQUEST_METHOD']; +$id = trim($_GET['id'] ?? ''); +$action = trim($_GET['action'] ?? ''); + +// ── POST ?action=reorder ────────────────────────────────────────────────────── +if ($method === 'POST' && $action === 'reorder') { + $body = read_body(); + if (!isset($body['ids']) || !is_array($body['ids'])) { + api_err('"ids" array is required.'); + } + + $posts = load_posts(); + $indexed = []; + foreach ($posts as $p) $indexed[$p['id']] = $p; + + $reordered = []; + foreach ($body['ids'] as $eid) { + if (!is_string($eid) || !isset($indexed[$eid])) { + api_err("Unknown id \"$eid\" in reorder list."); + } + $reordered[] = $indexed[$eid]; + unset($indexed[$eid]); + } + // Append any posts not mentioned in the ids list at the end + foreach ($indexed as $p) $reordered[] = $p; + + save_posts($reordered); + api_ok(['reordered' => count($reordered)]); +} + +// ── POST ?action=bulk_delete ────────────────────────────────────────────────── +if ($method === 'POST' && $action === 'bulk_delete') { + $body = read_body(); + if (!isset($body['ids']) || !is_array($body['ids'])) { + api_err('"ids" array is required.'); + } + + $posts = load_posts(); + $del_set = array_flip($body['ids']); + $kept = array_values(array_filter($posts, fn($p) => !isset($del_set[$p['id'] ?? '']))); + $deleted = count($posts) - count($kept); + + save_posts($kept); + api_ok(['deleted' => $deleted]); +} + +// ── GET — list or single ────────────────────────────────────────────────────── +if ($method === 'GET') { + $posts = load_posts(); + + if ($id !== '') { + $idx = find_post($posts, $id); + if ($idx === false) api_err("Post \"$id\" not found.", 404); + api_ok($posts[$idx]); + } + + // Filtering + $tag = trim($_GET['tag'] ?? ''); + if ($tag !== '') { + $posts = array_values(array_filter($posts, fn($p) => in_array($tag, (array)($p['tags'] ?? []), true))); + } + + // Pagination + $total = count($posts); + $limit = min(max(1, (int)($_GET['limit'] ?? $total)), API_MAX_LIMIT); + $offset = max(0, (int)($_GET['offset'] ?? 0)); + $page = array_slice($posts, $offset, $limit); + + api_ok([ + 'total' => $total, + 'limit' => $limit, + 'offset' => $offset, + 'items' => $page, + ]); +} + +// ── POST — create ───────────────────────────────────────────────────────────── +if ($method === 'POST' && $action === '') { + $body = read_body(); + $errors = validate_post($body, require_text: true); + if ($errors) api_err(implode(' ', $errors)); + + $posts = load_posts(); + + // Allow caller to supply id; generate one if missing or already taken + $new_id = trim((string)($body['id'] ?? '')); + if ($new_id === '' || find_post($posts, $new_id) !== false) { + $new_id = generate_id($posts); + } + + $safe = sanitise_post($body); + $post = [ + 'id' => $new_id, + 'text' => $safe['text'], + 'date' => $safe['date'] ?? (new DateTime('now', new DateTimeZone('UTC')))->format(DateTime::ATOM), + 'tags' => $safe['tags'] ?? [], + 'link' => $safe['link'] ?? '', + 'link_label' => $safe['link_label'] ?? '', + ]; + + array_unshift($posts, $post); // newest first + save_posts($posts); + api_ok($post, 201); +} + +// ── PUT — update ────────────────────────────────────────────────────────────── +if ($method === 'PUT') { + if ($id === '') api_err('?id= is required for PUT.'); + + $posts = load_posts(); + $idx = find_post($posts, $id); + if ($idx === false) api_err("Post \"$id\" not found.", 404); + + $body = read_body(); + $errors = validate_post($body, require_text: false); + if ($errors) api_err(implode(' ', $errors)); + + $safe = sanitise_post($body); + $posts[$idx] = array_merge($posts[$idx], $safe); + $posts[$idx]['id'] = $id; // id is immutable + + save_posts($posts); + api_ok($posts[$idx]); +} + +// ── DELETE — single ─────────────────────────────────────────────────────────── +if ($method === 'DELETE') { + if ($id === '') api_err('?id= is required for DELETE.'); + + $posts = load_posts(); + $idx = find_post($posts, $id); + if ($idx === false) api_err("Post \"$id\" not found.", 404); + + $deleted = $posts[$idx]; + array_splice($posts, $idx, 1); + save_posts($posts); + api_ok($deleted); +} + +api_err('Method not allowed.', 405); diff --git a/css/_notes/dwsync.xml b/css/_notes/dwsync.xml new file mode 100644 index 0000000..4807a6e --- /dev/null +++ b/css/_notes/dwsync.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/css/fonts/fontello/LICENSE.txt b/css/fonts/fontello/LICENSE.txt new file mode 100644 index 0000000..81c84bc --- /dev/null +++ b/css/fonts/fontello/LICENSE.txt @@ -0,0 +1,93 @@ +Font license info + + +## Font Awesome + + Copyright (C) 2016 by Dave Gandy + + Author: Dave Gandy + License: SIL () + Homepage: http://fortawesome.github.com/Font-Awesome/ + + +## Iconic + + Copyright (C) 2012 by P.J. Onori + + Author: P.J. Onori + License: SIL (http://scripts.sil.org/OFL) + Homepage: http://somerandomdude.com/work/iconic/ + + +## Linecons + + Copyright (C) 2013 by Designmodo + + Author: Designmodo for Smashing Magazine + License: CC BY () + Homepage: http://designmodo.com/linecons-free/ + + +## Typicons + + (c) Stephen Hutchings 2012 + + Author: Stephen Hutchings + License: SIL (http://scripts.sil.org/OFL) + Homepage: http://typicons.com/ + + +## Maki + + Copyright (C) Mapbox, LCC + + Author: Mapbox + License: BSD (https://github.com/mapbox/maki/blob/gh-pages/LICENSE.txt) + Homepage: http://mapbox.com/maki/ + + +## Entypo + + Copyright (C) 2012 by Daniel Bruce + + Author: Daniel Bruce + License: SIL (http://scripts.sil.org/OFL) + Homepage: http://www.entypo.com + + +## Elusive + + Copyright (C) 2013 by Aristeides Stathopoulos + + Author: Aristeides Stathopoulos + License: SIL (http://scripts.sil.org/OFL) + Homepage: http://aristeides.com/ + + +## Zocial + + Copyright (C) 2012 by Sam Collins + + Author: Sam Collins + License: MIT (http://opensource.org/licenses/mit-license.php) + Homepage: http://zocial.smcllns.com/ + + +## Modern Pictograms + + Copyright (c) 2012 by John Caserta. All rights reserved. + + Author: John Caserta + License: SIL (http://scripts.sil.org/OFL) + Homepage: http://thedesignoffice.org/project/modern-pictograms/ + + +## Brandico + + (C) 2012 by Vitaly Puzrin + + Author: Crowdsourced, for Fontello project + License: SIL (http://scripts.sil.org/OFL) + Homepage: + + diff --git a/css/fonts/fontello/README.txt b/css/fonts/fontello/README.txt new file mode 100644 index 0000000..beaab33 --- /dev/null +++ b/css/fonts/fontello/README.txt @@ -0,0 +1,75 @@ +This webfont is generated by http://fontello.com open source project. + + +================================================================================ +Please, note, that you should obey original font licenses, used to make this +webfont pack. Details available in LICENSE.txt file. + +- Usually, it's enough to publish content of LICENSE.txt file somewhere on your + site in "About" section. + +- If your project is open-source, usually, it will be ok to make LICENSE.txt + file publicly available in your repository. + +- Fonts, used in Fontello, don't require a clickable link on your site. + But any kind of additional authors crediting is welcome. +================================================================================ + + +Comments on archive content +--------------------------- + +- /font/* - fonts in different formats + +- /css/* - different kinds of css, for all situations. Should be ok with + twitter bootstrap. Also, you can skip style and assign icon classes + directly to text elements, if you don't mind about IE7. + +- demo.html - demo file, to show your webfont content + +- LICENSE.txt - license info about source fonts, used to build your one. + +- config.json - keeps your settings. You can import it back into fontello + anytime, to continue your work + + +Why so many CSS files ? +----------------------- + +Because we like to fit all your needs :) + +- basic file, .css - is usually enough, it contains @font-face + and character code definitions + +- *-ie7.css - if you need IE7 support, but still don't wish to put char codes + directly into html + +- *-codes.css and *-ie7-codes.css - if you like to use your own @font-face + rules, but still wish to benefit from css generation. That can be very + convenient for automated asset build systems. When you need to update font - + no need to manually edit files, just override old version with archive + content. See fontello source code for examples. + +- *-embedded.css - basic css file, but with embedded WOFF font, to avoid + CORS issues in Firefox and IE9+, when fonts are hosted on the separate domain. + We strongly recommend to resolve this issue by `Access-Control-Allow-Origin` + server headers. But if you ok with dirty hack - this file is for you. Note, + that data url moved to separate @font-face to avoid problems with + + + + + + \ No newline at end of file diff --git a/css/fonts/fontello/config.json b/css/fonts/fontello/config.json new file mode 100644 index 0000000..a7d220a --- /dev/null +++ b/css/fonts/fontello/config.json @@ -0,0 +1,692 @@ +{ + "name": "", + "css_prefix_text": "icon-", + "css_use_suffix": false, + "hinting": true, + "units_per_em": 1000, + "ascent": 850, + "glyphs": [ + { + "uid": "5408be43f7c42bccee419c6be53fdef5", + "css": "doc-text", + "code": 59394, + "src": "fontawesome" + }, + { + "uid": "e9fa538fd5913046497ac148e27cd8ea", + "css": "apple", + "code": 59401, + "src": "fontawesome" + }, + { + "uid": "a32d12927584e3c8a3dff23eb816d360", + "css": "foursquare", + "code": 59453, + "src": "fontawesome" + }, + { + "uid": "0f6a2573a7b6df911ed199bb63717e27", + "css": "github-circled", + "code": 59434, + "src": "fontawesome" + }, + { + "uid": "1145676a91138011729fa2909997af66", + "css": "linkedin-squared", + "code": 59431, + "src": "fontawesome" + }, + { + "uid": "da851b7c1f84ee83f93b29ae613558dc", + "css": "pinterest-circled", + "code": 59433, + "src": "fontawesome" + }, + { + "uid": "43fcf807461234935e65261328e1dff6", + "css": "tumblr", + "code": 59446, + "src": "fontawesome" + }, + { + "uid": "1f66490bf24c99e2c56b866d8fbd0372", + "css": "vimeo-squared", + "code": 59440, + "src": "fontawesome" + }, + { + "uid": "676cf66256441f09e4934ae6378b3e2d", + "css": "vine", + "code": 59491, + "src": "fontawesome" + }, + { + "uid": "f3d95e13eb43f3f6b8efe1eb424a1e3b", + "css": "vkontakte", + "code": 59462, + "src": "fontawesome" + }, + { + "uid": "8aff323697468c4a63993cde00386ec6", + "css": "windows", + "code": 59461, + "src": "fontawesome" + }, + { + "uid": "11ebb30e17efcd988a228ade5d3e8c74", + "css": "xing", + "code": 59454, + "src": "fontawesome" + }, + { + "uid": "47a1f80457068fbeab69fdb83d7d0817", + "css": "youtube-play", + "code": 59444, + "src": "fontawesome" + }, + { + "uid": "d090355c31f497b61d676416c1fd39fb", + "css": "twitter", + "code": 59430, + "src": "entypo" + }, + { + "uid": "bc50457410acf467b8b5721240768742", + "css": "facebook", + "code": 59429, + "src": "entypo" + }, + { + "uid": "e3606432570e59d4d852727bad063cd1", + "css": "stumbleupon-circled", + "code": 59467, + "src": "entypo" + }, + { + "uid": "d1945696d6bbbf84e388df9961f26a37", + "css": "sina-weibo", + "code": 59455, + "src": "entypo" + }, + { + "uid": "b86600decaba538aca184421752cd640", + "css": "soundcloud", + "code": 59456, + "src": "entypo" + }, + { + "uid": "f5b8e78b48e3028969a964e002daf745", + "css": "skype", + "code": 59442, + "src": "typicons" + }, + { + "uid": "33iuue66u2628uv4kz3vnmh8uee8o7rw", + "css": "rss", + "code": 59439, + "src": "modernpics" + }, + { + "uid": "1e2035b7cbaeb3d902cf9849e456069d", + "css": "delicious", + "code": 59448, + "src": "zocial" + }, + { + "uid": "aaf371ab44841e9aaffebd179d324ce4", + "css": "android", + "code": 59459, + "src": "zocial" + }, + { + "uid": "d089814548af1441aa00ecec47851f38", + "css": "fivehundredpx", + "code": 59457, + "src": "zocial" + }, + { + "uid": "c9829449672245d22b3d43d7d1a7cc90", + "css": "grooveshark", + "code": 59473, + "src": "zocial" + }, + { + "uid": "2f6ed771cf99f6826343c31ed0b08026", + "css": "forrst", + "code": 59441, + "src": "zocial" + }, + { + "uid": "8625d6e45fba1219638069e21eedbce8", + "css": "digg", + "code": 59449, + "src": "zocial" + }, + { + "uid": "b3a537446285bb3510bba57d20374818", + "css": "reddit", + "code": 59466, + "src": "zocial" + }, + { + "uid": "2dd7e3046b63beb19616dce63c5782a6", + "css": "blogger", + "code": 59447, + "src": "zocial" + }, + { + "uid": "003ba8187d6b00ec3e1ba75d00f1246a", + "css": "dribbble", + "code": 59436, + "src": "zocial" + }, + { + "uid": "231eeeedfdda9dd5d03dc8d03060007d", + "css": "flickr", + "code": 59445, + "src": "zocial" + }, + { + "uid": "3da502d7bf60ce54298aafe7b6dca55f", + "css": "meetup", + "code": 59464, + "src": "zocial" + }, + { + "uid": "916eba314d66dee45c9859d2fd146d36", + "css": "myspace", + "code": 59463, + "src": "zocial" + }, + { + "uid": "785a9b232e86ae750516adc5228a5aa7", + "css": "steam", + "code": 59475, + "src": "zocial" + }, + { + "uid": "d0ce443b8d7376716584f5f660a6bf72", + "css": "dropbox", + "code": 59469, + "src": "zocial" + }, + { + "uid": "f3d8d921ab15972d3a29da321c5c0558", + "css": "lastfm", + "code": 59438, + "src": "zocial" + }, + { + "uid": "6a85bc0b09a4f21d2a3ced2bdffbc770", + "css": "quora", + "code": 59476, + "src": "zocial" + }, + { + "uid": "0636c283a9822288a767609062bbf4a4", + "css": "wordpress", + "code": 59451, + "src": "zocial" + }, + { + "uid": "bdaca8aa91638109e25d72f1dcbb58c1", + "css": "angellist", + "code": 59477, + "src": "zocial" + }, + { + "uid": "01f67de2dc0137389f1c3aced6273acd", + "css": "icq", + "code": 59478, + "src": "brandico" + }, + { + "uid": "a0b54315916e54beb4737562e203f674", + "css": "behance", + "code": 59435, + "src": "elusive" + }, + { + "uid": "aec2c281c91ca07d01e47f444545036f", + "css": "deviantart", + "code": 59472, + "src": "elusive" + }, + { + "uid": "c37b15dbb7b146c359d45b577fba6c0e", + "css": "path", + "code": 59468, + "src": "elusive" + }, + { + "uid": "326b529aa8b8913d3bc4a632f3dcec69", + "css": "slideshare", + "code": 59458, + "src": "elusive" + }, + { + "uid": "a5e7f5574a71d31bdf2012f0c2acb2a3", + "css": "stackoverflow", + "code": 59452, + "src": "elusive" + }, + { + "uid": "220d0de4840e78031c5e7d583902d25c", + "css": "gplus", + "code": 59393, + "src": "custom_icons", + "selected": true, + "svg": { + "path": "M525.8 434.1C525.7 479.5 525.8 525 526 570.4 602 572.9 678.2 571.8 754.2 572.9 720.7 742 491.4 796.8 370 686.4 245.3 589.6 251.2 377.1 380.9 287.2 471.6 214.6 600.5 232.5 691.2 295.4 726.8 262.3 760.1 227 792.3 190.3 716.9 130 624.3 87 525.8 91.6 320.3 84.7 131.4 265.2 127.9 471.3 114.8 639.8 225.3 805 381.3 864.6 536.7 924.6 735.9 883.7 835.2 743.7 900.7 655.3 914.8 541.4 907.2 434.4 779.9 433.5 652.9 433.7 525.8 434.1ZM1269.1 433.5C1268.7 395.5 1268.5 357.3 1268.3 319.3 1230.4 319.3 1192.7 319.3 1155 319.3 1154.6 357.3 1154.2 395.3 1154 433.5 1116 433.7 1078 433.9 1040.1 434.3 1040.1 472.3 1040.1 510.1 1040.1 547.9 1078 548.3 1116.1 548.7 1154 549.1 1154.4 587.1 1154.4 625.1 1154.8 663.1 1192.7 663.1 1230.4 663.1 1268.3 663.1 1268.5 625.1 1268.7 587.1 1269.1 548.9 1307.2 548.5 1345.1 548.3 1383 547.9 1383 510.1 1383 472.1 1383 434.3 1345.1 433.9 1307 433.9 1269.1 433.5Z", + "width": 1497 + }, + "search": [ + "google plus" + ] + }, + { + "uid": "fb4794d89b77ab712ad56c0dc4c76c6b", + "css": "spotify", + "code": 59395, + "src": "fontawesome" + }, + { + "uid": "1b1a592d33fa26c37926cbe840eb60b8", + "css": "snapchat", + "code": 62124, + "src": "fontawesome" + }, + { + "uid": "eea8064584ca4397576380d9de87bfc3", + "css": "imdb", + "code": 62168, + "src": "fontawesome" + }, + { + "uid": "559647a6f430b3aeadbecd67194451dd", + "css": "menu", + "code": 59392, + "src": "fontawesome" + }, + { + "uid": "fbc6facd27f744438410cbcad545bf52", + "css": "tripadvisor", + "code": 62050, + "src": "fontawesome" + }, + { + "uid": "3d8b20949cd6f782067f9e652967f8a8", + "css": "videocam-outline", + "code": 59414, + "src": "typicons" + }, + { + "uid": "b1c81b3df9f4a9b8c393f405bb5293d8", + "css": "music-outline", + "code": 59525, + "src": "typicons" + }, + { + "uid": "155970c9f52b23f31026bab7163caa63", + "css": "codepen", + "code": 62252, + "src": "brandico" + }, + { + "uid": "572c9ded6a688698dc275b30ff30fefa", + "css": "music", + "code": 59542, + "src": "linecons" + }, + { + "uid": "9725db89b610135dc76cd8a21afffa83", + "css": "search", + "code": 59582, + "src": "linecons" + }, + { + "uid": "b59cc4d390a6df564a055f43c03a6af5", + "css": "camera", + "code": 59536, + "src": "linecons" + }, + { + "uid": "8573943a49ed6dd8f7819070445baa46", + "css": "photo", + "code": 59543, + "src": "linecons" + }, + { + "uid": "359f380b2113cb40259269aed843e33d", + "css": "attach", + "code": 59482, + "src": "linecons" + }, + { + "uid": "a58c05a8a3ba59ababd4860a30892560", + "css": "eye", + "code": 59537, + "src": "linecons" + }, + { + "uid": "06a2fbb04dffecdb444936073cae9123", + "css": "thumbs-up", + "code": 59483, + "src": "linecons" + }, + { + "uid": "2aeb4987b469d22fcb8b471b6d2cdfff", + "css": "pencil", + "code": 59571, + "src": "linecons" + }, + { + "uid": "1ea8ad6dbe1ae9a73565ad16cf88de19", + "css": "location", + "code": 59569, + "src": "linecons" + }, + { + "uid": "c2958cfd1eed4434ab6e4bd6ab337af9", + "css": "cup", + "code": 59572, + "src": "linecons" + }, + { + "uid": "f978da58836f23373882916f05fb70b4", + "css": "doc", + "code": 59573, + "src": "linecons" + }, + { + "uid": "29f4bce4a60650057c5248e30baa15a6", + "css": "note", + "code": 59586, + "src": "linecons" + }, + { + "uid": "143d93c9abdee0f4a7a2a810acc62b91", + "css": "sound", + "code": 59587, + "src": "linecons" + }, + { + "uid": "cc5e3696cf6c3ad8c32daaea05355a99", + "css": "tv", + "code": 59588, + "src": "linecons" + }, + { + "uid": "7697f1734d53461137f05faab3f79cf0", + "css": "desktop", + "code": 59589, + "src": "linecons" + }, + { + "uid": "6a9f3405aba67bcaee81e7ea38946b27", + "css": "mobile", + "code": 59590, + "src": "linecons" + }, + { + "uid": "52cde78d270e411ccf22c9ec02910d69", + "css": "cd", + "code": 59591, + "src": "linecons" + }, + { + "uid": "1e89103762be72b0720eda0468e62b90", + "css": "globe", + "code": 59559, + "src": "linecons" + }, + { + "uid": "6fbd93c0d220507921015a15575009e5", + "css": "paper-plane", + "code": 59470, + "src": "linecons" + }, + { + "uid": "4b7f817f780705a5d267ad59e855da91", + "css": "graduation-cap", + "code": 59535, + "src": "linecons" + }, + { + "uid": "02ad0683eaa9826f396fa7ccf1f1fb25", + "css": "database", + "code": 59555, + "src": "linecons" + }, + { + "uid": "6cc796276697d8a75ae21bb2df4badc9", + "css": "beaker", + "code": 59538, + "src": "linecons" + }, + { + "uid": "f5053e43ef8be31afb905801449ae26d", + "css": "truck", + "code": 59547, + "src": "linecons" + }, + { + "uid": "b08f8c33777cd6b512d9576fc6e7f405", + "css": "food", + "code": 59397, + "src": "linecons" + }, + { + "uid": "f8e8b267a9b949804f0c9dd7fed8e477", + "css": "diamond", + "code": 59551, + "src": "linecons" + }, + { + "uid": "d58dc0a0927a7dea2992f615f2fbf400", + "css": "wallet", + "code": 59553, + "src": "linecons" + }, + { + "uid": "862d4faacb164c0412cb3249d43d5d39", + "css": "t-shirt", + "code": 59576, + "src": "linecons" + }, + { + "uid": "fb3ee9c192c946342e0cdd12f06d7774", + "css": "lightbulb", + "code": 59565, + "src": "linecons" + }, + { + "uid": "ee22dc89481ea63f27654f59dcace804", + "css": "clock", + "code": 59566, + "src": "linecons" + }, + { + "uid": "d6944e1d9af302093d43b299bbecbbbe02", + "css": "home", + "code": 59398, + "src": "iconic" + }, + { + "uid": "05de65980bb8a3711da037c2cc25f4f8", + "css": "camera-outline", + "code": 59498, + "src": "typicons" + }, + { + "uid": "933f454f9d3342a055217fd695475d44", + "css": "vcard", + "code": 59399, + "src": "typicons" + }, + { + "uid": "eae068bed86aca956cdd10edcaaf24ee", + "css": "phone-outline", + "code": 59560, + "src": "typicons" + }, + { + "uid": "b7bd78e163801889af145e513d6c5383", + "css": "mic-outline", + "code": 59497, + "src": "typicons" + }, + { + "uid": "eea1af32a01c5e7a9ce5675bcff64a7b", + "css": "laptop", + "code": 59402, + "src": "typicons" + }, + { + "uid": "c710cda53f5e1f8705865d453b7f323f", + "css": "tablet", + "code": 59583, + "src": "typicons" + }, + { + "uid": "bb310a229d0d33a1cbe8aa151a7a8fcb", + "css": "briefcase", + "code": 59584, + "src": "typicons" + }, + { + "uid": "610efad3150b6959e1b3fc863f39c60c", + "css": "gift", + "code": 59404, + "src": "typicons" + }, + { + "uid": "935dd4069330896913745bbdd9699b0e", + "css": "wine", + "code": 59405, + "src": "typicons" + }, + { + "uid": "8f73806c205d38d9412c340f93ed0c7a", + "css": "coffee", + "code": 59531, + "src": "typicons" + }, + { + "uid": "2b8ea04aa0e500ae056f9c7934af18e6", + "css": "tree", + "code": 59563, + "src": "typicons" + }, + { + "uid": "51727ca007aa35ceabcaffc28934faee", + "css": "book-open", + "code": 59406, + "src": "entypo" + }, + { + "uid": "5d27926667773d186de09fda4312af27", + "css": "shop", + "code": 59550, + "src": "linecons" + }, + { + "uid": "4c1ef492f1d2c39a2250ae457cee2a6e", + "css": "instagram", + "code": 59596, + "src": "fontawesome" + }, + { + "uid": "679d36f8485f05180a2719c91b3f621e", + "css": "comment", + "code": 59396, + "src": "iconic" + }, + { + "uid": "aab7e7622e6ee1ed8166fb73f20e8c9a", + "css": "bicycle", + "code": 59400, + "src": "maki" + }, + { + "uid": "bf882b30900da12fca090d9796bc3030", + "css": "mail", + "code": 59518, + "src": "fontawesome" + }, + { + "uid": "bf09b1c6561dc0ced707476e2cd83d29", + "css": "medium", + "code": 62010, + "src": "fontawesome" + }, + { + "uid": "457c8e2b305e7af74c1be4f07a01ca92", + "css": "vcard-1", + "code": 59524, + "src": "entypo" + }, + { + "uid": "bcb868184ff9c35f8aef564f50c0d649", + "css": "feather", + "code": 59500, + "src": "entypo" + }, + { + "uid": "64f5a50cf7ac972a56a9a511acb5d28e", + "css": "map", + "code": 59403, + "src": "entypo" + }, + { + "uid": "540b6a4262be769515c79700618b4aea", + "css": "address", + "code": 59407, + "src": "entypo" + }, + { + "uid": "366c3c56b79031deacc953d7f92085e5", + "css": "eye-outline", + "code": 59496, + "src": "typicons" + }, + { + "uid": "2cfac2c1a9eb4552af0eba34def4b010", + "css": "skype-outline", + "code": 59408, + "src": "typicons" + }, + { + "uid": "gruasbs60jlu9us53csv9q7nnwy4d990", + "css": "address-1", + "code": 59437, + "src": "typicons" + }, + { + "uid": "13289e807e02b1d46f2fe10d81d6fba6", + "css": "location-outline", + "code": 59528, + "src": "typicons" + }, + { + "uid": "05a2f4c4839012434774cc1ceb4c0f9b", + "css": "download-alt", + "code": 59409, + "src": "elusive" + }, + { + "uid": "4e41b9df5bb73b7a245cfefdea685495", + "css": "instagram", + "code": 59396, + "src": "custom_icons", + "selected": false, + "svg": { + "path": "M500 0C364.2 0 347.2 0.6 293.9 3 240.6 5.4 204.3 13.9 172.5 26.2 139.6 39 111.7 56.1 83.9 83.9 56.1 111.7 39 139.6 26.2 172.5 13.9 204.3 5.4 240.6 3 293.8 0.6 347.2 0 364.2 0 500 0 635.8 0.6 652.8 3 706.1 5.4 759.4 13.9 795.7 26.2 827.5 39 860.4 56.1 888.3 83.9 916.1 111.7 943.9 139.6 961 172.5 973.8 204.3 986.1 240.6 994.6 293.9 997 347.2 999.4 364.2 1000 500 1000 635.8 1000 652.8 999.4 706.2 997 759.4 994.6 795.7 986.1 827.5 973.8 860.4 961 888.3 943.9 916.1 916.1 943.9 888.3 961 860.4 973.8 827.5 986.1 795.7 994.6 759.4 997 706.1 999.4 652.8 1000 635.8 1000 500 1000 364.2 999.4 347.2 997 293.8 994.6 240.6 986.1 204.3 973.8 172.5 961 139.6 943.9 111.7 916.1 83.9 888.3 56.1 860.4 39 827.5 26.2 795.7 13.9 759.4 5.4 706.2 3 652.8 0.6 635.8 0 500 0ZM500 90.1C633.5 90.1 649.3 90.6 702 93 750.8 95.2 777.3 103.4 794.9 110.2 818.2 119.3 834.9 130.1 852.4 147.6 869.9 165.1 880.7 181.8 889.8 205.1 896.6 222.7 904.8 249.2 907 298 909.4 350.7 909.9 366.5 909.9 500 909.9 633.5 909.4 649.3 907 702 904.8 750.8 896.6 777.3 889.8 794.9 880.7 818.2 869.9 834.9 852.4 852.4 834.9 869.9 818.2 880.7 794.9 889.8 777.3 896.6 750.8 904.8 702 907 649.3 909.4 633.5 909.9 500 909.9 366.5 909.9 350.7 909.4 298 907 249.2 904.8 222.7 896.6 205.1 889.8 181.8 880.7 165.1 869.9 147.6 852.4 130.1 834.9 119.3 818.2 110.2 794.9 103.4 777.3 95.2 750.8 93 702 90.6 649.3 90.1 633.5 90.1 500 90.1 366.5 90.6 350.7 93 298 95.2 249.2 103.4 222.7 110.2 205.1 119.3 181.8 130.1 165.1 147.6 147.6 165.1 130.1 181.8 119.3 205.1 110.2 222.7 103.4 249.2 95.2 298 93 350.7 90.6 366.5 90.1 500 90.1M500 666.7C408 666.7 333.3 592 333.3 500 333.3 408 408 333.3 500 333.3 592 333.3 666.7 408 666.7 500 666.7 592 592 666.7 500 666.7ZM500 243.2C358.2 243.2 243.2 358.2 243.2 500 243.2 641.8 358.2 756.8 500 756.8 641.8 756.8 756.8 641.8 756.8 500 756.8 358.2 641.8 243.2 500 243.2M826.9 233.1C826.9 266.2 800 293.1 766.9 293.1 733.8 293.1 706.9 266.2 706.9 233.1 706.9 200 733.8 173.1 766.9 173.1 800 173.1 826.9 200 826.9 233.1", + "width": 1000 + }, + "search": [ + "instagram" + ] + } + ] +} \ No newline at end of file diff --git a/css/fonts/fontello/css/_notes/dwsync.xml b/css/fonts/fontello/css/_notes/dwsync.xml new file mode 100644 index 0000000..7117f3d --- /dev/null +++ b/css/fonts/fontello/css/_notes/dwsync.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/css/fonts/fontello/css/animation.css b/css/fonts/fontello/css/animation.css new file mode 100644 index 0000000..ac5a956 --- /dev/null +++ b/css/fonts/fontello/css/animation.css @@ -0,0 +1,85 @@ +/* + Animation example, for spinners +*/ +.animate-spin { + -moz-animation: spin 2s infinite linear; + -o-animation: spin 2s infinite linear; + -webkit-animation: spin 2s infinite linear; + animation: spin 2s infinite linear; + display: inline-block; +} +@-moz-keyframes spin { + 0% { + -moz-transform: rotate(0deg); + -o-transform: rotate(0deg); + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -moz-transform: rotate(359deg); + -o-transform: rotate(359deg); + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +@-webkit-keyframes spin { + 0% { + -moz-transform: rotate(0deg); + -o-transform: rotate(0deg); + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -moz-transform: rotate(359deg); + -o-transform: rotate(359deg); + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +@-o-keyframes spin { + 0% { + -moz-transform: rotate(0deg); + -o-transform: rotate(0deg); + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -moz-transform: rotate(359deg); + -o-transform: rotate(359deg); + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +@-ms-keyframes spin { + 0% { + -moz-transform: rotate(0deg); + -o-transform: rotate(0deg); + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -moz-transform: rotate(359deg); + -o-transform: rotate(359deg); + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +@keyframes spin { + 0% { + -moz-transform: rotate(0deg); + -o-transform: rotate(0deg); + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -moz-transform: rotate(359deg); + -o-transform: rotate(359deg); + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} diff --git a/css/fonts/fontello/css/fontello-codes.css b/css/fonts/fontello/css/fontello-codes.css new file mode 100644 index 0000000..497922d --- /dev/null +++ b/css/fonts/fontello/css/fontello-codes.css @@ -0,0 +1,111 @@ + +.icon-menu:before { content: '\e800'; } /* '' */ +.icon-gplus:before { content: '\e801'; } /* '' */ +.icon-doc-text:before { content: '\e802'; } /* '' */ +.icon-spotify:before { content: '\e803'; } /* '' */ +.icon-comment:before { content: '\e804'; } /* '' */ +.icon-food:before { content: '\e805'; } /* '' */ +.icon-home:before { content: '\e806'; } /* '' */ +.icon-vcard:before { content: '\e807'; } /* '' */ +.icon-bicycle:before { content: '\e808'; } /* '' */ +.icon-apple:before { content: '\e809'; } /* '' */ +.icon-laptop:before { content: '\e80a'; } /* '' */ +.icon-map:before { content: '\e80b'; } /* '' */ +.icon-gift:before { content: '\e80c'; } /* '' */ +.icon-wine:before { content: '\e80d'; } /* '' */ +.icon-book-open:before { content: '\e80e'; } /* '' */ +.icon-address:before { content: '\e80f'; } /* '' */ +.icon-skype-outline:before { content: '\e810'; } /* '' */ +.icon-download-alt:before { content: '\e811'; } /* '' */ +.icon-videocam-outline:before { content: '\e816'; } /* '' */ +.icon-facebook:before { content: '\e825'; } /* '' */ +.icon-twitter:before { content: '\e826'; } /* '' */ +.icon-linkedin-squared:before { content: '\e827'; } /* '' */ +.icon-pinterest-circled:before { content: '\e829'; } /* '' */ +.icon-github-circled:before { content: '\e82a'; } /* '' */ +.icon-behance:before { content: '\e82b'; } /* '' */ +.icon-dribbble:before { content: '\e82c'; } /* '' */ +.icon-address-1:before { content: '\e82d'; } /* '' */ +.icon-lastfm:before { content: '\e82e'; } /* '' */ +.icon-rss:before { content: '\e82f'; } /* '' */ +.icon-vimeo-squared:before { content: '\e830'; } /* '' */ +.icon-forrst:before { content: '\e831'; } /* '' */ +.icon-skype:before { content: '\e832'; } /* '' */ +.icon-youtube-play:before { content: '\e834'; } /* '' */ +.icon-flickr:before { content: '\e835'; } /* '' */ +.icon-tumblr:before { content: '\e836'; } /* '' */ +.icon-blogger:before { content: '\e837'; } /* '' */ +.icon-delicious:before { content: '\e838'; } /* '' */ +.icon-digg:before { content: '\e839'; } /* '' */ +.icon-wordpress:before { content: '\e83b'; } /* '' */ +.icon-stackoverflow:before { content: '\e83c'; } /* '' */ +.icon-foursquare:before { content: '\e83d'; } /* '' */ +.icon-xing:before { content: '\e83e'; } /* '' */ +.icon-sina-weibo:before { content: '\e83f'; } /* '' */ +.icon-soundcloud:before { content: '\e840'; } /* '' */ +.icon-fivehundredpx:before { content: '\e841'; } /* '' */ +.icon-slideshare:before { content: '\e842'; } /* '' */ +.icon-android:before { content: '\e843'; } /* '' */ +.icon-windows:before { content: '\e845'; } /* '' */ +.icon-vkontakte:before { content: '\e846'; } /* '' */ +.icon-myspace:before { content: '\e847'; } /* '' */ +.icon-meetup:before { content: '\e848'; } /* '' */ +.icon-reddit:before { content: '\e84a'; } /* '' */ +.icon-stumbleupon-circled:before { content: '\e84b'; } /* '' */ +.icon-path:before { content: '\e84c'; } /* '' */ +.icon-dropbox:before { content: '\e84d'; } /* '' */ +.icon-paper-plane:before { content: '\e84e'; } /* '' */ +.icon-deviantart:before { content: '\e850'; } /* '' */ +.icon-grooveshark:before { content: '\e851'; } /* '' */ +.icon-steam:before { content: '\e853'; } /* '' */ +.icon-quora:before { content: '\e854'; } /* '' */ +.icon-angellist:before { content: '\e855'; } /* '' */ +.icon-icq:before { content: '\e856'; } /* '' */ +.icon-attach:before { content: '\e85a'; } /* '' */ +.icon-thumbs-up:before { content: '\e85b'; } /* '' */ +.icon-vine:before { content: '\e863'; } /* '' */ +.icon-eye-outline:before { content: '\e868'; } /* '' */ +.icon-mic-outline:before { content: '\e869'; } /* '' */ +.icon-camera-outline:before { content: '\e86a'; } /* '' */ +.icon-feather:before { content: '\e86c'; } /* '' */ +.icon-mail:before { content: '\e87e'; } /* '' */ +.icon-vcard-1:before { content: '\e884'; } /* '' */ +.icon-music-outline:before { content: '\e885'; } /* '' */ +.icon-location-outline:before { content: '\e888'; } /* '' */ +.icon-coffee:before { content: '\e88b'; } /* '' */ +.icon-graduation-cap:before { content: '\e88f'; } /* '' */ +.icon-camera:before { content: '\e890'; } /* '' */ +.icon-eye:before { content: '\e891'; } /* '' */ +.icon-beaker:before { content: '\e892'; } /* '' */ +.icon-music:before { content: '\e896'; } /* '' */ +.icon-photo:before { content: '\e897'; } /* '' */ +.icon-truck:before { content: '\e89b'; } /* '' */ +.icon-shop:before { content: '\e89e'; } /* '' */ +.icon-diamond:before { content: '\e89f'; } /* '' */ +.icon-wallet:before { content: '\e8a1'; } /* '' */ +.icon-database:before { content: '\e8a3'; } /* '' */ +.icon-globe:before { content: '\e8a7'; } /* '' */ +.icon-phone-outline:before { content: '\e8a8'; } /* '' */ +.icon-tree:before { content: '\e8ab'; } /* '' */ +.icon-lightbulb:before { content: '\e8ad'; } /* '' */ +.icon-clock:before { content: '\e8ae'; } /* '' */ +.icon-location:before { content: '\e8b1'; } /* '' */ +.icon-pencil:before { content: '\e8b3'; } /* '' */ +.icon-cup:before { content: '\e8b4'; } /* '' */ +.icon-doc:before { content: '\e8b5'; } /* '' */ +.icon-t-shirt:before { content: '\e8b8'; } /* '' */ +.icon-search:before { content: '\e8be'; } /* '' */ +.icon-tablet:before { content: '\e8bf'; } /* '' */ +.icon-briefcase:before { content: '\e8c0'; } /* '' */ +.icon-note:before { content: '\e8c2'; } /* '' */ +.icon-sound:before { content: '\e8c3'; } /* '' */ +.icon-tv:before { content: '\e8c4'; } /* '' */ +.icon-desktop:before { content: '\e8c5'; } /* '' */ +.icon-mobile:before { content: '\e8c6'; } /* '' */ +.icon-cd:before { content: '\e8c7'; } /* '' */ +.icon-instagram:before { content: '\e8cc'; } /* '' */ +.icon-medium:before { content: '\f23a'; } /* '' */ +.icon-tripadvisor:before { content: '\f262'; } /* '' */ +.icon-snapchat:before { content: '\f2ac'; } /* '' */ +.icon-imdb:before { content: '\f2d8'; } /* '' */ +.icon-codepen:before { content: '\f32c'; } /* '' */ \ No newline at end of file diff --git a/css/fonts/fontello/css/fontello-embedded.css b/css/fonts/fontello/css/fontello-embedded.css new file mode 100644 index 0000000..02d85ad --- /dev/null +++ b/css/fonts/fontello/css/fontello-embedded.css @@ -0,0 +1,164 @@ +@font-face { + font-family: 'fontello'; + src: url('../font/fontello.eot?14539542'); + src: url('../font/fontello.eot?14539542#iefix') format('embedded-opentype'), + url('../font/fontello.svg?14539542#fontello') format('svg'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'fontello'; + src: url('data:application/octet-stream;base64,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') format('woff'), + url('data:application/octet-stream;base64,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AWsBbAFtAW4BbwFwAARtZW51BWdwbHVzCGRvYy10ZXh0B3Nwb3RpZnkHY29tbWVudARmb29kBGhvbWUFdmNhcmQHYmljeWNsZQVhcHBsZQZsYXB0b3ADbWFwBGdpZnQEd2luZQlib29rLW9wZW4HYWRkcmVzcw1za3lwZS1vdXRsaW5lDGRvd25sb2FkLWFsdBB2aWRlb2NhbS1vdXRsaW5lCGZhY2Vib29rB3R3aXR0ZXIQbGlua2VkaW4tc3F1YXJlZBFwaW50ZXJlc3QtY2lyY2xlZA5naXRodWItY2lyY2xlZAdiZWhhbmNlCGRyaWJiYmxlCWFkZHJlc3MtMQZsYXN0Zm0DcnNzDXZpbWVvLXNxdWFyZWQGZm9ycnN0BXNreXBlDHlvdXR1YmUtcGxheQZmbGlja3IGdHVtYmxyB2Jsb2dnZXIJZGVsaWNpb3VzBGRpZ2cJd29yZHByZXNzDXN0YWNrb3ZlcmZsb3cKZm91cnNxdWFyZQR4aW5nCnNpbmEtd2VpYm8Kc291bmRjbG91ZA1maXZlaHVuZHJlZHB4CnNsaWRlc2hhcmUHYW5kcm9pZAd3aW5kb3dzCXZrb250YWt0ZQdteXNwYWNlBm1lZXR1cAZyZWRkaXQTc3R1bWJsZXVwb24tY2lyY2xlZARwYXRoB2Ryb3Bib3gLcGFwZXItcGxhbmUKZGV2aWFudGFydAtncm9vdmVzaGFyawVzdGVhbQVxdW9yYQlhbmdlbGxpc3QDaWNxBmF0dGFjaAl0aHVtYnMtdXAEdmluZQtleWUtb3V0bGluZQttaWMtb3V0bGluZQ5jYW1lcmEtb3V0bGluZQdmZWF0aGVyBG1haWwHdmNhcmQtMQ1tdXNpYy1vdXRsaW5lEGxvY2F0aW9uLW91dGxpbmUGY29mZmVlDmdyYWR1YXRpb24tY2FwBmNhbWVyYQNleWUGYmVha2VyBW11c2ljBXBob3RvBXRydWNrBHNob3AHZGlhbW9uZAZ3YWxsZXQIZGF0YWJhc2UFZ2xvYmUNcGhvbmUtb3V0bGluZQR0cmVlCWxpZ2h0YnVsYgVjbG9jawhsb2NhdGlvbgZwZW5jaWwDY3VwA2RvYwd0LXNoaXJ0BnNlYXJjaAZ0YWJsZXQJYnJpZWZjYXNlBG5vdGUFc291bmQCdHYHZGVza3RvcAZtb2JpbGUC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') format('truetype'); +} +/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ +/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ +/* +@media screen and (-webkit-min-device-pixel-ratio:0) { + @font-face { + font-family: 'fontello'; + src: url('../font/fontello.svg?14539542#fontello') format('svg'); + } +} +*/ + + [class^="icon-"]:before, [class*=" icon-"]:before { + font-family: "fontello"; + font-style: normal; + font-weight: normal; + speak: none; + + display: inline-block; + text-decoration: inherit; + width: 1em; + margin-right: .2em; + text-align: center; + /* opacity: .8; */ + + /* For safety - reset parent styles, that can break glyph codes*/ + font-variant: normal; + text-transform: none; + + /* fix buttons height, for twitter bootstrap */ + line-height: 1em; + + /* Animation center compensation - margins should be symmetric */ + /* remove if not needed */ + margin-left: .2em; + + /* you can be more comfortable with increased icons size */ + /* font-size: 120%; */ + + /* Uncomment for 3D effect */ + /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ +} +.icon-menu:before { content: '\e800'; } /* '' */ +.icon-gplus:before { content: '\e801'; } /* '' */ +.icon-doc-text:before { content: '\e802'; } /* '' */ +.icon-spotify:before { content: '\e803'; } /* '' */ +.icon-comment:before { content: '\e804'; } /* '' */ +.icon-food:before { content: '\e805'; } /* '' */ +.icon-home:before { content: '\e806'; } /* '' */ +.icon-vcard:before { content: '\e807'; } /* '' */ +.icon-bicycle:before { content: '\e808'; } /* '' */ +.icon-apple:before { content: '\e809'; } /* '' */ +.icon-laptop:before { content: '\e80a'; } /* '' */ +.icon-map:before { content: '\e80b'; } /* '' */ +.icon-gift:before { content: '\e80c'; } /* '' */ +.icon-wine:before { content: '\e80d'; } /* '' */ +.icon-book-open:before { content: '\e80e'; } /* '' */ +.icon-address:before { content: '\e80f'; } /* '' */ +.icon-skype-outline:before { content: '\e810'; } /* '' */ +.icon-download-alt:before { content: '\e811'; } /* '' */ +.icon-videocam-outline:before { content: '\e816'; } /* '' */ +.icon-facebook:before { content: '\e825'; } /* '' */ +.icon-twitter:before { content: '\e826'; } /* '' */ +.icon-linkedin-squared:before { content: '\e827'; } /* '' */ +.icon-pinterest-circled:before { content: '\e829'; } /* '' */ +.icon-github-circled:before { content: '\e82a'; } /* '' */ +.icon-behance:before { content: '\e82b'; } /* '' */ +.icon-dribbble:before { content: '\e82c'; } /* '' */ +.icon-address-1:before { content: '\e82d'; } /* '' */ +.icon-lastfm:before { content: '\e82e'; } /* '' */ +.icon-rss:before { content: '\e82f'; } /* '' */ +.icon-vimeo-squared:before { content: '\e830'; } /* '' */ +.icon-forrst:before { content: '\e831'; } /* '' */ +.icon-skype:before { content: '\e832'; } /* '' */ +.icon-youtube-play:before { content: '\e834'; } /* '' */ +.icon-flickr:before { content: '\e835'; } /* '' */ +.icon-tumblr:before { content: '\e836'; } /* '' */ +.icon-blogger:before { content: '\e837'; } /* '' */ +.icon-delicious:before { content: '\e838'; } /* '' */ +.icon-digg:before { content: '\e839'; } /* '' */ +.icon-wordpress:before { content: '\e83b'; } /* '' */ +.icon-stackoverflow:before { content: '\e83c'; } /* '' */ +.icon-foursquare:before { content: '\e83d'; } /* '' */ +.icon-xing:before { content: '\e83e'; } /* '' */ +.icon-sina-weibo:before { content: '\e83f'; } /* '' */ +.icon-soundcloud:before { content: '\e840'; } /* '' */ +.icon-fivehundredpx:before { content: '\e841'; } /* '' */ +.icon-slideshare:before { content: '\e842'; } /* '' */ +.icon-android:before { content: '\e843'; } /* '' */ +.icon-windows:before { content: '\e845'; } /* '' */ +.icon-vkontakte:before { content: '\e846'; } /* '' */ +.icon-myspace:before { content: '\e847'; } /* '' */ +.icon-meetup:before { content: '\e848'; } /* '' */ +.icon-reddit:before { content: '\e84a'; } /* '' */ +.icon-stumbleupon-circled:before { content: '\e84b'; } /* '' */ +.icon-path:before { content: '\e84c'; } /* '' */ +.icon-dropbox:before { content: '\e84d'; } /* '' */ +.icon-paper-plane:before { content: '\e84e'; } /* '' */ +.icon-deviantart:before { content: '\e850'; } /* '' */ +.icon-grooveshark:before { content: '\e851'; } /* '' */ +.icon-steam:before { content: '\e853'; } /* '' */ +.icon-quora:before { content: '\e854'; } /* '' */ +.icon-angellist:before { content: '\e855'; } /* '' */ +.icon-icq:before { content: '\e856'; } /* '' */ +.icon-attach:before { content: '\e85a'; } /* '' */ +.icon-thumbs-up:before { content: '\e85b'; } /* '' */ +.icon-vine:before { content: '\e863'; } /* '' */ +.icon-eye-outline:before { content: '\e868'; } /* '' */ +.icon-mic-outline:before { content: '\e869'; } /* '' */ +.icon-camera-outline:before { content: '\e86a'; } /* '' */ +.icon-feather:before { content: '\e86c'; } /* '' */ +.icon-mail:before { content: '\e87e'; } /* '' */ +.icon-vcard-1:before { content: '\e884'; } /* '' */ +.icon-music-outline:before { content: '\e885'; } /* '' */ +.icon-location-outline:before { content: '\e888'; } /* '' */ +.icon-coffee:before { content: '\e88b'; } /* '' */ +.icon-graduation-cap:before { content: '\e88f'; } /* '' */ +.icon-camera:before { content: '\e890'; } /* '' */ +.icon-eye:before { content: '\e891'; } /* '' */ +.icon-beaker:before { content: '\e892'; } /* '' */ +.icon-music:before { content: '\e896'; } /* '' */ +.icon-photo:before { content: '\e897'; } /* '' */ +.icon-truck:before { content: '\e89b'; } /* '' */ +.icon-shop:before { content: '\e89e'; } /* '' */ +.icon-diamond:before { content: '\e89f'; } /* '' */ +.icon-wallet:before { content: '\e8a1'; } /* '' */ +.icon-database:before { content: '\e8a3'; } /* '' */ +.icon-globe:before { content: '\e8a7'; } /* '' */ +.icon-phone-outline:before { content: '\e8a8'; } /* '' */ +.icon-tree:before { content: '\e8ab'; } /* '' */ +.icon-lightbulb:before { content: '\e8ad'; } /* '' */ +.icon-clock:before { content: '\e8ae'; } /* '' */ +.icon-location:before { content: '\e8b1'; } /* '' */ +.icon-pencil:before { content: '\e8b3'; } /* '' */ +.icon-cup:before { content: '\e8b4'; } /* '' */ +.icon-doc:before { content: '\e8b5'; } /* '' */ +.icon-t-shirt:before { content: '\e8b8'; } /* '' */ +.icon-search:before { content: '\e8be'; } /* '' */ +.icon-tablet:before { content: '\e8bf'; } /* '' */ +.icon-briefcase:before { content: '\e8c0'; } /* '' */ +.icon-note:before { content: '\e8c2'; } /* '' */ +.icon-sound:before { content: '\e8c3'; } /* '' */ +.icon-tv:before { content: '\e8c4'; } /* '' */ +.icon-desktop:before { content: '\e8c5'; } /* '' */ +.icon-mobile:before { content: '\e8c6'; } /* '' */ +.icon-cd:before { content: '\e8c7'; } /* '' */ +.icon-instagram:before { content: '\e8cc'; } /* '' */ +.icon-medium:before { content: '\f23a'; } /* '' */ +.icon-tripadvisor:before { content: '\f262'; } /* '' */ +.icon-snapchat:before { content: '\f2ac'; } /* '' */ +.icon-imdb:before { content: '\f2d8'; } /* '' */ +.icon-codepen:before { content: '\f32c'; } /* '' */ \ No newline at end of file diff --git a/css/fonts/fontello/css/fontello-ie7-codes.css b/css/fonts/fontello/css/fontello-ie7-codes.css new file mode 100644 index 0000000..40c99bc --- /dev/null +++ b/css/fonts/fontello/css/fontello-ie7-codes.css @@ -0,0 +1,111 @@ + +.icon-menu { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-gplus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-doc-text { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-spotify { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-comment { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-food { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-vcard { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-bicycle { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-apple { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-laptop { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-map { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-gift { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-wine { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-book-open { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-address { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-skype-outline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-download-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-videocam-outline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-facebook { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-twitter { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-linkedin-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-pinterest-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-github-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-behance { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-dribbble { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-address-1 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-lastfm { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-rss { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-vimeo-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-forrst { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-skype { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-youtube-play { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-flickr { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-tumblr { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-blogger { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-delicious { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-digg { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-wordpress { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-stackoverflow { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-foursquare { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-xing { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-sina-weibo { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-soundcloud { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-fivehundredpx { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-slideshare { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-android { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-windows { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-vkontakte { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-myspace { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-meetup { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-reddit { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-stumbleupon-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-path { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-dropbox { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-paper-plane { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-deviantart { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-grooveshark { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-steam { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-quora { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-angellist { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-icq { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-attach { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-thumbs-up { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-vine { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-eye-outline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-mic-outline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-camera-outline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-feather { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-mail { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-vcard-1 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-music-outline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-location-outline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-coffee { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-graduation-cap { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-camera { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-eye { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-beaker { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-music { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-photo { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-truck { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-shop { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-diamond { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-wallet { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-database { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-globe { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-phone-outline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-tree { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-lightbulb { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-clock { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-location { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-pencil { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-cup { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-doc { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-t-shirt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-search { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-tablet { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-briefcase { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-note { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-sound { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-tv { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-desktop { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-mobile { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-cd { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-instagram { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-medium { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-tripadvisor { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-snapchat { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-imdb { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-codepen { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } \ No newline at end of file diff --git a/css/fonts/fontello/css/fontello-ie7.css b/css/fonts/fontello/css/fontello-ie7.css new file mode 100644 index 0000000..bb7a73b --- /dev/null +++ b/css/fonts/fontello/css/fontello-ie7.css @@ -0,0 +1,122 @@ +[class^="icon-"], [class*=" icon-"] { + font-family: 'fontello'; + font-style: normal; + font-weight: normal; + + /* fix buttons height */ + line-height: 1em; + + /* you can be more comfortable with increased icons size */ + /* font-size: 120%; */ +} + +.icon-menu { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-gplus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-doc-text { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-spotify { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-comment { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-food { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-vcard { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-bicycle { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-apple { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-laptop { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-map { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-gift { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-wine { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-book-open { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-address { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-skype-outline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-download-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-videocam-outline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-facebook { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-twitter { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-linkedin-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-pinterest-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-github-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-behance { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-dribbble { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-address-1 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-lastfm { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-rss { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-vimeo-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-forrst { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-skype { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-youtube-play { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-flickr { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-tumblr { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-blogger { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-delicious { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-digg { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-wordpress { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-stackoverflow { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-foursquare { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-xing { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-sina-weibo { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-soundcloud { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-fivehundredpx { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-slideshare { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-android { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-windows { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-vkontakte { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-myspace { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-meetup { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-reddit { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-stumbleupon-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-path { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-dropbox { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-paper-plane { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-deviantart { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-grooveshark { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-steam { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-quora { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-angellist { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-icq { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-attach { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-thumbs-up { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-vine { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-eye-outline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-mic-outline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-camera-outline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-feather { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-mail { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-vcard-1 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-music-outline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-location-outline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-coffee { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-graduation-cap { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-camera { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-eye { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-beaker { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-music { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-photo { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-truck { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-shop { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-diamond { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-wallet { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-database { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-globe { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-phone-outline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-tree { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-lightbulb { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-clock { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-location { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-pencil { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-cup { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-doc { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-t-shirt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-search { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-tablet { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-briefcase { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-note { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-sound { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-tv { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-desktop { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-mobile { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-cd { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-instagram { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-medium { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-tripadvisor { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-snapchat { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-imdb { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-codepen { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } \ No newline at end of file diff --git a/css/fonts/fontello/css/fontello.css b/css/fonts/fontello/css/fontello.css new file mode 100644 index 0000000..3ab6b32 --- /dev/null +++ b/css/fonts/fontello/css/fontello.css @@ -0,0 +1,167 @@ +@font-face { + font-family: 'fontello'; + src: url('../font/fontello.eot?29896784'); + src: url('../font/fontello.eot?29896784#iefix') format('embedded-opentype'), + url('../font/fontello.woff2?29896784') format('woff2'), + url('../font/fontello.woff?29896784') format('woff'), + url('../font/fontello.ttf?29896784') format('truetype'), + url('../font/fontello.svg?29896784#fontello') format('svg'); + font-weight: normal; + font-style: normal; +} +/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ +/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ +/* +@media screen and (-webkit-min-device-pixel-ratio:0) { + @font-face { + font-family: 'fontello'; + src: url('../font/fontello.svg?29896784#fontello') format('svg'); + } +} +*/ + + [class^="icon-"]:before, [class*=" icon-"]:before { + font-family: "fontello"; + font-style: normal; + font-weight: normal; + speak: none; + + display: inline-block; + text-decoration: inherit; + width: 1em; + margin-right: .2em; + text-align: center; + /* opacity: .8; */ + + /* For safety - reset parent styles, that can break glyph codes*/ + font-variant: normal; + text-transform: none; + + /* fix buttons height, for twitter bootstrap */ + line-height: 1em; + + /* Animation center compensation - margins should be symmetric */ + /* remove if not needed */ + margin-left: .2em; + + /* you can be more comfortable with increased icons size */ + /* font-size: 120%; */ + + /* Font smoothing. That was taken from TWBS */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + /* Uncomment for 3D effect */ + /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ +} + +.icon-menu:before { content: '\e800'; } /* '' */ +.icon-gplus:before { content: '\e801'; } /* '' */ +.icon-doc-text:before { content: '\e802'; } /* '' */ +.icon-spotify:before { content: '\e803'; } /* '' */ +.icon-comment:before { content: '\e804'; } /* '' */ +.icon-food:before { content: '\e805'; } /* '' */ +.icon-home:before { content: '\e806'; } /* '' */ +.icon-vcard:before { content: '\e807'; } /* '' */ +.icon-bicycle:before { content: '\e808'; } /* '' */ +.icon-apple:before { content: '\e809'; } /* '' */ +.icon-laptop:before { content: '\e80a'; } /* '' */ +.icon-map:before { content: '\e80b'; } /* '' */ +.icon-gift:before { content: '\e80c'; } /* '' */ +.icon-wine:before { content: '\e80d'; } /* '' */ +.icon-book-open:before { content: '\e80e'; } /* '' */ +.icon-address:before { content: '\e80f'; } /* '' */ +.icon-skype-outline:before { content: '\e810'; } /* '' */ +.icon-download-alt:before { content: '\e811'; } /* '' */ +.icon-videocam-outline:before { content: '\e816'; } /* '' */ +.icon-facebook:before { content: '\e825'; } /* '' */ +.icon-twitter:before { content: '\e826'; } /* '' */ +.icon-linkedin-squared:before { content: '\e827'; } /* '' */ +.icon-pinterest-circled:before { content: '\e829'; } /* '' */ +.icon-github-circled:before { content: '\e82a'; } /* '' */ +.icon-behance:before { content: '\e82b'; } /* '' */ +.icon-dribbble:before { content: '\e82c'; } /* '' */ +.icon-address-1:before { content: '\e82d'; } /* '' */ +.icon-lastfm:before { content: '\e82e'; } /* '' */ +.icon-rss:before { content: '\e82f'; } /* '' */ +.icon-vimeo-squared:before { content: '\e830'; } /* '' */ +.icon-forrst:before { content: '\e831'; } /* '' */ +.icon-skype:before { content: '\e832'; } /* '' */ +.icon-youtube-play:before { content: '\e834'; } /* '' */ +.icon-flickr:before { content: '\e835'; } /* '' */ +.icon-tumblr:before { content: '\e836'; } /* '' */ +.icon-blogger:before { content: '\e837'; } /* '' */ +.icon-delicious:before { content: '\e838'; } /* '' */ +.icon-digg:before { content: '\e839'; } /* '' */ +.icon-wordpress:before { content: '\e83b'; } /* '' */ +.icon-stackoverflow:before { content: '\e83c'; } /* '' */ +.icon-foursquare:before { content: '\e83d'; } /* '' */ +.icon-xing:before { content: '\e83e'; } /* '' */ +.icon-sina-weibo:before { content: '\e83f'; } /* '' */ +.icon-soundcloud:before { content: '\e840'; } /* '' */ +.icon-fivehundredpx:before { content: '\e841'; } /* '' */ +.icon-slideshare:before { content: '\e842'; } /* '' */ +.icon-android:before { content: '\e843'; } /* '' */ +.icon-windows:before { content: '\e845'; } /* '' */ +.icon-vkontakte:before { content: '\e846'; } /* '' */ +.icon-myspace:before { content: '\e847'; } /* '' */ +.icon-meetup:before { content: '\e848'; } /* '' */ +.icon-reddit:before { content: '\e84a'; } /* '' */ +.icon-stumbleupon-circled:before { content: '\e84b'; } /* '' */ +.icon-path:before { content: '\e84c'; } /* '' */ +.icon-dropbox:before { content: '\e84d'; } /* '' */ +.icon-paper-plane:before { content: '\e84e'; } /* '' */ +.icon-deviantart:before { content: '\e850'; } /* '' */ +.icon-grooveshark:before { content: '\e851'; } /* '' */ +.icon-steam:before { content: '\e853'; } /* '' */ +.icon-quora:before { content: '\e854'; } /* '' */ +.icon-angellist:before { content: '\e855'; } /* '' */ +.icon-icq:before { content: '\e856'; } /* '' */ +.icon-attach:before { content: '\e85a'; } /* '' */ +.icon-thumbs-up:before { content: '\e85b'; } /* '' */ +.icon-vine:before { content: '\e863'; } /* '' */ +.icon-eye-outline:before { content: '\e868'; } /* '' */ +.icon-mic-outline:before { content: '\e869'; } /* '' */ +.icon-camera-outline:before { content: '\e86a'; } /* '' */ +.icon-feather:before { content: '\e86c'; } /* '' */ +.icon-mail:before { content: '\e87e'; } /* '' */ +.icon-vcard-1:before { content: '\e884'; } /* '' */ +.icon-music-outline:before { content: '\e885'; } /* '' */ +.icon-location-outline:before { content: '\e888'; } /* '' */ +.icon-coffee:before { content: '\e88b'; } /* '' */ +.icon-graduation-cap:before { content: '\e88f'; } /* '' */ +.icon-camera:before { content: '\e890'; } /* '' */ +.icon-eye:before { content: '\e891'; } /* '' */ +.icon-beaker:before { content: '\e892'; } /* '' */ +.icon-music:before { content: '\e896'; } /* '' */ +.icon-photo:before { content: '\e897'; } /* '' */ +.icon-truck:before { content: '\e89b'; } /* '' */ +.icon-shop:before { content: '\e89e'; } /* '' */ +.icon-diamond:before { content: '\e89f'; } /* '' */ +.icon-wallet:before { content: '\e8a1'; } /* '' */ +.icon-database:before { content: '\e8a3'; } /* '' */ +.icon-globe:before { content: '\e8a7'; } /* '' */ +.icon-phone-outline:before { content: '\e8a8'; } /* '' */ +.icon-tree:before { content: '\e8ab'; } /* '' */ +.icon-lightbulb:before { content: '\e8ad'; } /* '' */ +.icon-clock:before { content: '\e8ae'; } /* '' */ +.icon-location:before { content: '\e8b1'; } /* '' */ +.icon-pencil:before { content: '\e8b3'; } /* '' */ +.icon-cup:before { content: '\e8b4'; } /* '' */ +.icon-doc:before { content: '\e8b5'; } /* '' */ +.icon-t-shirt:before { content: '\e8b8'; } /* '' */ +.icon-search:before { content: '\e8be'; } /* '' */ +.icon-tablet:before { content: '\e8bf'; } /* '' */ +.icon-briefcase:before { content: '\e8c0'; } /* '' */ +.icon-note:before { content: '\e8c2'; } /* '' */ +.icon-sound:before { content: '\e8c3'; } /* '' */ +.icon-tv:before { content: '\e8c4'; } /* '' */ +.icon-desktop:before { content: '\e8c5'; } /* '' */ +.icon-mobile:before { content: '\e8c6'; } /* '' */ +.icon-cd:before { content: '\e8c7'; } /* '' */ +.icon-instagram:before { content: '\e8cc'; } /* '' */ +.icon-medium:before { content: '\f23a'; } /* '' */ +.icon-tripadvisor:before { content: '\f262'; } /* '' */ +.icon-snapchat:before { content: '\f2ac'; } /* '' */ +.icon-imdb:before { content: '\f2d8'; } /* '' */ +.icon-codepen:before { content: '\f32c'; } /* '' */ \ No newline at end of file diff --git a/css/fonts/fontello/demo.html b/css/fonts/fontello/demo.html new file mode 100644 index 0000000..90be1ca --- /dev/null +++ b/css/fonts/fontello/demo.html @@ -0,0 +1,473 @@ + + + + + + + + +
+

+ fontello + font demo +

+ +
+
+
+
icon-menu0xe800
+
icon-gplus0xe801
+
icon-doc-text0xe802
+
icon-spotify0xe803
+
+
+
icon-comment0xe804
+
icon-food0xe805
+
icon-home0xe806
+
icon-vcard0xe807
+
+
+
icon-bicycle0xe808
+
icon-apple0xe809
+
icon-laptop0xe80a
+
icon-map0xe80b
+
+
+
icon-gift0xe80c
+
icon-wine0xe80d
+
icon-book-open0xe80e
+
icon-address0xe80f
+
+
+
icon-skype-outline0xe810
+
icon-download-alt0xe811
+
icon-videocam-outline0xe816
+
icon-facebook0xe825
+
+
+
icon-twitter0xe826
+
icon-linkedin-squared0xe827
+
icon-pinterest-circled0xe829
+
icon-github-circled0xe82a
+
+
+
icon-behance0xe82b
+
icon-dribbble0xe82c
+
icon-address-10xe82d
+
icon-lastfm0xe82e
+
+
+
icon-rss0xe82f
+
icon-vimeo-squared0xe830
+
icon-forrst0xe831
+
icon-skype0xe832
+
+
+
icon-youtube-play0xe834
+
icon-flickr0xe835
+
icon-tumblr0xe836
+
icon-blogger0xe837
+
+
+
icon-delicious0xe838
+
icon-digg0xe839
+
icon-wordpress0xe83b
+
icon-stackoverflow0xe83c
+
+
+
icon-foursquare0xe83d
+
icon-xing0xe83e
+
icon-sina-weibo0xe83f
+
icon-soundcloud0xe840
+
+
+
icon-fivehundredpx0xe841
+
icon-slideshare0xe842
+
icon-android0xe843
+
icon-windows0xe845
+
+
+
icon-vkontakte0xe846
+
icon-myspace0xe847
+
icon-meetup0xe848
+
icon-reddit0xe84a
+
+
+
icon-stumbleupon-circled0xe84b
+
icon-path0xe84c
+
icon-dropbox0xe84d
+
icon-paper-plane0xe84e
+
+
+
icon-deviantart0xe850
+
icon-grooveshark0xe851
+
icon-steam0xe853
+
icon-quora0xe854
+
+
+
icon-angellist0xe855
+
icon-icq0xe856
+
icon-attach0xe85a
+
icon-thumbs-up0xe85b
+
+
+
icon-vine0xe863
+
icon-eye-outline0xe868
+
icon-mic-outline0xe869
+
icon-camera-outline0xe86a
+
+
+
icon-feather0xe86c
+
icon-mail0xe87e
+
icon-vcard-10xe884
+
icon-music-outline0xe885
+
+
+
icon-location-outline0xe888
+
icon-coffee0xe88b
+
icon-graduation-cap0xe88f
+
icon-camera0xe890
+
+
+
icon-eye0xe891
+
icon-beaker0xe892
+
icon-music0xe896
+
icon-photo0xe897
+
+
+
icon-truck0xe89b
+
icon-shop0xe89e
+
icon-diamond0xe89f
+
icon-wallet0xe8a1
+
+
+
icon-database0xe8a3
+
icon-globe0xe8a7
+
icon-phone-outline0xe8a8
+
icon-tree0xe8ab
+
+
+
icon-lightbulb0xe8ad
+
icon-clock0xe8ae
+
icon-location0xe8b1
+
icon-pencil0xe8b3
+
+
+
icon-cup0xe8b4
+
icon-doc0xe8b5
+
icon-t-shirt0xe8b8
+
icon-search0xe8be
+
+
+
icon-tablet0xe8bf
+
icon-briefcase0xe8c0
+
icon-note0xe8c2
+
icon-sound0xe8c3
+
+
+
icon-tv0xe8c4
+
icon-desktop0xe8c5
+
icon-mobile0xe8c6
+
icon-cd0xe8c7
+
+
+
icon-instagram0xe8cc
+
icon-medium0xf23a
+
icon-tripadvisor0xf262
+
icon-snapchat0xf2ac
+
+
+
icon-imdb0xf2d8
+
icon-codepen0xf32c
+
+
+ + + \ No newline at end of file diff --git a/css/fonts/fontello/font/_notes/dwsync.xml b/css/fonts/fontello/font/_notes/dwsync.xml new file mode 100644 index 0000000..6830742 --- /dev/null +++ b/css/fonts/fontello/font/_notes/dwsync.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/css/fonts/fontello/font/fontello.eot b/css/fonts/fontello/font/fontello.eot new file mode 100644 index 0000000..c9002bc Binary files /dev/null and b/css/fonts/fontello/font/fontello.eot differ diff --git a/css/fonts/fontello/font/fontello.svg b/css/fonts/fontello/font/fontello.svg new file mode 100644 index 0000000..af108f4 --- /dev/null +++ b/css/fonts/fontello/font/fontello.svg @@ -0,0 +1,230 @@ + + + +Copyright (C) 2017 by original authors @ fontello.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/css/fonts/fontello/font/fontello.ttf b/css/fonts/fontello/font/fontello.ttf new file mode 100644 index 0000000..14ac42b Binary files /dev/null and b/css/fonts/fontello/font/fontello.ttf differ diff --git a/css/fonts/fontello/font/fontello.woff b/css/fonts/fontello/font/fontello.woff new file mode 100644 index 0000000..d921f12 Binary files /dev/null and b/css/fonts/fontello/font/fontello.woff differ diff --git a/css/fonts/fontello/font/fontello.woff2 b/css/fonts/fontello/font/fontello.woff2 new file mode 100644 index 0000000..7e794d7 Binary files /dev/null and b/css/fonts/fontello/font/fontello.woff2 differ diff --git a/css/fonts/hk-grotesk/_notes/dwsync.xml b/css/fonts/hk-grotesk/_notes/dwsync.xml new file mode 100644 index 0000000..f1d686e --- /dev/null +++ b/css/fonts/hk-grotesk/_notes/dwsync.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/css/fonts/hk-grotesk/font/_notes/dwsync.xml b/css/fonts/hk-grotesk/font/_notes/dwsync.xml new file mode 100644 index 0000000..d9b0868 --- /dev/null +++ b/css/fonts/hk-grotesk/font/_notes/dwsync.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/css/fonts/hk-grotesk/font/hkgrotesk-bold-webfont.woff b/css/fonts/hk-grotesk/font/hkgrotesk-bold-webfont.woff new file mode 100644 index 0000000..35002c3 Binary files /dev/null and b/css/fonts/hk-grotesk/font/hkgrotesk-bold-webfont.woff differ diff --git a/css/fonts/hk-grotesk/font/hkgrotesk-bold-webfont.woff2 b/css/fonts/hk-grotesk/font/hkgrotesk-bold-webfont.woff2 new file mode 100644 index 0000000..b47714a Binary files /dev/null and b/css/fonts/hk-grotesk/font/hkgrotesk-bold-webfont.woff2 differ diff --git a/css/fonts/hk-grotesk/font/hkgrotesk-bolditalic-webfont.woff b/css/fonts/hk-grotesk/font/hkgrotesk-bolditalic-webfont.woff new file mode 100644 index 0000000..0924646 Binary files /dev/null and b/css/fonts/hk-grotesk/font/hkgrotesk-bolditalic-webfont.woff differ diff --git a/css/fonts/hk-grotesk/font/hkgrotesk-bolditalic-webfont.woff2 b/css/fonts/hk-grotesk/font/hkgrotesk-bolditalic-webfont.woff2 new file mode 100644 index 0000000..25ad7fb Binary files /dev/null and b/css/fonts/hk-grotesk/font/hkgrotesk-bolditalic-webfont.woff2 differ diff --git a/css/fonts/hk-grotesk/font/hkgrotesk-italic-webfont.woff b/css/fonts/hk-grotesk/font/hkgrotesk-italic-webfont.woff new file mode 100644 index 0000000..586427e Binary files /dev/null and b/css/fonts/hk-grotesk/font/hkgrotesk-italic-webfont.woff differ diff --git a/css/fonts/hk-grotesk/font/hkgrotesk-italic-webfont.woff2 b/css/fonts/hk-grotesk/font/hkgrotesk-italic-webfont.woff2 new file mode 100644 index 0000000..6f77961 Binary files /dev/null and b/css/fonts/hk-grotesk/font/hkgrotesk-italic-webfont.woff2 differ diff --git a/css/fonts/hk-grotesk/font/hkgrotesk-light-webfont.woff b/css/fonts/hk-grotesk/font/hkgrotesk-light-webfont.woff new file mode 100644 index 0000000..390d95a Binary files /dev/null and b/css/fonts/hk-grotesk/font/hkgrotesk-light-webfont.woff differ diff --git a/css/fonts/hk-grotesk/font/hkgrotesk-light-webfont.woff2 b/css/fonts/hk-grotesk/font/hkgrotesk-light-webfont.woff2 new file mode 100644 index 0000000..5bd874f Binary files /dev/null and b/css/fonts/hk-grotesk/font/hkgrotesk-light-webfont.woff2 differ diff --git a/css/fonts/hk-grotesk/font/hkgrotesk-lightitalic-webfont.woff b/css/fonts/hk-grotesk/font/hkgrotesk-lightitalic-webfont.woff new file mode 100644 index 0000000..c407daf Binary files /dev/null and b/css/fonts/hk-grotesk/font/hkgrotesk-lightitalic-webfont.woff differ diff --git a/css/fonts/hk-grotesk/font/hkgrotesk-lightitalic-webfont.woff2 b/css/fonts/hk-grotesk/font/hkgrotesk-lightitalic-webfont.woff2 new file mode 100644 index 0000000..824528d Binary files /dev/null and b/css/fonts/hk-grotesk/font/hkgrotesk-lightitalic-webfont.woff2 differ diff --git a/css/fonts/hk-grotesk/font/hkgrotesk-medium-webfont.woff b/css/fonts/hk-grotesk/font/hkgrotesk-medium-webfont.woff new file mode 100644 index 0000000..77f1306 Binary files /dev/null and b/css/fonts/hk-grotesk/font/hkgrotesk-medium-webfont.woff differ diff --git a/css/fonts/hk-grotesk/font/hkgrotesk-medium-webfont.woff2 b/css/fonts/hk-grotesk/font/hkgrotesk-medium-webfont.woff2 new file mode 100644 index 0000000..53752b3 Binary files /dev/null and b/css/fonts/hk-grotesk/font/hkgrotesk-medium-webfont.woff2 differ diff --git a/css/fonts/hk-grotesk/font/hkgrotesk-mediumitalic-webfont.woff b/css/fonts/hk-grotesk/font/hkgrotesk-mediumitalic-webfont.woff new file mode 100644 index 0000000..958fc47 Binary files /dev/null and b/css/fonts/hk-grotesk/font/hkgrotesk-mediumitalic-webfont.woff differ diff --git a/css/fonts/hk-grotesk/font/hkgrotesk-mediumitalic-webfont.woff2 b/css/fonts/hk-grotesk/font/hkgrotesk-mediumitalic-webfont.woff2 new file mode 100644 index 0000000..ae8b897 Binary files /dev/null and b/css/fonts/hk-grotesk/font/hkgrotesk-mediumitalic-webfont.woff2 differ diff --git a/css/fonts/hk-grotesk/font/hkgrotesk-regular-webfont.woff b/css/fonts/hk-grotesk/font/hkgrotesk-regular-webfont.woff new file mode 100644 index 0000000..65d781e Binary files /dev/null and b/css/fonts/hk-grotesk/font/hkgrotesk-regular-webfont.woff differ diff --git a/css/fonts/hk-grotesk/font/hkgrotesk-regular-webfont.woff2 b/css/fonts/hk-grotesk/font/hkgrotesk-regular-webfont.woff2 new file mode 100644 index 0000000..bdb3a00 Binary files /dev/null and b/css/fonts/hk-grotesk/font/hkgrotesk-regular-webfont.woff2 differ diff --git a/css/fonts/hk-grotesk/font/hkgrotesk-semibold-webfont.woff b/css/fonts/hk-grotesk/font/hkgrotesk-semibold-webfont.woff new file mode 100644 index 0000000..5987174 Binary files /dev/null and b/css/fonts/hk-grotesk/font/hkgrotesk-semibold-webfont.woff differ diff --git a/css/fonts/hk-grotesk/font/hkgrotesk-semibold-webfont.woff2 b/css/fonts/hk-grotesk/font/hkgrotesk-semibold-webfont.woff2 new file mode 100644 index 0000000..7cbca2c Binary files /dev/null and b/css/fonts/hk-grotesk/font/hkgrotesk-semibold-webfont.woff2 differ diff --git a/css/fonts/hk-grotesk/font/hkgrotesk-semibolditalic-webfont.woff b/css/fonts/hk-grotesk/font/hkgrotesk-semibolditalic-webfont.woff new file mode 100644 index 0000000..9c41ce7 Binary files /dev/null and b/css/fonts/hk-grotesk/font/hkgrotesk-semibolditalic-webfont.woff differ diff --git a/css/fonts/hk-grotesk/font/hkgrotesk-semibolditalic-webfont.woff2 b/css/fonts/hk-grotesk/font/hkgrotesk-semibolditalic-webfont.woff2 new file mode 100644 index 0000000..9c613f9 Binary files /dev/null and b/css/fonts/hk-grotesk/font/hkgrotesk-semibolditalic-webfont.woff2 differ diff --git a/css/fonts/hk-grotesk/style.css b/css/fonts/hk-grotesk/style.css new file mode 100644 index 0000000..6636f15 --- /dev/null +++ b/css/fonts/hk-grotesk/style.css @@ -0,0 +1,85 @@ +/* + HK Grotesk Font Free by Hanken Design Co. + License : OFL (SIL Open Font License) + downloaded from : https://fontlibrary.org/en/font/hk-grotesk +*/ + +@font-face { + font-family: 'HK Grotesk'; + src: url('font/hkgrotesk-light-webfont.woff2') format('woff2'), + url('font/hkgrotesk-light-webfont.woff') format('woff'); + font-weight: 300; + font-style: normal; +} + +@font-face { + font-family: 'HK Grotesk'; + src: url('font/hkgrotesk-lightitalic-webfont.woff2') format('woff2'), + url('font/hkgrotesk-lightitalic-webfont.woff') format('woff'); + font-weight: 300; + font-style: italic; +} + +@font-face { + font-family: 'HK Grotesk'; + src: url('font/hkgrotesk-regular-webfont.woff2') format('woff2'), + url('font/hkgrotesk-regular-webfont.woff') format('woff'); + font-weight: 400; + font-style: normal; +} + +@font-face { + font-family: 'HK Grotesk'; + src: url('font/hkgrotesk-italic-webfont.woff2') format('woff2'), + url('font/hkgrotesk-italic-webfont.woff') format('woff'); + font-weight: 400; + font-style: italic; +} + +@font-face { + font-family: 'HK Grotesk'; + src: url('font/hkgrotesk-medium-webfont.woff2') format('woff2'), + url('font/hkgrotesk-medium-webfont.woff') format('woff'); + font-weight: 500; + font-style: normal; +} + +@font-face { + font-family: 'HK Grotesk'; + src: url('font/hkgrotesk-mediumitalic-webfont.woff2') format('woff2'), + url('font/hkgrotesk-mediumitalic-webfont.woff') format('woff'); + font-weight: 500; + font-style: italic; +} + +@font-face { + font-family: 'HK Grotesk'; + src: url('font/hkgrotesk-semibold-webfont.woff2') format('woff2'), + url('font/hkgrotesk-semibold-webfont.woff') format('woff'); + font-weight: 600; + font-style: normal; +} + +@font-face { + font-family: 'HK Grotesk'; + src: url('font/hkgrotesk-semibolditalic-webfont.woff2') format('woff2'), + url('font/hkgrotesk-semibolditalic-webfont.woff') format('woff'); + font-weight: 600; + font-style: italic; +} + +@font-face { + font-family: 'HK Grotesk'; + src: url('font/hkgrotesk-bold-webfont.woff2') format('woff2'), + url('font/hkgrotesk-bold-webfont.woff') format('woff'); + font-weight: 700; + font-style: normal; +} + +@font-face { + font-family: 'HK Grotesk'; + src: url('font/hkgrotesk-bolditalic-webfont.woff2') format('woff2'), + url('font/hkgrotesk-bolditalic-webfont.woff') format('woff'); + font-weight: 700; + font-style: italic; +} \ No newline at end of file diff --git a/css/main.css b/css/main.css new file mode 100644 index 0000000..62a858d --- /dev/null +++ b/css/main.css @@ -0,0 +1,1080 @@ +/* -------------------------------------------- + + main.css : this is the main style file of the theme. + + Project : LazyGuy + Author : pixelwars + Url : pixelwars.org + + --- + + CONTEXT: + + 1. BASE (@base) + 2. TYPOGRAPHY (@typo) + 3. HELPERS (@helpers) + 4. LAYOUT (@layout) + 5. ELEMENTS (@elements) + 5. MEDIA QUERIES (@media) + + -------------------------------------------- */ + + + + +/* -------------------------------------------- + + 1. BASE (@base) - base html elements + + -------------------------------------------- */ +html, +body { + width: 100%; height: 100%; padding: 0; margin: 0; box-sizing: border-box; + } +* { + box-sizing: border-box; + } +body { + font-family: 'HK Grotesk', sans-serif; color: #222; background-color: #fff; font-size: 14px; line-height: 1.7; display: flex; align-items: center; + } +img { + max-width: 100%; height: auto; + } +::selection { + text-shadow: none; color: #000; background: #FFF9CD; + } +::-moz-selection { + text-shadow: none; color: #000; background: #FFF9CD; + } +/* Loader */ +html { + overflow: hidden; + } +html.loaded { + overflow: auto; + } +html:after { + content: ""; position: fixed; z-index: 1000; top: 0; left: 0; right: 0; bottom: 0; background: white; background-image: url(https://clifford.nyc3.cdn.digitaloceanspaces.com/public/tyclifford.com/images/preloader.gif); background-position: center center; background-repeat: no-repeat; background-size: 48px 48px; + } +html.loaded:after { + display: none; + } +body { + transition: all .6s cubic-bezier(0.77, 0, 0.175, 1); opacity: 0; + } +html.loaded body { + opacity: 1; + } + +/* -------------------------------------------- + + 2. TYPOGRAPHY (@typo) - typographic styles + + -------------------------------------------- */ +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: 'HK Grotesk', sans-serif; font-weight: 700; font-style: normal; margin: 1em 0 .66em 0; + } +h1:first-child, +h2:first-child, +h3:first-child, +h4:first-child, +h5:first-child, +h6:first-child { + margin-top: 0; + } +h1 { + font-size: 6vw; line-height: 1.1; margin-top: 0; + } +h2 { + font-size: 1.8em; line-height: 1.3; + } +h3 { + font-size: 1.25em; line-height: 1.3; + } +.profile-image + h1 { + margin-top: 28px; + } +h4 { + font-size: 12px; text-transform: uppercase; letter-spacing: 5px; + } +p:last-child { + margin-bottom: 0; + } +/* LIGHT TEXT */ +.is-text-light { + color: #fff; + } +.is-text-light a { + color: #fff; + } + + + +/* -------------------------------------------- + + 3. HELPERS (@helpers) - shorthand helper styles + + -------------------------------------------- */ +.rounded { + border-radius: 50%; + } +.soft-rounded { + border-radius: 8px; + } +.is-text-align-center { + text-align: center; + } +.is-text-align-right { + text-align: right; + } +.uppercase { + text-transform: uppercase; + } +.bold { + font-weight: bold; + } + +/* SMALL TYPO */ +.is-small-typo h1 { + font-size: 24px; line-height: 1.3; + } +.is-small-typo { + font-size: 14px; + } + +/* MEDIUM TYPO */ +.is-medium-typo h1 { + font-size: 36px; line-height: 1.2; + } +.is-medium-typo { + font-size: 15px; + } + + + +/* -------------------------------------------- + + 2. LAYOUT (@layout) - layout styles + + -------------------------------------------- */ + +/* CONTENT */ +.content-wrap { + width: 80%; margin: 0 auto; max-height: 100%; position: relative; z-index: 20; + } +.content { + padding: 3em 0; + } + +/* MEDIA */ +.media { + position: fixed; width: 100%; height: 100%; background-position: center center; background-size: cover; background-repeat: no-repeat; + } + +/* MASKED */ +.is-masked-light .media:after, +.is-masked-dark .media:after { + content: ""; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(255, 255, 255, 0.79); z-index:1; + } +/* MASKED DARK */ +.is-masked-dark .media:after { + background: rgba(35, 35, 35, 0.4); + } +/* MASKED GRADIENT BLUE */ +.is-masked-gradient-blue .media:after { + background-image: linear-gradient(60deg, #3d3393 0%, #2b76b9 37%, #2cacd1 65%, #35eb93 100%); opacity: .9; + } +/* MASKED GRADIENT PINK */ +.is-masked-gradient-pink .media:after { + background-image: linear-gradient(to right, #b8cbb8 0%, #b8cbb8 0%, #b465da 0%, #cf6cc9 33%, #ee609c 66%, #ee609c 100%); opacity: .9; + } +/* MASKED GRADIENT PURPLE */ +.is-masked-gradient-purple .media:after { + background-image: linear-gradient(to top, #9795f0 0%, #fbc8d4 100%); opacity: .9; + } +/* MASKED GRADIENT RED */ +.is-masked-gradient-red .media:after { + background-image: linear-gradient(to top, #ff0844 0%, #ffb199 100%); opacity: .9; + } +/* MASKED GRADIENT GREEN */ +.is-masked-gradient-green .media:after { + background-image: linear-gradient(180deg, #2af598 0%, #009efd 100%); opacity: .9; + } + + +/* PROFILE IMAGE */ +.profile-image { + max-height: 92px; + } +.profile-image.is-outside { + margin-top: -50%; margin-bottom: 0; + } + +/* LOGO */ +.logo { + margin-bottom: 24px; + } + + + + +/* -------------------------------------------- + + 5. ELEMENTS (@elements) - common used elements + + -------------------------------------------- */ + +/* SOCIAL LINKS */ +.social-link { + display: inline-block; margin: 8px 2px 8px 0; text-decoration: none; user-select: none; + } +.social-link:active { + transform: scale(0.8); + } +.social-link:before { + display: inline-block; padding: 0; text-align: center; font-size: 14px; font-family: "fontello"; width: 32px; line-height: 32px; color: #555; border: 1px solid #eee; border-radius: 50%; text-decoration: none; transition: all 0.3s; + } +html a.social-link:hover:before { + color: #fff; background: #444; border-color: transparent; + } +.social-link.facebook:before { + content: '\e825'; color: #3c5fac; border-color: #3c5fac; + } +.social-link.twitter:before { + content: '\e826'; color: #5ec3df; border-color: #5ec3df; + } +.social-link.flickr:before { + content: '\e835'; color: #FF0084; border-color: #FF0084; + } +.social-link.rss:before { + content: '\e82f'; color: #ff9900; border-color: #ff9900; + } +.social-link.dribbble:before { + content: '\e82c'; color: #EA4C89; border-color: #EA4C89; + } +.social-link.lastfm:before { + content: '\e82e'; color: #D51007; border-color: #D51007; + } +.social-link.linkedin:before { + content: '\e827'; color: #2089b5; border-color: #2089b5; + } +.social-link.vimeo:before { + content: '\e830'; color: #0dadd6; border-color: #0dadd6; + } +.social-link.google-plus:before { + content: '\e801'; color: #db4437; border-color: #db4437; + } +.social-link.forrst:before { + content: '\e831'; color: #5b9a68; border-color: #5b9a68; + } +.social-link.skype:before { + content: '\e832'; color: #00aff0; border-color: #00aff0; + } +.social-link.tumblr:before { + content: '\e836'; color: #2C4762; border-color: #2C4762; + } +.social-link.behance:before { + content: '\e82b'; color: #3878F6; border-color: #3878F6; + } +.social-link.blogger:before { + content: '\e837'; color: #fc9947; border-color: #fc9947; + } +.social-link.delicious:before { + content: '\e838'; color: #3274d1; border-color: #3274d1; + } +.social-link.digg:before { + content: '\e839'; color: #205891; border-color: #205891; + } +.social-link.github:before { + content: '\e82a'; color: #222; border-color: #222; + } +.social-link.wordpress:before { + content: '\e83b'; color: #0083b3; border-color: #0083b3; + } +.social-link.youtube:before { + content: "\e834"; color: #c8312b; border-color: #c8312b; + } +.social-link.pinterest:before { + content: "\e829"; color: #cb2027; border-color: #cb2027; + } +.social-link.instagram:before { + content: "\e8cc"; color: #e1306c; border-color: #e1306c; + } +.social-link.stack-overflow:before { + content: "\e83c"; color: #F90; border-color: #F90; + } +.social-link.foursquare:before { + content: "\e83d"; color: #009FE0; border-color: #009FE0; + } +.social-link.xing:before { + content: "\e83e"; color: #006567; border-color: #006567; + } +.social-link.weibo:before { + content: "\e83f"; color: #E64141; border-color: #E64141; + } +.social-link.soundcloud:before { + content: "\e840"; color: #FA3219; border-color: #FA3219; + } +.social-link.fivehundredpx:before { + content: "\e841"; color: #111; border-color: #111; + } +.social-link.slideshare:before { + content: "\e842"; color: #ED9D2C; border-color: #ED9D2C; + } +.social-link.vine:before { + content: "\e863"; color: #00BF8F; border-color: #00BF8F; + } +.social-link.vkontakte:before { + content: "\e846"; color: #6383A8; border-color: #6383A8; + } +.social-link.paypal:before { + content: "\e8b9"; color: #013791; border-color: #013791; + } +.social-link.spotify:before { + content: "\e803"; color: #85BB24; border-color: #85BB24; + } +.social-link.snapchat:before { + content: "\f2ac"; color: #fffc00; border-color: #fffc00; + } +.social-link.imdb:before { + content: "\f2d8"; color: #E6B91E; border-color: #E6B91E; + } +.social-link.email:before { + content: "\e87e"; color: #222; border-color: #222; + } +.social-link.facebook:hover:before { + background-color: #3c5fac; + } +.social-link.twitter:hover:before { + background-color: #5ec3df; + } +.social-link.flickr:hover:before { + background-color: #FF0084; + } +.social-link.rss:hover:before { + background-color: #ff9900; + } +.social-link.dribbble:hover:before { + background-color: #EA4C89; + } +.social-link.lastfm:hover:before { + background-color: #D51007; + } +.social-link.linkedin:hover:before { + background-color: #2089b5; + } +.social-link.vimeo:hover:before { + background-color: #0dadd6; + } +.social-link.google-plus:hover:before { + background-color: #db4437; + } +.social-link.forrst:hover:before { + background-color: #5b9a68; + } +.social-link.skype:hover:before { + background-color: #00aff0; + } +.social-link.picassa:hover:before { + background-color: #ffd34e; + } +.social-link.youtube:hover:before { + background-color: #c8312b; + } +.social-link.pinterest:hover:before { + background-color: #cb2027; + } +.social-link.tumblr:hover:before { + background-color: #2C4762; + } +.social-link.behance:hover:before { + background-color: #3878F6; + } +.social-link.blogger:hover:before { + background-color: #fc9947; + } +.social-link.delicious:hover:before { + background-color: #3274d1; + } +.social-link.digg:hover:before { + background-color: #205891; + } +.social-link.friendfeed:hover:before { + background-color: #2f72c4; + } +.social-link.github:hover:before { + background-color: #222; + } +.social-link.wordpress:hover:before { + background-color: #0083b3; + } +.social-link.instagram:hover:before { + background-color: #e1306c; + } +.social-link.stack-overflow:hover:before { + background-color: #F90; + } +.social-link.foursquare:hover:before { + background-color: #009FE0; + } +.social-link.xing:hover:before { + background-color: #006567; + } +.social-link.weibo:hover:before { + background-color: #E64141; + } +.social-link.soundcloud:hover:before { + background-color: #FA3219; + } +.social-link.fivehundredpx:hover:before { + background-color: #222; + } +.social-link.slideshare:hover:before { + background-color: #ED9D2C; + } +.social-link.vine:hover:before { + background-color: #00BF8F; + } +.social-link.vkontakte:hover:before { + background-color: #6383A8; + } +.social-link.paypal:hover:before { + background-color: #013791; + } +.social-link.spotify:hover:before { + background-color: #85BB24; + } +.social-link.snapchat:hover:before { + background-color: #fffc00; + } +.social-link.imdb:hover:before { + background-color: #E6B91E; + } +.social-link.email:hover:before { + background-color: #222; + } + +/* Big Social Icons */ +.is-social-big .social-link:before { + font-size: 16px; width: 42px; line-height: 42px; + } +/* Minmal Social Icons */ +.is-social-minimal .social-link:before { + border: none !important; + } +/* Minmal Social Icons */ +.is-social-minimal-dark .social-link, +.is-social-minimal-light .social-link, +.is-social-light-borders .social-link, +.is-social-dark-borders .social-link, +.is-social-solid .social-link { + color: inherit; + } +/* Minimal Dark Social Icons */ +.is-social-minimal-dark .social-link:before { + border: none !important; color: inherit; + } +/* Minimal Light Social Icons */ +.is-social-minimal-light .social-link:before { + border: none !important; color: rgba(0, 0, 0, 0.25); + } +.is-text-light .is-social-minimal-light .social-link:not(:hover):before { + color: rgba(255, 255, 255, 0.3); + } +/* Solid Social Icons */ +.is-social-solid .social-link:before { + border: none !important; color: inherit; + } +.is-social-solid .social-link:not(:hover):before { + background: rgba(0, 0, 0, 0.04); + } +.is-text-light .is-social-solid .social-link:not(:hover):before { + background: rgba(255, 255, 255, 0.08); + } +/* Light Borders Social Icons */ +.is-social-light-borders .social-link:before { + border-color: rgba(0, 0, 0, 0.09) !important; color: inherit; + } +.is-text-light .is-social-light-borders .social-link:before { + border-color: rgba(255, 255, 255, 0.13) !important; + } +/* Dark Borders Social Icons */ +.is-social-dark-borders .social-link:before { + border-color: rgba(0, 0, 0, 0.75) !important; color: inherit; + } +.is-text-light .is-social-dark-borders .social-link:before { + border-color: rgba(255, 255, 255, 0.8) !important; + } +.is-social-dark-borders .social-link:hover:before { + border-color: transparent !important; + } + + + + +/* LINK HOVER EFFECTS */ + +/* LINK 1 */ +.link-1 { + position: relative; text-decoration: none; display: inline-block; color: inherit; padding: 0 1px; transition: color ease 0.3s; + } +.link-1:after { + content: ''; position: absolute; z-index: -1; width: 100%; height: 6%; min-height: 2px; left: 0; bottom: 0; background-color: #222; transition: all ease 0.3s; + } +.link-1:hover { + color: #fff; + } +.link-1:hover:after { + height: 100%; + } +/* light links */ +.link-1.light:after { + background: #fff; + } +.link-1.light:hover { + color: #111; + } +/* blue links */ +.link-1.blue:after { + background: #0100ff; + } +/* green links */ +.link-1.green:after { + background: #00b388; + } +/* red links */ +.link-1.red:after { + background: #ff322e; + } +/* yellow links */ +.link-1.yellow:after { + background: #ffcc2f; + } +/* pink links */ +.link-1.pink:after { + background: #f94877; + } +/* purple links */ +.link-1.purple:after { + background: #554488; + } +/* tango links */ +.link-1.tango:after { + background: #E1523D; + } +/* aqua links */ +.link-1.aqua:after { + background: #0099ff; + } +/* navy links */ +.link-1.navy:after { + background: #414f5a; + } +/* leather links */ +.link-1.leather:after { + background: #AB9878; + } +/* azure links */ +.link-1.azure:after { + background: #02ACAB; + } +/* peach links */ +.link-1.peach:after { + background: #FEA680; + } +/* hot-pink links */ +.link-1.hot-pink:after { + background: #FE65B7; + } +/* coral links */ +.link-1.coral:after { + background: #F63341; + } +/* grey links */ +.link-1.grey:after { + background: #AAB8C1; + } + +/* LINK 2 */ +.link-2 { + position: relative; text-decoration: none; display: inline-block; color: inherit; padding: 0 1px; transition: color ease 0.3s; + } +.link-2:before, +.link-2:after { + content: ''; position: absolute; background-color: #222; z-index: -1; height: 2px; + } +.link-2:before { + width: 0%; left: 0; bottom: 0; transition: width ease 0.4s; + } +.link-2:after { + width: 100%; left: 0; bottom: 0; transition: all ease 0.6s; + } +.link-2:hover:before { + width: 100%; + } +.link-2:hover:after { + left: 100%; width: 0%; transition: all ease 0.2s; + } +/* light links */ +.link-2.light:before, +.link-2.light:after { + background: #fff; color: #111; + } +/* blue links */ +.link-2.blue:before, +.link-2.blue:after { + background: #0100ff; + } +/* green links */ +.link-2.green:before, +.link-2.green:after { + background: #00b388; + } +/* red links */ +.link-2.red:before, +.link-2.red:after { + background: #ff322e; + } +/* yellow links */ +.link-2.yellow:before, +.link-2.yellow:after { + background: #ffcc2f; + } +/* pink links */ +.link-2.pink:before, +.link-2.pink:after { + background: #f94877; + } +/* purple links */ +.link-2.purple:before, +.link-2.purple:after { + background: #554488; + } +/* tango links */ +.link-2.tango:before, +.link-2.tango:after { + background: #E1523D; + } +/* aqua links */ +.link-2.aqua:before, +.link-2.aqua:after { + background: #0099ff; + } +/* navy links */ +.link-2.navy:before, +.link-2.navy:after { + background: #414f5a; + } +/* leather links */ +.link-2.leather:before, +.link-2.leather:after { + background: #AB9878; + } +/* azure links */ +.link-2.azure:before, +.link-2.azure:after { + background: #02ACAB; + } +/* peach links */ +.link-2.peach:before, +.link-2.peach:after { + background: #FEA680; + } +/* hot-pink links */ +.link-2.hot-pink:before, +.link-2.hot-pink:after { + background: #FE65B7; + } +/* coral links */ +.link-2.coral:before, +.link-2.coral:after { + background: #F63341; + } +/* grey links */ +.link-2.grey:before, +.link-2.grey:after { + background: #AAB8C1; + } + + + +/* BUTTONS */ +.button { + border: 2px solid #222; font-family: inherit; font-size: 16px; line-height: 1; color: #222; text-decoration: none; background: none; cursor: pointer; padding: 16px 32px; margin: 10px 20px 0 0; display: inline-block; outline: none; position: relative; transition: all 0.3s; overflow: hidden; + } +.button:only-child { + margin-right: 0px; + } +.button.small { + font-size: 13px; padding: 8px 20px; + } +.button.rounded { + border-radius: 50px; + } +.button.soft-rounded { + border-radius: 5px; + } +.button.rounded-soft { + border-radius: 4px; + } +.button:after { + width: 0%; height: 100%; top: 0; left: 0; background: #222; content: ''; position: absolute; z-index: -1; transition: all 0.3s; + } +.button:hover, +.button:active { + color: #fff !important; + } +.button:active { + transition: all 0.1s; transform: scale(0.9); outline: 0; + } +.button:hover:after, +.button:active:after { + width: 100%; + } +/* light buttons */ +.button.light { + border-color: #fff; + } +.button.light:after { + background: #fff; + } +.button.light:hover, +.button.light:active { + color: #111 !important; + } +/* blue buttons */ +.button.blue { + border-color: #0100ff; color: #0100ff; + } +.button.blue:after { + background: #0100ff; + } +/* green buttons */ +.button.green { + border-color: #00b388; color: #00b388; + } +.button.green:after { + background: #00b388; + } +/* red buttons */ +.button.red { + border-color: #ff322e; color: #ff322e; + } +.button.red:after { + background: #ff322e; + } +/* yellow buttons */ +.button.yellow { + border-color: #ffcc2f; color: #ffcc2f; + } +.button.yellow:after { + background: #ffcc2f; + } +/* pink buttons */ +.button.pink { + border-color: #f94877; color: #f94877; + } +.button.pink:after { + background: #f94877; + } +/* purple buttons */ +.button.purple { + border-color: #554488; color: #554488; + } +.button.purple:after { + background: #554488; + } +/* tango buttons */ +.button.tango { + border-color: #E1523D; color: #E1523D; + } +.button.tango:after { + background: #E1523D; + } +/* aqua buttons */ +.button.aqua { + border-color: #0099ff; color: #0099ff; + } +.button.aqua:after { + background: #0099ff; + } +/* navy buttons */ +.button.navy { + border-color: #414f5a; color: #414f5a; + } +.button.navy:after { + background: #414f5a; + } +/* leather buttons */ +.button.leather { + border-color: #AB9878; color: #AB9878; + } +.button.leather:after { + background: #AB9878; + } +/* azure buttons */ +.button.azure { + border-color: #02ACAB; color: #02ACAB; + } +.button.azure:after { + background: #02ACAB; + } +/* peach buttons */ +.button.peach { + border-color: #FEA680; color: #FEA680; + } +.button.peach:after { + background: #FEA680; + } +/* hot-pink buttons */ +.button.hot-pink { + border-color: #FE65B7; color: #FE65B7; + } +.button.hot-pink:after { + background: #FE65B7; + } +/* coral buttons */ +.button.coral { + border-color: #F63341; color: #F63341; + } +.button.coral:after { + background: #F63341; + } +/* grey buttons */ +.button.grey { + border-color: #AAB8C1; color: #AAB8C1; + } +.button.grey:after { + background: #AAB8C1; + } +/* icons in buttons */ +.button i { + display: inline-block; margin-right: 5px; + } +.button i.right { + margin-right: 0; margin-left: 5px; + } +.button i:before { + font-size: 120%; + } + + +/* HIGHLIGHT TEXT */ +.highlight { + display: inline-block; padding: 0 10px; color: #fff; background: #111; + } +/* light highlights */ +.highlight.light { + background: #fff; color: #111; + } +/* blue highlights */ +.highlight.blue { + background: #0100ff; + } +/* green highlights */ +.highlight.green { + background: #00b388; + } +/* red highlights */ +.highlight.red { + background: #ff322e; + } +/* yellow highlights */ +.highlight.yellow { + background: #ffcc2f; + } +/* pink highlights */ +.highlight.pink { + background: #f94877; + } +/* purple highlights */ +.highlight.purple { + background: #554488; + } +/* tango highlights */ +.highlight.tango { + background: #E1523D; + } +/* aqua highlights */ +.highlight.aqua { + background: #0099ff; + } +/* navy highlights */ +.highlight.navy { + background: #414f5a; + } +/* leather highlights */ +.highlight.leather { + background: #AB9878; + } +/* azure highlights */ +.highlight.azure { + background: #02ACAB; + } +/* peach highlights */ +.highlight.peach { + background: #FEA680; + } +/* hot-pink highlights */ +.highlight.hot-pink { + background: #FE65B7; + } +/* coral highlights */ +.highlight.coral { + background: #F63341; + } +/* grey highlights */ +.highlight.grey { + background: #AAB8C1; + } + + +/* COLORED TEXT */ +/* blue text */ +.text.blue { + color: #0100ff; + } +/* green text */ +.text.green { + color: #00b388; + } +/* red text */ +.text.red { + color: #ff322e; + } +/* yellow text */ +.text.yellow { + color: #ffcc2f; + } +/* pink text */ +.text.pink { + color: #f94877; + } +/* purple text */ +.text.purple { + color: #554488; + } +/* tango text */ +.text.tango { + color: #E1523D; + } +/* aqua text */ +.text.aqua { + color: #0099ff; + } +/* navy text */ +.text.navy { + color: #414f5a; + } +/* leather text */ +.text.leather { + color: #AB9878; + } +/* azure text */ +.text.azure { + color: #02ACAB; + } +/* peach text */ +.text.peach { + color: #FEA680; + } +/* hot-pink text */ +.text.hot-pink { + color: #FE65B7; + } +/* coral text */ +.text.coral { + color: #F63341; + } +/* grey text */ +.text.grey { + color: #AAB8C1; + } + +/* TYPED TEXT */ +#typed-strings { + display: none; + } +.typed-cursor{ + opacity: 1; -webkit-animation: blink 0.7s infinite; animation: blink 0.7s infinite; + } +@-webkit-keyframes blink{ + 0% { opacity:1; } + 50% { opacity:0; } + 100% { opacity:1; } + } +@keyframes blink{ + 0% { opacity:1; } + 50% { opacity:0; } + 100% { opacity:1; } + } + + +/* -------------------------------------------- + + 6. MEDIA QUERIES (@media) - base html elements + + -------------------------------------------- */ + +/* TABLETS */ +@media screen and (min-width: 768px) { + + body { + font-size: 19px; + } + + .profile-image { + max-height: 128px; + } + .profile-image.small { + max-height: 92px; + } + .profile-image.big { + max-height: 192px; + } + + h1 { + font-size: 4vw; line-height: 1; + } + + + /* MEDIA */ + .media { + position: fixed; top: 0; left: 0; width: 100%; height: 100%; bottom: 0; + } + +} + +/* DESKTOPS */ +@media screen and (min-width: 1200px) { + + .content-wrap { + width: 50%; + } + .content { + padding: 4em 0; + } + + /* BUTTONS */ + .button.huge { + font-size: 20px; padding: 22px 40px; + } + +} + +/* DESKTOPS HIGH RES */ +@media screen and (min-width: 1600px) { + + .content-wrap { + width: 50%; + } + .content { + padding: 6em 0; + } + +} \ No newline at end of file diff --git a/functions.php b/functions.php new file mode 100644 index 0000000..b9a1f0a --- /dev/null +++ b/functions.php @@ -0,0 +1,27 @@ + diff --git a/i/.gitignore b/i/.gitignore new file mode 100644 index 0000000..1e1ed2c --- /dev/null +++ b/i/.gitignore @@ -0,0 +1 @@ +config-custom.php \ No newline at end of file diff --git a/i/LICENSE b/i/LICENSE new file mode 100644 index 0000000..e7867ea --- /dev/null +++ b/i/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Painted Sky Studios + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/i/Parsedown.php b/i/Parsedown.php new file mode 100644 index 0000000..1b9d6d5 --- /dev/null +++ b/i/Parsedown.php @@ -0,0 +1,1712 @@ +DefinitionData = array(); + + # standardize line breaks + $text = str_replace(array("\r\n", "\r"), "\n", $text); + + # remove surrounding line breaks + $text = trim($text, "\n"); + + # split text into lines + $lines = explode("\n", $text); + + # iterate through lines to identify blocks + $markup = $this->lines($lines); + + # trim line breaks + $markup = trim($markup, "\n"); + + return $markup; + } + + # + # Setters + # + + function setBreaksEnabled($breaksEnabled) + { + $this->breaksEnabled = $breaksEnabled; + + return $this; + } + + protected $breaksEnabled; + + function setMarkupEscaped($markupEscaped) + { + $this->markupEscaped = $markupEscaped; + + return $this; + } + + protected $markupEscaped; + + function setUrlsLinked($urlsLinked) + { + $this->urlsLinked = $urlsLinked; + + return $this; + } + + protected $urlsLinked = true; + + function setSafeMode($safeMode) + { + $this->safeMode = (bool) $safeMode; + + return $this; + } + + protected $safeMode; + + protected $safeLinksWhitelist = array( + 'http://', + 'https://', + 'ftp://', + 'ftps://', + 'mailto:', + 'data:image/png;base64,', + 'data:image/gif;base64,', + 'data:image/jpeg;base64,', + 'irc:', + 'ircs:', + 'git:', + 'ssh:', + 'news:', + 'steam:', + ); + + # + # Lines + # + + protected $BlockTypes = array( + '#' => array('Header'), + '*' => array('Rule', 'List'), + '+' => array('List'), + '-' => array('SetextHeader', 'Table', 'Rule', 'List'), + '0' => array('List'), + '1' => array('List'), + '2' => array('List'), + '3' => array('List'), + '4' => array('List'), + '5' => array('List'), + '6' => array('List'), + '7' => array('List'), + '8' => array('List'), + '9' => array('List'), + ':' => array('Table'), + '<' => array('Comment', 'Markup'), + '=' => array('SetextHeader'), + '>' => array('Quote'), + '[' => array('Reference'), + '_' => array('Rule'), + '`' => array('FencedCode'), + '|' => array('Table'), + '~' => array('FencedCode'), + ); + + # ~ + + protected $unmarkedBlockTypes = array( + 'Code', + ); + + # + # Blocks + # + + protected function lines(array $lines) + { + $CurrentBlock = null; + + foreach ($lines as $line) + { + if (chop($line) === '') + { + if (isset($CurrentBlock)) + { + $CurrentBlock['interrupted'] = true; + } + + continue; + } + + if (strpos($line, "\t") !== false) + { + $parts = explode("\t", $line); + + $line = $parts[0]; + + unset($parts[0]); + + foreach ($parts as $part) + { + $shortage = 4 - mb_strlen($line, 'utf-8') % 4; + + $line .= str_repeat(' ', $shortage); + $line .= $part; + } + } + + $indent = 0; + + while (isset($line[$indent]) and $line[$indent] === ' ') + { + $indent ++; + } + + $text = $indent > 0 ? substr($line, $indent) : $line; + + # ~ + + $Line = array('body' => $line, 'indent' => $indent, 'text' => $text); + + # ~ + + if (isset($CurrentBlock['continuable'])) + { + $Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock); + + if (isset($Block)) + { + $CurrentBlock = $Block; + + continue; + } + else + { + if ($this->isBlockCompletable($CurrentBlock['type'])) + { + $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); + } + } + } + + # ~ + + $marker = $text[0]; + + # ~ + + $blockTypes = $this->unmarkedBlockTypes; + + if (isset($this->BlockTypes[$marker])) + { + foreach ($this->BlockTypes[$marker] as $blockType) + { + $blockTypes []= $blockType; + } + } + + # + # ~ + + foreach ($blockTypes as $blockType) + { + $Block = $this->{'block'.$blockType}($Line, $CurrentBlock); + + if (isset($Block)) + { + $Block['type'] = $blockType; + + if ( ! isset($Block['identified'])) + { + $Blocks []= $CurrentBlock; + + $Block['identified'] = true; + } + + if ($this->isBlockContinuable($blockType)) + { + $Block['continuable'] = true; + } + + $CurrentBlock = $Block; + + continue 2; + } + } + + # ~ + + if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted'])) + { + $CurrentBlock['element']['text'] .= "\n".$text; + } + else + { + $Blocks []= $CurrentBlock; + + $CurrentBlock = $this->paragraph($Line); + + $CurrentBlock['identified'] = true; + } + } + + # ~ + + if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type'])) + { + $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); + } + + # ~ + + $Blocks []= $CurrentBlock; + + unset($Blocks[0]); + + # ~ + + $markup = ''; + + foreach ($Blocks as $Block) + { + if (isset($Block['hidden'])) + { + continue; + } + + $markup .= "\n"; + $markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']); + } + + $markup .= "\n"; + + # ~ + + return $markup; + } + + protected function isBlockContinuable($Type) + { + return method_exists($this, 'block'.$Type.'Continue'); + } + + protected function isBlockCompletable($Type) + { + return method_exists($this, 'block'.$Type.'Complete'); + } + + # + # Code + + protected function blockCode($Line, $Block = null) + { + if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted'])) + { + return; + } + + if ($Line['indent'] >= 4) + { + $text = substr($Line['body'], 4); + + $Block = array( + 'element' => array( + 'name' => 'pre', + 'handler' => 'element', + 'text' => array( + 'name' => 'code', + 'text' => $text, + ), + ), + ); + + return $Block; + } + } + + protected function blockCodeContinue($Line, $Block) + { + if ($Line['indent'] >= 4) + { + if (isset($Block['interrupted'])) + { + $Block['element']['text']['text'] .= "\n"; + + unset($Block['interrupted']); + } + + $Block['element']['text']['text'] .= "\n"; + + $text = substr($Line['body'], 4); + + $Block['element']['text']['text'] .= $text; + + return $Block; + } + } + + protected function blockCodeComplete($Block) + { + $text = $Block['element']['text']['text']; + + $Block['element']['text']['text'] = $text; + + return $Block; + } + + # + # Comment + + protected function blockComment($Line) + { + if ($this->markupEscaped or $this->safeMode) + { + return; + } + + if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!') + { + $Block = array( + 'markup' => $Line['body'], + ); + + if (preg_match('/-->$/', $Line['text'])) + { + $Block['closed'] = true; + } + + return $Block; + } + } + + protected function blockCommentContinue($Line, array $Block) + { + if (isset($Block['closed'])) + { + return; + } + + $Block['markup'] .= "\n" . $Line['body']; + + if (preg_match('/-->$/', $Line['text'])) + { + $Block['closed'] = true; + } + + return $Block; + } + + # + # Fenced Code + + protected function blockFencedCode($Line) + { + if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([^`]+)?[ ]*$/', $Line['text'], $matches)) + { + $Element = array( + 'name' => 'code', + 'text' => '', + ); + + if (isset($matches[1])) + { + /** + * https://www.w3.org/TR/2011/WD-html5-20110525/elements.html#classes + * Every HTML element may have a class attribute specified. + * The attribute, if specified, must have a value that is a set + * of space-separated tokens representing the various classes + * that the element belongs to. + * [...] + * The space characters, for the purposes of this specification, + * are U+0020 SPACE, U+0009 CHARACTER TABULATION (tab), + * U+000A LINE FEED (LF), U+000C FORM FEED (FF), and + * U+000D CARRIAGE RETURN (CR). + */ + $language = substr($matches[1], 0, strcspn($matches[1], " \t\n\f\r")); + + $class = 'language-'.$language; + + $Element['attributes'] = array( + 'class' => $class, + ); + } + + $Block = array( + 'char' => $Line['text'][0], + 'element' => array( + 'name' => 'pre', + 'handler' => 'element', + 'text' => $Element, + ), + ); + + return $Block; + } + } + + protected function blockFencedCodeContinue($Line, $Block) + { + if (isset($Block['complete'])) + { + return; + } + + if (isset($Block['interrupted'])) + { + $Block['element']['text']['text'] .= "\n"; + + unset($Block['interrupted']); + } + + if (preg_match('/^'.$Block['char'].'{3,}[ ]*$/', $Line['text'])) + { + $Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1); + + $Block['complete'] = true; + + return $Block; + } + + $Block['element']['text']['text'] .= "\n".$Line['body']; + + return $Block; + } + + protected function blockFencedCodeComplete($Block) + { + $text = $Block['element']['text']['text']; + + $Block['element']['text']['text'] = $text; + + return $Block; + } + + # + # Header + + protected function blockHeader($Line) + { + if (isset($Line['text'][1])) + { + $level = 1; + + while (isset($Line['text'][$level]) and $Line['text'][$level] === '#') + { + $level ++; + } + + if ($level > 6) + { + return; + } + + $text = trim($Line['text'], '# '); + + $Block = array( + 'element' => array( + 'name' => 'h' . min(6, $level), + 'text' => $text, + 'handler' => 'line', + ), + ); + + return $Block; + } + } + + # + # List + + protected function blockList($Line) + { + list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]'); + + if (preg_match('/^('.$pattern.'[ ]+)(.*)/', $Line['text'], $matches)) + { + $Block = array( + 'indent' => $Line['indent'], + 'pattern' => $pattern, + 'element' => array( + 'name' => $name, + 'handler' => 'elements', + ), + ); + + if($name === 'ol') + { + $listStart = stristr($matches[0], '.', true); + + if($listStart !== '1') + { + $Block['element']['attributes'] = array('start' => $listStart); + } + } + + $Block['li'] = array( + 'name' => 'li', + 'handler' => 'li', + 'text' => array( + $matches[2], + ), + ); + + $Block['element']['text'] []= & $Block['li']; + + return $Block; + } + } + + protected function blockListContinue($Line, array $Block) + { + if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches)) + { + if (isset($Block['interrupted'])) + { + $Block['li']['text'] []= ''; + + $Block['loose'] = true; + + unset($Block['interrupted']); + } + + unset($Block['li']); + + $text = isset($matches[1]) ? $matches[1] : ''; + + $Block['li'] = array( + 'name' => 'li', + 'handler' => 'li', + 'text' => array( + $text, + ), + ); + + $Block['element']['text'] []= & $Block['li']; + + return $Block; + } + + if ($Line['text'][0] === '[' and $this->blockReference($Line)) + { + return $Block; + } + + if ( ! isset($Block['interrupted'])) + { + $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']); + + $Block['li']['text'] []= $text; + + return $Block; + } + + if ($Line['indent'] > 0) + { + $Block['li']['text'] []= ''; + + $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']); + + $Block['li']['text'] []= $text; + + unset($Block['interrupted']); + + return $Block; + } + } + + protected function blockListComplete(array $Block) + { + if (isset($Block['loose'])) + { + foreach ($Block['element']['text'] as &$li) + { + if (end($li['text']) !== '') + { + $li['text'] []= ''; + } + } + } + + return $Block; + } + + # + # Quote + + protected function blockQuote($Line) + { + if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) + { + $Block = array( + 'element' => array( + 'name' => 'blockquote', + 'handler' => 'lines', + 'text' => (array) $matches[1], + ), + ); + + return $Block; + } + } + + protected function blockQuoteContinue($Line, array $Block) + { + if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) + { + if (isset($Block['interrupted'])) + { + $Block['element']['text'] []= ''; + + unset($Block['interrupted']); + } + + $Block['element']['text'] []= $matches[1]; + + return $Block; + } + + if ( ! isset($Block['interrupted'])) + { + $Block['element']['text'] []= $Line['text']; + + return $Block; + } + } + + # + # Rule + + protected function blockRule($Line) + { + if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text'])) + { + $Block = array( + 'element' => array( + 'name' => 'hr' + ), + ); + + return $Block; + } + } + + # + # Setext + + protected function blockSetextHeader($Line, array $Block = null) + { + if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted'])) + { + return; + } + + if (chop($Line['text'], $Line['text'][0]) === '') + { + $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2'; + + return $Block; + } + } + + # + # Markup + + protected function blockMarkup($Line) + { + if ($this->markupEscaped or $this->safeMode) + { + return; + } + + if (preg_match('/^<(\w[\w-]*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches)) + { + $element = strtolower($matches[1]); + + if (in_array($element, $this->textLevelElements)) + { + return; + } + + $Block = array( + 'name' => $matches[1], + 'depth' => 0, + 'markup' => $Line['text'], + ); + + $length = strlen($matches[0]); + + $remainder = substr($Line['text'], $length); + + if (trim($remainder) === '') + { + if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) + { + $Block['closed'] = true; + + $Block['void'] = true; + } + } + else + { + if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) + { + return; + } + + if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder)) + { + $Block['closed'] = true; + } + } + + return $Block; + } + } + + protected function blockMarkupContinue($Line, array $Block) + { + if (isset($Block['closed'])) + { + return; + } + + if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open + { + $Block['depth'] ++; + } + + if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close + { + if ($Block['depth'] > 0) + { + $Block['depth'] --; + } + else + { + $Block['closed'] = true; + } + } + + if (isset($Block['interrupted'])) + { + $Block['markup'] .= "\n"; + + unset($Block['interrupted']); + } + + $Block['markup'] .= "\n".$Line['body']; + + return $Block; + } + + # + # Reference + + protected function blockReference($Line) + { + if (preg_match('/^\[(.+?)\]:[ ]*?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches)) + { + $id = strtolower($matches[1]); + + $Data = array( + 'url' => $matches[2], + 'title' => null, + ); + + if (isset($matches[3])) + { + $Data['title'] = $matches[3]; + } + + $this->DefinitionData['Reference'][$id] = $Data; + + $Block = array( + 'hidden' => true, + ); + + return $Block; + } + } + + # + # Table + + protected function blockTable($Line, array $Block = null) + { + if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted'])) + { + return; + } + + if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '') + { + $alignments = array(); + + $divider = $Line['text']; + + $divider = trim($divider); + $divider = trim($divider, '|'); + + $dividerCells = explode('|', $divider); + + foreach ($dividerCells as $dividerCell) + { + $dividerCell = trim($dividerCell); + + if ($dividerCell === '') + { + continue; + } + + $alignment = null; + + if ($dividerCell[0] === ':') + { + $alignment = 'left'; + } + + if (substr($dividerCell, - 1) === ':') + { + $alignment = $alignment === 'left' ? 'center' : 'right'; + } + + $alignments []= $alignment; + } + + # ~ + + $HeaderElements = array(); + + $header = $Block['element']['text']; + + $header = trim($header); + $header = trim($header, '|'); + + $headerCells = explode('|', $header); + + foreach ($headerCells as $index => $headerCell) + { + $headerCell = trim($headerCell); + + $HeaderElement = array( + 'name' => 'th', + 'text' => $headerCell, + 'handler' => 'line', + ); + + if (isset($alignments[$index])) + { + $alignment = $alignments[$index]; + + $HeaderElement['attributes'] = array( + 'style' => 'text-align: '.$alignment.';', + ); + } + + $HeaderElements []= $HeaderElement; + } + + # ~ + + $Block = array( + 'alignments' => $alignments, + 'identified' => true, + 'element' => array( + 'name' => 'table', + 'handler' => 'elements', + ), + ); + + $Block['element']['text'] []= array( + 'name' => 'thead', + 'handler' => 'elements', + ); + + $Block['element']['text'] []= array( + 'name' => 'tbody', + 'handler' => 'elements', + 'text' => array(), + ); + + $Block['element']['text'][0]['text'] []= array( + 'name' => 'tr', + 'handler' => 'elements', + 'text' => $HeaderElements, + ); + + return $Block; + } + } + + protected function blockTableContinue($Line, array $Block) + { + if (isset($Block['interrupted'])) + { + return; + } + + if ($Line['text'][0] === '|' or strpos($Line['text'], '|')) + { + $Elements = array(); + + $row = $Line['text']; + + $row = trim($row); + $row = trim($row, '|'); + + preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches); + + foreach ($matches[0] as $index => $cell) + { + $cell = trim($cell); + + $Element = array( + 'name' => 'td', + 'handler' => 'line', + 'text' => $cell, + ); + + if (isset($Block['alignments'][$index])) + { + $Element['attributes'] = array( + 'style' => 'text-align: '.$Block['alignments'][$index].';', + ); + } + + $Elements []= $Element; + } + + $Element = array( + 'name' => 'tr', + 'handler' => 'elements', + 'text' => $Elements, + ); + + $Block['element']['text'][1]['text'] []= $Element; + + return $Block; + } + } + + # + # ~ + # + + protected function paragraph($Line) + { + $Block = array( + 'element' => array( + 'name' => 'p', + 'text' => $Line['text'], + 'handler' => 'line', + ), + ); + + return $Block; + } + + # + # Inline Elements + # + + protected $InlineTypes = array( + '"' => array('SpecialCharacter'), + '!' => array('Image'), + '&' => array('SpecialCharacter'), + '*' => array('Emphasis'), + ':' => array('Url'), + '<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'), + '>' => array('SpecialCharacter'), + '[' => array('Link'), + '_' => array('Emphasis'), + '`' => array('Code'), + '~' => array('Strikethrough'), + '\\' => array('EscapeSequence'), + ); + + # ~ + + protected $inlineMarkerList = '!"*_&[:<>`~\\'; + + # + # ~ + # + + public function line($text, $nonNestables=array()) + { + $markup = ''; + + # $excerpt is based on the first occurrence of a marker + + while ($excerpt = strpbrk($text, $this->inlineMarkerList)) + { + $marker = $excerpt[0]; + + $markerPosition = strpos($text, $marker); + + $Excerpt = array('text' => $excerpt, 'context' => $text); + + foreach ($this->InlineTypes[$marker] as $inlineType) + { + # check to see if the current inline type is nestable in the current context + + if ( ! empty($nonNestables) and in_array($inlineType, $nonNestables)) + { + continue; + } + + $Inline = $this->{'inline'.$inlineType}($Excerpt); + + if ( ! isset($Inline)) + { + continue; + } + + # makes sure that the inline belongs to "our" marker + + if (isset($Inline['position']) and $Inline['position'] > $markerPosition) + { + continue; + } + + # sets a default inline position + + if ( ! isset($Inline['position'])) + { + $Inline['position'] = $markerPosition; + } + + # cause the new element to 'inherit' our non nestables + + foreach ($nonNestables as $non_nestable) + { + $Inline['element']['nonNestables'][] = $non_nestable; + } + + # the text that comes before the inline + $unmarkedText = substr($text, 0, $Inline['position']); + + # compile the unmarked text + $markup .= $this->unmarkedText($unmarkedText); + + # compile the inline + $markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']); + + # remove the examined text + $text = substr($text, $Inline['position'] + $Inline['extent']); + + continue 2; + } + + # the marker does not belong to an inline + + $unmarkedText = substr($text, 0, $markerPosition + 1); + + $markup .= $this->unmarkedText($unmarkedText); + + $text = substr($text, $markerPosition + 1); + } + + $markup .= $this->unmarkedText($text); + + return $markup; + } + + # + # ~ + # + + protected function inlineCode($Excerpt) + { + $marker = $Excerpt['text'][0]; + + if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(? strlen($matches[0]), + 'element' => array( + 'name' => 'code', + 'text' => $text, + ), + ); + } + } + + protected function inlineEmailTag($Excerpt) + { + if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches)) + { + $url = $matches[1]; + + if ( ! isset($matches[2])) + { + $url = 'mailto:' . $url; + } + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'a', + 'text' => $matches[1], + 'attributes' => array( + 'href' => $url, + ), + ), + ); + } + } + + protected function inlineEmphasis($Excerpt) + { + if ( ! isset($Excerpt['text'][1])) + { + return; + } + + $marker = $Excerpt['text'][0]; + + if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches)) + { + $emphasis = 'strong'; + } + elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches)) + { + $emphasis = 'em'; + } + else + { + return; + } + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => $emphasis, + 'handler' => 'line', + 'text' => $matches[1], + ), + ); + } + + protected function inlineEscapeSequence($Excerpt) + { + if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters)) + { + return array( + 'markup' => $Excerpt['text'][1], + 'extent' => 2, + ); + } + } + + protected function inlineImage($Excerpt) + { + if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[') + { + return; + } + + $Excerpt['text']= substr($Excerpt['text'], 1); + + $Link = $this->inlineLink($Excerpt); + + if ($Link === null) + { + return; + } + + $Inline = array( + 'extent' => $Link['extent'] + 1, + 'element' => array( + 'name' => 'img', + 'attributes' => array( + 'src' => $Link['element']['attributes']['href'], + 'alt' => $Link['element']['text'], + ), + ), + ); + + $Inline['element']['attributes'] += $Link['element']['attributes']; + + unset($Inline['element']['attributes']['href']); + + return $Inline; + } + + protected function inlineLink($Excerpt) + { + $Element = array( + 'name' => 'a', + 'handler' => 'line', + 'nonNestables' => array('Url', 'Link'), + 'text' => null, + 'attributes' => array( + 'href' => null, + 'title' => null, + ), + ); + + $extent = 0; + + $remainder = $Excerpt['text']; + + if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder, $matches)) + { + $Element['text'] = $matches[1]; + + $extent += strlen($matches[0]); + + $remainder = substr($remainder, $extent); + } + else + { + return; + } + + if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*"|\'[^\']*\'))?\s*[)]/', $remainder, $matches)) + { + $Element['attributes']['href'] = $matches[1]; + + if (isset($matches[2])) + { + $Element['attributes']['title'] = substr($matches[2], 1, - 1); + } + + $extent += strlen($matches[0]); + } + else + { + if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches)) + { + $definition = strlen($matches[1]) ? $matches[1] : $Element['text']; + $definition = strtolower($definition); + + $extent += strlen($matches[0]); + } + else + { + $definition = strtolower($Element['text']); + } + + if ( ! isset($this->DefinitionData['Reference'][$definition])) + { + return; + } + + $Definition = $this->DefinitionData['Reference'][$definition]; + + $Element['attributes']['href'] = $Definition['url']; + $Element['attributes']['title'] = $Definition['title']; + } + + return array( + 'extent' => $extent, + 'element' => $Element, + ); + } + + protected function inlineMarkup($Excerpt) + { + if ($this->markupEscaped or $this->safeMode or strpos($Excerpt['text'], '>') === false) + { + return; + } + + if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w[\w-]*[ ]*>/s', $Excerpt['text'], $matches)) + { + return array( + 'markup' => $matches[0], + 'extent' => strlen($matches[0]), + ); + } + + if ($Excerpt['text'][1] === '!' and preg_match('/^/s', $Excerpt['text'], $matches)) + { + return array( + 'markup' => $matches[0], + 'extent' => strlen($matches[0]), + ); + } + + if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w[\w-]*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches)) + { + return array( + 'markup' => $matches[0], + 'extent' => strlen($matches[0]), + ); + } + } + + protected function inlineSpecialCharacter($Excerpt) + { + if ($Excerpt['text'][0] === '&' and ! preg_match('/^&#?\w+;/', $Excerpt['text'])) + { + return array( + 'markup' => '&', + 'extent' => 1, + ); + } + + $SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot'); + + if (isset($SpecialCharacter[$Excerpt['text'][0]])) + { + return array( + 'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';', + 'extent' => 1, + ); + } + } + + protected function inlineStrikethrough($Excerpt) + { + if ( ! isset($Excerpt['text'][1])) + { + return; + } + + if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches)) + { + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'del', + 'text' => $matches[1], + 'handler' => 'line', + ), + ); + } + } + + protected function inlineUrl($Excerpt) + { + if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/') + { + return; + } + + if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE)) + { + $url = $matches[0][0]; + + $Inline = array( + 'extent' => strlen($matches[0][0]), + 'position' => $matches[0][1], + 'element' => array( + 'name' => 'a', + 'text' => $url, + 'attributes' => array( + 'href' => $url, + ), + ), + ); + + return $Inline; + } + } + + protected function inlineUrlTag($Excerpt) + { + if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches)) + { + $url = $matches[1]; + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'a', + 'text' => $url, + 'attributes' => array( + 'href' => $url, + ), + ), + ); + } + } + + # ~ + + protected function unmarkedText($text) + { + if ($this->breaksEnabled) + { + $text = preg_replace('/[ ]*\n/', "
\n", $text); + } + else + { + $text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "
\n", $text); + $text = str_replace(" \n", "\n", $text); + } + + return $text; + } + + # + # Handlers + # + + protected function element(array $Element) + { + if ($this->safeMode) + { + $Element = $this->sanitiseElement($Element); + } + + $markup = '<'.$Element['name']; + + if (isset($Element['attributes'])) + { + foreach ($Element['attributes'] as $name => $value) + { + if ($value === null) + { + continue; + } + + $markup .= ' '.$name.'="'.self::escape($value).'"'; + } + } + + $permitRawHtml = false; + + if (isset($Element['text'])) + { + $text = $Element['text']; + } + // very strongly consider an alternative if you're writing an + // extension + elseif (isset($Element['rawHtml'])) + { + $text = $Element['rawHtml']; + $allowRawHtmlInSafeMode = isset($Element['allowRawHtmlInSafeMode']) && $Element['allowRawHtmlInSafeMode']; + $permitRawHtml = !$this->safeMode || $allowRawHtmlInSafeMode; + } + + if (isset($text)) + { + $markup .= '>'; + + if (!isset($Element['nonNestables'])) + { + $Element['nonNestables'] = array(); + } + + if (isset($Element['handler'])) + { + $markup .= $this->{$Element['handler']}($text, $Element['nonNestables']); + } + elseif (!$permitRawHtml) + { + $markup .= self::escape($text, true); + } + else + { + $markup .= $text; + } + + $markup .= ''; + } + else + { + $markup .= ' />'; + } + + return $markup; + } + + protected function elements(array $Elements) + { + $markup = ''; + + foreach ($Elements as $Element) + { + $markup .= "\n" . $this->element($Element); + } + + $markup .= "\n"; + + return $markup; + } + + # ~ + + protected function li($lines) + { + $markup = $this->lines($lines); + + $trimmedMarkup = trim($markup); + + if ( ! in_array('', $lines) and substr($trimmedMarkup, 0, 3) === '

') + { + $markup = $trimmedMarkup; + $markup = substr($markup, 3); + + $position = strpos($markup, "

"); + + $markup = substr_replace($markup, '', $position, 4); + } + + return $markup; + } + + # + # Deprecated Methods + # + + function parse($text) + { + $markup = $this->text($text); + + return $markup; + } + + protected function sanitiseElement(array $Element) + { + static $goodAttribute = '/^[a-zA-Z0-9][a-zA-Z0-9-_]*+$/'; + static $safeUrlNameToAtt = array( + 'a' => 'href', + 'img' => 'src', + ); + + if (isset($safeUrlNameToAtt[$Element['name']])) + { + $Element = $this->filterUnsafeUrlInAttribute($Element, $safeUrlNameToAtt[$Element['name']]); + } + + if ( ! empty($Element['attributes'])) + { + foreach ($Element['attributes'] as $att => $val) + { + # filter out badly parsed attribute + if ( ! preg_match($goodAttribute, $att)) + { + unset($Element['attributes'][$att]); + } + # dump onevent attribute + elseif (self::striAtStart($att, 'on')) + { + unset($Element['attributes'][$att]); + } + } + } + + return $Element; + } + + protected function filterUnsafeUrlInAttribute(array $Element, $attribute) + { + foreach ($this->safeLinksWhitelist as $scheme) + { + if (self::striAtStart($Element['attributes'][$attribute], $scheme)) + { + return $Element; + } + } + + $Element['attributes'][$attribute] = str_replace(':', '%3A', $Element['attributes'][$attribute]); + + return $Element; + } + + # + # Static Methods + # + + protected static function escape($text, $allowQuotes = false) + { + return htmlspecialchars($text, $allowQuotes ? ENT_NOQUOTES : ENT_QUOTES, 'UTF-8'); + } + + protected static function striAtStart($string, $needle) + { + $len = strlen($needle); + + if ($len > strlen($string)) + { + return false; + } + else + { + return strtolower(substr($string, 0, $len)) === strtolower($needle); + } + } + + static function instance($name = 'default') + { + if (isset(self::$instances[$name])) + { + return self::$instances[$name]; + } + + $instance = new static(); + + self::$instances[$name] = $instance; + + return $instance; + } + + private static $instances = array(); + + # + # Fields + # + + protected $DefinitionData; + + # + # Read-Only + + protected $specialCharacters = array( + '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|', + ); + + protected $StrongRegex = array( + '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s', + '_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us', + ); + + protected $EmRegex = array( + '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s', + '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us', + ); + + protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?'; + + protected $voidElements = array( + 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', + ); + + protected $textLevelElements = array( + 'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont', + 'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing', + 'i', 'rp', 'del', 'code', 'strike', 'marquee', + 'q', 'rt', 'ins', 'font', 'strong', + 's', 'tt', 'kbd', 'mark', + 'u', 'xm', 'sub', 'nobr', + 'sup', 'ruby', + 'var', 'span', + 'wbr', 'time', + ); +} diff --git a/i/README.md b/i/README.md new file mode 100644 index 0000000..2f5a665 --- /dev/null +++ b/i/README.md @@ -0,0 +1,60 @@ +# Dead Simple Blog + +Version 1.1 (2022-11-15) + +- [Installation](#installation) +- [Usage](#usage) +- [Changelog](#changelog) +- [Other Versions](#other-versions) +- [Attributions](#attributions) + +I've wanted for a long time to create a simple way of blogging that eschews basically all bells and whistles. Many "flat file" Content Management Systems exist already, as well as "static site generators", but none of these that I looked at were simple enough for my liking. + +I don't want to have to install Ruby, or Python, or Composer, or whatever else on a server to run a blog. On the other hand, installing WordPress or one of the other popular PHP-based CMSes for this use case is like hammering in a nail with a sledgehammer. + +Many people dislike PHP, and while it has its warts, I like it. I like using (vanilla) PHP simply because it is nearly ubiquitous. Having to install or configure it is often unnecessary because it is usually *already* installed, configured, and running. + +I wanted to use plain text files, but some formatting is nice -- Markdown was the obvious solution for this, since it offers quite a lot of options in terms of text formatting, without sacrificing the readability of the plain text itself. I was not keen on adding dependencies but I found [Parsedown](http://parsedown.org) which offers Markdown parsing by including a single PHP file. I can deal with that. + +That's really all there is to it -- dead simple PHP-based templating, and Markdown-formatted plain text content. + +I know I'm probably forgetting about a million edge cases, but I want to keep it simple. So, we'll roll with this for now and add features as they become necessary. + +## Installation + +Download the files and upload them to a webserver somewhere. That's it! + +## Usage + +1. Duplicate `config-default.php` as `config-custom.php`, and change the config variables to your liking. +2. Create text files with a NUMERIC file name, I use YYYY-MM-DD date-based names (e.g. 2018-10-30.md). +3. Format text files with Markdown, or not. Whatever. ;) +4. If you need to link to image/video/audio/etc. files, you can upload them to the media folder. +4. Upload text files to the `content` directory. +5. You're done!! + +## Changelog + +### Version 1.1 + +- Updated Parsedown to 1.7.4 +- Config has now been moved to `config-default.php`, added support for `config-custom.php` +- Added dark mode! Adjust the `APPEARANCE` constant to enable +- Change default file type to .md files instead of .txt. Adjust `FILE_EXT` constant if needed +- Reorganize folder structure (CSS and fonts are now in `/src`) +- Small text update to `/content/drafts/AboutDrafts.md` +- Defined `$content` in global scope to avoid PHP errors in some configurations +- Added default favicon at `/img/favicon.png` +- Including `fonts.css` in `` instead of using @import for better caching behaviour +- Added file hash query strings to `` tags in `` for better caching behaviour +- `index.php` now uses `
` for content on list view and `
` on single post view +- List view now explicitly sorts posts using `sortPosts()` function - default is sorting by filename, descending + +## Other Versions + +User @shoaiyb has a fork going with additional features that I didn't think were "absolutely necessary" but if you want some extra bells and whistles, I recommend checking out [his fork](https://github.com/shoaiyb/dead-simple-blog). + +## Attributions + +- [Parsedown](https://github.com/erusev/parsedown) by Emanuil Rusev +- [Blog Icon](https://thenounproject.com/icon/blog-3557350/) by Gregor Cresnar from [Noun Project](https://thenounproject.com) diff --git a/i/config-default.php b/i/config-default.php new file mode 100644 index 0000000..189f896 --- /dev/null +++ b/i/config-default.php @@ -0,0 +1,20 @@ + + +Your browser does not support the video tag. + \ No newline at end of file diff --git a/i/content/drafts/2023-10-14.md b/i/content/drafts/2023-10-14.md new file mode 100644 index 0000000..2708fc0 --- /dev/null +++ b/i/content/drafts/2023-10-14.md @@ -0,0 +1,13 @@ +# Moving along + +This is NOW the new website look! It'll grow over time of course, and *maybe* more functionality. So far: + +- Still no advertisements +- Reduced footprint + + +And... reduced footprint in itself makes a huge difference. I used to think the previous website was a good way to go for layouts, but nope. I eventually wanted even *less*. + +Previous posts will slowly be migrated over. + +Thank you for reading... if you do. \ No newline at end of file diff --git a/i/content/drafts/2024-04-07.md b/i/content/drafts/2024-04-07.md new file mode 100644 index 0000000..c3dfe39 --- /dev/null +++ b/i/content/drafts/2024-04-07.md @@ -0,0 +1,4 @@ +# It's Always Sunny In... + +https://isnick.nyc3.digitaloceanspaces.com/public/yt/Frank%20Reynolds%20and%20his%20gun%20-%20It%27s%20Always%20Sunny%20in%20Philadelphia.mp4 + diff --git a/i/content/drafts/2024-05-25.md b/i/content/drafts/2024-05-25.md new file mode 100644 index 0000000..73372d7 --- /dev/null +++ b/i/content/drafts/2024-05-25.md @@ -0,0 +1,3 @@ +# Mitch Hedberg + + \ No newline at end of file diff --git a/i/content/drafts/2024-06-10.md b/i/content/drafts/2024-06-10.md new file mode 100644 index 0000000..9cc490d --- /dev/null +++ b/i/content/drafts/2024-06-10.md @@ -0,0 +1,3 @@ +# Foggy morning + +
@_tyclifford E-ride. #back #fog #morning ♬ In the Forest - Lesfm & Olexy
\ No newline at end of file diff --git a/i/content/drafts/2024-08-05.md b/i/content/drafts/2024-08-05.md new file mode 100644 index 0000000..0320786 --- /dev/null +++ b/i/content/drafts/2024-08-05.md @@ -0,0 +1,74 @@ +# Use AI or it'll use you! + +![](https://cdn.buymeacoffee.com/uploads/project_updates/2024/08/d411324c7963565d2b370e87d5ea2346.png) + +So, out of all artificial intelligent conversations, what I hear the most is *will it take jobs,* or *it will take jobs.* Yes, AI will take jobs, but I believe it'll be the jobs of those *not* using AI. While I'm not immune and also not trying to proceed biasedly, it's trivial to me in the usage of where AI belongs in my daily Life. + +There are several times I go to AI for summaries or quick History searches, with a little spice. "Top 10 songs with a Halloween story." Crazy, but AI can put together something, even if it's a little off - and with tuning, it can become even better. + +I can train AI on projects, and then draft policies for me to add human touch to, or outright correct (because it may be heading in the wrong direction or misunderstand completely) bits as I go on. And this is exactly my point, in a secretarial manner, AI has helped progress a lot of my projects, and I firmly believe I wouldn't have the outside of the box thinking as I do now if I didn't use it - sure, I could talk with people, and I do, but AI offers a pressing feature: Privacy + +Providing you have the resources (hardware) to do so, you may run models "offline" or on your network to compute privacy focused tasks - and I take great advantage of this. Yes, it doesn't have real-time information, but it has *enough* information, and if you're aware of its shortage of information you can fill in the blanks, and or update it as you go with new bits. + +If you're interested, here is a Github link: [nomic-ai/gpt4all: GPT4All: Chat with Local LLMs on Any Device (github.com)](https://github.com/nomic-ai/gpt4all) + +Don't hesitate to reach out for guidance to launch the program. Keep in mind your computer must have at least 16 GB of RAM, and a moderately recent CPU (se, 2019 and onwards) to make effective use of it. There *are* setups to run on tiny devices such as Raspberry Pi, but I am unfamiliar with those. + +A chatter pointed out open-source software to help with subtitling and I have yet to explore the Raspberry Pi method. + +## We're likely better off fusing with technology + +Back to 2008 for a short minute: I was the privacy nut. The kid you didn't want to discuss privacy with because I'd go off on many subjects, going against many companies--sometimes with little insight as to *why* something may have been executed the way it was - of course! I was young and naïve. and I'm still naïve, but with more experience. + +I'd push the mission of not using smartphones for everything, not using payments on smartphones, and not using cards in general, especially tied to smartphones - and some of this changed in incredible ways, for me anyway. You'll rarely see me *not* using my smartphone to checkout with Apple Pay or tap to pay. + +Not to bore you too much, but I should include some back History here for you to understand how I've changed some of my opinions. It's only fair. + +**My view of the web in 2010**: + +> *Title: # [How the Web Works and How it Could Work](https://snick512.wordpress.com/2010/12/29/how-the-web-works-and-how-it-could-work/)* +> +> *Forever web pages have been improving. Don’t say they haven’t either. Remember messy ol’ HTML? Now there’s CSS to go with that, and Javascript too.* +> +> *The traditional sense in going to a webstie is you load it, and click on links – It’s just that simple. What if this was a little more complex, but still easy?* +> +> *Imagine a web page that could be organised in any manner according to the person behind the browser. No specific edits, no specific alignments, no specific anything. One could add pictures, video, music, and share these edits with their friends. More-so these edits would automatically be visible to anyone who was “following.” Is this confusing? I hope not. Just think of it as a Status / Twitter on steroids. A way for people to manipulate web pages as they see fit.* +> +> *I would assume that every web page would look normal – But would then be manipulated by each visitor (if they wanted to). Of course these edits would be locked to said user.* +> +> *In-depth* +> +> *If you’ve ever used Google Docs, or Etherpad, you should understand exactly where I’m going with this. Multiple people could edit a page, and have a deeper interaction with the website/online service. Eh? Sure, a comments box, Social buttons, and so on aren’t bad. (But) What if these changes stuck, and could be de/centralised as needed. Similar to the diaspora model, ‘cept a little more intense.* +> +> *Though I could be jumping the line here. Maybe the web isn’t ready for a such thing, yet. Wh*o knows. + +### and a small step into what I believe about Privacy + +We should be afforded individual privacy, and this layer should encompass these spots (and more, but don't take this concrete, I'm just flying my thoughts here): + +- Right to disconnect + +- Right to be forgotten + +- Non-intrusive DRM, and outright no DRM + + +Digital Rights Management is naturally a thorn in privacy due to its call home aspects. If you're on Windows Media Player or iTunes, the service must occasionally dial home to see if you're "allowed" to play the requested files. The same rings true for video games - there are certain games refusing to play offline without first checking on Internet connectivity and licensing, despite the individual only wanting to play offline. + +This is another reason disc-less systems are a bad idea. It erodes tangible copies from being used and makes the user dependent on DRM completely. + +After awhile, services start to learn when you're awake, as a very tiny example. Imagine AI calculating your schedule. + +## Bringing it together + +Kinda confusing but bringing it together; We are likely better off fusing with technology in a privacy-first manner. Meaning, anything to do with AI should always be executed with the individual's privacy in mind. We can surely have a wonderful time on the Internet, and with technology but I don't believe it'll be possible if it's not checked in regard to privacy. + +Thoughts on future use is artificial intelligence will become more accessible to the everyday person *without* third party service, and I don't believe it'll be effective until then. + +AI is still expensive, financially and privacy wise. + +I believe you should become aware of AI and learn how to make use of it to help understand how it's using your data, and how you can use it to better your own Life. + +I want the web to be fun and free and use all its fruits, but we need to take grip of it now before we don't know how to. + +Be safe and thank you for reading! \ No newline at end of file diff --git a/i/content/drafts/2024-10-24.md b/i/content/drafts/2024-10-24.md new file mode 100644 index 0000000..8f87196 --- /dev/null +++ b/i/content/drafts/2024-10-24.md @@ -0,0 +1,5 @@ +# Airbnb fails when it comes to hidden cameras + +"[...] Airbnb employee also revealed that when a guest complains of a hidden camera, the company doesn’t – as a matter of practice – notify law enforcement, not even when a child is involved. The company may, however, reach out to hosts about complaints as part of internal inquiries – a move law enforcement experts say could hinder criminal investigations because it gives suspects time to destroy evidence." + +[https://www.cnn.com/2024/07/09/business/airbnb-hidden-camera-invs/index.html](https://www.cnn.com/2024/07/09/business/airbnb-hidden-camera-invs/index.html) \ No newline at end of file diff --git a/i/content/drafts/2a-v.html b/i/content/drafts/2a-v.html new file mode 100644 index 0000000..66f7fdb --- /dev/null +++ b/i/content/drafts/2a-v.html @@ -0,0 +1,30 @@ + + + + + + + + + + + + diff --git a/i/content/drafts/AboutDrafts.md b/i/content/drafts/AboutDrafts.md new file mode 100644 index 0000000..240a2c2 --- /dev/null +++ b/i/content/drafts/AboutDrafts.md @@ -0,0 +1,9 @@ +# Draft Posts + +Draft posts can be put in this folder, they will not be included in the listing of posts until they are moved up into the parent folder. + +## Warning! + +Permissions on this folder are set to User Read/Write ONLY, which will prevent the general public from accessing the text files directly. But beware, if these folder permissions are overwritten then those files will be viewable. + +I will probably do up some Apache and nginx configurations that will secure this in a more reliable way, but in the meantime if you're not sure, you can always save your drafts offline. \ No newline at end of file diff --git a/i/img/favicon.png b/i/img/favicon.png new file mode 100644 index 0000000..f77440c Binary files /dev/null and b/i/img/favicon.png differ diff --git a/i/index.php b/i/index.php new file mode 100644 index 0000000..5ca525a --- /dev/null +++ b/i/index.php @@ -0,0 +1,134 @@ +getFilename(); + $b_value = $b->getFilename(); + return strcmp($b_value, $a_value); // Reversed to get descending +} + +if ( $is_post ) { + // Single post page + $post_name = filter_var($_GET['post'], FILTER_SANITIZE_NUMBER_INT); + $file_path = __DIR__.'/content/'.$post_name.'.'.FILE_EXT; + if ( file_exists($file_path) ) { + $file = fopen($file_path, 'r'); + $post_title = trim(fgets($file),'#'); + fclose($file); + // Process the Markdown + $parsedown = new Parsedown(); + $content = $parsedown->text(file_get_contents($file_path)); + } else { + $content = ' +

Not Found

+

Sorry, couldn\'t find a post with that name. Please try again, or go to the + home page to select a different post.

'; + } +} else { + // Blog main page - list all posts + $files = new DirectoryIterator(__DIR__.'/content/'); + $files_array = []; + foreach ($files as $file) { + if ( $file->isFile() && $file->getExtension() == FILE_EXT ) { + array_push($files_array, $file->getFileInfo()); + } + } + usort($files_array, 'sortPosts'); // See sortPosts() function above + foreach ($files_array as $file) { + $filename_no_ext = $file->getBasename('.'.FILE_EXT); + $file_pointer = $file->openFile(); + $post_title = trim($file_pointer->fgets(),'#'); + $content .= '

'.$post_title.' '.$filename_no_ext.'

'; + } +} + +// Appending file hashes to the hrefs allows us to cache the files indefinitely, +// but immediately serve a new version once the file changes. +$style_hash = hash('md5', file_get_contents(__DIR__.'/src/css/style-'.APPEARANCE.'.css')); +$fonts_hash = hash('md5', file_get_contents(__DIR__.'/src/css/fonts.css')); +$icon_hash = hash('md5', file_get_contents(__DIR__.'/img/favicon.png')); + +?> + + + + <?php if ( !empty($_GET['post']) ) { echo $post_title.' - '; } ?><?php echo BLOG_TITLE; ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+

Blurbs

+ + '.$content.''; + ?> + + +
+

+ + +
+ + +
+ + + + + + + + + + diff --git a/i/src/css/fonts.css b/i/src/css/fonts.css new file mode 100644 index 0000000..b2ba993 --- /dev/null +++ b/i/src/css/fonts.css @@ -0,0 +1,36 @@ +@font-face { + font-family: 'lato'; + src: url('../fonts/lato/lato-regular-webfont.woff2') format('woff2'), + url('../fonts/lato/lato-regular-webfont.woff') format('woff'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'lato'; + src: url('../fonts/lato/lato-italic-webfont.woff2') format('woff2'), + url('../fonts/lato/lato-italic-webfont.woff') format('woff'); + font-weight: normal; + font-style: italic; +} +@font-face { + font-family: 'lato'; + src: url('../fonts/lato/lato-bold-webfont.woff2') format('woff2'), + url('../fonts/lato/lato-bold-webfont.woff') format('woff'); + font-weight: bold; + font-style: normal; +} +@font-face { + font-family: 'lato'; + src: url('../fonts/lato/lato-bolditalic-webfont.woff2') format('woff2'), + url('../fonts/lato/lato-bolditalic-webfont.woff') format('woff'); + font-weight: bold; + font-style: italic; +} +@font-face { + font-family: 'slabo_13px'; + src: url('../fonts/slabo13px/slabo13px-regular-webfont.woff2') format('woff2'), + url('../fonts/slabo13px/slabo13px-regular-webfont.woff') format('woff'); + font-weight: normal; + font-style: normal; +} + diff --git a/i/src/css/style-dark.css b/i/src/css/style-dark.css new file mode 100644 index 0000000..f76d081 --- /dev/null +++ b/i/src/css/style-dark.css @@ -0,0 +1,83 @@ +body { + color: #fff; + background: #333; + font: 18px 'lato', sans-serif; + line-height: 1.5; +} + +body>* { + max-width: 40em; + margin: 0 auto; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: 'slabo_13px', 'lato', sans-serif; +} + +a, +a:link, +a:visited { + color: #0bf; + transition: color .2s ease; +} + +a:hover { + color: #09d; +} + +a:active { + color: #057; +} + +header { + border-bottom: 1px solid #555; +} + +h1.blog-title a { + text-decoration: none; +} + +h2.list-title a { + color: #fff; + text-decoration: none; +} + +h1.blog-title a:hover, +h2.list-title a:hover { + text-decoration: underline; +} + +h2.list-title a:hover { + color: #ddd; +} + +code { + font: normal 1em 'Inconsolata', monospace; + color: #fb557e; + background: #222; + padding: 0 .33em; + border-radius: .33em; +} + +hr { + border: none; + border-top: 1px solid #555; +} + +footer { + color: #ddd; + padding: 0 1em; + margin: 2em auto 0; + background: #444; + box-sizing: border-box; + overflow: auto; +} + +footer .postscript { + font-style: italic; +} \ No newline at end of file diff --git a/i/src/css/style-light.css b/i/src/css/style-light.css new file mode 100644 index 0000000..eafb110 --- /dev/null +++ b/i/src/css/style-light.css @@ -0,0 +1,82 @@ +body { + color: #333; + background: #fff; + font: 18px 'lato', sans-serif; + line-height: 1.5; +} + +body>* { + max-width: 40em; + margin: 0 auto; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: 'slabo_13px', 'lato', sans-serif; +} + +a, +a:link, +a:visited { + color: #08c; + transition: color .2s ease; +} + +a:hover { + color: #0bf; +} + +a:active { + color: #046; +} + +header { + border-bottom: 1px solid #ccc; +} + +h1.blog-title a { + text-decoration: none; +} + +h2.list-title a { + color: #000; + text-decoration: none; +} + +h1.blog-title a:hover, +h2.list-title a:hover { + text-decoration: underline; +} + +h2.list-title a:hover { + color: #ddd; +} + +code { + font: normal 1em 'Inconsolata', monospace; + color: #fb557e; + background: #eee; + padding: 0 .33em; + border-radius: .33em; +} + +hr { + border: none; + border-top: 1px solid #ccc; +} + +footer { + color: #888; + padding: 1em; + margin: 2em auto 0; + background: #eee; + box-sizing: border-box; +} + +footer .postscript { + font-style: italic; +} \ No newline at end of file diff --git a/i/src/fonts/lato/lato-bold-webfont.woff b/i/src/fonts/lato/lato-bold-webfont.woff new file mode 100644 index 0000000..08dbef6 Binary files /dev/null and b/i/src/fonts/lato/lato-bold-webfont.woff differ diff --git a/i/src/fonts/lato/lato-bold-webfont.woff2 b/i/src/fonts/lato/lato-bold-webfont.woff2 new file mode 100644 index 0000000..8d09ca0 Binary files /dev/null and b/i/src/fonts/lato/lato-bold-webfont.woff2 differ diff --git a/i/src/fonts/lato/lato-bolditalic-webfont.woff b/i/src/fonts/lato/lato-bolditalic-webfont.woff new file mode 100644 index 0000000..c626adb Binary files /dev/null and b/i/src/fonts/lato/lato-bolditalic-webfont.woff differ diff --git a/i/src/fonts/lato/lato-bolditalic-webfont.woff2 b/i/src/fonts/lato/lato-bolditalic-webfont.woff2 new file mode 100644 index 0000000..d2327f2 Binary files /dev/null and b/i/src/fonts/lato/lato-bolditalic-webfont.woff2 differ diff --git a/i/src/fonts/lato/lato-italic-webfont.woff b/i/src/fonts/lato/lato-italic-webfont.woff new file mode 100644 index 0000000..b01835d Binary files /dev/null and b/i/src/fonts/lato/lato-italic-webfont.woff differ diff --git a/i/src/fonts/lato/lato-italic-webfont.woff2 b/i/src/fonts/lato/lato-italic-webfont.woff2 new file mode 100644 index 0000000..e0b6782 Binary files /dev/null and b/i/src/fonts/lato/lato-italic-webfont.woff2 differ diff --git a/i/src/fonts/lato/lato-regular-webfont.woff b/i/src/fonts/lato/lato-regular-webfont.woff new file mode 100644 index 0000000..d0948e5 Binary files /dev/null and b/i/src/fonts/lato/lato-regular-webfont.woff differ diff --git a/i/src/fonts/lato/lato-regular-webfont.woff2 b/i/src/fonts/lato/lato-regular-webfont.woff2 new file mode 100644 index 0000000..8de6b9f Binary files /dev/null and b/i/src/fonts/lato/lato-regular-webfont.woff2 differ diff --git a/i/src/fonts/slabo13px/slabo13px-regular-webfont.woff b/i/src/fonts/slabo13px/slabo13px-regular-webfont.woff new file mode 100644 index 0000000..88b2358 Binary files /dev/null and b/i/src/fonts/slabo13px/slabo13px-regular-webfont.woff differ diff --git a/i/src/fonts/slabo13px/slabo13px-regular-webfont.woff2 b/i/src/fonts/slabo13px/slabo13px-regular-webfont.woff2 new file mode 100644 index 0000000..bad44f4 Binary files /dev/null and b/i/src/fonts/slabo13px/slabo13px-regular-webfont.woff2 differ diff --git a/images/Kelsey-Jeffries2.jpg b/images/Kelsey-Jeffries2.jpg new file mode 100644 index 0000000..d249502 Binary files /dev/null and b/images/Kelsey-Jeffries2.jpg differ diff --git a/images/about-small.jpg b/images/about-small.jpg new file mode 100644 index 0000000..b3dd6c0 Binary files /dev/null and b/images/about-small.jpg differ diff --git a/images/blogger.jpg b/images/blogger.jpg new file mode 100644 index 0000000..733e8ba Binary files /dev/null and b/images/blogger.jpg differ diff --git a/images/coder.jpg b/images/coder.jpg new file mode 100644 index 0000000..733e8ba Binary files /dev/null and b/images/coder.jpg differ diff --git a/images/duotone.jpg b/images/duotone.jpg new file mode 100644 index 0000000..733e8ba Binary files /dev/null and b/images/duotone.jpg differ diff --git a/images/fa.jpg b/images/fa.jpg new file mode 100644 index 0000000..6f22d97 Binary files /dev/null and b/images/fa.jpg differ diff --git a/images/fa2.jpg b/images/fa2.jpg new file mode 100644 index 0000000..c1d723a Binary files /dev/null and b/images/fa2.jpg differ diff --git a/images/ico/fa-bl.ico b/images/ico/fa-bl.ico new file mode 100644 index 0000000..1907d4e Binary files /dev/null and b/images/ico/fa-bl.ico differ diff --git a/images/ico/fa-bl.jpg b/images/ico/fa-bl.jpg new file mode 100644 index 0000000..9f199c9 Binary files /dev/null and b/images/ico/fa-bl.jpg differ diff --git a/images/lady.jpg b/images/lady.jpg new file mode 100644 index 0000000..733e8ba Binary files /dev/null and b/images/lady.jpg differ diff --git a/images/owner.jpg b/images/owner.jpg new file mode 100644 index 0000000..733e8ba Binary files /dev/null and b/images/owner.jpg differ diff --git a/images/patreon.png b/images/patreon.png new file mode 100644 index 0000000..c0cdcaf Binary files /dev/null and b/images/patreon.png differ diff --git a/images/preloader.gif b/images/preloader.gif new file mode 100644 index 0000000..2eb19be Binary files /dev/null and b/images/preloader.gif differ diff --git a/images/radio-tl2.png b/images/radio-tl2.png new file mode 100644 index 0000000..953efc8 Binary files /dev/null and b/images/radio-tl2.png differ diff --git a/images/radio.png b/images/radio.png new file mode 100644 index 0000000..8478a94 Binary files /dev/null and b/images/radio.png differ diff --git a/images/rizz_.jpg b/images/rizz_.jpg new file mode 100644 index 0000000..9eb100f Binary files /dev/null and b/images/rizz_.jpg differ diff --git a/images/rizz_schedule.jpg b/images/rizz_schedule.jpg new file mode 100644 index 0000000..feeb4ab Binary files /dev/null and b/images/rizz_schedule.jpg differ diff --git a/images/rr_t.png b/images/rr_t.png new file mode 100644 index 0000000..24b79e4 Binary files /dev/null and b/images/rr_t.png differ diff --git a/images/woman.jpg b/images/woman.jpg new file mode 100644 index 0000000..733e8ba Binary files /dev/null and b/images/woman.jpg differ diff --git a/index.php b/index.php new file mode 100644 index 0000000..e3548dc --- /dev/null +++ b/index.php @@ -0,0 +1,1176 @@ + '', + 'email' => '', + 'patreon' => '', + 'bluesky' => '', + 'tiktok' => '', + 'youtube' => '', + 'web' => '', +]; + +// ── Helper: relative time ────────────────────────────────────────────────────── +function relative_time(string $iso): string { + try { + $dt = new DateTime($iso, new DateTimeZone('UTC')); + $now = new DateTime('now', new DateTimeZone('UTC')); + $diff = $now->getTimestamp() - $dt->getTimestamp(); + if ($diff < 60) return 'just now'; + if ($diff < 3600) return floor($diff / 60) . 'm ago'; + if ($diff < 86400) return floor($diff / 3600) . 'h ago'; + if ($diff < 604800) return floor($diff / 86400) . 'd ago'; + return $dt->format('M j, Y'); + } catch (Exception $e) { + return htmlspecialchars($iso); + } +} +?> + + + + + + + + + + + + + + + + + + + + <?= htmlspecialchars(SITE_NAME) ?> — <?= htmlspecialchars(SITE_TAGLINE) ?> + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ <?= htmlspecialchars(PROFILE_IMG_ALT) ?> +
+
+

tyclifford.com

+

+ Hi, I'm a dude living in +

+
+ +
+

+
+ +
+ + 🎤 Guest + + + 📅 Schedule + +
+ +
+ + + + + +
+ + +
+ +
+
+ +
+ Updates + View all +
+
+
+ + + + post + +
+
+ +
+ +
+ New updates available + ↑ Show +
+ + +
    + +
  • no updates yet

  • + + +
  • +
    + + # + + + + + + + View post + +
    +

    + + > + + + +
  • + + +
+ + +
+
+
+ + +
+
+ Support +
+
+ +
Find me
+ +
+
+ +
+ +
+ +  ·  +
+ +
+ + + + + + + + + + + + + + + diff --git a/index_config/.htaccess b/index_config/.htaccess new file mode 100644 index 0000000..38c4466 --- /dev/null +++ b/index_config/.htaccess @@ -0,0 +1,3 @@ + + Require all denied + diff --git a/index_config/bludit.php b/index_config/bludit.php new file mode 100644 index 0000000..f79280a --- /dev/null +++ b/index_config/bludit.php @@ -0,0 +1,84 @@ + 0 && file_exists($cache_file) && (time() - filemtime($cache_file)) < $ttl) { + $cached = json_decode(file_get_contents($cache_file), true); + if (is_array($cached)) return $cached; + } + + // Build API request + $api_url = rtrim(BLUDIT_URL, '/') . '/api/pages'; + $count = max(1, min(20, (int)(BLUDIT_POST_COUNT ?? 5))); + + $ctx = stream_context_create([ + 'http' => [ + 'method' => 'GET', + 'header' => implode("\r\n", [ + 'X-Bludit-API-Token: ' . BLUDIT_API_TOKEN, + 'Accept: application/json', + ]), + 'timeout' => 5, + 'ignore_errors' => true, + ], + 'ssl' => ['verify_peer' => true], + ]); + + $raw = @file_get_contents($api_url, false, $ctx); + if ($raw === false) return []; + + $data = json_decode($raw, true); + if (!isset($data['data']) || !is_array($data['data'])) return []; + + $snippet_len = (int)(BLUDIT_SNIPPET_LEN ?? 110); + $posts = []; + + foreach (array_slice($data['data'], 0, $count) as $page) { + // Skip drafts / non-published + if (($page['type'] ?? '') !== 'published') continue; + + // Build snippet from content (strip HTML, collapse whitespace) + $raw_body = $page['content'] ?? $page['contentRaw'] ?? ''; + $plain = preg_replace('/\s+/', ' ', strip_tags(html_entity_decode($raw_body))); + $snippet = mb_strlen($plain) > $snippet_len + ? mb_substr($plain, 0, $snippet_len) . '…' + : $plain; + + $posts[] = [ + 'title' => html_entity_decode($page['title'] ?? 'Untitled'), + 'snippet' => $snippet, + 'url' => $page['permalink'] ?? (rtrim(BLUDIT_URL, '/') . '/' . ($page['key'] ?? '')), + 'date' => $page['date'] ?? '', + 'tags' => array_values((array)($page['tags'] ?? [])), + ]; + + if (count($posts) >= $count) break; + } + + // Cache result + if ($ttl > 0 && !empty($posts)) { + file_put_contents($cache_file, json_encode($posts), LOCK_EX); + } + + return $posts; +} diff --git a/index_config/config.php b/index_config/config.php new file mode 100644 index 0000000..32cd9e0 --- /dev/null +++ b/index_config/config.php @@ -0,0 +1,118 @@ + + + + + + + Freesound - Login + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + + + + +
+ +
+ + + + +
+ + + + + + + + + + + + + + + + + + diff --git a/index_config/updates_feed.php b/index_config/updates_feed.php new file mode 100644 index 0000000..7b2cf6b --- /dev/null +++ b/index_config/updates_feed.php @@ -0,0 +1,59 @@ + + * + * The optional ?since= parameter lets the client ask "give me only posts newer + * than this timestamp" so it can detect new arrivals without comparing full + * arrays. + * + * Response: + * { + * "ok": true, + * "total": , // total posts in the feed + * "new_since": , // posts newer than ?since= (0 if not supplied) + * "items": [ … ] // full array, newest first + * } + */ + +require_once __DIR__ . '/config.php'; + +header('Content-Type: application/json; charset=utf-8'); +header('Cache-Control: no-store'); +header('X-Content-Type-Options: nosniff'); + +// ── Load feed ───────────────────────────────────────────────────────────────── +$posts = []; +if (file_exists(UPDATES_JSON)) { + $raw = json_decode(file_get_contents(UPDATES_JSON), true); + if (is_array($raw)) $posts = $raw; +} + +// ── Count posts newer than ?since= ─────────────────────────────────────────── +$since = trim($_GET['since'] ?? ''); +$new_count = 0; + +if ($since !== '') { + try { + $since_dt = new DateTime($since, new DateTimeZone('UTC')); + foreach ($posts as $p) { + try { + $post_dt = new DateTime($p['date'] ?? '', new DateTimeZone('UTC')); + if ($post_dt > $since_dt) $new_count++; + } catch (Exception $e) { /* skip malformed dates */ } + } + } catch (Exception $e) { /* ignore bad since param */ } +} + +echo json_encode([ + 'ok' => true, + 'total' => count($posts), + 'new_since' => $new_count, + 'items' => $posts, +], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); diff --git a/js/_notes/dwsync.xml b/js/_notes/dwsync.xml new file mode 100644 index 0000000..01e8862 --- /dev/null +++ b/js/_notes/dwsync.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..e85417d --- /dev/null +++ b/js/main.js @@ -0,0 +1,72 @@ +/* + Name: LazyGuy + Description: Personal Landing Page + Version: 1.0.1 + Author: pixelwars +*/ + +(function () { + 'use strict'; + + + // WAIT FOR DOM TO BE LOADED + document.addEventListener("DOMContentLoaded", function() { + + // VIDEO BACKGROUND + jarallax(document.querySelectorAll('.media'), { + speed: 0, + zIndex: 1 + }); + + }); // dom loaded + + // WAIT FOR ALL TO BE LOADED + window.onload = function() { + + // add loaded class to html + var root = document.documentElement; + root.className += ' loaded'; + + // TYPING EFFECT + Typed.new('#typed', { + stringsElement: document.getElementById('typed-strings'), + loop: true, + typeSpeed: 7, + backSpeed: 2, + startDelay: 1000, + backDelay: 1200 + }); + }; // all loaded + + +}()); + + + +/* + PLUGINS +*/ + +/*! + * Name : Just Another Parallax [Jarallax] + * Version : 1.12.2 + * Author : nK + * GitHub : https://github.com/nk-o/jarallax + */!function(n){var o={};function i(e){if(o[e])return o[e].exports;var t=o[e]={i:e,l:!1,exports:{}};return n[e].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.m=n,i.c=o,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(t,e){if(1&e&&(t=i(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)i.d(n,o,function(e){return t[e]}.bind(null,o));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="",i(i.s=10)}([,,function(e,t){e.exports=function(e){"complete"===document.readyState||"interactive"===document.readyState?e.call():document.attachEvent?document.attachEvent("onreadystatechange",function(){"interactive"===document.readyState&&e.call()}):document.addEventListener&&document.addEventListener("DOMContentLoaded",e)}},function(n,e,t){(function(e){var t="undefined"!=typeof window?window:void 0!==e?e:"undefined"!=typeof self?self:{};n.exports=t}).call(this,t(4))},function(e,t){function n(e){return(n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}var o=function(){return this}();try{o=o||new Function("return this")()}catch(e){"object"===("undefined"==typeof window?"undefined":n(window))&&(o=window)}e.exports=o},,,,,,function(e,t,n){e.exports=n(11)},function(e,t,n){"use strict";n.r(t);var o=n(2),i=n.n(o),a=n(3),r=n(12);function l(e){return(l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}var s,c,u=a.window.jarallax;a.window.jarallax=r.default,a.window.jarallax.noConflict=function(){return a.window.jarallax=u,this},void 0!==a.jQuery&&((s=function(){for(var e=arguments.length,t=new Array(e),n=0;ne.length)&&(t=e.length);for(var n=0,o=new Array(t);n + * GitHub : https://github.com/nk-o/jarallax + */!function(o){var i={};function n(e){if(i[e])return i[e].exports;var t=i[e]={i:e,l:!1,exports:{}};return o[e].call(t.exports,t,t.exports,n),t.l=!0,t.exports}n.m=o,n.c=i,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)n.d(o,i,function(e){return t[e]}.bind(null,i));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=6)}([,,function(e,t){e.exports=function(e){"complete"===document.readyState||"interactive"===document.readyState?e.call():document.attachEvent?document.attachEvent("onreadystatechange",function(){"interactive"===document.readyState&&e.call()}):document.addEventListener&&document.addEventListener("DOMContentLoaded",e)}},function(o,e,t){(function(e){var t="undefined"!=typeof window?window:void 0!==e?e:"undefined"!=typeof self?self:{};o.exports=t}).call(this,t(4))},function(e,t){function o(e){return(o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}var i=function(){return this}();try{i=i||new Function("return this")()}catch(e){"object"===("undefined"==typeof window?"undefined":o(window))&&(i=window)}e.exports=i},,function(e,t,o){e.exports=o(7)},function(e,t,o){"use strict";o.r(t);var i=o(8),n=o(3),a=o.n(n),r=o(2),l=o.n(r),p=o(9);a.a.VideoWorker=a.a.VideoWorker||i.default,Object(p.default)(),l()(function(){void 0!==a.a.jarallax&&a.a.jarallax(document.querySelectorAll("[data-jarallax-video]"))})},function(e,t,o){"use strict";o.r(t),o.d(t,"default",function(){return v});var i=o(3),s=o.n(i);function n(e){return(n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function a(e,t){for(var o=0;o=u.options.endTime&&(u.options.loop?u.play(u.options.startTime):u.pause())},150):clearInterval(o)},onError:function(e){u.fire("error",e)}},(i=!u.$video)&&((n=document.createElement("div")).setAttribute("id",u.playerID),e.appendChild(n),document.body.appendChild(e)),u.player=u.player||new s.a.YT.Player(u.playerID,u.playerOptions),i&&(u.$video=document.getElementById(u.playerID),u.videoWidth=parseInt(u.$video.getAttribute("width"),10)||1280,u.videoHeight=parseInt(u.$video.getAttribute("height"),10)||720)),"vimeo"===u.type&&(u.playerOptions={id:u.videoID,autopause:0,transparent:0,autoplay:u.options.autoplay?1:0,loop:u.options.loop?1:0,muted:u.options.mute?1:0},u.options.volume&&(u.playerOptions.volume=u.options.volume),u.options.showContols||(u.playerOptions.badge=0,u.playerOptions.byline=0,u.playerOptions.portrait=0,u.playerOptions.title=0,u.playerOptions.background=1),u.$video||(a="",Object.keys(u.playerOptions).forEach(function(e){""!==a&&(a+="&"),a+="".concat(e,"=").concat(encodeURIComponent(u.playerOptions[e]))}),u.$video=document.createElement("iframe"),u.$video.setAttribute("id",u.playerID),u.$video.setAttribute("src","https://player.vimeo.com/video/".concat(u.videoID,"?").concat(a)),u.$video.setAttribute("frameborder","0"),u.$video.setAttribute("mozallowfullscreen",""),u.$video.setAttribute("allowfullscreen",""),e.appendChild(u.$video),document.body.appendChild(e)),u.player=u.player||new s.a.Vimeo.Player(u.$video,u.playerOptions),u.options.startTime&&u.options.autoplay&&u.player.setCurrentTime(u.options.startTime),u.player.getVideoWidth().then(function(e){u.videoWidth=e||1280}),u.player.getVideoHeight().then(function(e){u.videoHeight=e||720}),u.player.on("timeupdate",function(e){r||(u.fire("started",e),r=1),u.fire("timeupdate",e),u.options.endTime&&u.options.endTime&&e.seconds>=u.options.endTime&&(u.options.loop?u.play(u.options.startTime):u.pause())}),u.player.on("play",function(e){u.fire("play",e),u.options.startTime&&0===e.seconds&&u.play(u.options.startTime)}),u.player.on("pause",function(e){u.fire("pause",e)}),u.player.on("ended",function(e){u.fire("ended",e)}),u.player.on("loaded",function(e){u.fire("ready",e)}),u.player.on("volumechange",function(e){u.fire("volumechange",e)}),u.player.on("error",function(e){u.fire("error",e)})),"local"===u.type&&(u.$video||(u.$video=document.createElement("video"),u.options.showContols&&(u.$video.controls=!0),u.options.mute?u.$video.muted=!0:u.$video.volume&&(u.$video.volume=u.options.volume/100),u.options.loop&&(u.$video.loop=!0),u.$video.setAttribute("playsinline",""),u.$video.setAttribute("webkit-playsinline",""),u.$video.setAttribute("id",u.playerID),e.appendChild(u.$video),document.body.appendChild(e),Object.keys(u.videoID).forEach(function(e){var t,o,i,n;t=u.$video,o=u.videoID[e],i="video/".concat(e),(n=document.createElement("source")).src=o,n.type=i,t.appendChild(n)})),u.player=u.player||u.$video,u.player.addEventListener("playing",function(e){l||u.fire("started",e),l=1}),u.player.addEventListener("timeupdate",function(e){u.fire("timeupdate",e),u.options.endTime&&u.options.endTime&&this.currentTime>=u.options.endTime&&(u.options.loop?u.play(u.options.startTime):u.pause())}),u.player.addEventListener("play",function(e){u.fire("play",e)}),u.player.addEventListener("pause",function(e){u.fire("pause",e)}),u.player.addEventListener("ended",function(e){u.fire("ended",e)}),u.player.addEventListener("loadedmetadata",function(){u.videoWidth=this.videoWidth||1280,u.videoHeight=this.videoHeight||720,u.fire("ready"),u.options.autoplay&&u.play(u.options.startTime)}),u.player.addEventListener("volumechange",function(e){u.getVolume(function(e){u.options.volume=e}),u.fire("volumechange",e)}),u.player.addEventListener("error",function(e){u.fire("error",e)})),p(u.$video)})}},{key:"init",value:function(){this.playerID="VideoWorker-".concat(this.ID)}},{key:"loadAPI",value:function(){if(!p||!u){var e,t,o="";if("youtube"!==this.type||p||(p=1,o="https://www.youtube.com/iframe_api"),"vimeo"===this.type&&!u){if(u=1,void 0!==s.a.Vimeo)return;o="https://player.vimeo.com/api/player.js"}o&&(e=document.createElement("script"),t=document.getElementsByTagName("head")[0],e.src=o,t.appendChild(e),e=t=null)}}},{key:"onAPIready",value:function(e){var t;"youtube"===this.type&&(void 0!==s.a.YT&&0!==s.a.YT.loaded||d?"object"===n(s.a.YT)&&1===s.a.YT.loaded?e():y.done(function(){e()}):(d=1,window.onYouTubeIframeAPIReady=function(){window.onYouTubeIframeAPIReady=null,y.resolve("done"),e()})),"vimeo"===this.type&&(void 0!==s.a.Vimeo||c?void 0!==s.a.Vimeo?e():m.done(function(){e()}):(c=1,t=setInterval(function(){void 0!==s.a.Vimeo&&(clearInterval(t),m.resolve("done"),e())},20))),"local"===this.type&&e()}}])&&a(e.prototype,t),o&&a(e,o),i}()},function(e,t,o){"use strict";o.r(t),o.d(t,"default",function(){return n});var r=o(8),i=o(3),p=o.n(i);function n(){var e,t,l,o,n,i,a=0t&&(e=(t=a.container.width)*n.image.height/n.image.width,o=0,i+=(a.image.height-e)/2),"IFRAME"===r&&(e+=400,i-=200),n.css(n.$video,{width:"".concat(t,"px"),marginLeft:"".concat(o,"px"),height:"".concat(e,"px"),marginTop:"".concat(i,"px")})),a},o=e.prototype.initImg,e.prototype.initImg=function(){var e=this,t=o.apply(e);return e.options.videoSrc||(e.options.videoSrc=e.$item.getAttribute("data-jarallax-video")||null),e.options.videoSrc?(e.defaultInitImgResult=t,!0):t},n=e.prototype.canInitParallax,e.prototype.canInitParallax=function(){var o=this,e=n.apply(o);if(!o.options.videoSrc)return e;var t=new r.default(o.options.videoSrc,{autoplay:!0,loop:o.options.videoLoop,showContols:!1,startTime:o.options.videoStartTime||0,endTime:o.options.videoEndTime||0,mute:o.options.videoVolume?0:1,volume:o.options.videoVolume||0});function i(){o.image.$default_item&&(o.image.$item=o.image.$default_item,o.image.$item.style.display="block",o.coverImage(),o.clipContainer(),o.onScroll())}if(t.isValid())if(this.options.disableParallax()&&(e=!0,o.image.position="absolute",o.options.type="scroll",o.options.speed=1),e){if(t.on("ready",function(){var e;o.options.videoPlayOnlyVisible?(e=o.onScroll,o.onScroll=function(){e.apply(o),o.videoError||!o.options.videoLoop&&(o.options.videoLoop||o.videoEnded)||(o.isVisible()?t.play():t.pause())}):t.play()}),t.on("started",function(){o.image.$default_item=o.image.$item,o.image.$item=o.$video,o.image.width=o.video.videoWidth||1280,o.image.height=o.video.videoHeight||720,o.coverImage(),o.clipContainer(),o.onScroll(),o.image.$default_item&&(o.image.$default_item.style.display="none")}),t.on("ended",function(){o.videoEnded=!0,i()}),t.on("error",function(){o.videoError=!0,i()}),o.video=t,!o.defaultInitImgResult&&(o.image.src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7","local"!==t.type))return t.getImageURL(function(e){o.image.bgImage='url("'.concat(e,'")'),o.init()}),!1}else o.defaultInitImgResult||t.getImageURL(function(e){var t=o.$item.getAttribute("style");t&&o.$item.setAttribute("data-jarallax-original-styles",t),o.css(o.$item,{"background-image":'url("'.concat(e,'")'),"background-position":"center","background-size":"cover"})});return e},i=e.prototype.destroy,e.prototype.destroy=function(){var e=this;e.image.$default_item&&(e.image.$item=e.image.$default_item,delete e.image.$default_item),i.apply(e)})}}]); + + + + + +// Typed.js | Copyright (c) 2016 Matt Boldt | www.mattboldt.com +// The MIT License (MIT) +!function(t,s,e){"use strict";var i=function(t,s){var i=this;this.el=t,this.options={},Object.keys(r).forEach(function(t){i.options[t]=r[t]}),Object.keys(s).forEach(function(t){i.options[t]=s[t]}),this.isInput="input"===this.el.tagName.toLowerCase(),this.attr=this.options.attr,this.showCursor=!this.isInput&&this.options.showCursor,this.elContent=this.attr?this.el.getAttribute(this.attr):this.el.textContent,this.contentType=this.options.contentType,this.typeSpeed=this.options.typeSpeed,this.startDelay=this.options.startDelay,this.backSpeed=this.options.backSpeed,this.backDelay=this.options.backDelay,e&&this.options.stringsElement instanceof e?this.stringsElement=this.options.stringsElement[0]:this.stringsElement=this.options.stringsElement,this.strings=this.options.strings,this.strPos=0,this.arrayPos=0,this.stopNum=0,this.loop=this.options.loop,this.loopCount=this.options.loopCount,this.curLoop=0,this.stop=!1,this.cursorChar=this.options.cursorChar,this.shuffle=this.options.shuffle,this.sequence=[],this.build()};i.prototype={constructor:i,init:function(){var t=this;t.timeout=setTimeout(function(){for(var s=0;s":";";t.substr(s+1).charAt(0)!==h&&(a+=t.substr(s).charAt(0),s++,!(s+1>t.length)););s++,a+=h}}i.timeout=setTimeout(function(){if(s===t.length){if(i.options.onStringTyped(i.arrayPos),i.arrayPos===i.strings.length-1&&(i.options.callback(),i.curLoop++,i.loop===!1||i.curLoop===i.loopCount))return;i.timeout=setTimeout(function(){i.backspace(t,s)},i.backDelay)}else{0===s&&i.options.preStringTyped(i.arrayPos);var e=t.substr(0,s+1);i.attr?i.el.setAttribute(i.attr,e):i.isInput?i.el.value=e:"html"===i.contentType?i.el.innerHTML=e:i.el.textContent=e,s++,i.typewrite(t,s)}},e)},e)}},backspace:function(t,s){if(this.stop!==!0){var e=Math.round(70*Math.random())+this.backSpeed,i=this;i.timeout=setTimeout(function(){if("html"===i.contentType&&">"===t.substr(s).charAt(0)){for(var e="";"<"!==t.substr(s-1).charAt(0)&&(e-=t.substr(s).charAt(0),s--,!(s<0)););s--,e+="<"}var r=t.substr(0,s);i.attr?i.el.setAttribute(i.attr,r):i.isInput?i.el.value=r:"html"===i.contentType?i.el.innerHTML=r:i.el.textContent=r,s>i.stopNum?(s--,i.backspace(t,s)):s<=i.stopNum&&(i.arrayPos++,i.arrayPos===i.strings.length?(i.arrayPos=0,i.shuffle&&(i.sequence=i.shuffleArray(i.sequence)),i.init()):i.typewrite(i.strings[i.sequence[i.arrayPos]],s))},e)}},shuffleArray:function(t){var s,e,i=t.length;if(i)for(;--i;)e=Math.floor(Math.random()*(i+1)),s=t[e],t[e]=t[i],t[i]=s;return t},reset:function(){var t=this;clearInterval(t.timeout);this.el.getAttribute("id");this.el.textContent="","undefined"!=typeof this.cursor&&"undefined"!=typeof this.cursor.parentNode&&this.cursor.parentNode.removeChild(this.cursor),this.strPos=0,this.arrayPos=0,this.curLoop=0,this.options.resetCallback()}},i["new"]=function(t,e){var r=Array.prototype.slice.apply(s.querySelectorAll(t));r.forEach(function(t){var s=t._typed,r="object"==typeof e&&e;s&&s.reset(),t._typed=s=new i(t,r),"string"==typeof e&&s[e]()})},e&&(e.fn.typed=function(t){return this.each(function(){var s=e(this),r=s.data("typed"),o="object"==typeof t&&t;r&&r.reset(),s.data("typed",r=new i(this,o)),"string"==typeof t&&r[t]()})}),t.Typed=i;var r={strings:["These are the default values...","You know what you should do?","Use your own!","Have a great day!"],stringsElement:null,typeSpeed:0,startDelay:0,backSpeed:0,shuffle:!1,backDelay:500,loop:!1,loopCount:!1,showCursor:!0,cursorChar:"|",attr:null,contentType:"html",callback:function(){},preStringTyped:function(){},onStringTyped:function(){},resetCallback:function(){}}}(window,document,window.jQuery); \ No newline at end of file diff --git a/live/2index.php b/live/2index.php new file mode 100644 index 0000000..9443d48 --- /dev/null +++ b/live/2index.php @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + LIve TyClifford.com - Hobbies are my hobby. + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + +
+ + + + + + + +
+ Become a Patreon + Liberapay + Donate via PayPal + + + + + + +
+

+ +
+
+ + + + + + diff --git a/live/chat.php b/live/chat.php new file mode 100644 index 0000000..640bb62 --- /dev/null +++ b/live/chat.php @@ -0,0 +1,950 @@ + ['min'=> 0, 'max'=> 479, 'w'=> 320, 'h'=> 50], + 'mobile-rect' => ['min'=> 0, 'max'=> 599, 'w'=> 300, 'h'=> 250], + 'tablet-banner' => ['min'=> 480, 'max'=> 767, 'w'=> 468, 'h'=> 60], + 'leaderboard' => ['min'=> 768, 'max'=>1023, 'w'=> 728, 'h'=> 90], + 'billboard' => ['min'=> 1024, 'max'=>null, 'w'=> 970, 'h'=> 90], + 'large-rect' => ['min'=> 600, 'max'=>null, 'w'=> 336, 'h'=> 280], + 'custom' => ['min'=> 0, 'max'=>null, 'w'=>null, 'h'=>null], +]; +?> + +' . "\n"; + foreach ($ads as $ad_idx => $ad) { + $sizes = array_values(array_filter($ad['sizes'] ?? [])); + if (empty($sizes)) continue; + $zone_counter++; + $unit_id = 'adunit-' . $zone_counter; + $click = htmlspecialchars(trim($ad['click_url'] ?? '')); + echo "
\n"; + echo " \n"; + foreach ($sizes as $vi => $sv) { + $murl = htmlspecialchars(trim($sv['media_url'] ?? '')); + $mtype = $sv['media_type'] ?? 'image'; + if (!$murl) continue; + $wrap_o = $click ? "" : ''; + $media = ($mtype === 'video') + ? "" + : "\"\""; + echo "
\n"; + echo " {$wrap_o}Ad{$media}{$wrap_c}\n"; + echo "
\n"; + } + echo "
\n"; + } + echo '' . "\n"; +} +?> + + + + + + + + + + + + + + + + + + + + Live — TyClifford.com + + + + + + + + + + + + + + + + +
+ + + + + +
+
+
+
Ty Clifford
+
tyclifford.com / live
+
+
+ + +
+ + +
+ + +
+
+ +
+ +
+ + +
+

Live Chat

+
+
+ +
+ + +
+ + +
+
+

Support the stream

+

Hobbies are my hobby.

+
+ +

+ If you're enjoying the stream, consider throwing a few bucks in the jar. + Patreon members keep this going — even a free membership helps. + You can also tip via PayPal, no account needed. +

+ + +
+ + +
+

About

+

Ty Clifford

+

+ Based in Keyser, West Virginia. Nerd, tinkerer, and + creator. Hobbies are my hobby — from tech and podcasting to whatever + rabbit hole I fell into this week. +

+ Follow along on Patreon + and join the conversation. +

+
+ + + + +
+ + + + + +
+ + + +
+ +
+ + + + + + + diff --git a/live/index.php b/live/index.php new file mode 100644 index 0000000..3cef05b --- /dev/null +++ b/live/index.php @@ -0,0 +1,671 @@ + + + + + + + + + + + + + + + + + + + + + Live — TyClifford.com + + + + + + + + + + + + + +
+ + +
+
+
+
Ty Clifford
+
tyclifford.com / live
+
+ +
+ + +
+ + +
+ +
+ +
+ + +
+ + +
+ + +
+
+

Support the stream

+

Hobbies are my hobby.

+
+ +

+ If you're enjoying the stream, consider throwing a few bucks in the jar. + Patreon members keep this going - even a free membership helps. + You can also tip via PayPal, no account needed. +

+ + +
+ + +
+

About

+

Ty Clifford

+

+ Based in Keyser, West Virginia. Nerd, tinkerer, and + creator. Hobbies are my hobby — from tech and podcasting to whatever + rabbit hole I fell into this week. +

+ Follow along on Patreon + and join the conversation. +

+
+ + + + +
+ + +
+ + + +
+ +
+ + + diff --git a/live/template.php b/live/template.php new file mode 100644 index 0000000..c24e064 --- /dev/null +++ b/live/template.php @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + Member area at TyClifford.com - Hobbies are my hobby. + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +

+ + + Free Shoutcast HostingRadio Stream Hosting +

+ + + +

📅 Be a Guest

+
+ + Surveys
+
Quality control, Submit report.

+ + + + + +
+

+ +
+
+ + + + + + + + + \ No newline at end of file diff --git a/media_upload.php b/media_upload.php new file mode 100644 index 0000000..e4038f6 --- /dev/null +++ b/media_upload.php @@ -0,0 +1,137 @@ + 'image', + 'image/jpg' => 'image', + 'image/png' => 'image', + 'image/gif' => 'image', + 'image/webp' => 'image', + 'image/avif' => 'image', + 'image/svg+xml' => 'image', + 'video/mp4' => 'video', + 'video/quicktime' => 'video', // .mov + 'video/webm' => 'video', + 'video/ogg' => 'video', + 'video/x-msvideo' => 'video', // .avi + 'video/x-matroska'=> 'video', // .mkv +])); + +// ══════════════════════════════════════════════════════════════════════════════ + +header('Content-Type: application/json; charset=utf-8'); +header('X-Content-Type-Options: nosniff'); + +function upload_ok(array $data): never { + echo json_encode(['ok' => true] + $data, JSON_UNESCAPED_SLASHES); + exit; +} +function upload_err(string $msg, int $code = 400): never { + http_response_code($code); + echo json_encode(['ok' => false, 'error' => $msg]); + exit; +} + +// ── Auth ────────────────────────────────────────────────────────────────────── +if (!file_exists(UPLOAD_KEY_FILE)) upload_err('API key file not configured.', 503); + +$supplied = trim( + $_SERVER['HTTP_X_API_KEY'] ?? $_SERVER['HTTP_X_Api_Key'] ?? ($_GET['api_key'] ?? '') +); +if ($supplied === '') upload_err('Missing API key.', 401); + +$valid_keys = array_filter( + array_map('trim', file(UPLOAD_KEY_FILE)), + fn($l) => $l !== '' && !str_starts_with($l, '#') +); +$authed = false; +foreach ($valid_keys as $k) { if (hash_equals($k, $supplied)) { $authed = true; break; } } +if (!$authed) upload_err('Invalid API key.', 403); + +// ── Request validation ──────────────────────────────────────────────────────── +if ($_SERVER['REQUEST_METHOD'] !== 'POST') upload_err('POST required.', 405); +if (empty($_FILES['file'])) upload_err('No file received.'); + +$file = $_FILES['file']; +$error = $file['error'] ?? UPLOAD_ERR_NO_FILE; + +if ($error === UPLOAD_ERR_INI_SIZE || $error === UPLOAD_ERR_FORM_SIZE) + upload_err('File exceeds size limit.'); +if ($error !== UPLOAD_ERR_OK) + upload_err('Upload error code: ' . $error); + +if ($file['size'] > UPLOAD_MAX_BYTES) + upload_err('File too large (max ' . round(UPLOAD_MAX_BYTES / 1048576) . ' MB).'); + +// ── MIME detection (finfo, not extension trust) ─────────────────────────────── +$finfo = new finfo(FILEINFO_MIME_TYPE); +$mime = $finfo->file($file['tmp_name']); + +$allowed = unserialize(UPLOAD_ALLOWED); +if (!isset($allowed[$mime])) { + upload_err('File type not allowed: ' . $mime . '. Allowed: JPEG, PNG, GIF, WEBP, AVIF, SVG, MP4, MOV, WEBM, OGG, AVI, MKV.'); +} +$media_type = $allowed[$mime]; + +// ── Generate unique filename preserving original extension ──────────────────── +$orig_ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); +// Normalise common aliases +$ext_map = ['jpg' => 'jpg', 'jpeg' => 'jpg', 'jpe' => 'jpg', + 'png' => 'png', 'gif' => 'gif', 'webp' => 'webp', + 'avif' => 'avif', 'svg' => 'svg', + 'mp4' => 'mp4', 'mov' => 'mov', 'webm' => 'webm', + 'ogg' => 'ogg', 'ogv' => 'ogg', 'avi' => 'avi', 'mkv' => 'mkv']; +$safe_ext = $ext_map[$orig_ext] ?? $orig_ext; +$filename = bin2hex(random_bytes(12)) . '.' . $safe_ext; +$dest = UPLOAD_DIR . $filename; + +// ── Create upload directory if it doesn't exist ─────────────────────────────── +if (!is_dir(UPLOAD_DIR)) { + if (!mkdir(UPLOAD_DIR, 0755, true)) { + upload_err('Cannot create upload directory.', 500); + } + // Drop an index.html to prevent directory listing + file_put_contents(UPLOAD_DIR . 'index.html', ''); +} + +// ── Move file ───────────────────────────────────────────────────────────────── +if (!move_uploaded_file($file['tmp_name'], $dest)) { + upload_err('Failed to save file.', 500); +} + +// ── Respond ─────────────────────────────────────────────────────────────────── +upload_ok([ + 'url' => UPLOAD_URL . '/' . $filename, + 'filename' => $filename, + 'type' => $media_type, // 'image' | 'video' + 'mime' => $mime, + 'size' => $file['size'], +]); diff --git a/members/.htaccess b/members/.htaccess new file mode 100644 index 0000000..4ed1619 --- /dev/null +++ b/members/.htaccess @@ -0,0 +1,3 @@ + + Require all denied + diff --git a/members/gate.php b/members/gate.php new file mode 100644 index 0000000..22db473 --- /dev/null +++ b/members/gate.php @@ -0,0 +1,963 @@ + at the very top of any protected page. + * + * How it works: + * - Only emails pre-loaded into gate_emails.sqlite are allowed through. + * - Each email may hold exactly ONE active session at a time. A new login + * from any browser/device silently invalidates all previous sessions for + * that email (token rotation). + * - Banned emails see a 403 Denied screen regardless of session state. + * - Unknown emails see a "not on the list" error with a Patreon signup nudge. + * + * Requirements: PHP 7.4+, PDO + pdo_sqlite extension. + * Manage the allowlist with gate_admin.php. + */ + +// ── Config ──────────────────────────────────────────────────────────────────── +define('GATE_DB_PATH', __DIR__ . '/gate_emails.sqlite'); +define('GATE_SESSION_KEY', 'gate_verified'); +define('GATE_SESSION_EMAIL','gate_email'); +define('GATE_SESSION_TOKEN','gate_token'); +define('GATE_CAPTCHA_KEY', 'gate_captcha_answer'); +define('GATE_CAPTCHA_Q', 'gate_captcha_q'); +define('GATE_PATREON_URL', 'https://www.patreon.com/tyclifford'); // ← replace +// ───────────────────────────────────────────────────────────────────────────── + +session_start(); + +// ═════════════════════════════════════════════════════════════════════════════ +// DATABASE +// ═════════════════════════════════════════════════════════════════════════════ + +function gate_db(): PDO { + static $pdo = null; + if ($pdo !== null) return $pdo; + + $pdo = new PDO('sqlite:' . GATE_DB_PATH); + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $pdo->exec("PRAGMA journal_mode=WAL"); + + $pdo->exec("CREATE TABLE IF NOT EXISTS gate_emails ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + email TEXT NOT NULL UNIQUE COLLATE NOCASE, + status TEXT NOT NULL DEFAULT 'valid' + CHECK(status IN ('valid','banned')), + session_token TEXT DEFAULT NULL, + added_at DATETIME DEFAULT CURRENT_TIMESTAMP, + last_login DATETIME DEFAULT NULL, + login_ip TEXT DEFAULT NULL, + login_page TEXT DEFAULT NULL + )"); + + return $pdo; +} + +function gate_get_record(string $email): ?array { + $stmt = gate_db()->prepare( + "SELECT * FROM gate_emails WHERE email = :e COLLATE NOCASE LIMIT 1" + ); + $stmt->execute([':e' => strtolower(trim($email))]); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + return $row ?: null; +} + +function gate_create_session(string $email): void { + $token = bin2hex(random_bytes(32)); + $db = gate_db(); + + $stmt = $db->prepare( + "UPDATE gate_emails + SET session_token = :t, + last_login = CURRENT_TIMESTAMP, + login_ip = :ip, + login_page = :p + WHERE email = :e COLLATE NOCASE" + ); + $stmt->execute([ + ':t' => $token, + ':ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown', + ':p' => $_SERVER['REQUEST_URI'] ?? '', + ':e' => strtolower(trim($email)), + ]); + + $_SESSION[GATE_SESSION_KEY] = true; + $_SESSION[GATE_SESSION_EMAIL] = strtolower(trim($email)); + $_SESSION[GATE_SESSION_TOKEN] = $token; +} + +// ═════════════════════════════════════════════════════════════════════════════ +// REQUEST HANDLING +// ═════════════════════════════════════════════════════════════════════════════ + +function gate_kill_session(): void { + $_SESSION = []; + if (ini_get('session.use_cookies')) { + $p = session_get_cookie_params(); + setcookie(session_name(), '', time() - 42000, + $p['path'], $p['domain'], $p['secure'], $p['httponly']); + } + session_destroy(); + session_start(); +} + +if (!empty($_SESSION[GATE_SESSION_KEY])) { + $sess_email = $_SESSION[GATE_SESSION_EMAIL] ?? ''; + $sess_token = $_SESSION[GATE_SESSION_TOKEN] ?? ''; + $record = $sess_email !== '' ? gate_get_record($sess_email) : null; + + if ($record === null) { + gate_kill_session(); + $gate_form_error = 'Your access has been removed. Please contact an administrator.'; + } elseif ($record['status'] === 'banned') { + gate_kill_session(); + gate_render_denied($sess_email); + } elseif ($record['session_token'] !== $sess_token) { + gate_kill_session(); + $gate_form_error = 'Your session was ended because you signed in from another location.'; + } else { + return; + } +} + +$gate_form_error = $gate_form_error ?? ''; +$gate_show_patreon = false; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $email = strtolower(trim($_POST['email'] ?? '')); + $answer = trim($_POST['captcha'] ?? ''); + $expected = $_SESSION[GATE_CAPTCHA_KEY] ?? null; + + if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) { + $gate_form_error = 'Please enter a valid email address.'; + + } elseif ($expected === null || (int)$answer !== (int)$expected) { + $gate_form_error = 'Incorrect answer — please try again.'; + + } else { + $record = gate_get_record($email); + + if ($record === null) { + $gate_form_error = 'That email isn\'t on the access list yet.'; + $gate_show_patreon = true; + + } elseif ($record['status'] === 'banned') { + gate_render_denied($email); + + } else { + gate_create_session($email); + unset($_SESSION[GATE_CAPTCHA_KEY], $_SESSION[GATE_CAPTCHA_Q]); + $scheme = (($_SERVER['HTTPS'] ?? '') === 'on') ? 'https' : 'http'; + header('Location: ' . $scheme . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']); + exit; + } + } +} + +if (empty($_SESSION[GATE_CAPTCHA_KEY]) || $gate_form_error) { + $a = random_int(2, 12); + $b = random_int(1, 10); + $_SESSION[GATE_CAPTCHA_Q] = "$a + $b"; + $_SESSION[GATE_CAPTCHA_KEY] = $a + $b; +} +$captcha_question = $_SESSION[GATE_CAPTCHA_Q]; + +// ═════════════════════════════════════════════════════════════════════════════ +// STYLES +// ═════════════════════════════════════════════════════════════════════════════ + +function gate_styles(): void { ?> + + + + + + +Access Denied + + + +
+
+ + + + +
+
Account Suspended
+

+ Access for this email address has been revoked. +

+

status: 403 forbidden  |  contact to appeal

+
+ + + + + + + +Members Access + + + + +
+ + +
+ +
+ + Membership via Patreon +
+ +
+
+ Get free access
by joining my Patreon +
+
+ +

+ This area is open to all my Patreon members — no payment required. + Simply join as a free member and your email is added to the access list. + You can choose to upgrade to a paid tier anytime to support the work and unlock extra perks. +

+ + +
+ +
+ Sign up at least 24 hours before your first visit. + After joining Patreon, it may take up to 24 hours for your email to + be synced and activated on the access list. Plan ahead! +
+
+ + +
    +
  • + + Free member — full access to this area, no charge +
  • +
  • + + Paid tier — support the work & unlock extra perks +
  • +
+ + + + + Join Free on Patreon + +

+ Already a paid member?  Manage your tier → +

+ +
+ + +
+ +
+

Already a member

+

Sign in with
your Patreon email

+
+ +

+ Use the email address linked to your Patreon account. +

+ + + + + + + + + + +
+ +
+ + +
+ +
+ +
+
+ = +
+
+ +
+
+
+ + +
+ + + +
+ +
+ + + + Add email as valid + * php gate_admin.php invalidate Ban email (denies access, kills session) + * php gate_admin.php restore Restore a banned email to valid + * php gate_admin.php delete Remove email from allowlist entirely + * php gate_admin.php import Import emails from CSV (added as valid) + * php gate_admin.php export Export full allowlist to CSV + * + * CSV format (import): one email per line, or email,status with header row. + * CSV format (export): email,status,added_at,last_login,login_ip,login_page + * + * Requirements: PHP 7.4+, pdo_sqlite. + * Drop alongside gate.php and gate_emails.sqlite. + */ + +// ── Config ──────────────────────────────────────────────────────────────────── +define('GATE_DB_PATH', __DIR__ . '/gate_emails.sqlite'); +// ───────────────────────────────────────────────────────────────────────────── + +if (php_sapi_name() !== 'cli') { + http_response_code(403); + exit("This script must be run from the command line.\n"); +} + +// ═════════════════════════════════════════════════════════════════════════════ +// DATABASE +// ═════════════════════════════════════════════════════════════════════════════ + +function db(): PDO { + static $pdo = null; + if ($pdo !== null) return $pdo; + + if (!file_exists(GATE_DB_PATH)) { + // Create fresh DB if it doesn't exist yet (first-time setup) + out("Database not found — creating new one at: " . GATE_DB_PATH); + } + + $pdo = new PDO('sqlite:' . GATE_DB_PATH); + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $pdo->exec("PRAGMA journal_mode=WAL"); + + // Create or migrate to the current schema + $pdo->exec("CREATE TABLE IF NOT EXISTS gate_emails ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + email TEXT NOT NULL UNIQUE COLLATE NOCASE, + status TEXT NOT NULL DEFAULT 'valid' + CHECK(status IN ('valid','banned')), + session_token TEXT DEFAULT NULL, + added_at DATETIME DEFAULT CURRENT_TIMESTAMP, + last_login DATETIME DEFAULT NULL, + login_ip TEXT DEFAULT NULL, + login_page TEXT DEFAULT NULL + )"); + + // ── Migrate from the old multi-row schema (v1/v2) if present ───────────── + $cols = array_column( + $pdo->query("PRAGMA table_info(gate_emails)")->fetchAll(PDO::FETCH_ASSOC), + 'name' + ); + + $needs_migration = in_array('ip', $cols, true) && !in_array('status', $cols, true); + + if ($needs_migration) { + out("Detected old schema — migrating to allowlist format..."); + + // Pull distinct emails, preserving ban state + $rows = $pdo->query( + "SELECT email, + MAX(CASE WHEN invalidated = 1 THEN 1 ELSE 0 END) AS ever_banned, + MIN(visited) AS first_seen + FROM gate_emails + GROUP BY email COLLATE NOCASE" + )->fetchAll(PDO::FETCH_ASSOC); + + // Rename old table, create new one, migrate data + $pdo->exec("ALTER TABLE gate_emails RENAME TO gate_emails_legacy"); + $pdo->exec("CREATE TABLE gate_emails ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + email TEXT NOT NULL UNIQUE COLLATE NOCASE, + status TEXT NOT NULL DEFAULT 'valid' + CHECK(status IN ('valid','banned')), + session_token TEXT DEFAULT NULL, + added_at DATETIME DEFAULT CURRENT_TIMESTAMP, + last_login DATETIME DEFAULT NULL, + login_ip TEXT DEFAULT NULL, + login_page TEXT DEFAULT NULL + )"); + + $ins = $pdo->prepare( + "INSERT OR IGNORE INTO gate_emails (email, status, added_at) + VALUES (:e, :s, :a)" + ); + foreach ($rows as $r) { + $ins->execute([ + ':e' => strtolower(trim($r['email'])), + ':s' => $r['ever_banned'] ? 'banned' : 'valid', + ':a' => $r['first_seen'], + ]); + } + + $migrated = count($rows); + out("Migration complete — $migrated email(s) moved. Legacy table kept as 'gate_emails_legacy'."); + } + + return $pdo; +} + +/** Fetch the single row for an email or null. */ +function db_get(string $email): ?array { + $stmt = db()->prepare( + "SELECT * FROM gate_emails WHERE email = :e COLLATE NOCASE LIMIT 1" + ); + $stmt->execute([':e' => strtolower(trim($email))]); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + return $row ?: null; +} + +// ═════════════════════════════════════════════════════════════════════════════ +// OUTPUT HELPERS +// ═════════════════════════════════════════════════════════════════════════════ + +const COL_WIDTH = 110; + +function out(string $msg = ''): void { echo $msg . PHP_EOL; } +function fail(string $msg): never { fwrite(STDERR, "ERROR: $msg\n"); exit(1); } +function hr(): void { out(str_repeat('─', COL_WIDTH)); } + +function prompt_confirm(string $question): bool { + fwrite(STDOUT, $question . ' [y/N] '); + return strtolower(trim(fgets(STDIN))) === 'y'; +} + +function normalize_email(string $raw): string { + $email = strtolower(trim($raw)); + if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) { + fail("'$raw' is not a valid email address."); + } + return $email; +} + +function status_label(string $status): string { + return $status === 'banned' ? '🚫 banned' : '✓ valid'; +} + +function print_table(array $rows): void { + if (empty($rows)) { + out("(no records)"); + return; + } + hr(); + printf("%-4s %-38s %-10s %-19s %-19s %-15s %s\n", + 'ID', 'Email', 'Status', 'Added', 'Last Login', 'Login IP', 'Login Page'); + hr(); + foreach ($rows as $r) { + printf("%-4s %-38s %-10s %-19s %-19s %-15s %s\n", + $r['id'], + $r['email'], + status_label($r['status']), + $r['added_at'] ?? '—', + $r['last_login'] ?? 'never', + $r['login_ip'] ?? '—', + $r['login_page'] ?? '—' + ); + } + hr(); +} + +// ═════════════════════════════════════════════════════════════════════════════ +// COMMANDS +// ═════════════════════════════════════════════════════════════════════════════ + +/** List all emails (or only banned ones). */ +function cmd_list(bool $banned_only = false): void { + $sql = $banned_only + ? "SELECT * FROM gate_emails WHERE status = 'banned' ORDER BY email" + : "SELECT * FROM gate_emails ORDER BY email"; + $rows = db()->query($sql)->fetchAll(PDO::FETCH_ASSOC); + $n = count($rows); + $label = $banned_only ? "Banned emails" : "All allowlisted emails"; + out("$label ($n record" . ($n !== 1 ? 's' : '') . "):"); + print_table($rows); +} + +/** Add a new email as valid (no-op if already present). */ +function cmd_add(string $email): void { + $email = normalize_email($email); + + $existing = db_get($email); + if ($existing !== null) { + out("'$email' is already in the allowlist (status: {$existing['status']})."); + out("Use 'restore' to re-enable a banned email, or 'invalidate' to ban it."); + return; + } + + $stmt = db()->prepare( + "INSERT INTO gate_emails (email, status) VALUES (:e, 'valid')" + ); + $stmt->execute([':e' => $email]); + out("✓ Added '$email' as valid."); +} + +/** Ban an email — kills its active session token, denies all future access. */ +function cmd_invalidate(string $email): void { + $email = normalize_email($email); + $record = db_get($email); + + if ($record === null) { + fail("'$email' is not in the allowlist. Use 'add' first."); + } + if ($record['status'] === 'banned') { + out("'$email' is already banned."); + return; + } + + db()->prepare( + "UPDATE gate_emails + SET status = 'banned', + session_token = NULL + WHERE email = :e COLLATE NOCASE" + )->execute([':e' => $email]); + + out("✓ Banned '$email'."); + out(" Their session token has been cleared — the next page load will deny them."); +} + +/** Restore a banned email to valid status. */ +function cmd_restore(string $email): void { + $email = normalize_email($email); + $record = db_get($email); + + if ($record === null) { + fail("'$email' is not in the allowlist."); + } + if ($record['status'] === 'valid') { + out("'$email' is already valid. Nothing to restore."); + return; + } + + db()->prepare( + "UPDATE gate_emails SET status = 'valid' WHERE email = :e COLLATE NOCASE" + )->execute([':e' => $email]); + + out("✓ Restored '$email' to valid status."); + out(" They may now pass through the gate again."); +} + +/** Permanently remove an email from the allowlist. */ +function cmd_delete(string $email): void { + $email = normalize_email($email); + $record = db_get($email); + + if ($record === null) { + fail("'$email' is not in the allowlist."); + } + + if (!prompt_confirm("Permanently remove '$email' from the allowlist. Confirm?")) { + out("Aborted — nothing changed."); + exit(0); + } + + db()->prepare("DELETE FROM gate_emails WHERE email = :e COLLATE NOCASE") + ->execute([':e' => $email]); + + out("✓ Deleted '$email' from the allowlist."); +} + +// ───────────────────────────────────────────────────────────────────────────── +// CSV IMPORT +// ───────────────────────────────────────────────────────────────────────────── + +/** + * Import emails from a CSV file. + * + * Accepted formats: + * A) Single-column — one email per line (with or without header "email"): + * user@example.com + * + * B) Two-column — email + status (imported status respected): + * email,status + * user@example.com,valid + * bad@example.com,banned + * + * Rules: + * - New emails are inserted with status from the file (default: valid). + * - Existing emails: status is updated only if the CSV explicitly provides one + * AND it differs — otherwise the existing record is left untouched. + * - Blank lines and lines starting with # are skipped. + * - Invalid email addresses are reported and skipped. + */ +function cmd_import(string $path): void { + if (!file_exists($path)) { + fail("File not found: $path"); + } + + $handle = fopen($path, 'r'); + if (!$handle) fail("Cannot open: $path"); + + $added = 0; + $updated = 0; + $skipped = 0; + $invalid = 0; + $line_no = 0; + + // Peek at the first non-blank, non-comment line to detect format + $has_status_col = false; + $first_data = null; + $rewind_lines = []; + + while (!feof($handle)) { + $raw = fgets($handle); + $line_no++; + $trimmed = trim($raw); + if ($trimmed === '' || str_starts_with($trimmed, '#')) { + $rewind_lines[] = $raw; + continue; + } + $cols = str_getcsv($trimmed); + if (count($cols) >= 2) { + $h0 = strtolower(trim($cols[0])); + $h1 = strtolower(trim($cols[1])); + // If it looks like a header row, note format and skip it + if ($h0 === 'email' && in_array($h1, ['status', 'banned', 'valid'], true)) { + $has_status_col = true; + break; + } + // Data row with two cols — infer status column present + if (filter_var(trim($cols[0]), FILTER_VALIDATE_EMAIL) + && in_array($h1, ['valid', 'banned'], true)) { + $has_status_col = true; + } + } + $first_data = $raw; + break; + } + + // Restart from beginning + rewind($handle); + $line_no = 0; + + $db = db(); + $ins = $db->prepare( + "INSERT INTO gate_emails (email, status) VALUES (:e, :s) + ON CONFLICT(email) DO NOTHING" + ); + $upd = $db->prepare( + "UPDATE gate_emails SET status = :s WHERE email = :e COLLATE NOCASE" + ); + $chk = $db->prepare( + "SELECT status FROM gate_emails WHERE email = :e COLLATE NOCASE LIMIT 1" + ); + + $db->beginTransaction(); + + try { + while (!feof($handle)) { + $raw = fgets($handle); + if ($raw === false) break; + $line_no++; + $trimmed = trim($raw); + + // Skip blanks and comments + if ($trimmed === '' || str_starts_with($trimmed, '#')) continue; + + $cols = str_getcsv($trimmed); + $raw_email = trim($cols[0] ?? ''); + $raw_status = strtolower(trim($cols[1] ?? '')); + + // Skip header row + if (strtolower($raw_email) === 'email' && in_array($raw_status, ['status','valid','banned',''], true)) { + continue; + } + + // Validate email + $email = strtolower($raw_email); + if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + out(" Line $line_no: skipping invalid address — '$raw_email'"); + $invalid++; + continue; + } + + // Determine intended status + $status = in_array($raw_status, ['valid', 'banned'], true) ? $raw_status : 'valid'; + + // Check existing record + $chk->execute([':e' => $email]); + $existing_status = $chk->fetchColumn(); + + if ($existing_status === false) { + // New email — insert + $ins->execute([':e' => $email, ':s' => $status]); + $added++; + } elseif ($has_status_col && $raw_status !== '' && $existing_status !== $status) { + // Existing email with explicit new status — update + $upd->execute([':s' => $status, ':e' => $email]); + $updated++; + } else { + $skipped++; + } + } + + $db->commit(); + } catch (Throwable $ex) { + $db->rollBack(); + fail("Import failed at line $line_no: " . $ex->getMessage()); + } + + fclose($handle); + + out("✓ Import complete from: $path"); + out(" Added: $added"); + out(" Updated: $updated"); + out(" Skipped (already present, no change): $skipped"); + if ($invalid > 0) { + out(" Invalid (skipped): $invalid"); + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// CSV EXPORT +// ───────────────────────────────────────────────────────────────────────────── + +/** + * Export the full allowlist to a CSV file. + * + * Output columns: email, status, added_at, last_login, login_ip, login_page + * The session_token column is intentionally excluded for security. + */ +function cmd_export(string $path): void { + if (file_exists($path) && !prompt_confirm("'$path' already exists. Overwrite?")) { + out("Aborted — nothing changed."); + exit(0); + } + + $handle = fopen($path, 'w'); + if (!$handle) fail("Cannot write to: $path"); + + $rows = db()->query( + "SELECT email, status, added_at, last_login, login_ip, login_page + FROM gate_emails + ORDER BY email" + )->fetchAll(PDO::FETCH_ASSOC); + + // Header + fputcsv($handle, ['email', 'status', 'added_at', 'last_login', 'login_ip', 'login_page']); + + foreach ($rows as $r) { + fputcsv($handle, [ + $r['email'], + $r['status'], + $r['added_at'] ?? '', + $r['last_login'] ?? '', + $r['login_ip'] ?? '', + $r['login_page'] ?? '', + ]); + } + + fclose($handle); + + $n = count($rows); + out("✓ Exported $n email" . ($n !== 1 ? 's' : '') . " to: $path"); +} + +// ═════════════════════════════════════════════════════════════════════════════ +// DISPATCH +// ═════════════════════════════════════════════════════════════════════════════ + +$argv = $_SERVER['argv'] ?? []; +$cmd = $argv[1] ?? ''; +$arg = $argv[2] ?? ''; + +function usage(): void { + out("gate_admin.php — manage the gate.php email allowlist"); + out(""); + out("Commands:"); + out(" php gate_admin.php list List all allowlisted emails"); + out(" php gate_admin.php list-banned List only banned emails"); + out(" php gate_admin.php add Add email to allowlist as valid"); + out(" php gate_admin.php invalidate Ban email (kills session, denies access)"); + out(" php gate_admin.php restore Restore a banned email to valid"); + out(" php gate_admin.php delete Remove email from allowlist entirely"); + out(" php gate_admin.php import Import emails from CSV (added as valid)"); + out(" php gate_admin.php export Export allowlist to CSV"); + out(""); + out("CSV import format (two supported variants):"); + out(" Simple: one email per line"); + out(" Extended: email,status (header row optional; status = valid|banned)"); + out(""); + out("CSV export columns: email, status, added_at, last_login, login_ip, login_page"); + out(""); + out("Ban lifecycle:"); + out(" add → email joins allowlist as valid"); + out(" invalidate → status = banned, session_token cleared (denied on next request)"); + out(" restore → status = valid (user can log in again)"); + out(" delete → row removed entirely (user treated as unknown on next visit)"); +} + +match ($cmd) { + 'list' => cmd_list(false), + 'list-banned' => cmd_list(true), + 'add' => $arg ? cmd_add($arg) : fail("Usage: php gate_admin.php add "), + 'invalidate' => $arg ? cmd_invalidate($arg) : fail("Usage: php gate_admin.php invalidate "), + 'restore' => $arg ? cmd_restore($arg) : fail("Usage: php gate_admin.php restore "), + 'delete' => $arg ? cmd_delete($arg) : fail("Usage: php gate_admin.php delete "), + 'import' => $arg ? cmd_import($arg) : fail("Usage: php gate_admin.php import "), + 'export' => $arg ? cmd_export($arg) : fail("Usage: php gate_admin.php export "), + default => usage(), +}; diff --git a/members/gate_admin.php b/members/gate_admin.php new file mode 100644 index 0000000..74d0ab0 --- /dev/null +++ b/members/gate_admin.php @@ -0,0 +1,513 @@ + Add email as valid + * php gate_admin.php invalidate Ban email (denies access, kills session) + * php gate_admin.php restore Restore a banned email to valid + * php gate_admin.php delete Remove email from allowlist entirely + * php gate_admin.php import Import emails from CSV (added as valid) + * php gate_admin.php export Export full allowlist to CSV + * + * CSV format (import): one email per line, or email,status with header row. + * CSV format (export): email,status,added_at,last_login,login_ip,login_page + * + * Requirements: PHP 7.4+, pdo_sqlite. + * Drop alongside gate.php and gate_emails.sqlite. + */ + +// ── Config ──────────────────────────────────────────────────────────────────── +define('GATE_DB_PATH', __DIR__ . '/gate_emails.sqlite'); +// ───────────────────────────────────────────────────────────────────────────── + +if (php_sapi_name() !== 'cli') { + http_response_code(403); + exit("This script must be run from the command line.\n"); +} + +// ═════════════════════════════════════════════════════════════════════════════ +// DATABASE +// ═════════════════════════════════════════════════════════════════════════════ + +function db(): PDO { + static $pdo = null; + if ($pdo !== null) return $pdo; + + if (!file_exists(GATE_DB_PATH)) { + // Create fresh DB if it doesn't exist yet (first-time setup) + out("Database not found — creating new one at: " . GATE_DB_PATH); + } + + $pdo = new PDO('sqlite:' . GATE_DB_PATH); + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $pdo->exec("PRAGMA journal_mode=WAL"); + + // Create or migrate to the current schema + $pdo->exec("CREATE TABLE IF NOT EXISTS gate_emails ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + email TEXT NOT NULL UNIQUE COLLATE NOCASE, + status TEXT NOT NULL DEFAULT 'valid' + CHECK(status IN ('valid','banned')), + session_token TEXT DEFAULT NULL, + added_at DATETIME DEFAULT CURRENT_TIMESTAMP, + last_login DATETIME DEFAULT NULL, + login_ip TEXT DEFAULT NULL, + login_page TEXT DEFAULT NULL + )"); + + // ── Migrate from the old multi-row schema (v1/v2) if present ───────────── + $cols = array_column( + $pdo->query("PRAGMA table_info(gate_emails)")->fetchAll(PDO::FETCH_ASSOC), + 'name' + ); + + $needs_migration = in_array('ip', $cols, true) && !in_array('status', $cols, true); + + if ($needs_migration) { + out("Detected old schema — migrating to allowlist format..."); + + // Pull distinct emails, preserving ban state + $rows = $pdo->query( + "SELECT email, + MAX(CASE WHEN invalidated = 1 THEN 1 ELSE 0 END) AS ever_banned, + MIN(visited) AS first_seen + FROM gate_emails + GROUP BY email COLLATE NOCASE" + )->fetchAll(PDO::FETCH_ASSOC); + + // Rename old table, create new one, migrate data + $pdo->exec("ALTER TABLE gate_emails RENAME TO gate_emails_legacy"); + $pdo->exec("CREATE TABLE gate_emails ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + email TEXT NOT NULL UNIQUE COLLATE NOCASE, + status TEXT NOT NULL DEFAULT 'valid' + CHECK(status IN ('valid','banned')), + session_token TEXT DEFAULT NULL, + added_at DATETIME DEFAULT CURRENT_TIMESTAMP, + last_login DATETIME DEFAULT NULL, + login_ip TEXT DEFAULT NULL, + login_page TEXT DEFAULT NULL + )"); + + $ins = $pdo->prepare( + "INSERT OR IGNORE INTO gate_emails (email, status, added_at) + VALUES (:e, :s, :a)" + ); + foreach ($rows as $r) { + $ins->execute([ + ':e' => strtolower(trim($r['email'])), + ':s' => $r['ever_banned'] ? 'banned' : 'valid', + ':a' => $r['first_seen'], + ]); + } + + $migrated = count($rows); + out("Migration complete — $migrated email(s) moved. Legacy table kept as 'gate_emails_legacy'."); + } + + return $pdo; +} + +/** Fetch the single row for an email or null. */ +function db_get(string $email): ?array { + $stmt = db()->prepare( + "SELECT * FROM gate_emails WHERE email = :e COLLATE NOCASE LIMIT 1" + ); + $stmt->execute([':e' => strtolower(trim($email))]); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + return $row ?: null; +} + +// ═════════════════════════════════════════════════════════════════════════════ +// OUTPUT HELPERS +// ═════════════════════════════════════════════════════════════════════════════ + +const COL_WIDTH = 110; + +function out(string $msg = ''): void { echo $msg . PHP_EOL; } +function fail(string $msg): never { fwrite(STDERR, "ERROR: $msg\n"); exit(1); } +function hr(): void { out(str_repeat('─', COL_WIDTH)); } + +function prompt_confirm(string $question): bool { + fwrite(STDOUT, $question . ' [y/N] '); + return strtolower(trim(fgets(STDIN))) === 'y'; +} + +function normalize_email(string $raw): string { + $email = strtolower(trim($raw)); + if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) { + fail("'$raw' is not a valid email address."); + } + return $email; +} + +function status_label(string $status): string { + return $status === 'banned' ? '🚫 banned' : '✓ valid'; +} + +function print_table(array $rows): void { + if (empty($rows)) { + out("(no records)"); + return; + } + hr(); + printf("%-4s %-38s %-10s %-19s %-19s %-15s %s\n", + 'ID', 'Email', 'Status', 'Added', 'Last Login', 'Login IP', 'Login Page'); + hr(); + foreach ($rows as $r) { + printf("%-4s %-38s %-10s %-19s %-19s %-15s %s\n", + $r['id'], + $r['email'], + status_label($r['status']), + $r['added_at'] ?? '—', + $r['last_login'] ?? 'never', + $r['login_ip'] ?? '—', + $r['login_page'] ?? '—' + ); + } + hr(); +} + +// ═════════════════════════════════════════════════════════════════════════════ +// COMMANDS +// ═════════════════════════════════════════════════════════════════════════════ + +/** List all emails (or only banned ones). */ +function cmd_list(bool $banned_only = false): void { + $sql = $banned_only + ? "SELECT * FROM gate_emails WHERE status = 'banned' ORDER BY email" + : "SELECT * FROM gate_emails ORDER BY email"; + $rows = db()->query($sql)->fetchAll(PDO::FETCH_ASSOC); + $n = count($rows); + $label = $banned_only ? "Banned emails" : "All allowlisted emails"; + out("$label ($n record" . ($n !== 1 ? 's' : '') . "):"); + print_table($rows); +} + +/** Add a new email as valid (no-op if already present). */ +function cmd_add(string $email): void { + $email = normalize_email($email); + + $existing = db_get($email); + if ($existing !== null) { + out("'$email' is already in the allowlist (status: {$existing['status']})."); + out("Use 'restore' to re-enable a banned email, or 'invalidate' to ban it."); + return; + } + + $stmt = db()->prepare( + "INSERT INTO gate_emails (email, status) VALUES (:e, 'valid')" + ); + $stmt->execute([':e' => $email]); + out("✓ Added '$email' as valid."); +} + +/** Ban an email — kills its active session token, denies all future access. */ +function cmd_invalidate(string $email): void { + $email = normalize_email($email); + $record = db_get($email); + + if ($record === null) { + fail("'$email' is not in the allowlist. Use 'add' first."); + } + if ($record['status'] === 'banned') { + out("'$email' is already banned."); + return; + } + + db()->prepare( + "UPDATE gate_emails + SET status = 'banned', + session_token = NULL + WHERE email = :e COLLATE NOCASE" + )->execute([':e' => $email]); + + out("✓ Banned '$email'."); + out(" Their session token has been cleared — the next page load will deny them."); +} + +/** Restore a banned email to valid status. */ +function cmd_restore(string $email): void { + $email = normalize_email($email); + $record = db_get($email); + + if ($record === null) { + fail("'$email' is not in the allowlist."); + } + if ($record['status'] === 'valid') { + out("'$email' is already valid. Nothing to restore."); + return; + } + + db()->prepare( + "UPDATE gate_emails SET status = 'valid' WHERE email = :e COLLATE NOCASE" + )->execute([':e' => $email]); + + out("✓ Restored '$email' to valid status."); + out(" They may now pass through the gate again."); +} + +/** Permanently remove an email from the allowlist. */ +function cmd_delete(string $email): void { + $email = normalize_email($email); + $record = db_get($email); + + if ($record === null) { + fail("'$email' is not in the allowlist."); + } + + if (!prompt_confirm("Permanently remove '$email' from the allowlist. Confirm?")) { + out("Aborted — nothing changed."); + exit(0); + } + + db()->prepare("DELETE FROM gate_emails WHERE email = :e COLLATE NOCASE") + ->execute([':e' => $email]); + + out("✓ Deleted '$email' from the allowlist."); +} + +// ───────────────────────────────────────────────────────────────────────────── +// CSV IMPORT +// ───────────────────────────────────────────────────────────────────────────── + +/** + * Import emails from a CSV file. + * + * Accepted formats: + * A) Single-column — one email per line (with or without header "email"): + * user@example.com + * + * B) Two-column — email + status (imported status respected): + * email,status + * user@example.com,valid + * bad@example.com,banned + * + * Rules: + * - New emails are inserted with status from the file (default: valid). + * - Existing emails: status is updated only if the CSV explicitly provides one + * AND it differs — otherwise the existing record is left untouched. + * - Blank lines and lines starting with # are skipped. + * - Invalid email addresses are reported and skipped. + */ +function cmd_import(string $path): void { + if (!file_exists($path)) { + fail("File not found: $path"); + } + + $handle = fopen($path, 'r'); + if (!$handle) fail("Cannot open: $path"); + + $added = 0; + $updated = 0; + $skipped = 0; + $invalid = 0; + $line_no = 0; + + // Peek at the first non-blank, non-comment line to detect format + $has_status_col = false; + $first_data = null; + $rewind_lines = []; + + while (!feof($handle)) { + $raw = fgets($handle); + $line_no++; + $trimmed = trim($raw); + if ($trimmed === '' || str_starts_with($trimmed, '#')) { + $rewind_lines[] = $raw; + continue; + } + $cols = str_getcsv($trimmed); + if (count($cols) >= 2) { + $h0 = strtolower(trim($cols[0])); + $h1 = strtolower(trim($cols[1])); + // If it looks like a header row, note format and skip it + if ($h0 === 'email' && in_array($h1, ['status', 'banned', 'valid'], true)) { + $has_status_col = true; + break; + } + // Data row with two cols — infer status column present + if (filter_var(trim($cols[0]), FILTER_VALIDATE_EMAIL) + && in_array($h1, ['valid', 'banned'], true)) { + $has_status_col = true; + } + } + $first_data = $raw; + break; + } + + // Restart from beginning + rewind($handle); + $line_no = 0; + + $db = db(); + $ins = $db->prepare( + "INSERT INTO gate_emails (email, status) VALUES (:e, :s) + ON CONFLICT(email) DO NOTHING" + ); + $upd = $db->prepare( + "UPDATE gate_emails SET status = :s WHERE email = :e COLLATE NOCASE" + ); + $chk = $db->prepare( + "SELECT status FROM gate_emails WHERE email = :e COLLATE NOCASE LIMIT 1" + ); + + $db->beginTransaction(); + + try { + while (!feof($handle)) { + $raw = fgets($handle); + if ($raw === false) break; + $line_no++; + $trimmed = trim($raw); + + // Skip blanks and comments + if ($trimmed === '' || str_starts_with($trimmed, '#')) continue; + + $cols = str_getcsv($trimmed); + $raw_email = trim($cols[0] ?? ''); + $raw_status = strtolower(trim($cols[1] ?? '')); + + // Skip header row + if (strtolower($raw_email) === 'email' && in_array($raw_status, ['status','valid','banned',''], true)) { + continue; + } + + // Validate email + $email = strtolower($raw_email); + if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + out(" Line $line_no: skipping invalid address — '$raw_email'"); + $invalid++; + continue; + } + + // Determine intended status + $status = in_array($raw_status, ['valid', 'banned'], true) ? $raw_status : 'valid'; + + // Check existing record + $chk->execute([':e' => $email]); + $existing_status = $chk->fetchColumn(); + + if ($existing_status === false) { + // New email — insert + $ins->execute([':e' => $email, ':s' => $status]); + $added++; + } elseif ($has_status_col && $raw_status !== '' && $existing_status !== $status) { + // Existing email with explicit new status — update + $upd->execute([':s' => $status, ':e' => $email]); + $updated++; + } else { + $skipped++; + } + } + + $db->commit(); + } catch (Throwable $ex) { + $db->rollBack(); + fail("Import failed at line $line_no: " . $ex->getMessage()); + } + + fclose($handle); + + out("✓ Import complete from: $path"); + out(" Added: $added"); + out(" Updated: $updated"); + out(" Skipped (already present, no change): $skipped"); + if ($invalid > 0) { + out(" Invalid (skipped): $invalid"); + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// CSV EXPORT +// ───────────────────────────────────────────────────────────────────────────── + +/** + * Export the full allowlist to a CSV file. + * + * Output columns: email, status, added_at, last_login, login_ip, login_page + * The session_token column is intentionally excluded for security. + */ +function cmd_export(string $path): void { + if (file_exists($path) && !prompt_confirm("'$path' already exists. Overwrite?")) { + out("Aborted — nothing changed."); + exit(0); + } + + $handle = fopen($path, 'w'); + if (!$handle) fail("Cannot write to: $path"); + + $rows = db()->query( + "SELECT email, status, added_at, last_login, login_ip, login_page + FROM gate_emails + ORDER BY email" + )->fetchAll(PDO::FETCH_ASSOC); + + // Header + fputcsv($handle, ['email', 'status', 'added_at', 'last_login', 'login_ip', 'login_page']); + + foreach ($rows as $r) { + fputcsv($handle, [ + $r['email'], + $r['status'], + $r['added_at'] ?? '', + $r['last_login'] ?? '', + $r['login_ip'] ?? '', + $r['login_page'] ?? '', + ]); + } + + fclose($handle); + + $n = count($rows); + out("✓ Exported $n email" . ($n !== 1 ? 's' : '') . " to: $path"); +} + +// ═════════════════════════════════════════════════════════════════════════════ +// DISPATCH +// ═════════════════════════════════════════════════════════════════════════════ + +$argv = $_SERVER['argv'] ?? []; +$cmd = $argv[1] ?? ''; +$arg = $argv[2] ?? ''; + +function usage(): void { + out("gate_admin.php — manage the gate.php email allowlist"); + out(""); + out("Commands:"); + out(" php gate_admin.php list List all allowlisted emails"); + out(" php gate_admin.php list-banned List only banned emails"); + out(" php gate_admin.php add Add email to allowlist as valid"); + out(" php gate_admin.php invalidate Ban email (kills session, denies access)"); + out(" php gate_admin.php restore Restore a banned email to valid"); + out(" php gate_admin.php delete Remove email from allowlist entirely"); + out(" php gate_admin.php import Import emails from CSV (added as valid)"); + out(" php gate_admin.php export Export allowlist to CSV"); + out(""); + out("CSV import format (two supported variants):"); + out(" Simple: one email per line"); + out(" Extended: email,status (header row optional; status = valid|banned)"); + out(""); + out("CSV export columns: email, status, added_at, last_login, login_ip, login_page"); + out(""); + out("Ban lifecycle:"); + out(" add → email joins allowlist as valid"); + out(" invalidate → status = banned, session_token cleared (denied on next request)"); + out(" restore → status = valid (user can log in again)"); + out(" delete → row removed entirely (user treated as unknown on next visit)"); +} + +match ($cmd) { + 'list' => cmd_list(false), + 'list-banned' => cmd_list(true), + 'add' => $arg ? cmd_add($arg) : fail("Usage: php gate_admin.php add "), + 'invalidate' => $arg ? cmd_invalidate($arg) : fail("Usage: php gate_admin.php invalidate "), + 'restore' => $arg ? cmd_restore($arg) : fail("Usage: php gate_admin.php restore "), + 'delete' => $arg ? cmd_delete($arg) : fail("Usage: php gate_admin.php delete "), + 'import' => $arg ? cmd_import($arg) : fail("Usage: php gate_admin.php import "), + 'export' => $arg ? cmd_export($arg) : fail("Usage: php gate_admin.php export "), + default => usage(), +}; diff --git a/members/gate_emails.sqlite b/members/gate_emails.sqlite new file mode 100644 index 0000000..cb94c99 Binary files /dev/null and b/members/gate_emails.sqlite differ diff --git a/members/index.php b/members/index.php new file mode 100644 index 0000000..c8b06c6 --- /dev/null +++ b/members/index.php @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + Ty Clifford - Hobbies are my hobby. + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +

Currently no content

+ +
+

+ +
+ + +
+ + + + + + + diff --git a/patreon/index.php b/patreon/index.php new file mode 100644 index 0000000..69df830 --- /dev/null +++ b/patreon/index.php @@ -0,0 +1 @@ + diff --git a/player/README.md b/player/README.md new file mode 100644 index 0000000..4d26cb1 --- /dev/null +++ b/player/README.md @@ -0,0 +1,118 @@ +# TyClifford.com — Media Player + +A self-hosted multimedia player website built with PHP + SQLite + Video.js. +Styled to match the existing `gate.php` / `index.php` dark theme exactly. + +--- + +## Requirements + +- PHP 8.0+ with `pdo_sqlite`, `fileinfo` extensions +- Write permissions on the `data/` and `media/` directories + +--- + +## Installation + +1. **Upload** all files to your web server (e.g. `/live/` or wherever `/index.php` lives). + +2. **Set permissions** so PHP can write to these directories: + ```bash + mkdir -p data media/videos media/thumbs + chmod 755 data media media/videos media/thumbs + ``` + +3. **Visit** `index.php` — the SQLite database is created automatically on first load. + +4. **Log into admin** at `admin/login.php` + - Default password: **`admin`** + - ⚠️ Change it immediately in **Admin → Settings → Change Password** + +--- + +## File Structure + +``` +/ ← web root (deploy here) +├── index.php ← Main player + catalogue (home page) +├── catalogue.php ← Full video catalogue browse page +├── includes/ +│ ├── db.php ← SQLite bootstrap + helper functions +│ └── layout.php ← Shared HTML head, topbar, footer, pagination +├── admin/ +│ ├── index.php ← Video list with search + pagination +│ ├── add.php ← Add new video with multi-format sources +│ ├── edit.php ← Edit video metadata + manage sources +│ ├── settings.php ← Site settings, per-page count, password +│ ├── login.php ← Admin login +│ ├── logout.php ← Session destroy + redirect +│ └── auth.php ← Session auth helpers +├── media/ ← All media files (created automatically) +│ ├── videos/ ← Uploaded video files +│ └── thumbs/ ← Uploaded thumbnail images +└── data/ + └── media.db ← SQLite database (auto-created) +``` + +--- + +## Usage + +### Adding Videos (Admin) + +1. Go to `admin/add.php` +2. Fill in title, description, duration, sort order +3. Upload a thumbnail (JPG/PNG/WebP) +4. Upload one or more video files — each can be a different format or quality: + - **MP4** (H.264) — widest compatibility + - **WebM** (VP9) — smaller size, modern browsers + - **OGV** — Firefox fallback (legacy) +5. Set quality labels (1080p, 720p, 480p, etc.) +6. Save — Video.js will auto-select the best format the browser supports + +### Catalogue Per-Page + +Configurable in **Admin → Settings → Videos per page** (1–50). +The home page catalogue and the full catalogue page both respect this setting. + +### Publishing + +Videos can be toggled between **Published** and **Draft** from the admin list. +Only published videos appear on the public-facing pages. + +### Sorting + +Use the **Sort Order** field — lower numbers appear first. Default is 0. + +--- + +## Player Features + +- **Video.js 8.x** — responsive, accessible HTML5 player +- Custom skin matching the site's dark purple/green theme +- Playback rate controls: 0.5×, 1×, 1.25×, 1.5×, 2× +- Multiple source fallback — browser picks best format automatically +- Thumbnail posters +- Keyboard accessible + +--- + +## Security Notes + +- Change the default admin password (`admin`) immediately after setup +- Consider adding `.htaccess` HTTP auth or IP restriction to `/admin/` +- The `data/` directory should not be web-accessible; add to `.htaccess`: + ```apache + + Require all denied + + ``` +- For production, consider adding CSRF tokens to admin forms + +--- + +## Customisation + +- **Branding**: Edit site title/subtitle in Admin → Settings +- **Theme colours**: All CSS variables are in `includes/layout.php` `:root {}` +- **Social links**: Edit the About/Links cards in `index.php` diff --git a/player/admin/add.php b/player/admin/add.php new file mode 100644 index 0000000..936b82d --- /dev/null +++ b/player/admin/add.php @@ -0,0 +1,307 @@ +prepare(" + INSERT INTO videos (title, description, slug, thumbnail, duration, sort_order, published) + VALUES (?, ?, ?, ?, ?, ?, ?) + ")->execute([$title, $description, $slug, $thumbnail, $duration, $sort_order, $published]); + + $video_id = (int)$db->lastInsertId(); + + // Handle video source files + $source_labels = $_POST['source_label'] ?? []; + $source_quality = $_POST['source_quality'] ?? []; + $source_order = $_POST['source_sort'] ?? []; + $source_files = $_FILES['source_file'] ?? []; + + $videos_dir = MEDIA_DIR . 'videos/'; + if (!is_dir($videos_dir)) mkdir($videos_dir, 0755, true); + + $inserted_sources = 0; + $allowed_video = ['mp4','webm','ogv','ogg','mov','mkv','avi']; + + if (!empty($source_files['tmp_name'])) { + foreach ($source_files['tmp_name'] as $i => $tmp) { + if (empty($tmp) || !is_uploaded_file($tmp)) continue; + $orig_name = $source_files['name'][$i]; + $ext = strtolower(pathinfo($orig_name, PATHINFO_EXTENSION)); + if (!in_array($ext, $allowed_video)) continue; + + $fname = $slug . '_' . ($i+1) . '_' . time() . '.' . $ext; + if (move_uploaded_file($tmp, $videos_dir . $fname)) { + $label = trim($source_labels[$i] ?? ''); + $quality = trim($source_quality[$i] ?? ''); + $mime = mime_from_ext($fname); + $sort = (int)($source_order[$i] ?? $i); + $db->prepare(" + INSERT INTO video_sources (video_id, label, file_path, mime_type, quality, sort_order) + VALUES (?,?,?,?,?,?) + ")->execute([$video_id, $label, 'videos/' . $fname, $mime, $quality, $sort]); + $inserted_sources++; + } + } + } + + header('Location: edit.php?id=' . $video_id . '&saved=1'); + exit; + } + } +} + +render_head('Add Video — Admin', ' + .form-grid { display:grid; grid-template-columns:1fr; gap:1.2rem; } + @media(min-width:700px) { .form-grid { grid-template-columns:1fr 1fr; } } + .form-grid .span2 { grid-column:1/-1; } + + .sources-section { display:flex; flex-direction:column; gap:.85rem; } + .source-row { + background:var(--surface-2); border:1px solid var(--border); + border-radius:var(--r); padding:1rem; + display:grid; grid-template-columns:1fr 1fr 1fr auto; + gap:.75rem; align-items:end; + } + @media(max-width:700px) { .source-row { grid-template-columns:1fr 1fr; } } + .add-source-btn { align-self:flex-start; } + + .section-divider { + border:none; border-top:1px solid var(--border); margin:1.5rem 0 .5rem; + } + .section-heading { + font-family:"Share Tech Mono",monospace; font-size:.65rem; + letter-spacing:.15em; text-transform:uppercase; color:var(--text-3); + margin-bottom:.75rem; + } + .hint { font-size:.72rem; color:var(--text-3); margin-top:.2rem; } + + .admin-layout { display:grid; grid-template-columns:1fr; gap:1.5rem; } + @media(min-width:900px) { .admin-layout { grid-template-columns:220px 1fr; } } + .admin-nav { + background:var(--surface); border:1px solid var(--border); + border-radius:calc(var(--r)+2px); padding:1.2rem; + display:flex; flex-direction:column; gap:.4rem; + align-self:start; position:sticky; top:1rem; + } + .admin-nav-label { font-family:"Share Tech Mono",monospace; font-size:.58rem; letter-spacing:.18em; text-transform:uppercase; color:var(--text-3); margin-bottom:.3rem; padding-left:.5rem; } + .admin-nav a { display:flex; align-items:center; gap:.6rem; padding:.55rem .75rem; border-radius:var(--r); font-size:.82rem; color:var(--text-2); text-decoration:none; transition:background .15s,color .15s; } + .admin-nav a:hover { background:rgba(255,255,255,.05); color:var(--text); } + .admin-nav a.active { background:rgba(176,96,255,.12); color:var(--purple); } + .nav-divider { border:none; border-top:1px solid var(--border); margin:.4rem 0; } +'); +?> +
+
+
+
+
Ty Clifford
+
admin / add video
+
+
+ +
+ + +
+ + +
+ + +
+
+
+

New Entry

+

Add Video

+
+ ← Back +
+ +
+ + +
+

Video Details

+
+
+ + +
+
+ + +
+
+ + + e.g. 3600 = 1 hour. Leave 0 if unknown. +
+
+ + + Lower numbers appear first. +
+
+ + + JPG, PNG, WebP or GIF. Shown as 16:9 preview. +
+
+ + Uncheck to save as draft. +
+
+
+ + +
+

Video Sources (Multiple Formats)

+

+ Upload the same video in multiple formats/qualities. The browser will automatically pick the best one it can play. + Accepted: MP4, WebM, OGV, MOV, MKV, AVI. +

+ +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+ +
+ + Cancel +
+ +
+
+
+ + +
+ + + + diff --git a/player/admin/auth.php b/player/admin/auth.php new file mode 100644 index 0000000..e59568f --- /dev/null +++ b/player/admin/auth.php @@ -0,0 +1,36 @@ +prepare("SELECT * FROM video_sources WHERE id=? AND video_id=?"); + $src->execute([$sid, $id]); + $s = $src->fetch(); + if ($s) { + $f = MEDIA_DIR . $s['file_path']; + if (file_exists($f)) unlink($f); + $db->prepare("DELETE FROM video_sources WHERE id=?")->execute([$sid]); + $msg = 'Source file removed.'; + } + $video = get_video_by_id($id); // refresh +} + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $action = $_POST['action'] ?? 'save'; + + if ($action === 'save') { + $title = trim($_POST['title'] ?? ''); + $description = trim($_POST['description'] ?? ''); + $duration = (int)($_POST['duration'] ?? 0); + $sort_order = (int)($_POST['sort_order'] ?? 0); + $published = isset($_POST['published']) ? 1 : 0; + + if (!$title) { + $error = 'Title is required.'; + } else { + $slug = make_slug($title, $id); + + // Handle thumbnail replacement + $thumbnail = $video['thumbnail']; + if (!empty($_FILES['thumbnail']['tmp_name'])) { + $ext = strtolower(pathinfo($_FILES['thumbnail']['name'], PATHINFO_EXTENSION)); + $allowed = ['jpg','jpeg','png','webp','gif']; + if (in_array($ext, $allowed)) { + $thumb_dir = MEDIA_DIR . 'thumbs/'; + if (!is_dir($thumb_dir)) mkdir($thumb_dir, 0755, true); + // Remove old thumb + if ($thumbnail && file_exists(MEDIA_DIR . $thumbnail)) unlink(MEDIA_DIR . $thumbnail); + $fname = $slug . '_thumb_' . time() . '.' . $ext; + if (move_uploaded_file($_FILES['thumbnail']['tmp_name'], $thumb_dir . $fname)) { + $thumbnail = 'thumbs/' . $fname; + } + } else { + $error = 'Thumbnail must be JPG, PNG, WebP, or GIF.'; + } + } + + if (!$error) { + $db->prepare(" + UPDATE videos SET title=?, description=?, slug=?, thumbnail=?, + duration=?, sort_order=?, published=?, + updated_at=datetime('now') + WHERE id=? + ")->execute([$title, $description, $slug, $thumbnail, $duration, $sort_order, $published, $id]); + + // Update existing sources sort/label/quality + $upd_labels = $_POST['src_label'] ?? []; + $upd_quality = $_POST['src_quality'] ?? []; + $upd_sort = $_POST['src_sort'] ?? []; + $upd_ids = $_POST['src_id'] ?? []; + foreach ($upd_ids as $i => $sid) { + $db->prepare("UPDATE video_sources SET label=?, quality=?, sort_order=? WHERE id=? AND video_id=?") + ->execute([ + $upd_labels[$i] ?? '', + $upd_quality[$i] ?? '', + (int)($upd_sort[$i] ?? $i), + (int)$sid, $id + ]); + } + + // Add new source files + $new_labels = $_POST['new_source_label'] ?? []; + $new_quality = $_POST['new_source_quality'] ?? []; + $new_sort = $_POST['new_source_sort'] ?? []; + $new_files = $_FILES['new_source_file'] ?? []; + $videos_dir = MEDIA_DIR . 'videos/'; + if (!is_dir($videos_dir)) mkdir($videos_dir, 0755, true); + $allowed_video = ['mp4','webm','ogv','ogg','mov','mkv','avi']; + + if (!empty($new_files['tmp_name'])) { + foreach ($new_files['tmp_name'] as $i => $tmp) { + if (empty($tmp) || !is_uploaded_file($tmp)) continue; + $orig = $new_files['name'][$i]; + $ext = strtolower(pathinfo($orig, PATHINFO_EXTENSION)); + if (!in_array($ext, $allowed_video)) continue; + $fname = $slug . '_' . time() . '_' . $i . '.' . $ext; + if (move_uploaded_file($tmp, $videos_dir . $fname)) { + $db->prepare("INSERT INTO video_sources (video_id,label,file_path,mime_type,quality,sort_order) VALUES (?,?,?,?,?,?)") + ->execute([$id, + trim($new_labels[$i] ?? ''), + 'videos/' . $fname, + mime_from_ext($fname), + trim($new_quality[$i] ?? '720p'), + (int)($new_sort[$i] ?? $i), + ]); + } + } + } + + header('Location: edit.php?id=' . $id . '&saved=1'); + exit; + } + } + } +} + +// Refresh +$video = get_video_by_id($id); + +render_head('Edit Video — Admin', ' + .form-grid { display:grid; grid-template-columns:1fr; gap:1.2rem; } + @media(min-width:700px) { .form-grid { grid-template-columns:1fr 1fr; } } + .form-grid .span2 { grid-column:1/-1; } + .section-heading { font-family:"Share Tech Mono",monospace; font-size:.65rem; letter-spacing:.15em; text-transform:uppercase; color:var(--text-3); margin-bottom:.75rem; } + .hint { font-size:.72rem; color:var(--text-3); margin-top:.2rem; } + + .source-list { display:flex; flex-direction:column; gap:.75rem; } + .source-item { + background:var(--surface-2); border:1px solid var(--border); + border-radius:var(--r); padding:.85rem 1rem; + } + .source-item-header { display:flex; align-items:center; justify-content:space-between; gap:.75rem; margin-bottom:.75rem; flex-wrap:wrap; } + .source-item-file { font-family:"Share Tech Mono",monospace; font-size:.68rem; color:var(--text-2); word-break:break-all; } + .source-item-mime { font-family:"Share Tech Mono",monospace; font-size:.6rem; color:var(--text-3); } + .source-item-fields { display:grid; grid-template-columns:1fr 1fr 80px; gap:.6rem; } + @media(max-width:600px) { .source-item-fields { grid-template-columns:1fr 1fr; } } + + .add-source-row { background:var(--surface-2); border:1px solid var(--border-2); border-radius:var(--r); padding:1rem; display:grid; grid-template-columns:1fr 1fr 1fr auto; gap:.75rem; align-items:end; } + @media(max-width:700px) { .add-source-row { grid-template-columns:1fr 1fr; } } + + .admin-layout { display:grid; grid-template-columns:1fr; gap:1.5rem; } + @media(min-width:900px) { .admin-layout { grid-template-columns:220px 1fr; } } + .admin-nav { background:var(--surface); border:1px solid var(--border); border-radius:calc(var(--r)+2px); padding:1.2rem; display:flex; flex-direction:column; gap:.4rem; align-self:start; position:sticky; top:1rem; } + .admin-nav-label { font-family:"Share Tech Mono",monospace; font-size:.58rem; letter-spacing:.18em; text-transform:uppercase; color:var(--text-3); margin-bottom:.3rem; padding-left:.5rem; } + .admin-nav a { display:flex; align-items:center; gap:.6rem; padding:.55rem .75rem; border-radius:var(--r); font-size:.82rem; color:var(--text-2); text-decoration:none; transition:background .15s,color .15s; } + .admin-nav a:hover { background:rgba(255,255,255,.05); color:var(--text); } + .nav-divider { border:none; border-top:1px solid var(--border); margin:.4rem 0; } + + .thumb-preview { width:100%; max-width:240px; aspect-ratio:16/9; object-fit:cover; border-radius:var(--r); border:1px solid var(--border); background:var(--surface-2); display:block; margin-bottom:.5rem; } +'); +?> +
+
+
+
+
Ty Clifford
+
admin / edit video
+
+
+ +
+ + +
+ + +
+ + +
+ + +
+
+
+

Editing

+

+
+ ← Back +
+ +
+ + + +
+

Video Details

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + Current thumbnail + Current: + + + Upload new image to replace current thumbnail. +
+
+
+ + + +
+

Existing Sources

+
+ $s): ?> +
+
+
+
+
+
+ + + Remove + +
+
+ +
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
+ + + +
+

Add New Source Files

+

Upload additional formats (MP4, WebM, OGV, MOV, MKV, AVI).

+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+ +
+ + Cancel +
+
+
+
+ + +
+ + + + diff --git a/player/admin/index.php b/player/admin/index.php new file mode 100644 index 0000000..9c4e318 --- /dev/null +++ b/player/admin/index.php @@ -0,0 +1,295 @@ +prepare("DELETE FROM videos WHERE id=?")->execute([$id]); + $msg = 'Video deleted.'; + } + } + + if ($action === 'toggle_published' && !empty($_POST['id'])) { + $id = (int)$_POST['id']; + $row = $db->prepare("SELECT published FROM videos WHERE id=?"); + $row->execute([$id]); + $cur = $row->fetchColumn(); + $db->prepare("UPDATE videos SET published=? WHERE id=?")->execute([$cur ? 0 : 1, $id]); + $msg = 'Video ' . ($cur ? 'unpublished' : 'published') . '.'; + } +} + +// Search + pagination +$search = trim($_GET['q'] ?? ''); +$page = max(1, (int)($_GET['page'] ?? 1)); +$per_page = (int)setting('catalogue_per_page', '10'); + +$where_sql = $search ? "WHERE v.title LIKE :q OR v.description LIKE :q" : ''; +$bind = $search ? [':q' => '%' . $search . '%'] : []; + +$total_stmt = $db->prepare("SELECT COUNT(*) FROM videos v $where_sql"); +$total_stmt->execute($bind); +$total = (int)$total_stmt->fetchColumn(); +$total_pages = max(1, (int)ceil($total / $per_page)); +$page = min($page, $total_pages); +$offset = ($page - 1) * $per_page; + +$list_stmt = $db->prepare(" + SELECT v.id, v.title, v.slug, v.thumbnail, v.duration, v.published, v.sort_order, + v.created_at, + COUNT(vs.id) AS source_count + FROM videos v + LEFT JOIN video_sources vs ON vs.video_id = v.id + $where_sql + GROUP BY v.id + ORDER BY v.sort_order ASC, v.id DESC + LIMIT :limit OFFSET :offset +"); +foreach ($bind as $k => $val) $list_stmt->bindValue($k, $val); +$list_stmt->bindValue(':limit', $per_page, PDO::PARAM_INT); +$list_stmt->bindValue(':offset', $offset, PDO::PARAM_INT); +$list_stmt->execute(); +$videos = $list_stmt->fetchAll(); + +$qs = fn($p) => '?' . http_build_query(array_filter(['q' => $search, 'page' => $p])); + +render_head('Admin — TyClifford.com', ' + .admin-layout { display:grid; grid-template-columns:1fr; gap:1.5rem; } + @media(min-width:900px) { .admin-layout { grid-template-columns:220px 1fr; } } + + .admin-nav { + background:var(--surface); border:1px solid var(--border); + border-radius:calc(var(--r)+2px); padding:1.2rem; + display:flex; flex-direction:column; gap:.4rem; + align-self:start; position:sticky; top:1rem; + } + .admin-nav-label { font-family:"Share Tech Mono",monospace; font-size:.58rem; letter-spacing:.18em; text-transform:uppercase; color:var(--text-3); margin-bottom:.3rem; padding-left:.5rem; } + .admin-nav a { + display:flex; align-items:center; gap:.6rem; + padding:.55rem .75rem; border-radius:var(--r); + font-size:.82rem; color:var(--text-2); text-decoration:none; + transition:background .15s, color .15s; + } + .admin-nav a:hover { background:rgba(255,255,255,.05); color:var(--text); } + .admin-nav a.active { background:rgba(176,96,255,.12); color:var(--purple); } + .admin-nav a svg { flex-shrink:0; } + .nav-divider { border:none; border-top:1px solid var(--border); margin:.4rem 0; } + + .admin-main { display:flex; flex-direction:column; gap:1rem; } + .admin-toolbar { display:flex; align-items:center; gap:.75rem; flex-wrap:wrap; justify-content:space-between; } + .search-form { display:flex; gap:.5rem; flex:1; min-width:0; max-width:360px; } + .search-form input { flex:1; } + + .video-table { width:100%; border-collapse:collapse; } + .video-table th, .video-table td { + padding:.65rem .85rem; text-align:left; + border-bottom:1px solid var(--border); + font-size:.82rem; + } + .video-table th { + font-family:"Share Tech Mono",monospace; font-size:.62rem; letter-spacing:.12em; + text-transform:uppercase; color:var(--text-3); background:var(--surface-2); + } + .video-table tr:hover td { background:rgba(255,255,255,.02); } + .video-table .thumb-cell { width:80px; } + .video-table .thumb-cell img { width:72px; height:40px; object-fit:cover; border-radius:4px; display:block; background:var(--surface-2); } + .video-table .thumb-cell .no-thumb-sm { width:72px; height:40px; border-radius:4px; background:var(--surface-2); display:flex; align-items:center; justify-content:center; font-family:"Share Tech Mono",monospace; font-size:.45rem; color:var(--text-3); } + .video-table .title-cell { max-width:250px; } + .video-table .title-cell a { color:var(--text); text-decoration:none; font-weight:600; } + .video-table .title-cell a:hover { color:var(--purple); } + .video-table .title-cell .slug { font-family:"Share Tech Mono",monospace; font-size:.65rem; color:var(--text-3); display:block; margin-top:.15rem; } + .video-table .actions-cell { white-space:nowrap; } + .badge { display:inline-flex; align-items:center; padding:.18em .55em; border-radius:99px; font-family:"Share Tech Mono",monospace; font-size:.6rem; letter-spacing:.08em; } + .badge-green { background:rgba(0,232,122,.12); color:var(--green); border:1px solid rgba(0,232,122,.25); } + .badge-gray { background:rgba(255,255,255,.05); color:var(--text-3); border:1px solid var(--border-2); } + + .table-wrap { overflow-x:auto; background:var(--surface); border:1px solid var(--border); border-radius:calc(var(--r)+2px); } + + .admin-footer-bar { display:flex; align-items:center; justify-content:space-between; gap:1rem; flex-wrap:wrap; } + .rec-count { font-family:"Share Tech Mono",monospace; font-size:.63rem; color:var(--text-3); letter-spacing:.06em; } +'); +?> +
+
+
+
+
Ty Clifford
+
admin / dashboard
+
+
+ +
+ + +
+ + +
+ + + + + +
+ +
+
+ + + + ✕ Clear + +
+ + + Add Video + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ThumbTitle / SlugDurationSourcesStatusAddedActions
+ Add one!' ?> +
+ + + +
NO IMG
+ +
+ + / + + + + file + + + + + + + +
+ + + Edit + +
+ + + +
+
+ + + +
+
+
+
+ + 1 || $total > 0): ?> + + + +
+
+ + +
+ + diff --git a/player/admin/login.php b/player/admin/login.php new file mode 100644 index 0000000..1e90005 --- /dev/null +++ b/player/admin/login.php @@ -0,0 +1,81 @@ + +
+
+
+
+
Ty Clifford
+
admin dashboard
+
+
+ ← Back to site +
+ + + + +
+ + diff --git a/player/admin/logout.php b/player/admin/logout.php new file mode 100644 index 0000000..021547f --- /dev/null +++ b/player/admin/logout.php @@ -0,0 +1,5 @@ +query("SELECT COUNT(*) FROM videos")->fetchColumn(); +$total_sources = $db->query("SELECT COUNT(*) FROM video_sources")->fetchColumn(); +$db_size = file_exists(DB_PATH) ? round(filesize(DB_PATH)/1024, 1) : 0; +$media_size = 0; +if (is_dir(MEDIA_DIR)) { + $iter = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(MEDIA_DIR, FilesystemIterator::SKIP_DOTS)); + foreach ($iter as $f) $media_size += $f->getSize(); + $media_size = round($media_size / 1048576, 1); +} + +$msg_type = str_starts_with($msg, 'ERROR') ? 'error' : 'success'; + +render_head('Settings — Admin', ' + .admin-layout { display:grid; grid-template-columns:1fr; gap:1.5rem; } + @media(min-width:900px) { .admin-layout { grid-template-columns:220px 1fr; } } + .admin-nav { background:var(--surface); border:1px solid var(--border); border-radius:calc(var(--r)+2px); padding:1.2rem; display:flex; flex-direction:column; gap:.4rem; align-self:start; position:sticky; top:1rem; } + .admin-nav-label { font-family:"Share Tech Mono",monospace; font-size:.58rem; letter-spacing:.18em; text-transform:uppercase; color:var(--text-3); margin-bottom:.3rem; padding-left:.5rem; } + .admin-nav a { display:flex; align-items:center; gap:.6rem; padding:.55rem .75rem; border-radius:var(--r); font-size:.82rem; color:var(--text-2); text-decoration:none; transition:background .15s,color .15s; } + .admin-nav a:hover { background:rgba(255,255,255,.05); color:var(--text); } + .admin-nav a.active { background:rgba(176,96,255,.12); color:var(--purple); } + .nav-divider { border:none; border-top:1px solid var(--border); margin:.4rem 0; } + .form-grid { display:grid; grid-template-columns:1fr; gap:1rem; } + @media(min-width:600px) { .form-grid { grid-template-columns:1fr 1fr; } } + .section-heading { font-family:"Share Tech Mono",monospace; font-size:.65rem; letter-spacing:.15em; text-transform:uppercase; color:var(--text-3); margin-bottom:.75rem; } + .hint { font-size:.72rem; color:var(--text-3); margin-top:.2rem; } + .stat-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(130px,1fr)); gap:.75rem; } + .stat-tile { background:var(--surface-2); border:1px solid var(--border); border-radius:var(--r); padding:1rem; display:flex; flex-direction:column; gap:.3rem; } + .stat-val { font-family:"Share Tech Mono",monospace; font-size:1.4rem; color:var(--text); font-weight:700; } + .stat-label{ font-family:"Share Tech Mono",monospace; font-size:.6rem; letter-spacing:.1em; text-transform:uppercase; color:var(--text-3); } +'); +?> +
+
+
+
+
Ty Clifford
+
admin / settings
+
+
+ +
+ + +
+ + +
+ + +
+ + +
+

Overview

+
+
+ + Videos +
+
+ + Source Files +
+
+ KB + Database +
+
+ MB + Media Storage +
+
+
+ + +
+

General Settings

+
+ +
+
+ + +
+
+ + +
+
+ + + How many thumbnails to show per page (1–50). +
+
+ +
+
+ + +
+

Change Password

+
+ +
+
+ + +
+
+
+ + + Minimum 6 characters. +
+
+ + +
+
+ +
+
+ + +
+

File Locations

+
+
Database: data/media.db
+
Videos: media/videos/
+
Thumbnails: media/thumbs/
+
Default password: admin — change immediately!
+
+
+ +
+
+ + +
+ + diff --git a/player/catalogue.php b/player/catalogue.php new file mode 100644 index 0000000..676d5f2 --- /dev/null +++ b/player/catalogue.php @@ -0,0 +1,128 @@ + +
+ + + + + + +
+
+ +

no videos in the catalogue yet

+
+
+ + + + 1): ?> + + + + + +
+ + diff --git a/player/data/.htaccess b/player/data/.htaccess new file mode 100644 index 0000000..b66e808 --- /dev/null +++ b/player/data/.htaccess @@ -0,0 +1 @@ +Require all denied diff --git a/player/data/media.db b/player/data/media.db new file mode 100644 index 0000000..370f174 Binary files /dev/null and b/player/data/media.db differ diff --git a/player/embed.php b/player/embed.php new file mode 100644 index 0000000..2507e99 --- /dev/null +++ b/player/embed.php @@ -0,0 +1,246 @@ + on third-party sites. + * + * Usage: + */ +require_once __DIR__ . '/includes/db.php'; + +$slug = trim($_GET['v'] ?? ''); +$video = $slug ? get_video_by_slug($slug) : null; + +// Determine absolute base URL for media files. +// We need absolute URLs here because the embed lives on a foreign domain. +$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; +$host = $_SERVER['HTTP_HOST'] ?? 'localhost'; +$script = dirname($_SERVER['SCRIPT_NAME'] ?? '/'); +$script = rtrim($script, '/'); +$base_url = $scheme . '://' . $host . $script . '/'; // e.g. https://tyclifford.com/live/ + +// X-Frame-Options: allow embedding from any origin. +// If you want to restrict, change ALLOWALL to SAMEORIGIN or remove for CSP header instead. +header('X-Frame-Options: ALLOWALL'); +header('Content-Security-Policy: frame-ancestors *'); +?> + + + + + + <?= $video ? htmlspecialchars($video['title'], ENT_QUOTES) . ' — TyClifford.com' : 'Video Player' ?> + + + + + + +
+
+ + + + + + + + + + +
+ + + + + +

+
+ + +
+ + + + + + + diff --git a/player/includes/db.php b/player/includes/db.php new file mode 100644 index 0000000..183861b --- /dev/null +++ b/player/includes/db.php @@ -0,0 +1,168 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); + $pdo->exec('PRAGMA journal_mode=WAL'); + $pdo->exec('PRAGMA foreign_keys=ON'); + + // Schema + $pdo->exec(" + CREATE TABLE IF NOT EXISTS settings ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL + ); + + CREATE TABLE IF NOT EXISTS videos ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT NOT NULL DEFAULT 'Untitled', + description TEXT NOT NULL DEFAULT '', + slug TEXT NOT NULL UNIQUE, + thumbnail TEXT NOT NULL DEFAULT '', + duration INTEGER NOT NULL DEFAULT 0, + sort_order INTEGER NOT NULL DEFAULT 0, + published INTEGER NOT NULL DEFAULT 1, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + updated_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + + CREATE TABLE IF NOT EXISTS video_sources ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + video_id INTEGER NOT NULL REFERENCES videos(id) ON DELETE CASCADE, + label TEXT NOT NULL DEFAULT '', + file_path TEXT NOT NULL, + mime_type TEXT NOT NULL DEFAULT 'video/mp4', + quality TEXT NOT NULL DEFAULT '720p', + sort_order INTEGER NOT NULL DEFAULT 0 + ); + + CREATE INDEX IF NOT EXISTS idx_videos_published ON videos(published); + CREATE INDEX IF NOT EXISTS idx_videos_sort ON videos(sort_order, id); + CREATE INDEX IF NOT EXISTS idx_sources_video ON video_sources(video_id); + "); + + // Default settings + $defaults = [ + 'catalogue_per_page' => '10', + 'admin_password' => password_hash('admin', PASSWORD_DEFAULT), + 'site_title' => 'Ty Clifford', + 'site_sub' => 'tyclifford.com / media', + ]; + $ins = $pdo->prepare("INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)"); + foreach ($defaults as $k => $v) $ins->execute([$k, $v]); + + return $pdo; +} + +function setting(string $key, string $fallback = ''): string { + $row = get_db()->prepare("SELECT value FROM settings WHERE key=?"); + $row->execute([$key]); + $r = $row->fetch(); + return $r ? $r['value'] : $fallback; +} + +function set_setting(string $key, string $value): void { + get_db()->prepare("INSERT OR REPLACE INTO settings (key,value) VALUES (?,?)") + ->execute([$key, $value]); +} + +function make_slug(string $title, int $id = 0): string { + $slug = strtolower(trim(preg_replace('/[^a-z0-9]+/i', '-', $title), '-')); + if ($id) { + $exists = get_db()->prepare("SELECT id FROM videos WHERE slug=? AND id!=?"); + $exists->execute([$slug, $id]); + } else { + $exists = get_db()->prepare("SELECT id FROM videos WHERE slug=?"); + $exists->execute([$slug]); + } + if ($exists->fetch()) $slug .= '-' . ($id ?: time()); + return $slug ?: 'video-' . time(); +} + +function mime_from_ext(string $path): string { + $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION)); + return match($ext) { + 'mp4' => 'video/mp4', + 'webm' => 'video/webm', + 'ogv' => 'video/ogg', + 'mov' => 'video/quicktime', + 'mkv' => 'video/x-matroska', + 'avi' => 'video/x-msvideo', + default => 'video/mp4', + }; +} + +function format_duration(int $seconds): string { + if ($seconds <= 0) return ''; + $h = intdiv($seconds, 3600); + $m = intdiv($seconds % 3600, 60); + $s = $seconds % 60; + if ($h) return sprintf('%d:%02d:%02d', $h, $m, $s); + return sprintf('%d:%02d', $m, $s); +} + +function h(string $s): string { + return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); +} + +function get_videos_paginated(int $page, int $per_page, bool $published_only = true): array { + $db = get_db(); + $where = $published_only ? 'WHERE v.published=1' : ''; + $offset = ($page - 1) * $per_page; + $total = $db->query("SELECT COUNT(*) FROM videos v $where")->fetchColumn(); + $rows = $db->prepare(" + SELECT v.*, GROUP_CONCAT(vs.file_path,'||') AS source_paths + FROM videos v + LEFT JOIN video_sources vs ON vs.video_id = v.id + $where + GROUP BY v.id + ORDER BY v.sort_order ASC, v.id DESC + LIMIT ? OFFSET ? + "); + $rows->execute([$per_page, $offset]); + return [ + 'videos' => $rows->fetchAll(), + 'total' => (int)$total, + 'page' => $page, + 'per_page' => $per_page, + 'total_pages' => (int)ceil($total / $per_page), + ]; +} + +function get_video_by_slug(string $slug): ?array { + $db = get_db(); + $row = $db->prepare("SELECT * FROM videos WHERE slug=? AND published=1"); + $row->execute([$slug]); + $v = $row->fetch(); + if (!$v) return null; + $sources = $db->prepare("SELECT * FROM video_sources WHERE video_id=? ORDER BY sort_order ASC"); + $sources->execute([$v['id']]); + $v['sources'] = $sources->fetchAll(); + return $v; +} + +function get_video_by_id(int $id): ?array { + $db = get_db(); + $row = $db->prepare("SELECT * FROM videos WHERE id=?"); + $row->execute([$id]); + $v = $row->fetch(); + if (!$v) return null; + $sources = $db->prepare("SELECT * FROM video_sources WHERE video_id=? ORDER BY sort_order ASC"); + $sources->execute([$v['id']]); + $v['sources'] = $sources->fetchAll(); + return $v; +} diff --git a/player/includes/layout.php b/player/includes/layout.php new file mode 100644 index 0000000..8407b0a --- /dev/null +++ b/player/includes/layout.php @@ -0,0 +1,275 @@ + + + + + + + <?= h($title) ?> + + + + + + + + + + +
+
+
+ +
+
+
+ +
+ + +'; + $prev = $current - 1; + $next = $current + 1; + if ($prev >= 1) { + echo ''; + } else { + echo ''; + } + $range = range(max(1, $current-2), min($total, $current+2)); + if (!in_array(1, $range)) { echo '1'; } + foreach ($range as $p) { + if ($p === $current) echo '' . $p . ''; + else echo '' . $p . ''; + } + if (!in_array($total, $range)) { echo '' . $total . ''; } + if ($next <= $total) { + echo ''; + } else { + echo ''; + } + echo ''; +} diff --git a/player/index.php b/player/index.php new file mode 100644 index 0000000..6e270cc --- /dev/null +++ b/player/index.php @@ -0,0 +1,452 @@ +query("SELECT slug FROM videos WHERE published=1 ORDER BY sort_order ASC, id DESC LIMIT 1")->fetch(); + if ($latest) $video = get_video_by_slug($latest['slug']); +} + +$per_page = (int)setting('catalogue_per_page', '10'); +$page = max(1, (int)($_GET['page'] ?? 1)); +$paged = get_videos_paginated($page, $per_page); + +// Build absolute base URL used for embed snippet generation +$_scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; +$_host = $_SERVER['HTTP_HOST'] ?? 'localhost'; +$_script = rtrim(dirname($_SERVER['SCRIPT_NAME'] ?? '/'), '/'); +$_base_url = $_scheme . '://' . $_host . $_script . '/'; + +render_head('Media — TyClifford.com', ' + /* ── Catalogue grid ── */ + .cat-section { display:flex; flex-direction:column; gap:1rem; } + .cat-header { display:flex; align-items:center; justify-content:space-between; gap:1rem; flex-wrap:wrap; } + .cat-grid { + display:grid; + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + gap:.85rem; + } + @media(min-width:600px) { .cat-grid { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); } } + @media(min-width:1000px) { .cat-grid { grid-template-columns: repeat(5, 1fr); } } + + .cat-thumb { + background:var(--surface-2); + border:1px solid var(--border); + border-radius:calc(var(--r)+2px); + overflow:hidden; + text-decoration:none; + color:var(--text); + display:flex; flex-direction:column; + transition:border-color .2s, transform .15s, box-shadow .2s; + position:relative; + cursor:pointer; + } + .cat-thumb:hover { border-color:var(--purple); transform:translateY(-2px); box-shadow:0 6px 24px rgba(176,96,255,.18); } + .cat-thumb.active { border-color:var(--green); box-shadow:0 0 0 1px var(--green), 0 4px 20px rgba(0,232,122,.2); } + .cat-thumb-img { + position:relative; width:100%; padding-top:56.25%; background:#000; + overflow:hidden; + } + .cat-thumb-img img { + position:absolute; inset:0; width:100%; height:100%; object-fit:cover; + transition:transform .3s; + } + .cat-thumb:hover .cat-thumb-img img { transform:scale(1.04); } + .cat-thumb-img .no-thumb { + position:absolute; inset:0; display:flex; align-items:center; justify-content:center; + font-family:"Share Tech Mono",monospace; font-size:.6rem; color:var(--text-3); letter-spacing:.1em; + background:var(--surface); + } + .cat-thumb-dur { + position:absolute; bottom:4px; right:6px; + background:rgba(0,0,0,.75); backdrop-filter:blur(4px); + font-family:"Share Tech Mono",monospace; font-size:.55rem; letter-spacing:.05em; + color:#fff; padding:.15em .45em; border-radius:3px; + } + .play-overlay { + position:absolute; inset:0; display:flex; align-items:center; justify-content:center; + background:rgba(0,0,0,.35); opacity:0; transition:opacity .2s; + } + .cat-thumb:hover .play-overlay { opacity:1; } + .play-overlay svg { filter:drop-shadow(0 2px 8px rgba(0,0,0,.5)); } + .cat-thumb-body { padding:.55rem .65rem; display:flex; flex-direction:column; gap:.2rem; flex:1; } + .cat-thumb-title { font-size:.78rem; font-weight:600; color:var(--text); line-height:1.35; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; } + .cat-thumb-meta { font-family:"Share Tech Mono",monospace; font-size:.58rem; color:var(--text-3); letter-spacing:.06em; margin-top:auto; padding-top:.35rem; } + .cat-active-indicator { + position:absolute; top:0; left:0; right:0; height:2px; + background:var(--green); opacity:0; transition:opacity .2s; + } + .cat-thumb.active .cat-active-indicator { opacity:1; } + + /* ── Video.js custom skin ── */ + .video-js { + width:100% !important; + height:100% !important; + position:absolute !important; + inset:0 !important; + } + .video-js .vjs-big-play-button { + background:rgba(176,96,255,.75) !important; + border:2px solid rgba(176,96,255,.9) !important; + border-radius:50% !important; + width:64px !important; height:64px !important; + top:50% !important; left:50% !important; + transform:translate(-50%,-50%) !important; + margin:0 !important; + line-height:64px !important; + transition:background .2s !important; + } + .video-js:hover .vjs-big-play-button { background:rgba(176,96,255,.95) !important; } + .video-js .vjs-control-bar { + background:linear-gradient(transparent,rgba(0,0,0,.85)) !important; + height:3.2em !important; + } + .video-js .vjs-play-progress { background:var(--green) !important; } + .video-js .vjs-volume-level { background:var(--green) !important; } + .video-js .vjs-slider { background:rgba(255,255,255,.15) !important; } + .video-js .vjs-load-progress { background:rgba(255,255,255,.1) !important; } + .video-js .vjs-current-time, + .video-js .vjs-duration, + .video-js .vjs-time-divider { color:rgba(255,255,255,.8) !important; font-size:.7em !important; } + + /* ── Lower grid ── */ + .lower-grid { display:grid; grid-template-columns:1fr; gap:1rem; } + @media(min-width:640px) { .lower-grid { grid-template-columns:1fr 1fr; } } + @media(min-width:900px) { .lower-grid { grid-template-columns:2fr 1fr 1fr; } } + .card-about { background:var(--surface-2); } + .card-social { background:var(--surface-2); } + .social-list { list-style:none; display:flex; flex-direction:column; gap:.5rem; } + .social-list a { + display:flex; align-items:center; gap:.65rem; + padding:.6rem .8rem; border:1px solid var(--border-2); border-radius:var(--r); + font-family:"Share Tech Mono",monospace; font-size:.78rem; color:var(--text-2); + text-decoration:none; transition:border-color .2s, color .2s, background .2s; + } + .social-list a:hover { border-color:var(--green); color:var(--text); background:rgba(0,232,122,.04); } + .social-list a svg { flex-shrink:0; color:var(--text-3); transition:color .2s; } + .social-list a:hover svg { color:var(--green); } + + /* empty state */ + .empty-state { + padding:3rem 1rem; text-align:center; display:flex; flex-direction:column; + align-items:center; gap:1rem; + } + .empty-state svg { color:var(--text-3); opacity:.4; } + .empty-state p { font-family:"Share Tech Mono",monospace; font-size:.75rem; color:var(--text-3); letter-spacing:.1em; } + + /* no-video placeholder */ + .no-video { display:flex; flex-direction:column; align-items:center; justify-content:center; gap:.75rem; } + .no-video p { font-family:"Share Tech Mono",monospace; font-size:.7rem; color:var(--text-3); letter-spacing:.1em; } + + /* pagination row */ + .cat-footer { display:flex; align-items:center; justify-content:space-between; gap:1rem; flex-wrap:wrap; } + .cat-count { font-family:"Share Tech Mono",monospace; font-size:.65rem; color:var(--text-3); letter-spacing:.06em; } + + /* ── Embed panel ── */ + .embed-panel { + border-top:1px solid var(--border); + overflow:hidden; + max-height:0; + transition:max-height .35s cubic-bezier(.4,0,.2,1), padding .35s; + padding:0 1.1rem; + } + .embed-panel.open { max-height:320px; padding:.9rem 1.1rem; } + .embed-panel-inner { display:flex; flex-direction:column; gap:.75rem; } + .embed-label { font-family:"Share Tech Mono",monospace; font-size:.6rem; letter-spacing:.14em; text-transform:uppercase; color:var(--text-3); } + .embed-code-wrap { position:relative; } + .embed-code { + display:block; width:100%; + background:var(--surface-2); border:1px solid var(--border-2); border-radius:var(--r); + padding:.65rem .85rem; padding-right:5.5rem; + font-family:"Share Tech Mono",monospace; font-size:.68rem; color:var(--text-2); + white-space:nowrap; overflow-x:auto; line-height:1.6; + resize:none; cursor:text; + scrollbar-width:thin; + } + .embed-copy-btn { + position:absolute; right:.4rem; top:50%; transform:translateY(-50%); + padding:.32rem .7rem; font-size:.65rem; font-weight:600; + background:rgba(176,96,255,.15); color:var(--purple); + border:1px solid rgba(176,96,255,.35); border-radius:calc(var(--r) - 1px); + cursor:pointer; font-family:"Share Tech Mono",monospace; letter-spacing:.06em; + transition:background .2s, color .2s; + white-space:nowrap; + } + .embed-copy-btn:hover { background:rgba(176,96,255,.28); } + .embed-copy-btn.copied { background:rgba(0,232,122,.15); color:var(--green); border-color:rgba(0,232,122,.35); } + .embed-row { display:flex; gap:.6rem; align-items:center; flex-wrap:wrap; } + .embed-direct-link { + font-family:"Share Tech Mono",monospace; font-size:.65rem; color:var(--text-3); + text-decoration:none; border-bottom:1px solid rgba(255,255,255,.1); + transition:color .15s, border-color .15s; + } + .embed-direct-link:hover { color:var(--green); border-color:rgba(0,232,122,.4); } + .embed-preview-link { + font-family:"Share Tech Mono",monospace; font-size:.65rem; color:var(--purple); + text-decoration:none; border-bottom:1px solid rgba(176,96,255,.2); + transition:color .15s; + } + .embed-preview-link:hover { color:#c880ff; } + .embed-toggle-btn { + display:inline-flex; align-items:center; gap:.4rem; + font-family:"Share Tech Mono",monospace; font-size:.65rem; letter-spacing:.08em; + color:var(--text-3); background:transparent; border:1px solid var(--border-2); + border-radius:var(--r); padding:.35rem .7rem; cursor:pointer; + transition:color .2s, border-color .2s, background .2s; + } + .embed-toggle-btn:hover { color:var(--text-2); border-color:var(--purple); background:rgba(176,96,255,.07); } + .embed-toggle-btn.active { color:var(--purple); border-color:var(--purple); background:rgba(176,96,255,.1); } +'); +?> +
+ + + + +
+
+ + + +
+ +

no video selected

+
+ +
+ + + + '; + ?> +
+
+
+ iframe embed code +
+ + +
+
+
+ Direct player URL — + + + + + ↗ Preview + +
+
+ + Recommended: width="560" height="315" — any 16:9 size works. Add style="border:0;border-radius:6px" for no border. + +
+
+
+ +
+ + +
+
+

Video Catalogue

+ Browse all → +
+ + +
+
+ +

no videos yet

+
+
+ + + + 1): ?> + + + +
+ + +
+ +
+

About

+

Ty Clifford

+

+ Based in Keyser, West Virginia. Nerd, tinkerer, and creator. + Hobbies are my hobby — from tech and podcasting to whatever rabbit hole I fell into this week. +

+
+ + + +
+ + +
+ + + + diff --git a/player/media/.DS_Store b/player/media/.DS_Store new file mode 100644 index 0000000..800bfce Binary files /dev/null and b/player/media/.DS_Store differ diff --git a/player/media/.gitkeep b/player/media/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/player/media/thumbs/josie_thumb_1780555321.png b/player/media/thumbs/josie_thumb_1780555321.png new file mode 100644 index 0000000..b5be868 Binary files /dev/null and b/player/media/thumbs/josie_thumb_1780555321.png differ diff --git a/player/media/videos/josie_1_1780555321.mp4 b/player/media/videos/josie_1_1780555321.mp4 new file mode 100644 index 0000000..19b3e6d Binary files /dev/null and b/player/media/videos/josie_1_1780555321.mp4 differ diff --git a/post.php b/post.php new file mode 100644 index 0000000..4caf6ab --- /dev/null +++ b/post.php @@ -0,0 +1,1234 @@ + + + + + + + <?= htmlspecialchars(POST_PAGE_TITLE) ?> + + + + + + + + + +
+ + +
+
+
+

Post Manager

+

Enter your API key to continue

+
+
+
+ + +
+
+ +
+
+
+ + + +
+ + + + diff --git a/schedule/index.php b/schedule/index.php new file mode 100644 index 0000000..e08b76f --- /dev/null +++ b/schedule/index.php @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + Ty Clifford - Hobbies are my hobby. + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +

+ + +
+

+ +
+ + +
+ + + + + + + diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..ee5c173 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,27 @@ + + + + https://tyclifford.com/ + 2023-12-16T04:42:06+00:00 + 1.00 + + + https://tyclifford.com/radio/ + 2023-12-16T04:42:06+00:00 + 0.80 + + + https://tyclifford.com/podcast/ + 2023-12-16T04:42:06+00:00 + 0.80 + + + https://tyclifford.com/tip/ + 2023-12-16T04:42:06+00:00 + 0.80 + + diff --git a/status/config.php b/status/config.php new file mode 100644 index 0000000..3586b5f --- /dev/null +++ b/status/config.php @@ -0,0 +1,34 @@ + → redirects to /status/post.php?id= + */ +require_once __DIR__ . '/config.php'; + +// ── Redirect bare ?id= to the single-post page ─────────────────────────────── +if (isset($_GET['id']) && trim($_GET['id']) !== '') { + $safe_id = rawurlencode(trim($_GET['id'])); + header('Location: ' . STATUS_BASE_URL . '/post.php?id=' . $safe_id, true, 301); + exit; +} + +// ── Load data ───────────────────────────────────────────────────────────────── +function status_load_posts(): array { + if (!file_exists(STATUS_JSON_PATH)) return []; + $raw = json_decode(file_get_contents(STATUS_JSON_PATH), true); + return is_array($raw) ? $raw : []; +} + +function status_relative_time(string $iso): string { + try { + $dt = new DateTime($iso, new DateTimeZone('UTC')); + $now = new DateTime('now', new DateTimeZone('UTC')); + $diff = $now->getTimestamp() - $dt->getTimestamp(); + if ($diff < 60) return 'just now'; + if ($diff < 3600) return floor($diff / 60) . 'm ago'; + if ($diff < 86400) return floor($diff / 3600) . 'h ago'; + if ($diff < 604800) return floor($diff / 86400) . 'd ago'; + if ($diff < 2592000) return floor($diff / 604800) . 'w ago'; + return $dt->format('M j, Y'); + } catch (Exception $e) { return ''; } +} + +function status_abs_date(string $iso): string { + try { + $dt = new DateTime($iso, new DateTimeZone('UTC')); + return $dt->format('F j, Y · g:i A') . ' UTC'; + } catch (Exception $e) { return $iso; } +} + +function status_iso(string $iso): string { + try { + $dt = new DateTime($iso, new DateTimeZone('UTC')); + return $dt->format(DateTime::ATOM); + } catch (Exception $e) { return $iso; } +} + +$all_posts = status_load_posts(); +$total = count($all_posts); +$per_page = max(1, (int) STATUS_PER_PAGE); +$total_pages = max(1, (int) ceil($total / $per_page)); +$page = max(1, min($total_pages, (int) ($_GET['page'] ?? 1))); +$offset = ($page - 1) * $per_page; +$posts = array_slice($all_posts, $offset, $per_page); + +$page_title = STATUS_PAGE_TITLE . ($page > 1 ? ' — Page ' . $page : ''); +$canonical = STATUS_BASE_URL . '/' . ($page > 1 ? '?page=' . $page : ''); +?> + + + + + + <?= htmlspecialchars($page_title) ?> — <?= htmlspecialchars(STATUS_SITE_NAME) ?> + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + update + +
+ + +
+

Status & Updates

+

+

+
+ + +
+ +

No updates yet.

+ + + $post): + $post_id = htmlspecialchars($post['id'] ?? ''); + $post_text = htmlspecialchars($post['text'] ?? ''); + $post_date = $post['date'] ?? ''; + $post_link = $post['link'] ?? ''; + $post_link_label = htmlspecialchars($post['link_label'] ?? $post_link); + $post_tags = (array)($post['tags'] ?? []); + $post_url = htmlspecialchars(STATUS_BASE_URL . '/post.php?id=' . rawurlencode($post['id'] ?? '')); + $is_last = ($i === count($posts) - 1); + ?> + + + +
+ + + 1): ?> + + + + + +
+ + diff --git a/status/post.php b/status/post.php new file mode 100644 index 0000000..83c20de --- /dev/null +++ b/status/post.php @@ -0,0 +1,266 @@ + + * Returns 404 with styled error card if the ID is not found. + */ +require_once __DIR__ . '/config.php'; + +// ── Load helpers (reuse from index) ────────────────────────────────────────── +function status_load_posts(): array { + if (!file_exists(STATUS_JSON_PATH)) return []; + $raw = json_decode(file_get_contents(STATUS_JSON_PATH), true); + return is_array($raw) ? $raw : []; +} + +function status_relative_time(string $iso): string { + try { + $dt = new DateTime($iso, new DateTimeZone('UTC')); + $now = new DateTime('now', new DateTimeZone('UTC')); + $diff = $now->getTimestamp() - $dt->getTimestamp(); + if ($diff < 60) return 'just now'; + if ($diff < 3600) return floor($diff / 60) . 'm ago'; + if ($diff < 86400) return floor($diff / 3600) . 'h ago'; + if ($diff < 604800) return floor($diff / 86400) . 'd ago'; + if ($diff < 2592000) return floor($diff / 604800) . 'w ago'; + return $dt->format('M j, Y'); + } catch (Exception $e) { return ''; } +} + +function status_abs_date(string $iso): string { + try { + $dt = new DateTime($iso, new DateTimeZone('UTC')); + return $dt->format('F j, Y · g:i A') . ' UTC'; + } catch (Exception $e) { return $iso; } +} + +function status_iso(string $iso): string { + try { + $dt = new DateTime($iso, new DateTimeZone('UTC')); + return $dt->format(DateTime::ATOM); + } catch (Exception $e) { return $iso; } +} + +// ── Find post ──────────────────────────────────────────────────────────────── +$req_id = trim($_GET['id'] ?? ''); +$all_posts = status_load_posts(); +$post = null; +$post_idx = null; + +foreach ($all_posts as $i => $p) { + if (($p['id'] ?? '') === $req_id) { + $post = $p; + $post_idx = $i; + break; + } +} + +$found = ($post !== null); +if (!$found) http_response_code(404); + +// ── Prev / next navigation ─────────────────────────────────────────────────── +$prev_post = ($found && $post_idx > 0) ? $all_posts[$post_idx - 1] : null; +$next_post = ($found && $post_idx < count($all_posts) - 1) ? $all_posts[$post_idx + 1] : null; + +// ── Meta ────────────────────────────────────────────────────────────────────── +$page_title = $found + ? 'Update #' . htmlspecialchars($post['id']) . ' — ' . htmlspecialchars(STATUS_SITE_NAME) + : '404 Not Found — ' . htmlspecialchars(STATUS_SITE_NAME); + +$canonical = STATUS_BASE_URL . '/post.php?id=' . rawurlencode($req_id); +$og_desc = $found ? mb_substr(strip_tags($post['text'] ?? ''), 0, 200) : 'Update not found.'; +?> + + + + + + <?= $page_title ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + All updates + + + # + +
+ + + +
+ +

Update not found

+

+ No update with ID exists. + It may have been deleted or the link may be incorrect. +

+ + ← Back to all updates + +
+ + + + +
+

+

+ # +

+
+ +
+
+ + + + + +

+ + + + > + + + + + + +
+
+ UID + +
+ +
+ Published + +
+ +
+ +
+
+ + + + + + + + + + +
+ + diff --git a/status/shared_styles.php b/status/shared_styles.php new file mode 100644 index 0000000..4e16f3f --- /dev/null +++ b/status/shared_styles.php @@ -0,0 +1,627 @@ + diff --git a/tip/index.php b/tip/index.php new file mode 100644 index 0000000..ef31932 --- /dev/null +++ b/tip/index.php @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + Ty Clifford - Hobbies are my hobby. + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

+ Tip Jar +

+

Financial support helps with creativity. Any support is appreciated.

+ + Become a Patreon + Liberapay + Donate via PayPal + + +
+ +

+ +
+ + +
+ + + + + + + diff --git a/world3.png b/world3.png new file mode 100644 index 0000000..5ec782e Binary files /dev/null and b/world3.png differ