Compare commits

...

40 Commits

Author SHA1 Message Date
1d24b8bcad move reaction section to own template 2023-12-29 18:35:09 -06:00
129dfdd7c8 format the html 2023-12-29 18:34:55 -06:00
bbc89f5969 have reactions enabled 2023-12-29 18:34:42 -06:00
bb63b18391 Move the col class to gallery 2023-12-29 18:22:12 -06:00
c8fd56837f move masonry to official cdn 2023-12-29 17:50:53 -06:00
eaddc0641f hide the title if there's a headline, add the reaction button to headline 2023-12-29 17:50:33 -06:00
40911124fd Add a headline 2023-12-29 17:25:50 -06:00
92a985b3e1 ensure title and heading contain something before rendering containers 2023-12-29 17:25:41 -06:00
31c761a991 Show card loading for example 2023-12-29 17:19:46 -06:00
4efdf5a674 Add headline to card 2023-12-29 17:19:21 -06:00
9fda7b5714 remove bootstrap from repo 2023-12-29 16:27:13 -06:00
a8e4957ce9 don't need image sizes 2023-12-29 16:14:08 -06:00
94e51683e2 make description and title optional; can't react to no-title pictures 2023-12-29 16:12:43 -06:00
1c26150822 more compact and more compliant(?) titles bars 2023-12-29 16:05:14 -06:00
61345ebb61 fix typo, disable call to action 2023-12-29 15:50:35 -06:00
b0520d2548 enable masonry 2023-12-29 15:48:28 -06:00
98741cde04 Add masonry 2023-12-29 15:46:25 -06:00
4b0af0ae99 rewrite script and css loading 2023-12-29 15:44:09 -06:00
664e4607ba Reset to 100% height (maybe we need max height) 2023-12-29 15:27:20 -06:00
28d29f8054 add some more images of different sizes 2023-12-29 15:05:17 -06:00
03a295140c limit on x220 is 9 2023-12-29 15:02:20 -06:00
ff8182d9c3 Ensure we're actually checking the value 2023-12-29 15:01:54 -06:00
5622dc6b28 fix icon spacing in call to action 2023-12-29 12:16:37 -06:00
fba84938c8 Add inverted theme classes 2023-12-29 11:55:55 -06:00
40103c42b1 move bootstrap assets to static dir 2023-12-29 11:55:15 -06:00
f17cb52c2e use this, and add reaction counter 2023-12-29 11:24:33 -06:00
a7c56f8d43 rework test to have reaction counts 2023-12-29 11:24:16 -06:00
25a86c634c rename cards to this 2023-12-29 11:23:58 -06:00
b675961b9c move call to action vars to be scoped 2023-12-29 10:18:03 -06:00
04f860e06a only show card footer if there are reactions 2023-12-28 23:39:18 -06:00
ba8935a3d3 color reactions the same as body text 2023-12-28 23:37:47 -06:00
41157d5205 fix color mode js 2023-12-28 23:36:56 -06:00
1876732d56 and this one 2023-12-28 23:28:17 -06:00
e6150c8ae6 forgot this one 2023-12-28 23:27:45 -06:00
4cabbe2013 adjust comments 2023-12-28 23:25:35 -06:00
25a3373f2b Format test script, and strip whitespace 2023-12-28 23:22:23 -06:00
61dcf7caae sync vscode settings 2023-12-28 23:15:03 -06:00
1042957ca4 ignore all logs 2023-12-28 23:14:35 -06:00
572036a292 don't ignore the gallery template 2023-12-28 23:13:28 -06:00
226cd3438e Change license to BSD-3 Clause from MIT 2023-12-28 23:08:00 -06:00
16 changed files with 459 additions and 170 deletions

4
.gitignore vendored
View File

