kopia lustrzana https://github.com/lowtechmag/solar_v2
initial commit
rodzic
5e22155f46
commit
61e005fdd7
|
@ -0,0 +1,22 @@
|
|||
# ---> Hugo
|
||||
# Generated files by hugo
|
||||
/public/
|
||||
/resources/_gen/
|
||||
.hugo_build.lock
|
||||
|
||||
# Executable may be added to repository
|
||||
hugo.exe
|
||||
hugo.darwin
|
||||
hugo.linux
|
||||
|
||||
.DS_Store
|
||||
|
||||
# solar_v2 theme specific
|
||||
content/*/*/images/dithers/
|
||||
content/*/images/dithers/
|
||||
|
||||
#Python stuff
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
# Solar v.2
|
||||
|
||||
- Rebuild of Low-tech Magazine's Solar theme with Hugo
|
||||
- Builds the entire site in minutes rather than hours :)
|
||||
- Makes use of additional taxonomies that are possible in Hugo
|
||||
|
||||
Requires Hugo 0.10x or newer!
|
||||
|
||||
## Local Development
|
||||
```
|
||||
hugo server
|
||||
```
|
||||
|
||||
## Organizing content
|
||||
|
||||
Content is organized as [Hugo Page Bundles](https://gohugo.io/content-management/page-bundles/).
|
||||
|
||||
That means that each post is a directory which contains:
|
||||
|
||||
* the article (`index.md`)
|
||||
* the translations (`index.lang.md`)
|
||||
* the images in the article (`images/`)
|
||||
* dithered versions of the images (`images/dithers/`)
|
||||
* comments in various languages (`comments.en.md`)
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
how-to-build-a-low-tech-internet/
|
||||
├── comments.en.md
|
||||
├── images
|
||||
│ ├── air-jaldi-epostman.png
|
||||
│ ├── dithers
|
||||
│ │ ├── air-jaldi-epostman_dithered.png
|
||||
│ │ ├── freifunk-wifi-node_dithered.png
|
||||
│ │ ├── node-air-jaldi-network_dithered.png
|
||||
│ │ ├── node-spanish-guifi-network_dithered.png
|
||||
│ │ ├── node-tegola_dithered.png
|
||||
│ │ ├── sneakernet-on-rails_dithered.png
|
||||
│ │ ├── tegola-project-low-tech-internet_dithered.png
|
||||
│ │ ├── wifi-link_dithered.png
|
||||
│ │ └── wireless-links-spanish-guifi-network_dithered.png
|
||||
│ ├── freifunk-wifi-node.jpg
|
||||
│ ├── node-air-jaldi-network.png
|
||||
│ ├── node-spanish-guifi-network.png
|
||||
│ ├── node-tegola.jpg
|
||||
│ ├── sneakernet-on-rails.jpg
|
||||
│ ├── tegola-project-low-tech-internet.png
|
||||
│ ├── wifi-link.jpg
|
||||
│ └── wireless-links-spanish-guifi-network.jpg
|
||||
├── index.de.md
|
||||
├── index.en.md
|
||||
├── index.es.md
|
||||
└── index.fr.md
|
||||
```
|
||||
At least one article is required: `index.md` or `index.lang.md`.
|
||||
|
||||
## Formatting articles
|
||||
|
||||
The design relies on the following [front matter](https://gohugo.io/content-management/front-matter/) fields:
|
||||
|
||||
```
|
||||
---
|
||||
title: "How to Build a Low-tech Internet"
|
||||
date: "2015-10-26"
|
||||
summary: "If we want the internet to keep working in circumstances where access to energy is more limited, we can learn important lessons from alternative network technologies."
|
||||
slug: "how-to-build-a-low-tech-internet"
|
||||
lang: "en"
|
||||
authors: ["Kris De Decker" ]
|
||||
categories: ["Low-tech Solutions"]
|
||||
tags: ["ICT" ]
|
||||
featured_image: "tegola-project-low-tech-internet.png"
|
||||
draft: False
|
||||
---
|
||||
```
|
||||
|
||||
In the case of a translation you can specify the translators as well:
|
||||
|
||||
__!! Careful, only some metadata should to be translated, the other needs to be left intact.__
|
||||
|
||||
Specifically, the metadata keys (`title`, `date`, `summary` etc) should remain intact wheras the metadata values can be translated (such as the contents of `title` or `summary`).
|
||||
|
||||
However do __not__ translate the values of `slug`, `categories`, `tags` and `featured_image`.
|
||||
|
||||
```
|
||||
---
|
||||
title: "Cómo construir una Internet de Baja Tecnología" #TO TRANSLATE
|
||||
date: "2015-10-26"
|
||||
summary: "Si queremos que internet siga funcionando en circunstancias en que el acceso a la energía es más limitado, entonces podemos aprender lecciones importantes de las tecnologías de red alternativas." #TO TRANSLATE
|
||||
slug: "how-to-build-a-low-tech-internet"
|
||||
lang: "es" #ADD THE CORRECT LANG code (fr, es,etc.)
|
||||
authors: ["Kris De Decker" ]
|
||||
categories: ["Low-tech Solutions"]
|
||||
tags: ["ICT" ]
|
||||
featured_image: "tegola-project-low-tech-internet.png"
|
||||
translators: ["Colectivo Disonancia"] #ADD TRANSLATOR FOR THIS LANGUAGE
|
||||
draft: False
|
||||
---
|
||||
```
|
||||
|
||||
To add several authors or several tags, we use the following syntax:
|
||||
|
||||
```
|
||||
---
|
||||
authors: ["Kris De Decker","Marie Verdeil" ]
|
||||
tags: ["ICT", "another tag", "another other tag"]
|
||||
---
|
||||
```
|
||||
### Image shortcodes
|
||||
|
||||
The design relies on shortcodes for images rather than markdown image tags:
|
||||
|
||||
`{{% figure src="yutampo2.png" %}} Una borsa d’acqua calda giapponese detta yutampo, fatta di plastica rigida. Fonte: All About Japan. [https://allabout-japan.com/en/article/6244/](https://allabout-japan.com/en/article/6244/) {{% /figure %}}
|
||||
`
|
||||
|
||||
|
||||
### Reader comments
|
||||
If there are any comments to be rendered under an article they should be in a file called `comments.lang.md` and each comment rendered as such:
|
||||
```
|
||||
{{< comment name="Lord Byron" >}}
|
||||
As the Liberty lads o'er the sea
|
||||
Bought their freedom, and cheaply, with blood
|
||||
So we, boys, we
|
||||
Will die fighting, or live free,
|
||||
And down with all kings but King Ludd”
|
||||
{{</ comment >}}
|
||||
```
|
||||
|
||||
## Author & Translator pages
|
||||
|
||||
This site builds custom taxonomies for `Authors` and `Translators` which can be accessed via `http://localhost:1313/authors/` and `http://localhost:1313/translators/` respectively. Individual data about each author or translator can be written in `content/authors/authorname/index.md`
|
||||
|
||||
|
||||
# Additional utilities
|
||||
|
||||
In `utils` there are various utilities to be used before or after site rendering.
|
||||
|
||||
## dithering tool
|
||||
|
||||
`dither_images.py` recursively traverses folders and creates dithered versions of the images it finds. These are stored in the same folder as the images in a folder called "dithers".
|
||||
|
||||
### Installation & Depedencies
|
||||
|
||||
depends on [Pillow](https://pillow.readthedocs.io) and [hitherdither](https://github.com/hbldh/hitherdither)
|
||||
|
||||
`pip install Pillow git+https://www.github.com/hbldh/hitherdither`
|
||||
|
||||
### Usage
|
||||
|
||||
Dither all the images found in the subdirectories of `content`
|
||||
`python3 utils/dither_images.py --directory content/`
|
||||
|
||||
Colorize the dithers as well based on the LTM categories:
|
||||
`python3 utils/dither_images.py --directory content/ --colorize`
|
||||
|
||||
Run the script with more debug output:
|
||||
`python3 utils/dither_images.py --directory content/ --colorize --verbose`
|
||||
|
||||
Remove all dithered files in the subdirectories of `content`:
|
||||
`python3 utils/dither_images.py --remove --directory content/`
|
||||
|
||||
## Page Size Calculator
|
||||
|
||||
This script recursively traverses folders and enumerates the file size of all html pages and associated media.
|
||||
The calculated total file size is then added to the HTML page. The script looks for a `div` with class `page-size` to add the page metadata in to. This div is currently found in `layouts/partials/footer.html`
|
||||
|
||||
#### Installation & Dependencies
|
||||
|
||||
Relies on BeautifulSoup
|
||||
|
||||
`pip install bs4`
|
||||
|
||||
|
||||
#### Usage
|
||||
|
||||
This script should be run *after* the site has been generated on the resulting files. It is a post-processing step.
|
||||
In the case of Hugo, this is usually the directory called `public`. Add the baseurl that you also use in production:
|
||||
|
||||
`python3 utils/calculate_size.py --directory public/ --baseURL https://solar.lowtechmagazine.com`
|
||||
|
||||
## build_site.sh
|
||||
|
||||
This is a script to build the hugo site and run the various support scripts. It assumes you generate and deploy the site on the same machine.
|
||||
|
||||
It can be used in `cron` to make a daily build at 12:15 and log the output.
|
||||
|
||||
`15 12 * * * /bin/bash /path/to/repo/utils/build_site.sh > /path/to/build.log 2>&1`
|
||||
|
||||
## pelican to hugo converter
|
||||
|
||||
`convert_to_hugo.py` converts posts of the Pelican format of Solar v1 to Hugo Page Bundles. Needs to be run only **once and never again** because it will _overwrite whatever you have in your content folder!_
|
||||
|
||||
You need to edit the file to set the input and output paths etc.
|
||||
|
||||
**N.B. this tool will do 95.3% of the work but you will need to manually fix a few individual files**
|
||||
|
||||
### Installation & Dependencies
|
||||
|
||||
depends on [jinja2](https://jinja.palletsprojects.com/en/3.1.x/)
|
||||
|
||||
`pip install jinja2`
|
||||
|
||||
### Usage
|
||||
|
||||
You need to first edit the file to set the input and output paths. These can be found around line 20.
|
||||
|
||||
`python3 utils/convert_to_hugo.py`
|
||||
|
||||
# Contributions
|
||||
|
||||
The Solar v.2 theme was made by
|
||||
|
||||
* [Marie Otsuka](https://motsuka.com/)[^1]
|
||||
* [Roel Roscam Abbing](https://test.roelof.info)[^1]
|
||||
* [Marie Verdeil](https://verdeil.net/)
|
||||
|
||||
With contributions by
|
||||
* [Erhard Maria Klein](http://www.weitblick.de/)
|
||||
|
||||
[^1]: Marie and Roel created the [original Pelican theme](https://github.com/lowtechmag/solar) for the first version of https://solar.lowtechmagazine.com
|
|
@ -0,0 +1,84 @@
|
|||
@media print {
|
||||
html {
|
||||
font-size: 10.5pt;
|
||||
}
|
||||
body {
|
||||
background: none;
|
||||
}
|
||||
h1, footer h2 {
|
||||
font-size: 1.2rem;
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
}
|
||||
.subtitle {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
.icon {
|
||||
height: 1rem;
|
||||
}
|
||||
}
|
||||
h1.entry-title {
|
||||
font-size: 2rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
p.summary {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.entry-content {
|
||||
columns: 2;
|
||||
column-gap: 20pt;
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
a:after {
|
||||
content:" (" attr(href) ") ";
|
||||
font-size: var(--font-small);
|
||||
font-weight: normal;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
figure {
|
||||
max-width: none;
|
||||
margin: 1rem auto 0.5rem auto;
|
||||
background-color: white !important;
|
||||
mix-blend-mode: normal !important;
|
||||
page-break-inside: avoid;
|
||||
img {
|
||||
max-width: 100%;
|
||||
mix-blend-mode: normal !important;
|
||||
}
|
||||
}
|
||||
.caption {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
h2, h3, p, .footnote {
|
||||
max-width: none;
|
||||
width: 100%;
|
||||
}
|
||||
p {
|
||||
margin: 0;
|
||||
line-height: 1.2;
|
||||
text-indent: 2rem;
|
||||
}
|
||||
h2+p, .caption, .caption+p, .footnote p {
|
||||
text-indent: 0;
|
||||
}
|
||||
blockquote p {
|
||||
line-height: 1.1;
|
||||
text-indent: 0;
|
||||
margin: 1rem 0;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
}
|
||||
#battery_data {
|
||||
display: none;
|
||||
}
|
||||
nav, #battery, #comment-list, #related, #post-nav, ul.cols .featured-img, footer .dashboard {
|
||||
display: none;
|
||||
}
|
||||
ul.cols li {
|
||||
max-width: 50%;
|
||||
}
|
||||
}
|
Plik diff jest za duży
Load Diff
File diff suppressed because one or more lines are too long
Plik diff jest za duży
Load Diff
|
@ -0,0 +1,163 @@
|
|||
baseURL = "/"
|
||||
languageCode = "en-us"
|
||||
title = "LOW←TECH MAGAZINE"
|
||||
|
||||
buildFuture = true
|
||||
buildDrafts = false
|
||||
paginate = 12
|
||||
|
||||
DefaultContentLanguage = "en"
|
||||
pluralizelisttitles = "false"
|
||||
|
||||
refLinksErrorLevel = "WARNING"
|
||||
refLinksNotFoundURL = "/"
|
||||
|
||||
timeout = 60000
|
||||
|
||||
rssLimit = 3
|
||||
|
||||
[markup.goldmark.renderer]
|
||||
unsafe = true
|
||||
|
||||
[markup.goldmark.parser.attribute]
|
||||
block = true
|
||||
|
||||
|
||||
|
||||
[markup]
|
||||
[markup.highlight]
|
||||
anchorLineNos = false
|
||||
codeFences = true
|
||||
guessSyntax = false
|
||||
hl_Lines = ''
|
||||
hl_inline = false
|
||||
lineAnchors = ''
|
||||
lineNoStart = 1
|
||||
lineNos = false
|
||||
lineNumbersInTable = true
|
||||
noClasses = true
|
||||
noHl = false
|
||||
style = 'onesenterprise'
|
||||
tabWidth = 4
|
||||
|
||||
|
||||
enableInlineShortcodes = true
|
||||
|
||||
[permalinks]
|
||||
posts = '/:year/:month/:slugorfilename/'
|
||||
categories = '/:slug/'
|
||||
|
||||
[params]
|
||||
author = "Kris De Decker"
|
||||
newsletter = "https://d69baa34.sibforms.com/serve/MUIEAJWIw9w82Dl4ua6FQArPaI-3Qb-zVTwPNabHQgFH51MiGF69Smy9LOC_HPoUmBj0emaXsXT87gcQXDPvtu-AZsJCHWhkkv21CdrcQu4GdnYAhZ-MrIPhwGDecagLzYxqfvkaqXg2ODcbJU4ByoDmzJK3ZTczDo2jcWtfn-En0MGKLVkgxx9TgdHqYoPabMJCMF-agLEclEwv"
|
||||
description = "This is a solar-powered website, which means it sometimes goes offline"
|
||||
|
||||
[menu]
|
||||
[[menu.main]]
|
||||
identifier = "about"
|
||||
name = "About"
|
||||
url = "/about/"
|
||||
weight = 1
|
||||
|
||||
[[menu.main]]
|
||||
identifier = "lowtechtitle"
|
||||
name = "Low-tech Solutions"
|
||||
url = "/low-tech-solutions/"
|
||||
weight = 2
|
||||
|
||||
[[menu.main]]
|
||||
identifier = "hightechtitle"
|
||||
name = "High-tech Problems"
|
||||
url = "/high-tech-problems/"
|
||||
weight = 3
|
||||
|
||||
[[menu.main]]
|
||||
identifier = "obsoletetitle"
|
||||
name = "Obsolete Technology"
|
||||
url = "/obsolete-technology/"
|
||||
weight = 4
|
||||
|
||||
[[menu.main]]
|
||||
identifier = "offline"
|
||||
name = "Offline Reading"
|
||||
url = "/offline-reading/"
|
||||
weight = 5
|
||||
|
||||
[[menu.main]]
|
||||
identifier = "archives"
|
||||
name = "Archive"
|
||||
url = "/archives/"
|
||||
weight = 6
|
||||
|
||||
[[menu.main]]
|
||||
identifier = "donate"
|
||||
name = "Donate"
|
||||
url = "/donate/"
|
||||
weight = 7
|
||||
|
||||
[[menu.main]]
|
||||
identifier = "ntm"
|
||||
name = "NTM"
|
||||
url = "https://www.notechmagazine.com/"
|
||||
weight = 8
|
||||
|
||||
defaultContentLanguage = 'en'
|
||||
[languages]
|
||||
[languages.en]
|
||||
languageName = 'English'
|
||||
weight = 1
|
||||
|
||||
[languages.fr]
|
||||
languageName = 'Français'
|
||||
description = ''
|
||||
weight = 2
|
||||
[languages.de]
|
||||
languageName = 'Deutsch'
|
||||
weight = 3
|
||||
[languages.nl]
|
||||
languageName = 'Nederlands'
|
||||
weight = 4
|
||||
[languages.es]
|
||||
languageName = 'Español'
|
||||
weight = 5
|
||||
[languages.it]
|
||||
languageName = 'Italiano'
|
||||
weight = 6
|
||||
[languages.pt]
|
||||
languageName = 'Português'
|
||||
weight = 7
|
||||
[languages.pl]
|
||||
languageName = 'Polski'
|
||||
weight = 8
|
||||
[languages.ar]
|
||||
languageName = 'العربية'
|
||||
languagedirection = 'rtl'
|
||||
weight = 9
|
||||
[languages.vn]
|
||||
languageName = 'Tiếng Việt'
|
||||
weight = 10
|
||||
[languages.ko]
|
||||
languageName = '한국어'
|
||||
weight = 11
|
||||
|
||||
|
||||
|
||||
[taxonomies]
|
||||
author = "authors"
|
||||
tag = "tags"
|
||||
category = "categories"
|
||||
translator = "translators"
|
||||
lang = "languages"
|
||||
|
||||
[related]
|
||||
includeNewer = true
|
||||
threshold = 80
|
||||
toLower = false
|
||||
[[related.indices]]
|
||||
name = 'tags'
|
||||
weight = 100
|
||||
[[related.indices]]
|
||||
name = 'lang'
|
||||
weight = 80
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
title: "Nicht gefunden"
|
||||
summary: "Diese Seite konnte nicht gefunden werden"
|
||||
noindex: true
|
||||
lang: de
|
||||
url: "404"
|
||||
categories: [""]
|
||||
---
|
||||
Es scheint, dass etwas schief gelaufen ist.... Möglicherweise wurden einige Beiträge verschoben und dieser Link wurde noch nicht aktualisiert. Oder vielleicht ist dieser Artikel noch nicht übersetzt?
|
||||
|
||||
Bitte konsultieren Sie unser [Archiv](de/archives/), um schnell alle Inhalte wiederzufinden.
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
title: "Not Found"
|
||||
summary: "This page could not be found"
|
||||
noindex: true
|
||||
lang: en
|
||||
url: "404"
|
||||
categories: [""]
|
||||
---
|
||||
It seems that something went wrong.... Some posts may have been moved and this link has not been updated yet. Or maybe this article isn't yet translated?
|
||||
|
||||
Please consult our [archives](/archives/) to quickly find back all content.
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
title: "Erreur 404"
|
||||
summary: "La page que vous recherchez est introuvable"
|
||||
noindex: true
|
||||
lang: fr
|
||||
url: "404"
|
||||
categories: [""]
|
||||
---
|
||||
Il semble que la page que vous recherchez n'existe pas, ou n'existe plus. Il se peut qu'elle n'est pas encore été traduite ou qu'elle ai changé d'emplacement.
|
||||
|
||||
Consultez notre [archive](/fr/archives/) pour retrouver facilment tout le contenu disponible.
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
title: "Niet gevonden"
|
||||
summary: "Deze pagina kon niet worden gevonden"
|
||||
noindex: true
|
||||
lang: nl
|
||||
url: "404"
|
||||
categories: [""]
|
||||
---
|
||||
|
||||
We zijn nog bezig met het overzetten van posts en wellicht is deze link nog niet overgezet of vertaald.
|
||||
|
||||
Raadpleeg het [archief](/nl/archives/) om snel artikelen te kunnen terugvinden.
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
cascade:
|
||||
_build:
|
||||
publishResources: false
|
||||
---
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
cascade:
|
||||
_build:
|
||||
publishResources: false
|
||||
---
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
cascade:
|
||||
_build:
|
||||
publishResources: false
|
||||
---
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
cascade:
|
||||
_build:
|
||||
publishResources: false
|
||||
---
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
cascade:
|
||||
_build:
|
||||
publishResources: false
|
||||
---
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
cascade:
|
||||
_build:
|
||||
publishResources: false
|
||||
---
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
cascade:
|
||||
_build:
|
||||
publishResources: false
|
||||
---
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
cascade:
|
||||
_build:
|
||||
publishResources: false
|
||||
---
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
cascade:
|
||||
_build:
|
||||
publishResources: false
|
||||
---
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
cascade:
|
||||
_build:
|
||||
publishResources: false
|
||||
---
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
cascade:
|
||||
_build:
|
||||
publishResources: false
|
||||
---
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: Archive
|
||||
slug: archives
|
||||
lang: ar
|
||||
summary: archive intro
|
||||
layout: archive
|
||||
---
|
||||
|
||||
Archive page content
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: Archiv
|
||||
slug: archives
|
||||
lang: de
|
||||
summary: archive intro
|
||||
layout: archive
|
||||
---
|
||||
|
||||
Archive page content
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: Archivo
|
||||
slug: archives
|
||||
lang: es
|
||||
summary: archive intro
|
||||
layout: archive
|
||||
---
|
||||
|
||||
Archive page content
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: Archive
|
||||
slug: archives
|
||||
lang: fr
|
||||
summary: archive intro
|
||||
layout: archive
|
||||
---
|
||||
|
||||
Archive page content
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
title: Archivio
|
||||
slug: archives
|
||||
lang: it
|
||||
layout: archive
|
||||
---
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: Archive
|
||||
slug: archives
|
||||
lang: ko
|
||||
summary: archive intro
|
||||
layout: archive
|
||||
---
|
||||
|
||||
Archive page content
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: Archive
|
||||
slug: archives
|
||||
lang: en
|
||||
summary: archive intro
|
||||
layout: archive
|
||||
---
|
||||
|
||||
Archive page content
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: Archief
|
||||
slug: archives
|
||||
lang: nl
|
||||
summary: archive intro
|
||||
layout: archive
|
||||
---
|
||||
|
||||
Archive page content
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: Archiwum
|
||||
slug: archives
|
||||
lang: pl
|
||||
summary: archive intro
|
||||
layout: archive
|
||||
---
|
||||
|
||||
Archive page content
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: Archive
|
||||
slug: archives
|
||||
lang: pt
|
||||
summary: archive intro
|
||||
layout: archive
|
||||
---
|
||||
|
||||
Archive page content
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: Archive
|
||||
slug: archives
|
||||
lang: vn
|
||||
summary: archive intro
|
||||
layout: archive
|
||||
---
|
||||
|
||||
Archive page content
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
---
|
||||
{{< comment name="Name" >}}
|
||||
|
||||
This is the comment text.
|
||||
|
||||
{{</ comment >}}
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 40 KiB |
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 201 KiB |
|
@ -0,0 +1,393 @@
|
|||
---
|
||||
title: "Article Template: How to write articles and translations in Markdown for Hugo?"
|
||||
date: ""
|
||||
summary: "This page goes over the specific markdown syntax that should be used to write articles, add translations and comments in the new hugo solar web. "
|
||||
lang: "en"
|
||||
authors: ["Marie Verdeil" ]
|
||||
categories: [""]
|
||||
tags: [""]
|
||||
unlisted: true
|
||||
draft: false
|
||||
featured_image: "image.png"
|
||||
---
|
||||
|
||||
{{% figure src="image.png" %}}
|
||||
A screenshot of the markdown file for this page.
|
||||
{{% /figure %}}
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Files](#files)
|
||||
- [Structure](#structure)
|
||||
- [Creating a new article](#folder-name)
|
||||
- [Index](#index)
|
||||
- [Comments file](#comments-files)
|
||||
- [Article Syntax](#syntax)
|
||||
- [Front Matter](#front-matter)
|
||||
- [Syntax rules](#rules)
|
||||
- [Main rules](#main-rules)
|
||||
- [Internal links](#internal-links)
|
||||
- [Image shortcodes](#syntax-images)
|
||||
- [Comments](#syntax-comments)
|
||||
- [Translations](#translations)
|
||||
- [Translations front matter](#translations-front-matter)
|
||||
- [Translations internal links](#translations-internal-links)
|
||||
- [Translations of site metadata](#translations-meta)
|
||||
|
||||
|
||||
## Files {#files}
|
||||
|
||||
Each articles lives in the folder `posts` on Gitlab, with the following structure.
|
||||
|
||||
#### **Structure** {#structure}
|
||||
|
||||
Each post/article is a folder which contains:
|
||||
|
||||
* the article in english (`index.md`) or (`index.en.md`)
|
||||
* the translations (`index.lang.md`)
|
||||
* the images in the article (`images/`)
|
||||
* dithered versions of the images (`images/dithers/`)
|
||||
* comments in various languages (`comments.en.md`)
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
how-to-build-a-low-tech-internet/
|
||||
├── comments.en.md
|
||||
├── images
|
||||
│ ├── air-jaldi-epostman.png
|
||||
│ ├── dithers
|
||||
│ │ ├── air-jaldi-epostman_dithered.png
|
||||
│ │ ├── freifunk-wifi-node_dithered.png
|
||||
│ │ ├── node-air-jaldi-network_dithered.png
|
||||
│ │ ├── node-spanish-guifi-network_dithered.png
|
||||
│ │ ├── node-tegola_dithered.png
|
||||
│ │ ├── sneakernet-on-rails_dithered.png
|
||||
│ │ ├── tegola-project-low-tech-internet_dithered.png
|
||||
│ │ ├── wifi-link_dithered.png
|
||||
│ │ └── wireless-links-spanish-guifi-network_dithered.png
|
||||
│ ├── freifunk-wifi-node.jpg
|
||||
│ ├── node-air-jaldi-network.png
|
||||
│ ├── node-spanish-guifi-network.png
|
||||
│ ├── node-tegola.jpg
|
||||
│ ├── sneakernet-on-rails.jpg
|
||||
│ ├── tegola-project-low-tech-internet.png
|
||||
│ ├── wifi-link.jpg
|
||||
│ └── wireless-links-spanish-guifi-network.jpg
|
||||
├── index.de.md
|
||||
├── index.en.md
|
||||
├── index.es.md
|
||||
└── index.fr.md
|
||||
```
|
||||
|
||||
#### **Folder Name** {#folder-name}
|
||||
|
||||
To create on new article, be sure to create a new folder in `posts/` with the name of the article, containing at least:
|
||||
- 1 index file
|
||||
- 1 comments file (see below)
|
||||
- 1 `images/` folder where your images will live.
|
||||
|
||||
|
||||
To create an new page that isn't an article, place the folder directly in `content/` or in `content/about/` for the about section.
|
||||
|
||||
By default, your folder name is the article slug. The slug should match the title, but with the following rules. Use `"-"`instead of `" "` spaces and **don't** include special character (no `,=;/%?! &.@` etc.)
|
||||
|
||||
```
|
||||
/posts/my-article-name/index.en.md
|
||||
```
|
||||
will become:
|
||||
```
|
||||
https://solar.lowtechmagazine.com/YYYY/MM/my-article-name/
|
||||
```
|
||||
|
||||
|
||||
### **Index** {#index}
|
||||
|
||||
The article content should be in a file named `index.lang.md`. For an english article it would be `index.en.md`, for french translation `index.fr.md`, etc.
|
||||
|
||||
|
||||
Regarding the syntax of the index files see the [Syntax section below](#syntax)..
|
||||
|
||||
|
||||
### **Comments file** {#comments-files}
|
||||
|
||||
The comments should now be placed in a different file in the article folder. The different comments are separated by languages and will appear in the corresponding article version. Each comments files should be named "comments.lang.md". So for english it would be `comments.en.md`, for dutch `comments.nl.md`, etc.
|
||||
|
||||
The Comments will appear automatically at the end of the article, no need to add anything in the article file.
|
||||
|
||||
Regarding the syntax of the comments files see the [Comments section below](#syntax-comments).
|
||||
|
||||
|
||||
### **Output**
|
||||
|
||||
The folder should look like this at start:
|
||||
```
|
||||
my-article-name/
|
||||
├── comments.en.md
|
||||
├── images/
|
||||
│ └── images goes here.
|
||||
└── index.lang.md
|
||||
```
|
||||
|
||||
|
||||
## Syntax {#syntax}
|
||||
|
||||
The syntax of the markdown file has a few changes and additions in Hugo, which we will go over in this section. The specific changes related to Translations can be found in the[Translations section below](#translations).
|
||||
|
||||
## Front matter data {#front-matter}
|
||||
|
||||
The Front matter data at the top of each article should follow the following syntax:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: "Article Title"
|
||||
date: "2015-10-26"
|
||||
summary: "Article Summary"
|
||||
lang: "en"
|
||||
authors: ["Kris De Decker" ]
|
||||
categories: ["Low-tech Solutions"]
|
||||
tags: ["tag", "tag2" ]
|
||||
translators: [""]
|
||||
featured_image: "image.png"
|
||||
draft: false
|
||||
---
|
||||
```
|
||||
|
||||
**_!Do not forget the `---` at the first and last line of the front matter!_**
|
||||
|
||||
|
||||
- Date should use the following YYYY-MM-DD syntax.
|
||||
```yaml
|
||||
date: "2015-10-26"
|
||||
```
|
||||
|
||||
- Language should be using on the following: en (english), nl (dutch), fr (french) pl (polish), pt (portuguese), es (spanish), de (german), it (italian ), vn (vietnamese), ar (arabic), ko (korean).
|
||||
To add a different language translations changes in the config file are necessary.
|
||||
|
||||
```yaml
|
||||
lang: "en"
|
||||
```
|
||||
|
||||
- The authors, tags and translators fields support several entry, using this syntax:
|
||||
```yaml
|
||||
authors: ["Kris De Decker" ]
|
||||
authors: ["Kris De Decker", "Roel Roscam Abbing" ]
|
||||
tags: ["ICT", "transportation" ]
|
||||
```
|
||||
- The correct spelling for categories is:
|
||||
`"Low-tech Solutions"` (Blue), `"High-tech Problems"` (Red), `"Obsolete Technology"` (Green), `"About"` or `" "` (BW)
|
||||
|
||||
|
||||
|
||||
```yaml
|
||||
categories: ["Low-tech Solutions"]
|
||||
```
|
||||
|
||||
- The featured image will appear as a thumbnail on the category page. Make sure the image is placed inside the `images/` folder. Do not include the file path, just the image with the correct extension (.png, .jpg).
|
||||
```yaml
|
||||
featured_image: "image.png"
|
||||
```
|
||||
- `draft: false` is the default. Setting this to `draft: true` will not generate the article. It will not be visible on the site anymore, only on gitlab.
|
||||
```yaml
|
||||
draft: false
|
||||
```
|
||||
|
||||
_**Always include at least:**_ `title: "", date: "", summary: "Article Summary", lang: "en"`
|
||||
|
||||
Other metadata fields are available:
|
||||
|
||||
|
||||
|
||||
- `slug: ""` : By default, the slug is the filename but you can overwrite this by adding a slug.
|
||||
```yaml
|
||||
slug: "this-is-a-slug"
|
||||
```
|
||||
|
||||
- `unlisted: true` : Include this field to mark the article as unlisted: it will still be accessible via the url but won't be listed in the index page.
|
||||
```yaml
|
||||
unlisted: true
|
||||
```
|
||||
- `translators: [""]` : see [Translations section below](#translations)
|
||||
```yaml
|
||||
translators: [""]
|
||||
```
|
||||
## Syntax Rules {#rules}
|
||||
|
||||
The rest of of the document uses [regular markdown syntax](https://www.markdownguide.org/cheat-sheet), with a few exception. Markup conventions as follows:
|
||||
|
||||
|
||||
### **Main rules**
|
||||
|
||||
- `## Big headers are h2` and render as:
|
||||
## (Big headers are h2)
|
||||
|
||||
- `### Sub-header are h3` and render as:
|
||||
### Sub-header are h3
|
||||
|
||||
- `> Quotes` render as:
|
||||
> Quotes
|
||||
|
||||
- `* Lists` / ` - Lists` render as this list.
|
||||
|
||||
|
||||
* _Footnote references_ use this syntax: `[^number]` and render as [^1]
|
||||
* _Footnotes_ appear the bottom of the document. The syntax is `[^1]: text`
|
||||
|
||||
[^1]: Footnotes appear here the bottom of the document.
|
||||
|
||||
- `[Hyperlinks](url)` linking to other websites render as: [Hyperlinks](url)
|
||||
|
||||
|
||||
### **Internal Links**
|
||||
|
||||
To link to other articles on the solar website, we use a hugo specific shortcode to call the article folder. This has several advantages:
|
||||
1. The url will not break if the article slug changes, since we are calling the file itself.
|
||||
2. We don't need to change the url when translating an article, it's automatic: see [translations section](#translations-internal-links).
|
||||
|
||||
- _Shortcode is written as follow and looks like this:_ [Text](/).
|
||||
```go
|
||||
[Text]({{</* ref "/path-to-folder" /*>}})
|
||||
```
|
||||
|
||||
The file path should start from within the content folder and link to the article or page folder, not the slug!
|
||||
|
||||
- _Examples:_
|
||||
```go
|
||||
[Donate]({{</* ref "/donate" */>}})
|
||||
[here]({{</* ref "/posts/power-water-networks/" */>}})
|
||||
```
|
||||
|
||||
|
||||
* _To link to a section in the article_ (render as: [Link to section](#section).)
|
||||
```go
|
||||
[Link to Section](#section)
|
||||
|
||||
### Section Header{#section}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
## Images shortcodes {#syntax-images}
|
||||
|
||||
Images now use specific shortcodes instead of the classic markdown syntax. This allows t include a toggle linking to the original images and to embed the caption within the image and better control its styling.
|
||||
|
||||
The shortcode is written:
|
||||
``` go
|
||||
{{%/* figure src="image-1.png" %}}
|
||||
Here goes the image caption.
|
||||
You can include footnotes [^1],
|
||||
[Hyperlinks](https://solar.lowtechmagazine.com),
|
||||
and *regular* __markdown__ syntax.
|
||||
{{% /figure */%}}
|
||||
```
|
||||
and render as:
|
||||
|
||||
{{% figure src="image-1.png" %}}
|
||||
This is an image of the shortcode that generated it. You can include footnotes [^1],
|
||||
[Hyperlinks](https://solar.lowtechmagazine.com),
|
||||
and *regular* __markdown__ syntax.
|
||||
{{% /figure %}}
|
||||
|
||||
Captions are handy to include sources and additional info but are also useful for screen-readers users (people who cannot see images). Describing the image is thoughtful of them.
|
||||
|
||||
To render uncompressed images (not dithered and not compressed in `.webp`), use the normal markdown syntax. This comes in handy for comic pages, for example. Please pre-compress the images to prevent overcrowding the server with big files.
|
||||
```markdown
|
||||
![here goes your alt text ](image-filename.png)
|
||||
```
|
||||
|
||||
|
||||
## Comments {#syntax-comments}
|
||||
|
||||
Comments are now added in a dedicated `comments.lang.md` file, as explained above.
|
||||
The file should start with the following lines:
|
||||
```yaml
|
||||
---
|
||||
---
|
||||
```
|
||||
Each comment is then added:
|
||||
```go
|
||||
{{</* comment name="Name" >}}
|
||||
|
||||
This is the comment text.
|
||||
|
||||
{{</ comment */>}}
|
||||
```
|
||||
Check out the result [at the bottom](#comments-title)
|
||||
|
||||
## Translations {#translations}
|
||||
|
||||
To translate an article in a different language, another `index.lang.md` file should be created in the article folder as detailed above.
|
||||
|
||||
### **Front matter** {#translations-front-matter}
|
||||
|
||||
Not all front matter should be translated, or the website might give an error.
|
||||
- _Front-matter that should be translated:_
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: "Translate the title"
|
||||
date: "YYYY-MM-DD" #of the translation
|
||||
summary: "Translate the Article Summary"
|
||||
lang: "en" #add the language code (fr, nl, etc.)
|
||||
translators: ["add translator name", "other translator"]
|
||||
---
|
||||
```
|
||||
|
||||
- _Front matter that shouldn't change, no matter the language:_
|
||||
```yaml
|
||||
---
|
||||
authors: ["Kris De Decker" ]
|
||||
categories: ["Low-tech Solutions"]
|
||||
tags: ["tag", "tag2" ]
|
||||
featured_image: "image.png"
|
||||
draft: false
|
||||
---
|
||||
```
|
||||
|
||||
### **Internal Links** {#translations-internal-links}
|
||||
|
||||
When linking to articles on the website, the shortcode will handle directing to the correct translation automatically:
|
||||
|
||||
On a french article `index.fr.md` the link will redirect to the french [donate](/fr/donate) page.
|
||||
```go
|
||||
[Donate]({{</* ref "/donate" */>}})
|
||||
```
|
||||
|
||||
Another example: this article [Bring back the Horses]({{< ref "/posts/bring-back-the-horses" >}}) isn't yet available in dutch. The shortcode below in a `index.nl.md_ file would lead to the english version, until the dutch translation is available:
|
||||
```go
|
||||
[Bring back the Horses]({{</* ref "/posts/bring-back-the-horses" */>}})
|
||||
```
|
||||
|
||||
|
||||
[^1]: Footnote that are correctly linked appear here at the bottom of the document. You should use the following syntax for the footnotes:
|
||||
|
||||
|
||||
### **Translating Site Metadata** {#translations-meta}
|
||||
|
||||
Another thing that needs to be translated is the many metadata words used in the website. Such as:
|
||||
|
||||
- "Translated by"
|
||||
- "Written by"
|
||||
- "View Original Image / View Dithered Image"
|
||||
- "Subscribe to our Newsletter"
|
||||
- etc.
|
||||
|
||||
This metadata is stored in configuration files called `lang.toml` (`pl.toml`, `fr.toml`, etc.). Find this folder in `solar > i18n > lang.toml`
|
||||
|
||||
_The syntax is (here for `nl.toml`):_
|
||||
```toml
|
||||
[pagesize]
|
||||
other = 'Fill in here the word for page size'
|
||||
[written_by]
|
||||
other = 'Door'
|
||||
[translated_by]
|
||||
other = 'Vertaald door'
|
||||
```
|
||||
The `[key]` should not be changed and be the same in every language.
|
||||
|
||||
The most complete files are the french (`fr.toml`) and dutch (`nl.toml`) one, refer to those to know what expressions need translation. Untranslated expressions will default back to english.
|
||||
|
||||
|
||||
Please reach out _marie @ verdeil . net_ if you have any remaining questions.
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
title: Mitwirkende
|
||||
slug: contributors
|
||||
lang: de
|
||||
summary: "Autor*innen und Übersetzer*innen von LOW←TECH MAGAZINE"
|
||||
layout: contributors
|
||||
---
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
title: Contributors
|
||||
slug: contributors
|
||||
lang: en
|
||||
summary: "Authors and translators for LOW←TECH MAGAZINE"
|
||||
layout: contributors
|
||||
---
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
title: Contributeurs
|
||||
slug: contributors
|
||||
lang: fr
|
||||
summary: "Auteur.rices et traducteur.rices (en français) de LOW←TECH MAGAZINE"
|
||||
layout: contributors
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
title: Contributors
|
||||
slug: contributors
|
||||
lang: nl
|
||||
summary: "Authors and translators for LOW←TECH MAGAZINE"
|
||||
layout: contributors
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
---
|
||||
title: "Feeds"
|
||||
date: ""
|
||||
summary: "The website is available via feeds."
|
||||
slug: "feeds"
|
||||
aliases: "rss"
|
||||
lang: "en"
|
||||
authors: ["" ]
|
||||
categories: [""]
|
||||
tags: []
|
||||
url: /feeds/
|
||||
---
|
||||
|
||||
We provide multiple feeds for feed readers. There is one feed for each language.
|
||||
|
||||
|
||||
## Arabic
|
||||
|
||||
- [Arabic full feed (RSS)](/ar/posts/index.xml)
|
||||
|
||||
## Dutch
|
||||
|
||||
- [Dutch full feed (RSS)](/nl/posts/index.xml)
|
||||
|
||||
## English
|
||||
|
||||
- [English full feed (RSS)](/posts/index.xml)
|
||||
|
||||
|
||||
## French
|
||||
|
||||
- [French full feed (RSS)](/fr/posts/index.xml)
|
||||
|
||||
## German
|
||||
|
||||
- [German full feed (RSS)](/de/posts/index.xml)
|
||||
|
||||
|
||||
## Italian
|
||||
|
||||
- [Italian full feed (RSS)](/it/posts/index.xml)
|
||||
|
||||
|
||||
## Korean
|
||||
|
||||
- [Korean full feed (RSS)](/ko/posts/index.xml)
|
||||
|
||||
|
||||
## Polish
|
||||
|
||||
- [Polish full feed (RSS)](/pl/posts/index.xml)
|
||||
|
||||
|
||||
## Portuguese
|
||||
|
||||
- [Portuguese full feed (RSS)](/pt/posts/index.xml)
|
||||
|
||||
|
||||
## Spanish
|
||||
|
||||
- [Spanish full feed (RSS)](/es/posts/index.xml)
|
||||
|
||||
|
||||
## Vietnamese
|
||||
|
||||
- [Vietnamese full feed (RSS)](/vn/posts/index.xml)
|
||||
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 519 KiB |
|
@ -0,0 +1,42 @@
|
|||
---
|
||||
title: "Power"
|
||||
date: ""
|
||||
summary: "This website runs on a [solar powered server](/de/about/the-solar-website) located in Barcelona, and will go off-line during longer periods of bad weather. This page shows live data relating to power supply, power demand, and energy storage."
|
||||
slug: "power"
|
||||
lang: "de"
|
||||
authors: ["" ]
|
||||
categories: [""]
|
||||
tags: []
|
||||
featured_image: "solar-powered-server-weather-2.png"
|
||||
---
|
||||
|
||||
## Power supply
|
||||
|
||||
This is a forecast for the coming days, updated daily:
|
||||
<p class="forecast">
|
||||
</p>
|
||||
|
||||
This weather forecast is [powered by BrightSky](https://brightsky.dev/).
|
||||
|
||||
## Power demand
|
||||
|
||||
These are live power statistics of the solar powered server:
|
||||
<dl id="server">
|
||||
</dl>
|
||||
|
||||
(* load average per 15 minutes)
|
||||
|
||||
## Battery meter
|
||||
|
||||
The background of this website is a battery meter, designed to always display the relationship of the energy powering the website and the visitor traffic consuming it. Since 12 January 2020, the website runs on a 30W solar panel and a 168 Wh lead-acid battery.
|
||||
|
||||
We also installed a new battery meter, which simply represents the voltage of our battery. For example, a storage capacity of 68% converts to 12.68V, and the other way around. Showing a correct representation of the storage capacity requires calibration and algorithms in relation to a specific battery. This is troublesome, because we [keep experimenting with different sizes of batteries and solar panels]({{< ref "/posts/how-sustainable-is-a-solar-powered-website" >}}) to find the optimal balance between uptime and sustainability.
|
||||
|
||||
The voltage reading is the "naked" data on which each battery meter relies to display a percentage. The voltage of the battery doesn't always correlate with the energy storage capacity -- it's also influenced by the electric load (which lowers the voltage) and the solar insolation (which increases the voltage).
|
||||
|
||||
Because our load (the server) has a rather constant power use, during the day our battery meter reflects the local solar conditions. If the panel receives full sun, the voltage will most likely raise above 13V, colouring the whole website in yellow. However, if it's cloudy enough, the battery meter will decrease and the blue background is revealed.
|
||||
|
||||
Because our load is constant, during the night the battery meter reflects the storage capacity of the battery accurately. When the voltage of the battery drops below 12V, and the whole page is coloured in blue, the solar charge controller shuts down the system and the website goes offline.
|
||||
|
||||
{{% figure src="solar-powered-server-weather-2.png" %}} The accessibility of this website depends on the weather in Barcelona, Spain. {{% /figure %}}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
title: "Power"
|
||||
date: ""
|
||||
summary: "This website runs on a [solar powered server](/about/the-solar-website) located in Barcelona, and will go off-line during longer periods of bad weather. This page shows live data relating to power supply, power demand, and energy storage."
|
||||
slug: "power"
|
||||
lang: "en"
|
||||
authors: ["" ]
|
||||
categories: [""]
|
||||
tags: []
|
||||
featured_image: "solar-powered-server-weather-2.png"
|
||||
---
|
||||
|
||||
## Power supply
|
||||
|
||||
This is a forecast for the coming days, updated daily:
|
||||
<p class="forecast">
|
||||
</p>
|
||||
|
||||
This weather forecast is [powered by BrightSky](https://brightsky.dev/).
|
||||
|
||||
## Power demand
|
||||
|
||||
These are live power statistics of the solar powered server:
|
||||
<dl id="server">
|
||||
</dl>
|
||||
|
||||
(* load average per 15 minutes)
|
||||
|
||||
## Battery meter
|
||||
|
||||
The background of the top of every page is a battery meter, designed to display the relationship of the energy powering the website and the visitor traffic consuming it.
|
||||
|
||||
The battery meter simply represents the voltage of our battery. For example, a storage capacity of 68% equals 12.68V, and a storage capacity of 8% equals 12.08V. The voltage reading is the "naked" data on which each battery meter relies to display a percentage. It doesn't always correlate with the energy storage capacity, because it's also influenced by the electric load (which lowers the voltage) and the solar insolation (which increases the voltage).
|
||||
|
||||
Because our load (the server) has a rather constant power use, during the day our battery meter reflects the local solar conditions. If the panel receives full sun, the voltage will raise above 13V, coloring the whole website in yellow. However, if it gets cloudy, the battery meter will decrease and the blue background is revealed. During the night, the battery meter reflects the storage capacity of the battery accurately.
|
||||
|
||||
When the voltage of the battery drops below 12V, and the whole page is coloured in blue, the solar charge controller shuts down the system and the website goes offline. It will come back when the panel receives full sun again.
|
||||
|
||||
Showing a "correct" representation of the storage capacity requires calibration and algorithms in relation to a specific battery. This is troublesome, because we [keep experimenting with different sizes of batteries and solar panels]({{< ref "/posts/how-sustainable-is-a-solar-powered-website" >}}) to find the optimal balance between uptime and sustainability. Furthermore, the naked data are more informative for people who understand how a solar PV system works. For example, the battery meter also reveals the state and age of the battery.
|
||||
|
||||
Since 12 January 2020, the website runs on a 30W solar panel and a (new) 168 Wh lead-acid battery. From September 2018 to January 2020, the website was powered by a 50W solar panel and an (old) 86 Wh battery.
|
||||
|
||||
{{% figure src="solar-powered-server-weather-2.png" %}} The accessibility of this website depends on the weather in Barcelona, Spain. {{% /figure %}}
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
title: "Power"
|
||||
date: ""
|
||||
summary: "This website runs on a [solar powered server](/about/the-solar-website) located in Barcelona, and will go off-line during longer periods of bad weather. This page shows live data relating to power supply, power demand, and energy storage."
|
||||
slug: "power"
|
||||
lang: "es"
|
||||
authors:
|
||||
categories: [""]
|
||||
tags: []
|
||||
featured_image: "solar-powered-server-weather-2.png"
|
||||
---
|
||||
|
||||
## Power supply
|
||||
|
||||
This is a forecast for the coming days, updated daily:
|
||||
<p class="forecast">
|
||||
</p>
|
||||
|
||||
This weather forecast is [powered by BrightSky](https://brightsky.dev/).
|
||||
|
||||
## Power demand
|
||||
|
||||
These are live power statistics of the solar powered server:
|
||||
<dl id="server">
|
||||
</dl>
|
||||
|
||||
(* load average per 15 minutes)
|
||||
|
||||
## Battery meter
|
||||
|
||||
The background of this website is a battery meter, designed to always display the relationship of the energy powering the website and the visitor traffic consuming it. Since 12 January 2020, the website runs on a 30W solar panel and a 168 Wh lead-acid battery.
|
||||
|
||||
We also installed a new battery meter, which simply represents the voltage of our battery. For example, a storage capacity of 68% converts to 12.68V, and the other way around. Showing a correct representation of the storage capacity requires calibration and algorithms in relation to a specific battery. This is troublesome, because we [keep experimenting with different sizes of batteries and solar panels]({{< ref "/posts/how-sustainable-is-a-solar-powered-website" >}}) to find the optimal balance between uptime and sustainability.
|
||||
|
||||
The voltage reading is the "naked" data on which each battery meter relies to display a percentage. The voltage of the battery doesn't always correlate with the energy storage capacity -- it's also influenced by the electric load (which lowers the voltage) and the solar insolation (which increases the voltage).
|
||||
|
||||
Because our load (the server) has a rather constant power use, during the day our battery meter reflects the local solar conditions. If the panel receives full sun, the voltage will most likely raise above 13V, colouring the whole website in yellow. However, if it's cloudy enough, the battery meter will decrease and the blue background is revealed.
|
||||
|
||||
|
||||
Because our load is constant, during the night the battery meter reflects the storage capacity of the battery accurately. When the voltage of the battery drops below 12V, and the whole page is coloured in blue, the solar charge controller shuts down the system and the website goes offline.
|
||||
|
||||
{{% figure src="solar-powered-server-weather-2.png" %}} The accessibility of this website depends on the weather in Barcelona, Spain. {{% /figure %}}
|
||||
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
---
|
||||
title: "Power"
|
||||
date: ""
|
||||
summary: "This website runs on a [solar powered server](/fr/about/the-solar-website) located in Barcelona, and will go off-line during longer periods of bad weather. This page shows live data relating to power supply, power demand, and energy storage."
|
||||
slug: "power"
|
||||
lang: "fr"
|
||||
authors: ["" ]
|
||||
categories: [""]
|
||||
tags: []
|
||||
featured_image: "solar-powered-server-weather-2.png"
|
||||
---
|
||||
|
||||
## Power supply
|
||||
|
||||
This is a forecast for the coming days, updated daily:
|
||||
<p class="forecast">
|
||||
</p>
|
||||
|
||||
This weather forecast is [powered by BrightSky](https://brightsky.dev/).
|
||||
|
||||
## Power demand
|
||||
|
||||
These are live power statistics of the solar powered server:
|
||||
<dl id="server">
|
||||
</dl>
|
||||
|
||||
(* load average per 15 minutes)
|
||||
|
||||
## Battery meter
|
||||
|
||||
The background of this website is a battery meter, designed to always display the relationship of the energy powering the website and the visitor traffic consuming it. Since 12 January 2020, the website runs on a 30W solar panel and a 168 Wh lead-acid battery.
|
||||
|
||||
We also installed a new battery meter, which simply represents the voltage of our battery. For example, a storage capacity of 68% converts to 12.68V, and the other way around. Showing a correct representation of the storage capacity requires calibration and algorithms in relation to a specific battery. This is troublesome, because we [keep experimenting with different sizes of batteries and solar panels]({{< ref "/posts/how-sustainable-is-a-solar-powered-website" >}}) to find the optimal balance between uptime and sustainability.
|
||||
|
||||
The voltage reading is the "naked" data on which each battery meter relies to display a percentage. The voltage of the battery doesn't always correlate with the energy storage capacity -- it's also influenced by the electric load (which lowers the voltage) and the solar insolation (which increases the voltage).
|
||||
|
||||
Because our load (the server) has a rather constant power use, during the day our battery meter reflects the local solar conditions. If the panel receives full sun, the voltage will most likely raise above 13V, colouring the whole website in yellow. However, if it's cloudy enough, the battery meter will decrease and the blue background is revealed.
|
||||
|
||||
Because our load is constant, during the night the battery meter reflects the storage capacity of the battery accurately. When the voltage of the battery drops below 12V, and the whole page is coloured in blue, the solar charge controller shuts down the system and the website goes offline.
|
||||
|
||||
{{% figure src="solar-powered-server-weather-2.png" %}} The accessibility of this website depends on the weather in Barcelona, Spain. {{% /figure %}}
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
title: "Power"
|
||||
date: ""
|
||||
summary: "This website runs on a [solar powered server](/it/about/the-solar-website) located in Barcelona, and will go off-line during longer periods of bad weather. This page shows live data relating to power supply, power demand, and energy storage."
|
||||
slug: "power"
|
||||
lang: "it"
|
||||
authors:
|
||||
categories: [""]
|
||||
tags: []
|
||||
featured_image: "solar-powered-server-weather-2.png"
|
||||
---
|
||||
|
||||
## Power supply
|
||||
|
||||
This is a forecast for the coming days, updated daily:
|
||||
<p class="forecast">
|
||||
</p>
|
||||
|
||||
This weather forecast is [powered by BrightSky](https://brightsky.dev/).
|
||||
|
||||
## Power demand
|
||||
|
||||
These are live power statistics of the solar powered server:
|
||||
<dl id="server">
|
||||
</dl>
|
||||
|
||||
(* load average per 15 minutes)
|
||||
|
||||
## Battery meter
|
||||
|
||||
The background of this website is a battery meter, designed to always display the relationship of the energy powering the website and the visitor traffic consuming it. Since 12 January 2020, the website runs on a 30W solar panel and a 168 Wh lead-acid battery.
|
||||
|
||||
We also installed a new battery meter, which simply represents the voltage of our battery. For example, a storage capacity of 68% converts to 12.68V, and the other way around. Showing a correct representation of the storage capacity requires calibration and algorithms in relation to a specific battery. This is troublesome, because we [keep experimenting with different sizes of batteries and solar panels]({{< ref "/posts/how-sustainable-is-a-solar-powered-website" >}}) to find the optimal balance between uptime and sustainability.
|
||||
|
||||
The voltage reading is the "naked" data on which each battery meter relies to display a percentage. The voltage of the battery doesn't always correlate with the energy storage capacity -- it's also influenced by the electric load (which lowers the voltage) and the solar insolation (which increases the voltage).
|
||||
|
||||
Because our load (the server) has a rather constant power use, during the day our battery meter reflects the local solar conditions. If the panel receives full sun, the voltage will most likely raise above 13V, colouring the whole website in yellow. However, if it's cloudy enough, the battery meter will decrease and the blue background is revealed.
|
||||
|
||||
|
||||
Because our load is constant, during the night the battery meter reflects the storage capacity of the battery accurately. When the voltage of the battery drops below 12V, and the whole page is coloured in blue, the solar charge controller shuts down the system and the website goes offline.
|
||||
|
||||
{{% figure src="solar-powered-server-weather-2.png" %}} The accessibility of this website depends on the weather in Barcelona, Spain. {{% /figure %}}
|
||||
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
title: "Energie"
|
||||
date: ""
|
||||
summary: "Deze website draait op [zonne-energie](/nl/about/the-solar-website). De server staat opgesteld in Barcelona en gaat offline gedurende langere periodes van slecht weer. Deze pagina geeft \"live\" informatie weer over de productie van zonne-energie, de energieopslag, en het energieverbruik van de website."
|
||||
slug: "power"
|
||||
lang: "nl"
|
||||
authors: ["" ]
|
||||
categories: [""]
|
||||
tags: []
|
||||
featured_image: "solar-powered-server-weather-2.png"
|
||||
---
|
||||
|
||||
## Energieproductie
|
||||
|
||||
Dit is de lokale weersverwachting voor de komende dagen (dagelijkse update):
|
||||
<p class="forecast">
|
||||
</p>
|
||||
|
||||
Dit weerbericht [powered by BrightSky](https://brightsky.dev/).
|
||||
|
||||
## Energievraag
|
||||
|
||||
Dit zijn de data die door de server worden geregistreerd:
|
||||
<dl id="server">
|
||||
</dl>
|
||||
|
||||
(* gemiddelde per 15 minuten)
|
||||
|
||||
## Energieopslag
|
||||
|
||||
De achtergrond van deze website is een batterijmeter, die aangeeft over hoeveel energieopslag de zonne-installatie beschikt. Sinds 12 januari 2020 draait de website op haar huidige configuratie: een zonnepaneel van 30W en een loodzuurbatterij van 168 watt-uur.
|
||||
|
||||
De batterijmeter toont het voltage van de batterij. Bijvoorbeeld een opslagcapaciteit van 68% komt overeen met een voltage van 12.68 volt. Het voltage komt echter niet altijd overeen met de capaciteit van de energieopslag.
|
||||
|
||||
De elektrische lading van de server (die het voltage van de batterij verlaagt) kan makkelijk worden ingecalculeerd omdat het elektriciteitsverbruik van de server vrijwel constant is. Maar de zonnestraling (die het voltage van de batterij doet toenemen) is daarentegen heel veranderlijk.
|
||||
|
||||
Tijdens de nacht reflecteert het voltage accuraat de opslagcapaciteit van de batterij: de batterijmeter is dan heel precies en zal langzaam zakken. Overdag weerspiegelt de batterijmeter de lokale weersomstandigheden. Als het zonnepaneel volle zon ontvangt, stijgt het voltage boven de 13 volt en wordt de hele website in het geel gekleurd. Als het bewolkt genoeg is, dan zakt de batterijmeter en wordt de blauwe achtergrond onthuld. Als het voltage van de batterij onder de 12V zakt, en de hele pagina in het blauw is gekleurd, sluit de zonneregelaar het systeem af en gaat de website offline. Ze komt weer online als de zon opnieuw schijnt.
|
||||
|
||||
{{% figure src="solar-powered-server-weather-2.png" %}} De toegankelijkheid van deze website hangt af van het weer in Barcelona, Spanje. {{% /figure %}}
|
|
@ -0,0 +1,41 @@
|
|||
---
|
||||
title: "Power"
|
||||
date: ""
|
||||
summary: "This website runs on a [solar powered server](/pl/about/the-solar-website/) located in Barcelona, and will go off-line during longer periods of bad weather. This page shows live data relating to power supply, power demand, and energy storage."
|
||||
slug: "power"
|
||||
lang: "pl"
|
||||
authors: ["" ]
|
||||
categories: [""]
|
||||
tags: []
|
||||
featured_image: "solar-powered-server-weather-2.png"
|
||||
---
|
||||
|
||||
## Power supply
|
||||
|
||||
This is a forecast for the coming days, updated daily:
|
||||
<p class="forecast">
|
||||
</p>
|
||||
|
||||
This weather forecast is [powered by BrightSky](https://brightsky.dev/).
|
||||
|
||||
## Power demand
|
||||
|
||||
These are live power statistics of the solar powered server:
|
||||
<dl id="server">
|
||||
</dl>
|
||||
|
||||
(* load average per 15 minutes)
|
||||
|
||||
## Battery meter
|
||||
|
||||
The background of this website is a battery meter, designed to always display the relationship of the energy powering the website and the visitor traffic consuming it. Since 12 January 2020, the website runs on a 30W solar panel and a 168 Wh lead-acid battery.
|
||||
|
||||
We also installed a new battery meter, which simply represents the voltage of our battery. For example, a storage capacity of 68% converts to 12.68V, and the other way around. Showing a correct representation of the storage capacity requires calibration and algorithms in relation to a specific battery. This is troublesome, because we [keep experimenting with different sizes of batteries and solar panels]({{< ref "/posts/how-sustainable-is-a-solar-powered-website" >}}) to find the optimal balance between uptime and sustainability.
|
||||
|
||||
The voltage reading is the "naked" data on which each battery meter relies to display a percentage. The voltage of the battery doesn't always correlate with the energy storage capacity -- it's also influenced by the electric load (which lowers the voltage) and the solar insolation (which increases the voltage).
|
||||
|
||||
Because our load (the server) has a rather constant power use, during the day our battery meter reflects the local solar conditions. If the panel receives full sun, the voltage will most likely raise above 13V, colouring the whole website in yellow. However, if it's cloudy enough, the battery meter will decrease and the blue background is revealed.
|
||||
|
||||
Because our load is constant, during the night the battery meter reflects the storage capacity of the battery accurately. When the voltage of the battery drops below 12V, and the whole page is coloured in blue, the solar charge controller shuts down the system and the website goes offline.
|
||||
|
||||
{{% figure src="solar-powered-server-weather-2.png" %}} The accessibility of this website depends on the weather in Barcelona, Spain. {{% /figure %}}
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
title: "Power"
|
||||
date: ""
|
||||
summary: "This website runs on a [solar powered server](/pt/about/the-solar-website) located in Barcelona, and will go off-line during longer periods of bad weather. This page shows live data relating to power supply, power demand, and energy storage."
|
||||
slug: "power"
|
||||
lang: "pt"
|
||||
authors:
|
||||
categories: [""]
|
||||
tags: []
|
||||
featured_image: "solar-powered-server-weather-2.png"
|
||||
---
|
||||
|
||||
## Power supply
|
||||
|
||||
This is a forecast for the coming days, updated daily:
|
||||
<p class="forecast">
|
||||
</p>
|
||||
|
||||
This weather forecast is [powered by BrightSky](https://brightsky.dev/).
|
||||
|
||||
## Power demand
|
||||
|
||||
These are live power statistics of the solar powered server:
|
||||
<dl id="server">
|
||||
</dl>
|
||||
|
||||
(* load average per 15 minutes)
|
||||
|
||||
## Battery meter
|
||||
|
||||
The background of this website is a battery meter, designed to always display the relationship of the energy powering the website and the visitor traffic consuming it. Since 12 January 2020, the website runs on a 30W solar panel and a 168 Wh lead-acid battery.
|
||||
|
||||
We also installed a new battery meter, which simply represents the voltage of our battery. For example, a storage capacity of 68% converts to 12.68V, and the other way around. Showing a correct representation of the storage capacity requires calibration and algorithms in relation to a specific battery. This is troublesome, because we [keep experimenting with different sizes of batteries and solar panels]({{< ref "/posts/how-sustainable-is-a-solar-powered-website" >}}) to find the optimal balance between uptime and sustainability.
|
||||
|
||||
The voltage reading is the "naked" data on which each battery meter relies to display a percentage. The voltage of the battery doesn't always correlate with the energy storage capacity -- it's also influenced by the electric load (which lowers the voltage) and the solar insolation (which increases the voltage).
|
||||
|
||||
Because our load (the server) has a rather constant power use, during the day our battery meter reflects the local solar conditions. If the panel receives full sun, the voltage will most likely raise above 13V, colouring the whole website in yellow. However, if it's cloudy enough, the battery meter will decrease and the blue background is revealed.
|
||||
|
||||
|
||||
Because our load is constant, during the night the battery meter reflects the storage capacity of the battery accurately. When the voltage of the battery drops below 12V, and the whole page is coloured in blue, the solar charge controller shuts down the system and the website goes offline.
|
||||
|
||||
{{% figure src="solar-powered-server-weather-2.png" %}} The accessibility of this website depends on the weather in Barcelona, Spain. {{% /figure %}}
|
||||
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
title: "Privacy"
|
||||
date: ""
|
||||
summary: "This website does not use cookies, advertising services, or tracking codes. We collect server logs to understand traffic on the server. This information is viewed only by us and is not used to make user profiles."
|
||||
slug: "privacy"
|
||||
lang: "en"
|
||||
categories: [""]
|
||||
tags: []
|
||||
|
||||
---
|
||||
|
||||
Example of server logs below:
|
||||
|
||||
109.69.14.162 - - [13/Sep/2018:01:59:35 +0200] "GET
|
||||
/dithers/heated-sock.png HTTP/2.0" 200 30190
|
||||
"https://solar.lowtechmagazine.com/category/low-tech-solutions2.html"
|
||||
"Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko)
|
||||
Chrome/65.0.3325.183 Safari/537.36 Vivaldi/1.96.1147.55"
|
||||
109.69.14.162 - - [13/Sep/2018:01:59:35 +0200] "GET
|
||||
/dithers/XYZ-cargo-Trike.png HTTP/2.0" 200 45829
|
||||
"https://solar.lowtechmagazine.com/category/low-tech-solutions2.html"
|
||||
"Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko)
|
||||
Chrome/65.0.3325.183 Safari/537.36 Vivaldi/1.96.1147.55"
|
||||
109.69.14.162 - - [13/Sep/2018:01:59:35 +0200] "GET
|
||||
/dithers/fireless-cooker-in-kitchen.png HTTP/2.0" 200 44844
|
||||
"https://solar.lowtechmagazine.com/category/low-tech-solutions2.html"
|
||||
"Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko)
|
||||
Chrome/65.0.3325.183 Safari/537.36 Vivaldi/1.96.1147.55"
|
||||
109.69.14.162 - - [13/Sep/2018:01:59:36 +0200] "GET /favicon.ico
|
||||
HTTP/2.0" 200 6481
|
||||
"https://solar.lowtechmagazine.com/category/low-tech-solutions2.html"
|
||||
"Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko)
|
||||
Chrome/65.0.3325.183 Safari/537.36 Vivaldi/1.96.1147.55"
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: Translators
|
||||
lang: en
|
||||
summary: "Authors and translators for LOW←TECH MAGAZINE"
|
||||
layout: contributors
|
||||
---
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
title: Traducteurs
|
||||
lang: fr
|
||||
summary: "Authors and translators for LOW←TECH MAGAZINE"
|
||||
---
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
[sitesubtitle]
|
||||
other = 'Diese Website ist solarbetrieben und geht daher manchmal offline.'
|
||||
[about]
|
||||
other = 'Über'
|
||||
[lowtechtitle]
|
||||
other = 'Low-Tech Lösungen'
|
||||
[lowtechdescription]
|
||||
other = 'Interessante Möglichkeiten ergeben sich, entweder wenn man (eine) alte Technologie mit neuer Erkenntnis und neuen Materialien kombiniert oder alte Konzepte und traditionelle Erkenntnis auf moderne Technologie überträgt.'
|
||||
[hightechtitle]
|
||||
other = 'High-Tech Probleme'
|
||||
[hightechdescription]
|
||||
other = 'Technologie ist innerhalb unserer Gesellschaft zum Gegenstand von Idolatrie geworden, aber technologische Fortschritt zielt – meistens – darauf ab, Probleme zu lösen, die von früheren technischen Erfindungen ausgelöst wurden.'
|
||||
[obsoletetitle]
|
||||
other = 'Vergessene Technologie'
|
||||
[obsoletedescription]
|
||||
other = 'Vergangenes Wissen und oft vergessene Kenntnisse und Technologien enthalten ein großes Potenzial für die Gestaltung einer nachhaltigen Gesellschaft.'
|
||||
[offline]
|
||||
other = 'Offline Lesen'
|
||||
[archives]
|
||||
other = 'Archiv'
|
||||
[donate]
|
||||
other = 'Spenden'
|
||||
[trans]
|
||||
other = 'Übersetzt von'
|
||||
[pagesize]
|
||||
other = 'Seitengröße'
|
||||
[written_by]
|
||||
other = 'Von'
|
||||
[translated_by]
|
||||
other = 'Übersetzt von'
|
||||
[authors]
|
||||
other = ''
|
||||
[translators]
|
||||
other = ''
|
||||
[contributors]
|
||||
other = ''
|
||||
[translations]
|
||||
other= ''
|
||||
# post-footer
|
||||
[subscribeNEWS]
|
||||
other = 'Erhalten Sie unseren Newsletter'
|
||||
[supportLTM]
|
||||
other = 'Unterstützen Sie das Low-tech Magazine mit'
|
||||
[or]
|
||||
other = 'oder'
|
||||
[readoffline]
|
||||
other = 'Kaufen Sie die gedruckte Webseite'
|
|
@ -0,0 +1,53 @@
|
|||
[sitesubtitle]
|
||||
other = 'Este sitio web funciona con energía solar, lo que significa que en ocasiones estará fuera de línea.'
|
||||
[about]
|
||||
other = 'Acerca de'
|
||||
[lowtechtitle]
|
||||
other = 'Soluciones de Baja Tecnología'
|
||||
[lowtechdescription]
|
||||
other = 'Posibilidades interesantes surgen cuando se combina la tecnología antigua con nuevos conocimientos y nuevos materiales, o cuando se aplican conceptos tradicionales a la tecnología moderna.'
|
||||
[hightechtitle]
|
||||
other = 'Problemas de Alta Tecnología'
|
||||
[hightechdescription]
|
||||
other = 'La tecnología se ha convertido en el ídolo de nuestra sociedad, pero el progreso tecnológico está, en la mayoría de los casos, dirigido a resolver problemas causados por las mismas invenciones técnicas anteriores.'
|
||||
[obsoletetitle]
|
||||
other = 'Tecnologías Obsoletas'
|
||||
[obsoletedescription]
|
||||
other = 'Existe un gran potencial en el conocimiento y las tecnologías pasadas, las cuales son a menudo olvidadas a la hora de diseñar una sociedad sostenible'
|
||||
[offline]
|
||||
other = 'Lectura fuera de línea'
|
||||
[archives]
|
||||
other = 'Archivo'
|
||||
[donate]
|
||||
other = 'Donaciones'
|
||||
[trans]
|
||||
other = 'Traducido por'
|
||||
[newsletterlink]
|
||||
other = 'https://d69baa34.sibforms.com/serve/MUIEAEZUWQsqfc4iKwh6GDA0LS_6AE98wa1FP5PR4GpWzonWKBB5kuC2lPxvZDfq3TFEMX0TRy6KUp0QmzTaqBvvisJ5zpgu6FeI2lTw-8WjgPZBWxio3IKivik9Pd-EyiEzPwXuAkwkw0jhIXWwx2mYOuSPW06G1aOktFLZ2oV8YP58E2eMWj1AG-FK7PWiZXGE28K8WvV-ZPfT'
|
||||
[pagesize]
|
||||
other = 'Tamaño de página'
|
||||
[written_by]
|
||||
other = ''
|
||||
[translated_by]
|
||||
other = 'Traducido por'
|
||||
[authors]
|
||||
other = ''
|
||||
[translators]
|
||||
other = ''
|
||||
[contributors]
|
||||
other = ''
|
||||
[translations]
|
||||
other= ''
|
||||
[vieworig]
|
||||
other = ''
|
||||
[viewdither]
|
||||
other = ''
|
||||
#post-footer
|
||||
[subscribeNEWS]
|
||||
other = 'Suscríbete a nuestro boletín'
|
||||
[supportLTM]
|
||||
other = 'Apoye a Low-tech Magazine a través de'
|
||||
[or]
|
||||
other = 'o'
|
||||
[readoffline]
|
||||
other = 'Lea Low-tech Magazine sin conexión'
|
|
@ -0,0 +1,56 @@
|
|||
[sitesubtitle]
|
||||
other = 'Ce site fonctionne à l’énergie solaire, et se retrouve parfois hors-ligne'
|
||||
[about]
|
||||
other = 'À propos'
|
||||
[lowtechtitle]
|
||||
other = 'Solutions Low-tech'
|
||||
[lowtechdescription]
|
||||
other = 'Des possibilités intéressantes se présentent lors de la combinaison de l’ancienne technologie avec de nouvelles connaissances et de nouveaux matériaux, ou lors de l’application d’anciens concepts et de connaissances traditionnelles à la technologie moderne.'
|
||||
[hightechtitle]
|
||||
other = 'Problèmes High-tech'
|
||||
[hightechdescription]
|
||||
other = 'Le progrès technologique est devenu l’idole de notre société, mais il crée plus de problèmes qu’il en résout.'
|
||||
[obsoletetitle]
|
||||
other = 'Technologie Ancienne'
|
||||
[obsoletedescription]
|
||||
other = 'Il y a beaucoup de potentiel dans les connaissances et technologies anciennes et souvent oubliées lorsqu’il s’agit de concevoir une société durable.'
|
||||
[offline]
|
||||
other = 'Lire le magazine hors-ligne'
|
||||
[archives]
|
||||
other = 'Archive'
|
||||
[donate]
|
||||
other = 'Faire un don'
|
||||
[trans]
|
||||
other = 'Traduit par'
|
||||
[newsletterlink]
|
||||
other = 'https://d69baa34.sibforms.com/serve/MUIEANc2lrp0ZlxefJj9bGWkRWAP8XKI8G25tXyMryhx1Q6iKLoxg-A9u3QuJxksFS7rQuYNdNjVBqcJfwig9kXB6QzKRFg0KK2ZhiJjarVqjLKhFw2Ej58I5aLFMcgBWzD0MrDKgWiQgF_qMW1-rhMF_nsEY44QyiGRITSt0oJGZGZMjXkhgKH6t_x5-HgMgcnO1J4fSoQ_2iw-'
|
||||
[pagesize]
|
||||
other = 'Taille de la page'
|
||||
[written_by]
|
||||
other = 'Ecrit par'
|
||||
[translated_by]
|
||||
other = 'Traduit par'
|
||||
[authors]
|
||||
other = 'Auteurs'
|
||||
[translators]
|
||||
other = 'Traducteurs'
|
||||
[contributors]
|
||||
other = 'Contributeurs'
|
||||
[theme]
|
||||
other = 'Thème'
|
||||
[translations]
|
||||
other = 'Traductions'
|
||||
[vieworig]
|
||||
other = "Voir l'original"
|
||||
[viewdither]
|
||||
other = 'Voir la version compressée'
|
||||
#post-footer
|
||||
[subscribeNEWS]
|
||||
other = 'Abonnez-vous à notre newsletter'
|
||||
[supportLTM]
|
||||
other = 'Soutenez Low-tech Magazine sur'
|
||||
[or]
|
||||
other = 'ou'
|
||||
[readoffline]
|
||||
other = 'Lire le magazine sur papier'
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
[sitesubtitle]
|
||||
other = 'Questo e un sito a energia solare, il che vuol dire che a volte va offline.'
|
||||
[about]
|
||||
other = 'Riguardo'
|
||||
[lowtechtitle]
|
||||
other = 'Siluzioni low tech'
|
||||
[lowtechdescription]
|
||||
other = 'Si manifestano possibilità interessanti quando vecchie tecnologie si combinano insieme a nuove conoscenze e nuovi materiali, o quando vecchi concetti e il sapere tradizionale vengono applicati alla tecnologia moderna.'
|
||||
[hightechtitle]
|
||||
other = 'Problemi dell’hi-tech'
|
||||
[hightechdescription]
|
||||
other = 'L’hi-tech è diventato l’idolo della nostra società, ma il progresso tecnologico è mirato, il più delle volte, a risolvere problemi causati da invenzioni tecniche precedenti.'
|
||||
[obsoletetitle]
|
||||
other = 'Tecnologia obsoleta'
|
||||
[obsoletedescription]
|
||||
other = 'Quando si tratta di progettare una società sostenibile, c’è un grande potenziale nelle conoscenze e tecnologie passate e spesso dimenticate.'
|
||||
[offline]
|
||||
other = 'Lettura offline'
|
||||
[archives]
|
||||
other = 'Archivio'
|
||||
[donate]
|
||||
other = 'Donare'
|
||||
[written_by]
|
||||
other = ''
|
||||
[translated_by]
|
||||
other = ''
|
||||
[trans]
|
||||
other = 'Tradotto da'
|
||||
[authors]
|
||||
other = ''
|
||||
[translators]
|
||||
other = ''
|
||||
[contributors]
|
||||
other = ''
|
||||
[translations]
|
||||
other= ''
|
||||
[pagesize]
|
||||
other = 'Dimensione pagina'
|
||||
[vieworig]
|
||||
other = ''
|
||||
[viewdither]
|
||||
other = ''
|
||||
# post-footer
|
||||
[subscribeNEWS]
|
||||
other = ''
|
||||
[supportLTM]
|
||||
other = ''
|
||||
[or]
|
||||
other = ''
|
||||
[readoffline]
|
||||
other = ''
|
|
@ -0,0 +1,57 @@
|
|||
[sitesubtitle]
|
||||
other = 'Deze website draait op zonne-energie, wat betekent dat ze af en toe uit de lucht gaat'
|
||||
[about]
|
||||
other = 'Over ons'
|
||||
[lowtechtitle]
|
||||
other = 'Lowtech Oplossingen'
|
||||
[lowtechdescription]
|
||||
other = 'Er ontstaan interessante mogelijkheden als oude technologie wordt gecombineerd met nieuwe inzichten en materialen, of wanneer traditionele denkwijzen worden toegepast op nieuwe technologie.'
|
||||
[hightechtitle]
|
||||
other = 'Hightech Problemen'
|
||||
[hightechdescription]
|
||||
other = 'Technologische vooruitgang is de heilige koe van de moderne maatschappij, maar creëert steeds vaker problemen in plaats van oplossingen.'
|
||||
[obsoletetitle]
|
||||
other = 'Vergeten Technologie'
|
||||
[obsoletedescription]
|
||||
other = 'Oude en vaak vergeten kennis en technologie kan veel inspiratie bieden voor een duurzame samenleving.'
|
||||
[offline]
|
||||
other = 'Offline Lezen'
|
||||
[archives]
|
||||
other = 'Archief'
|
||||
[donate]
|
||||
other = 'Nieuwsbrief'
|
||||
[trans]
|
||||
other = 'Vertaald door'
|
||||
[newsletterlink]
|
||||
other = 'https://d69baa34.sibforms.com/serve/MUIEAHQzQiVLl_sv5NX9Sii_mfBoFQThNwPv_rtFv0ABEc9OFnwTy_OUeTB7iy-sFym7LiQipYTbtM3PUqohPJDtydCCgro4hClGkznEtvMbbhXU8NLAkLOowbKx_ToeTDsoEjF6m0FvWskEbgmT9t40R1SYOw8Hb5sk6dEpF_G91Lm-c1BbIIlmyl59CZCGMjkfPRF0IuzmQCas'
|
||||
[pagesize]
|
||||
other = 'Paginagrootte'
|
||||
[written_by]
|
||||
other = 'Door'
|
||||
[translated_by]
|
||||
other = 'Vertaald door'
|
||||
[authors]
|
||||
other = ''
|
||||
[translators]
|
||||
other = ''
|
||||
[contributors]
|
||||
other = ''
|
||||
[theme]
|
||||
other = ''
|
||||
[commentstitle]
|
||||
other = 'Reacties'
|
||||
[commentsdescription]
|
||||
other = 'Als je op dit artikel wil reageren, stuur dan een mailtje naar solar (at) lowtechmagazine (dot) com.'
|
||||
[vieworig]
|
||||
other = ''
|
||||
[viewdither]
|
||||
other = ''
|
||||
# post-footer
|
||||
[subscribeNEWS]
|
||||
other = 'Schrijf je in op onze nieuwsbrief'
|
||||
[supportLTM]
|
||||
other = 'Steun Low-tech Magazine via'
|
||||
[or]
|
||||
other = 'of'
|
||||
[readoffline]
|
||||
other = 'Lees Low-tech Magazine op papier'
|
|
@ -0,0 +1,51 @@
|
|||
[sitesubtitle]
|
||||
other = 'Ta strona zasilana jest energią słoneczną co oznacza, że czasami może być niedostępna.'
|
||||
[about]
|
||||
other = 'O nas'
|
||||
[lowtechtitle]
|
||||
other = 'Proste technologie, proste rozwiązania'
|
||||
[lowtechdescription]
|
||||
other = 'Wiele współczesnych problemów można rozwiązać łącząc dawne technologie ze współczesną wiedzą i nowoczesnymi materiałami, lub aplikując tradycyjne podejście i materiały do nowoczesnych technologii.'
|
||||
[hightechtitle]
|
||||
other = 'Problemy zaawansowanych technologii'
|
||||
[hightechdescription]
|
||||
other = 'Technologia stała się bogiem naszego społeczeństwa, jednak rozwój technologiczny niesie ze sobą nie tylko korzyści, ale również zagrożenia.'
|
||||
[obsoletetitle]
|
||||
other = 'Zapomniane technologie'
|
||||
[obsoletedescription]
|
||||
other = 'W zapomnianych technologiach minionych czasów tkwi wielki potencjał do stworzenia nowego, zrównoważonego społeczeństwa.'
|
||||
[offline]
|
||||
other = 'Czytanie offline'
|
||||
[archives]
|
||||
other = 'Archiwum'
|
||||
[donate]
|
||||
other = 'Wspomóż nas'
|
||||
[trans]
|
||||
other = 'Przekład'
|
||||
[written_by]
|
||||
other = ''
|
||||
[translated_by]
|
||||
other = 'Przekład '
|
||||
[authors]
|
||||
other = ''
|
||||
[translators]
|
||||
other = ''
|
||||
[contributors]
|
||||
other = ''
|
||||
[translations]
|
||||
other= ''
|
||||
[pagesize]
|
||||
other = 'Rozmiar strony'
|
||||
[vieworig]
|
||||
other = ''
|
||||
[viewdither]
|
||||
other = ''
|
||||
# post-footer
|
||||
[subscribeNEWS]
|
||||
other = 'Zapisz się do naszego newslettera'
|
||||
[supportLTM]
|
||||
other = 'Wesprzyj Low-tech Magazine przez'
|
||||
[or]
|
||||
other = 'lub'
|
||||
[readoffline]
|
||||
other = 'Czytaj Low-tech Magazine off-line'
|
|
@ -0,0 +1,50 @@
|
|||
[sitesubtitle]
|
||||
other = 'Este site é movido a energia solar, o que significa que, às vezes, ele fica fora do ar.'
|
||||
|
||||
[lowtechtitle]
|
||||
other = 'Soluções de baixa tecnologia'
|
||||
[lowtechdescription]
|
||||
other = 'Possibilidades interessantes surgem quando você combina tecnologias antigas com conhecimentos e materiais novos, ou quando você aplica antigos conceitos e conhecimentos tradicionais a tecnologias modernas.'
|
||||
[hightechtitle]
|
||||
other = 'Problemas de alta tecnologia'
|
||||
[hightechdescription]
|
||||
other = 'A tecnologia se tornou o ídolo de nossa sociedade, mas o progresso tecnológico é -- na maioria das vezes -- direcionado à resolução de problemas causados por invenções técnicas anteriores.'
|
||||
[obsoletetitle]
|
||||
other = 'Tecnologias obsoletas'
|
||||
[obsoletedescription]
|
||||
other = 'Há muito potencial em conhecimentos e tecnologias passadas -- e muitas vezes esquecidas -- quando se trata de projetar uma sociedade sustentável.'
|
||||
[offline]
|
||||
other = 'Lendo Offline'
|
||||
[donate]
|
||||
other = 'Faça uma doação'
|
||||
[trans]
|
||||
other = ''
|
||||
[newsletterlink]
|
||||
other = 'https://d69baa34.sibforms.com/serve/MUIEANc2lrp0ZlxefJj9bGWkRWAP8XKI8G25tXyMryhx1Q6iKLoxg-A9u3QuJxksFS7rQuYNdNjVBqcJfwig9kXB6QzKRFg0KK2ZhiJjarVqjLKhFw2Ej58I5aLFMcgBWzD0MrDKgWiQgF_qMW1-rhMF_nsEY44QyiGRITSt0oJGZGZMjXkhgKH6t_x5-HgMgcnO1J4fSoQ_2iw-'
|
||||
[pagesize]
|
||||
other = ''
|
||||
[written_by]
|
||||
other = ''
|
||||
[translated_by]
|
||||
other = ''
|
||||
[authors]
|
||||
other = ''
|
||||
[translators]
|
||||
other = ''
|
||||
[contributors]
|
||||
other = ''
|
||||
[translations]
|
||||
other= ''
|
||||
[vieworig]
|
||||
other = ''
|
||||
[viewdither]
|
||||
other = ''
|
||||
# post-footer
|
||||
[subscribeNEWS]
|
||||
other = 'Inscreva-se em nossa newsletter (english)'
|
||||
[supportLTM]
|
||||
other = 'Apoie a Low-tech Magazine via'
|
||||
[or]
|
||||
other = 'or'
|
||||
[readoffline]
|
||||
other = 'Compre a versão impressa do site (english)'
|
|
@ -0,0 +1,31 @@
|
|||
{{ define "main" }}
|
||||
|
||||
<main class='article {{ range .Params.categories }} {{ . | urlize }} {{ end }}'>
|
||||
|
||||
<article id="{{ .Title | urlize }}">
|
||||
<section id="content" class="article">
|
||||
<header class="entry-header">
|
||||
<h1 class="entry-title">
|
||||
{{ .Title }}</h1>
|
||||
|
||||
<p class="summary">
|
||||
<p class="summary">
|
||||
{{ .Summary }}
|
||||
</p>
|
||||
|
||||
</p>
|
||||
|
||||
</header>
|
||||
|
||||
<div class="entry-content">
|
||||
<p class="summary">
|
||||
{{ .Content }}
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
</article>
|
||||
</main>
|
||||
{{ end }}
|
|
@ -0,0 +1,10 @@
|
|||
{{- $img := .Page.Resources.GetMatch .Destination -}}
|
||||
{{- if and (not $img) .Page.File -}}
|
||||
{{ $path := path.Join .Page.File.Dir .Destination }}
|
||||
{{- $img = resources.Get $path -}}
|
||||
{{- end -}}
|
||||
{{- with $img -}}
|
||||
<img class="uncompressed" src="{{ $img.RelPermalink }}" alt="{{ $.Text }}" />
|
||||
{{- else -}}
|
||||
<img class="uncompressed" src="{{ .Destination | safeURL }}" alt="{{ $.Text }}" />{{- end -}}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<a href="{{ .Destination | safeURL }}"{{ with .Title}} title="{{ . }}"{{ end }}{{ if or (strings.HasPrefix .Destination "https") (strings.HasPrefix .Destination "http")}} target="_blank"{{ end }} >{{ .Text | safeHTML }}</a>
|
|
@ -0,0 +1,81 @@
|
|||
{{ define "main" }}
|
||||
<main id="archive-list">
|
||||
<header>
|
||||
<h1 class="entry-title">{{i18n "archives" | default .Title}}</h1>
|
||||
</header>
|
||||
<div id="filters">
|
||||
<div class="filter active desc" id="date">
|
||||
Date
|
||||
</div>
|
||||
|
||||
<div class="filter asc" id="title">
|
||||
Title
|
||||
</div>
|
||||
|
||||
<div class="filter asc" id="cat">
|
||||
Theme
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
{{ $posts := where .Site.RegularPages "Type" "in" "posts" }} {{ $notunlisted := where .Site.RegularPages ".Params.unlisted" "!=" "true" }} {{ $archive := $posts | intersect $notunlisted }} {{ range $archive.ByDate.Reverse }}
|
||||
<li class="{{ range .Params.categories }} {{ . | urlize }} {{ end }}">
|
||||
<article>
|
||||
<time class="published" datetime="{{.Date }}">{{.Date | time.Format ":date_long"}}</time>
|
||||
<div class="article-title"><a href="{{.Permalink}}" rel="bookmark" title="{{.Title}}">{{.Title}}</a></div>
|
||||
<div class="category">
|
||||
{{ range .Params.categories }} {{ . }} {{ end }}
|
||||
</div>
|
||||
</article>
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
<!-- /#archive-list -->
|
||||
</main>
|
||||
|
||||
|
||||
<!-- LIST SORTING-------------->
|
||||
<script src="{{ .Site.BaseURL }}js/tinysort.min.js"></script>
|
||||
<script>
|
||||
var listElements = document.querySelectorAll('#archive-list ul li');
|
||||
var filters = document.getElementsByClassName('filter');
|
||||
for (var i = 0; i < filters.length; i++) {
|
||||
filters[i].addEventListener('click', sort, false);
|
||||
}
|
||||
|
||||
function sort() {
|
||||
for (var j = 0; j < filters.length; j++) {
|
||||
filters[j].classList.remove('active');
|
||||
}
|
||||
this.classList.add('active');
|
||||
this.classList.toggle('desc');
|
||||
this.classList.toggle('asc');
|
||||
var type = (this).id;
|
||||
switch (type) {
|
||||
case "title":
|
||||
tinysort(listElements, {
|
||||
selector: 'div.article-title a',
|
||||
attr: "title",
|
||||
order: (this.isAsc = !this.isAsc) ? 'asc' : 'desc'
|
||||
});
|
||||
break;
|
||||
case "date":
|
||||
tinysort(listElements, {
|
||||
selector: 'time.published',
|
||||
attr: 'datetime',
|
||||
order: (this.isAsc = !this.isAsc) ? 'asc' : 'desc'
|
||||
});
|
||||
break;
|
||||
case "cat":
|
||||
tinysort(listElements, {
|
||||
selector: '.category',
|
||||
order: (this.isAsc = !this.isAsc) ? 'asc' : 'desc'
|
||||
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
{{end}}
|
|
@ -0,0 +1,29 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="{{ .Site.LanguageCode | default "en-us" }}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>{{ if not .IsHome }}{{ .Title }} | {{ end }}{{ .Site.Title }}</title>
|
||||
{{ with .Site.Params.description }}<meta name="description" content="{{ . }}">{{ end }}
|
||||
{{ with .Site.Params.author }}<meta name="author" content="{{ . }}">{{ end }}
|
||||
|
||||
<link rel="icon" href="{{ .Site.BaseURL }}icons/sun.svg">
|
||||
|
||||
{{ $style := resources.Get "/css/style.scss" | resources.ToCSS }}
|
||||
<link rel="stylesheet" href="{{ $style.Permalink }}">
|
||||
|
||||
{{- partial "feeds" . -}}
|
||||
{{- partial "opengraph" . -}}
|
||||
|
||||
|
||||
</head>
|
||||
<body id="{{ .Title | urlize }}">
|
||||
{{ partial "battery" . }}
|
||||
{{ partial "header" . }}
|
||||
{{ block "main" . }}{{ end }}
|
||||
{{ partial "footer" . }}
|
||||
|
||||
<script src="{{ .Site.BaseURL }}js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,57 @@
|
|||
{{ define "main" }}
|
||||
<main>
|
||||
<section class="article" id="authors">
|
||||
<header class="entry-header">
|
||||
<h1 class="entry-title">{{ i18n "contributors" | default "Contributors"}}</h1>
|
||||
<p class="summary">
|
||||
{{ .Summary }}
|
||||
</p>
|
||||
|
||||
{{ if .IsTranslated }}
|
||||
<div class="metadata">
|
||||
<div class="translations">
|
||||
{{i18n "translations" | default "Translations" }} {{ range .Translations }}
|
||||
<a href="{{ .Permalink }}">{{ .Lang }}</a> {{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</header>
|
||||
<div class="entry-content">
|
||||
<p>
|
||||
{{ .Content }}
|
||||
</p>
|
||||
|
||||
{{/* {{printf "%#v" .Site.Taxonomies }} */}}
|
||||
|
||||
<h2>{{i18n "authors" | default "Authors" }}</h2>
|
||||
|
||||
{{ range $key, $author := .Site.Taxonomies.authors.ByCount }}
|
||||
|
||||
<details>
|
||||
<summary>{{ .Page.Title }} ({{ .Count }})</summary>
|
||||
<ul class="page-list">
|
||||
{{ range $author.Pages }}
|
||||
{{ if not (in .Params.categories "About") }}
|
||||
<li hugo-nav="{{ .RelPermalink}}"><a href="{{ .Permalink}}">{{ .LinkTitle }}</a></li>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</ul>
|
||||
</details>
|
||||
{{ end }}
|
||||
<h2>{{i18n "translators" | default "Translators" }} <span>({{ .Language.LanguageName }})</span></h2>
|
||||
{{ if .Site.Taxonomies.translators }} {{ range $trans, $translator := .Site.Taxonomies.translators.ByCount }}
|
||||
<details>
|
||||
<summary>{{ .Page.Title }} ({{ .Count }})</summary>
|
||||
<ul class="page-list">
|
||||
{{ range $translator.Pages }}
|
||||
<li hugo-nav="{{ .RelPermalink}}"><a href="{{ .Permalink}}">{{ .LinkTitle }}</a></li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</details>
|
||||
{{ end }} {{ else }}
|
||||
<p>{{i18n "notrans" | default "There are no translators for this language. The author(s) handled the translations directly." }}</p>
|
||||
{{ end }}
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
{{ end }}
|
|
@ -0,0 +1,25 @@
|
|||
{{ define "main" }} {{ $categoryTitle := .Title }} {{ $categoryDescription := ' ' }}
|
||||
{{ if (eq .Data.Term "Low-tech Solutions") }} {{ $categoryTitle = i18n "lowtechtitle" }}
|
||||
{{ $categoryDescription = i18n "lowtechdescription" | default "Interesting possibilities arise when you combine old technology with new knowledge and new materials, or when you apply old concepts and traditional knowledge to modern technology." }}
|
||||
{{ else if (eq .Data.Term "High-tech Problems")}} {{ $categoryTitle = i18n "hightechtitle" }} {{ $categoryDescription = i18n "hightechdescription" | default "Technology has become the idol of our society, but technological progress is—more often than not—aimed at solving problems caused by earlier technical inventions." }}
|
||||
{{ else if (eq .Data.Term "Obsolete Technology")}} {{ $categoryTitle = i18n "obsoletetitle" }} {{ $categoryDescription = i18n "obsoletedescription" | default "There is a lot of potential in past and often forgotten knowledge and technologies when it comes to designing a sustainable society." }}
|
||||
{{ else if (eq .Data.Term "About")}} {{ $categoryTitle = i18n "about" }} {{ $categoryDescription = i18n "aboutdescription" | default "" }}{{ end }}
|
||||
<main class="article-list">
|
||||
<header>
|
||||
{{ if (eq .Data.Singular "author")}} {{ i18n "written_by" | default "Written by"}} {{ else if (eq .Data.Singular "translator")}} {{ i18n "translated_by" | default "Translated by"}} {{ else if (eq .Data.Singular "tag")}} {{ i18n "theme" | default "Theme"}}{{ end }}
|
||||
<h1 class="entry-title">{{ $categoryTitle | default .Title }}</h1>
|
||||
{{ if not (eq $categoryDescription ' ') }}
|
||||
<p class="summary">{{$categoryDescription}}
|
||||
</p>
|
||||
{{ end }}
|
||||
</header>
|
||||
<section id="list" class="grid">
|
||||
{{ $allposts := .Pages }}
|
||||
{{ $notunlisted := where .RegularPages ".Params.unlisted" "!=" "true" }}
|
||||
{{ $posts := $allposts | intersect $notunlisted }}
|
||||
{{ $paginator := .Paginate $posts }}
|
||||
{{ range $paginator.Pages }} {{ partial "article-list/default" . }} {{ end }}
|
||||
</section>
|
||||
{{ if gt $paginator.TotalPages 1}} {{ partial "pagination" . }} {{ end }}
|
||||
</main>
|
||||
{{ end }}
|
|
@ -0,0 +1,48 @@
|
|||
{{- $pctx := . -}}
|
||||
{{- if .IsHome -}}{{ $pctx = .Site }}{{- end -}}
|
||||
{{- $pages := slice -}}
|
||||
{{- if or $.IsHome $.IsSection -}}
|
||||
{{- $pages = $pctx.RegularPages -}}
|
||||
{{- else -}}
|
||||
{{- $pages = $pctx.Pages -}}
|
||||
{{- end -}}
|
||||
{{- $limit := .Site.Config.Services.RSS.Limit -}}
|
||||
{{- if ge $limit 1 -}}
|
||||
{{- $pages = $pages | first $limit -}}
|
||||
{{- end -}}
|
||||
{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>{{ .Site.Title }} {{.Site.Language.LanguageName}}</title>
|
||||
<link>{{ .Permalink }}</link>
|
||||
<description>{{i18n "sitesubtitle" | default .Site.Params.description}}</description>
|
||||
<generator>Hugo {{hugo.Version}}</generator>{{ with .Site.Language.Lang }}
|
||||
<language>{{.}}</language>{{end}}{{ with .Site.Author.email }}
|
||||
<managingEditor>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</managingEditor>{{end}}{{ with .Site.Author.email }}
|
||||
<webMaster>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</webMaster>{{end}}{{ with .Site.Copyright }}
|
||||
<copyright>{{.}}</copyright>{{end}}{{ if not .Date.IsZero }}
|
||||
<lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate>{{ end }}
|
||||
{{- with .OutputFormats.Get "RSS" -}}
|
||||
{{ printf "<atom:link href=%q rel=\"self\" type=%q />" .Permalink .MediaType | safeHTML }}
|
||||
{{- end -}}
|
||||
{{ range $pages }}
|
||||
<item>
|
||||
<title>{{ .Title }}</title>
|
||||
<link>{{ .Permalink }}</link>
|
||||
{{- if .Params.featured_image -}}
|
||||
{{- $img := strings.TrimSuffix (path.Ext .Params.featured_image) .Params.featured_image }}
|
||||
{{ $dithered := (print "images/dithers/" $img "_dithered.png") -}}
|
||||
{{ $dithered_image := (.Page.Resources.ByType "image").GetMatch $dithered }}
|
||||
<enclosure url="{{with $dithered_image }}{{ .Permalink }}{{end}}" type="image/png" length="{{ (os.Stat (path.Join (path.Dir .Page.File.Path) $dithered_image)).Size }}" ></enclosure>
|
||||
{{ else }}
|
||||
<enclosure url="{{ .Site.BaseURL }}icons/sun.svg }}" type="image/png" length=""></enclosure>
|
||||
{{- end -}}
|
||||
<pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
|
||||
{{ with .Site.Author.email }}<author>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</author>{{end}}
|
||||
<guid>{{ .Permalink }}</guid>
|
||||
|
||||
<description>{{ .Content | html }}</description>
|
||||
</item>
|
||||
{{ end }}
|
||||
</channel>
|
||||
</rss>
|
|
@ -0,0 +1,109 @@
|
|||
{{ define "main" }}
|
||||
|
||||
<main class='article {{ range .Params.categories }} {{ . | urlize }} {{ end }}'>
|
||||
|
||||
<article>
|
||||
<section id="content" class="article">
|
||||
<header class="entry-header">
|
||||
<h1 class="entry-title">
|
||||
{{ .Title }}</h1>
|
||||
|
||||
<p class="summary">
|
||||
{{ .Summary }}
|
||||
</p>
|
||||
<div class="metadata">
|
||||
{{/* disable metadata author for about category */}}
|
||||
{{ if not (in .Params.categories "About") }}
|
||||
{{if (.GetTerms "authors")}}
|
||||
<div class="authors">
|
||||
<span class="byline">{{i18n "written_by" | default "Written by" }}</span>
|
||||
{{ range (.GetTerms "authors") }}
|
||||
<span class="author"><a href="{{ .Permalink }}">{{ .Title }}</a></span>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ if (.GetTerms "translators") }}
|
||||
|
||||
<div class="translations">
|
||||
<span class="byline">{{i18n "translated_by" | default "translated by"}}</span>
|
||||
{{ range (.GetTerms "translators") }}
|
||||
<span class="author"><a href="{{ .Permalink }}">{{ .Title }}</a></span>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ if .IsTranslated }}
|
||||
<div class="translations">
|
||||
<span class="byline">{{i18n "translations" | default "Translations"}}</span>
|
||||
{{ range .Translations }}
|
||||
<a href="{{ .Permalink }}">{{ .Lang }}</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="entry-content">
|
||||
{{- $contents := split .Content `<div class="footnotes" role="doc-endnotes">` -}}
|
||||
{{ index $contents 0 | safeHTML }}
|
||||
</div>
|
||||
{{ if not (in .Params.categories "About") }}
|
||||
{{ if not (in .Params.categories "") }}
|
||||
{{ partial "post-footer" }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</section>
|
||||
|
||||
{{ $commentsfile := printf "comments.%s.md" .Lang }}
|
||||
{{ with .Resources.GetMatch $commentsfile }}
|
||||
<section class="comments" id="comments">
|
||||
<h2 id="comments-title">{{ i18n "commentstitle" | default "Comments"}} </h2>
|
||||
<p><em>{{ i18n "commentsdescription" | default "To make a comment, please send an e-mail to solar (at) lowtechmagazine (dot) com. Your e-mail address is not used for other purposes, and will be deleted after the comment is published. If you don’t want your real name to be published, sign the e-mail with the name you want to appear."}}</em></p>
|
||||
<details>
|
||||
<summary><span id="comment-count"></span> {{ i18n "commentstitle" | default "Reactions"}}</summary>
|
||||
<div id="comments-list">
|
||||
{{ .Content }}
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
{{ end }}
|
||||
|
||||
<section id="reference">
|
||||
<div class="footnotes" role="doc-endnotes">
|
||||
{{ index $contents 1 | safeHTML }}
|
||||
</section>
|
||||
|
||||
|
||||
{{$tags := (.GetTerms "tags")}}
|
||||
{{ if $tags }}
|
||||
|
||||
<section id="related" class="article-list">
|
||||
<h3 class="related">Related Articles</h3>
|
||||
<div class="post-info gray">
|
||||
<p class="tags">{{ i18n "theme" | default "Themes"}}:
|
||||
{{ range $tags }}
|
||||
<a href="{{ .Permalink }}" class="tag">{{ .LinkTitle }}</a>
|
||||
{{ end }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid">
|
||||
{{ $allposts := where .Site.RegularPages "Type" "in" "posts" }}
|
||||
{{ $notunlisted := where site.RegularPages ".Params.unlisted" "!=" "true" }}
|
||||
{{ $posts := $allposts | intersect $notunlisted }}
|
||||
{{ $related := $posts.RelatedIndices . "tags" "lang" | first 4}}
|
||||
{{ range $related }}
|
||||
{{ if isset .Params ("categories") }}
|
||||
|
||||
{{ partial "article-list/default" . }}
|
||||
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
|
||||
</article>
|
||||
</main>
|
||||
{{ end }}
|
|
@ -0,0 +1,25 @@
|
|||
{{ define "main" }}
|
||||
<main class="author-list">
|
||||
<header>
|
||||
<h1 class="entry-title">{{.Title }}</h1>
|
||||
</header>
|
||||
|
||||
<section id="list">
|
||||
<div class="entry-content">
|
||||
{{ range .Data.Terms.ByCount }}
|
||||
<ul class="page-list">
|
||||
<li class="author-item">
|
||||
<a href="{{ .Page.Permalink }}">
|
||||
{{ .Page.Title }}
|
||||
</a> ({{ .Count }})
|
||||
</li>
|
||||
</ul>
|
||||
{{ end }}
|
||||
{{ if or (eq .Data.Plural "authors") (eq .Data.Plural "translators")}}
|
||||
<h4>See all <a href="{{ absLangURL "contributors" }}">{{i18n "contributors" | default "Contributors" }}</a> ({{ .Language.LanguageName}})</h4>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</main>
|
||||
{{ end }}
|
|
@ -0,0 +1,26 @@
|
|||
{{ define "main" }}
|
||||
<main class="home article-list" >
|
||||
<!-- Recent Articles -->
|
||||
{{ $allposts := where .Site.RegularPages "Type" "in" "posts" }}
|
||||
|
||||
{{ $notunlisted := where .Site.RegularPages ".Params.unlisted" "!=" "true" }}
|
||||
{{ $posts := $allposts | intersect $notunlisted }}
|
||||
{{ $pages := after 1 $posts.ByDate.Reverse }}
|
||||
{{ $recent := first 1 $posts.ByDate.Reverse }}
|
||||
|
||||
<section id="home-listing" class="grid">
|
||||
{{ $paginator := .Paginate $pages }}
|
||||
{{ if not $paginator.HasPrev }}
|
||||
{{ range $recent }}
|
||||
{{ partial "article-list/featured" . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ range $paginator.Pages }}
|
||||
{{ partial "article-list/default" . }}
|
||||
{{ end }}
|
||||
</section>
|
||||
{{ if gt $paginator.TotalPages 1}}
|
||||
{{ partial "pagination" . }}
|
||||
{{ end }}
|
||||
</main>
|
||||
{{ end }}
|
|
@ -0,0 +1,14 @@
|
|||
<div class="article {{ range .Params.categories }} {{ . | urlize }} {{ end }}">
|
||||
<a href="{{.Permalink}}">
|
||||
{{$url := .Permalink}}
|
||||
{{$filename := strings.Replace .Params.featured_image (path.Ext .Params.featured_image) "_dithered.png"}}
|
||||
{{ $imgurl := path.Join "images" "dithers" $filename }}
|
||||
{{ with .Resources.GetMatch $imgurl }}
|
||||
<div class="featured-img" style="background-image: url('{{ .Permalink }}');"></div>
|
||||
{{ end }}
|
||||
|
||||
<h3 class="entry-title">{{.Title}}</h3>
|
||||
<p class="index-summary">{{.Summary}}</p>
|
||||
<time class="published">{{.Date | time.Format ":date_long"}}</time>
|
||||
</a>
|
||||
</div>
|
|
@ -0,0 +1,19 @@
|
|||
<div class="cover {{ range .Params.categories }} {{ . | urlize }} {{ end }}">
|
||||
<a href="{{.Permalink}}">
|
||||
<div class="text">
|
||||
<h2 class="entry-title">{{ .Title }}</h2>
|
||||
<p class="index-summary">
|
||||
{{ .Summary}}
|
||||
</p>
|
||||
<time class="published">{{.Date | time.Format ":date_long"}}</time>
|
||||
</div>
|
||||
<div class="image">
|
||||
{{$url := .Permalink}}
|
||||
{{$filename := strings.Replace .Params.featured_image (path.Ext .Params.featured_image) "_dithered.png"}}
|
||||
{{ $imgurl := path.Join "images" "dithers" $filename }}
|
||||
{{ with .Resources.GetMatch $imgurl }}
|
||||
<div class="featured-img" style="background-image: url('{{ .Permalink }}');"></div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
|
@ -0,0 +1,13 @@
|
|||
<div class="battery_bg">
|
||||
<div id="battery_data" class="bat_status" data-charging="no">
|
||||
<a href="{{ absLangURL "/power/" }}" title="Power">
|
||||
<span id="charge_icon">
|
||||
<svg class="icon battery-svg" viewBox="0 0 500 500"><title>Battery used</title><polygon class="svg_stroke" points="327.01 127.16 327.01 47.31 172.99 47.31 172.99 127.16 115.52 127.16 115.52 452.69 384.48 452.69 384.48 127.16 327.01 127.16"/><polygon class="svg_fill" points="357.64 408.86 158.26 408.86 158.26 209.49 357.64 408.86"/></svg>
|
||||
|
||||
<svg class="icon sun-svg" viewBox="0 0 500 500"><title>Battery charging</title><circle class="svg_fill" cx="248.48" cy="252.55" r="97.03"/><rect class="svg_fill" x="234.53" y="17.45" width="27.9" height="112.39"/><rect class="svg_fill" x="234.53" y="375.25" width="27.9" height="112.39"/><rect class="svg_fill" x="413.42" y="196.35" width="27.9" height="112.39" transform="translate(679.92 -174.83) rotate(90)"/><rect class="svg_fill" x="55.63" y="196.35" width="27.9" height="112.39" transform="translate(322.12 182.97) rotate(90)"/><rect class="svg_fill" x="361.03" y="69.85" width="27.9" height="112.39" transform="translate(198.96 -228.23) rotate(45)"/><rect class="svg_fill" x="108.03" y="322.85" width="27.9" height="112.39" transform="translate(303.75 24.77) rotate(45)"/><rect class="svg_fill" x="361.03" y="322.85" width="27.9" height="112.39" transform="translate(908.15 381.93) rotate(135)"/><rect class="svg_fill" x="108.03" y="69.85" width="27.9" height="112.39" transform="translate(297.35 128.93) rotate(135)"/></svg>
|
||||
</span>
|
||||
<span id="level"></span></a>
|
||||
</div>
|
||||
<div id="battery">
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,3 @@
|
|||
{{ range .AlternativeOutputFormats -}}
|
||||
{{ printf `<link rel="%s" type="%s" href="%s" title="%s" />` .Rel .MediaType.Type .Permalink $.Site.Title | safeHTML }}
|
||||
{{ end -}}
|
|
@ -0,0 +1,42 @@
|
|||
<footer class="site-footer">
|
||||
<div id=page-size>{{ i18n "pagesize" | default "Page Size"}}: <span id="size-value"> $PLACEHOLDER</span></div>
|
||||
<div id=back-to-top><a id="" href="#top" title="Back to top"> ↑ </a></div>
|
||||
<h2>
|
||||
<a href="{{ .Site.BaseURL }}">{{ .Site.Title }}</a>
|
||||
</h2>
|
||||
<div class="dashboard">
|
||||
<ul class="grid">
|
||||
<li>
|
||||
<h3>Server Stats</h3>
|
||||
<dl id="stats">
|
||||
</dl>
|
||||
</li>
|
||||
<li>
|
||||
<h3>Forecast</h3>
|
||||
<div class="forecast"></div>
|
||||
</li>
|
||||
<li>
|
||||
<h3>Info</h3>
|
||||
{{ partial "site-footer" }}
|
||||
</li>
|
||||
<li>
|
||||
<h3>Contact</h3>
|
||||
<p>
|
||||
© Kris De Decker<br> solar [at] lowtechmagazine [dot] com</p>
|
||||
|
||||
<div class="social">
|
||||
<!-- RSS FEED -->
|
||||
<a href="/feeds" type="application/atom+xml" rel="alternate" target="_blank"><svg viewBox="0 0 80 80" class="icon"><title>RSS Feed</title><circle cx="19.91" cy="58.23" r="6.86"/><path d="M67.89,65.72H55.7A41.86,41.86,0,0,0,13.89,23.91V11.73A54.06,54.06,0,0,1,67.89,65.72Z"/><path d="M48.93,65.72H36.75A22.88,22.88,0,0,0,13.89,42.87V30.68A35.08,35.08,0,0,1,48.93,65.72Z"/></svg></a>
|
||||
|
||||
<a href="https://twitter.com/lowtechmagazine" target="_blank"><svg viewBox="0 0 80 80" class="icon"><title>Twitter</title><path d="M65.05,29.23c0,.56,0,1.12,0,1.68,0,17.1-13,36.8-36.8,36.8A36.54,36.54,0,0,1,8.44,61.91a26.68,26.68,0,0,0,3.12.16,25.9,25.9,0,0,0,16.06-5.53,13,13,0,0,1-12.09-9,16.28,16.28,0,0,0,2.44.2,13.67,13.67,0,0,0,3.4-.44A12.93,12.93,0,0,1,11,34.64v-.16a13,13,0,0,0,5.85,1.64,13,13,0,0,1-4-17.3A36.76,36.76,0,0,0,39.51,32.36a14.58,14.58,0,0,1-.32-3,12.95,12.95,0,0,1,22.38-8.85,25.45,25.45,0,0,0,8.21-3.12,12.9,12.9,0,0,1-5.69,7.13,25.93,25.93,0,0,0,7.45-2A27.79,27.79,0,0,1,65.05,29.23Z"/></svg></a>
|
||||
|
||||
<a href="https://www.patreon.com/lowtechmagazine" target="_blank"><svg viewBox="0 -4.5 256 256" transform="scale(0.85)" preserveAspectRatio="xMidYMid" class="icon"><title>Patreon</title><path d="M45.1355837,0 L45.1355837,246.35001 L0,246.35001 L0,0 L45.1355837,0 Z M163.657111,0 C214.65668,0 256,41.3433196 256,92.3428889 C256,143.342458 214.65668,184.685778 163.657111,184.685778 C112.657542,184.685778 71.3142222,143.342458 71.3142222,92.3428889 C71.3142222,41.3433196 112.657542,0 163.657111,0 Z"> </path></svg></a>
|
||||
|
||||
<a href='{{ i18n "newsletterlink" | default .Site.Params.Newsletter }}' target="_blank"><svg viewBox="0 0 80 80" class="icon"><title>Newsletter</title><path d="M4.95,13.14v55H74.33v-55Zm54.34,8.12L39.64,39.74,20,21.26ZM13.07,60V25.91l26.56,25,26.57-25V60Z"/></svg></a>
|
||||
</div>
|
||||
<!-- /.social -->
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</footer>
|
|
@ -0,0 +1,9 @@
|
|||
<header class="site-header">
|
||||
<h1><a id="top" href="{{ absLangURL "/" }}">{{ site.Title }}</a></h1>
|
||||
<div class="subtitle">
|
||||
<a href="{{ absLangURL "power" }}">{{i18n "sitesubtitle" | default .Site.Params.description}}
|
||||
</a>
|
||||
</div>
|
||||
{{ partial "nav" . }}
|
||||
|
||||
</header>
|
|
@ -0,0 +1,12 @@
|
|||
<div id="lang-menu" title="languages">
|
||||
<svg id="globe" class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.97 19.97"><circle class="stroke" cx="9.99" cy="9.99" r="9.24"/><ellipse class="stroke" cx="9.99" cy="9.99" rx="4.63" ry="9.24"/><line class="stroke" x1="9.99" y1="0.75" x2="9.99" y2="19.22"/><line class="stroke" x1="18.39" y1="12.99" x2="1.58" y2="12.99"/><line class="stroke" x1="18.39" y1="6.99" x2="1.58" y2="6.99"/></svg>
|
||||
</div>
|
||||
<nav id="languages">
|
||||
<ul>
|
||||
{{ range $.Site.Home.AllTranslations }}
|
||||
<li>
|
||||
<a class="lang-option" href="{{ .Permalink }}">{{ .Language.LanguageName}}</a>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</nav>
|
|
@ -0,0 +1,20 @@
|
|||
<nav id="menu">
|
||||
<div id="menu-s">
|
||||
{{ partial "lang-nav" . }}
|
||||
<span id="m-btn">menu</span>
|
||||
</div>
|
||||
<ul id="menu-list">
|
||||
|
||||
{{ $currentPage := . }} {{ range .Site.Menus.main }}
|
||||
<li class="{{ if $currentPage.HasMenuCurrent " main " . }}active {{ end }}">
|
||||
<a href="{{ if i18n .Identifier }} {{ absLangURL .URL }} {{else}} {{.URL}} {{ end }}" data-nav="{{.Name | urlize}}" {{if strings.HasPrefix .URL "https" }} target="_blank" {{end}}>
|
||||
<span>{{ i18n .Identifier | default .Name }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{{ end }}
|
||||
<li class="rss">
|
||||
<a href='{{ i18n "newsletterlink" | default .Site.Params.Newsletter }}' target="_blank"><svg viewBox="0 0 80 80" class="icon"><title>Newsletter</title><path d="M4.95,13.14v55H74.33v-55Zm54.34,8.12L39.64,39.74,20,21.26ZM13.07,60V25.91l26.56,25,26.57-25V60Z"/></svg></a>
|
||||
<a href="/feeds" type="application/atom+xml" rel="alternate" target="_blank"><svg viewBox="0 0 80 80" class="icon"><title>RSS Feed</title><circle cx="19.91" cy="58.23" r="6.86"/><path d="M67.89,65.72H55.7A41.86,41.86,0,0,0,13.89,23.91V11.73A54.06,54.06,0,0,1,67.89,65.72Z"/><path d="M48.93,65.72H36.75A22.88,22.88,0,0,0,13.89,42.87V30.68A35.08,35.08,0,0,1,48.93,65.72Z"/></svg></a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
|
@ -0,0 +1,64 @@
|
|||
<meta property="og:title" content="{{ if not .IsHome }}{{ .Title }}{{ else }}{{ .Site.Title }}{{end }}" />
|
||||
<meta name="twitter:title" content="{{ if not .IsHome }}{{ .Title }}{{ else }}{{ .Site.Title }}{{end }}"/>
|
||||
|
||||
<meta property="og:description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end }}" />
|
||||
|
||||
<meta name="twitter:description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end -}}"/>
|
||||
|
||||
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}" />
|
||||
<meta property="og:url" content="{{ .Permalink }}" />
|
||||
|
||||
{{- if .Params.featured_image -}}
|
||||
|
||||
{{ $img := strings.TrimSuffix (path.Ext .Params.featured_image) .Params.featured_image }}
|
||||
|
||||
{{ $dithered := (print "images/dithers/" $img "_dithered.png") }}
|
||||
|
||||
<meta property="og:image" itemprop="image" content="{{ (path.Join .RelPermalink $dithered ) | absURL }}" />
|
||||
<meta property="og:image:secure_url" content="{{ (path.Join .RelPermalink $dithered ) | absURL }}" />
|
||||
<meta property="og:image:type" content="image/png">
|
||||
<meta name="twitter:card" content="summary_large_image"/>
|
||||
<meta name="twitter:image" content="{{ (path.Join .RelPermalink $dithered ) | absURL }}"/>
|
||||
|
||||
{{ else }}
|
||||
|
||||
<meta property="og:image" content="{{ .Site.BaseURL }}icons/sun.svg" />
|
||||
<meta property="og:image:secure_url" content="{{ .Site.BaseURL }}icons/sun.svg" />
|
||||
<meta property="og:image:type" content="image/svg+xml">
|
||||
<meta property="og:image:width" content="300" />
|
||||
<meta property="og:image:height" content="300" />
|
||||
<meta name="twitter:card" content="summary"/>
|
||||
{{- end}}
|
||||
|
||||
{{- if .IsPage }}
|
||||
{{- $iso8601 := "2006-01-02T15:04:05-07:00" -}}
|
||||
<meta property="article:section" content="{{ .Section }}" />
|
||||
{{ with .PublishDate }}<meta property="article:published_time" {{ .Format $iso8601 | printf "content=%q" | safeHTMLAttr }} />{{ end }}
|
||||
{{ with .Lastmod }}<meta property="article:modified_time" {{ .Format $iso8601 | printf "content=%q" | safeHTMLAttr }} />{{ end }}
|
||||
{{- end -}}
|
||||
|
||||
{{- with .Site.Title }}<meta property="og:site_name" content="{{ . }}" />{{ end }}
|
||||
|
||||
{{- /* If it is part of a series, link to related articles */}}
|
||||
{{- $permalink := .Permalink }}
|
||||
{{- $siteSeries := site.Taxonomies.series }}
|
||||
{{ with .Params.series }}{{- range $name := . }}
|
||||
{{- $series := index $siteSeries ($name | urlize) }}
|
||||
{{- range $page := first 6 $series.Pages }}
|
||||
{{- if ne $page.Permalink $permalink }}<meta property="og:see_also" content="{{ $page.Permalink }}" />{{ end }}
|
||||
{{- end }}
|
||||
{{ end }}{{ end }}
|
||||
|
||||
{{- if .IsPage }}
|
||||
{{ if .Params.authors }}
|
||||
{{- range .Params.authors}}
|
||||
<meta property="article:author" content="{{ . }}" />
|
||||
<meta name="citation_author" content="{{ . }}">
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
<meta property="article:author" content="{{ .Site.Params.Author }}" />
|
||||
<meta name="citation_author" content="{{ .Site.Params.Author }}">
|
||||
{{ end }}
|
||||
{{- with .Params.tags }}{{ range first 6 . }}
|
||||
<meta property="article:tag" content="{{ . }}" />{{ end }}
|
||||
{{- end }}{{ end }}
|
|
@ -0,0 +1,9 @@
|
|||
<nav class="pagination">
|
||||
{{ if .Paginator.HasPrev }}
|
||||
<a href="{{ .Paginator.Prev.URL }}">«</a>
|
||||
{{ end }}
|
||||
{{ .Paginator.PageNumber }} / {{ .Paginator.TotalPages }}
|
||||
{{ if .Paginator.HasNext }}
|
||||
<a href="{{ .Paginator.Next.URL }}">»</a>
|
||||
{{ end }}
|
||||
</nav>
|
|
@ -0,0 +1,9 @@
|
|||
{{- $newsletterlink := i18n "newsletterlink" | default "https://d69baa34.sibforms.com/serve/MUIEAJWIw9w82Dl4ua6FQArPaI-3Qb-zVTwPNabHQgFH51MiGF69Smy9LOC_HPoUmBj0emaXsXT87gcQXDPvtu-AZsJCHWhkkv21CdrcQu4GdnYAhZ-MrIPhwGDecagLzYxqfvkaqXg2ODcbJU4ByoDmzJK3ZTczDo2jcWtfn-En0MGKLVkgxx9TgdHqYoPabMJCMF-agLEclEwv"
|
||||
-}}
|
||||
<div class="post-footer">
|
||||
<ul>
|
||||
<li><a href="{{$newsletterlink}}">{{i18n "subscribeNEWS" | default "Subscribe to our newsletter"}}</a>.</li>
|
||||
<li> {{i18n "supportLTM" | default "Support Low-tech Magazine via"}} <a href="https://www.paypal.me/lowtechmagazine">Paypal</a> {{i18n "or" | default "or"}} <a href="https://www.patreon.com/lowtechmagazine">Patreon</a>.</li>
|
||||
<li><a href="{{ absLangURL "offline-reading/" }}">{{i18n "readoffline" | default "Read Low-tech Magazine offline"}}</a>.</li>
|
||||
</ul>
|
||||
</div>
|
|
@ -0,0 +1,17 @@
|
|||
<div class="footer-links">
|
||||
<a href="{{ absLangURL "about" }}">
|
||||
{{ i18n "about"| default "About"}}
|
||||
</a>
|
||||
<a href="{{ absLangURL "power" }}">
|
||||
{{ i18n "power"| default "Power"}}
|
||||
</a>
|
||||
<a href="{{ absLangURL "about/team" }}">
|
||||
{{ i18n "team"| default "Colophon"}}
|
||||
</a>
|
||||
<a href="{{ absLangURL "donate" }}">
|
||||
{{ i18n "donate"| default "Donate"}}
|
||||
</a>
|
||||
<a href="/privacy">
|
||||
{{ i18n "privacy"| default "Privacy Policy"}}
|
||||
</a>
|
||||
</div>
|
|
@ -0,0 +1,5 @@
|
|||
{{ $optBlock := dict "display" "block" }}
|
||||
<div class="comment">
|
||||
<h5>{{ .Get "name" }}</h5>
|
||||
{{ .Inner | $.Page.RenderString $optBlock }}
|
||||
</div>
|
|
@ -0,0 +1,58 @@
|
|||
{{- $old_filename := .Get "src" -}}
|
||||
{{- $img := (.Page.Resources.ByType "image").GetMatch (path.Join "images" $old_filename ) -}}
|
||||
{{- if $img -}}
|
||||
{{- $thumb := $img.Fit "800x800 q90 webp" -}}
|
||||
{{- $new_filename := replace $old_filename (path.Ext $old_filename) "" -}}
|
||||
{{- $dithered := printf "images/dithers/%s_dithered.png" $new_filename -}}
|
||||
{{ $dithered_image := (.Page.Resources.ByType "image").GetMatch $dithered }}
|
||||
{{- $optBlock := dict "display" "block" -}}
|
||||
{{- $alt := .Inner -}}
|
||||
<div class="article-img {{if le $img.Width (mul $img.Height 1.2)}} vertical{{end}}">
|
||||
<figure data-imgstate="dither">
|
||||
<img src="{{with $dithered_image }}{{ .Permalink }}{{end}}" alt='{{ with $alt }}{{ $alt | markdownify| plainify }}{{ else }}{{ .Get "alt" }}{{ . | markdownify| plainify }}{{ end }}' data-original="{{if $thumb}}{{$thumb.Permalink}}{{else}}images/{{$old_filename }}{{end}}" data-dither="{{with $dithered_image }}{{ .RelPermalink }}{{end}}" loading="lazy"/> </figure>
|
||||
<div class="figure-controls">
|
||||
<figcaption class="caption">
|
||||
|
||||
{{ .Inner }}<svg class="dither-toggle icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<rect class="svg_fill" x="13.51" y="13.58" width="24.28" height="24.28"/><rect class="svg_fill" x="37.93" y="37.86" width="24.28" height="24.28"/><rect class="svg_fill" x="62.21" y="13.58" width="24.28" height="24.28"/><rect class="svg_fill" x="13.51" y="62.14" width="24.28" height="24.28"/><rect class="svg_fill" x="62.21" y="62.14" width="24.28" height="24.28"/>
|
||||
</svg>
|
||||
<div class="imgindicator">
|
||||
<span class="tooltip view-orig">
|
||||
{{ i18n "vieworig" | default "View original image"}}
|
||||
</span>
|
||||
<span class="tooltip view-dither">
|
||||
{{ i18n "viewdither" | default "View dithered image"}}
|
||||
</span>
|
||||
</div>
|
||||
</figcaption>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{- else -}}
|
||||
|
||||
{{ $new_filename := replace $old_filename (path.Ext $old_filename) ""}}
|
||||
{{ $dithered := printf "images/dithers/%s_dithered.png" $new_filename }}
|
||||
{{ $dithered_image := (.Page.Resources.ByType "image").GetMatch $dithered }}
|
||||
{{- $alt := .Inner -}}
|
||||
<div class="article-img">
|
||||
<figure data-imgstate="dither">
|
||||
<img src="{{with $dithered_image }}{{ .Permalink }}{{end}}" alt='{{ with $alt }}{{ $alt | markdownify| plainify }}{{ else }}{{ .Get "alt" }}{{ . | markdownify| plainify }}{{ end }}' data-original="images/{{$old_filename }}" data-dither="{{with $dithered_image }}{{ .Permalink }}{{end}}" loading="lazy"/> </figure>
|
||||
<div class="figure-controls">
|
||||
<figcaption class="caption">
|
||||
|
||||
{{ .Inner }}<svg class="dither-toggle icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<rect class="svg_fill" x="13.51" y="13.58" width="24.28" height="24.28"/><rect class="svg_fill" x="37.93" y="37.86" width="24.28" height="24.28"/><rect class="svg_fill" x="62.21" y="13.58" width="24.28" height="24.28"/><rect class="svg_fill" x="13.51" y="62.14" width="24.28" height="24.28"/><rect class="svg_fill" x="62.21" y="62.14" width="24.28" height="24.28"/>
|
||||
</svg>
|
||||
<div class="imgindicator">
|
||||
<span class="tooltip view-orig">
|
||||
{{ i18n "vieworig" | default "View original image"}}
|
||||
</span>
|
||||
<span class="tooltip view-dither">
|
||||
{{ i18n "viewdither" | default "View dithered image"}}
|
||||
</span>
|
||||
</div>
|
||||
</figcaption>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{{- $old_filename := .Get "src" -}}
|
||||
{{- $img := (.Page.Resources.ByType "image").GetMatch (path.Join "images" $old_filename ) -}}
|
||||
{{- if $img -}}
|
||||
{{- $alt := .Inner -}}
|
||||
{{- $new_filename := replace $old_filename (path.Ext $old_filename) "" -}}
|
||||
{{- $dithered := printf "images/dithers/%s_dithered.png" $new_filename -}}
|
||||
{{ $dithered_image := (.Page.Resources.ByType "image").GetMatch $dithered }}
|
||||
<div class="article-img {{if le $img.Width $img.Height}} vertical{{end}}">
|
||||
<figure data-imgstate="dither">
|
||||
<img src="{{with $dithered_image }}{{ .Permalink }}{{end}}" alt='{{ with $alt }}{{ $alt | markdownify| plainify }}{{ else }}{{ .Get "alt" }}{{ . | markdownify| plainify }}{{ end }}' loading="lazy"/></figure>
|
||||
<figcaption class="caption">
|
||||
{{ .Inner }}
|
||||
</figcaption>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
|
@ -0,0 +1,12 @@
|
|||
{{- $path := .Get 0 -}}
|
||||
{{- with site.GetPage $path -}}
|
||||
{{- .Permalink -}}
|
||||
{{- else -}}
|
||||
{{- with .Page.Sites.First.GetPage $path -}}
|
||||
{{- .Permalink -}}
|
||||
{{/* code to produce errors on not found */}}
|
||||
{{- else -}}
|
||||
{{/* {{- warnf "The %q shortcode was unable to resolve %q to a page. See %s" .Name $path .Position -}} */}}
|
||||
|
||||
{{- end -}}
|
||||
{{- end -}}
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 500 500" style="enable-background:new 0 0 500 500;" xml:space="preserve">
|
||||
<title>bat</title>
|
||||
<circle cx="248.5" cy="252.6" r="97"/>
|
||||
<rect x="234.5" y="17.5" width="27.9" height="112.4"/>
|
||||
<rect x="234.5" y="375.2" width="27.9" height="112.4"/>
|
||||
<rect x="371.2" y="238.6" width="112.4" height="27.9"/>
|
||||
<rect x="13.4" y="238.6" width="112.4" height="27.9"/>
|
||||
<rect x="318.8" y="112.1" transform="matrix(0.7071 -0.7071 0.7071 0.7071 20.7095 302.0763)" width="112.4" height="27.9"/>
|
||||
<rect x="65.8" y="365.1" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -232.2856 197.2768)" width="112.4" height="27.9"/>
|
||||
<rect x="361" y="322.9" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -158.2369 376.1901)" width="27.9" height="112.4"/>
|
||||
<rect x="108" y="69.9" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -53.4381 123.1909)" width="27.9" height="112.4"/>
|
||||
</svg>
|
Po Szerokość: | Wysokość: | Rozmiar: 1.1 KiB |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><svg id="weather" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 671.36 101.66"><g><circle cx="51.34" cy="50.83" r="19.81"/><rect x="48.49" y="2.82" width="5.7" height="22.95"/><rect x="48.49" y="75.89" width="5.7" height="22.95"/><rect x="85.03" y="39.36" width="5.7" height="22.95" transform="translate(138.71 -37.04) rotate(90)"/><rect x="11.96" y="39.36" width="5.7" height="22.95" transform="translate(65.64 36.02) rotate(90)"/><rect x="74.33" y="13.52" width="5.7" height="22.95" transform="translate(40.28 -47.25) rotate(45)"/><rect x="22.66" y="65.19" width="5.7" height="22.95" transform="translate(61.68 4.42) rotate(45)"/><rect x="74.33" y="65.19" width="5.7" height="22.95" transform="translate(185.95 76.3) rotate(135)"/><rect x="22.66" y="13.52" width="5.7" height="22.95" transform="translate(61.22 24.64) rotate(135)"/></g><g><g><rect x="155.07" y="2.82" width="5.7" height="22.95"/><rect x="118.54" y="39.36" width="5.7" height="22.95" transform="translate(172.21 -70.55) rotate(90)"/><rect x="180.9" y="13.52" width="5.7" height="22.95" transform="translate(71.5 -122.61) rotate(45)"/><rect x="129.24" y="65.19" width="5.7" height="22.95" transform="translate(92.9 -70.94) rotate(45)"/><rect x="129.24" y="13.52" width="5.7" height="22.95" transform="translate(243.16 -50.72) rotate(135)"/><path d="M157.99,51.51c.08,0,.16,.01,.24,.01,1.14-7.25,6.59-13.06,13.64-14.74-3.58-3.56-8.52-5.76-13.96-5.76-10.94,0-19.81,8.87-19.81,19.81,0,4.16,1.29,8.02,3.48,11.21,2.86-6.21,9.13-10.53,16.41-10.53Z"/></g><path d="M222.23,70.84c0-7.91-5.47-14.53-12.82-16.33-1.03-6.2-6.4-10.94-12.89-10.94-1.98,0-3.85,.45-5.53,1.24-3.05-3.07-7.27-4.98-11.94-4.98-8.39,0-15.33,6.15-16.6,14.19-.08,0-.15-.01-.23-.01-9.29,0-16.82,7.53-16.82,16.82,0,3.75,1.24,7.2,3.32,10l-9.78,6.82h67.75l-.35-.05c8.87-.47,15.92-7.79,15.92-16.78Z"/></g><path d="M335.68,59.53c0-9.32-6.44-17.11-15.1-19.23-1.21-7.31-7.54-12.88-15.19-12.88-2.33,0-4.54,.53-6.52,1.46-3.59-3.62-8.56-5.86-14.07-5.86-9.89,0-18.06,7.25-19.55,16.71-.09,0-.18-.01-.27-.01-10.94,0-19.81,8.87-19.81,19.81,0,4.42,1.46,8.48,3.91,11.78l-11.52,8.04h79.79l-.42-.05c10.44-.56,18.74-9.18,18.74-19.76Z"/><path d="M419.66,69.78c-13.86,0-25.1-11.24-25.1-25.1s11.24-25.1,25.1-25.1c.68,0,1.34,.03,2.01,.09-5.69-3.59-12.42-5.68-19.64-5.68-20.34,0-36.84,16.49-36.84,36.84s16.49,36.84,36.84,36.84c16.07,0,29.73-10.3,34.76-24.65-4.49,4.19-10.51,6.76-17.13,6.76Z"/><g><path d="M567.53,70.84c0-7.91-5.47-14.53-12.82-16.33-1.03-6.2-6.4-10.94-12.89-10.94-1.98,0-3.85,.45-5.53,1.24-3.05-3.07-7.27-4.98-11.94-4.98-8.39,0-15.33,6.15-16.6,14.19-.08,0-.15-.01-.23-.01-9.29,0-16.82,7.53-16.82,16.82,0,3.75,1.24,7.2,3.32,10l-9.78,6.82h67.75l-.35-.05c8.87-.47,15.92-7.79,15.92-16.78Z"/><path d="M497.7,44.39c-7.48,0-13.54-6.06-13.54-13.54s6.06-13.54,13.54-13.54c.36,0,.72,.02,1.08,.05-3.07-1.94-6.7-3.06-10.59-3.06-10.97,0-19.87,8.89-19.87,19.87s8.89,19.87,19.87,19.87c8.67,0,16.03-5.55,18.75-13.29-2.42,2.26-5.67,3.65-9.24,3.65Z"/></g><path d="M667.75,55.79c0-20.44-15.69-37.21-35.68-38.95V5.55h-6v11.22c-20.41,1.32-36.55,18.28-36.55,39.02h36.55v20.46c0,11-4.6,11-6.81,11-3.5,0-6.81,0-6.81-10.08h-6c0,11.12,3.95,16.08,12.81,16.08,5.85,0,12.81-2.95,12.81-17v-20.46h35.68Z"/></svg>
|
Po Szerokość: | Wysokość: | Rozmiar: 3.2 KiB |
|
@ -0,0 +1,157 @@
|
|||
// console.log('script loaded');
|
||||
|
||||
let url = "/api/stats.json";
|
||||
let data;
|
||||
let solar_stats = [];
|
||||
let battery_stats = [];
|
||||
let general_stats = [];
|
||||
|
||||
loadJSON();
|
||||
|
||||
async function loadJSON() {
|
||||
const response = await fetch(url);
|
||||
const data = await response.json();
|
||||
setupBatteryMeter(data);
|
||||
populateDashboard(data);
|
||||
populateForecast(data);
|
||||
|
||||
if (window.location.href.indexOf('/power/') > -1) {
|
||||
//load general stats on power page
|
||||
populateData(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function setupBatteryMeter(data) {
|
||||
//setup visible battery level
|
||||
let level = parseInt(data.charge);
|
||||
let indicator = document.getElementById('battery_data');
|
||||
document.getElementById('battery').style.height = (100-level) + '%';
|
||||
// indicator.style.top = 100 - parseInt(level) + "vh";
|
||||
indicator.style.top = (100-level) + "vh";
|
||||
|
||||
if (data.charging == "no") {
|
||||
// battery is draining, show battery level
|
||||
document.getElementById('level').textContent = level;
|
||||
} else {
|
||||
// sun is out!
|
||||
indicator.setAttribute('data-charging', 'yes');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function pushData(arr) {
|
||||
// returns a list of dt/dd pairs from a two-dimensional array
|
||||
let stats = [];
|
||||
for (i = 0; i < arr.length; i++) {
|
||||
stats.push("<dt>" + arr[i][0] + "</dt><dd>" + arr[i][1] + "</dd>");
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
|
||||
function populateData(data) {
|
||||
let load = ((data.load_15 / 2) * 100).toFixed(2) + '%';
|
||||
|
||||
let general_stats = [
|
||||
["Local time", data.local_time],
|
||||
["Uptime", data.uptime],
|
||||
["Power usage", data.W],
|
||||
["Current draw", data.A],
|
||||
["Voltage", data.V],
|
||||
["CPU temperature", data.temperature + "°C"],
|
||||
["CPU load average *", load],
|
||||
["Solar panel active", data.charging],
|
||||
["Battery capacity", data.charge + "%"]
|
||||
];
|
||||
|
||||
let dl = document.getElementById('server');
|
||||
dl.innerHTML = pushData(general_stats).join("");
|
||||
}
|
||||
|
||||
|
||||
function populateForecast(data) {
|
||||
const weather_ignore = ["snow", "sleet", "wind", "fog"]; //because Barcelona is paradise
|
||||
const weather_data = ["today_icon", "tomorrow_icon", "day_after_t_icon"];
|
||||
const weather_days = ["today", "tomorrow", "day after tomorrow"];
|
||||
let forecast = "";
|
||||
|
||||
for (let i = 0; i < weather_data.length; i++) {
|
||||
|
||||
let icon_name = weather_data[i]
|
||||
let text = data[icon_name].replace(/-/g, " ");
|
||||
let weather_icon;
|
||||
//use cloud icon for all overcast weather
|
||||
if (weather_ignore.includes(data[icon_name])) {
|
||||
weather_icon = "cloudy";
|
||||
} else {
|
||||
weather_icon = data[icon_name];
|
||||
}
|
||||
forecast += '<span class="weather_day" id="' + weather_days[i] + '" title="' + text + '">' + weather_days[i] + '</span><span class="weather_icon ' + weather_icon + '"> </span><span class="weather_text"> ' + text + '</span>';
|
||||
}
|
||||
|
||||
let weatherinfo = document.querySelectorAll('.forecast');
|
||||
|
||||
[].forEach.call(weatherinfo, function(target) {
|
||||
target.innerHTML = forecast;
|
||||
});
|
||||
}
|
||||
|
||||
function populateDashboard(data) {
|
||||
let bat_text = "";
|
||||
|
||||
if(data.charging=='no'){
|
||||
bat_text = data.charge + "%, not charging";
|
||||
}else{
|
||||
bat_text = "charging";
|
||||
}
|
||||
|
||||
let footer_data = [
|
||||
['Location', 'Barcelona'],
|
||||
['Time', data.local_time],
|
||||
['Battery status', bat_text],
|
||||
['Power used', data.W],
|
||||
['Uptime', data.uptime]
|
||||
];
|
||||
|
||||
document.getElementById('stats').innerHTML = pushData(footer_data).join("");
|
||||
}
|
||||
|
||||
// language menu toggle
|
||||
const langmenu = document.getElementById('lang-menu');
|
||||
langmenu.addEventListener('click', function() {
|
||||
console.log('togglelanguages');
|
||||
document.getElementById('languages').classList.toggle("lang-expanded");
|
||||
});
|
||||
|
||||
//mobile menu toggle
|
||||
const mobilemenu = document.getElementById('m-btn');
|
||||
mobilemenu.addEventListener('click', function() {
|
||||
console.log('togglemenu');
|
||||
document.getElementById('menu-list').classList.toggle("show");
|
||||
});
|
||||
|
||||
|
||||
const comments = document.querySelectorAll('.comment');
|
||||
if ( comments.length > 0 ){
|
||||
//update comment count
|
||||
document.getElementById('comment-count').innerText = comments.length;
|
||||
}
|
||||
|
||||
|
||||
const dither_icons = document.querySelectorAll('.dither-toggle');
|
||||
dither_icons.forEach(icon => {
|
||||
icon.addEventListener('click', function() {
|
||||
let figure = icon.closest('.figure-controls').previousElementSibling;
|
||||
let img = figure.querySelector('img');
|
||||
|
||||
if( figure.getAttribute('data-imgstate') == "dither"){
|
||||
figure.setAttribute('data-imgstate', 'undither');
|
||||
let original = img.getAttribute('data-original');
|
||||
img.src = original;
|
||||
}else{
|
||||
figure.setAttribute('data-imgstate', 'dither');
|
||||
let dither= img.getAttribute('data-dither');
|
||||
img.src = dither;
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
/**
|
||||
* TinySort is a small script that sorts HTML elements. It sorts by text- or attribute value, or by that of one of it's children.
|
||||
* @summary A nodeElement sorting script.
|
||||
* @version 2.3.6
|
||||
* @license MIT
|
||||
* @author Ron Valstar <ron@ronvalstar.nl>
|
||||
* @copyright Ron Valstar <ron@ronvalstar.nl>
|
||||
* @namespace tinysort
|
||||
*/
|
||||
!function(e,t){"use strict";function r(){return t}"function"==typeof define&&define.amd?define("tinysort",r):e.tinysort=t}(this,function(){"use strict";function e(e,n){function s(){0===arguments.length?v({}):t(arguments,function(e){v(x(e)?{selector:e}:e)}),d=$.length}function v(e){var t=!!e.selector,n=t&&":"===e.selector[0],o=r(e||{},m);$.push(r({hasSelector:t,hasAttr:!(o.attr===l||""===o.attr),hasData:o.data!==l,hasFilter:n,sortReturnNumber:"asc"===o.order?1:-1},o))}function S(){t(e,function(e,t){M?M!==e.parentNode&&(k=!1):M=e.parentNode;var r=$[0],n=r.hasFilter,o=r.selector,a=!o||n&&e.matchesSelector(o)||o&&e.querySelector(o),l=a?R:V,s={elm:e,pos:t,posn:l.length};B.push(s),l.push(s)}),D=R.slice(0)}function y(e,t,r){for(var n=r(e.toString()),o=r(t.toString()),a=0;n[a]&&o[a];a++)if(n[a]!==o[a]){var l=Number(n[a]),s=Number(o[a]);return l==n[a]&&s==o[a]?l-s:n[a]>o[a]?1:-1}return n.length-o.length}function N(e){for(var t,r,n=[],o=0,a=-1,l=0;t=(r=e.charAt(o++)).charCodeAt(0);){var s=46==t||t>=48&&57>=t;s!==l&&(n[++a]="",l=s),n[a]+=r}return n}function C(e,r){var n=0;for(0!==p&&(p=0);0===n&&d>p;){var l=$[p],s=l.ignoreDashes?f:u;if(t(h,function(e){var t=e.prepare;t&&t(l)}),l.sortFunction)n=l.sortFunction(e,r);else if("rand"==l.order)n=Math.random()<.5?1:-1;else{var c=a,g=w(e,l),m=w(r,l),v=""===g||g===o,S=""===m||m===o;if(g===m)n=0;else if(l.emptyEnd&&(v||S))n=v&&S?0:v?1:-1;else{if(!l.forceStrings){var C=x(g)?g&&g.match(s):a,b=x(m)?m&&m.match(s):a;if(C&&b){var A=g.substr(0,g.length-C[0].length),F=m.substr(0,m.length-b[0].length);A==F&&(c=!a,g=i(C[0]),m=i(b[0]))}}n=g===o||m===o?0:l.natural&&(isNaN(g)||isNaN(m))?y(g,m,N):m>g?-1:g>m?1:0}}t(h,function(e){var t=e.sort;t&&(n=t(l,c,g,m,n))}),n*=l.sortReturnNumber,0===n&&p++}return 0===n&&(n=e.pos>r.pos?1:-1),n}function b(){var e=R.length===B.length;if(k&&e)O?R.forEach(function(e,t){e.elm.style.order=t}):M?M.appendChild(A()):console.warn("parentNode has been removed");else{var t=$[0],r=t.place,n="org"===r,o="start"===r,a="end"===r,l="first"===r,s="last"===r;if(n)R.forEach(F),R.forEach(function(e,t){E(D[t],e.elm)});else if(o||a){var c=D[o?0:D.length-1],i=c&&c.elm.parentNode,u=i&&(o&&i.firstChild||i.lastChild);u&&(u!==c.elm&&(c={elm:u}),F(c),a&&i.appendChild(c.ghost),E(c,A()))}else if(l||s){var f=D[l?0:D.length-1];E(F(f),A())}}}function A(){return R.forEach(function(e){q.appendChild(e.elm)}),q}function F(e){var t=e.elm,r=c.createElement("div");return e.ghost=r,t.parentNode.insertBefore(r,t),e}function E(e,t){var r=e.ghost,n=r.parentNode;n.insertBefore(t,r),n.removeChild(r),delete e.ghost}function w(e,t){var r,n=e.elm;return t.selector&&(t.hasFilter?n.matchesSelector(t.selector)||(n=l):n=n.querySelector(t.selector)),t.hasAttr?r=n.getAttribute(t.attr):t.useVal?r=n.value||n.getAttribute("value"):t.hasData?r=n.getAttribute("data-"+t.data):n&&(r=n.textContent),x(r)&&(t.cases||(r=r.toLowerCase()),r=r.replace(/\s+/g," ")),null===r&&(r=g),r}function x(e){return"string"==typeof e}x(e)&&(e=c.querySelectorAll(e)),0===e.length&&console.warn("No elements to sort");var D,M,q=c.createDocumentFragment(),B=[],R=[],V=[],$=[],k=!0,z=e.length&&e[0].parentNode,L=z.rootNode!==document,O=e.length&&(n===o||n.useFlex!==!1)&&!L&&-1!==getComputedStyle(z,null).display.indexOf("flex");return s.apply(l,Array.prototype.slice.call(arguments,1)),S(),R.sort(C),b(),R.map(function(e){return e.elm})}function t(e,t){for(var r,n=e.length,o=n;o--;)r=n-o-1,t(e[r],r)}function r(e,t,r){for(var n in t)(r||e[n]===o)&&(e[n]=t[n]);return e}function n(e,t,r){h.push({prepare:e,sort:t,sortBy:r})}var o,a=!1,l=null,s=window,c=s.document,i=parseFloat,u=/(-?\d+\.?\d*)\s*$/g,f=/(\d+\.?\d*)\s*$/g,h=[],d=0,p=0,g=String.fromCharCode(4095),m={selector:l,order:"asc",attr:l,data:l,useVal:a,place:"org",returns:a,cases:a,natural:a,forceStrings:a,ignoreDashes:a,sortFunction:l,useFlex:a,emptyEnd:a};return s.Element&&function(e){e.matchesSelector=e.matchesSelector||e.mozMatchesSelector||e.msMatchesSelector||e.oMatchesSelector||e.webkitMatchesSelector||function(e){for(var t=this,r=(t.parentNode||t.document).querySelectorAll(e),n=-1;r[++n]&&r[n]!=t;);return!!r[n]}}(Element.prototype),r(n,{loop:t}),r(e,{plugin:n,defaults:m})}());
|
|
@ -0,0 +1,51 @@
|
|||
#!/bin/bash
|
||||
# © 2023 Roel Roscam Abbing, released as AGPLv3
|
||||
# see https://www.gnu.org/licenses/agpl-3.0.html
|
||||
# Support your local low-tech magazine: https://solar.lowtechmagazine.com/donate/
|
||||
|
||||
now=`date`
|
||||
baseURL="" #the URL of the website e.g. htttps://solar.lowtechmagazine.com/
|
||||
contentDir="" #the directory where your HUGO articles are e.g. /path/to/repo/solar_v2/content/
|
||||
repoDir="" #the full path to the repository
|
||||
outputDir="" # the directory where you export the site to.
|
||||
|
||||
|
||||
while getopts f flag
|
||||
do
|
||||
case "${flag}" in
|
||||
f) updated="forced rebuild";;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ $updated != "forced rebuild" ]]; then
|
||||
echo "Checking for update $now"
|
||||
updated=$(git -C $repoDir pull origin main)
|
||||
fi
|
||||
|
||||
|
||||
if echo $updated | grep -q "Already up to date";
|
||||
then
|
||||
echo "Git up to date $now"
|
||||
else
|
||||
echo "Git was not up to date"
|
||||
echo $updated
|
||||
echo "Rebuilding the site"
|
||||
|
||||
cd $repoDir
|
||||
|
||||
echo "Dithering new images"
|
||||
/usr/bin/python3 utils/dither_images.py -d $contentDir --colorize
|
||||
|
||||
echo "Generating site"
|
||||
hugo -b $baseURL --destination $outputDir
|
||||
|
||||
echo "Calculating page sizes"
|
||||
/usr/bin/python3 utils/calculate_size.py --directory $outputDir --baseURL $baseURL
|
||||
|
||||
echo "Removing original media from" $outputDir
|
||||
/usr/bin/python3 utils/clean_output.py --directory $outputDir
|
||||
|
||||
after=`date`
|
||||
echo "Site regeneration started $now"
|
||||
echo "Site regeneration finished $after"
|
||||
fi
|
|
@ -0,0 +1,176 @@
|
|||
# Page Metadata
|
||||
# © 2022 Roel Roscam Abbing, released as AGPLv3
|
||||
# see https://www.gnu.org/licenses/agpl-3.0.html
|
||||
# Support your local low-tech magazine: https://solar.lowtechmagazine.com/donate.html
|
||||
|
||||
import os
|
||||
import argparse
|
||||
import shutil
|
||||
from bs4 import BeautifulSoup
|
||||
import logging
|
||||
import sys
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
"""
|
||||
This script recursively traverses folders and enumerates the file size of all html pages and associated media.
|
||||
The calculated total file size is then added to the HTML page.
|
||||
"""
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-d', '--directory', help="Set the directory to traverse", default="."
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-rm', '--remove', help="Removes all the folders with dithers and their contents", action="store_true"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-b', '--baseURL', help="hostname (and path) to the root, e.g. https://solar.lowtechmagazine.com"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-v', '--verbose', help="Print out more detailed information about what this script is doing", action="store_true"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.verbose:
|
||||
logging.basicConfig(level=logging.DEBUG, stream=sys.stdout)
|
||||
else:
|
||||
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
|
||||
|
||||
content_dir = args.directory
|
||||
base_url = args.baseURL
|
||||
|
||||
def get_printable_size(byte_size):
|
||||
"""
|
||||
Pretty file sizes.
|
||||
Thanks Pobux!
|
||||
https://gist.github.com/Pobux/0c474672b3acd4473d459d3219675ad8
|
||||
"""
|
||||
BASE_SIZE = 1024.00
|
||||
MEASURE = ["B", "KB", "MB", "GB", "TB", "PB"]
|
||||
|
||||
def _fix_size(size, size_index):
|
||||
if not size:
|
||||
return "0"
|
||||
elif size_index == 0:
|
||||
return str(size)
|
||||
else:
|
||||
return "{:.2f}".format(size)
|
||||
|
||||
current_size = byte_size
|
||||
size_index = 0
|
||||
|
||||
while current_size >= BASE_SIZE and len(MEASURE) != size_index:
|
||||
current_size = current_size / BASE_SIZE
|
||||
size_index = size_index + 1
|
||||
|
||||
size = _fix_size(current_size, size_index)
|
||||
measure = MEASURE[size_index]
|
||||
return size + measure
|
||||
|
||||
def get_assets(soup):
|
||||
"""
|
||||
Lists all the page assets such as scripts and icons in a given HMTL page.
|
||||
"""
|
||||
assets = []
|
||||
for a in soup.findAll('link', {'rel':['apple-touch-icon','icon','stylesheet']}):
|
||||
a = a['href'].split('?')[0]
|
||||
if a not in assets:
|
||||
assets.append(a)
|
||||
for s in soup.findAll('script'):
|
||||
if ['src'] in s:
|
||||
s = s['src']
|
||||
if s not in assets:
|
||||
assets.append(s)
|
||||
|
||||
return assets
|
||||
|
||||
def get_media(html_file):
|
||||
"""
|
||||
Lists all the images on a given HTML page.
|
||||
"""
|
||||
html_file = open(html_file).read()
|
||||
soup = BeautifulSoup(html_file, 'html.parser')
|
||||
media = []
|
||||
|
||||
for img in soup(['img', 'object']):
|
||||
media.append(img['src'])
|
||||
|
||||
featured_images = soup.findAll('div', {'class':'featured-img'})
|
||||
for fi in featured_images:
|
||||
fi = fi['style']
|
||||
start = fi.find("url('")
|
||||
end = fi.find("');")
|
||||
url = fi[start+len("url('"):end]
|
||||
media.append(url)
|
||||
|
||||
assets = get_assets(soup)
|
||||
#assets = list(set(assets))
|
||||
media = list(set(media+assets)) # duplicate media don't increase page size
|
||||
return media, soup
|
||||
|
||||
def insert_metadata(output_file, metadata, soup):
|
||||
"""
|
||||
Adds page metadata to a given HTML page and saves it.
|
||||
"""
|
||||
tag = soup.find('div', {'id':'page-size'})
|
||||
if tag:
|
||||
with open(output_file,'w') as f:
|
||||
tag.string = '{}'.format(metadata)
|
||||
f.write(str(soup))
|
||||
|
||||
logging.info("Checking file sizes for '{}'".format(content_dir))
|
||||
|
||||
for root, dirs, files in os.walk(os.path.abspath(content_dir), topdown=True):
|
||||
for fname in files:
|
||||
if fname.endswith(".html"):
|
||||
logging.debug("Checking file size for '{}'".format(os.path.join(root, fname)))
|
||||
media_size = 0
|
||||
media, soup = get_media(os.path.join(root, fname))
|
||||
|
||||
for m in media:
|
||||
file_name = m.replace(base_url, '')
|
||||
|
||||
# current problematic with pagebundles is images are in the same folder as html file
|
||||
# but html file might link to images in other page bundles so we need to determine
|
||||
# whether the image is in this page bundle or another:
|
||||
|
||||
media_this_page = os.path.join(root, file_name.strip('/'))
|
||||
media_other_page = os.path.join(content_dir, file_name.strip('/'))
|
||||
|
||||
try:
|
||||
if os.path.exists(media_this_page):
|
||||
m = media_this_page
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
if os.path.exists(media_other_page):
|
||||
m = media_other_page
|
||||
except:
|
||||
pass
|
||||
|
||||
if os.path.exists(m):
|
||||
item_size = os.path.getsize(m)
|
||||
media_size = media_size + item_size
|
||||
logging.debug("Found {} {}".format(m, get_printable_size(item_size)))
|
||||
else:
|
||||
# if the file path can't be found it might actually not be there at all..
|
||||
logging.debug("❌ {} not found!".format(m))
|
||||
|
||||
|
||||
current_file = os.path.join(root, fname)
|
||||
file_size = os.path.getsize(current_file)
|
||||
|
||||
file_size = file_size + media_size
|
||||
metadata = get_printable_size(file_size)
|
||||
metadata = get_printable_size(file_size+len(metadata)) # count the extra metadata as well
|
||||
|
||||
insert_metadata(os.path.join(root, fname), metadata, soup)
|
||||
logging.debug("{} is {}".format(os.path.join(root,fname), metadata))
|
||||
|
||||
logging.info("Done checking filesizes")
|
|
@ -0,0 +1,96 @@
|
|||
# clean_output
|
||||
# © 2023 Roel Roscam Abbing, released as AGPLv3
|
||||
# see https://www.gnu.org/licenses/agpl-3.0.html
|
||||
# Support your local low-tech magazine: https://solar.lowtechmagazine.com/donate.html
|
||||
|
||||
import os
|
||||
import argparse
|
||||
import logging
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
"""
|
||||
This script recursively traverses folders and deletes files not in "dithers" or containing hugos resizing pattern in the filname.
|
||||
This catches media in your content repository which is not used but still copied over to the final site.
|
||||
Use with caution and adjust the pattern to your own situation!
|
||||
"""
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-d', '--directory', help="Set the directory to traverse", default="."
|
||||
)
|
||||
parser.add_argument(
|
||||
'-v', '--verbose', help="Print out more detailed information about what this script is doing", action="store_true"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
content_dir = args.directory
|
||||
|
||||
def get_printable_size(byte_size):
|
||||
"""
|
||||
Pretty file sizes.
|
||||
Thanks Pobux!
|
||||
https://gist.github.com/Pobux/0c474672b3acd4473d459d3219675ad8
|
||||
"""
|
||||
BASE_SIZE = 1024.00
|
||||
MEASURE = ["B", "KB", "MB", "GB", "TB", "PB"]
|
||||
|
||||
def _fix_size(size, size_index):
|
||||
if not size:
|
||||
return "0"
|
||||
elif size_index == 0:
|
||||
return str(size)
|
||||
else:
|
||||
return "{:.2f}".format(size)
|
||||
|
||||
current_size = byte_size
|
||||
size_index = 0
|
||||
|
||||
while current_size >= BASE_SIZE and len(MEASURE) != size_index:
|
||||
current_size = current_size / BASE_SIZE
|
||||
size_index = size_index + 1
|
||||
|
||||
size = _fix_size(current_size, size_index)
|
||||
measure = MEASURE[size_index]
|
||||
return size + measure
|
||||
|
||||
def calculate_dir_size(content_dir):
|
||||
size = 0
|
||||
for path, dirs, files in os.walk(os.path.abspath(content_dir)):
|
||||
for f in files:
|
||||
fp = os.path.join(path, f)
|
||||
size += os.path.getsize(fp)
|
||||
|
||||
return(size)
|
||||
|
||||
exclude_dirs = set(["dithers"])
|
||||
image_ext = [".jpg", ".JPG", ".jpeg", ".png", ".gif", ".webp", ".tiff", ".bmp"]
|
||||
|
||||
pattern = "_800x800_fit_q90"
|
||||
|
||||
if args.verbose:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
else:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
logging.info("Deleting images in {} and lower".format(os.path.abspath(content_dir)))
|
||||
|
||||
count = 0
|
||||
|
||||
size = calculate_dir_size(content_dir)
|
||||
|
||||
logging.info("Directory is {} before cleanup".format(get_printable_size(size)))
|
||||
for root, dirs, files in os.walk(os.path.abspath(content_dir), topdown=True):
|
||||
dirs[:] = [d for d in dirs if d not in exclude_dirs]
|
||||
for fname in files:
|
||||
if fname.endswith(tuple(image_ext)):
|
||||
if pattern not in fname:
|
||||
f = os.path.join(root, fname)
|
||||
count+=1
|
||||
os.remove(f)
|
||||
logging.debug("🗑 {}".format(fname))
|
||||
|
||||
logging.info("Deleted {} original images".format(count))
|
||||
size = calculate_dir_size(content_dir)
|
||||
logging.info("Directory is {} after cleanup".format(get_printable_size(size)))
|
|
@ -0,0 +1,183 @@
|
|||
# image dithering script
|
||||
# © 2022 Roel Roscam Abbing, released as AGPLv3
|
||||
# see https://www.gnu.org/licenses/agpl-3.0.html
|
||||
# Support your local low-tech magazine: https://solar.lowtechmagazine.com/donate.html
|
||||
|
||||
import hitherdither
|
||||
import os
|
||||
import argparse
|
||||
import shutil
|
||||
from PIL import Image
|
||||
import logging
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
"""
|
||||
This script recursively traverses folders and creates dithered versions of the images it finds.
|
||||
These are stored in the same folder as the images in a folder called "dithers".
|
||||
"""
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-d', '--directory', help="Set the directory to traverse", default="."
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-rm', '--remove', help="Removes all the folders with dithers and their contents", action="store_true"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-c', '--colorize', help="Colorizes the dithered images", action="store_true"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-v', '--verbose', help="Print out more detailed information about what this script is doing", action="store_true"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
image_ext = [".jpg", ".JPG", ".jpeg", ".png", ".gif", ".webp", ".tiff", ".bmp"]
|
||||
|
||||
|
||||
content_dir = args.directory
|
||||
|
||||
if args.verbose:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
else:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
exclude_dirs = set(["dithers"])
|
||||
|
||||
|
||||
logging.info("Dithering all images in {} and subfolders".format(content_dir))
|
||||
logging.debug("excluding directories: {}".format("".join(exclude_dirs)))
|
||||
|
||||
def colorize(source_image, category):
|
||||
"""
|
||||
Picks a colored dithering palette based on the post category.
|
||||
"""
|
||||
|
||||
colors = {
|
||||
'low-tech': hitherdither.palette.Palette([(30,32,40), (11,21,71),(57,77,174),(158,168,218),(187,196,230),(243,244,250)]),
|
||||
'obsolete': hitherdither.palette.Palette([(9,74,58), (58,136,118),(101,163,148),(144,189,179),(169,204,195),(242,247,246)]),
|
||||
'high-tech': hitherdither.palette.Palette([(86,9,6), (197,49,45),(228,130,124),(233,155,151),(242,193,190),(252,241,240)]),
|
||||
'grayscale': hitherdither.palette.Palette([(25,25,25), (75,75,75),(125,125,125),(175,175,175),(225,225,225),(250,250,250)])
|
||||
}
|
||||
|
||||
|
||||
if category:
|
||||
|
||||
for i in colors.keys():
|
||||
if i in category.lower():
|
||||
color = colors[i]
|
||||
logging.info("Applying color palette '{}' for {}".format(i, category))
|
||||
break
|
||||
else:
|
||||
logging.info("No category for {}, {}".format(source_image, category))
|
||||
print("No category for {}, {}".format(source_image, category))
|
||||
color = colors['grayscale']
|
||||
|
||||
else:
|
||||
logging.info("No category for {}, {}".format(source_image, category))
|
||||
print("No category for {}, {}".format(source_image, category))
|
||||
color = colors['grayscale']
|
||||
|
||||
return color
|
||||
|
||||
|
||||
def dither_image(source_image, output_image, category ='grayscale'):
|
||||
#see hitherdither docs for different dithering algos and settings
|
||||
|
||||
if args.colorize:
|
||||
palette = colorize(source_image, category)
|
||||
else:
|
||||
palette = hitherdither.palette.Palette([(25,25,25), (75,75,75),(125,125,125),(175,175,175),(225,225,225),(250,250,250)])
|
||||
|
||||
try:
|
||||
img= Image.open(source_image).convert('RGB')
|
||||
img.thumbnail((800,800), Image.LANCZOS)
|
||||
#palette = palettes[category]
|
||||
threshold = [96, 96, 96]
|
||||
img_dithered = hitherdither.ordered.bayer.bayer_dithering(img, palette, threshold, order=8)
|
||||
#if args.colorize:
|
||||
# img_dithered = colorize(img_dithered, category)
|
||||
# logging.debug("Created {} in category {}".format(img_dithered, category))
|
||||
|
||||
img_dithered.save(output_image, optimize=True)
|
||||
|
||||
except Exception as e:
|
||||
logging.debug("❌ failed to convert {}".format(source_image))
|
||||
logging.debug(e)
|
||||
|
||||
def delete_dithers(content_dir):
|
||||
logging.info("Deleting 'dither' folders in {} and below".format(content_dir))
|
||||
for root, dirs, files in os.walk(content_dir, topdown=True):
|
||||
if root.endswith('dithers'):
|
||||
shutil.rmtree(root)
|
||||
logging.info("Removed {}".format(root))
|
||||
|
||||
|
||||
def parse_front_matter(md):
|
||||
with open(md) as f:
|
||||
contents = f.readlines()
|
||||
cat = None
|
||||
for l in contents:
|
||||
if l.startswith("categories: "):
|
||||
cat = l.split("categories: ")[1]
|
||||
cat = cat.strip("[")
|
||||
cat = cat.strip()
|
||||
cat = cat.strip("]")
|
||||
|
||||
logging.debug("Categories: {} from {}".format(cat, l.strip()))
|
||||
return cat
|
||||
|
||||
prev_root = None
|
||||
|
||||
if args.remove:
|
||||
delete_dithers(
|
||||
os.path.abspath(content_dir)
|
||||
)
|
||||
else:
|
||||
for root, dirs, files in os.walk(os.path.abspath(content_dir), topdown=True):
|
||||
logging.debug("Checking next folder {}".format(root))
|
||||
|
||||
dirs[:] = [d for d in dirs if d not in exclude_dirs]
|
||||
|
||||
category = None
|
||||
if prev_root is None:
|
||||
prev_root = root
|
||||
|
||||
if prev_root is not root:
|
||||
if files:
|
||||
if any(x.endswith(tuple(image_ext)) for x in files):
|
||||
if not os.path.exists(os.path.join(root,'dithers')):
|
||||
os.mkdir(os.path.join(root,'dithers'))
|
||||
logging.info("📁 created in {}".format(root))
|
||||
|
||||
if args.colorize:
|
||||
#iterate over md files to find one with a category
|
||||
if not category:
|
||||
for i in os.listdir(root):
|
||||
if i.startswith('index'):
|
||||
category2 = parse_front_matter(os.path.join(root,i))
|
||||
|
||||
break
|
||||
|
||||
|
||||
for fname in files:
|
||||
if fname.endswith(tuple(image_ext)):
|
||||
file_, ext = os.path.splitext(fname)
|
||||
source_image= os.path.join(root,fname)
|
||||
output_image = os.path.join(os.path.join(root, 'dithers'), file_+'_dithered.png')
|
||||
if not os.path.exists(output_image):
|
||||
if not args.colorize:
|
||||
category2 = "grayscale"
|
||||
dither_image(source_image,output_image, category2)
|
||||
logging.info("🖼 converted {}".format(fname))
|
||||
logging.debug(output_image)
|
||||
else:
|
||||
logging.debug("Dithered version of {} found, skipping".format(fname))
|
||||
|
||||
prev_root = root
|
||||
|
||||
|
||||
logging.info("Done dithering")
|
Ładowanie…
Reference in New Issue