commit fa4a173e8e339356f61a25c38ee563b0348e62cf
parent 9844d063587165f8687a25f4369fc55c20de0808
Author: knutsen <samuel@knutsen.co>
Date: Wed, 29 Sep 2021 16:18:51 +0100
Cache/pre-render certain templates that do not change.
Diffstat:
20 files changed, 680 insertions(+), 767 deletions(-)
diff --git a/public/style.css b/public/style.css
@@ -53,6 +53,15 @@ body {
margin-right: auto;
}
+a.link {
+ display: inline-block;
+ transition: all .1s ease;
+}
+
+a.link:hover {
+ transform: skewX(-7deg);
+}
+
hr {
background: none;
border: none;
@@ -125,11 +134,12 @@ blockquote > *:last-child {
background-color: #acb9f8;
display: inline-block;
margin: 0.3em 0.3em;
- padding: 0.12em 0.3em;
+ padding: 0.12em 0;
box-shadow: 0.3em 0.3em 0 #00000030;
transition: 0.3s;
font-size: 20pt;
- width: 12.5%;
+ text-align: center;
+ width: 15%;
font-weight: bold;
font-variant: small-caps;
}
diff --git a/shiimoe.py b/shiimoe.py
@@ -1,202 +1,5 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
+import shiimoe
-from flask import Flask, render_template, send_from_directory, request, redirect
-
-import markdown
-import markdown.extensions.codehilite
-import markdown.extensions.fenced_code
-import pymdownx, pymdownx.emoji
-import bleach
-
-from datetime import datetime
-import os
-import json
-import re
-
-BLOG_PATH = os.path.realpath('./blog')
-STATIC_PATH = os.path.realpath('./static')
-PUBLIC_PATH = os.path.realpath('./public')
-COMMENT_FILE = os.path.realpath('./comments.json')
-DATE_FORMAT = "%Y-%m-%d %H:%M"
-app = Flask(__name__, static_folder=PUBLIC_PATH)
-md = markdown.Markdown(
- extensions=[
- 'fenced_code', 'codehilite',
- 'pymdownx.emoji', 'smarty', 'attr_list',
- 'full_yaml_metadata', 'markdown_captions'],
- extension_configs={
- 'pymdownx.emoji': {
- 'emoji_index': pymdownx.emoji.gemoji,
- 'emoji_generator': pymdownx.emoji.to_png,
- }
- })
-
-
-ALLOWED_TAGS = list(set(bleach.sanitizer.ALLOWED_TAGS + [
- 'ul', 'ol', 'li', 'p', 'pre', 'code', 'blockquote',
- 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'br',
- 'strong', 'em', 'a', 'img'
-]))
-ALLOWED_ATTRIBUTES = {
- **bleach.sanitizer.ALLOWED_ATTRIBUTES,
- 'a': ['href', 'title'],
- 'img': ['src', 'title', 'alt']
-}
-ALLOWED_PROTOCOLS = list(set(bleach.sanitizer.ALLOWED_PROTOCOLS
- + ['http', 'https', 'mailto']))
-
-def read_comments():
- comments = None
- try:
- with open(COMMENT_FILE, 'r') as f:
- comments = json.load(f)
- except FileNotFoundError:
- comments = []
-
- return comments
-
-def is_subpath(path, dir):
- return path.startswith(dir)
-
-def send_static_page(path, filename):
- path = os.path.realpath(os.path.join(STATIC_PATH, path))
- if not is_subpath(path, STATIC_PATH):
- return 403
-
- if not filename.endswith('.html'):
- filename += '.html'
- return send_from_directory(path, filename)
-
-def send_public_asset(path, filename):
- path = os.path.realpath(os.path.join(PUBLIC_PATH, path))
- if not is_subpath(path, PUBLIC_PATH):
- return 403
-
- return send_from_directory(path, filename)
-
-@app.route('/')
-def home():
- return send_static_page('./', 'index.html')
-
-def render_guestbook(**kw):
- comments = read_comments()
- comments.reverse()
-
- for comment in comments:
- comment['comment'] = bleach.clean(comment['comment'],
- tags=ALLOWED_TAGS,
- attributes=ALLOWED_ATTRIBUTES,
- protocols=ALLOWED_PROTOCOLS)
- comment['comment'] = md.convert(comment['comment'])
-
- kw['comments'] = comments
- return render_template('guestbook.html', **kw)
-
-@app.route('/guestbook')
-@app.route('/guestbook.html')
-def guestbook():
- return render_guestbook()
-
-@app.route('/blog')
-@app.route('/blog/index.html')
-def blogindex():
- posts = []
- for file in os.listdir(BLOG_PATH):
- slug = file[:-3]
- meta = None
- with open(f"{BLOG_PATH}/{file}", 'r') as f:
- md.convert(f.read())
- meta = md.Meta
- posts.append({
- 'slug': slug,
- 'title': meta['title'],
- 'published': meta['published'],
- 'date': datetime.strftime(meta['published'], '%Y-%m-%d'),
- 'datetime': meta['published'].isoformat()
- })
- print(posts)
- posts.sort(key = lambda post: post['published'], reverse=True)
- return render_template("blogindex.html", posts = posts)
-
-@app.route('/blog/<slug>')
-@app.route('/blog/<slug>.html')
-def blogpost(slug):
- keywords = {
- 'slug': slug,
- 'title': None
- }
- for post in os.listdir(BLOG_PATH):
- if post == slug + ".md":
- with open(f"{BLOG_PATH}/{post}", 'r') as file:
- content = file.read()
- keywords["content"] = md.convert(content)
- keywords["title"] = md.Meta['title']
- break
-
- if 'content' not in keywords:
- return send_static_page('./', '404.html'), 404
-
-
- return render_template("blogpost.html", **keywords)
-
-
-LINK_MATCH = re.compile("https?://")
-
-@app.route('/postcomment', methods=['POST'])
-def postcomment():
- now = datetime.now()
-
- name = request.form.get('name')
- comment = request.form.get('comment')
- date = request.form.get('date') or datetime.strftime(now, DATE_FORMAT)
- ip = (request.environ.get('HTTP_X_REAL_IP')
- or request.environ.get('REMOTE_ADDR')
- or request.remote_addr)
-
- err = lambda msg: render_guestbook(error_msg=msg, dcomment=comment, dname=name)
-
- # Comment or name left empty, send error message.
- if not (name or "").strip() or not (comment or "").strip():
- return err("Please provide a name and comment.")
-
- # Spam filtering!!
- if len(name) > 110:
- return err("You're taking the piss with a name that long mate.")
- if len(comment) > 850:
- return err("No more than 850 characters!")
- if name.lower() == "annasysdek":
- return err("Sorry, that name is toxic. Pick another.")
- if len(LINK_MATCH.findall(comment)) != 0:
- return err("No links!")
-
- comments = read_comments()
- if any(c['comment'] == comment for c in comments):
- return err("Duplicate comment. Please write unique comments.")
-
- comments.append({
- 'name': name,
- 'comment': comment,
- 'date': date,
- 'ip': ip
- })
-
- with open(COMMENT_FILE, 'w') as f:
- json.dump(comments, f, indent=4, separators=(",", ": "))
-
- return redirect('/guestbook', code=302)
-
-
-@app.route('/<path:filepath>')
-def serve_static(filepath):
- path, filename = os.path.split(filepath)
- if os.path.exists(os.path.join(PUBLIC_PATH, path, filename)):
- return send_public_asset(path, filename)
- if not filename.endswith('.html'):
- filename += '.html'
- if os.path.exists(os.path.join(STATIC_PATH, path, filename)):
- return send_static_page(path, filename)
- # Otherwise, 404.
- return send_static_page('./', '404.html'), 404
-
-if __name__ == "__main__":
- app.run(debug=True)
+if __name__ == '__main__':
+ shiimoe.app.run(debug=True)
diff --git a/shiimoe/__init__.py b/shiimoe/__init__.py
@@ -0,0 +1,264 @@
+#!/usr/bin/env python3
+
+from flask import Flask, render_template, send_from_directory, request, redirect
+import jinja2
+
+import markdown
+import markdown.extensions.codehilite
+import markdown.extensions.fenced_code
+import pymdownx, pymdownx.emoji
+import bleach
+
+from datetime import datetime
+import os
+import random
+import json
+import re
+
+BLOG_PATH = os.path.realpath('./blog')
+STATIC_PATH = os.path.realpath('./static')
+PUBLIC_PATH = os.path.realpath('./public')
+TEMPLATE_PATH = os.path.realpath('./shiimoe/templates')
+COMMENT_FILE = os.path.realpath('./comments.json')
+DATE_FORMAT = "%Y-%m-%d %H:%M"
+
+app = Flask(__name__, static_folder=PUBLIC_PATH)
+
+md = markdown.Markdown(
+ extensions=[
+ 'fenced_code', 'codehilite',
+ 'pymdownx.emoji', 'smarty', 'attr_list',
+ 'full_yaml_metadata', 'markdown_captions'],
+ extension_configs={
+ 'pymdownx.emoji': {
+ 'emoji_index': pymdownx.emoji.gemoji,
+ 'emoji_generator': pymdownx.emoji.to_png,
+ }
+ })
+
+ALLOWED_TAGS = list(set(bleach.sanitizer.ALLOWED_TAGS + [
+ 'ul', 'ol', 'li', 'p', 'pre', 'code', 'blockquote',
+ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'br',
+ 'strong', 'em', 'a', 'img'
+]))
+ALLOWED_ATTRIBUTES = {
+ **bleach.sanitizer.ALLOWED_ATTRIBUTES,
+ 'a': ['href', 'title'],
+ 'img': ['src', 'title', 'alt']
+}
+ALLOWED_PROTOCOLS = list(set(bleach.sanitizer.ALLOWED_PROTOCOLS
+ + ['http', 'https', 'mailto']))
+
+MASCOT_COUNT = int(len(os.listdir(f"{PUBLIC_PATH}/mascots")) / 2)
+
+def get_blog_posts():
+ posts = []
+ for file in os.listdir(BLOG_PATH):
+ slug = file[:-3]
+ meta = None
+ with open(f"{BLOG_PATH}/{file}", 'r') as f:
+ md.convert(f.read())
+ meta = md.Meta
+ posts.append({
+ 'slug': slug,
+ 'title': meta['title'],
+ 'published': meta['published'],
+ 'date': datetime.strftime(meta['published'], '%Y-%m-%d'),
+ 'datetime': meta['published'].isoformat()
+ })
+ posts.sort(key = lambda post: post['published'], reverse=True)
+ return tuple(posts)
+
+def get_blog_post(slug):
+ keywords = {
+ 'slug': slug,
+ 'title': None
+ }
+ try:
+ with open(f"{BLOG_PATH}/{slug}.md", 'r') as file:
+ content = file.read()
+ keywords["content"] = md.convert(content)
+ keywords["title"] = md.Meta['title']
+ except FileNotFoundError:
+ return None
+
+ return keywords
+
+STATIC_BLOG_POSTS = get_blog_posts()
+
+def _template(tmplt, **kw):
+ kw['mascot'] = random.randint(1, MASCOT_COUNT)
+ return render_template(tmplt, **kw)
+
+def static_template(tmplt, **kw):
+ kw['mascot'] = random.randint(1, MASCOT_COUNT)
+ kw['request'] = { 'path': f"/{tmplt[:-5]}" }
+ if tmplt == 'blogindex.html':
+ kw['request']['path'] = '/blog'
+ env = jinja2.Environment(
+ loader=jinja2.PackageLoader('shiimoe','templates'))
+ template = env.get_template(tmplt)
+ return template.render(**kw)
+
+def frozen(d):
+ if isinstance(d, dict):
+ return frozenset(frozen(item) for item in d.items())
+ if isinstance(d, list) or isinstance(d, tuple):
+ return tuple(frozen(item) for item in d)
+ return d
+
+STATIC_TEMPLATES = {
+ ('index.html', frozen({})): static_template('index.html'),
+ ('music.html', frozen({})): static_template('music.html'),
+ ('school.html', frozen({})): static_template('school.html'),
+ ('norsk.html', frozen({})): static_template('music.html'),
+ ('books.html', frozen({})): static_template('books.html'),
+ ('blogindex.html', frozen({
+ 'posts': STATIC_BLOG_POSTS
+ })): static_template('blogindex.html', posts=STATIC_BLOG_POSTS),
+}
+
+for blogpost in os.listdir(BLOG_PATH):
+ slug = blogpost[:-3]
+ kw = get_blog_post(slug)
+ STATIC_TEMPLATES[('blogpost.html', frozen(kw))] = (
+ static_template('blogpost.html', **kw)
+ )
+
+def template(tmplt, **kw):
+ rendered = STATIC_TEMPLATES.get((tmplt, frozen(kw)))
+ if rendered:
+ print("Serving cached static template:", tmplt)
+ return rendered
+ print("Server rendering template:", tmplt)
+ return _template(tmplt, **kw)
+
+def read_comments():
+ comments = None
+ try:
+ with open(COMMENT_FILE, 'r') as f:
+ comments = json.load(f)
+ except FileNotFoundError:
+ comments = []
+
+ return comments
+
+def is_subpath(path, dir):
+ return path.startswith(dir)
+
+def send_static_page(path, filename):
+ path = os.path.realpath(os.path.join(STATIC_PATH, path))
+ if not is_subpath(path, STATIC_PATH):
+ return 403
+
+ if not filename.endswith('.html'):
+ filename += '.html'
+ return send_from_directory(path, filename)
+
+def send_public_asset(path, filename):
+ path = os.path.realpath(os.path.join(PUBLIC_PATH, path))
+ if not is_subpath(path, PUBLIC_PATH):
+ return 403
+
+ return send_from_directory(path, filename)
+
+@app.route('/')
+def home():
+ return template('index.html')
+
+def render_guestbook(**kw):
+ comments = read_comments()
+ comments.reverse()
+
+ for comment in comments:
+ comment['comment'] = bleach.clean(comment['comment'],
+ tags=ALLOWED_TAGS,
+ attributes=ALLOWED_ATTRIBUTES,
+ protocols=ALLOWED_PROTOCOLS)
+ comment['comment'] = md.convert(comment['comment'])
+
+ kw['comments'] = comments
+ return template('guestbook.html', **kw)
+
+@app.route('/guestbook')
+@app.route('/guestbook.html')
+def guestbook():
+ return render_guestbook()
+
+@app.route('/blog')
+@app.route('/blog/index.html')
+def blogindex():
+ posts = get_blog_posts()
+ return template('blogindex.html', posts=posts)
+
+@app.route('/blog/<slug>')
+@app.route('/blog/<slug>.html')
+def blogpost(slug):
+ keywords = get_blog_post(slug)
+ if keywords is None:
+ return send_static_page('./', '404.html'), 404
+
+ return template('blogpost.html', **keywords)
+
+LINK_MATCH = re.compile("https?://")
+
+@app.route('/postcomment', methods=['POST'])
+def postcomment():
+ now = datetime.now()
+
+ name = request.form.get('name')
+ comment = request.form.get('comment')
+ date = request.form.get('date') or datetime.strftime(now, DATE_FORMAT)
+ ip = (request.environ.get('HTTP_X_REAL_IP')
+ or request.environ.get('REMOTE_ADDR')
+ or request.remote_addr)
+
+ err = lambda msg: render_guestbook(error_msg=msg, dcomment=comment, dname=name)
+
+ # Comment or name left empty, send error message.
+ if not (name or "").strip() or not (comment or "").strip():
+ return err("Please provide a name and comment.")
+
+ # Spam filtering!!
+ if len(name) > 110:
+ return err("You're taking the piss with a name that long mate.")
+ if len(comment) > 850:
+ return err("No more than 850 characters!")
+ if name.lower() == "annasysdek":
+ return err("Sorry, that name is toxic. Pick another.")
+ if len(LINK_MATCH.findall(comment)) != 0:
+ return err("No links!")
+
+ comments = read_comments()
+ if any(c['comment'] == comment for c in comments):
+ return err("Duplicate comment. Please write unique comments.")
+
+ comments.append({
+ 'name': name,
+ 'comment': comment,
+ 'date': date,
+ 'ip': ip
+ })
+
+ with open(COMMENT_FILE, 'w') as f:
+ json.dump(comments, f, indent=4, separators=(",", ": "))
+
+ return redirect('/guestbook', code=302)
+
+
+@app.route('/<path:filepath>')
+def serve_static(filepath):
+ path, filename = os.path.split(filepath)
+ if os.path.exists(os.path.join(PUBLIC_PATH, path, filename)):
+ return send_public_asset(path, filename)
+ if not filename.endswith('.html'):
+ filename += '.html'
+ if os.path.exists(os.path.join(TEMPLATE_PATH, path, filename)):
+ return template(os.path.join(path, filename))
+ if os.path.exists(os.path.join(STATIC_PATH, path, filename)):
+ return send_static_page(path, filename)
+ # Otherwise, 404.
+ return send_static_page('./', '404.html'), 404
+
+if __name__ == '__main__':
+ app.run(debug=True)
diff --git a/shiimoe/templates/blogindex.html b/shiimoe/templates/blogindex.html
@@ -0,0 +1,46 @@
+{% extends 'layout.html' %}
+{% block head %}
+ <style>
+ .blog-posts {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ }
+ .blog-listing a {
+ display: flex;
+ justify-content: space-between;
+ padding: 0.7em 1em;
+ margin: 0.3em 0;
+ border-radius: 5pt;
+ transition: all .05s ease;
+ text-decoration: none;
+ }
+ .blog-listing a:hover {
+ background-color: #679dd727;
+ transform: scale(1.01);
+ }
+ .blog-listing .title {
+ text-decoration: underline;
+ }
+ .blog-listing .published {
+ opacity: 0.4;
+ }
+ </style>
+{% endblock %}
+{% block header %}
+ <a href="/"><h1>{% block title %}Blog List{% endblock %}!!</h1></a>
+{% endblock %}
+{% block content %}
+ <ul class="blog-posts">
+ {% for post in posts %}
+ <li class="blog-listing">
+ <a href="/blog/{{ post['slug'] }}">
+ <span class="title">{{ post['title'] }}</span>
+ <span class="published">
+ (<time datetime="{{ post['datetime'] }}">{{ post['date'] }}</time>)
+ </span>
+ </a>
+ </li>
+ {% endfor %}
+ </ul>
+{% endblock %}
diff --git a/shiimoe/templates/blogpost.html b/shiimoe/templates/blogpost.html
@@ -0,0 +1,41 @@
+{% extends 'layout.html' %}
+{% block title %}{{ title }}{% endblock %}
+{% block head %}
+ <style>
+ .blog-title {
+ text-align: left;
+ font-size: 3em;
+ text-shadow: none;
+ font-weight: 300;
+ }
+ article figure {
+ display: flex;
+ margin-left: 0;
+ margin-right: 0;
+ padding: 0;
+ justify-content: center;
+ align-content: center;
+ align-items: center;
+ flex-direction: column;
+ }
+ article figure figcaption {
+ font-style: italic;
+ font-size: small;
+ margin-top: 1em;
+ }
+ article img {
+ max-width: 100%;
+ max-height: 30em;
+ border-radius: 7pt;
+ border: 1pt solid #fff8;
+ box-shadow: 0 4px 20px -7px #0006;
+ }
+ </style>
+{% endblock %}
+{% block header %}
+ <a href="/"><h1>Shimo!!</h1></a>
+{% endblock %}
+{% block content %}
+ <h1 class="blog-title">{{ title }}</h1>
+ <article>{{ content | safe }}</article>
+{% endblock %}
diff --git a/shiimoe/templates/books.html b/shiimoe/templates/books.html
@@ -0,0 +1,9 @@
+{% extends 'layout.html' %}
+{% block header %}
+ <a href="/"><h1>{% block title %}Blog List{% endblock %}!!</h1></a>
+{% endblock %}
+{% block content %}
+ <p><em>The Trial?</em> more like <em>The Smile</em>, because it makes me happy
+ when I read it (✿◡‿◡)</p>
+ <p>Har du lest <em>Stoner</em>? Du har ikke? Slutt å les dette og begynn å lese det i stedet for!</p>
+{% endblock %}
diff --git a/shiimoe/templates/guestbook.html b/shiimoe/templates/guestbook.html
@@ -0,0 +1,168 @@
+{% extends 'layout.html' %}
+{% block head %}
+ <style>
+ .name {
+ float: left;
+ }
+ .date {
+ float: right;
+ font-family: var(--monospace);
+ }
+ .date, .name {
+ color: #0007;
+ }
+ .comment-text {
+ margin-top: 0.5rem;
+ }
+ .comments {
+ margin-top: 3rem;
+ }
+ .comments a {
+ color: #4e87bd;
+ text-decoration: underline;
+ }
+ .comments a:hover {
+ color: #3d6f9f;
+ }
+ .comments h1, .comments h2, .comments h3,
+ .comments h4, .comments h5, .comments h6 {
+ text-align: left;
+ margin: 0.4em 0 0 0;
+ border-bottom: 1pt solid #0000000f;
+ }
+ .comments img {
+ max-width: 100%;
+ max-height: 20em;
+ display: block;
+ border: 2pt solid #0000001a;
+ border-radius: 5pt;
+ margin: 0.5em 0;
+ filter: drop-shadow(0 4pt 8pt #0004);
+ }
+ .input {
+ font-family: var(--serif);
+ padding: 0.4rem 0.6rem;
+ font-size: 0.9em;
+ width: 30%;
+ border: 1px solid #0004;
+ border-radius: 3pt;
+ }
+ .textbox {
+ margin: 0.5em 0;
+ padding: 0;
+ position: relative;
+ }
+ .textbox > i {
+ position: absolute;
+ left: 0.6em;
+ bottom: 0.7em;
+ opacity: 0.3;
+ font-size: 1rem;
+ }
+ .comment-input {
+ font-family: var(--monospace);
+ height: 8em;
+ margin: 0em;
+ }
+ .comment {
+ margin: 2rem 0 1.5rem 0;
+ background: #00000008;
+ padding: 0.7rem 1.2rem 0 1.2rem;
+ border-bottom: 3pt solid #0000001a;
+ }
+ .comment-text p:nth-child(1) {
+ margin-top: 0;
+ }
+ input[type="submit"] {
+ margin-left: 30%;
+ font-family: inherit;
+ transform-origin: left;
+ transform: translateX(-50%);
+ }
+ .error {
+ margin: 1em 0 2em 0;
+ padding: 1em 1.5em;
+ border: 2pt solid #ff6b0091;
+ border-radius: 10pt;
+ border-top-left-radius: 0;
+ background: #ce285bad;
+ color: white;
+ }
+ </style>
+{% endblock %}
+{% block header %}
+ <a href="/"><h1>{% block title %}Guestbook{% endblock %}!!</h1></a>
+{% endblock %}
+{% block content %}
+ {% if error_msg is defined %}
+ <p class="error">{{ error_msg }}</p>
+ {% endif %}
+
+ <form action="/postcomment" method="POST" name="commentBox">
+ <input class="name-input input"
+ name="name" placeholder="Your name"
+ {% if dname is defined %} value="{{ dname }}" {% endif %}
+ required>
+ <div class="textbox">
+ <textarea class="comment-input input" name="comment"
+ placeholder="# Your comment

(no links, ≤850 characters)"
+ required>{{ dcomment }}</textarea>
+ <i class="fab fa-markdown"></i>
+ </div>
+ <input type="submit" value="Post">
+ </form>
+ <!-- add gender radio boxes (femboy) -->
+
+ <script>
+ // Script to get local time, will just fall back on server time
+ // if the script does not run.
+ const form = document.forms.namedItem("commentBox");
+ form.addEventListener('submit', ev => {
+ ev.preventDefault();
+
+ const formData = new FormData(form);
+
+ const now = new Date();
+ const { year, month, day } = {
+ year: String(now.getFullYear()),
+ month: String(now.getMonth() + 1).padStart(2, '0'),
+ day: String(now.getDate()).padStart(2, '0')
+ };
+ const { hour, minute } = {
+ hour: String(now.getHours()).padStart(2, '0'),
+ minute: String(now.getMinutes()).padStart(2, '0')
+ };
+ const dateString = `${year}-${month}-${day} ${hour}:${minute}`;
+
+ // Append to form:
+ formData.append('date', dateString);
+
+ // Send form:
+ const req = new XMLHttpRequest();
+ req.open(form.method, form.action, true);
+ req.onload = event => {
+ document.write(req.response);
+ };
+
+ req.send(formData);
+ }, false);
+ </script>
+
+ {% if comments is defined %}
+ <div class="comments">
+ {% for comment in comments %}
+ <div class="comment">
+ <span class="name">~ {{ comment['name'] }}</span><span class="date">~{{ comment['date'] }}</span>
+ <div style="clear: left;"></div>
+ <div class="comment-text" style="font-size: 120%;">
+ {{ comment['comment'] | safe }}
+ </div>
+ </div>
+ {% else %}
+ <p align="center">No comment posted yet... :(</p>
+ {% endfor %}
+ </div>
+ {% else %}
+ <center><p>Comments weren't given by renderer!!</p></center>
+ {% endif %}
+{% endblock %}
diff --git a/shiimoe/templates/index.html b/shiimoe/templates/index.html
@@ -0,0 +1,34 @@
+{% extends 'layout.html' %}
+{% block title %}Hjem{% endblock %}
+{% block header %}
+ <a href="/"><h1>Shimo!!</h1></a>
+{% endblock %}
+{% block content %}
+ <noscript><p>Welcome, Based JavaScript Hater.</p></noscript>
+
+ <p>Hei, jeg heter Shimo, jeg studerer fysikk og kjemi på universitet.
+ Jeg liker å lese (favoritten min er Kafka) og spile gitar.
+ Waifuen min er Yui, selvfølgelig.
+ </p>
+
+ <p>
+ Kontakt: <a class="link" href="mailto:sam@shii.moe"><code>sam@shii.moe</code></a>,
+ <a class="link" href="https://twitter.com/shimofruit"><code>@shimofruit</code></a>.
+ </p>
+
+ <h2>Sign the
+ <a class="titleButton"
+ style="width:auto; padding:0 0.25em; margin: 0;"
+ href="/guestbook">Guestbook!!</a>
+ </h2>
+
+ <h3>Reasons Why The Ladies Love Me:</h3>
+ <ol>
+ <li>I'm 6'</li>
+ <li>I have a blåhaj</li>
+ <li>My Mum says I'm cool</li>
+ <li>I'll give you kisses</li>
+ <li>I have a PGP key: <a class="link" href="shiimoe.pgp"><code>A5E16A6FFC9DD158</code></a></li>
+ <li>My website is <a class="link" href="https://github.com/Shiimoe/shii.moe">open source!</a></li>
+ </ol>
+{% endblock %}
diff --git a/shiimoe/templates/layout.html b/shiimoe/templates/layout.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+
+{% macro nav_link(link, title) -%}
+ {% if request.path == link %}
+ <div class="titleButton"><a href="/">Hjem</a></div>
+ {% else %}
+ <div class="titleButton"><a href="{{ link }}">{{ title }}</a></div>
+ {% endif %}
+{%- endmacro %}
+
+<html lang="en-GB">
+<head>
+ {% block meta %}
+ <meta charset="UTF-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ {% endblock %}
+ {% block head %}
+ {% endblock %}
+ {% block default_head %}
+ <link rel="stylesheet" type="text/css" href="/style.css"/>
+ <link rel="icon" type="image/png" href="/mascots/{{ mascot }}-square.png">
+ <link rel="preload" as="image" href="/background-300x300.png">
+ <title>{% block title %}{% endblock %} – Shiimoe!!</title>
+ {% endblock %}
+</head>
+<body>
+ <div id="content">
+ {% block header %}
+ {% endblock %}
+ {% block nav %}
+ <div class="titleBar">
+ {{ nav_link('/music', 'Musikk') }}
+ {{ nav_link('/school', 'Skole') }}
+ {{ nav_link('/norsk', 'Norsk') }}
+ {{ nav_link('/books', 'Bøker') }}
+ {{ nav_link('/blog', 'Blog') }}
+ </div>
+ {% endblock %}
+ {% block content %}
+ {% endblock %}
+ </div>
+ {% block scripts %}
+ <noscript><style>#mascot { opacity: 0.85; }</style></noscript>
+ <img src="/mascots/{{ mascot }}.png" id="mascot"></img>
+ <script src="/colours.js"></script>
+ <script src="/image.js"></script>
+ <script>randomMascot(NOVELTY);</script>
+ {% endblock %}
+</body>
+</html>
diff --git a/shiimoe/templates/music.html b/shiimoe/templates/music.html
@@ -0,0 +1,32 @@
+{% extends 'layout.html' %}
+{% block header %}
+ <a href="/"><h1>{% block title %}Musikk{% endblock %}!!</h1></a>
+{% endblock %}
+{% block content %}
+ <div class="guitarAudio">
+ <audio controls s>
+ <source src="lemon.mp3" type="audio/mpeg">
+ </audio>
+ </div>
+
+ <div class = "guitarCentre">
+ <img src="gita.jpg" class="guitarPicture">
+ <p class="guitarText">A recording of <em>Lemon Meringue Tie</em> by <em>Dance Gavin Dance</em> played on my <em>Epiphone Les Paul</em>, coming
+ through an <em>Orange Crush 20</em> with a <em>Boss DS-1</em> for distortion.
+ </p>
+ <p class="guitarText">Et opptak av <em>Lemon Merginue Tie</em> av <em>Dance Gavin Dance</em> spillet
+ på <em>Epiphone Les Paul</em>en min, gjennom en <em>Orange Crush 20</em> med en <em>Boss DS-1</em>
+ for forvrengning.</p>
+ </div>
+
+ <div class ="guitarAudio">
+ <audio controls s class="audio">
+ <source src="rio.mp3" type="audio/mpeg">
+ </audio>
+ </div>
+
+ <div class = "guitarCentre">
+ <img src="classical.jpg" class="guitarPicture">
+ <p class="guitarText">Dette musikkstykket heter <em>Rio by Night</em> av <em>Vincent Lindsey-Clark</em>, det er veldig enkel, men det er gøy å spille.</p>
+ </div>
+{% endblock %}
diff --git a/shiimoe/templates/norsk.html b/shiimoe/templates/norsk.html
@@ -0,0 +1,10 @@
+{% extends 'layout.html' %}
+{% block header %}
+ <a href="/"><h1>{% block title %}Norsk{% endblock %}!!</h1></a>
+{% endblock %}
+{% block content %}
+ <p>Hei, kjæresten min er Norsk, han prøver å lære meg Norsk, men det går ikke veldig bra.</p>
+ <p>Jeg laget dette i GIMP mange år siden.</p>
+
+ <img src="norsk.png" class="image-card">
+{% endblock %}
diff --git a/shiimoe/templates/school.html b/shiimoe/templates/school.html
@@ -0,0 +1,9 @@
+{% extends 'layout.html' %}
+{% block header %}
+ <a href="/"><h1>{% block title %}Skole{% endblock %}!!</h1></a>
+{% endblock %}
+{% block content %}
+ <h3 style="text-align: center;">Maths</h3>
+
+ <iframe class="pdf" src="maths.pdf" width="100%" height="900pt"></iframe>
+{% endblock %}
diff --git a/static/books.html b/static/books.html
@@ -1,37 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
- <title>Shimo!!</title>
- <link rel='stylesheet' type='text/css' href="style.css"/>
- <link rel='icon' type='image/png' href="/mascots/2-square.png">
- <link rel="preload" as="image" href="/background-300x300.png">
- <meta charset="utf-8"/>
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
-</head>
-<body>
- <div id="content">
-
- <a href="/"><h1>Bøker!!</h1></a>
-
- <div class="titleBar">
- <div class="titleButton"><a href="/music">Musik</a></div>
- <div class="titleButton"><a href="/school">Skole</a></div>
- <div class="titleButton"><a href="/norsk">Norsk</a></div>
- <div class="titleButton"><a href="/">Hjem</a></div>
- <div class="titleButton"><a href="/blog">Blog</a></div>
- </div>
-
- <p><em>The Trial?</em> more like <em>The Smile</em>, because it makes me happy
- when I read it (✿◡‿◡)</p>
- <p>Har du lest <em>Stoner</em>? Du har ikke? Slutt å les dette og begynn å lese det i stedet for!</p>
-
- </div>
-
- <noscript><style>#mascot { opacity: 0.85; }</style></noscript>
- <img src="/mascots/2.png" id="mascot"></img>
- <script src="colours.js"></script>
- <script src="image.js"></script>
- <script>randomMascot(NOVELTY);</script>
-</body>
-</html>
diff --git a/static/index.html b/static/index.html
@@ -1,56 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
- <title>Shimo!!</title>
- <link rel='stylesheet' type='text/css' href="style.css">
- <link rel='icon' type='image/png' href="/mascots/1-square.png">
- <link rel="preload" as="image" href="/background-300x300.png">
- <meta charset="utf-8"/>
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
-</head>
-<body>
- <div id="content">
-
- <a href="/"><h1>Shimo!!</h1></a>
-
- <div class="titleBar">
- <div class="titleButton"><a href="/music">Musik</a></div>
- <div class="titleButton"><a href="/school">Skole</a></div>
- <div class="titleButton"><a href="/norsk">Norsk</a></div>
- <div class="titleButton"><a href="/books">Bøker</a></div>
- <div class="titleButton"><a href="/blog">Blog</a></div>
- </div>
-
- <noscript><p>Welcome, Based JavaScript Hater.</p></noscript>
-
- <p>Hei, jeg heter Shimo, jeg studerer fysikk og kjemi på universitet.
- Jeg liker å lese (favoritten min er Kafka) og spile gitar.
- Waifuen min er Yui, selvfølgelig.
- </p>
-
- <p>
- Kontakt: <a href="mailto:sam@shii.moe"><code>sam@shii.moe</code></a>,
- <a href="https://twitter.com/shimofruit"><code>@shimofruit</code></a>.
- </p>
-
- <h2>Sign the <a class="titleButton" style="width:auto; margin: 0;" href="/guestbook">Guestbook!!</a></h2>
-
- <h3>Reasons Why The Ladies Love Me:</h3>
- <ol>
- <li>I'm 6'</li>
- <li>I have a blåhaj</li>
- <li>My Mum says I'm cool</li>
- <li>I'll give you kisses</li>
- <li>I have a PGP key: <a href="shiimoe.pgp"><code>A5E16A6FFC9DD158</code></a></li>
- <li>My website is <a href="https://github.com/Shiimoe/shii.moe">open source!</a></li>
- </ol>
- </div>
-
- <noscript><style>#mascot { opacity: 0.85; }</style></noscript>
- <img src="/mascots/1.png" id="mascot"></img>
- <script src="colours.js"></script>
- <script src="image.js"></script>
- <script>randomMascot(NOVELTY);</script>
-</body>
-</html>
diff --git a/static/music.html b/static/music.html
@@ -1,62 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
- <title>Musik!!</title>
- <link rel='stylesheet' type='text/css' href="style.css"/>
- <link rel='icon' type='image/png' href="/mascots/3-square.png">
- <link rel="preload" as="image" href="/background-300x300.png">
- <meta charset="utf-8"/>
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
-</head>
-<body>
- <div id="content">
- <a href="/"><h1>Musik!!</h1></a>
-
- <div class="titleBar">
- <div class="titleButton"><a href="/">Hjem</a></div>
- <div class="titleButton"><a href="/school">Skole</a></div>
- <div class="titleButton"><a href="/norsk">Norsk</a></div>
- <div class="titleButton"><a href="/books">Bøker</a></div>
- <div class="titleButton"><a href="/blog">Blog</a></div>
- </div>
-
- <div class="guitarAudio">
- <audio controls s>
- <source src="lemon.mp3" type="audio/mpeg">
- </audio>
- </div>
-
-
- <div class = "guitarCentre">
- <img src="gita.jpg" class="guitarPicture">
- <p class="guitarText">A recording of <em>Lemon Meringue Tie</em> by <em>Dance Gavin Dance</em> played on my <em>Epiphone Les Paul</em>, coming
- through an <em>Orange Crush 20</em> with a <em>Boss DS-1</em> for distortion.
- </p>
- <p class="guitarText">Et opptak av <em>Lemon Merginue Tie</em> av <em>Dance Gavin Dance</em> spillet
- på <em>Epiphone Les Paul</em>en min, gjennom en <em>Orange Crush 20</em> med en <em>Boss DS-1</em>
- for forvrengning.</p>
- </div>
-
- <div class ="guitarAudio">
- <audio controls s class="audio">
- <source src="rio.mp3" type="audio/mpeg">
- </audio>
- </div>
-
- <div class = "guitarCentre">
- <img src="classical.jpg" class="guitarPicture">
- <p class="guitarText">Dette musikkstykket heter <em>Rio by Night</em> av <em>Vincent Lindsey-Clark</em>, det er veldig
- enkel, men det er gøy å spille.</p>
- </div>
-
-
- </div>
-
- <noscript><style>#mascot { opacity: 0.85; }</style></noscript>
- <img src="/mascots/3.png" id="mascot"></img>
- <script src="colours.js"></script>
- <script src="image.js"></script>
- <script>randomMascot(NOVELTY);</script>
-</body>
-</html>
diff --git a/static/norsk.html b/static/norsk.html
@@ -1,38 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
- <title>Shimo!!</title>
- <link rel='stylesheet' type='text/css' href="style.css"/>
- <link rel='icon' type='image/png' href="/mascots/4-square.png">
- <link rel="preload" as="image" href="/background-300x300.png">
- <meta charset="utf-8"/>
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
-</head>
-<body>
- <div id="content">
-
- <a href="/"><h1>Norsk!!</h1></a>
-
- <div class="titleBar">
- <div class="titleButton"><a href="/music">Musik</a></div>
- <div class="titleButton"><a href="/school">Skole</a></div>
- <div class="titleButton"><a href="/">Hjem</a></div>
- <div class="titleButton"><a href="/books">Bøker</a></div>
- <div class="titleButton"><a href="/blog">Blog</a></div>
- </div>
-
- <p>Hei, kjæresten min er Norsk, han prøver å lære meg Norsk, men det går ikke veldig bra.</p>
- <p>Jeg laget dette i GIMP mange år siden.</p>
-
- <img src="norsk.png" class="image-card">
-
- </div>
-
- <noscript><style>#mascot { opacity: 0.85; }</style></noscript>
- <img src="/mascots/4.png" id="mascot"></img>
- <script src="colours.js"></script>
- <script src="image.js"></script>
- <script>randomMascot(NOVELTY);</script>
-</body>
-</html>
diff --git a/static/school.html b/static/school.html
@@ -1,37 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
- <title>Shimo!!</title>
- <link rel='stylesheet' type='text/css' href="style.css"/>
- <link rel='icon' type='image/png' href="/mascots/5-square.png">
- <link rel="preload" as="image" href="/background-300x300.png">
- <meta charset="utf-8"/>
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
-</head>
-<body>
- <div id="content">
-
- <a href="/"><h1>Skole!!</h1></a>
-
- <div class="titleBar">
- <div class="titleButton"><a href="/music">Musik</a></div>
- <div class="titleButton"><a href="/">Hjem</a></div>
- <div class="titleButton"><a href="/norsk">Norsk</a></div>
- <div class="titleButton"><a href="/books">Bøker</a></div>
- <div class="titleButton"><a href="/blog">Blog</a></div>
- </div>
-
- <h3 style="text-align: center;">Maths</h3>
-
- <iframe class="pdf" src="maths.pdf" width="100%" height="900pt"></iframe>
-
- </div>
-
- <noscript><style>#mascot { opacity: 0.85; }</style></noscript>
- <img src="/mascots/5.png" id="mascot"></img>
- <script src="colours.js"></script>
- <script src="image.js"></script>
- <script>randomMascot(NOVELTY);</script>
-</body>
-</html>
diff --git a/templates/blogindex.html b/templates/blogindex.html
@@ -1,71 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <link rel='stylesheet' type='text/css' href="style.css"/>
- <link rel='icon' type='image/png' href="/mascots/8-square.png">
- <link rel="preload" as="image" href="/background-300x300.png">
- <title>Blog Index</title>
- <style>
- .blog-posts {
- margin: 0;
- padding: 0;
- list-style: none;
- }
- .blog-listing a {
- display: flex;
- justify-content: space-between;
- padding: 0.7em 1em;
- margin: 0.3em 0;
- border-radius: 5pt;
- transition: background-color .05s ease;
- text-decoration: none;
- }
- .blog-listing a:hover {
- background-color: #679dd727;
- }
- .blog-listing .title {
- text-decoration: underline;
- }
- .blog-listing .published {
- opacity: 0.4;
- }
- </style>
-</head>
-<body>
- <div id="content">
-
- <a href="/"><h1>Blog Posts!!</h1></a>
-
- <div class="titleBar">
- <div class="titleButton"><a href="/music">Musik</a></div>
- <div class="titleButton"><a href="/school">Skole</a></div>
- <div class="titleButton"><a href="/norsk">Norsk</a></div>
- <div class="titleButton"><a href="/books">Bøker</a></div>
- <div class="titleButton"><a href="/blog">Blog</a></div>
- </div>
-
- <ul class="blog-posts">
- {% for post in posts %}
- <li class="blog-listing">
- <a href="/blog/{{ post['slug'] }}">
- <span class="title">{{ post['title'] }}</span>
- <span class="published">
- (<time datetime="{{ post['datetime'] }}">{{ post['date'] }}</time>)
- </span>
- </a>
- </li>
- {% endfor %}
- </ul>
-
- </div>
- <noscript><style>#mascot { opacity: 0.85; }</style></noscript>
- <img src="/mascots/8.png" id="mascot"></img>
- <script src="/colours.js"></script>
- <script src="/image.js"></script>
- <script>randomMascot(NOVELTY);</script>
-
-</body>
-</html>-
\ No newline at end of file
diff --git a/templates/blogpost.html b/templates/blogpost.html
@@ -1,67 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <link rel='stylesheet' type='text/css' href="/style.css"/>
- <link rel='icon' type='image/png' href="/mascots/8-square.png">
- <link rel="preload" as="image" href="/background-300x300.png">
- <title>{{ title }}</title>
- <style>
- .blog-title {
- text-align: left;
- font-size: 3em;
- text-shadow: none;
- font-weight: 300;
- }
- article figure {
- display: flex;
- margin-left: 0;
- margin-right: 0;
- padding: 0;
- justify-content: center;
- align-content: center;
- align-items: center;
- flex-direction: column;
- }
- article figure figcaption {
- font-style: italic;
- font-size: small;
- margin-top: 1em;
- }
- article img {
- max-width: 100%;
- max-height: 30em;
- border-radius: 7pt;
- border: 1pt solid #fff8;
- box-shadow: 0 4px 20px -7px #0006;
- }
- </style>
-</head>
-<body>
- <div id="content">
-
- <a href="/"><h1>Shimo!!</h1></a>
-
- <div class="titleBar">
- <div class="titleButton"><a href="/music">Musik</a></div>
- <div class="titleButton"><a href="/school">Skole</a></div>
- <div class="titleButton"><a href="/norsk">Norsk</a></div>
- <div class="titleButton"><a href="/books">Bøker</a></div>
- <div class="titleButton"><a href="/blog">Blog</a></div>
- </div>
-
- <h1 class="blog-title">{{ title }}</h1>
- <article>{{ content | safe }}</article>
-
- </div>
-
- <noscript><style>#mascot { opacity: 0.85; }</style></noscript>
- <img src="/mascots/8.png" id="mascot"></img>
- <script src="/colours.js"></script>
- <script src="/image.js"></script>
- <script>randomMascot(NOVELTY);</script>
-
-</body>
-</html>-
\ No newline at end of file
diff --git a/templates/guestbook.html b/templates/guestbook.html
@@ -1,194 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
- <title>Shimo!!</title>
- <style>
- .name {
- float: left;
- }
- .date {
- float: right;
- font-family: var(--monospace);
- }
- .date, .name {
- color: #0007;
- }
- .comment-text {
- margin-top: 0.5rem;
- }
- .comments {
- margin-top: 3rem;
- }
- .comments a {
- color: #4e87bd;
- text-decoration: underline;
- }
- .comments a:hover {
- color: #3d6f9f;
- }
- .comments h1, .comments h2, .comments h3,
- .comments h4, .comments h5, .comments h6 {
- text-align: left;
- margin: 0.4em 0 0 0;
- border-bottom: 1pt solid #0000000f;
- }
- .comments img {
- max-width: 100%;
- max-height: 20em;
- display: block;
- border: 2pt solid #0000001a;
- border-radius: 5pt;
- margin: 0.5em 0;
- filter: drop-shadow(0 4pt 8pt #0004);
- }
- .input {
- font-family: var(--serif);
- padding: 0.4rem 0.6rem;
- font-size: 0.9em;
- width: 30%;
- border: 1px solid #0004;
- border-radius: 3pt;
- }
- .textbox {
- margin: 0.5em 0;
- padding: 0;
- position: relative;
- }
- .textbox > i {
- position: absolute;
- left: 0.6em;
- bottom: 0.7em;
- opacity: 0.3;
- font-size: 1rem;
- }
- .comment-input {
- font-family: var(--monospace);
- height: 8em;
- margin: 0em;
- }
- .comment {
- margin: 2rem 0 1.5rem 0;
- background: #00000008;
- padding: 0.7rem 1.2rem 0 1.2rem;
- border-bottom: 3pt solid #0000001a;
- }
- .comment-text p:nth-child(1) {
- margin-top: 0;
- }
- input[type="submit"] {
- margin-left: 30%;
- font-family: inherit;
- transform-origin: left;
- transform: translateX(-50%);
- }
- .error {
- margin: 1em 0 2em 0;
- padding: 1em 1.5em;
- border: 2pt solid #ff6b0091;
- border-radius: 10pt;
- border-top-left-radius: 0;
- background: #ce285bad;
- color: white;
- }
- </style>
-
- <link rel='stylesheet' type='text/css' href="style.css"/>
- <link rel='icon' type='image/png' href="/mascots/8-square.png">
- <link rel="preload" as="image" href="/background-300x300.png">
- <meta charset="utf-8"/>
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
-</head>
-<body>
- <div id="content">
-
- <a href="/"><h1>Shimo!!</h1></a>
-
- <div class="titleBar">
- <div class="titleButton"><a href="/music">Musik</a></div>
- <div class="titleButton"><a href="/school">Skole</a></div>
- <div class="titleButton"><a href="/norsk">Norsk</a></div>
- <div class="titleButton"><a href="/books">Bøker</a></div>
- <div class="titleButton"><a href="/blog">Blog</a></div>
- </div>
-
- {% if error_msg is defined %}
- <p class="error">{{ error_msg }}</p>
- {% endif %}
-
- <form action="/postcomment" method="POST" name="commentBox">
- <input class="name-input input"
- name="name" placeholder="Your name"
- {% if dname is defined %} value="{{ dname }}" {% endif %}
- required>
- <div class="textbox">
- <textarea class="comment-input input" name="comment"
- placeholder="# Your comment

(no links, ≤850 characters)"
- required>{{ dcomment }}</textarea>
- <i class="fab fa-markdown"></i>
- </div>
- <input type="submit" value="Post">
- </form>
- <!-- add gender radio boxes (femboy) -->
-
- <script>
- // Script to get local time, will just fall back on server time
- // if the script does not run.
- const form = document.forms.namedItem("commentBox");
- form.addEventListener('submit', ev => {
- ev.preventDefault();
-
- const formData = new FormData(form);
-
- const now = new Date();
- const { year, month, day } = {
- year: String(now.getFullYear()),
- month: String(now.getMonth() + 1).padStart(2, '0'),
- day: String(now.getDate()).padStart(2, '0')
- };
- const { hour, minute } = {
- hour: String(now.getHours()).padStart(2, '0'),
- minute: String(now.getMinutes()).padStart(2, '0')
- };
- const dateString = `${year}-${month}-${day} ${hour}:${minute}`;
-
- // Append to form:
- formData.append('date', dateString);
-
- // Send form:
- const req = new XMLHttpRequest();
- req.open(form.method, form.action, true);
- req.onload = event => {
- document.write(req.response);
- };
-
- req.send(formData);
- }, false);
- </script>
-
- {% if comments is defined %}
- <div class="comments">
- {% for comment in comments %}
- <div class="comment">
- <span class="name">~ {{ comment['name'] }}</span><span class="date">~{{ comment['date'] }}</span>
- <div style="clear: left;"></div>
- <div class="comment-text" style="font-size: 120%;">
- {{ comment['comment'] | safe }}
- </div>
- </div>
- {% else %}
- <p align="center">No comment posted yet... :(</p>
- {% endfor %}
- </div>
- {% else %}
- <center><p>Comments weren't given by renderer!!</p></center>
- {% endif %}
- </div>
-
- <noscript><style>#mascot { opacity: 0.85; }</style></noscript>
- <img src="/mascots/8.png" id="mascot"></img>
- <script src="/colours.js"></script>
- <script src="/image.js"></script>
- <script>randomMascot(NOVELTY);</script>
-</body>
-</html>