@@ -1,9 +1,7 @@
pathlist*.txt
thumbs/*
GenThumb.log
*.log
index*.html
.vscode
gallery.html
**/example/**
**/*venv/**
test.html

6
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"license.author": "Code Fox <c0de@c0de.dev>",
"license.filename": "license",
"license.extension": ".txt",
"license.default": "bsd-3-clause"
}

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2023 Code Fox <c0de@c0de.dev>
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.

28
license.txt Normal file
View File

@@ -0,0 +1,28 @@
BSD 3-Clause License
Copyright (c) 2023, Code Fox <c0de@c0de.dev>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,9 +1,19 @@
{% if static.scripts.bootstrap %}
{% if static.scripts | length >= 1 +%}
<!-- Scripts -->
{% for _, script in static.scripts | items %}
{# Load scripts at the bottom by default #}
{% if not script.place_in_head | default(False) %}
<script
src="{{ static.scripts.bootstrap.src | default('') }}"
integrity="{{ static.scripts.bootstrap.integrity | default('') }}"
crossorigin="{{ static.scripts.bootstrap.crossorigin | default('anonymous' if static.scripts.bootstrap.integrity else '') }}"
src="{{ script.src }}"
{{- 'integrity="' + script.integrity + '"' if script.integrity else omit }}
{{- 'crossorigin="' + script.crossorigin + '"' if script.integrity else omit }}
></script>
{% endif %}
{% endfor %}
{% endif %}
</body>
</html>

View File

@@ -1,4 +1,4 @@
<!--
<!--{#
#~ Var | Required | Default ~#
active | no | false
header | yes | none
@@ -10,20 +10,20 @@
- icon | no | none
- text | yes | none
... (etc)
-->
{% if active | default(false) %}
#}-->
{% if call_to_action.active | default(false) %}
<section class="py-5 text-center container">
<div class="row py-lg-5">
<div class="col-lg-6 col-md-8 mx-auto">
<h1 class="fw-light">{{ header }}</h1>
<p class="lead text-body-secondary">{{ lead_text }}</p>
<h1 class="fw-light">{{ call_to_action.header }}</h1>
<p class="lead text-body-secondary">{{ call_to_action.lead_text }}</p>
{% if action_buttons %}
{% if call_to_action.buttons %}
<p>
{% for button in action_buttons %}
{% for button in call_to_action.buttons %}
<a
href="{{ button.href | default('#') }}"
class="btn btn-{{ button.theme | default('primary') }} my-2 d-inline-flex"
class="btn btn-{{ button.theme | default('primary') }} my-2"
>
{% if button.icon %}
<i class="bi-{{ button.icon }}"></i>

View File

@@ -1,40 +1,58 @@
<div class="col">
<div class="card shadow-sm">
<div class="card-header">
<span class="flex">{{ gallary.title }}</span>
<a
href="#"
class="btn btn-link float-end"
data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-title="Add a reaction"
>
<i class="bi-heart-fill text-end" style="font-size: 1rem; color: pink;"></i>
{% if this.headline and this.headline|length >= 1 and not this.title %}
<div class="card-header position-relative">
{{ this.headline }}
{% if gallery.reactions_enabled|default(False) %}
<a href="#" class="btn btn-link float-end">
<i class="bi-heart-fill position-absolute translate-middle top-50 end-1 text-danger"></i>
</a>
{% endif %}
</div>
{% endif %}
<img
class="card-image-top"
width="{{ gallary.width | default('100%') }}"
height="{{ gallary.height | default('225') }}"
src="{{ gallary.src }}"
alt="{{ gallary.alt_text if gallary.alt_text else gallary.title }}"
src="{{ this.src }}"
alt="{{ this.alt_text if this.alt_text else this.title }}"
/>
<div class="card-body">
<p class="card-text">{{ gallary.description | default('') }}</p>
</div>
{% if this.description and this.description|length >= 1 %}
<div class="card-body position-relative">
<div class="card-footer text-body-secondary text-end">
{% if gallary.reactions %}
{% for reaction in gallary.reactions %}
{# TODO: limit total number of reactions shown #}
<span class="badge rounded-pill">
<i class="bi-{{ reaction }}" style="color: black;"></i>
</span>
{% endfor %}
{% if this.title and this.title|length >= 1 %}
<h5 class="card-title">{{ this.title }}</h5>
{% if this.subtitle and this.subtitle|length >= 1 %}
<h6 class="card-subtitle mb-2 text-body-secondary">{{ this.subtitle }}</h6>
{% endif %}
{% if gallery.reactions_enabled|default(False) %}
<a href="#" class="btn btn-link position-absolute bottom-1 end-0">
<i class="bi-heart-fill text-danger"></i>
</a>
{% endif %}
{% endif %}
<p class="card-text">{{ this.description }}</p>
{% if this.subtext and this.subtext|length >= 1 %}
<p class="card-text">
<small class="text-body-secondary">
{{ this.subtext }}
</small>
</p>
{% endif %}
</div>
</div>
{% endif %}
{# Don't render reaction section if disabled or max reactions are zero (or less) #}
{% if gallery.reactions_enabled|default(False) or gallery.max_reactions_per_card|int <= 0 %}
{% include 'card_reaction_section.html' %}
{% endif %}
</div>

View File

@@ -1,4 +1,3 @@
<div class="col">
<div class="card" aria-hidden="true">
<div class="card-header">
<div class="d-flex align-items-center">
@@ -20,4 +19,4 @@
</p>
</div>
</div>
</div>

View File

@@ -0,0 +1,35 @@
{% if this.reactions and gallery.max_reactions_per_card|int >= 1 %}
<div class="card-footer text-body-secondary text-end">
{% set reaction_iterations = namespace(value=0) %}
{% for reaction, reaction_count in this.reactions|dictsort(by="value") %}
{# Don't render reaction if we've reached the max number, or its counter is 0 (or lower) #}
{% if reaction_iterations.value|int != gallery.max_reactions_per_card|int and reaction_count|int >= 1 %}
<button class="btn btn-link btn-sm disabled text-body position-relative">
{# TODO: Enable the button, and make clicking it toggle adding/removing that reaction to the counter, also color reaction button #}
<i class="bi-{{ reaction }}"></i>
{# Only show counter if more than 2 people gave the same reaction #}
{% if reaction_count|int >= 2 %}
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-body-inverted text-body-inverted">
{# Don't render reaction counts larger than 1000 #}
{{ reaction_count if reaction_count <= 999 else '1K+' }}
<span class="visually-hidden">
{{ reaction_count }} people reacted with {{ reaction }}
</span>
</span>
{% endif %}
</button>
{% set reaction_iterations.value = reaction_iterations.value + 1 %}
{% endif %}
{% endfor %}
{% set reaction_iterations = none %}
</div>
{% endif %}

View File

@@ -1,9 +1,9 @@
<!--
<!--{#
#~ Var | Required | Default ~#
main_line | no | none
extra_lines (list)
- (text to print) | no | none
-->
#}-->
<footer class="text-body-secondary py-5">
<div class="container">

12
templates/gallery.html Normal file
View File

@@ -0,0 +1,12 @@
<div class="album py-5 bg-body-tertiary">
<div class="container">
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 g-3" data-memory='{"PercentPossition": true}'>
{% for this in gallery_items %}
<div class="col-md">{% include 'card.html' %}</div>
<div class="col-md">{% include 'card_loading.html' %}</div>
{% endfor %}
</div>
</div>
</div>

View File

@@ -1,4 +1,4 @@
<!--
<!--{#
#~ Var | Required | Default ~#
language | no | en
css_theme_name | no | auto
@@ -40,7 +40,7 @@
- type | no | none
- color | no | none
... (etc)
-->
#}-->
<!doctype html>
<html
@@ -48,12 +48,24 @@
data-bs-theme="{{ css_theme_name | default('auto') }}"
>
<head>
{% if static.scripts.theme %}
{# Scripts that should be loaded first to avoid flickering (theme, etc) #}
{% if static.scripts | length >= 1 %}
<!-- Scripts -->
{% for _, script in static.scripts | items %}
{# Only load script if it should be in the head #}
{% if script.place_in_head | default(False) %}
<script
src="{{ static.scripts.theme.src | default('') }}"
integrity="{{ static.scripts.theme.integrity | default('') }}"
crossorigin="{{ static.scripts.theme.crossorigin | default('anonymous' if static.scripts.theme.integrity else '') }}"
src="{{ script.src }}"
{{- 'integrity="' + script.integrity + '"' if script.integrity else omit }}
{{- 'crossorigin="' + script.crossorigin + '"' if script.integrity else omit }}
></script>
{% endif %}
{% endfor %}
{% endif %}
<meta name="viewport" content="width=device-width, initial-scale=1" />
@@ -92,31 +104,19 @@
<link rel="icon" href="/docs/5.3/assets/img/favicons/favicon.ico">
-->
{% if static.css.bootstrap %}
<link
rel="stylesheet"
href="{{ static.css.bootstrap.src }}"
integrity="{{ static.css.bootstrap.integrity | default('') }}"
crossorigin="{{ static.css.bootstrap.crossorigin | default('anonymous' if static.css.bootstrap.integrity else '') }}"
/>
{% endif %}
{% if static.css | length >= 1 +%}
{% if static.css.theme %}
<link
rel="stylesheet"
href="{{ static.css.theme.src }}"
integrity="{{ static.css.theme.integrity | default('') }}"
crossorigin="{{ static.css.theme.crossorigin | default('anonymous' if static.css.theme.integrity else '') }}"
/>
{% endif %}
<!-- CSS -->
{% if static.css.icons %}
{% for _, style in static.css | items %}
<link
rel="stylesheet"
href="{{ static.css.icons.src }}"
integrity="{{ static.css.icons.integrity | default('') }}"
crossorigin="{{ static.css.icons.crossorigin | default('anonymous' if static.css.icons.integrity else '') }}"
href="{{ style.src }}"
{{- 'integrity="' + style.integrity + '"' if style.integrity else omit }}
{{- 'crossorigin="' + style.crossorigin + '"' if style.integrity else omit }}
/>
{% endfor %}
{% endif %}
</head>

View File

@@ -1,4 +1,4 @@
<!--
<!--{#
#~ Var | Required | Default ~#
theme | no | dark
text_theme | no | white
@@ -12,7 +12,7 @@
- text | yes | none
- icon | no | none
... (etc)
-->
#}-->
<header data-bs-theme="{{ theme | default('dark') }}">
<div class="collapse text-bg-{{ theme | default('dark') }}" id="navbarHeader">
<div class="container">

View File

@@ -0,0 +1,80 @@
/*!
* Color mode toggler for Bootstrap's docs (https://getbootstrap.com/)
* Copyright 2011-2023 The Bootstrap Authors
* Licensed under the Creative Commons Attribution 3.0 Unported License.
*/
(() => {
'use strict'
const getStoredTheme = () => localStorage.getItem('theme')
const setStoredTheme = theme => localStorage.setItem('theme', theme)
const getPreferredTheme = () => {
const storedTheme = getStoredTheme()
if (storedTheme) {
return storedTheme
}
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}
const setTheme = theme => {
if (theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.setAttribute('data-bs-theme', 'dark')
} else {
document.documentElement.setAttribute('data-bs-theme', theme)
}
}
setTheme(getPreferredTheme())
const showActiveTheme = (theme, focus = false) => {
const themeSwitcher = document.querySelector('#bd-theme')
if (!themeSwitcher) {
return
}
const themeSwitcherText = document.querySelector('#bd-theme-text')
const activeThemeIcon = document.querySelector('.theme-icon-active use')
const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`)
const svgOfActiveBtn = btnToActive.querySelector('svg use').getAttribute('href')
document.querySelectorAll('[data-bs-theme-value]').forEach(element => {
element.classList.remove('active')
element.setAttribute('aria-pressed', 'false')
})
btnToActive.classList.add('active')
btnToActive.setAttribute('aria-pressed', 'true')
activeThemeIcon.setAttribute('href', svgOfActiveBtn)
const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.dataset.bsThemeValue})`
themeSwitcher.setAttribute('aria-label', themeSwitcherLabel)
if (focus) {
themeSwitcher.focus()
}
}
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
const storedTheme = getStoredTheme()
if (storedTheme !== 'light' && storedTheme !== 'dark') {
setTheme(getPreferredTheme())
}
})
window.addEventListener('DOMContentLoaded', () => {
showActiveTheme(getPreferredTheme())
document.querySelectorAll('[data-bs-theme-value]')
.forEach(toggle => {
toggle.addEventListener('click', () => {
const theme = toggle.getAttribute('data-bs-theme-value')
setStoredTheme(theme)
setTheme(theme)
showActiveTheme(theme, true)
})
})
})
})()

