diff --git a/config.toml b/config.toml
index f72bcc0..d974545 100644
--- a/config.toml
+++ b/config.toml
@@ -208,6 +208,14 @@ menu = [
# The RSS icon will be shown if (1) it's enabled and (2) the following variable is set to true.
feed_icon = true
+# Email address for footer's social section.
+# Protect against spambots:
+# 1. Use base64 for email (convert at https://www.base64encode.org/ or `printf 'your@email.com' | base64`).
+# 2. Or, set 'encode_plaintext_email' to true for auto-encoding (only protects on site, not in public repos).
+email = "bWFpbEBleGFtcGxlLmNvbQ==" # base64 encoded version of "mail@example.com"
+# Decoding requires ~400 bytes of JavaScript. If JS is disabled, the email won't be displayed.
+encode_plaintext_email = true # Setting is ignored if email is already encoded.
+
# The icons available can be found in "social_icons" in the "static" folder.
socials = [
{ name = "github", url = "https://github.com/welpo/", icon = "github" },
diff --git a/content/blog/javascript.ca.md b/content/blog/javascript.ca.md
index 22c0433..2ed512c 100644
--- a/content/blog/javascript.ca.md
+++ b/content/blog/javascript.ca.md
@@ -1,18 +1,23 @@
+++
title = "Sense JavaScript obligatori"
date = 2023-01-06
-updated = 2023-08-02
+updated = 2023-08-18
description = "JavaScript només s'utilitza quan HTML i CSS no són suficients."
[taxonomies]
tags = ["funcionalitat", "tutorial"]
+
+[extra]
+footnote_backlinks = true
+++
Aquest tema no requereix JavaScript obligatori. Opcionalment, pot carregar una quantitat mínima per afegir algunes característiques que són impossibles d'aconseguir amb HTML i CSS.
## Opcions habilitades globalment
-L'**interruptor de mode clar/fosc** pot habilitar-se configurant `theme_switcher = true` a la secció `[extra]` del teu `config.toml` (~900 bytes de JavaScript).
+- L'**interruptor de mode clar/fosc** pot habilitar-se configurant `theme_switcher = true` a la secció `[extra]` del teu `config.toml` (~900 bytes de JavaScript).
+
+- **Decodificació del correu** (~400 bytes). Per protegir el teu lloc correu dels [robots de correu brossa](https://ca.wikipedia.org/wiki/Robot_de_correu_brossa), pots configurar `encode_plaintext_email = true`. Si el teu lloc web està en un repositori públic, per a una protecció millorada, considera configurar el teu `email` com una cadena codificada en base64[^1] directament (per exemple: `email = "bWFpbEBleGFtcGxlLmNvbQ=="`).
## Configuracions que es poden habilitar tant globalment com en publicacions individuals
@@ -28,3 +33,7 @@ Per habilitar aquestes configuracions globalment, afegeix-les a la secció `[ext
- [**Comentaris**](@/blog/comments.ca.md). giscus (2 KB), utterances (1 KB), Hyvor Talk (~800 bytes) o Isso (1KB) es poden habilitar globalment configurant `enabled_for_all_posts = true` a la secció apropiada del teu fitxer `config.toml` (`[extra.giscus]`, `[extra.utterances]`, `[extra.hyvortalk]` o `[extra.isso]`). Per habilitar comentaris en publicacions individuals, configura el nom del sistema `= true` (per exemple, `hyvortalk = true`) al front matter del post.
A part d'això, és un tema ràpid amb HTML i CSS que funciona sense JavaScript. Just com hauria de ser (la majoria de) la web :-)
+
+
+
+[^1]: Per codificar el teu correu en base64 pots utilitzar [eines en línia](https://www.base64encode.org/) o, al terminal, executa: `printf 'mail@example.com' | base64`.
diff --git a/content/blog/javascript.es.md b/content/blog/javascript.es.md
index c85a22f..297140b 100644
--- a/content/blog/javascript.es.md
+++ b/content/blog/javascript.es.md
@@ -1,18 +1,23 @@
+++
title = "Sin JavaScript obligatorio"
date = 2023-01-06
-updated = 2023-08-02
+updated = 2023-08-18
description = "JavaScript solo se utiliza cuando HTML y CSS no son suficientes."
[taxonomies]
tags = ["funcionalidad", "tutorial"]
+
+[extra]
+footnote_backlinks = true
+++
Este tema no tiene JavaScript obligatorio. Opcionalmente, puede cargar una cantidad mínima para agregar algunas características que son imposibles de lograr con HTML y CSS.
## Opciones habilitadas globalmente
-El **interruptor de modo claro/oscuro** puede habilitarse configurando `theme_switcher = true` en la sección `[extra]` de tu `config.toml` (~900 bytes de JavaScript).
+- El **interruptor de modo claro/oscuro** puede habilitarse configurando `theme_switcher = true` en la sección `[extra]` de tu `config.toml` (~900 bytes de JavaScript).
+
+- **Decodificación de correo electrónico** (~400 bytes). Para protegerte contra los bots que escanean tu correo electrónico desde tu sitio web, puedes configurar `encode_plaintext_email = true`. Si tu sitio está en un repositorio público, para una protección extra, considera configurar tu `email` como una cadena codificada en base64[^1] directamente.
## Configuraciones que pueden habilitarse tanto globalmente como en publicaciones individuales
@@ -27,3 +32,7 @@ Para habilitar estas configuraciones globalmente, añádelas en la sección `[ex
- [**Comentarios**](@/blog/comments.es.md). giscus (2 KB), utterances (1 KB), Hyvor Talk (~800 bytes) o Isso (1KB) se pueden habilitar globalmente configurando `enabled_for_all_posts = true` en el apartado apropiado de tu archivo `config.toml` (`[extra.giscus]`, `[extra.utterances]`, `[extra.hyvortalk]` o `[extra.isso]`). Para habilitar comentarios en publicaciones individuales, configura el nombre del sistema `= true` (por ejemplo, `hyvortalk = true`) en el front matter del post.
Aparte de eso, es un tema rápido con HTML y CSS que funciona con JavaScript deshabilitado. Justo como debería ser (en su mayoría) la web :-)
+
+
+
+[^1]: Para codificar tu correo en base64 puedes usar [herramientas en línea](https://www.base64encode.org/) o, en tu terminal, ejecuta: `printf '
diff --git a/content/blog/javascript.md b/content/blog/javascript.md
index 29fe968..36f3bc9 100644
--- a/content/blog/javascript.md
+++ b/content/blog/javascript.md
@@ -1,18 +1,23 @@
+++
title = "No mandatory JavaScript"
date = 2023-01-06
-updated = 2023-08-02
+updated = 2023-08-18
description = "JavaScript is only used when HTML and CSS aren't enough."
[taxonomies]
tags = ["showcase", "tutorial"]
+
+[extra]
+footnote_backlinks = true
+++
This theme has no mandatory JavaScript. Optionally, it can load a minimal amount to add some features that are impossible to achieve with HTML and CSS.
## Globally enabled settings
-The **light/dark mode switch** can be enabled by setting `theme_switcher = true` in the `[extra]` section of your `config.toml` (~900 bytes of JavaScript).
+- The **light/dark mode switch** can be enabled by setting `theme_switcher = true` in the `[extra]` section of your `config.toml` (~900 bytes of JavaScript).
+
+- **E-mail decoding** (~400 bytes). To protect against spambots scraping your e-mail from your website, you can set `encode_plaintext_email = true`. If your site is on a public repository, for extra protection, consider setting your `email` as a base64-encoded string[^1] directly.
## Settings that can be enabled globally and for individual posts
@@ -27,3 +32,7 @@ To enable these settings globally, add them in the `[extra]` section of your `co
- [**Comments**](@/blog/comments.md). giscus (2 KB), utterances (1 KB), Hyvor Talk (~800 bytes) or Isso (1KB) can be globally enabled by setting `enabled_for_all_posts = true` in the right section of your `config.toml` (i.e. `[extra.giscus]`, `[extra.utterances]`, `[extra.hyvortalk]` or `[extra.isso]`). To enable comments on individual posts, set the name of the system `= true` (e.g. `hyvortalk = true`) in the post's front matter.
Other than that, it's a fast theme with HTML and CSS which works with JavaScript disabled. Just the way (most of) the web should be :-)
+
+
+
+[^1]: To encode your email in base64 you can use [online tools](https://www.base64encode.org/) or, on your terminal, run: `printf 'mail@example.com' | base64`.
diff --git a/static/js/decodeMail.js b/static/js/decodeMail.js
new file mode 100644
index 0000000..d62c70c
--- /dev/null
+++ b/static/js/decodeMail.js
@@ -0,0 +1,44 @@
+(function () {
+ 'use strict';
+
+ // Utility function: Base64 Decoding.
+ function decodeBase64(encodedString) {
+ try {
+ // Can't use atob() directly because it doesn't support non-ascii characters.
+ // And non-ascii characters are allowed in email addresses and domains.
+ // See https://en.wikipedia.org/wiki/Email_address#Internationalization
+ // Code below adapted from Jackie Han: https://stackoverflow.com/a/64752311
+ const byteString = atob(encodedString);
+
+ // Convert byteString to an array of char codes.
+ const charCodes = [...byteString].map(char => char.charCodeAt(0));
+
+ // Use TypedArray.prototype.set() to copy the char codes into a Uint8Array.
+ const bytes = new Uint8Array(charCodes.length);
+ bytes.set(charCodes);
+
+ const decoder = new TextDecoder("utf-8");
+ return decoder.decode(bytes);
+ } catch (e) {
+ console.error("Failed to decode Base64 string: ", e);
+ return null;
+ }
+ }
+
+ // Utility function: Update href of an element with a decoded email.
+ function updateEmailHref(element) {
+ const encodedEmail = element.getAttribute('data-encoded-email');
+ const decodedEmail = decodeBase64(encodedEmail);
+
+ if (decodedEmail) {
+ element.setAttribute('href', `mailto:${decodedEmail}`);
+ } else {
+ // If the decoding fails, hide the email link.
+ element.style.display = 'none';
+ }
+ }
+
+ // Fetch and process email elements with the "data-encoded-email" attribute.
+ const encodedEmailElements = document.querySelectorAll('[data-encoded-email]');
+ encodedEmailElements.forEach(updateEmailHref);
+})();
diff --git a/static/js/decodeMail.min.js b/static/js/decodeMail.min.js
new file mode 100644
index 0000000..2b766a0
--- /dev/null
+++ b/static/js/decodeMail.min.js
@@ -0,0 +1 @@
+!function(){"use strict";document.querySelectorAll("[data-encoded-email]").forEach(function(e){var t=function(e){try{var t=[...atob(e)].map(e=>e.charCodeAt(0)),r=new Uint8Array(t.length);return r.set(t),new TextDecoder("utf-8").decode(r)}catch(e){return console.error("Failed to decode Base64 string: ",e),null}}(e.getAttribute("data-encoded-email"));t?e.setAttribute("href","mailto:"+t):e.style.display="none"})}();
diff --git a/templates/partials/footer.html b/templates/partials/footer.html
index 35d6f17..bc0dd04 100644
--- a/templates/partials/footer.html
+++ b/templates/partials/footer.html
@@ -10,6 +10,34 @@
{%- endif -%}
+
+ {# Mail icon #}
+ {%- if config.extra.email -%}
+ {%- set email_already_encoded = (config.extra.email is not containing("@")) -%}
+ {%- set email_needs_decoding = email_already_encoded or config.extra.encode_plaintext_email -%}
+
+ {%- if email_already_encoded -%}
+ {%- set encoded_email = config.extra.email -%}
+ {# Verify the pre-encoded e-mail is valid (i.e. contains an '@') #}
+ {%- set decoded_email = encoded_email | base64_decode -%}
+ {%- if '@' not in decoded_email -%}
+ {{ throw(message="ERROR: The provided e-mail appears to be base64-encoded, but does not decode to a valid e-mail address.")}}
+ {%- endif -%}
+ {%- elif config.extra.encode_plaintext_email -%}
+ {%- set encoded_email = config.extra.email | base64_encode -%}
+ {%- endif -%}
+
+
+ {%- if email_needs_decoding -%}
+
+ {%- else -%}
+
+ {%- endif -%}
+
+
+
+ {%- endif -%}
+
{% for social in config.extra.socials %}
@@ -24,4 +52,9 @@
{%- if lang != config.default_language %} {{ trans(key="powered_by" | safe, lang=lang) }} {% else %} Powered by {% endif %} Zola {%- if lang != config.default_language %} {{ trans(key="and" | safe, lang=lang) }} {% else %} & {% endif %} tabi
+
+ {# Load the decoding script if email is encoded #}
+ {%- if email_needs_decoding -%}
+
+ {%- endif -%}