Compare commits

..

52 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
39042d2526 card data 2023-12-28 22:40:44 -06:00
c846c8e789 Fix rendering of the cards 2023-12-28 22:40:33 -06:00
50a67a5491 ignore test.html 2023-12-28 22:09:55 -06:00
7cbfe6e346 Create test render script 2023-12-28 22:09:37 -06:00
df7042942b put footer text into own dict 2023-12-28 22:08:20 -06:00
d6c66161a5 update header docs, remove commented meta tags 2023-12-28 22:08:02 -06:00
26acdaf66a use different way for bootstrap icons 2023-12-28 22:07:38 -06:00
8a28bd5ce2 ignore venv 2023-12-28 22:06:17 -06:00
4edd2fb59d Create a bunch of templates 2023-12-28 21:14:10 -06:00
0ed2e1ee7b ignore examples 2023-12-28 21:13:59 -06:00
03b9bf6e63 delete original templates 2023-12-28 21:12:38 -06:00
0f75811000 Update license to my new name 2023-12-27 19:32:17 -06:00
20 changed files with 798 additions and 291 deletions

8
.gitignore vendored
View File

@ -1,6 +1,8 @@
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) 2019 David Todd (c0de@c0defox.es)
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.

13
templates/base.html Normal file
View File

@ -0,0 +1,13 @@
{% include 'header.html' %}
{% include 'body_top.html' %}
{% include 'navbar.html' %}
<main>
{% include 'call_to_action.html' %}
{% include 'gallery.html' %}
</main>
{% include 'footer.html' %}
{% include 'body_bottom.html' %}

View File