View File

@@ -0,0 +1,88 @@
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
@media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
.b-example-divider {
width: 100%;
height: 3rem;
background-color: rgba(0, 0, 0, .1);
border: solid rgba(0, 0, 0, .15);
border-width: 1px 0;
box-shadow: inset 0 .5em 1.5em rgba(0, 0, 0, .1), inset 0 .125em .5em rgba(0, 0, 0, .15);
}
.b-example-vr {
flex-shrink: 0;
width: 1.5rem;
height: 100vh;
}
.bi {
vertical-align: -.125em;
fill: currentColor;
}
.nav-scroller {
position: relative;
z-index: 2;
height: 2.75rem;
overflow-y: hidden;
}
.nav-scroller .nav {
display: flex;
flex-wrap: nowrap;
padding-bottom: 1rem;
margin-top: -1px;
overflow-x: auto;
text-align: center;
white-space: nowrap;
-webkit-overflow-scrolling: touch;
}
.btn-bd-primary {
--bd-violet-bg: #712cf9;
--bd-violet-rgb: 112.520718, 44.062154, 249.437846;
--bs-btn-font-weight: 600;
--bs-btn-color: var(--bs-white);
--bs-btn-bg: var(--bd-violet-bg);
--bs-btn-border-color: var(--bd-violet-bg);
--bs-btn-hover-color: var(--bs-white);
--bs-btn-hover-bg: #6528e0;
--bs-btn-hover-border-color: #6528e0;
--bs-btn-focus-shadow-rgb: var(--bd-violet-rgb);
--bs-btn-active-color: var(--bs-btn-hover-color);
--bs-btn-active-bg: #5a23c8;
--bs-btn-active-border-color: #5a23c8;
}
.bd-mode-toggle {
z-index: 1500;
}
.bd-mode-toggle .dropdown-menu .active .bi {
display: block !important;
}
/*
Surprised this doesn't exist by default.
Inverted colors for the current theme; works with the color switcher
*/
.bg-body-inverted {
background-color: var(--bs-body-color);
}
.text-body-inverted {
color: var(--bs-body-bg);
}

114
test.py
View File

@@ -19,85 +19,121 @@ template_vars = {
"static": {
"scripts": {
"bootstrap": {
"src": "./templates/example/bootstrap.bundle.min.js",
"src": "https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js",
},
"theme": {
"src": ".templates/example/color-modes.js"
}
"src": "./templates/static/color-modes.js",
"place_in_head": True,
},
"masonry": {
"src": "https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js"
},
},
"css": {
"bootstrap": {
"src": "./templates/example/bootstrap.min.css"
},
"theme": {
"src": "./templates/example/gallery.css"
},
"bootstrap": {"src": "https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"},
"theme": {"src": "./templates/static/gallery.css"},
"icons": {
"src": "https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.2/font/bootstrap-icons.min.css"
}
}
},
},
},
"meta_list": [
{"name": "description", "content": "This is a description"},
{"name": "generator", "content": "simple-s3-gallery v.2023.12"},
{"name": "author", "content": "Code Fox"},
{ "name": "theme-color", "content": "#712cf9" }
{"name": "theme-color", "content": "#712cf9"},
],
"left_header": "About the gallery",
"left_text": "This is an example gallery that is generated via a template",
"right_header": "Contact",
"link_list": [
{"href": "https://c0defox.es", "text": "Fox :3"},
{ "href": "https://furry.engineer/c0de", "text": "Mastodon", "icon": "mastodon" }
{"href": "https://furry.engineer/c0de", "text": "Mastodon", "icon": "mastodon"},
],
"footer": {
"main_line": "Copyright 2024 Code Fox",
"extra_lines": [
"This is another line",
"This is yet another line"
]
"extra_lines": ["This is another line", "This is yet another line"],
},
"call_to_action": {
"active": False,
"header": "This is a call to action",
"lead_text": "Some interesting text for the user",
"action_buttons": [
{ "href": "1" },
{ "href": "2", "theme": "danger" },
"buttons": [
{"text": "something"},
{"theme": "danger", "text": "something dangerous"},
{"href": "3", "theme": "warning", "icon": "exclamation-diamond-fill"},
{ "href": "3", "theme": "warning", "icon": "exclamation-diamond-fill", "text": "some text" }
{
"href": "3",
"theme": "warning",
"icon": "exclamation-diamond-fill",
"text": "some text",
},
],
},
"gallery": {
"max_reactions_per_card": 9,
"reactions_enabled": True,
},
"gallery_items": [
{
"src": "https://files.c0defox.es/Pictures/arctic-fox.jpg",
"headline": "Alopex-Vulpes",
"description": "An arctic fox :3",
"title": "fox",
"reactions": [
"heart-fill",
"backpack4",
"balloon-fill",
"bag-x-fill"
],
"reactions": {
"0-circle": 1,
"1-circle": 2,
"2-circle": 3,
"3-circle": 4,
"4-circle": 5,
"5-circle": 6,
"6-circle": 7,
"7-circle": 8,
"8-circle": 9,
"9-circle": 10,
"0-circle-fill": 11,
"1-circle-fill": 12,
"2-circle-fill": 13,
"3-circle-fill": 14,
"4-circle-fill": 15,
"5-circle-fill": 16,
"6-circle-fill": 17,
"7-circle-fill": 18,
"8-circle-fill": 19,
"9-circle-fill": 20,
},
},
{
"src": "https://files.c0defox.es/Pictures/arctic-fox.jpg",
"description": "An arctic fox :3",
"title": "fox",
"reactions": [
"heart",
"backpack3",
"balloon",
"bag-x"
"src": "https://files.c0defox.es/Pictures/20150825_114759.jpg",
"description": "The inside of a Data Center",
"title": "Data Center",
"reactions": {"heart-fill": 1024, "bag-x-fill": 50},
},
{
"src": "https://secure.gravatar.com/avatar/1e346a54257cf0a9932fcfc1e61c015d?s=200",
"description": "",
"title": "",
},
{
"src": "https://c0defox.es/paw-tail.svg",
"alt_text": "a fox tail next to a paw print",
"title": "",
},
],
}
]
}
def main():
"""main method"""
env = Environment(loader=FileSystemLoader("templates"), autoescape=select_autoescape())
env = Environment(
loader=FileSystemLoader("templates"),
autoescape=select_autoescape(),
trim_blocks=True,
lstrip_blocks=True,
)
template = env.get_template("base.html")
print(template.render(**template_vars))
if __name__ == "__main__":
main()