@ -0,0 +1,19 @@
{% 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="{{ script.src }}"
{{- 'integrity="' + script.integrity + '"' if script.integrity else omit }}
{{- 'crossorigin="' + script.crossorigin + '"' if script.integrity else omit }}
></script>
{% endif %}
{% endfor %}
{% endif %}
</body>
</html>

53
templates/body_top.html Normal file
View File

@ -0,0 +1,53 @@
<body>
<!-- Bootstrap Color Picker -->
<svg xmlns="http://www.w3.org/2000/svg" class="d-none">
<symbol id="check2" viewBox="0 0 16 16">
<path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/>
</symbol>
<symbol id="circle-half" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/>
</symbol>
<symbol id="moon-stars-fill" viewBox="0 0 16 16">
<path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"/>
<path d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z"/>
</symbol>
<symbol id="sun-fill" viewBox="0 0 16 16">
<path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"/>
</symbol>
</svg>
<div class="dropdown position-fixed bottom-0 end-0 mb-3 me-3 bd-mode-toggle">
<button class="btn btn-bd-primary py-2 dropdown-toggle d-flex align-items-center"
id="bd-theme"
type="button"
aria-expanded="false"
data-bs-toggle="dropdown"
aria-label="Toggle theme (auto)">
<svg class="bi my-1 theme-icon-active" width="1em" height="1em"><use href="#circle-half"></use></svg>
<span class="visually-hidden" id="bd-theme-text">Toggle theme</span>
</button>
<ul class="dropdown-menu dropdown-menu-end shadow" aria-labelledby="bd-theme-text">
<li>
<button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="light" aria-pressed="false">
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em"><use href="#sun-fill"></use></svg>
Light
<svg class="bi ms-auto d-none" width="1em" height="1em"><use href="#check2"></use></svg>
</button>
</li>
<li>
<button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="dark" aria-pressed="false">
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em"><use href="#moon-stars-fill"></use></svg>
Dark
<svg class="bi ms-auto d-none" width="1em" height="1em"><use href="#check2"></use></svg>
</button>
</li>
<li>
<button type="button" class="dropdown-item d-flex align-items-center active" data-bs-theme-value="auto" aria-pressed="true">
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em"><use href="#circle-half"></use></svg>
Auto
<svg class="bi ms-auto d-none" width="1em" height="1em"><use href="#check2"></use></svg>
</button>
</li>
</ul>
</div>
<!-- End Bootstrap Color Picker -->

View File

@ -0,0 +1,39 @@
<!--{#
#~ Var | Required | Default ~#
active | no | false
header | yes | none
lead_text | yes | none
action_buttons (list, optional)
n. (list index)
- href | no | #
- theme | no | primary
- icon | no | none
- text | yes | none
... (etc)
#}-->
{% 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">{{ call_to_action.header }}</h1>
<p class="lead text-body-secondary">{{ call_to_action.lead_text }}</p>
{% if call_to_action.buttons %}
<p>
{% for button in call_to_action.buttons %}
<a
href="{{ button.href | default('#') }}"
class="btn btn-{{ button.theme | default('primary') }} my-2"
>
{% if button.icon %}
<i class="bi-{{ button.icon }}"></i>
{% endif %}
{{ button.text }}
</a>
{% endfor %}
</p>
{% endif %}
</div>
</div>
</section>
{% endif %}

View File

@ -1,8 +1,58 @@
<div class="col-md-3">
<a href="{{FULLLINK}}" target="{{FULLLINK}}">
<div class="card mb-3 bg-dark text-white">
<div class="card-header">{{TITLE}}</div>
<img class="card-img" width="250" src="{{THUMBNAIL}}" />
</div>
</a>
<div class="card shadow-sm">
{% 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"
src="{{ this.src }}"
alt="{{ this.alt_text if this.alt_text else this.title }}"
/>
{% if this.description and this.description|length >= 1 %}
<div class="card-body position-relative">
{% 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>
{% 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

@ -0,0 +1,22 @@
<div class="card" aria-hidden="true">
<div class="card-header">
<div class="d-flex align-items-center">
<strong role="status">Loading...</strong>
<div class="spinner-border ms-auto" aria-hidden="true"></div>
</div>
</div>
<svg class="bd-placeholder-img card-img-top" width="100%" height="225" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: Loading..." preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c"/><text x="50%" y="50%" fill="#eceeef" dy=".3em">Loading...</text></svg>
<div class="card-body">
<h5 class="card-title placeholder-glow">
<span class="placeholder col-6"></span>
</h5>
<p class="card-text placeholder-glow">
<span class="placeholder col-7"></span>
<span class="placeholder col-4"></span>
<span class="placeholder col-4"></span>
<span class="placeholder col-6"></span>
<span class="placeholder col-8"></span>
</p>
</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 %}

23
templates/footer.html Normal file
View File

@ -0,0 +1,23 @@
<!--{#
#~ 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">
<p class="float-end mb-1">
<a href="#">Back to top</a>
</p>
<p class="mb-1">{{ footer.main_line | default('') }}</p>
{% if footer.extra_lines %}
{% for line in footer.extra_lines %}
{% if line %}
<p class="mb-0">{{ line }}</p>
{% endif %}
{% endfor %}
{% endif %}
</div>
</footer>

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>

122
templates/header.html Normal file
View File

@ -0,0 +1,122 @@
<!--{#
#~ Var | Required | Default ~#
language | no | en
css_theme_name | no | auto
canonical_url | yes | none
page_title | yes | none
static (dict)
- scripts (dict)
- bootstrap (used in footer.html)
- src | yes | none
- integrity | no | empty
- crossorigin | no | empty if no integrity, anonymous otherwise
- theme
- src | yes | none
- integrity | no | empty
- crossorigin | no | empty if no integrity, anonymous otherwise
- css (dict)
- bootstrap
- src | yes | none
- integrity | no | empty
- crossorigin | no | empty if no integrity, anonymous otherwise
- theme
- src | yes | none
- integrity | no | empty
- crossorigin | no | empty if no integrity, anonymous otherwise
- icons
- src | yes | none
- integrity | no | empty
- crossorigin | no | empty if no integrity, anonymous otherwise
meta_list
n. (list index)
- name | yes | none
- content | yes | none
... (etc)
favicon_list (list of dicts)
n. (list index)
- rel | no | icon
- src | yes | none
- sizes | no | none
- type | no | none
- color | no | none
... (etc)
#}-->
<!doctype html>
<html
lang="{{ language | default('en') }}"
data-bs-theme="{{ css_theme_name | default('auto') }}"
>
<head>
{# 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="{{ 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" />
<meta charset="{{ charset | default('utf-8') }}" />
<link rel="canonical" href="{{ canonical_url }}" />
<title>{{ page_title }}</title>
{% if meta_list %}
<!-- meta-tags -->
{% for meta in meta_list %}
<meta name="{{ meta.name }}" content="{{ meta.content }}" />
{% endfor %}
{% endif %}
{% if favicon_list %}
<!-- Favicons -->
{% for icon in favicon_list %}
<link
rel="{{ icon.rel | default('icon') }}"
href="{{ icon.src }}"
sizes="{{ icon.sizes | default('') }}"
type="{{ icon.type | default('') }}"
color="{{ icon.color | default('') }}"
/>
{% endfor %}
{% endif %}
<!--
#~ These are to be provided by the favicon_list above ~#
<link rel="apple-touch-icon" href="/docs/5.3/assets/img/favicons/apple-touch-icon.png" sizes="180x180">
<link rel="icon" href="/docs/5.3/assets/img/favicons/favicon-32x32.png" sizes="32x32" type="image/png">
<link rel="icon" href="/docs/5.3/assets/img/favicons/favicon-16x16.png" sizes="16x16" type="image/png">
<link rel="manifest" href="/docs/5.3/assets/img/favicons/manifest.json">
<link rel="mask-icon" href="/docs/5.3/assets/img/favicons/safari-pinned-tab.svg" color="#712cf9">
<link rel="icon" href="/docs/5.3/assets/img/favicons/favicon.ico">
-->
{% if static.css | length >= 1 +%}
<!-- CSS -->
{% for _, style in static.css | items %}
<link
rel="stylesheet"
href="{{ style.src }}"
{{- 'integrity="' + style.integrity + '"' if style.integrity else omit }}
{{- 'crossorigin="' + style.crossorigin + '"' if style.integrity else omit }}
/>
{% endfor %}
{% endif %}
</head>

57
templates/navbar.html Normal file
View File

@ -0,0 +1,57 @@
<!--{#
#~ Var | Required | Default ~#
theme | no | dark
text_theme | no | white
page_title | yes | none
left_header | no | About
left_text | no | none
right_header | no | none
link_list
n. (list index)
- href | yes | none
- 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">
<div class="row">
<div class="col-sm-8 col-md-7 py-4">
<h4>{{ left_header | default('About') }}</h4>
<p class="text-body-secondary">{{ left_text | default('') }}</p>
</div>
<div class="col-sm-4 offset-md-1 py-4">
<h4>{{ right_header | default('') }}</h4>
{% if link_list %}
<ul class="list-unstyled">
{% for link in link_list %}
{% if link.href and link.text %}
<li><a href={{ link.href }} class="text-{{ text_theme | default('white') }}">
{% if link.icon %}
<i class="bi-{{ link.icon }}"></i>
{% endif %}
{{ link.text }}
</a></li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
</div>
</div>
</div>
</div>
<div class="navbar navbar-{{ theme | default('dark') }} bg-{{ theme | default('dark') }} shadow-sm">
<div class="container">
<a href="#" class="navbar-brand d-flex align-items-center">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" aria-hidden="true" class="me-2" viewBox="0 0 24 24"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>
<strong>{{ page_title }}</strong>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarHeader" aria-controls="navbarHeader" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
</div>
</div>
</header>

View File

@ -1,123 +0,0 @@
<html>
<head>
<title>Simple S3 Gallery</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="description" content="" />
<meta name="author" content="David Todd" />
<link rel="icon" type="image/png" href="https://secure.gravatar.com/avatar/1e346a54257cf0a9932fcfc1e61c015d" />
<!-- Bootstrap core CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<style>
.jumbotron {
padding-top: 3rem;
padding-bottom: 3rem;
margin-bottom: 0;
background-color: #fff;
}
@media (min-width: 768px) {
.jumbotron {
padding-top: 6rem;
padding-bottom: 6rem;
}
}
.jumbotron p:last-child {
margin-bottom: 0;
}
.jumbotron-heading {
font-weight: 300;
}
.jumbotron .container {
max-width: 40rem;
}
.card-header {
text-align: center;
padding: 0.25rem 0rem;
}
footer {
padding-top: 3rem;
padding-bottom: 3rem;
}
footer p {
margin-bottom: .25rem;
}
</style>
</head>
<body>
<header>
<div class="collapse bg-dark" id="navbarHeader">
<div class="container">
<div class="row">
<div class="col-sm-8 col-md-7 py-4">
<h4 class="text-white">About</h4>
<p class="text-muted">This was a small gallery that I put together to make my photography available to the world. Images are grouped in folders for what I've done.</p>
</div>
<div class="col-sm-4 offset-md-1 py-4">
<h4 class="text-white">Contact</h4>
<ul class="list-unstyled">
<li><a href="https://c0defox.es" class="text-white">My contact site</a></li>
<li><a href="https://t.me/c0defox" class="text-white">Hit me up on Telegram</a></li>
<li><a href="c0de#0689" class="text-white">My Discord is c0de#0689</a></li>
</ul>
</div>
</div>
</div>
</div>
<div class="navbar navbar-dark bg-dark shadow-sm">
<div class="container d-flex justify-content-between">
<a href="#" class="navbar-brand d-flex align-items-center">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" aria-hidden="true" class="mr-2" viewBox="0 0 24 24" focusable="false"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>
<strong>Simple S3 Gallery</strong>
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarHeader" aria-controls="navbarHeader" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
</div>
</div>
</header>
<main role="main">
<!-- Optional for creating a notification -->
<!--
<section class="jumbotron text-center">
<div class="container">
<h1 class="jumbotron-heading">Album example</h1>
<p class="lead text-muted">Something short and leading about the collection below—its contents, the creator, etc. Make it short and sweet, but not too short so folks dont simply skip over it entirely.</p>
<p>
<a href="#" class="btn btn-primary my-2">Main call to action</a>
<a href="#" class="btn btn-secondary my-2">Secondary action</a>
</p>
</div>
</section>
-->
<div class="album py-4 bg-dark">
<div class="container">
<div class="row">
{{THUMBROW}}
</div>
</div>
</div>
</main>
<footer class="text-muted">
<div class="container">
<p class="float-right"><a href="#">Back to top</a></p>
<p><a href="https://github.com/alopexc0de/simple-s3-gallery"></a>Simple S3 Gallery</a> &copy; 2019 <a href="https://c0defox.es">David Todd</a> - All photos are &copy; David Todd</p>
</div>
</footer>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</body>
</html>

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);
}

View File

@ -1,137 +0,0 @@
<html>
<head>
<title>Simple S3 Gallery</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="description" content="" />
<meta name="author" content="David Todd" />
<link rel="icon" type="image/png" href="https://secure.gravatar.com/avatar/1e346a54257cf0a9932fcfc1e61c015d" />
<!-- Bootstrap core CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<style>
.bg-dark {
background-color: #343a40cc !important;
}
nav.bcrumb {
margin-left: 1rem;
margin-top: 0.5rem;
}
.jumbotron {
padding-top: 3rem;
padding-bottom: 3rem;
margin-bottom: 0;
background-color: #fff;
}
@media (min-width: 768px) {
.jumbotron {
padding-top: 6rem;
padding-bottom: 6rem;
}
}
.jumbotron p:last-child {
margin-bottom: 0;
}
.jumbotron-heading {
font-weight: 300;
}
.jumbotron .container {
max-width: 40rem;
}
.card-header {
text-align: center;
padding: 0.25rem 0rem;
}
footer {
padding-top: 3rem;
padding-bottom: 3rem;
}
footer p {
margin-bottom: .25rem;
}
</style>
</head>
<body>
<header>
<div class="collapse bg-dark" id="navbarHeader">
<div class="container">
<div class="row">
<div class="col-sm-8 col-md-7 py-4">
<h4 class="text-white">About</h4>
<p class="text-white">This is a small gallery to represent images that I have taken over the past few years. Most of these are unsorted and unedited, and thus a lot are blurry or have lighting issues. I have taken, and thereby own all photos on this site.</p>
</div>
<div class="col-sm-4 offset-md-1 py-4">
<h4 class="text-white">Contact</h4>
<ul class="list-unstyled">
<li><a href="https://c0defox.es" class="text-white">My contact site</a></li>
<li><a href="https://t.me/c0defox" class="text-white">Hit me up on Telegram</a></li>
<li><a href="c0de#0689" class="text-white">My Discord is c0de#0689</a></li>
</ul>
</div>
</div>
</div>
</div>
<div class="navbar navbar-dark bg-dark shadow-sm">
<div class="container d-flex justify-content-between">
<span class="navbar-brand d-flex align-items-center">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" aria-hidden="true" class="mr-2" viewBox="0 0 24 24" focusable="false"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>
<strong>Simple S3 Gallery</strong>
<nav class="text-dark bcrumb" aria-label="breadcrumb">
<ol class="breadcrumb">
{{BREADCRUMBS}}
</ol>
</nav>
</span>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarHeader" aria-controls="navbarHeader" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
</div>
</div>
</header>
<main role="main">
<!-- Optional for creating a notification -->
<!--
<section class="jumbotron text-center">
<div class="container">
<h1 class="jumbotron-heading">Album example</h1>
<p class="lead text-muted">Something short and leading about the collection below—its contents, the creator, etc. Make it short and sweet, but not too short so folks dont simply skip over it entirely.</p>
<p>
<a href="#" class="btn btn-primary my-2">Main call to action</a>
<a href="#" class="btn btn-secondary my-2">Secondary action</a>
</p>
</div>
</section>
-->
<div class="album py-3 bg-dark">
<div class="container">
<div class="row">
{{THUMBROW}}
</div>
</div>
</div>
</main>
<footer class="text-muted">
<div class="container">
<p class="float-right"><a href="#">Back to top</a></p>
<p><a href="https://github.com/alopexc0de/simple-s3-gallery">Simple S3 Gallery</a> &copy; 2019 <a href="https://c0defox.es">David Todd</a> - All photos are &copy; David Todd</p>
</div>
</footer>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</body>
</html>

139
test.py Normal file
View File

@ -0,0 +1,139 @@
#!/usr/bin/python3
"""
test template rendering
python3 test.py | tee test.html
python3 -m http.server
"""
from jinja2 import Environment, FileSystemLoader, select_autoescape
template_vars = {
"charset": "utf-8",
"language": "en",
"css_theme_name": "auto",
"canonical_url": "http://example.com",
"page_title": "This is a title",
"theme": "dark",
"text_theme": "white",
"static": {
"scripts": {
"bootstrap": {
"src": "https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js",
},
"theme": {
"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": "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"},
],
"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"},
],
"footer": {
"main_line": "Copyright 2024 Code Fox",
"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",
"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",
},
],
},
"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",
"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/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(),
trim_blocks=True,
lstrip_blocks=True,
)
template = env.get_template("base.html")
print(template.render(**template_vars))
if __name__ == "__main__":
main()