Compare commits

19 Commits

Author SHA1 Message Date
3e29592b48 Updated build packages, Updated png analyser page
Update package-lock.json and png-analyser.jinja
2025-09-08 00:38:18 +02:00
30da615199 Updated VAT calculator, fixed minor TLD issues, Added strings, Optimized assets
Update sidebar.yml, commons.yml, and 10 more files...
2025-09-08 00:15:59 +02:00
615affcc2d Added VAT calculator tool and applet, Cleaned up launch logs and DOM trash
Update .gitignore, vat-calculator.yml, and 17 more files...
2025-09-07 20:52:02 +02:00
eb2ffa296b Added uppercase option to UUID generator
Update uuid-generator.yml, uuid-generator.yml, and 2 more files...
2025-08-25 21:23:24 +02:00
41286a8253 Updated png and bmp related-libs, Updated home page and sidebar
Update sidebar.yml, home.yml, and 7 more files...
2025-08-25 00:03:01 +02:00
a20b45d86e Update IbanGenerator and UUIDGenerator styles, Updated standalone style
Update extra.css, nibblepoker.min.css, and 2 more files...
2025-08-25 00:00:15 +02:00
690b3179ed Updated and fixed IbanGenerator tool
Update purebasic-structure-on-stack.md, iban-generator.yml, and 4 more files...
2025-08-24 18:55:54 +02:00
b11ed247dc Updated standalone stylesheet to prevent missing grid PNG fetches
Update nibblepoker.min.css
2025-08-24 17:16:21 +02:00
c58cb8a405 Implemented CRC32-IEEE for PNG files
Update crc32.mjs, data-utils.mjs, and png-utils.mjs
2025-04-03 23:52:14 +02:00
3e2b917d21 Updated build scripts, removed trash
Update tools.json, tools.json, and 3 more files...
2025-04-03 21:55:37 +02:00
df93ee47b2 Re-enabled stubs for PNG analyser and ICO maker, Implemented Data utils, file utils and PNG parser, Still testing
Update png-analyser.yml, png-chunk-analyser.yml, and 17 more files...
2025-04-03 21:55:09 +02:00
92d7b245a2 Fixed bug in IBAN generator that ignored sepa and non-sepa inclusion/exclusion
Update iban-generator.mjs
2025-03-30 22:03:13 +02:00
4f11e82290 Added theme color meta tags
Update base_standalone.jinja and base_www.jinja
2025-03-30 22:02:41 +02:00
cca310eeac Updated privacy policy, Added missing old privacy files, Updated home text
Update home.yml, privacy.yml, and 16 more files...
2025-03-30 18:20:32 +02:00
9eba329603 Added basic opengraph support to project and tools pages
Update excel-password-remover.yml, iban-generator.yml, and 9 more files...
2025-03-30 16:56:43 +02:00
ff0fa72a4b Fixed missing translations on surrounding DOM
Update base_www.jinja and _project.jinja
2025-03-30 16:09:01 +02:00
6276a74f30 Implemented filtering and ordering for projects and tools
Update app.py, circuitpython-ebyte-e32.yml, and 9 more files...
2025-03-30 15:39:21 +02:00
accdf741f1 Added packaging script
Update .gitignore and package-for-release.cmd
2025-03-30 15:17:21 +02:00
4d80ed906f Merge pull request #2 from aziascreations/dev-full-revamp
Complete refactoring/revamp
2025-03-30 14:54:00 +02:00
88 changed files with 3023 additions and 468 deletions

2
.gitignore vendored
View File

@@ -4,6 +4,7 @@
# Build artifacts
__pycache__/
*.tar
# NodeJS' BS
node_modules/
@@ -13,5 +14,6 @@ node_modules/
*.ai
# Temp
static/resources/DecimalJs*
static/resources/SortableJS
static/resources/Standalone

9
app.py
View File

@@ -9,7 +9,8 @@ from flask import render_template
from werkzeug.exceptions import HTTPException
from website.content import get_projects, get_tools, sanitize_input_tags, load_content_items, get_content, \
get_applets, get_projects_languages, get_projects_by_languages
get_applets, get_projects_languages, get_projects_by_languages, get_sorted_tools_by_tags, \
get_sorted_projects_by_tags
from website.contributors import reload_contributors_data
from website.domains import ALLOWED_DOMAINS
from website.l10n.utils import get_user_lang, localize, reload_strings, l10n_url_abs, l10n_url_switch, DEFAULT_LANG
@@ -119,9 +120,11 @@ def inject_processors():
get_applets=get_applets,
# get_articles=get_articles,
get_projects=get_projects,
get_projects_by_languages=get_projects_by_languages,
get_projects_languages=get_projects_languages,
# get_projects_by_languages=get_projects_by_languages,
# get_projects_languages=get_projects_languages,
get_tools=get_tools,
get_sorted_projects_by_tags=get_sorted_projects_by_tags,
get_sorted_tools_by_tags=get_sorted_tools_by_tags,
# Renderers
render_button=render_button,

View File

@@ -0,0 +1,7 @@
applets:
- id: "png-analyser"
resources:
scripts:
- "applet://png-analyser.mjs"
stylesheets:
- "applet://png-analyser.css"

View File

@@ -1,7 +0,0 @@
applets:
- id: "png-chunk-analyser"
resources:
scripts:
- "applet://png-chunk-analyser.mjs"
stylesheets:
- "applet://png-chunk-analyser.css"

View File

@@ -0,0 +1,7 @@
applets:
- id: "vat-calculator"
resources:
scripts:
- "applet://vat-calculator.mjs"
stylesheets:
- "applet://vat-calculator.css"

View File

@@ -16,7 +16,7 @@ projects:
title_key: "meta.title"
description_key: "meta.description"
index:
priority: 105
priority: 3000
enable: true
title_key: "meta.title"
preamble_key: "meta.description"

View File

@@ -16,7 +16,7 @@ projects:
title_key: "meta.title"
description_key: "meta.description"
index:
priority: 105
priority: 1000
enable: true
title_key: "meta.title"
preamble_key: "meta.description"

View File

@@ -16,7 +16,7 @@ projects:
title_key: "meta.title"
description_key: "meta.description"
index:
priority: 105
priority: 2000
enable: true
title_key: "meta.title"
preamble_key: "meta.description"

View File

@@ -14,35 +14,41 @@
#
#-
- title_key: text.applications
abs_href: "/content/?tags=application;web"
icon: fad fa-browser
active_id: application
- title_key: text.projects
abs_href: "/content/?tags="
icon: fad fa-briefcase
active_id: projects
has_new_until_utc: 0
- title_key: text.libraries
abs_href: "/content/?tags=library"
icon: fad fa-puzzle-piece
active_id: library
has_new_until_utc: 0
#- title_key: text.applications
# abs_href: "/content/?tags=application;web"
# icon: fad fa-browser
# active_id: application
# has_new_until_utc: 0
#
#- title_key: text.libraries
# abs_href: "/content/?tags=library"
# icon: fad fa-puzzle-piece
# active_id: library
# has_new_until_utc: 0
#
#- title_key: text.electronics
# abs_href: "/content/?tags=electronic"
# icon: fad fa-microchip
# active_id: electronic
# has_new_until_utc: 0
- title_key: text.electronics
abs_href: "/content/?tags=electronic"
icon: fad fa-microchip
active_id: electronic
has_new_until_utc: 0
- title_key: text.experiments
abs_href: "/content/?tags=experiments"
icon: fad fa-flask-vial
active_id: electronic
has_new_until_utc: 0
#- title_key: text.experiments
# abs_href: "/content/?tags=experiments"
# icon: fad fa-flask-vial
# active_id: electronic
# has_new_until_utc: 0
- title_key: text.tools
abs_href: "/tools"
icon: fad fa-toolbox
active_id: tools
has_new_until_utc: 1760986472
has_new_until_utc: 1759856025
- title_key: text.downloads
raw_href: "https://files.nibblepoker.lu/"
@@ -52,11 +58,11 @@
-
- title_key: text.about
abs_href: "/about"
icon: fad fa-user
active_id: about
has_new_until_utc: 0
#- title_key: text.about
# abs_href: "/about"
# icon: fad fa-user
# active_id: about
# has_new_until_utc: 0
- title_key: text.contact
abs_href: "/contact"

View File

@@ -14,3 +14,4 @@
- "/tools/iban-generator/"
- "/tools/excel-password-remover/"
- "/tools/uuid-generator/"
- "/tools/vat-calculator/"

View File

@@ -91,3 +91,48 @@ format.json: "JSON"
format.yaml: "YAML"
action.generate: "Generate"
country.afghanistan: "Afghanistan"
country.albania: "Albania"
country.algeria: "Algeria"
country.andorra: "Andorra"
country.angola: "Angola"
country.anguilla: "Anguilla"
country.argentina: "Argentina"
country.australia: "Australia"
country.austria: "Austria"
country.azerbaijan: "Azerbaijan"
country.azores: "Azores"
country.bahamas: "Bahamas"
country.belgium: "Belgium"
country.bulgaria: "Bulgaria"
country.corsica: "Corsica"
country.croatia: "Croatia"
country.cyprus: "Cyprus"
country.czechia: "Czechia"
country.denmark: "Denmark"
country.estonia: "Estonia"
country.finland: "Finland"
country.france: "France"
country.france.corsica: "France - Corsica"
country.germany: "Germany"
country.greece: "Greece"
country.hungary: "Hungary"
country.ireland: "Ireland"
country.italy: "Italy"
country.latvia: "Latvia"
country.lithuania: "lithuania"
country.luxembourg: "Luxembourg"
country.madeira: "Madeira"
country.malta: "Malta"
country.monaco: "Monaco"
country.netherlands: "Netherlands"
country.poland: "Poland"
country.portugal: "Portugal"
country.portugal.azores: "Portugal - Azores"
country.portugal.madeira: "Portugal - Madeira"
country.romania: "Romania"
country.slovenia: "Slovenia"
country.slovakia: "Slovakia"
country.spain: "Spain"
country.sweden: "Sweden"

View File

@@ -2,9 +2,13 @@
meta.title: Excel Password Remover
meta.description: >-
Small web page from which you can easily remove a password from an Excel
Small web application from which you can easily remove a password from an Excel
worksheet. It works by leaving the task of editing the XML files on an Excel
document to your browser instead to keep everything local.
meta.description.light: >-
Small web application from which you can easily remove a password from an Excel
worksheet directly in your browser.
#article.subtitle: >-
# <a
# href="https://github.com/aziascreations/Excel-Worksheet-Password-Remover"><i

View File

@@ -23,20 +23,24 @@ updates.title: Updates
updates.text.privacy: Updated our privacy policy.
updates.5.date: March 30 2025
updates.5.text.1: Complete redesign of the site to use a standardized style.
updates.5.text.2: Changed some of our VPS providers to <a href="https://hosteam.pl/">HosTeam</a>.
updates.4.date: November 30 2023
updates.4.text.1: Centralized DNS servers & implemented GeoDNS.
updates.4.text.2: Added US CDN hosted by <a href="https://www.chicagovps.net/">ChicagoVPS</a>.
updates.4.text.3: All other regions use the EU CDN hosted by <a href="https://www.ionos.fr/">IONOS</a>.
updates.4.text.3: All other regions use the EU CDN hosted by IONOS.
updates.3.date: November 12 2023
updates.3.text.1: Other services are back online.
updates.3.text.2: Changed our host to <a href="https://www.ionos.fr/">IONOS</a>.
updates.3.text.2: Changed our host to IONOS.
updates.3.text.3: Finished all side pages.
updates.2.date: August 15 2023
updates.2.text.1: The website is back online.
updates.2.text.2: New and much lighter design.
updates.2.text.3: Changed our host to <a href="https://hostbrr.com/">HostBrr</a>.
updates.2.text.3: Changed our host to HostBrr.
updates.2.text.4: Added a section for web-based tools.
updates.1.date: September 9 2022

View File

@@ -1,16 +1,26 @@
# IBAN Generator - EN
meta.title: "IBAN Generator"
meta.description: "Web application that allows you to generate IBANs in bulk for the 89 countries that support them.<br>
The IBANs are generated completely randomly and may contain a 'BankID' that has not yet been assigned by the
national bank of the respective country."
meta.description.light: "Web application that allows you to generate IBANs in bulk for the 89 countries that support them."
country.label: "Country"
option.count: "IBAN Count"
option.human.readable: "Format for readability"
option.prefer.numbers: "Prefer numbers over letters"
option.for.each: "Generate <i>X</i> for each country"
option.sepa.enable: "Enable SEPA countries"
option.non-sepa.enable: "Enable non-SEPA countries"
option.prefer.random: "Do not prefer letters nor numbers"
option.prefer.numbers: "Prefer numbers over letters"
option.prefer.letters: "Prefer letters over numbers"
option.human.format.none: "No formatting"
option.human.format.standard: "Use recommended spacing"
option.human.format.4by4: "Use 4 character-wide spacing"
license.1: "The code for this project is released in the public domain."
license.2: "The original source code can be found on <a href=\"https://github.com/aziascreations/Web-NibblePoker\">GitHub</a>."
license.3: "Data from Swift's

View File

@@ -4,7 +4,8 @@ meta.title: "Icon Maker"
meta.description: "..."
file.selection.title: "File Selection"
file.selection.1: "Drop your file(s) here or click on the buttons."
enable.expert.mode: "Enable expert mode"
enable.binary.blobs: "Allow binary blobs"

View File

@@ -89,19 +89,27 @@ v2.update.history.5.desc.1: Added a section regarding data collection through
DNS servers.
v2.update.history.5.desc.2: Changed references to external services to reflect
the usage of ChicagoVPS
v2.update.history.6.date: 2025/03/30
v2.update.history.6.desc.1: Simplification of the 'Third Parties' section.
v2.update.history.6.desc.2: Removal of unnecessary mentions of our <abbr title="Virtual Private Server">VPS</abbr> providers.
v2.update.end.2: In the event of a change to our privacy policy, you will
be informed explicitly, and a copy of previous versions of the policy will be available
through this page.
v2.third.title: Third Parties
v2.third.intro.1: Our websites uses some <abbr title="Virtual private server">VPS</abbr>
provided by IONOS and ChicagoVPS in order to put in place a <abbr title="Content
delivery network">CDN</abbr> system.
v2.third.intro.2: The goal of this system is to improve your browsing experience
with the help of a private caching service and custom traffic filtering rules.
v2.third.intro.3: No data should be collected on their side due to the nature
of the server leased to us.
v2.third.intro.4: 'If you''d wish to consult their privacy policy and their
partners'', you can do so by using the following URLs:'
#v2.third.intro.1: Our websites uses some <abbr title="Virtual private server">VPS</abbr>
# provided by IONOS and ChicagoVPS in order to put in place a <abbr title="Content
# delivery network">CDN</abbr> system.
#v2.third.intro.2: The goal of this system is to improve your browsing experience
# with the help of a private caching service and custom traffic filtering rules.
#v2.third.intro.3: No data should be collected on their side due to the nature
# of the server leased to us.
#v2.third.intro.4: 'If you''d wish to consult their privacy policy and their
# partners'', you can do so by using the following URLs:'
v3.third.intro.1: "Our services do not share any data with any third parties whatsoever."
v2.cookies.title: Cookies
v2.cookies.intro.1: Our websites doesn't use nor store any cookies in your
browser.

View File

@@ -1,7 +0,0 @@
{
"tools.head.title": "Tools - NibblePoker",
"tools.head.description": "TODO: description",
"tools.og.title": "NibblePoker - Tools",
"tools.og.description": "TODO: description",
"tools.header.title": "Tools"
}

View File

@@ -1,6 +1,7 @@
# EN - UUID Generator
meta.title: "UUID Generator"
meta.description: "Web application that allows you to generate UUIDs in bulk for all your unique identifier needs."
type.label: "UUID Type"
@@ -9,6 +10,7 @@ type.uuid4: "UUID4 / GUID4"
option.count: "UUID Count"
option.hyphen: "Add hyphens"
option.guid_brackets: "Add GUID brackets"
option.uppercase: "Generate in uppercase"
generate: "Generate"

View File

@@ -0,0 +1,52 @@
# EN - VAT Calculator
meta.title: "VAT Calculator"
meta.description: "Simple VAT calculator with a selection of common rates for 32 countries/regions."
preset.label: "Official rates"
option.detailed: "Show VAT rate types"
radio.rate: "VAT rate: "
radio.untaxed: "Excl. VAT: "
radio.taxed: "Incl. VAT: "
text.radio.explanation: "The selected radio input indicates the automatically calculated field."
rate.option.custom: "Custom rate"
rate.type.standard: "Standard"
rate.type.intermediate: "Intermediate"
rate.type.preferential: "Preferential"
rate.type.reduced: "Reduced"
rate.type.reduced.super: "Super reduced"
rate.type.special: "Special"
option.decimal-places: "Decimal places count"
option.trim-zeroes: "Trim trailing zeroes"
rounding.mode.label: "Rounding mode"
rounding.mode.group.regular: "Regular rounding"
rounding.mode.group.half: "Half rounding&emsp;(Towards nearest neighbour)"
rounding.mode.up: "Away from zero&emsp;(Up)"
rounding.mode.down: "Towards zero&emsp;(Down)"
rounding.mode.ceil: "Towards infinity&emsp;(Ceil)"
rounding.mode.floor: "Towards negative infinity&emsp;(Floor)"
rounding.mode.up.half: "Away from zero if equidistant&emsp;(Half up)"
rounding.mode.down.half: "Towards zero if equidistant&emsp;(Half down)"
rounding.mode.even.half: "Towards even neighbour if equidistant&emsp;(Half even)"
rounding.mode.ceil.half: "Towards infinity if equidistant&emsp;(Half ceil)"
rounding.mode.floor.half: "Towards negative infinity if equidistant&emsp;(Half floor)"
#rounding.mode.up.half: "Towards nearest neighbour, away from zero if equidistant&emsp;(Half up)"
#rounding.mode.down.half: "Towards nearest neighbour, towards zero if equidistant&emsp;(Half down)"
#rounding.mode.even.half: "Towards nearest neighbour, towards even neighbour if equidistant&emsp;(Half even)"
#rounding.mode.ceil.half: "Towards nearest neighbour, towards infinity if equidistant&emsp;(Half ceil)"
#rounding.mode.floor.half: "Towards nearest neighbour, towards negative infinity if equidistant&emsp;(Half floor)"
license.text.1: "This tool uses the <a href=\"https://github.com/MikeMcl/decimal.js-light\">decimal.js-light</a>
library, which is licensed under the <a href=\"https://github.com/MikeMcl/decimal.js-light/blob/master/LICENCE.md\">MIT license</a>."
license.text.2: "The rest of this tool's code is released in the <a href=\"https://github.com/aziascreations/Web-NibblePoker\">public domain</a>."

View File

@@ -91,3 +91,48 @@ format.json: "JSON"
format.yaml: "YAML"
action.generate: "Générer"
country.afghanistan: "Afghanistan"
country.albania: "Albanie"
country.algeria: "Algérie"
country.andorra: "Andorre"
country.angola: "Angola"
country.anguilla: "Anguilla"
country.argentina: "Argentine"
country.australia: "Australie"
country.austria: "Autriche"
country.azerbaijan: "Azerbaïdjan"
country.azores: "Açores"
country.bahamas: "Bahamas"
country.belgium: "Belgique"
country.bulgaria: "Bulgarie"
country.corsica: "Corse"
country.croatia: "Croatie"
country.cyprus: "Chypre"
country.czechia: "Tchéquie"
country.denmark: "Danemark"
country.estonia: "Estonie"
country.finland: "Finlande"
country.france: "France"
country.france.corsica: "France - Corse"
country.germany: "Allemagne"
country.greece: "Grèce"
country.hungary: "Hongrie"
country.ireland: "Irlande"
country.italy: "Italie"
country.latvia: "Lettonie"
country.lithuania: "Lituanie"
country.luxembourg: "Luxembourg"
country.madeira: "Madère"
country.malta: "Malte"
country.monaco: "Monaco"
country.netherlands: "Pays-Bas"
country.poland: "Pologne"
country.portugal: "Portugal"
country.portugal.azores: "Portugal - Açores"
country.portugal.madeira: "Portugal - Madère"
country.romania: "Roumanie"
country.slovenia: "Slovénie"
country.slovakia: "Slovaquie"
country.spain: "Espagne"
country.sweden: "Suède"

View File

@@ -6,6 +6,10 @@ meta.description: >-
feuille de calcul Excel depuis votre navigateur web sans avoir à uploader le
fichier sur internet. Cette application laisse votre navigateur modifier les
fichiers XML du fichier Excel afin de tout garder en local.
meta.description.light: >-
Application web qui permet de facilement retirer le mot de passe d'un ficher Excel depuis
votre navigateur web sans avoir à uploader le fichier sur internet.
#article.subtitle: >-
# <a
# href="https://github.com/aziascreations/Excel-Worksheet-Password-Remover"><i

View File

@@ -15,7 +15,7 @@ header.title: Page d'accueil
intro.title: Bienvenue sur %0
intro.text.1: Ce site web contient une collection de mes travaux personnels tels
que des articles de blog, des logiciels utilitaires ou d'autres formes de médias.<br>Tout
est accessible gratuitement et sous des licences à l'open source.
est accessible gratuitement et sous des licences open source.
intro.text.2: Si vous souhaitez me contacter, vous pouvez le faire via la page
de contact lié dans la barre de navigation latérale.
@@ -24,21 +24,24 @@ showcase.title: Vitrine
updates.title: Updates
updates.text.privacy: Mise-à-jour de notre politique de confidentialité.
updates.5.date: 30 mars 2025
updates.5.text.1: Refonte complète du site pour utiliser un style standardisé.
updates.5.text.2: Changements de certains providers de VPS pour <a href="https://hosteam.pl/">HosTeam</a>.
updates.4.date: 30 novembre 2023
updates.4.text.1: Centralisation des serveurs DNS & implémentation de GeoDNS.
updates.4.text.2: Ajout d'un CDN pour l'Amérique du Nord hébergé par <a href="https://www.chicagovps.net/">ChicagoVPS</a>.
updates.4.text.3: Les autres régions utilisent le CDN Européen hébergé par <a
href="https://www.ionos.fr/">IONOS</a>.
updates.4.text.3: Les autres régions utilisent le CDN Européen hébergé par IONOS.
updates.3.date: 12 novembre 2023
updates.3.text.1: Les services annexes sont disponibles.
updates.3.text.2: Changement d'hébergeur vers <a href="https://www.ionos.fr/">IONOS</a>.
updates.3.text.2: Changement d'hébergeur vers IONOS.
updates.3.text.3: Finition des pages annexes.
updates.2.date: 15 août 2023
updates.2.text.1: Le site internet est à nouveau disponible.
updates.2.text.2: Mise en place d'un nouveau design plus léger.
updates.2.text.3: Changement d'hébergeur vers <a href="https://hostbrr.com/">HostBrr</a>.
updates.2.text.3: Changement d'hébergeur vers HostBrr.
updates.2.text.4: Ajout d'une nouvelle section pour les outils.
updates.1.date: 9 septembre 2022

View File

@@ -1,16 +1,26 @@
# IBAN Generator - FR
meta.title: "Générateur d'IBAN"
meta.description: "Application web qui vous permet de générer des IBANs en masse pour les 89 pays qui les supportent.<br>
Les IBANs sont générés complètement aléatoirement et risquent de contenir un 'BankID' qui n'est pas encore attribué
par la banque nationale du pays concerné."
meta.description.light: "Application web qui vous permet de générer des IBANs en masse pour les 89 pays qui les supportent."
country.label: "Pays"
option.count: "Nombre d'IBAN"
option.human.readable: "Formatter pour lecture"
option.prefer.numbers: "Favoriser les nombres aux lettres"
option.for.each: "Générer <i>X</i> pour chaque pays"
option.sepa.enable: "Activer les pays SEPA"
option.non-sepa.enable: "Activer les pays non-SEPA"
option.prefer.random: "Pas de favorisation des lettres ou nombres"
option.prefer.numbers: "Favoriser les nombres aux lettres"
option.prefer.letters: "Favoriser les lettres aux nombres"
option.human.format.none: "Aucun formatage"
option.human.format.standard: "Utiliser le format standard des pays"
option.human.format.4by4: "Formater en segments de 4 caractères"
license.1: "Le code de ce projet est publié dans le domaine public."
license.2: "Le code source original peut être trouvé sur <a href=\"https://github.com/aziascreations/Web-NibblePoker\">GitHub</a>."
license.3: "Les données du

View File

@@ -4,7 +4,8 @@ meta.title: "Fabricateur d'icônes"
meta.description: "..."
file.selection.title : "Sélection de fichier(s)"
file.selection.1 : "Déposez vos fichiers ici ou cliquez sur les boutons."
enable.expert.mode: "Activer le mode expert"
enable.binary.blobs: "Autoriser les blobs binaires"

View File

@@ -95,19 +95,27 @@ v2.update.history.5.desc.1: Ajout de la section sur la collection des données
par le serveur DNS.
v2.update.history.5.desc.2: Changement des références aux services externes
pour indiquer l'utilisation de ChicagoVPS.
v2.update.history.6.date: 2025/03/30
v2.update.history.6.desc.1: Simplification de la section 'Organismes tiers'.
v2.update.history.6.desc.2: Suppression des mentions inutiles de nous fournisseurs de <abbr title="Serveur privé virtuel">VPS</abbr>.
v2.update.end.2: En cas de changement, vous serez clairement informé et une
copie des anciennes versions de notre politique sera disponible au travers de cette
page.
v2.third.title: Organismes tiers
v2.third.intro.1: Ce site web utilise des <abbr title="Serveur privé virtuel">VPS</abbr>
proposés par IONOS et ChicagoVPS dans le but de mettre en place un système de <abbr
title="Réseau de diffusion de contenu (CDN)">RDC</abbr>.
v2.third.intro.2: Ceci a pour but d'améliorer l'expérience des personnes le
visitant grâce à un système de filtrage et caching privé.
v2.third.intro.3: Due à la nature des serveurs loués, aucune donnée ne devraient
être collectées de leur côté.
v2.third.intro.4: 'Si vous souhaitez consulter leur politique de confidentialité
ainsi que celle de leur partenaires, vous pouvez le faire en suivant les liens ci-dessous:'
#v2.third.intro.1: Ce site web utilise des <abbr title="Serveur privé virtuel">VPS</abbr>
# proposés par IONOS et ChicagoVPS dans le but de mettre en place un système de <abbr
# title="Réseau de diffusion de contenu (CDN)">RDC</abbr>.
#v2.third.intro.2: Ceci a pour but d'améliorer l'expérience des personnes le
# visitant grâce à un système de filtrage et caching privé.
#v2.third.intro.3: Due à la nature des serveurs loués, aucune donnée ne devraient
# être collectées de leur côté.
#v2.third.intro.4: 'Si vous souhaitez consulter leur politique de confidentialité
# ainsi que celle de leur partenaires, vous pouvez le faire en suivant les liens ci-dessous:'
v3.third.intro.1: "Nos services ne partagent aucune donnée avec des tiers, quels qu'ils soient."
v2.cookies.title: Cookies de navigation
v2.cookies.intro.1: Ce site web n'utilise pas, et ne stocke aucun cookies
dans votre navigateur internet.

View File

@@ -1,7 +0,0 @@
{
"tools.head.title": "Outils - NibblePoker",
"tools.head.description": "TODO: description",
"tools.og.title": "NibblePoker - Outils",
"tools.og.description": "TODO: description",
"tools.header.title": "Outils"
}

View File

@@ -1,6 +1,7 @@
# FR - UUID Generator
meta.title: "Générateur d'UUID"
meta.description: "Application web qui vous permet de générer des UUID en masse pour tout vos besoins en identifiants uniques."
type.label: "Type d'UUID"
@@ -9,6 +10,7 @@ type.uuid4: "UUID4 / GUID4"
option.count: "Nombre d'UUID/GUID"
option.hyphen: "Ajouter trait d'union"
option.guid_brackets: "Ajouter accolades pour GUID"
option.uppercase: "Générer en majuscules"
generate: "Générer"

View File

@@ -0,0 +1,52 @@
# FR - VAT Calculator
meta.title: "Calculateur de TVA"
meta.description: "Simple calculateur de TVA avec une selection de taux communs pour 32 pays/régions."
preset.label: "Taux officiel"
option.detailed: "Afficher le type de TVA"
radio.rate: "Taux: "
radio.untaxed: "<abbr title=\"Hors Taxe sur la Valeur Ajoutée\">HTVA</abbr>: "
radio.taxed: "<abbr title=\"Toutes Taxes Comprises\">TTC</abbr>: "
text.radio.explanation: "Le cercle en vert indique la valeur automatiquement calculée."
rate.option.custom: "Taux personalisé"
rate.type.standard: "Standard"
rate.type.intermediate: "Intermédiaire"
rate.type.preferential: "Préférentiel"
rate.type.reduced: "Réduit"
rate.type.reduced.super: "Super réduit"
rate.type.special: "Spécial"
option.decimal-places: "Nombre de décimales"
option.trim-zeroes: "Supprimer les zéros de fin"
rounding.mode.label: "Type d'arrondi"
rounding.mode.group.regular: "Arrondis classiques"
rounding.mode.group.half: "Demi-arrondis&emsp;(Vers le plus proche)"
rounding.mode.up: "Loin de zéro&emsp;(Arrondi au supérieur)"
rounding.mode.down: "Vers zéro&emsp;(Arrondi à l'inférieur)"
rounding.mode.ceil: "Vers l'infini&emsp;(Par plafond)"
rounding.mode.floor: "Vers moins l'infini&emsp;(Par plancher)"
rounding.mode.up.half: "Loin de zéro si équidistant&emsp;(Demi supérieur)"
rounding.mode.down.half: "Vers zéro si équidistant&emsp;(Demi inférieur)"
rounding.mode.even.half: "Vers le pair si équidistant&emsp;(Demi pair)"
rounding.mode.ceil.half: "Vers l'infini&emsp;(Demi plafond)"
rounding.mode.floor.half: "Vers moins l'infini&emsp;(Demi plancher)"
#rounding.mode.up.half: "Vers le plus proche, loin de zéro si équidistant&emsp;(Demi supérieur)"
#rounding.mode.down.half: "Vers le plus proche, vers zéro si équidistant&emsp;(Demi inférieur)"
#rounding.mode.even.half: "Vers le plus proche, vers le pair si équidistant&emsp;(Demi pair)"
#rounding.mode.ceil.half: "Vers le plus proche, vers l'infini&emsp;(Demi plafond)"
#rounding.mode.floor.half: "Vers le plus proche, vers moins l'infini&emsp;(Demi plancher)"
license.text.1: "Cet outil utilise la bibliothèque <a href=\"https://github.com/MikeMcl/decimal.js-light\">decimal.js-light</a>,
qui est distribuée sous licence <a href=\"https://github.com/MikeMcl/decimal.js-light/blob/master/LICENCE.md\">MIT</a>."
license.text.2: "Le reste du code de cet outil est placé dans le <a href=\"https://github.com/aziascreations/Web-NibblePoker\">domaine public</a>."

View File

@@ -5,10 +5,10 @@ tools:
metadata:
head:
title_key: "meta.title"
description_key: "meta.description"
description_key: "meta.description.light"
opengraph:
title_key: "meta.title"
description_key: "meta.description"
description_key: "meta.description.light"
type: null
url: null
image_url: "/resources/NibblePoker/images/tools/excel-password-remover/main.png"
@@ -17,7 +17,7 @@ tools:
title_key: "meta.title"
description_key: "meta.description"
index:
priority: 100
priority: 2000
enable: true
title_key: "meta.title"
preamble_key: "meta.description"

View File

@@ -5,10 +5,10 @@ tools:
metadata:
head:
title_key: "meta.title"
description_key: "meta.description"
description_key: "meta.description.light"
opengraph:
title_key: "meta.title"
description_key: "meta.description"
description_key: "meta.description.light"
type: null
url: null
image_url: "/resources/NibblePoker/images/tools/iban-generator/main.png"
@@ -17,14 +17,14 @@ tools:
title_key: "meta.title"
description_key: "meta.description"
index:
priority: 100
priority: 3000
enable: true
title_key: "meta.title"
preamble_key: "meta.description"
image_url: "/resources/NibblePoker/images/tools/iban-generator/main.png"
image_alt_key: ""
general:
icon: "fa-duotone fa-solid fa-credit-card-front"
icon: "fa-duotone fa-credit-card-front"
title_key: "meta.title"
subtitle_key: "article.subtitle"
tags:

View File

@@ -16,7 +16,7 @@ tools:
title_key: "meta.title"
description_key: "meta.description"
index:
priority: 100
priority: 1500
enable: true
title_key: "meta.title"
preamble_key: "meta.description"
@@ -27,4 +27,5 @@ tools:
title_key: "meta.title"
subtitle_key: "article.subtitle"
tags:
- "undefined"
- "utility"
- "graphics"

View File

@@ -1,6 +1,6 @@
tools:
- id: "png-chunk-analyser"
applet_id: "png-chunk-analyser"
- id: "png-analyser"
applet_id: "png-analyser"
metadata:
head:
title_key: "meta.title"

View File

@@ -17,14 +17,14 @@ tools:
title_key: "meta.title"
description_key: "meta.description"
index:
priority: 100
priority: 1000
enable: true
title_key: "meta.title"
preamble_key: "meta.description"
image_url: "/resources/NibblePoker/images/tools/uuid-generator/main.png"
image_alt_key: ""
general:
icon: "fab fa-python"
icon: "fa-duotone fa-memo-pad"
title_key: "meta.title"
subtitle_key: "article.subtitle"
tags:

View File

@@ -0,0 +1,32 @@
tools:
- id: "vat-calculator"
applet_id: "vat-calculator"
metadata:
head:
title_key: "meta.title"
description_key: "meta.description"
opengraph:
title_key: "meta.title"
description_key: "meta.description"
type: null
url: null
image_url: "/resources/NibblePoker/images/tools/vat-calculator/main-quiet.png"
image_type: null
twitter:
title_key: "meta.title"
description_key: "meta.description"
index:
priority: 500
enable: true
title_key: "meta.title"
preamble_key: "meta.description"
image_url: "/resources/NibblePoker/images/tools/vat-calculator/main-quiet.png"
image_alt_key: ""
general:
icon: "fa-solid fa-memo-pad"
title_key: "meta.title"
subtitle_key: "article.subtitle"
tags:
- "calculator"
- "finance"

View File

@@ -83,6 +83,26 @@ call "%~dp0node_modules\.bin\rollup" uuid-generator.mjs --file uuid-generator.js
call "%~dp0node_modules\.bin\terser" uuid-generator.js -c -m -o uuid-generator.min.js
popd
:js-pnganalyser-minify
echo Minifying PNG Analyzer
pushd %CD%
cd %~dp0\..\static\resources\NibblePoker\applets\png-analyser\
echo ^> static\resources\NibblePoker\applets\png-analyser\png-analyser.mjs
call "%~dp0node_modules\.bin\rollup" png-analyser.mjs --file png-analyser.js
call "%~dp0node_modules\.bin\terser" png-analyser.js -c -m -o png-analyser.min.js
popd
:js-vatcalculator-minify
echo Minifying VAT Calculator
pushd %CD%
cd %~dp0\..\static\resources\NibblePoker\applets\vat-calculator\
echo ^> static\resources\NibblePoker\applets\vat-calculator\vat-calculator.mjs
call "%~dp0node_modules\.bin\rollup" vat-calculator.mjs --file vat-calculator.js
call "%~dp0node_modules\.bin\terser" vat-calculator.js -c -m -o vat-calculator.min.js
popd
:js-nibblepoker-end
:end

View File

@@ -0,0 +1,17 @@
@echo off
pushd %CD%
cd /D "%~dp0"
cd ..\static\resources\NibblePoker
echo.
echo Removing minified JS files
echo --------------------------
echo Deleting "*.js" in "static\resources\NibblePoker" ...
del /S /Q *.js
:end
popd

View File

@@ -0,0 +1,31 @@
@echo off
pushd %CD%
:: Going into the project's directory
cd /D "%~dp0"
call delete-pycache.cmd
cd ..
echo.
echo Creating Release Package
echo ------------------------
echo %CD%
del release.tar 2> nul
7z a "release.tar" ^
-xr!*.pdn ^
-xr!*.ai ^
data/ ^
static/ ^
templates/ ^
website/ ^
.dockerignore ^
.env ^
app.py ^
Dockerfile ^
requirements.txt
popd

View File

@@ -5,9 +5,10 @@
"packages": {
"": {
"devDependencies": {
"browserify": "^17.0.1",
"html-minifier-terser": "^7.2.0",
"minify": "^10.2.0",
"rollup": "^3.27.2",
"rollup": "^4.48.1",
"sass": "^1.63.6",
"terser": "^5.19.0",
"typescript": "^5.1.6"

View File

@@ -1,10 +1,11 @@
{
"devDependencies": {
"minify": "^10.2.0",
"rollup": "^3.27.2",
"rollup": "^4.48.1",
"sass": "^1.63.6",
"terser": "^5.19.0",
"typescript": "^5.1.6",
"html-minifier-terser": "^7.2.0"
"html-minifier-terser": "^7.2.0",
"browserify": "^17.0.1"
}
}

1
static/resources/Externals/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.svg

View File

@@ -30,10 +30,20 @@ import {initCore} from "../../js/nibblepoker-core.mjs";
/** @type {HTMLInputElement} */
const eOptionCount = document.querySelector("input#iban-generator-option-count");
/** @type {HTMLInputElement} */
const eOptionPrettyPrint = document.querySelector("input#iban-generator-option-pretty");
///** @type {HTMLInputElement} */
//const eOptionPreferRandom = document.querySelector("input#iban-generator-option-prefer-random");
/** @type {HTMLInputElement} */
const eOptionPreferNumbers = document.querySelector("input#iban-generator-option-prefer-numbers");
/** @type {HTMLInputElement} */
const eOptionPreferLetters = document.querySelector("input#iban-generator-option-prefer-letters");
/** @type {HTMLInputElement} */
const eOptionFormatNone = document.querySelector("input#iban-generator-option-format-none");
/** @type {HTMLInputElement} */
const eOptionFormatStandard = document.querySelector("input#iban-generator-option-format-standard");
/** @type {HTMLInputElement} */
const eOptionFormat4By4 = document.querySelector("input#iban-generator-option-format-4by4");
/** @type {HTMLElement} */
const eGenerateButton = document.querySelector("#iban-generator-generate");
@@ -75,14 +85,14 @@ import {initCore} from "../../js/nibblepoker-core.mjs";
/** @returns {number} */
function getDesiredCount() {
return getInputCount(eOptionCount, 1, 1000);
return getInputCount(eOptionCount, 1, 10000);
}
function changeDesiredCount(difference = 0) {
if(difference !== 0) {
eOptionCount.value = getDesiredCount(eOptionCount, 1, 1000) + difference;
eOptionCount.value = getDesiredCount(eOptionCount, 1, 10000) + difference;
}
eOptionCount.value = getDesiredCount(eOptionCount, 1, 1000);
eOptionCount.value = getDesiredCount(eOptionCount, 1, 10000);
}
window.onload = function () {
@@ -108,27 +118,58 @@ import {initCore} from "../../js/nibblepoker-core.mjs";
let desiredCount = getDesiredCount();
let preferNumbers = eOptionPreferNumbers.checked;
let prettyIban = eOptionPrettyPrint.checked;
let preferLetters = eOptionPreferLetters.checked;
let ibanFormat = (
eOptionFormatNone.checked ? 0 : (
eOptionFormatStandard.checked ? 1 : (
eOptionFormat4By4.checked ? 2 : 0
)
)
);
/** @type {IbanSpecification[]} */
let targetSpecs;
if(eOptionForEach.checked) {
targetSpecs = Object.values(countriesSpecs);
// BUGFIX: Removing unwanted specs.
if(!eOptionEnableSepa.checked) {
targetSpecs = targetSpecs.filter(ibanSpec => !ibanSpec.isSepa);
}
if(!eOptionEnableNonSepa.checked) {
targetSpecs = targetSpecs.filter(ibanSpec => ibanSpec.isSepa);
}
} else {
targetSpecs = [countriesSpecs[eOptionCountry.value]];
}
targetSpecs.forEach(spec => {
if((spec.isSepa && !eOptionEnableSepa.checked) || (!spec.isSepa && !eOptionEnableNonSepa.checked)) {
return;
}
for(let i = 0; i < desiredCount; i++) {
if(prettyIban) {
if(ibanFormat === 1) {
// standard
lastIBANs.push(
spec.getFormattedIban(
new StandardIban(spec.countryCode, spec.generateRandomBban(preferNumbers), spec).toString()
new StandardIban(spec.countryCode, spec.generateRandomBban(preferNumbers, preferLetters), spec)
.toString()
)
);
} else {
} else if(ibanFormat === 2) {
// 4-by-4
lastIBANs.push(
new StandardIban(spec.countryCode, spec.generateRandomBban(preferNumbers), spec).toString()
new StandardIban(spec.countryCode, spec.generateRandomBban(preferNumbers, preferLetters), spec)
.toString()
.match(/.{1,4}/g)
.join(' ')
);
} else {
// none
lastIBANs.push(
new StandardIban(spec.countryCode, spec.generateRandomBban(preferNumbers, preferLetters), spec)
.toString()
);
}
}

View File

@@ -0,0 +1,8 @@
.ico-maker-advanced {
display: none;
}
input[type="checkbox"]#ico-maker-enable-expert-mode:checked ~ .ico-maker-advanced {
display: block;
}

View File

@@ -0,0 +1,34 @@
import {initCore} from "../../js/nibblepoker-core.mjs"
import {parsePngFile} from "../../libs/png-utils.mjs";
import {parseBmpFile} from "../../libs/bmp-utils.mjs";
{
initCore();
const toolId = "png-analyser";
const eFileInput = document.getElementById(`${toolId}-test-input`);
window.onload = function () {
eFileInput.addEventListener('change', function(e) {
let files = e.target.files;
console.log(files);
if(files[0].name.endsWith(".png")) {
parsePngFile(files[0]).then(pngFile => {
console.log(pngFile);
console.log(pngFile.getImageHeaderChunk().getWidth());
console.log(pngFile.getImageHeaderChunk().getHeight());
});
} else if(files[0].name.endsWith(".bmp")) {
parseBmpFile(files[0]).then(bmpFile => {
console.log(bmpFile);
});
}
});
};
}

View File

@@ -19,6 +19,8 @@ import {initCore} from "../../js/nibblepoker-core.mjs";
const eOptionHyphenInput = document.querySelector("input#uuid-generator-option-hyphens");
/** @type {HTMLInputElement} */
const eOptionGuidBracketsInput = document.querySelector("input#uuid-generator-option-guid-brackets");
/** @type {HTMLInputElement} */
const eOptionUppercaseInput = document.querySelector("input#uuid-generator-option-uppercase");
/** @type {HTMLElement} */
const eGenerateButton = document.querySelector("#uuid-generator-generate");
@@ -40,14 +42,14 @@ import {initCore} from "../../js/nibblepoker-core.mjs";
}
function changeDesiredCount(difference = 0) {
if(difference !== 0) {
if (difference !== 0) {
eOptionCountInput.value = getDesiredCount() + difference;
}
eOptionCountInput.value = getDesiredCount();
}
window.onload = function () {
eGenerateButton.addEventListener("click", function() {
eGenerateButton.addEventListener("click", function () {
ePreviewTextArea.value = "";
let desiredCount = getDesiredCount();
@@ -57,20 +59,26 @@ import {initCore} from "../../js/nibblepoker-core.mjs";
let addGuidBrackets = eOptionGuidBracketsInput.checked;
lastUUIDs = [];
for(let i= 0; i < desiredCount; i++) {
lastUUIDs.push(uuidGenerator(addHyphens, addGuidBrackets));
ePreviewTextArea.value += uuidGenerator(addHyphens, addGuidBrackets) + "\n";
if (eOptionUppercaseInput.checked) {
for (let i = 0; i < desiredCount; i++) {
lastUUIDs.push(uuidGenerator(addHyphens, addGuidBrackets).toUpperCase());
}
} else {
for (let i = 0; i < desiredCount; i++) {
lastUUIDs.push(uuidGenerator(addHyphens, addGuidBrackets));
}
}
ePreviewTextArea.value = lastUUIDs.join("\n");
});
// Count option
eOptionCountInput.addEventListener("change", function() {
eOptionCountInput.addEventListener("change", function () {
changeDesiredCount(0);
});
eOptionCountInput.addEventListener("mousewheel", function(e) {
eOptionCountInput.addEventListener("mousewheel", function (e) {
// Handling wheel scroll on count field.
if(e.wheelDelta < 0) {
if (e.wheelDelta < 0) {
changeDesiredCount(-1);
} else {
changeDesiredCount(1);
@@ -78,19 +86,19 @@ import {initCore} from "../../js/nibblepoker-core.mjs";
});
// Download buttons
eDownloadRawButton.addEventListener("click", function() {
eDownloadRawButton.addEventListener("click", function () {
if (lastUUIDs.length <= 0) {
return;
}
downloadStringAsFile(lastUUIDs.join("\n"), "uuids.txt", "text/plain");
});
eDownloadJsonButton.addEventListener("click", function() {
eDownloadJsonButton.addEventListener("click", function () {
if (lastUUIDs.length <= 0) {
return;
}
downloadStringAsFile(JSON.stringify(lastUUIDs, null, 4), "uuids.json", "application/json");
});
eDownloadYamlButton.addEventListener("click", function() {
eDownloadYamlButton.addEventListener("click", function () {
if (lastUUIDs.length <= 0) {
return;
}

View File

@@ -0,0 +1,248 @@
import {initCore} from "../../js/nibblepoker-core.mjs";
//import {Decimal} from "../../../DecimalJs/10.6.0/decimal.mjs";
import {Decimal} from "../../../DecimalJs-Light/2.5.1/decimal.mjs";
import {getInputCount, getInputNumber} from "../../libs/input-utils.mjs";
// Tool-centric stuff
{
initCore();
const classesReadonly = ["bkgd-gray"];
const calcRadioGroupName = "vat_calc_target";
/** @type {HTMLLabelElement} */
const ePresetShortLabel = document.querySelector("label[for=vat-calculator-preset-short]");
/** @type {HTMLSelectElement} */
const ePresetShortSelect = document.getElementById("vat-calculator-preset-short");
/** @type {HTMLLabelElement} */
const ePresetDetailedLabel = document.querySelector("label[for=vat-calculator-preset-detailed]");
/** @type {HTMLSelectElement} */
const ePresetDetailedSelect = document.getElementById("vat-calculator-preset-detailed");
/** @type {HTMLInputElement} */
const eCheckboxDetailedPreset = document.getElementById("vat-calculator-detailed-presets");
/** @type {HTMLSpanElement} */
const ePresetEchoedCountry = document.getElementById("vat-calculator-preset-country-echo");
/** @type {HTMLButtonElement} */
const eButtonDecimalPlacesMinus = document.getElementById("vat-calculator-decimal-places-minus");
/** @type {HTMLInputElement} */
const eInputDecimalPlaces = document.getElementById("vat-calculator-option-decimal-places");
/** @type {HTMLButtonElement} */
const eButtonDecimalPlacesPlus = document.getElementById("vat-calculator-decimal-places-plus");
/* #vat-calculator-detailed-trim-zeroes */
/** @type {HTMLInputElement} */
const eCalcRateRadio = document.getElementById("vat-calculator-radio-rate");
/** @type {HTMLInputElement} */
const eCalcRateInput = document.getElementById("vat-calculator-input-rate");
/** @type {HTMLInputElement} */
const eCalcUntaxedRadio = document.getElementById("vat-calculator-radio-untaxed");
/** @type {HTMLInputElement} */
const eCalcUntaxedInput = document.getElementById("vat-calculator-input-untaxed");
/** @type {HTMLInputElement} */
const eCalcTaxedRadio = document.getElementById("vat-calculator-radio-taxed");
/** @type {HTMLInputElement} */
const eCalcTaxedInput = document.getElementById("vat-calculator-input-taxed");
/** @type {HTMLSelectElement} */
const eRoundingModeSelect = document.getElementById("vat-calculator-rounding-mode");
/**
* Handles the switch between the short and detailed standard rates selects
*/
function handlePresetDetailLevelChange() {
ePresetShortLabel.hidden = eCheckboxDetailedPreset.checked;
ePresetShortSelect.hidden = eCheckboxDetailedPreset.checked;
ePresetDetailedLabel.hidden = !eCheckboxDetailedPreset.checked;
ePresetDetailedSelect.hidden = !eCheckboxDetailedPreset.checked;
}
/** @returns {number} */
function getDecimalPlaces() {
return getInputCount(eInputDecimalPlaces, 0, 99);
}
function changeDecimalPlacesDesiredCount(difference = 0) {
if (difference !== 0) {
eInputDecimalPlaces.value = getInputCount(eInputDecimalPlaces, 0, 99) + difference;
}
eInputDecimalPlaces.value = getInputCount(eInputDecimalPlaces, 0, 99);
}
/**
* Handles the locking and unlocking of the calculator input fields.
* @param eInput {HTMLInputElement}
* @param isLocked {boolean}
*/
function setCalcFieldLockStatus(eInput, isLocked) {
eInput.readOnly = isLocked;
classesReadonly.forEach((roClass) => {
eInput.classList.remove(roClass);
if(isLocked) {
eInput.classList.add(roClass);
}
});
}
function handleCalcValueChange() {
let vatRate = getInputNumber(eCalcRateInput);
let untaxedValue = getInputNumber(eCalcUntaxedInput);
let taxedValue = getInputNumber(eCalcTaxedInput);
if(eCalcRateRadio.checked) {
if(untaxedValue === null || taxedValue === null || isNaN(untaxedValue) || isNaN(taxedValue)) {
return;
}
untaxedValue = new Decimal(eCalcUntaxedInput.value);
taxedValue = new Decimal(eCalcTaxedInput.value);
eCalcRateInput.value = taxedValue
.minus(untaxedValue)
.div(untaxedValue)
.times(100)
.toDecimalPlaces(getDecimalPlaces());
} else if(eCalcUntaxedRadio.checked) {
if(vatRate === null || taxedValue === null || isNaN(vatRate) || isNaN(taxedValue)) {
return;
}
vatRate = new Decimal(eCalcRateInput.value).dividedBy(100).plus(1);
taxedValue = new Decimal(eCalcTaxedInput.value);
eCalcUntaxedInput.value = taxedValue
.dividedBy(vatRate)
.toDecimalPlaces(getDecimalPlaces());
} else if(eCalcTaxedRadio.checked) {
if(vatRate === null || untaxedValue === null || isNaN(vatRate) || isNaN(untaxedValue)) {
return;
}
vatRate = new Decimal(eCalcRateInput.value).dividedBy(100).plus(1);
untaxedValue = new Decimal(eCalcUntaxedInput.value);
eCalcTaxedInput.value = untaxedValue
.times(vatRate)
.toDecimalPlaces(getDecimalPlaces());
}
}
function handleDecimalConfigChange() {
Decimal.set({
rounding: getInputCount(eRoundingModeSelect, 0, 8),
precision: 99,
defaults: true,
});
}
function handlePresetChange() {
if(ePresetShortSelect.value.length > 0) {
let eSelectedOption = ePresetShortSelect.querySelector('option:checked');
if(eSelectedOption === null) {
return;
}
let eSelectedOptionGroup = eSelectedOption.closest('optgroup');
if(eSelectedOptionGroup === null) {
return;
}
ePresetEchoedCountry.innerHTML = eSelectedOptionGroup.label;
} else {
ePresetEchoedCountry.innerHTML = "";
}
}
window.onload = function () {
// Handling the detailed rate toggle
eCheckboxDetailedPreset.addEventListener("click", function () {
handlePresetDetailLevelChange();
});
// Handling the rate select input
ePresetShortSelect.addEventListener("change", function () {
ePresetDetailedSelect.selectedIndex = ePresetShortSelect.selectedIndex;
eCalcRateInput.value = ePresetShortSelect.value;
handlePresetChange();
handleCalcValueChange();
});
ePresetDetailedSelect.addEventListener("change", function () {
ePresetShortSelect.selectedIndex = ePresetDetailedSelect.selectedIndex;
eCalcRateInput.value = ePresetDetailedSelect.value;
handlePresetChange();
handleCalcValueChange();
});
// Handling calc radio input change
document.addEventListener("change", (e) => {
if (e.target.type === "radio" && e.target.name === calcRadioGroupName) {
setCalcFieldLockStatus(eCalcRateInput, e.target.value === "0");
setCalcFieldLockStatus(eCalcUntaxedInput, e.target.value === "1");
setCalcFieldLockStatus(eCalcTaxedInput, e.target.value === "2");
ePresetDetailedSelect.disabled = e.target === eCalcRateRadio;
ePresetShortSelect.disabled = e.target === eCalcRateRadio;
}
});
eCalcRateRadio.addEventListener("change", function () {
ePresetShortSelect.selectedIndex = 0;
ePresetDetailedSelect.selectedIndex = 0;
handlePresetChange();
});
// Handling decimal places options
eButtonDecimalPlacesMinus.addEventListener("click", function () {
changeDecimalPlacesDesiredCount(-1);
handleDecimalConfigChange();
handleCalcValueChange();
});
eButtonDecimalPlacesPlus.addEventListener("click", function () {
changeDecimalPlacesDesiredCount(1);
handleDecimalConfigChange();
handleCalcValueChange();
});
eInputDecimalPlaces.addEventListener("change", function() {
changeDecimalPlacesDesiredCount(0);
handleDecimalConfigChange();
handleCalcValueChange();
});
eInputDecimalPlaces.addEventListener("mousewheel", function(e) {
// Handling wheel scroll on count field.
if(e.wheelDelta < 0) {
changeDecimalPlacesDesiredCount(-1);
} else {
changeDecimalPlacesDesiredCount(1);
}
handleDecimalConfigChange();
handleCalcValueChange();
});
// Handling other DecimalJs config fields
eRoundingModeSelect.addEventListener("change", function() {
handleDecimalConfigChange();
handleCalcValueChange();
});
// Handling the calculator field changes
eCalcRateInput.addEventListener("change", function() {
ePresetShortSelect.selectedIndex = 0;
ePresetDetailedSelect.selectedIndex = 0;
handleCalcValueChange();
handlePresetChange();
});
eCalcUntaxedInput.addEventListener("change", function() {
handleCalcValueChange();
});
eCalcTaxedInput.addEventListener("change", function() {
handleCalcValueChange();
});
handlePresetDetailLevelChange();
handleDecimalConfigChange();
handlePresetChange();
}
}

View File

@@ -15,22 +15,6 @@ input[type=file].np-file-input-drop {
cursor: pointer;
}
/* Nicer checkboxes, move to CSS 8 */
input[type=checkbox] {
text-align: center;
padding: 0;
&:before {
content: "✘";
}
&:checked {
&:before {
content: "✔";
}
}
}
/* Top margin trimmer for heading renderer */

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1,57 @@
import {loadFileAsUint8Array} from "./file-utils.mjs";
import {peekUInt32BE, peekUInt16BE} from "./data-utils.mjs";
/*export const test = {
WINDOWS: "BM",
OS2_STRUCT_BITMAP_ARRAY: "BA",
OS2_STRUCT_COLOR_ICON: "CI",
OS2_CONST_COLOR_POINTER: "CP",
OS2_STRUCT_ICON: "IC",
OS2_POINTER: "PT",
}*/
export class BmpHeader {
/** @type {Uint8Array} */
data;
/**
* @param byteArray {Uint8Array|null}
*/
constructor(byteArray) {
if(byteArray === null || byteArray === undefined) {
this.data = new Uint8Array(14);
} else {
this.data = byteArray;
}
}
getSignature() {
return peekUInt16BE(this.data, 0);
}
getFileSize() {
return peekUInt32BE(this.data, 2);
}
getReserved() {
return peekUInt32BE(this.data, 6);
}
getDataOffset() {
return peekUInt32BE(this.data, 10);
}
}
export class CrudeBmpFile {
constructor(file = null, fileData = null) {
this.originalFile = file;
}
}
export function parseBmpFile(file) {
return loadFileAsUint8Array(file).then(byteBuffer => {
return new CrudeBmpFile(file, byteBuffer);
});
}

View File

@@ -0,0 +1,33 @@
/**
* @param data {Uint8Array}
* @param crc32 {number}
* @return {number}
*/
export function stepCrc32IEEE(data, crc32 = 0xffffffff) {
for (let i = 0; i < data.length; i++) {
crc32 ^= data[i];
for (let j = 0; j < 8; j++) {
crc32 = (crc32 >>> 1) ^ (crc32 & 1 ? 0xedb88320 : 0);
}
}
return crc32;
}
/**
* @param crc32 {number}
* @return {number}
*/
export function finishCrc32IEEE(crc32) {
return (crc32 ^ 0xffffffff) >>> 0;
}
/**
* Calculates the CRC32-IEEE for the given data
* @param data {Uint8Array}
* @param crc32 {number}
* @return {number} The resulting CRC32
*/
export function crc32IEEE(data, crc32 = 0xffffffff) {
return finishCrc32IEEE(stepCrc32IEEE(data, crc32));
}

View File

@@ -0,0 +1,113 @@
/**
* Compares two UintXArray and checks if their content is the same.
* @param array1 {Uint8Array|Uint16Array|Uint32Array}
* @param array2 {Uint8Array|Uint16Array|Uint32Array}
* @return {boolean}
*/
export function areUintArraysEqual(array1, array2) {
if ((typeof array1) !== (typeof array2)) {
return false;
}
if (array1.length !== array2.length) {
return false;
}
for (let i = 0; i < array1.length; i++) {
if (array1[i] !== array2[i]) {
return false;
}
}
return true;
}
/**
* Peeks a UInt16 from the given data at the given offset in Big-Endian.
* @param data {Uint8Array} - Data to read from.
* @param offset {number} - Offset to read from in the given `data`.
* @return {number} The peeked number.
* @throws RangeError If the given offset is too close or over the end of the data.
*/
export function peekUInt16BE(data, offset = 0) {
if(offset + 2 > data.length) {
throw new RangeError(`Offset is too far into the given data ! (${offset} & ${data.length})`);
}
return new DataView(data.buffer, offset, 2).getUint16(0, false);
}
/**
* Peeks a UInt16 from the given data at the given offset in Little-Endian.
* @param data {Uint8Array} - Data to read from.
* @param offset {number} - Offset to read from in the given `data`.
* @return {number} The peeked number.
* @throws RangeError If the given offset is too close or over the end of the data.
*/
export function peekUInt16LE(data, offset = 0) {
if(offset + 2 > data.length) {
throw new RangeError(`Offset is too far into the given data ! (${offset} & ${data.length})`);
}
return new DataView(data.buffer, offset, 2).getUint16(0, true);
}
/**
* Peeks a UInt32 from the given data at the given offset in Big-Endian.
* @param data {Uint8Array} - Data to read from.
* @param offset {number} - Offset to read from in the given `data`.
* @return {number} The peeked number.
* @throws RangeError If the given offset is too close or over the end of the data.
*/
export function peekUInt32BE(data, offset = 0) {
if(offset + 4 > data.length) {
throw new RangeError(`Offset is too far into the given data ! (${offset} & ${data.length})`);
}
return new DataView(data.buffer, offset, 4).getUint32(0, false);
}
/**
* Peeks a UInt32 from the given data at the given offset in Little-Endian.
* @param data {Uint8Array} - Data to read from.
* @param offset {number} - Offset to read from in the given `data`.
* @return {number} The peeked number.
* @throws RangeError If the given offset is too close or over the end of the data.
*/
export function peekUInt32LE(data, offset = 0) {
if(offset + 4 > data.length) {
throw new RangeError(`Offset is too far into the given data ! (${offset} & ${data.length})`);
}
return new DataView(data.buffer, offset, 4).getUint32(0, true);
}
/**
* Converts a given `Uint8Array` to a hexadecimal representation.
* @param data The data to be transformed into a string.
* @return {string} The resulting hexadecimal string.
*/
export function Uint8ArrayToHex(data) {
return Array.prototype.map.call(data, x => ('00' + x.toString(16)).slice(-2)).join('');
}
/*export function decimalToHexString(number) {
if (number < 0) {
number = 0xFFFFFFFF + number + 1;
}
return number.toString(16).toUpperCase();
}*/
export function AsciiStringToUint8Array(asciiText) {
return Uint8Array.from(Array.from(asciiText).map(asciiLetter => asciiLetter.charCodeAt(0)));
}
export function Int32ToUint8Array(number) {
/*let arr = new ArrayBuffer(4);
new DataView(arr).setUint32(0, number);
return new Uint8Array(arr);*/
return new Uint8Array([
(number >>> 24) & 0xFF,
(number >>> 16) & 0xFF,
(number >>> 8) & 0xFF,
number & 0xFF
]);
}

View File

@@ -0,0 +1,28 @@
/**
* Reads a file and returns its content as a string.
* @param {File} file - The file to read.
* @returns {Promise<string>} A promise that resolves with the file content as a string.
*/
export function loadFileAsText(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsText(file);
});
}
/**
* Reads a file and returns its content as a `Uint8Array`.
* @param {File} file - The file to read.
* @returns {Promise<Uint8Array>} A promise that resolves with the file content as a byte buffer.
*/
export function loadFileAsUint8Array(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(new Uint8Array(reader.result));
reader.onerror = reject;
reader.readAsArrayBuffer(file);
});
}

View File

@@ -269,7 +269,7 @@ export class IbanSpecification {
return iban.match(/.{1,4}/g).join(' ');
}
generateRandomBban(preferNumbers = false) {
generateRandomBban(preferNumbers = false, preferLetters = false) {
let returnedBban = "";
let patternParts = ("_" + this.bbanFormat + "0").split("!");
@@ -291,7 +291,11 @@ export class IbanSpecification {
if(preferNumbers) {
elementChoices = charsN;
} else {
elementChoices = charsC;
if(preferLetters) {
elementChoices = charsA;
} else {
elementChoices = charsC;
}
}
break;
case 'a':

View File

@@ -2,21 +2,7 @@
// Author: Herwin Bozet (@NibblePoker)
// License: Public Domain (This code)
/**
* Retrieves the number from an `HTMLInputElement`
* @param eInput {HTMLInputElement} The `HTMLInputElement` from which the value will be retrieved.
* @param min {number|null} If given, sets a minimum the value can have when returned.
* @param max {number|null} If given, sets a maximum the value can have when returned.
* @returns {number} The value from the given `HTMLInputElement`, or `1` if no valid one was given.
*/
export function getInputCount(eInput, min = null, max = null) {
let desiredCount = null;
try {
desiredCount = parseInt(eInput.value);
} catch (e) {
console.error(e);
}
function postProcessNumber(desiredCount, min = null, max = null) {
if (desiredCount === null) {
desiredCount = 1;
}
@@ -34,3 +20,37 @@ export function getInputCount(eInput, min = null, max = null) {
return desiredCount;
}
/**
* Retrieves the integer number from an `HTMLInputElement`
* @param eInput {HTMLInputElement|HTMLSelectElement} The `HTMLInputElement` from which the value will be retrieved.
* @param min {number|null} If given, sets a minimum the value can have when returned.
* @param max {number|null} If given, sets a maximum the value can have when returned.
* @returns {number|NaN} The value from the given `HTMLInputElement`, or `1` if no valid one was given.
*/
export function getInputCount(eInput, min = null, max = null) {
let desiredCount = null;
try {
desiredCount = parseInt(eInput.value);
} catch (e) {
console.error(e);
}
return postProcessNumber(desiredCount, min, max);
}
/**
* Retrieves the float number from an `HTMLInputElement`
* @param eInput {HTMLInputElement|HTMLSelectElement} The `HTMLInputElement` from which the value will be retrieved.
* @param min {number|null} If given, sets a minimum the value can have when returned.
* @param max {number|null} If given, sets a maximum the value can have when returned.
* @returns {number|NaN} The value from the given `HTMLInputElement`, or `1` if no valid one was given.
*/
export function getInputNumber(eInput, min = null, max = null) {
let desiredCount = null;
try {
desiredCount = parseFloat(eInput.value);
} catch (e) {
console.error(e);
}
return postProcessNumber(desiredCount, min, max);
}

View File

@@ -0,0 +1,261 @@
import {stepCrc32IEEE, finishCrc32IEEE} from "./crc32.mjs";
import {areUintArraysEqual, peekUInt32BE, AsciiStringToUint8Array, Int32ToUint8Array, Uint8ArrayToHex} from "./data-utils.mjs"
import {loadFileAsUint8Array} from "./file-utils.mjs";
/**
* Parent class extended by all PNG-related errors.
*/
export class PngError extends Error {}
export class PngInvalidFileHeaderError extends PngError {}
export class PngInvalidStructureError extends PngError {}
export class PngInvalidChunkNameError extends PngError {}
export class PngInvalidChunkChecksumError extends PngError {}
export class PngInvalidImageHeaderError extends PngError {}
export class PngMissingCriticalChunksError extends PngError {}
export const PngFileHeader = new Uint8Array([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]);
export class PngChunk {
/** @type {string} */
type;
/** @type {Uint8Array} */
data;
/**
* @param type {string}
* @param data {Uint8Array}
* @param expectedChecksum {Uint8Array|null}
* @throws PngInvalidChunkNameError If the given chunk name isn't a valid chunk name.
*/
constructor(type, data, expectedChecksum) {
this.type = type;
this.data = data;
if(expectedChecksum !== null) {
if(!areUintArraysEqual(expectedChecksum, this.getChecksumUint8Array())) {
throw new PngInvalidChunkChecksumError(
`Invalid checksum was given ! (Expected ${
Uint8ArrayToHex(expectedChecksum)}, got ${
Uint8ArrayToHex(this.getChecksumUint8Array())})`
);
}
}
}
getChecksumNumber() {
return finishCrc32IEEE(
stepCrc32IEEE(
this.data,
stepCrc32IEEE(AsciiStringToUint8Array(this.type))
)
)
}
getChecksumUint8Array() {
return Int32ToUint8Array(this.getChecksumNumber());
}
}
class PngImageHeaderChunk extends PngChunk {
/**
* @param type {string}
* @param data {Uint8Array}
* @param expectedChecksum {Uint8Array|null}
* @throws PngInvalidChunkNameError If the given chunk name isn't a valid chunk name.
* @throws PngInvalidImageHeaderError If the given chunk's size isn't exactly 13 bytes.
*/
constructor(type, data, expectedChecksum) {
super(type, data, expectedChecksum);
if(this.data.length !== 13) {
throw new PngInvalidImageHeaderError(`Invalid IHDR chunk size, got ${this.data.length} instead of 13 !`);
}
}
getWidth() {
return peekUInt32BE(this.data, 0);
}
getHeight() {
return peekUInt32BE(this.data, 4);
}
getBitDepth() {
return this.data[8];
}
getColorType() {
return this.data[9];
}
getCompressionMethod() {
return this.data[10];
}
getFilterMethod() {
return this.data[11];
}
getInterlaceMethod() {
return this.data[12];
}
/**
* Converts a `PngChunk` to a `PngImageHeaderChunk`.
* @param pngChunk {PngChunk}
* @return {PngImageHeaderChunk}
*/
static fromPngChunk(pngChunk) {
return new PngImageHeaderChunk(pngChunk.type, pngChunk.data, null);
}
}
export class PngFile {
/** @type {File|null} */
originalFile;
/**
* Optional trailing data located after the 'IEND' chunk.
* @type {Uint8Array|null}
*/
trailingData;
/** @type {PngChunk[]} */
chunks;
/**
* @param file {File|null}
* @param fileData {Uint8Array|null}
* @throws PngInvalidFileHeaderError If `fileData` is provided and doesn't contain a valid PNG file header.
* @throws PngMissingCriticalChunksError If `fileData` is provided and is missing some critical chunks.
*/
constructor(file = null, fileData = null) {
this.originalFile = file;
this.chunks = [];
// Parsing and validating the data if given
if(fileData !== null) {
this.#validateFileHeader(fileData);
this.#parseChunks(fileData);
if(!this.hasEndChunk() || this.getChunkByType("IHDR") === null) {
throw new PngMissingCriticalChunksError("One or more critical chunk is missing from the PNG file !");
}
}
}
/**
* @param originalFileData {Uint8Array}
*/
#validateFileHeader = (originalFileData) => {
if(originalFileData.length < 8) {
throw new PngInvalidFileHeaderError(
`The file header's length is smaller than the required 8 ! (Got: ${originalFileData.length})`);
}
if(!areUintArraysEqual(originalFileData.slice(0, 8), PngFileHeader)) {
throw new PngInvalidFileHeaderError(
"The file header didn't have the expected data !\n" +
`Expected: ${PngFileHeader}\\n` +
`Got: ${originalFileData.slice(0, 8)}`
);
}
}
/**
* @param originalFileData {Uint8Array}
* @throws TypeError If a chunk's type couldn't be parsed.
*/
#parseChunks = (originalFileData) => {
let currentOffset = PngFileHeader.length;
while(currentOffset < originalFileData.length) {
// Checking if we haven't encountered an IEND, and we encountered a truncated file or trash data.
if(currentOffset + 12 > originalFileData.length) {
throw new PngInvalidStructureError("Unable to parse more chunks and no 'IEND' was encountered !");
}
const chunkLength = peekUInt32BE(originalFileData.slice(currentOffset, currentOffset + 4));
const chunkType = new TextDecoder().decode(originalFileData.slice(currentOffset + 4, currentOffset + 8));
// Checking if we have enough data left to read for the chunk's data.
if(currentOffset + 12 + chunkLength > originalFileData.length) {
throw new PngInvalidStructureError("Not enough data left to read the chunk !")
}
let chunkChecksum = originalFileData.slice(
currentOffset + 8 + chunkLength, currentOffset + 8 + chunkLength + 4);
this.chunks.push(
new PngChunk(
chunkType,
(chunkLength === 0) ?
new Uint8Array(0) :
originalFileData.slice(currentOffset + 8, currentOffset + 8 + chunkLength),
chunkChecksum,
//originalFileData.slice(currentOffset + 4, currentOffset + 8)
)
);
if(chunkType === "IEND") {
break;
}
currentOffset += 12 + chunkLength;
}
// Handling trailing data
if(currentOffset !== originalFileData.length) {
this.trailingData = originalFileData.slice(currentOffset, originalFileData.length);
}
}
/**
* Attempts to retrieve a chunk via its type.
* @param chunkName {string} The desired chunk's type.
* @return {PngChunk|null}
*/
getChunkByType(chunkName) {
for(let iChunk in this.chunks) {
if(this.chunks[iChunk].type === chunkName) {
return this.chunks[iChunk];
}
}
return null;
}
hasEndChunk() {
return this.getChunkByType("IEND") !== null;
}
/**
* @return {PngImageHeaderChunk|null}
*/
getImageHeaderChunk() {
let desiredChunk = this.getChunkByType("IHDR");
if(desiredChunk !== null) {
desiredChunk = PngImageHeaderChunk.fromPngChunk(desiredChunk);
}
return desiredChunk;
}
}
/**
* Reads and parses a given PNG file.
* @param {File} file - The PNG file to process.
* @returns {Promise<PngFile>} A promise that resolves with parsed PNG file.
*/
export function parsePngFile(file) {
return loadFileAsUint8Array(file).then(byteBuffer => {
return new PngFile(file, byteBuffer);
});
}

View File

@@ -0,0 +1,29 @@
# Introduction
This privacy policy is written in accordance with the 12th and 13th articles of the GDPR.
If you wish to consult it, you can do so on the following websites:
* https://gdpr.eu/
* https://eur-lex.europa.eu/
# What data do we collect ?
This website, does not collect any personal data, be it through access logs, cookies or any third-party services.
# Changes to our privacy policy
The content of this privacy policy was written and last updated on the 4th of December 2021.
In the event of a change to our privacy policy, you will be informed explicitly, and a copy of previous versions of the policy will be available on this page.
# How to contact us ?
If you wish to contact us for more information regarding our privacy policy, please contact us via the form included on the contact page, or at the following email address:
* herwin.bozet@gmail.com
# How to contact the appropriate authorities ?
Should you wish to report a complaint or if you feel that our privacy policy has not addressed your concern in a satisfactory manner, you may contact your national Data Protection Authority (DPA).
More information on this procedure can be found on the following websites:
* https://ec.europa.eu/ (English)
* https://gegevensbeschermingsautoriteit.be/ (French)

View File

@@ -0,0 +1,29 @@
# Introduction
La politique de confidentialité ci-présente a été écrite en accord avec les articles 12 et 13 de la RGPD.
Si vous souhaitez consulter le texte officiel en question, vous pouvez le faire sur les sites internet suivants :
* https://gdpr.eu/
* https://eur-lex.europa.eu/
# Quelles données sont collectées ?
Ce site web ne collecte aucune donnée personnelle, que ce soit au travers des journaux d'évènements sur nos serveurs, des cookies ou via quelconque autre tiers parti.
# Changements à notre politique de confidentialité
Le contenu de notre politique de confidentialité a été écrit et modifié pour la dernière fois le 4 décembre 2021.
En cas de changement, vous serez clairement informé et une copie des anciennes versions de notre politique sera disponible sur cette page.
# Comment nous contacter ?
Si vous souhaitez nous contacter afin d'obtenir plus d'informations concernant notre politique de confidentialité, nous vous recommandons d'utiliser le formulaire présent sur la page de contact, ou par courriel à l'adresse suivante:
* herwin.bozet@gmail.com
# Comment contacter les autorités compétentes ?
Dans l'éventualité où vous souhaiteriez déposer une plainte pour une quelconque raison en rapport avec notre politique de confidentialité, veuillez vous adresser à l'autorité nationale de protection des données (DPA).
Les informations concernant cette procédure peuvent être trouvées sur les sites internet suivants:
* https://ec.europa.eu/ (Anglais)
* https://gegevensbeschermingsautoriteit.be/ (Français)

View File

@@ -0,0 +1,76 @@
# Introduction
This privacy policy is written in accordance with the 12th and 13th articles of the GDPR.
If you wish to consult it, you can do so on the following websites:
https://gdpr.eu/
https://eur-lex.europa.eu/
# Data collection
This websites only collects data through generic access logs in order to detect and block bad actors from accessing this website.
None of the data collected is used for any other purpose,it is never shared with any other third-party and is never use in any sort of analytics.
Here is the list of private data being collected:
* IP address forwarded to us by CloudFlare
* Browser's User-Agent
And here is the list of non-private data being collected:
* Requested resource' URI
* Date and time
Once the data has been logged in the access logs, it is automatically retrieved and processed by a locally-hosted application every 30 seconds and then deleted from said logs.
This application compares this information against a list of known threat sources, targets and behaviours and if a match is found, the private information is anonymized and stored for 7 days pending a manual review.
Once that 7 day period has elapsed, or once the report has been reviewed, all the relevant data is automatically deleted.
If your request wasn't flagged as potentially malicious, every data collected from it is thrown out instantly.
This process should normally never be triggered for regular traffic since most of the triggering actions are ones that should not be possible to accomplish through normal browsing.
However, this process isn't infallible and there is always an off chance that false positives may happen.
# Third Parties
Our websites uses some services provided by CloudFlare in order to prevent bad actors from accessing this website via their "Web Application Firewall", as well as for caching purpose in order to improve your browsing experience.
None of the data that may be gathered by CloudFlare is ever used.
If you'd wish to consult their privacy policy, you can do so on their "Trust Hub" at the following URLs:
https://www.cloudflare.com/trust-hub/gdpr/ (English)
https://www.cloudflare.com/fr-fr/trust-hub/gdpr/ (French)
# Cookies
Our websites doesn't use nor store any cookies in your browser.
In the unlikely event one is present, we recommend you check CloudFlare's privacy policy on their "Trust Hub" at the following URLs:
https://www.cloudflare.com/trust-hub/gdpr/ (English)
https://www.cloudflare.com/fr-fr/trust-hub/gdpr/ (French)
This can be caused by malicious traffic coming from your network, or when you visit some private and restricted subdomains.
# Changes to our privacy policy
The content of this privacy policy was originally written on the 4th of December 2021 and was last updated on the 18th of March 2022.
* 2021/12/04
* Original version
* 2022/03/18
* Changed section on data collection to reflect new policy.
* Added mention about CloudFlare and linked to their privacy policy.
* Improved the "Changes to our privacy policy" section.
In the event of a change to our privacy policy, you will be informed explicitly, and a copy of previous versions of the policy will be available through this page.
# How to contact us ?
If you wish to contact us for more information regarding our privacy policy, please contact us via the form included on the contact page, or at the following email address:
* herwin.bozet@gmail.com
# How to contact the appropriate authorities ?
Should you wish to report a complaint or if you feel that our privacy policy has not addressed your concern in a satisfactory manner, you may contact your national Data Protection Authority (DPA).
More information on this procedure can be found on the following websites:
* https://ec.europa.eu/ (English)
* https://gegevensbeschermingsautoriteit.be/ (French)

View File

@@ -0,0 +1,77 @@
# Introduction
La politique de confidentialité ci-présente a été écrite en accord avec les articles 12 et 13 de la RGPD.
Si vous souhaitez consulter le texte officiel en question, vous pouvez le faire sur les sites internet suivants :
* https://gdpr.eu/
* https://eur-lex.europa.eu/
# Collecte de données
Ce site web collecte des données au travers des journaux d'évènements afin de détecter et bloquer de potentiels acteurs malveillants.
Les données collectées ne sont en aucun cas utilisées pour une quelconque autre raison.
Voici la liste des données personelles collectées:
* L'addresse IP source détéctée par CloudFlare
* Le "User-Agent" de votre navigateur internet
Et voici la liste des données non-personelles collectées:
* L'URI de la ressource demandée
* La date et l'heure
Les données présentes dans les journaux d'évènements sont extraites et retirées pour être traitées par une application qui tourne en local toute les 30 secondes.
L'application en question utilise et compare ces informations avec une liste d'acteurs malveillants et comportement suspects connus.
Si une correspondance est détectée, les informations privées sont anonymisées et sont stockées pendant 7 jours en attendant qu'un examen manuel soit effectué.
Après cette période de 7 jours, ou après qu'un examen ait été effectué, les données sont automatiquement supprimées.
Toute information concernant une requête non suspecte est automatiquement supprimée.
Ce processus n'est normalement jamais utilisé sur des requêtes provenant d'un trafic légitime dû au fait qu'il isole principalement les actions et requêtes qui ne devraient pas être possible sur ce site.
Cependant, ce processus n'est pas infaillible et il est toujours possible qu'une requête soit isolée par erreur.
# Organismes tiers
Ce site web utilise les services proposés par CloudFlare afin d'empêcher des acteurs malveillants d'y accéder grâce au "web application Firewall", ainsi que pour améliorer l'expérience des personnes le visitant grâce à leur CDN.
Aucune des données collectées et stockées par CloudFlare n'est utilisé.
Si vous souhaitez consulter leur politique de confidentialité, vous pouvez le faire sur leur "Trust Hub" en utilisant les liens ci-dessous:
* https://www.cloudflare.com/trust-hub/gdpr/ (Anglais)
* https://www.cloudflare.com/fr-fr/trust-hub/gdpr/ (Français)
# Cookies de navigation
Ce site web n'utilise pas, et ne stocke aucun cookies dans votre navigateur internet.
Dans l'éventualité où l'un serait présent, nous vous recommandons de consulter le "Trust Hub" de CloudFlare via les liens suivants pour plus d'informations:
* https://www.cloudflare.com/trust-hub/gdpr/ (Anglais)
* https://www.cloudflare.com/fr-fr/trust-hub/gdpr/ (Français)
Ce genre de situation peut être causée par le fait que du trafic suspect provient de votre navigateur internet, ou lors d'une visite sur un de nos sous-domaines privés.
# Changements à notre politique de confidentialité
Le contenu de notre politique de confidentialité a été originalement écrit et le 4 décembre 2021 modifié pour la dernière fois le 18 mars 2022.
* 2021/12/04
* Version originale
* 2022/03/18
* Mise-à-jour de la section sur la collection des données.
* Ajout d'une mention de CloudFlare et ajout de liens vers leur politique de confidentialité.
* Amélioration de la section "Changements à notre politique de confidentialité".
En cas de changement, vous serez clairement informé et une copie des anciennes versions de notre politique sera disponible au travers de cette page.
# Comment nous contacter ?
Si vous souhaitez nous contacter afin d'obtenir plus d'informations concernant notre politique de confidentialité, nous vous recommandons d'utiliser le formulaire présent sur la page de contact, ou par courriel à l'adresse suivante:
* herwin.bozet@gmail.com
# Comment contacter les autorités compétentes ?
Dans l'éventualité où vous souhaiteriez déposer une plainte pour une quelconque raison en rapport avec notre politique de confidentialité, veuillez vous adresser à l'autorité nationale de protection des données (DPA).
Les informations concernant cette procédure peuvent être trouvées sur les sites internet suivants:
* https://ec.europa.eu/ (Anglais)
* https://gegevensbeschermingsautoriteit.be/ (Français)

View File

@@ -0,0 +1,77 @@
# Introduction
This privacy policy is written in accordance with the 12th and 13th articles of the GDPR.
If you wish to consult it, you can do so on the following websites:
https://gdpr.eu/
https://eur-lex.europa.eu/
# Data collection
This websites only collects data through generic access logs in order to detect and block bad actors from accessing this website.
None of the data collected is used for any other purpose,it is never shared with any other third-party and is never use in any sort of analytics.
Here is the list of private data being collected:
* IP address
* Browser's User-Agent
And here is the list of non-private data being collected:
* Requested resource' URI
* Date and time
Once the data has been logged in the access logs, it is automatically retrieved and processed by a locally-hosted application every 30 seconds and then deleted from said logs.
This application compares this information against a list of known threat sources, targets and behaviours and if a match is found, the private information is anonymized and stored for 7 days pending a manual review.
Once that 7 day period has elapsed, or once the report has been reviewed, all the relevant data is automatically deleted.
If your request wasn't flagged as potentially malicious, every data collected from it is thrown out instantly.
This process should normally never be triggered for regular traffic since most of the triggering actions are ones that should not be possible to accomplish through normal browsing.
However, this process isn't infallible and there is always an off chance that false positives may happen.
# Third Parties
Our websites uses some services provided by v6Node in order to prevent bad actors from accessing this website and in order to put in place a reverse-proxy system.
The goal of this system is to improve your browsing experience with the help of a private caching service and custom traffic filtering rules.
None of the data that may be gathered by v6Node or the system described above is ever used or stored.
If you'd wish to consult their privacy policy and their partners', you can do so by using the following URLs:
* https://v6node.com/legal (English & German)
* https://v6node.b-cdn.net/legal/dataprotection.pdf (German)
# Cookies
Our websites doesn't use nor store any cookies in your browser.
# Changes to our privacy policy
The content of this privacy policy was originally written on the 4th of December 2021 and was last updated on the 18th of March 2022.
* 2021/12/04
* Original version
* 2022/03/18
* Changed section on data collection to reflect new policy.
* Added mention about CloudFlare and linked to their privacy policy.
* Improved the "Changes to our privacy policy" section.
* 2022/09/09
* Changed references to external services to reflect the migration to v6Node.
* Added mention about v6Node and linked to their privacy policy.
* Removed mentions of CloudFlare.
* Changed the "Cookies" section to indicate that none should be used on public domains.
In the event of a change to our privacy policy, you will be informed explicitly, and a copy of previous versions of the policy will be available through this page.
# How to contact us ?
If you wish to contact us for more information regarding our privacy policy, please contact us via the form included on the contact page, or at the following email address:
* herwin.bozet@gmail.com
# How to contact the appropriate authorities ?
Should you wish to report a complaint or if you feel that our privacy policy has not addressed your concern in a satisfactory manner, you may contact your national Data Protection Authority (DPA).
More information on this procedure can be found on the following websites:
* https://ec.europa.eu/ (English)
* https://gegevensbeschermingsautoriteit.be/ (French)

View File

@@ -0,0 +1,78 @@
# Introduction
La politique de confidentialité ci-présente a été écrite en accord avec les articles 12 et 13 de la RGPD.
Si vous souhaitez consulter le texte officiel en question, vous pouvez le faire sur les sites internet suivants :
* https://gdpr.eu/
* https://eur-lex.europa.eu/
# Collecte de données
Ce site web collecte des données au travers des journaux d'évènements afin de détecter et bloquer de potentiels acteurs malveillants.
Les données collectées ne sont en aucun cas utilisées pour une quelconque autre raison.
Voici la liste des données personelles collectées:
* L'addresse IP source
* Le "User-Agent" de votre navigateur internet
Et voici la liste des données non-personelles collectées:
* L'URI de la ressource demandée
* La date et l'heure
Les données présentes dans les journaux d'évènements sont extraites et retirées pour être traitées par une application qui tourne en local toute les 30 secondes.
L'application en question utilise et compare ces informations avec une liste d'acteurs malveillants et comportement suspects connus.
Si une correspondance est détectée, les informations privées sont anonymisées et sont stockées pendant 7 jours en attendant qu'un examen manuel soit effectué.
Après cette période de 7 jours, ou après qu'un examen ait été effectué, les données sont automatiquement supprimées.
Toute information concernant une requête non suspecte est automatiquement supprimée.
Ce processus n'est normalement jamais utilisé sur des requêtes provenant d'un trafic légitime dû au fait qu'il isole principalement les actions et requêtes qui ne devraient pas être possible sur ce site.
Cependant, ce processus n'est pas infaillible et il est toujours possible qu'une requête soit isolée par erreur.
# Organismes tiers
Ce site web utilise les services proposés par v6Node afin d'empêcher des acteurs malveillants d'y accéder et dans le but de mettre en place un système de reverse-proxy.
Ceci a pour but d'améliorer l'expérience des personnes le visitant grâce à un système de filtrage et caching privé.
Aucune des données collectées et stockées par v6Node et le système décris ci-dessus n'est utilisé ou stocké.
Si vous souhaitez consulter leur politique de confidentialité ainsi que celle de leur partenaires, vous pouvez le faire en suivant les liens ci-dessous:
* https://v6node.com/legal (Allemand & Anglais)
* https://v6node.b-cdn.net/legal/dataprotection.pdf (Allemand)
# Cookies de navigation
Ce site web n'utilise pas, et ne stocke aucun cookies dans votre navigateur internet.
# Changements à notre politique de confidentialité
Le contenu de notre politique de confidentialité a été originalement écrit et le 4 décembre 2021 modifié pour la dernière fois le 18 mars 2022.
* 2021/12/04
* Version originale
* 2022/03/18
* Mise-à-jour de la section sur la collection des données.
* Ajout d'une mention de CloudFlare et ajout de liens vers leur politique de confidentialité.
* Amélioration de la section "Changements à notre politique de confidentialité".
* 2022/09/09
* Changement des références aux services externes pour indiquer l'utilisation de v6Node.
* Ajout de mentions de v6Node et liens vers leur politique de vie confidentialité.
* Suppression de mentions de CloudFlare.
* Changement de la section "Cookies" afin d'indiquer qu'ils ne seront plus utilisés sur les domaines publics.
En cas de changement, vous serez clairement informé et une copie des anciennes versions de notre politique sera disponible au travers de cette page.
# Comment nous contacter ?
Si vous souhaitez nous contacter afin d'obtenir plus d'informations concernant notre politique de confidentialité, nous vous recommandons d'utiliser le formulaire présent sur la page de contact, ou par courriel à l'adresse suivante:
* herwin.bozet@gmail.com
# Comment contacter les autorités compétentes ?
Dans l'éventualité où vous souhaiteriez déposer une plainte pour une quelconque raison en rapport avec notre politique de confidentialité, veuillez vous adresser à l'autorité nationale de protection des données (DPA).
Les informations concernant cette procédure peuvent être trouvées sur les sites internet suivants:
* https://ec.europa.eu/ (Anglais)
* https://gegevensbeschermingsautoriteit.be/ (Français)

View File

@@ -0,0 +1,82 @@
# Introduction
This privacy policy is written in accordance with the 12th and 13th articles of the GDPR.
If you wish to consult it, you can do so on the following websites:
* https://gdpr.eu/
* https://eur-lex.europa.eu/
# Data collection
This websites only collects data through generic access logs in order to detect and block bad actors from accessing this website.
None of the data collected is used for any other purpose, it is never shared with any other third-party and is never use in any sort of analytics.
Here is the list of private data being collected:
* IP address
* Browser's User-Agent
And here is the list of non-private data being collected:
* Requested resource' URI
* Date and time
Once the data has been logged in the access logs, it is automatically retrieved and processed by a locally-hosted applications every 30-60 seconds and then deleted from said logs.
This application compares this information against a list of known threat sources, targets and behaviours and if a match is found, the private information is stored for 7 days pending a manual review.
Once that 7 day period has elapsed, or once the report has been reviewed, all the relevant data is automatically deleted.
If your request wasn't flagged as potentially malicious, every data collected from it is thrown out instantly.
This process should normally never be triggered for regular traffic since most of the triggering actions are ones that should not be possible to accomplish through normal browsing.
However, this process isn't infallible and there is always an off chance that false positives may happen.
# Third Parties
Our websites uses some VPS provided by IONOS in order to put in place a reverse-proxy system.
The goal of this system is to improve your browsing experience with the help of a private caching service and custom traffic filtering rules.
No data should be collected on their side due to the nature of the server leased from IONOS.
If you'd wish to consult their privacy policy and their partners', you can do so by using the following URLs:
* https://www.ionos.fr/terms-gtc/clause-de-confidentialite/ (French)
* https://www.ionos.com/terms-gtc/privacy-policy/ (English)
# Cookies
Our websites doesn't use nor store any cookies in your browser.
# Changes to our privacy policy
The content of this privacy policy was originally written on the 4th of December 2021 and was last updated on the 18th of March 2022.
* 2021/12/04
* Original version
* 2022/03/18
* Changed section on data collection to reflect new policy.
* Added mention about CloudFlare and linked to their privacy policy.
* Improved the "Changes to our privacy policy" section.
* 2022/09/09
* Changed references to external services to reflect the migration to v6Node.
* Added mention about v6Node and linked to their privacy policy.
* Removed mentions of CloudFlare.
* Changed the "Cookies" section to indicate that none should be used on public domains.
* 2023/11/11
* Changed references to external services to reflect the migration to IONOS.
* Removed mentions of v6Node.
* Changed section on data collection to reflect new timings & infrastructure.
In the event of a change to our privacy policy, you will be informed explicitly, and a copy of previous versions of the policy will be available through this page.
# How to contact us ?
If you wish to contact us for more information regarding our privacy policy, please contact us via the form included on the contact page, or at the following email address:
* herwin.bozet@gmail.com
# How to contact the appropriate authorities ?
Should you wish to report a complaint or if you feel that our privacy policy has not addressed your concern in a satisfactory manner, you may contact your national Data Protection Authority (DPA).
More information on this procedure can be found on the following websites:
* https://ec.europa.eu/ (English)
* https://gegevensbeschermingsautoriteit.be/ (French)

View File

@@ -0,0 +1,83 @@
# Introduction
La politique de confidentialité ci-présente a été écrite en accord avec les articles 12 et 13 de la RGPD.
Si vous souhaitez consulter le texte officiel en question, vous pouvez le faire sur les sites internet suivants :
* https://gdpr.eu/
* https://eur-lex.europa.eu/
# Collecte de données
Ce site web collecte des données au travers des journaux d'évènements afin de détecter et bloquer de potentiels acteurs malveillants.
Les données collectées ne sont en aucun cas utilisées pour une quelconque autre raison.
Voici la liste des données personelles collectées:
* L'addresse IP source
* Le "User-Agent" de votre navigateur internet
Et voici la liste des données non-personelles collectées:
* L'URI de la ressource demandée
* La date et l'heure
Les données présentes dans les journaux d'évènements sont extraites et retirées pour être traitées par des applications qui tournent en local toute les 30 à 60 secondes.
L'application en question utilise et compare ces informations avec une liste d'acteurs malveillants et comportement suspects connus.
Si une correspondance est détectée, les informations privées sont stockées pendant 7 jours en attendant qu'un examen manuel soit effectué.
Après cette période de 7 jours, ou après qu'un examen ait été effectué, les données sont automatiquement supprimées.
Toute information concernant une requête non suspecte est automatiquement supprimée.
Ce processus n'est normalement jamais utilisé sur des requêtes provenant d'un trafic légitime dû au fait qu'il isole principalement les actions et requêtes qui ne devraient pas être possible sur ce site.
Cependant, ce processus n'est pas infaillible et il est toujours possible qu'une requête soit isolée par erreur.
# Organismes tiers
Ce site web utilise un VPS proposés par IONOS dans le but de mettre en place un système de reverse-proxy.
Ceci a pour but d'améliorer l'expérience des personnes le visitant grâce à un système de filtrage et caching privé.
Due à la nature du serveur loué à IONOS, aucune donnée ne devraient être collectées de leur côté.
Si vous souhaitez consulter leur politique de confidentialité ainsi que celle de leur partenaires, vous pouvez le faire en suivant les liens ci-dessous:
* https://www.ionos.fr/terms-gtc/clause-de-confidentialite/ (Français)
* https://www.ionos.com/terms-gtc/privacy-policy/ (Anglais)
# Cookies de navigation
Ce site web n'utilise pas, et ne stocke aucun cookies dans votre navigateur internet.
# Changements à notre politique de confidentialité
Le contenu de notre politique de confidentialité a été originalement écrit et le 4 décembre 2021 modifié pour la dernière fois le 18 mars 2022.
* 2021/12/04
* Version originale
* 2022/03/18
* Mise-à-jour de la section sur la collection des données.
* Ajout d'une mention de CloudFlare et ajout de liens vers leur politique de confidentialité.
* Amélioration de la section "Changements à notre politique de confidentialité".
* 2022/09/09
* Changement des références aux services externes pour indiquer l'utilisation de v6Node.
* Ajout de mentions de v6Node et liens vers leur politique de vie confidentialité.
* Suppression de mentions de CloudFlare.
* Changement de la section "Cookies" afin d'indiquer qu'ils ne seront plus utilisés sur les domaines publics.
* 2023/11/11
* Changement des références aux services externes pour indiquer l'utilisation de IONOS.
* Suppression de mentions de v6Node.
* Changement des temps de traitement et mentions de l'infrastructure dans la section "Changements à notre politique de confidentialité".
En cas de changement, vous serez clairement informé et une copie des anciennes versions de notre politique sera disponible au travers de cette page.
# Comment nous contacter ?
Si vous souhaitez nous contacter afin d'obtenir plus d'informations concernant notre politique de confidentialité, nous vous recommandons d'utiliser le formulaire présent sur la page de contact, ou par courriel à l'adresse suivante:
* herwin.bozet@gmail.com
# Comment contacter les autorités compétentes ?
Dans l'éventualité où vous souhaiteriez déposer une plainte pour une quelconque raison en rapport avec notre politique de confidentialité, veuillez vous adresser à l'autorité nationale de protection des données (DPA).
Les informations concernant cette procédure peuvent être trouvées sur les sites internet suivants:
* https://ec.europa.eu/ (Anglais)
* https://gegevensbeschermingsautoriteit.be/ (Français)

View File

@@ -0,0 +1,103 @@
# Introduction
This privacy policy is written in accordance with the 12th and 13th articles of the GDPR.
If you wish to consult it, you can do so on the following websites:
* https://gdpr.eu/
* https://eur-lex.europa.eu/
# Data collection (Web)
This websites only collects data through generic access logs in order to detect and block bad actors from accessing this website.
None of the data collected is used for any other purpose, it is never shared with any other third-party and is never use in any sort of analytics.
Here is the list of private data being collected:
* IP address
* Browser's User-Agent
And here is the list of non-private data being collected:
* Requested resource' URI
* Date and time
Once the data has been logged in the access logs, it is automatically retrieved and processed by a locally-hosted applications every 30-60 seconds and then deleted from said logs.
This application compares this information against a list of known threat sources, targets and behaviours and if a match is found, the private information is stored for 7 days pending a manual review.
Once that 7 day period has elapsed, or once the report has been reviewed, all the relevant data is automatically deleted.
If your request wasn't flagged as potentially malicious, every data collected from it is thrown out instantly.
This process should normally never be triggered for regular traffic since most of the triggering actions are ones that should not be possible to accomplish through normal browsing.
However, this process isn't infallible and there is always an off chance that false positives may happen.
# Data collection (DNS)
Our DNS servers collects data through generic access logs in order to detect and block bad actors.
None of the data collected is used for any other purpose, it is never shared with any other third-party and is never use in any sort of analytics.
Here is the list of private data being collected:
* IP address
And here is the list of non-private data being collected:
* Requested DNS record
* Date and time
All private data is deleted after a period of 7 days.
# Third Parties
Our websites uses some VPS provided by IONOS and ChicagoVPS in order to put in place a CDN system.
The goal of this system is to improve your browsing experience with the help of a private caching service and custom traffic filtering rules.
No data should be collected on their side due to the nature of the server leased to us.
If you'd wish to consult their privacy policy and their partners', you can do so by using the following URLs:
* IONOS
* French -> https://www.ionos.fr/terms-gtc/clause-de-confidentialite/
* English -> https://www.ionos.com/terms-gtc/privacy-policy/
* ChicagoVPS
* English -> https://www.chicagovps.net/wp-content/uploads/2023/07/Terms-and-Conditions-ChicagoVPS.pdf
# Cookies
Our websites doesn't use nor store any cookies in your browser.
# Changes to our privacy policy
The content of this privacy policy was originally written on the 4th of December 2021 and was last updated on the 18th of March 2022.
* 2021/12/04
* Original version
* 2022/03/18
* Changed section on data collection to reflect new policy.
* Added mention about CloudFlare and linked to their privacy policy.
* Improved the "Changes to our privacy policy" section.
* 2022/09/09
* Changed references to external services to reflect the migration to v6Node.
* Added mention about v6Node and linked to their privacy policy.
* Removed mentions of CloudFlare.
* Changed the "Cookies" section to indicate that none should be used on public domains.
* 2023/11/11
* Changed references to external services to reflect the migration to IONOS.
* Removed mentions of v6Node.
* Changed section on data collection to reflect new timings & infrastructure.
* 2023/11/30
* Added a section regarding data collection through DNS servers.
* Changed references to external services to reflect the usage of ChicagoVPS
In the event of a change to our privacy policy, you will be informed explicitly, and a copy of previous versions of the policy will be available through this page.
# How to contact us ?
If you wish to contact us for more information regarding our privacy policy, please contact us via the form included on the contact page, or at the following email address:
herwin.bozet@gmail.com
# How to contact the appropriate authorities ?
Should you wish to report a complaint or if you feel that our privacy policy has not addressed your concern in a satisfactory manner, you may contact your national Data Protection Authority (DPA).
More information on this procedure can be found on the following websites:
* https://ec.europa.eu/ (English)
* https://gegevensbeschermingsautoriteit.be/ (French)

View File

@@ -0,0 +1,104 @@
# Introduction
La politique de confidentialité ci-présente a été écrite en accord avec les articles 12 et 13 de la RGPD.
Si vous souhaitez consulter le texte officiel en question, vous pouvez le faire sur les sites internet suivants :
* https://gdpr.eu/
* https://eur-lex.europa.eu/
# Collecte de données (Web)
Ce site web collecte des données au travers des journaux d'évènements afin de détecter et bloquer de potentiels acteurs malveillants.
Les données collectées ne sont en aucun cas utilisées pour une quelconque autre raison.
Voici la liste des données personnelles collectées:
* L'addresse IP source
* Le "User-Agent" de votre navigateur internet
Et voici la liste des données non-personnelles collectées:
* L'URI de la ressource demandée
* La date et l'heure
Les données présentes dans les journaux d'évènements sont extraites et retirées pour être traitées par des applications qui tournent en local toute les 30 à 60 secondes.
L'application en question utilise et compare ces informations avec une liste d'acteurs malveillants et comportement suspects connus.
Si une correspondance est détectée, les informations privées sont stockées pendant 7 jours en attendant qu'un examen manuel soit effectué.
Après cette période de 7 jours, ou après qu'un examen ait été effectué, les données sont automatiquement supprimées.
Toute information concernant une requête non suspecte est automatiquement supprimée.
Ce processus n'est normalement jamais utilisé sur des requêtes provenant d'un trafic légitime dû au fait qu'il isole principalement les actions et requêtes qui ne devraient pas être possible sur ce site.
Cependant, ce processus n'est pas infaillible et il est toujours possible qu'une requête soit isolée par erreur.
# Collecte de données (DNS)
Nos serveurs DNS collectent des données au travers des journaux d'évènements afin de détecter et bloquer de potentiels acteurs malveillants.
Les données collectées ne sont en aucun cas utilisées pour une quelconque autre raison.
Voici la liste des données personnelles collectées:
* L'addresse IP source
Et voici la liste des données non-personnelles collectées:
* L'enregistrement DNS demandée
* La date et l'heure
Toutes les données personnelles sont automatiquement supprimées après une période de 7 jours.
# Organismes tiers
Ce site web utilise des VPS proposés par IONOS et ChicagoVPS dans le but de mettre en place un système de RDC.
Ceci a pour but d'améliorer l'expérience des personnes le visitant grâce à un système de filtrage et caching privé.
Due à la nature des serveurs loués, aucune donnée ne devraient être collectées de leur côté.
Si vous souhaitez consulter leur politique de confidentialité ainsi que celle de leur partenaires, vous pouvez le faire en suivant les liens ci-dessous:
* IONOS
* Français -> https://www.ionos.fr/terms-gtc/clause-de-confidentialite/
* Anglais -> https://www.ionos.com/terms-gtc/privacy-policy/
* ChicagoVPS
* Anglais -> https://www.chicagovps.net/wp-content/uploads/2023/07/Terms-and-Conditions-ChicagoVPS.pdf
# Cookies de navigation
Ce site web n'utilise pas, et ne stocke aucun cookies dans votre navigateur internet.
# Changements à notre politique de confidentialité
Le contenu de notre politique de confidentialité a été originalement écrit et le 4 décembre 2021 modifié pour la dernière fois le 18 mars 2022.
* 2021/12/04
* Version originale
* 2022/03/18
* Mise-à-jour de la section sur la collection des données.
* Ajout d'une mention de CloudFlare et ajout de liens vers leur politique de confidentialité.
* Amélioration de la section "Changements à notre politique de confidentialité".
* 2022/09/09
* Changement des références aux services externes pour indiquer l'utilisation de v6Node.
* Ajout de mentions de v6Node et liens vers leur politique de vie confidentialité.
* Suppression de mentions de CloudFlare.
* Changement de la section "Cookies" afin d'indiquer qu'ils ne seront plus utilisés sur les domaines publics.
* 2023/11/11
* Changement des références aux services externes pour indiquer l'utilisation de IONOS.
* Suppression de mentions de v6Node.
* Changement des temps de traitement et mentions de l'infrastructure dans la section "Changements à notre politique de confidentialité".
* 2023/11/30
* Ajout de la section sur la collection des données par le serveur DNS.
* Changement des références aux services externes pour indiquer l'utilisation de ChicagoVPS.
En cas de changement, vous serez clairement informé et une copie des anciennes versions de notre politique sera disponible au travers de cette page.
# Comment nous contacter ?
Si vous souhaitez nous contacter afin d'obtenir plus d'informations concernant notre politique de confidentialité, nous vous recommandons d'utiliser le formulaire présent sur la page de contact, ou par courriel à l'adresse suivante:
herwin.bozet@gmail.com
# Comment contacter les autorités compétentes ?
Dans l'éventualité où vous souhaiteriez déposer une plainte pour une quelconque raison en rapport avec notre politique de confidentialité, veuillez vous adresser à l'autorité nationale de protection des données (DPA).
Les informations concernant cette procédure peuvent être trouvées sur les sites internet suivants:
https://ec.europa.eu/ (Anglais)
https://gegevensbeschermingsautoriteit.be/ (Français)

View File

@@ -0,0 +1,97 @@
# Introduction
This privacy policy is written in accordance with the 12th and 13th articles of the GDPR.
If you wish to consult it, you can do so on the following websites:
* https://gdpr.eu/
* https://eur-lex.europa.eu/
# Data collection (Web)
This websites only collects data through generic access logs in order to detect and block bad actors from accessing this website.
None of the data collected is used for any other purpose, it is never shared with any other third-party and is never use in any sort of analytics.
Here is the list of private data being collected:
* IP address
* Browser's User-Agent
And here is the list of non-private data being collected:
* Requested resource' URI
* Date and time
Once the data has been logged in the access logs, it is automatically retrieved and processed by a locally-hosted applications every 30-60 seconds and then deleted from said logs.
This application compares this information against a list of known threat sources, targets and behaviours and if a match is found, the private information is stored for 7 days pending a manual review.
Once that 7 day period has elapsed, or once the report has been reviewed, all the relevant data is automatically deleted.
If your request wasn't flagged as potentially malicious, every data collected from it is thrown out instantly.
This process should normally never be triggered for regular traffic since most of the triggering actions are ones that should not be possible to accomplish through normal browsing.
However, this process isn't infallible and there is always an off chance that false positives may happen.
# Data collection (DNS)
Our DNS servers collects data through generic access logs in order to detect and block bad actors.
None of the data collected is used for any other purpose, it is never shared with any other third-party and is never use in any sort of analytics.
Here is the list of private data being collected:
* IP address
And here is the list of non-private data being collected:
* Requested DNS record
* Date and time
All private data is deleted after a period of 7 days.
# Third Parties
Our services do not share any data with any third parties whatsoever.
# Cookies
Our websites doesn't use nor store any cookies in your browser.
# Changes to our privacy policy
The content of this privacy policy was originally written on the 4th of December 2021 and was last updated on the 18th of March 2022.
* 2021/12/04
* Original version
* 2022/03/18
* Changed section on data collection to reflect new policy.
* Added mention about CloudFlare and linked to their privacy policy.
* Improved the "Changes to our privacy policy" section.
* 2022/09/09
* Changed references to external services to reflect the migration to v6Node.
* Added mention about v6Node and linked to their privacy policy.
* Removed mentions of CloudFlare.
* Changed the "Cookies" section to indicate that none should be used on public domains.
* 2023/11/11
* Changed references to external services to reflect the migration to IONOS.
* Removed mentions of v6Node.
* Changed section on data collection to reflect new timings & infrastructure.
* 2023/11/30
* Added a section regarding data collection through DNS servers.
* Changed references to external services to reflect the usage of ChicagoVPS
* 2025/03/30
* Simplification of the 'Third Parties' section.
* Removal of unnecessary mentions of our VPS providers.
In the event of a change to our privacy policy, you will be informed explicitly, and a copy of previous versions of the policy will be available through this page.
# How to contact us ?
If you wish to contact us for more information regarding our privacy policy, please contact us via the form included on the contact page, or at the following email address:
herwin.bozet@gmail.com
# How to contact the appropriate authorities ?
Should you wish to report a complaint or if you feel that our privacy policy has not addressed your concern in a satisfactory manner, you may contact your national Data Protection Authority (DPA).
More information on this procedure can be found on the following websites:
* https://ec.europa.eu/ (English)
* https://gegevensbeschermingsautoriteit.be/ (French)

View File

@@ -0,0 +1,98 @@
# Introduction
La politique de confidentialité ci-présente a été écrite en accord avec les articles 12 et 13 de la RGPD.
Si vous souhaitez consulter le texte officiel en question, vous pouvez le faire sur les sites internet suivants :
* https://gdpr.eu/
* https://eur-lex.europa.eu/
# Collecte de données (Web)
Ce site web collecte des données au travers des journaux d'évènements afin de détecter et bloquer de potentiels acteurs malveillants.
Les données collectées ne sont en aucun cas utilisées pour une quelconque autre raison.
Voici la liste des données personnelles collectées:
* L'addresse IP source
* Le "User-Agent" de votre navigateur internet
Et voici la liste des données non-personnelles collectées:
* L'URI de la ressource demandée
* La date et l'heure
Les données présentes dans les journaux d'évènements sont extraites et retirées pour être traitées par des applications qui tournent en local toute les 30 à 60 secondes.
L'application en question utilise et compare ces informations avec une liste d'acteurs malveillants et comportement suspects connus.
Si une correspondance est détectée, les informations privées sont stockées pendant 7 jours en attendant qu'un examen manuel soit effectué.
Après cette période de 7 jours, ou après qu'un examen ait été effectué, les données sont automatiquement supprimées.
Toute information concernant une requête non suspecte est automatiquement supprimée.
Ce processus n'est normalement jamais utilisé sur des requêtes provenant d'un trafic légitime dû au fait qu'il isole principalement les actions et requêtes qui ne devraient pas être possible sur ce site.
Cependant, ce processus n'est pas infaillible et il est toujours possible qu'une requête soit isolée par erreur.
# Collecte de données (DNS)
Nos serveurs DNS collectent des données au travers des journaux d'évènements afin de détecter et bloquer de potentiels acteurs malveillants.
Les données collectées ne sont en aucun cas utilisées pour une quelconque autre raison.
Voici la liste des données personnelles collectées:
* L'addresse IP source
Et voici la liste des données non-personnelles collectées:
* L'enregistrement DNS demandée
* La date et l'heure
Toutes les données personnelles sont automatiquement supprimées après une période de 7 jours.
# Organismes tiers
Nos services ne partagent aucune donnée avec des tiers, quels qu'ils soient.
# Cookies de navigation
Ce site web n'utilise pas, et ne stocke aucun cookies dans votre navigateur internet.
# Changements à notre politique de confidentialité
Le contenu de notre politique de confidentialité a été originalement écrit et le 4 décembre 2021 modifié pour la dernière fois le 18 mars 2022.
* 2021/12/04
* Version originale
* 2022/03/18
* Mise-à-jour de la section sur la collection des données.
* Ajout d'une mention de CloudFlare et ajout de liens vers leur politique de confidentialité.
* Amélioration de la section "Changements à notre politique de confidentialité".
* 2022/09/09
* Changement des références aux services externes pour indiquer l'utilisation de v6Node.
* Ajout de mentions de v6Node et liens vers leur politique de vie confidentialité.
* Suppression de mentions de CloudFlare.
* Changement de la section "Cookies" afin d'indiquer qu'ils ne seront plus utilisés sur les domaines publics.
* 2023/11/11
* Changement des références aux services externes pour indiquer l'utilisation de IONOS.
* Suppression de mentions de v6Node.
* Changement des temps de traitement et mentions de l'infrastructure dans la section "Changements à notre politique de confidentialité".
* 2023/11/30
* Ajout de la section sur la collection des données par le serveur DNS.
* Changement des références aux services externes pour indiquer l'utilisation de ChicagoVPS.
* 2025/03/30
* Simplification de la section 'Organismes tiers'.
* Suppression des mentions inutiles de nous fournisseurs de VPS.
En cas de changement, vous serez clairement informé et une copie des anciennes versions de notre politique sera disponible au travers de cette page.
# Comment nous contacter ?
Si vous souhaitez nous contacter afin d'obtenir plus d'informations concernant notre politique de confidentialité, nous vous recommandons d'utiliser le formulaire présent sur la page de contact, ou par courriel à l'adresse suivante:
herwin.bozet@gmail.com
# Comment contacter les autorités compétentes ?
Dans l'éventualité où vous souhaiteriez déposer une plainte pour une quelconque raison en rapport avec notre politique de confidentialité, veuillez vous adresser à l'autorité nationale de protection des données (DPA).
Les informations concernant cette procédure peuvent être trouvées sur les sites internet suivants:
https://ec.europa.eu/ (Anglais)
https://gegevensbeschermingsautoriteit.be/ (Français)

File diff suppressed because one or more lines are too long

View File

@@ -2,17 +2,12 @@
<!-- {{ render_h2(l10n("disclaimer.title", applet_data.id, user_lang)) }} -->
<label for="iban-generator-option-enable-sepa" class="mr-xxs">{{ l10n("option.sepa.enable", "iban-generator", user_lang) }}:</label>
<input id="iban-generator-option-enable-sepa" class="r-m border" type="checkbox" checked>
<input id="iban-generator-option-enable-sepa" class="r-m border cb-pretty" type="checkbox" checked>
<br>
<label for="iban-generator-option-enable-non-sepa" class="mr-xxs">{{ l10n("option.non-sepa.enable", "iban-generator", user_lang) }}:</label>
<input id="iban-generator-option-enable-non-sepa" class="r-m border" type="checkbox" checked>
<br>
<label for="iban-generator-option-foreach" class="mr-xxs">{{ l10n("option.for.each", "iban-generator", user_lang) }}:</label>
<input id="iban-generator-option-foreach" class="r-m border" type="checkbox">
<input id="iban-generator-option-enable-non-sepa" class="r-m border cb-pretty" type="checkbox" checked>
<hr class="subtle">
@@ -113,17 +108,45 @@
<br>
<label for="iban-generator-option-count" class="mr-xs">{{ l10n("option.count", "iban-generator", user_lang) }}:</label>
<input id="iban-generator-option-count" class="p-xxs border r-s" type="number" value="4" min="1" max="1000">
<input id="iban-generator-option-count" class="p-xxs border r-s" type="number" value="4" min="1" max="10000">
<br>
<label for="iban-generator-option-pretty" class="mr-xxs">{{ l10n("option.human.readable", "iban-generator", user_lang) }}:</label>
<input id="iban-generator-option-pretty" class="r-m border" type="checkbox" checked>
<label for="iban-generator-option-foreach" class="mr-xxs">{{ l10n("option.for.each", "iban-generator", user_lang) }}:</label>
<input id="iban-generator-option-foreach" class="r-m border cb-pretty" type="checkbox">
<hr class="subtle">
<input type="radio" id="iban-generator-option-prefer-random" name="iban_charset" value="0" class="radio-solid border mr-xxs radio-unchecked-subtle" checked>
<label for="iban-generator-option-prefer-random">{{ l10n("option.prefer.random", "iban-generator", user_lang) }}</label>
<br>
<label for="iban-generator-option-prefer-numbers" class="mr-xxs">{{ l10n("option.prefer.numbers", "iban-generator", user_lang) }}:</label>
<input id="iban-generator-option-prefer-numbers" class="r-m border" type="checkbox">
<input type="radio" id="iban-generator-option-prefer-numbers" name="iban_charset" value="0" class="radio-solid border mr-xxs radio-unchecked-subtle">
<label for="iban-generator-option-prefer-numbers">{{ l10n("option.prefer.numbers", "iban-generator", user_lang) }}</label>
<br>
<input type="radio" id="iban-generator-option-prefer-letters" name="iban_charset" value="0" class="radio-solid border mr-xxs radio-unchecked-subtle">
<label for="iban-generator-option-prefer-letters">{{ l10n("option.prefer.letters", "iban-generator", user_lang) }}</label>
<hr class="subtle">
<input type="radio" id="iban-generator-option-format-none" name="iban_format" value="none" class="radio-solid border mr-xxs radio-unchecked-subtle" checked>
<label for="iban-generator-option-format-none">{{ l10n("option.human.format.none", "iban-generator", user_lang) }}</label>
<br>
<input type="radio" id="iban-generator-option-format-standard" name="iban_format" value="standard" class="radio-solid border mr-xxs radio-unchecked-subtle">
<label for="iban-generator-option-format-standard">{{ l10n("option.human.format.standard", "iban-generator", user_lang) }}</label>
<br>
<input type="radio" id="iban-generator-option-format-4by4" name="iban_format" value="4by4" class="radio-solid border mr-xxs radio-unchecked-subtle">
<label for="iban-generator-option-format-4by4">{{ l10n("option.human.format.4by4", "iban-generator", user_lang) }}</label>
<hr class="subtle">

View File

@@ -1,26 +1,14 @@
<section id="{{ applet_data.id }}-introduction">
{{ render_h2(l10n("introduction.title", "commons", user_lang)) }}
{{ render_paragraph(l10n("introduction.1", applet_data.id, user_lang)) }}
{{ render_paragraph(l10n("introduction.2", applet_data.id, user_lang)) }}
</section>
{{ render_file_input(applet_data.id + "-image-input-file", true, ".png, .bmp, .ico", true, false, user_lang) }}
<section id="{{ applet_data.id }}-input">
{{ render_h2(l10n("input.title", "commons", user_lang)) }}
{{ render_paragraph(l10n("input.1", applet_data.id, user_lang)) }}
<hr class="subtle">
{{ render_file_input(applet_data.id + "-image-input-file", true, ".png, .bmp, .ico", true, false, user_lang) }}
<hr class="subtle">
<label for="{{ applet_data.id }}-enable-expert-mode" class="mr-xxs">
{{ l10n("enable.expert.mode", applet_data.id, user_lang) }}:
</label>
<input id="{{ applet_data.id }}-enable-expert-mode" class="r-m border" type="checkbox">
<!--<span class="ml-s t-italic t-muted">Toggles some uncommon features.</span>-->
<br>
<label for="{{ applet_data.id }}-enable-expert-mode" class="mr-xxs">
{{ l10n("enable.expert.mode", applet_data.id, user_lang) }}:
</label>
<input id="{{ applet_data.id }}-enable-expert-mode" class="r-m border" type="checkbox">
<div class="ico-maker-advanced">
<label for="{{ applet_data.id }}-enable-binary-blobs" class="mr-xxs">
{{ l10n("enable.binary.blobs", applet_data.id, user_lang) }}:
</label>
@@ -35,176 +23,174 @@
<span class="f-right">
{{ render_button("<i class=\"fa-duotone fa-solid fa-remove\"></i>Remove Binary Blobs", False, None, "bkgd-orange") }}
{{ render_button("<i class=\"fa-duotone fa-solid fa-remove\"></i>Remove Everything", False, None, "bkgd-red") }}
<!--{{ render_button("<i class=\"fa-duotone fa-solid fa-remove\"></i>Remove Everything", False, None, "bkgd-red") }}-->
</span>
</section>
</div>
<section id="{{ applet_data.id }}-icon-parts">
{{ render_h2(l10n("idk01.title", "commons", user_lang)) }}
{{ render_paragraph(l10n("idk01.1", applet_data.id, user_lang)) }}
<hr class="subtle">
{{ render_button("<i class=\"fa-duotone fa-solid fa-download\"></i>Download <span class=\"t-monospace\">.ico</span>", False, None, "bkgd-green") }}
{{ render_button("<i class=\"fa-duotone fa-solid fa-download\"></i>Download <span class=\"t-monospace\">.ico</span>", False, None, "bkgd-green") }}
<div class="border r-m ico-maker-advanced">
<table class="table-p-xxs ">
<tr>
<td class="bkgd-grid40 rl-m br t-size-14 t-noselect" rowspan="2">
<i class="fad fa-grip-vertical c-not-allowed px-xxs t-super-muted"></i>
</td>
<td class="w-full bkgd-gray">
<div class="px-xxs">
<p>Required ICO Headers</p>
</div>
</td>
</tr>
<tr>
<td class="bkgd-gray">
<div class="px-xxs">
<p class="t-italic t-half-muted">
<i class="fa-duotone fa-solid fa-weight-hanging mr-xs"></i>1.23 KiB
</p>
</div>
</td>
</tr>
</table>
</div>
<div id="{{ applet_data.id }}-icon-parts-list">
<div class="border r-m">
<table class="table-p-xxs ">
<table class="table-p-xxs table-v-center">
<tr>
<td class="bkgd-grid40 rl-m br t-size-14 t-noselect" rowspan="2">
<i class="fad fa-grip-vertical c-not-allowed px-xxs t-super-muted"></i>
<td class="bkgd-grid40 rl-m br t-size-14 t-noselect">
<i class="fad fa-grip-vertical cursor-pointer px-xxs"></i>
</td>
<td class="w-full bkgd-gray">
<td class="w-full bkgd-gray bb">
<div class="px-xxs">
<p>Required ICO Headers</p>
<p class="t-bold">
<i class="fa-duotone fa-solid fa-image mr-xxs t-muted"></i>
MyFile.png
</p>
</div>
</td>
<td rowspan="3" class="bl bkgd-dark rr-m">BKGD IMG</td>
</tr>
<tr>
<td class="bkgd-grid40 rl-m br t-size-14 t-noselect bt">
<i class="fad fa-caret-up cursor-pointer px-xxs"></i>
</td>
<td class="bkgd-gray">
<div class="px-xxs">
<p class="t-italic t-half-muted">
<i class="fa-duotone fa-solid fa-ruler-combined mr-xs"></i>32x32
<i class="fa-duotone fa-solid fa-weight-hanging mr-xs ml-m"></i>12.5 KiB
<i class="fa-duotone fa-solid fa-palette mr-xs ml-m"></i>32 bpp
<i class="fa-duotone fa-solid fa-swatchbook mr-xs ml-m"></i>256 colors
</p>
</div>
</td>
</tr>
<tr>
<td class="bkgd-grid40 rl-m br t-size-14 t-noselect">
<i class="fad fa-caret-down cursor-pointer px-xxs"></i>
</td>
<td class="bkgd-gray">
<div class="px-xxs">
<p class="t-italic t-half-muted">
<i class="fa-duotone fa-solid fa-weight-hanging mr-xs"></i>1.23 KiB
</p>
{{ render_button("<i class=\"fa-duotone fa-solid fa-gear\"></i>Options", False, None, "bkgd-blue") }}
{{ render_button("<i class=\"fa-duotone fa-solid fa-trash\"></i>Remove", False, None, "bkgd-orange") }}
{{ render_button("<i class=\"fa-duotone fa-solid fa-file-circle-info\"></i>Details", False, None, "") }}
</div>
</td>
</tr>
</table>
</div>
<div id="{{ applet_data.id }}-icon-parts-list">
<div class="border r-m">
<table class="table-p-xxs table-v-center">
<tr>
<td class="bkgd-grid40 rl-m br t-size-14 t-noselect">
<i class="fad fa-grip-vertical cursor-pointer px-xxs"></i>
</td>
<td class="w-full bkgd-gray bb">
<div class="px-xxs">
<p class="t-bold">
<i class="fa-duotone fa-solid fa-image mr-xxs t-muted"></i>
MyFile.png
</p>
</div>
</td>
<td rowspan="3" class="bl bkgd-dark rr-m">BKGD IMG</td>
</tr>
<tr>
<td class="bkgd-grid40 rl-m br t-size-14 t-noselect bt">
<i class="fad fa-caret-up cursor-pointer px-xxs"></i>
</td>
<td class="bkgd-gray">
<div class="px-xxs">
<p class="t-italic t-half-muted">
<i class="fa-duotone fa-solid fa-ruler-combined mr-xs"></i>32x32
<i class="fa-duotone fa-solid fa-weight-hanging mr-xs ml-m"></i>12.5 KiB
<i class="fa-duotone fa-solid fa-palette mr-xs ml-m"></i>32 bpp
<i class="fa-duotone fa-solid fa-swatchbook mr-xs ml-m"></i>256 colors
</p>
</div>
</td>
</tr>
<tr>
<td class="bkgd-grid40 rl-m br t-size-14 t-noselect">
<i class="fad fa-caret-down cursor-pointer px-xxs"></i>
</td>
<td class="bkgd-gray">
<div class="px-xxs">
{{ render_button("<i class=\"fa-duotone fa-solid fa-gear\"></i>Options", False, None, "bkgd-blue") }}
{{ render_button("<i class=\"fa-duotone fa-solid fa-trash\"></i>Remove", False, None, "bkgd-orange") }}
{{ render_button("<i class=\"fa-duotone fa-solid fa-file-circle-info\"></i>Details", False, None, "") }}
</div>
</td>
</tr>
</table>
</div>
<div class="border r-m">
<table class="table-p-xxs table-v-center">
<tr>
<td class="bkgd-grid40 rl-m br t-size-14 t-noselect">
<i class="fad fa-grip-vertical cursor-pointer px-xxs"></i>
</td>
<td class="w-full bkgd-gray bb">
<div class="px-xxs">
<p class="t-bold">
<i class="fa-duotone fa-solid fa-binary mr-xxs t-muted"></i>
Watermark.bin
</p>
</div>
</td>
</tr>
<tr>
<td class="bkgd-grid40 rl-m br t-size-14 t-noselect bt">
<i class="fad fa-caret-up cursor-pointer px-xxs"></i>
</td>
<td class="bkgd-gray">
<div class="px-xxs">
<p class="t-italic t-half-muted">
<i class="fa-duotone fa-solid fa-weight-hanging mr-xs"></i>12.5 KiB
</p>
</div>
</td>
</tr>
<tr>
<td class="bkgd-grid40 rl-m br t-size-14 t-noselect">
<i class="fad fa-caret-down cursor-pointer px-xxs"></i>
</td>
<td class="bkgd-gray">
<div class="px-xxs">
{{ render_button("<i class=\"fa-duotone fa-solid fa-trash\"></i>Remove", False, None, "bkgd-orange") }}
</div>
</td>
</tr>
</table>
</div>
<div class="border r-m">
<table class="table-p-xxs table-v-center">
<tr>
<td class="bkgd-grid40 rl-m br t-size-14 t-noselect">
<i class="fad fa-grip-vertical cursor-pointer px-xxs"></i>
</td>
<td class="w-full bkgd-gray bb">
<div class="px-xxs">
<p class="t-bold t-italic">
<i class="fa-duotone fa-solid fa-text-size mr-xxs t-muted"></i>
Custom Text
</p>
</div>
</td>
</tr>
<tr>
<td class="bkgd-grid40 rl-m br t-size-14 t-noselect bt">
<i class="fad fa-caret-up cursor-pointer px-xxs"></i>
</td>
<td class="bkgd-gray">
<div class="px-xxs">
<label for="test123" hidden>321</label>
<textarea name="test123" id="test123" cols="30" rows="6"
class="p-xxs border r-m w-full ta-resize-v"></textarea>
</div>
</td>
</tr>
<tr>
<td class="bkgd-grid40 rl-m br t-size-14 t-noselect">
<i class="fad fa-caret-down cursor-pointer px-xxs"></i>
</td>
<td class="bkgd-gray">
<div class="px-xxs">
{{ render_button("<i class=\"fa-duotone fa-solid fa-gear\"></i>Options", False, None, "bkgd-blue") }}
{{ render_button("<i class=\"fa-duotone fa-solid fa-trash\"></i>Remove", False, None, "bkgd-orange") }}
</div>
</td>
</tr>
</table>
</div>
<div class="border r-m">
<table class="table-p-xxs table-v-center">
<tr>
<td class="bkgd-grid40 rl-m br t-size-14 t-noselect">
<i class="fad fa-grip-vertical cursor-pointer px-xxs"></i>
</td>
<td class="w-full bkgd-gray bb">
<div class="px-xxs">
<p class="t-bold">
<i class="fa-duotone fa-solid fa-binary mr-xxs t-muted"></i>
Watermark.bin
</p>
</div>
</td>
</tr>
<tr>
<td class="bkgd-grid40 rl-m br t-size-14 t-noselect bt">
<i class="fad fa-caret-up cursor-pointer px-xxs"></i>
</td>
<td class="bkgd-gray">
<div class="px-xxs">
<p class="t-italic t-half-muted">
<i class="fa-duotone fa-solid fa-weight-hanging mr-xs"></i>12.5 KiB
</p>
</div>
</td>
</tr>
<tr>
<td class="bkgd-grid40 rl-m br t-size-14 t-noselect">
<i class="fad fa-caret-down cursor-pointer px-xxs"></i>
</td>
<td class="bkgd-gray">
<div class="px-xxs">
{{ render_button("<i class=\"fa-duotone fa-solid fa-trash\"></i>Remove", False, None, "bkgd-orange") }}
</div>
</td>
</tr>
</table>
</div>
</section>
<div class="border r-m">
<table class="table-p-xxs table-v-center">
<tr>
<td class="bkgd-grid40 rl-m br t-size-14 t-noselect">
<i class="fad fa-grip-vertical cursor-pointer px-xxs"></i>
</td>
<td class="w-full bkgd-gray bb">
<div class="px-xxs">
<p class="t-bold t-italic">
<i class="fa-duotone fa-solid fa-text-size mr-xxs t-muted"></i>
Custom Text
</p>
</div>
</td>
</tr>
<tr>
<td class="bkgd-grid40 rl-m br t-size-14 t-noselect bt">
<i class="fad fa-caret-up cursor-pointer px-xxs"></i>
</td>
<td class="bkgd-gray">
<div class="px-xxs">
<label for="test123" hidden>321</label>
<textarea name="test123" id="test123" cols="30" rows="6"
class="p-xxs border r-m w-full ta-resize-v"></textarea>
</div>
</td>
</tr>
<tr>
<td class="bkgd-grid40 rl-m br t-size-14 t-noselect">
<i class="fad fa-caret-down cursor-pointer px-xxs"></i>
</td>
<td class="bkgd-gray">
<div class="px-xxs">
{{ render_button("<i class=\"fa-duotone fa-solid fa-gear\"></i>Options", False, None, "bkgd-blue") }}
{{ render_button("<i class=\"fa-duotone fa-solid fa-trash\"></i>Remove", False, None, "bkgd-orange") }}
</div>
</td>
</tr>
</table>
</div>
</div>
<section id="{{ applet_data.id }}-licenses">
{{ render_h2(l10n("licenses.title", "commons", user_lang)) }}
{% if is_standalone %}
<hr class="subtle">
{{ render_paragraph(l10n("licenses.1", applet_data.id, user_lang)) }}
{{ render_paragraph(l10n("licenses.2", applet_data.id, user_lang)) }}
</section>
{% endif %}

View File

@@ -0,0 +1,38 @@
{{ render_file_input(tool_id + "-test-input", true, ".png, .bmp", true, true) }}
<div class="w-full ox-auto">
<table class="table-stylish table-p-s border r-m w-full table-no-wrap">
<thead>
<tr>
<th class="bkgd-grid30">Offset</th>
<th class="bkgd-grid30">Chunk Header</th>
<th class="bkgd-grid30">Chunk Data</th>
<th class="bkgd-grid30">Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td class="t-monospace">0x0000</td>
<td>2</td>
<td>2</td>
<td>
<button class="p-xs r-s border btn-primary">
<i class="fa-duotone fa-solid fa-binary"></i><span class="ml-xxs mobile-hide">Hex dump</span>
</button>
<button class="p-xs r-s border btn-primary">
<i class="fa-duotone fa-solid fa-download"></i><span class="ml-xxs mobile-hide">Export</span>
</button>
</td>
</tr>
<tr>
<td class="t-monospace">0x0010</td>
<td>4</td>
<td>4</td>
<td>4</td>
</tr>
</tbody>
</table>
</div>

View File

@@ -1,5 +0,0 @@
{{ render_h2(l10n("upload.title", "commons", user_lang)) }}
{{ render_file_input("test-input", true, None, true, true) }}

View File

@@ -9,15 +9,20 @@
<label for="uuid-generator-option-count" class="mr-xs">{{ l10n("option.count", "uuid-generator", user_lang) }}:</label>
<input id="uuid-generator-option-count" class="p-xxs border r-s" type="number" value="4" min="1" max="1000">
<br>
<hr class="subtle">
<label for="uuid-generator-option-hyphens" class="mr-xxs">{{ l10n("option.hyphen", "uuid-generator", user_lang) }}:</label>
<input id="uuid-generator-option-hyphens" class="r-m border" type="checkbox" checked>
<input id="uuid-generator-option-hyphens" class="r-m border cb-pretty" type="checkbox" checked>
<br>
<label for="uuid-generator-option-guid-brackets" class="mr-xxs">{{ l10n("option.guid_brackets", "uuid-generator", user_lang) }}:</label>
<input id="uuid-generator-option-guid-brackets" class="r-m border" type="checkbox">
<input id="uuid-generator-option-guid-brackets" class="r-m border cb-pretty" type="checkbox">
<br>
<label for="uuid-generator-option-uppercase" class="mr-xxs">{{ l10n("option.uppercase", "uuid-generator", user_lang) }}:</label>
<input id="uuid-generator-option-uppercase" class="r-m border cb-pretty" type="checkbox">
<hr class="subtle">

View File

@@ -0,0 +1,295 @@
{%
set all_vat_data = [
["afghanistan",
[[10, "standard"]], [
"https://ard.gov.af/file_download/432/FAQs+of+VAT+English.pdf",
]],
["austria",
[[10, "reduced"],[13, "reduced"],[20, "standard"]], [
"https://www.usp.gv.at/en/themen/steuern-finanzen/umsatzsteuer-ueberblick/steuersaetze-und-steuerbefreiungen-der-umsatzsteuer.html",
]],
["belgium",
[[6, "reduced"],[12, "intermediate"],[21, "standard"]], [
"https://finance.belgium.be/en/enterprises/vat/vat-obligation/rates-and-calculation/vat-rates",
]],
["bulgaria",
[[9, "reduced"],[20, "standard"]], [
"https://www.bulgaria-tax-law.bg/vat-rates-eu-member-states.html"]],
["croatia",
[[5, "reduced"],[13, "reduced"],[25, "standard"]], [
"https://porezna-uprava.gov.hr/en/value-added-tax-h-e-reinafter-vat-information-on-the-general-rules-rates-and-exemptions-registering-for-and-paying-vat-obtaining-a-refund/7313",
]],
["cyprus",
[[3, "reduced"],[5, "reduced"],[9, "reduced"],[19, "standard"]], [
"https://www.mof.gov.cy/mof/tax/taxdep.nsf/All/6F2D9F654287FF02C2258251002C8130",
]],
["czechia",
[[12, "reduced"],[21, "standard"]], [
"https://portal.gov.cz/en/informace/general-rules-and-vat-rates-INF-205",
]],
["denmark",
[[25, "standard"]], [
"https://skat.dk/erhverv/moms/fradrag-for-moms",
]],
["estonia",
[[9, "reduced"],[13, "reduced"],[24, "standard"]], [
"https://www.emta.ee/en/business-client/taxes-and-payment/value-added-tax",
"https://www.e-resident.gov.ee/blog/posts/a-guide-to-vat-for-e-residents/",
]],
["finland",
[[10, "reduced"],[14, "reduced"],[25.5, "standard"]], [
"https://www.vero.fi/en/businesses-and-corporations/taxes-and-charges/vat/rates-of-vat/",
]],
["france",
[[2.1, "reduced"],[5.5, "reduced"],[10, "intermediate"],[20, "standard"]], [
"https://www.economie.gouv.fr/cedef/les-fiches-pratiques/quels-sont-les-taux-de-tva-en-vigueur-en-france-et-dans-lunion",
]],
["france.corsica",
[[0.9, "special"],[2.1, "reduced"],[5.5, "reduced"],[10, "intermediate"],[13, "special"],[20, "standard"]], [
"https://www.economie.gouv.fr/particuliers/impots-et-fiscalite/gerer-mes-autres-impots-et-taxes/tva-quels-sont-les-taux-de-votre",
]],
["germany",
[[7, "reduced"],[19, "standard"]], [
"https://www.bundesfinanzministerium.de/Content/DE/Downloads/BMF_Schreiben/Steuerarten/Umsatzsteuer/Merkblaetter/2024-03-05-Umsatzsteuer-Merkblatt-Personenbefoerderung-Kraftomnibusse-englisch.pdf",
]],
["greece",
[[6, "reduced.super"],[13, "reduced"],[24, "standard"]], [
"https://www.gov.gr/en/sdg/taxes/vat/general/basic-vat-rates",
]],
["hungary",
[[5, "preferential"],[18, "preferential"],[27, "standard"]], [
"https://nav.gov.hu/pfile/file?path=/en/taxation/taxinfo/vat-liabilities-of-foreign-marketers-in-hungary",
]],
["ireland",
[[4.8, "reduced"],[9, "reduced"],[13.5, "reduced"],[23, "standard"]], [
"https://www.revenue.ie/en/vat/vat-rates/search-vat-rates/current-vat-rates.aspx",
]],
["italy",
[[4, "reduced"],[5, "reduced"],[10, "reduced"],[22, "standard"]], [
"https://www.agenziaentrate.gov.it/portale/web/english/nse/services/vat-mini-one-stop-shop/faq/vat-rates",
"https://www.agenziaentrate.gov.it/portale/web/english/general-vat-rules-and-rates"
]],
["latvia",
[[5, "reduced"],[12, "reduced"],[21, "standard"]], [
"https://www.fm.gov.lv/lv/tax-rates",
]],
["lithuania",
[[5, "reduced"],[9, "reduced"],[21, "standard"]], [
"https://finmin.lrv.lt/en/competence-areas/taxation/main-taxes/value-added-tax/",
]],
["luxembourg",
[[3, "reduced.super"],[8, "reduced"],[14, "intermediate"],[17, "standard"]], [
"https://logistics.public.lu/en/formalities-procedures/taxes/value-added-tax/national-operations.html",
]],
["malta",
[[5, "reduced"],[7, "reduced"],[12, "reduced"],[18, "standard"]], [
"https://mtca.gov.mt/business-tax/vat1/vat-compliance/vat-rates/vat-rates",
]],
["monaco",
[[2.1, "reduced"],[5.5, "reduced"],[10, "intermediate"],[20, "standard"]], [
"https://monentreprise.gouv.mc/en/themes/accounting-obligations-and-tax/tax/vat",
"https://www.economie.gouv.fr/particuliers/impots-et-fiscalite/gerer-mes-autres-impots-et-taxes/tva-quels-sont-les-taux-de-votre"
]],
["netherlands",
[[9, "reduced"],[21, "standard"]], [
"https://business.gov.nl/regulation/vat-rates-exemptions/",
]],
["poland",
[[5, "reduced"],[8, "reduced"],[23, "standard"]], [
"https://www.podatki.gov.pl/en/value-added-tax/general-vat-rules-and-rates/list-of-vat-rates/",
]],
["portugal",
[[6, "reduced"],[13, "intermediate"],[23, "standard"]], [
"https://www2.gov.pt/en/cidadaos-europeus-viajar-viver-e-fazer-negocios-em-portugal/impostos-para-atividades-economicas-em-portugal/imposto-sobre-valor-acrescentado-iva-em-portugal",
]],
["portugal.azores",
[[4, "reduced"],[9, "intermediate"],[16, "standard"]], [
"https://www2.gov.pt/en/cidadaos-europeus-viajar-viver-e-fazer-negocios-em-portugal/impostos-para-atividades-economicas-em-portugal/imposto-sobre-valor-acrescentado-iva-em-portugal",
]],
["portugal.madeira",
[[5, "reduced"],[12, "intermediate"],[22, "standard"]], [
"https://www2.gov.pt/en/cidadaos-europeus-viajar-viver-e-fazer-negocios-em-portugal/impostos-para-atividades-economicas-em-portugal/imposto-sobre-valor-acrescentado-iva-em-portugal",
]],
["romania",
[[5, "reduced"],[9, "reduced"],[19, "standard"]], [
"https://mfinante.gov.ro/referinte-tva",
"https://www.mfinante.gov.ro/static/10/Mfp/legislatie/Ghid_TVA_parteaI.htm"
]],
["slovakia",
[[5, "reduced"],[19, "reduced"],[23, "standard"]], [
"https://www.slovensko.sk/en/life-situation/life-situation/_value-added-tax/",
]],
["slovenia",
[[9.5, "reduced"],[22, "standard"]], [
"https://www.fu.gov.si/en/taxes_and_other_duties/areas_of_work/value_added_tax_vat",
]],
["spain",
[[4, "reduced"],[10, "reduced"],[21, "standard"]], [
"https://sede.agenciatributaria.gob.es/Sede/en_gb/iva/calculo-iva-repercutido-clientes/tipos-impositivos-iva.html"
"https://sede.agenciatributaria.gob.es/Sede/iva.html",
]],
["sweden",
[[6, "reduced"],[12, "reduced"],[25, "standard"]], [
"https://www.skatteverket.se/servicelankar/otherlanguages/englishengelska/businessesandemployers/startingandrunningaswedishbusiness/declaringtaxesbusinesses/vat/vatratesandvatexemption.4.676f4884175c97df419255d.html",
]],
]
%}
<label for="vat-calculator-preset-short" class="mr-xxs">
{{ l10n("preset.label", "vat-calculator", user_lang) }}:
</label>
<select name="vat-calculator-preset-short" id="vat-calculator-preset-short" class="p-xxs border r-s">
<option value="" selected>{{ l10n("rate.option.custom", "vat-calculator", user_lang) }}</option>
<hr>
{% for country_vat_data in all_vat_data %}
<optgroup label="{{ l10n("country." + country_vat_data[0], "commons", user_lang) }}">
{% for country_vat_rate in country_vat_data[1] %}
<option value="{{ country_vat_rate[0] }}">
{{ country_vat_rate[0] }} %
</option>
{% endfor %}
</optgroup>
{% endfor %}
</select>
<label for="vat-calculator-preset-detailed" class="mr-xxs" hidden>
{{ l10n("preset.label", "vat-calculator", user_lang) }}:
</label>
<select name="vat-calculator-preset-detailed" id="vat-calculator-preset-detailed" class="p-xxs border r-s" hidden>
<option value="" selected>{{ l10n("rate.option.custom", "vat-calculator", user_lang) }}</option>
<hr>
{% for country_vat_data in all_vat_data %}
<optgroup label="{{ l10n("country." + country_vat_data[0], "commons", user_lang) }}">
{% for country_vat_rate in country_vat_data[1] %}
<option value="{{ country_vat_rate[0] }}">
{{ country_vat_rate[0] }} %&emsp;({{ l10n("rate.type." + country_vat_rate[1], "vat-calculator", user_lang) }})
</option>
{% endfor %}
</optgroup>
{% endfor %}
</select>
<span id="vat-calculator-preset-country-echo" class="t-muted ml-xs t-italic mobile-hide"></span>
<br>
<label for="vat-calculator-detailed-presets" class="mr-xxs">{{ l10n("option.detailed", "vat-calculator", user_lang) }}:</label>
<input id="vat-calculator-detailed-presets" class="r-m border cb-pretty" type="checkbox">
<hr class="subtle">
<!-- TODO: Implement iframe-able template for multi-instance applets on the same page -->
<table class="">
<tr>
<td>
<input type="radio" id="vat-calculator-radio-rate" name="vat_calc_target" value="0"
class="radio-solid border mr-xxs radio-unchecked-subtle">
<label for="vat-calculator-radio-rate">
{{ l10n("radio.rate", "vat-calculator", user_lang) }}
</label>
</td>
<td>
<label for="vat-calculator-input-rate" class="mr-xs"></label>
<input id="vat-calculator-input-rate" class="p-xxs border r-s" type="number" min="0">
{% if not is_standalone %}<i class="fa-duotone fa-solid fa-percent ml-xxs t-muted"></i>{% else %}%{% endif %}
</td>
</tr>
<tr>
<td class="pt-xxs">
<input type="radio" id="vat-calculator-radio-untaxed" name="vat_calc_target" value="1"
class="radio-solid border mr-xxs radio-unchecked-subtle" checked>
<label for="vat-calculator-radio-untaxed">
{{ l10n("radio.untaxed", "vat-calculator", user_lang) }}
</label>
</td>
<td class="pt-xxs">
<label for="vat-calculator-input-untaxed" class="mr-xs"></label>
<input id="vat-calculator-input-untaxed" class="p-xxs border r-s bkgd-gray" type="number" min="0" readonly>
{% if not is_standalone %}<i class="fa-duotone fa-solid fa-money-bill ml-xxs t-muted"></i>{% endif %}
</td>
</tr>
<tr>
<td class="pt-xxs">
<input type="radio" id="vat-calculator-radio-taxed" name="vat_calc_target" value="2"
class="radio-solid border mr-xxs radio-unchecked-subtle">
<label for="vat-calculator-radio-taxed">
{{ l10n("radio.taxed", "vat-calculator", user_lang) }}
</label>
</td>
<td class="pt-xxs">
<label for="vat-calculator-input-taxed" class="mr-xs"></label>
<input id="vat-calculator-input-taxed" class="p-xxs border r-s" type="number" min="0">
{% if not is_standalone %}<i class="fa-duotone fa-solid fa-money-bill ml-xxs t-muted"></i>{% endif %}
</td>
</tr>
</table>
<p class="t-muted ml-xs mt-xs t-italic">{{ l10n("text.radio.explanation", "vat-calculator", user_lang) }}</p>
<hr class="subtle">
<label for="vat-calculator-rounding-mode" class="mr-xxs">
{{ l10n("rounding.mode.label", "vat-calculator", user_lang) }}:
</label>
<select name="vat-calculator-rounding-mode" id="vat-calculator-rounding-mode" class="p-xxs border r-s">
<optgroup label="{{ l10n("rounding.mode.group.regular", "vat-calculator", user_lang) }}">
<option value="0" selected>{{ l10n("rounding.mode.up", "vat-calculator", user_lang) }}</option>
<option value="1">{{ l10n("rounding.mode.down", "vat-calculator", user_lang) }}</option>
<option value="2">{{ l10n("rounding.mode.ceil", "vat-calculator", user_lang) }}</option>
<option value="3">{{ l10n("rounding.mode.floor", "vat-calculator", user_lang) }}</option>
</optgroup>
<optgroup label="{{ l10n("rounding.mode.group.half", "vat-calculator", user_lang) }}">
<option value="4">{{ l10n("rounding.mode.up.half", "vat-calculator", user_lang) }}</option>
<option value="5">{{ l10n("rounding.mode.down.half", "vat-calculator", user_lang) }}</option>
<option value="6">{{ l10n("rounding.mode.even.half", "vat-calculator", user_lang) }}</option>
<option value="7">{{ l10n("rounding.mode.ceil.half", "vat-calculator", user_lang) }}</option>
<option value="8">{{ l10n("rounding.mode.floor.half", "vat-calculator", user_lang) }}</option>
</optgroup>
</select>
<br>
<label for="vat-calculator-option-decimal-places" class="mr-xs">{{ l10n("option.decimal-places", "vat-calculator", user_lang) }}:</label>
<button id="vat-calculator-decimal-places-minus" class="p-xxs border br-0 rl-s {% if is_standalone %}px-xs{% endif %}">
{% if not is_standalone %}<i class="fa-duotone fa-solid fa-minus"></i>{% else %}-{% endif %}
</button>
<input id="vat-calculator-option-decimal-places" class="p-xxs border" type="number" value="2" min="0" max="99">
<button id="vat-calculator-decimal-places-plus" class="p-xxs border bl-0 rr-s {% if is_standalone %}px-xs{% endif %}">
{% if not is_standalone %}<i class="fa-duotone fa-solid fa-plus"></i>{% else %}+{% endif %}
</button>
<br hidden>
<label for="vat-calculator-detailed-trim-zeroes" class="mr-xxs" hidden>{{ l10n("option.trim-zeroes", "vat-calculator", user_lang) }}:</label>
<input id="vat-calculator-detailed-trim-zeroes" class="r-m border cb-pretty" type="checkbox" hidden>
<!--<hr class="subtle">
<details class="border bkgd-dark r-m mt-s">
<summary class="p-xs">Click to show/hide all classes</summary>
<div class="p-xs bt bkgd-grey">
<p>
<span class="code mr-xs">p-0</span>
</p>
</div>
</details>-->
{% if is_standalone %}
<hr class="subtle">
<p class="t-half-muted">
{{ l10n("license.text.1", "vat-calculator", user_lang) }}<br>
{{ l10n("license.text.2", "vat-calculator", user_lang) }}
</p>
{% endif %}

View File

@@ -7,6 +7,7 @@
<meta name="viewport" content="width=device-width"/>
<meta name="author" content="Herwin Bozet">
<meta name="robots" content="index, follow">
<meta name="theme-color" content="#6F2F65">
<!-- Note: https://css-tricks.com/probably-dont-base64-svg/ -->
<link rel="icon"

View File

@@ -9,21 +9,20 @@
<meta name="robots" content="index, follow">
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="alternate icon" href="/favicon.ico">
<meta name="theme-color" content="#6F2F65">
<!--<link rel="dns-prefetch" href="https://cdn.nibblepoker.lu/"/>-->
<link rel="preconnect" href="https://cdn.nibblepoker.lu/"/>
<link rel="preconnect" href="https://cdn.nibblepoker.{{ domain_tld }}/"/>
<link rel="prefetch" href="https://cdn.nibblepoker.lu/FontAwesomePro/6.7.2/webfonts/fa-brands-400.woff2" as="font" />
<link rel="prefetch" href="https://cdn.nibblepoker.lu/FontAwesomePro/6.7.2/webfonts/fa-duotone-900.woff2" as="font" />
<link rel="prefetch" href="https://cdn.nibblepoker.lu/FontAwesomePro/6.7.2/webfonts/fa-solid-900.woff2" as="font" />
<link rel="prefetch" href="https://cdn.nibblepoker.lu/NibblePoker/StandardCSS/3px-tile-0.1.png" as="image" />
<link rel="prefetch" href="https://cdn.nibblepoker.lu/NibblePoker/StandardCSS/3px-tile-0.4.png" as="image" />
<link rel="prefetch" href="https://cdn.nibblepoker.{{ domain_tld }}/FontAwesomePro/6.7.2/webfonts/fa-brands-400.woff2" as="font" />
<link rel="prefetch" href="https://cdn.nibblepoker.{{ domain_tld }}/FontAwesomePro/6.7.2/webfonts/fa-duotone-900.woff2" as="font" />
<link rel="prefetch" href="https://cdn.nibblepoker.{{ domain_tld }}/FontAwesomePro/6.7.2/webfonts/fa-solid-900.woff2" as="font" />
{% block extra_preloads %}{% endblock %}
<link rel="stylesheet" href="https://cdn.nibblepoker.{{ domain_tld }}/FontAwesomePro/6.7.2/css/all.min.css">
<!--<link rel="stylesheet" href="https://cdn.nibblepoker.{{ domain_tld }}/NibblePoker/StandardCSS/nibblepoker.min.css">-->
<link rel="stylesheet" href="https://cdn.nibblepoker.{{ domain_tld }}/NibblePoker/IndevCSS/nibblepoker.min.css">
<link rel="stylesheet" href="https://cdn.nibblepoker.{{ domain_tld }}/Quantum/Quantum.min.css">
<link rel="stylesheet" href="https://cdn.nibblepoker.{{ domain_tld }}/NibblePoker/StandardCSS/nibblepoker.min.css">
<!--<link rel="stylesheet" href="https://cdn.nibblepoker.{{ domain_tld }}/Quantum/Quantum.min.css">-->
<link rel="stylesheet" href="https://cdn.nibblepoker.{{ domain_tld }}/FamFamFam/FlagsExtended/famfamfam-flags.min.css">
<link rel="stylesheet" href="{{ url_for("static", filename="resources/NibblePoker/css/extra.css") }}">
@@ -34,6 +33,10 @@
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{% block head_title %}{% endblock %}</title>
{% block opengraph_tags %}{% endblock %}
{% block extra_head_tags %}{% endblock %}
</head>
<body class="layout-generic">
@@ -44,21 +47,42 @@
<details id="lang-selector" class="border p-mxs px-s bkgd-blank-dark r-m">
<summary>
<i class="fad fa-language"></i>
<span class="mobile-hide t-w-500">&nbsp;{{ l10n("menu.title", "langs", lang) }}</span>
<span class="mobile-hide t-w-500">&nbsp;{{ l10n("menu.title", "langs", user_lang) }}</span>
&nbsp;<i class="fa fa-angle-down"></i>
</summary>
<div class="p-xs border bkgd-surround r-m t-w-500">
<a href="{{ l10n_url_switch(request_path, "en") }}" class="a-hidden">
<p class="mb-s px-xxs">{{ l10n("english", "langs", lang) }}</p>
</a>
<a href="{{ l10n_url_switch(request_path, "fr") }}" class="a-hidden">
<p class="my-s px-xxs">{{ l10n("french", "langs", lang) }}</p>
</a>
<hr class="subtle m-0">
<a href="{{ l10n_url_switch(request_path) }}" class="a-hidden">
<p class="mt-xs px-xxs">{{ l10n("automatic", "langs", lang) }}</p>
</a>
<table class="w-full table-p-xxs">
<tr>
<td>
<a href="{{ l10n_url_switch(request_path, "en") }}" class="w-full d-inline-block a-hidden">
{{ l10n("english", "langs", user_lang) }}
</a>
</td>
<td class="t-center"><i class="famfamfam-flag-us"></i></td>
</tr>
<tr>
<td>
<a href="{{ l10n_url_switch(request_path, "fr") }}" class="w-full d-inline-block a-hidden">
{{ l10n("french", "langs", user_lang) }}
</a>
</td>
<td class="t-center"><i class="famfamfam-flag-be"></i></td>
</tr>
<tr>
<td colspan="2">
<hr class="subtle m-0">
</td>
</tr>
<tr>
<td>
<a href="{{ l10n_url_switch(request_path) }}" class="w-full d-inline-block a-hidden">
{{ l10n("automatic", "langs", user_lang) }}
</a>
</td>
<td class="t-center"><i class="famfamfam-flag-unknown"></i></td>
</tr>
</table>
</div>
</details>
</header>
@@ -67,7 +91,7 @@
<a href="{{ l10n_url_abs('/', raw_lang) }}" class="no-select">
<img class="logo-sidebar-v2"
src="https://cdn.nibblepoker.{{ domain_tld }}/NibblePoker/Logos/v2_full_shaded_optimized.svg"
alt="{{ l10n("logo.alt", "sidebar", lang) }}"
alt="{{ l10n("logo.alt", "sidebar", user_lang) }}"
draggable="false">
</a>
<div class="p-xs"></div>
@@ -82,7 +106,7 @@
>
<p class="t-size-18 t-w-500 py-xs sidebar-entry">
<i class="{{ sidebar_entry.icon }} pr-xs t-size-12 t-half-muted"></i>
<span class="t-size-12">{{ l10n(sidebar_entry.title_key, "sidebar", lang) }}</span>
<span class="t-size-12">{{ l10n(sidebar_entry.title_key, "sidebar", user_lang) }}</span>
{% if sidebar_entry.has_new() %}
<i class="fa-duotone fa-solid fa-sparkles ml-auto t-size-11 t-muted"></i>
{% endif %}
@@ -99,18 +123,19 @@
</main>
<footer class="d-flex flex-align-center w-full p-s py-xs">
<button id="sidebar-toggle-footer" class="p-xs border r-s t-size-10" aria-label="{{ l10n("alt.sidebar.button", "footer", lang) }}">
<button id="sidebar-toggle-footer" class="p-xs border r-s t-size-10"
aria-label="{{ l10n("alt.sidebar.button", "footer", user_lang) }}">
<i class="fa fa-bars px-xxs" aria-hidden="true"></i>
</button>
<p class="flex-fill t-center t-size-10 t-w-500 t-muted">
<a class="a-hidden" href="{{ l10n_url_abs('/privacy/', raw_lang) }}">
{{ l10n("text.privacy", "footer", lang) }}
{{ l10n("text.privacy", "footer", user_lang) }}
</a>
</p>
<a href="{{ l10n_url_abs('/', raw_lang) }}">
<img id="logo-footer"
src="https://cdn.nibblepoker.{{ domain_tld }}/NibblePoker/Logos/v2_full_unshaded_original.svg"
alt="{{ l10n("alt.logo", "footer", lang) }}" draggable="false">
alt="{{ l10n("alt.logo", "footer", user_lang) }}" draggable="false">
</a>
</footer>

View File

@@ -76,18 +76,7 @@
<section>
{{ render_h1(l10n("v2.third.title", "privacy", user_lang), "fad fa-handshake") }}
<div class="content-spacer">
{{ render_paragraph(l10n("v2.third.intro.1", "privacy", user_lang) +
'<br>' + l10n("v2.third.intro.2", "privacy", user_lang)) }}
{{ render_paragraph(l10n("v2.third.intro.3", "privacy", user_lang)) }}
{{ render_paragraph(l10n('v2.third.intro.4', "privacy", user_lang) +
'<br><i class="fad fa-globe ml-s t-size-8 mr-xs"></i>IONOS' +
'<a class="ml-s" href="https://www.ionos.fr/terms-gtc/clause-de-confidentialite/">' +
l10n('french', "langs", user_lang) + '</a>' +
'<a class="ml-s" href="https://www.ionos.com/terms-gtc/privacy-policy/">' +
l10n('english', "langs", user_lang) + '</a>' +
'<br><i class="fad fa-globe ml-s t-size-8 mr-xs"></i>ChicagoVPS' +
'<a class="ml-s" href="https://www.chicagovps.net/wp-content/uploads/2023/07/Terms-and-Conditions-ChicagoVPS.pdf">' +
l10n('english', "langs", user_lang) + '</a>') }}
{{ render_paragraph(l10n("v3.third.intro.1", "privacy", user_lang)) }}
</div>
</section>
@@ -105,18 +94,18 @@
<div class="content-spacer">
{{ render_paragraph(l10n("v2.update.intro.1", "privacy", user_lang)) }}
{{ render_paragraph('<i class="fad fa-calendar-alt mr-xs"></i>' + l10n("v2.update.history.1.date", "privacy", user_lang) +
'<span class="ml-l"><i class="fad fa-globe t-size-8 mr-xxs"></i><a href="/privacy/privacy_2021-12-04_en.txt">' +
'<span class="ml-l"><i class="fad fa-globe t-size-8 mr-xxs"></i><a href="/resources/NibblePoker/privacy/privacy_2021-12-04_en.txt">' +
l10n('english', "langs", user_lang) + '</a></span>' +
'<span class="ml-l"><i class="fad fa-globe t-size-8 mr-xxs"></i><a href="/privacy/privacy_2021-12-04_fr.txt">' +
'<span class="ml-l"><i class="fad fa-globe t-size-8 mr-xxs"></i><a href="/resources/NibblePoker/privacy/privacy_2021-12-04_fr.txt">' +
l10n('french', "langs", user_lang) + '</a></span>' )}}
{{ render_list_ul([
l10n('v2.update.history.1.desc.1', "privacy", user_lang)
]) }}
{{ render_paragraph('<i class="fad fa-calendar-alt mr-xs"></i>' + l10n("v2.update.history.2.date", "privacy", user_lang) +
'<span class="ml-l"><i class="fad fa-globe t-size-8 mr-xxs"></i><a href="/privacy/privacy_2022-03-18_en.txt">' +
'<span class="ml-l"><i class="fad fa-globe t-size-8 mr-xxs"></i><a href="/resources/NibblePoker/privacy/privacy_2022-03-18_en.txt">' +
l10n('english', "langs", user_lang) + '</a></span>' +
'<span class="ml-l"><i class="fad fa-globe t-size-8 mr-xxs"></i><a href="/privacy/privacy_2022-03-18_fr.txt">' +
'<span class="ml-l"><i class="fad fa-globe t-size-8 mr-xxs"></i><a href="/resources/NibblePoker/privacy/privacy_2022-03-18_fr.txt">' +
l10n('french', "langs", user_lang) + '</a></span>' )}}
{{ render_list_ul([
l10n('v2.update.history.2.desc.1', "privacy", user_lang),
@@ -125,9 +114,9 @@
]) }}
{{ render_paragraph('<i class="fad fa-calendar-alt mr-xs"></i>' + l10n("v2.update.history.3.date", "privacy", user_lang) +
'<span class="ml-l"><i class="fad fa-globe t-size-8 mr-xxs"></i><a href="/privacy/privacy_2022-09-09_en.txt">' +
'<span class="ml-l"><i class="fad fa-globe t-size-8 mr-xxs"></i><a href="/resources/NibblePoker/privacy/privacy_2022-09-09_en.txt">' +
l10n('english', "langs", user_lang) + '</a></span>' +
'<span class="ml-l"><i class="fad fa-globe t-size-8 mr-xxs"></i><a href="/privacy/privacy_2022-09-09_fr.txt">' +
'<span class="ml-l"><i class="fad fa-globe t-size-8 mr-xxs"></i><a href="/resources/NibblePoker/privacy/privacy_2022-09-09_fr.txt">' +
l10n('french', "langs", user_lang) + '</a></span>' )}}
{{ render_list_ul([
l10n('v2.update.history.3.desc.1', "privacy", user_lang),
@@ -137,9 +126,9 @@
]) }}
{{ render_paragraph('<i class="fad fa-calendar-alt mr-xs"></i>' + l10n("v2.update.history.4.date", "privacy", user_lang) +
'<span class="ml-l"><i class="fad fa-globe t-size-8 mr-xxs"></i><a href="/privacy/privacy_2023-11-11_en.txt">' +
'<span class="ml-l"><i class="fad fa-globe t-size-8 mr-xxs"></i><a href="/resources/NibblePoker/privacy/privacy_2023-11-11_en.txt">' +
l10n('english', "langs", user_lang) + '</a></span>' +
'<span class="ml-l"><i class="fad fa-globe t-size-8 mr-xxs"></i><a href="/privacy/privacy_2023-11-11_fr.txt">' +
'<span class="ml-l"><i class="fad fa-globe t-size-8 mr-xxs"></i><a href="/resources/NibblePoker/privacy/privacy_2023-11-11_fr.txt">' +
l10n('french', "langs", user_lang) + '</a></span>' )}}
{{ render_list_ul([
l10n('v2.update.history.4.desc.1', "privacy", user_lang),
@@ -148,15 +137,25 @@
]) }}
{{ render_paragraph('<i class="fad fa-calendar-alt mr-xs"></i>' + l10n("v2.update.history.5.date", "privacy", user_lang) +
'<span class="ml-l"><i class="fad fa-globe t-size-8 mr-xxs"></i><a href="/privacy/privacy_2023-11-30_en.txt">' +
'<span class="ml-l"><i class="fad fa-globe t-size-8 mr-xxs"></i><a href="/resources/NibblePoker/privacy/privacy_2023-11-30_en.txt">' +
l10n('english', "langs", user_lang) + '</a></span>' +
'<span class="ml-l"><i class="fad fa-globe t-size-8 mr-xxs"></i><a href="/privacy/privacy_2023-11-30_fr.txt">' +
'<span class="ml-l"><i class="fad fa-globe t-size-8 mr-xxs"></i><a href="/resources/NibblePoker/privacy/privacy_2023-11-30_fr.txt">' +
l10n('french', "langs", user_lang) + '</a></span>' )}}
{{ render_list_ul([
l10n('v2.update.history.5.desc.1', "privacy", user_lang),
l10n('v2.update.history.5.desc.2', "privacy", user_lang)
]) }}
{{ render_paragraph('<i class="fad fa-calendar-alt mr-xs"></i>' + l10n("v2.update.history.6.date", "privacy", user_lang) +
'<span class="ml-l"><i class="fad fa-globe t-size-8 mr-xxs"></i><a href="/resources/NibblePoker/privacy/privacy_2025-03-30_en.txt">' +
l10n('english', "langs", user_lang) + '</a></span>' +
'<span class="ml-l"><i class="fad fa-globe t-size-8 mr-xxs"></i><a href="/resources/NibblePoker/privacy/privacy_2025-03-30_fr.txt">' +
l10n('french', "langs", user_lang) + '</a></span>' )}}
{{ render_list_ul([
l10n('v2.update.history.6.desc.1', "privacy", user_lang),
l10n('v2.update.history.6.desc.2', "privacy", user_lang)
]) }}
{{ render_paragraph(l10n("v2.update.end.2", "privacy", user_lang)) }}
</div>
</section>

View File

@@ -14,21 +14,21 @@
{% block content_listing %}
{% for project_id, project_data in get_projects().items() %}
{% for project_data in get_sorted_projects_by_tags(requested_tags) %}
<hr class="subtle">
<!--<div class="p-s border r-m">-->
<div class="p-xs r-m">
<a href="{{ l10n_url_abs('/content/' + project_id, raw_lang) }}" class="a-hidden">
<a href="{{ l10n_url_abs('/content/' + project_data.id, raw_lang) }}" class="a-hidden">
<div class="content-search-entry">
<img class="content-search-image mr-s r-l"
src="{{ url_for("static", filename=project_data.metadata.index.image_url) }}"
alt="TODO">
<h3 class="mb-xs">
{{ l10n(project_data.metadata.index.title_key, project_id, user_lang) }}
{{ l10n(project_data.metadata.index.title_key, project_data.id, user_lang) }}
</h3>
<p>{{ l10n(project_data.metadata.index.preamble_key, project_id, user_lang) }}</p>
<p>{{ l10n(project_data.metadata.index.preamble_key, project_data.id, user_lang) }}</p>
</div>
</a>

View File

@@ -12,44 +12,58 @@
{% endblock %}
{% block main_content %}
{{ render_h1(l10n("intro.title", "home", user_lang, ["NibblePoker." + domain_tld] )) }}
<section>
{{ render_h1(l10n("intro.title", "home", user_lang, ["NibblePoker." + domain_tld] )) }}
{{ render_paragraph(l10n("intro.text.1", "home", user_lang)) }}
{{ render_paragraph(l10n("intro.text.2", "home", user_lang)) }}
<div class="content-spacer">
{{ render_paragraph(l10n("intro.text.1", "home", user_lang)) }}
{{ render_paragraph(l10n("intro.text.2", "home", user_lang)) }}
</div>
</section>
<section>
{{ render_h2(l10n("updates.title", "home", user_lang)) }}
{{ render_h2(l10n("updates.title", "home", user_lang)) }}
<div class="content-spacer">
<p><i class="fad fa-calendar-alt mr-xs"></i>{{ l10n("updates.5.date", "home", user_lang) }}</p>
{{ render_list_ul([
l10n("updates.5.text.1", "home", user_lang),
l10n("updates.5.text.2", "home", user_lang),
l10n("updates.text.privacy", "home", user_lang)
]) }}
<p><i class="fad fa-calendar-alt mr-xs mt-s"></i>{{ l10n("updates.4.date", "home", user_lang) }}</p>
{{ render_list_ul([
l10n("updates.4.text.1", "home", user_lang),
l10n("updates.4.text.2", "home", user_lang),
l10n("updates.4.text.3", "home", user_lang),
l10n("updates.text.privacy", "home", user_lang)
]) }}
<p><i class="fad fa-calendar-alt mr-xs mt-s"></i>{{ l10n("updates.4.date", "home", user_lang) }}</p>
{{ render_list_ul([
l10n("updates.4.text.1", "home", user_lang),
l10n("updates.4.text.2", "home", user_lang),
l10n("updates.4.text.3", "home", user_lang),
l10n("updates.text.privacy", "home", user_lang)
]) }}
<p><i class="fad fa-calendar-alt mr-xs mt-s"></i>{{ l10n("updates.3.date", "home", user_lang) }}</p>
{{ render_list_ul([
l10n("updates.3.text.1", "home", user_lang),
l10n("updates.3.text.2", "home", user_lang),
l10n("updates.3.text.3", "home", user_lang),
l10n("updates.text.privacy", "home", user_lang)
]) }}
<p><i class="fad fa-calendar-alt mr-xs mt-s"></i>{{ l10n("updates.3.date", "home", user_lang) }}</p>
{{ render_list_ul([
l10n("updates.3.text.1", "home", user_lang),
l10n("updates.3.text.2", "home", user_lang),
l10n("updates.3.text.3", "home", user_lang),
l10n("updates.text.privacy", "home", user_lang)
]) }}
<p><i class="fad fa-calendar-alt mr-xs mt-s"></i>{{ l10n("updates.2.date", "home", user_lang) }}</p>
{{ render_list_ul([
l10n("updates.2.text.1", "home", user_lang),
l10n("updates.2.text.2", "home", user_lang),
l10n("updates.2.text.3", "home", user_lang),
l10n("updates.2.text.4", "home", user_lang),
l10n("updates.text.privacy", "home", user_lang)
]) }}
<p><i class="fad fa-calendar-alt mr-xs mt-s"></i>{{ l10n("updates.2.date", "home", user_lang) }}</p>
{{ render_list_ul([
l10n("updates.2.text.1", "home", user_lang),
l10n("updates.2.text.2", "home", user_lang),
l10n("updates.2.text.3", "home", user_lang),
l10n("updates.2.text.4", "home", user_lang),
l10n("updates.text.privacy", "home", user_lang)
]) }}
<p><i class="fad fa-calendar-alt mr-xs mt-s"></i>{{ l10n("updates.1.date", "home", user_lang) }}</p>
{{ render_list_ul([
l10n("updates.1.text.1", "home", user_lang),
l10n("updates.text.privacy", "home", user_lang)
]) }}
<p><i class="fad fa-calendar-alt mr-xs mt-s"></i>{{ l10n("updates.1.date", "home", user_lang) }}</p>
{{ render_list_ul([
l10n("updates.1.text.1", "home", user_lang),
l10n("updates.text.privacy", "home", user_lang)
]) }}
</div>
</section>
{% endblock %}

View File

@@ -14,20 +14,20 @@
{% block content_listing %}
{% for tool_id, tool_data in get_tools().items() %}
{% for tool_data in get_sorted_tools_by_tags(requested_tags) %}
<hr class="subtle">
<!--<div class="p-s border r-m">-->
<div class="p-xs r-m">
<a href="{{ l10n_url_abs('/tools/' + tool_id, raw_lang) }}" class="a-hidden">
<a href="{{ l10n_url_abs('/tools/' + tool_data.id, raw_lang) }}" class="a-hidden">
<div class="content-search-entry">
<img class="content-search-image mr-s r-l"
src="{{ url_for("static", filename=tool_data.metadata.index.image_url) }}"
alt="TODO">
<h3 class="mb-xs">
{{ l10n(tool_data.metadata.index.title_key, tool_id, user_lang) }}
{{ l10n(tool_data.metadata.index.title_key, tool_data.id, user_lang) }}
</h3>
<p>{{ l10n(tool_data.metadata.index.preamble_key, tool_id, user_lang) }}</p>
<p>{{ l10n(tool_data.metadata.index.preamble_key, tool_data.id, user_lang) }}</p>
</div>
</a>

View File

@@ -23,6 +23,32 @@
{% endif %}
{% endblock %}
{% block opengraph_tags %}
<!-- Required fields -->
<meta property="og:title"
content="{{ l10n(tool_data.metadata.opengraph.title_key, tool_id, user_lang) }}">
<meta property="og:url"
content="{{ "https://nibblepoker." + domain_tld + "/tools/" + tool_id }}">
<meta property="og:description"
content="{{ l10n(tool_data.metadata.opengraph.description_key, tool_id, user_lang) }}">
<!-- Optional fields -->
{% if tool_data.metadata.opengraph.image_url is not none %}
<meta property="og:image"
content="{{ "https://nibblepoker." + domain_tld + tool_data.metadata.opengraph.image_url }}">
{% if tool_data.metadata.opengraph.image_type is not none %}
<meta property="og:image:type"
content="{{ tool_data.metadata.opengraph.image_type }}">
{% endif %}
{% endif %}
{% endblock %}
{% block extra_head_tags %}
<meta name="description"
content="{{ l10n(tool_data.metadata.head.description_key, tool_id, user_lang) }}">
<!--<meta name="keywords" content="HTML, CSS, JavaScript">-->
{% endblock %}
{% block main_content %}
{% if not is_standalone %}
{{ render_h1(

View File

@@ -13,6 +13,32 @@
<span class="mobile-hide"><span class="mx-s t-size-15">❱</span>{{ l10n(project_data.metadata.general.title_key, project_id, user_lang) }}</span>
{% endblock %}
{% block opengraph_tags %}
<!-- Required fields -->
<meta property="og:title"
content="{{ l10n(project_data.metadata.opengraph.title_key, project_id, user_lang) }}">
<meta property="og:url"
content="{{ "https://nibblepoker." + domain_tld + "/content/" + project_id }}">
<meta property="og:description"
content="{{ l10n(project_data.metadata.opengraph.description_key, project_id, user_lang) }}">
<!-- Optional fields -->
{% if project_data.metadata.opengraph.image_url is not none %}
<meta property="og:image"
content="{{ "https://nibblepoker." + domain_tld + project_data.metadata.opengraph.image_url }}">
{% if project_data.metadata.opengraph.image_type is not none %}
<meta property="og:image:type"
content="{{ project_data.metadata.opengraph.image_type }}">
{% endif %}
{% endif %}
{% endblock %}
{% block extra_head_tags %}
<meta name="description"
content="{{ l10n(project_data.metadata.head.description_key, project_id, user_lang) }}">
<!--<meta name="keywords" content="HTML, CSS, JavaScript">-->
{% endblock %}
{% block main_content %}
{{ render_h1(
'<i class="' + project_data.metadata.general.icon + ' t-size-16 mr-s t-muted"></i>' +
@@ -37,6 +63,7 @@
<script src="https://cdn.nibblepoker.lu/SplideJS/4.1.3/js/splide.min.js"></script>
<script src="https://cdn.nibblepoker.lu/HighlightJS/11.9.0-custom/highlight.min.js"></script>
<script src="{{ url_for("static", filename="resources/NibblePoker/js/nibblepoker-default.min.js") }}"></script>
<script src="{{ url_for("static", filename="/resources/NibblePoker/js/nibblepoker-splide.min.js") }}"></script>
<script src="{{ url_for("static", filename="/resources/NibblePoker/js/nibblepoker-code.min.js") }}"></script>
{% endblock %}

View File

@@ -25,12 +25,17 @@ def get_projects() -> LockedDict[str, ContentProject]:
return __CONTENT.projects
def get_projects_by_tags(tags: list[str]) -> dict[Any, ContentProject]:
project_obj: ContentProject
return {
project_key: project_value for project_key, project_value in __CONTENT.projects.items()
if any(tag in project_value.metadata.general.tags for tag in tags)
}
def get_sorted_projects_by_tags(tags: Optional[list[str]]) -> list[ContentProject]:
if tags is None:
return sorted(__CONTENT.projects.values(), key=lambda x: x.metadata.index.priority, reverse=True)
elif len(tags) == 0:
return sorted(__CONTENT.projects.values(), key=lambda x: x.metadata.index.priority, reverse=True)
else:
returned_list = [
x for x in __CONTENT.projects.values()
if any(tag in x.metadata.general.tags for tag in tags)
]
return sorted(returned_list, key=lambda x: x.metadata.index.priority, reverse=True)
def get_projects_by_languages(languages: list[str]) -> dict[Any, ContentProject]:
@@ -49,12 +54,17 @@ def get_tools() -> LockedDict[str, ContentTool]:
return __CONTENT.tools
def get_tools_by_tags(tags: list[str]) -> dict[Any, ContentProject]:
tool_obj: ContentTool
return {
tool_key: tool_value for tool_key, tool_value in __CONTENT.tools.items()
if any(tag in tool_value.metadata.general.tags for tag in tags)
}
def get_sorted_tools_by_tags(tags: Optional[list[str]]) -> list[ContentProject]:
if tags is None:
return sorted(__CONTENT.tools.values(), key=lambda x: x.metadata.index.priority, reverse=True)
elif len(tags) == 0:
return sorted(__CONTENT.tools.values(), key=lambda x: x.metadata.index.priority, reverse=True)
else:
returned_list = [
x for x in __CONTENT.tools.values()
if any(tag in x.metadata.general.tags for tag in tags)
]
return sorted(returned_list, key=lambda x: x.metadata.index.priority, reverse=True)
def sanitize_input_tags(input_tags: str) -> list[str]:
@@ -108,30 +118,7 @@ def load_content_items() -> None:
__CONTENT.projects[_project.id] = _project
#print(_project)
"""for project_item in os.listdir(os.path.join(os.getcwd(), "data/projects")):
project_item_path = os.path.join(os.getcwd(), "data/projects/", project_item)
if not os.path.isfile(project_item_path) or project_item.startswith("."):
continue
project_id = Path(project_item_path).stem
project_page_path = os.path.join(os.getcwd(), f"templates/projects/{project_id}.jinja")
if not all(os.path.isfile(project_file) for project_file in
[project_item_path, project_page_path]):
print(f"Unable to load project '{project_item}' due to missing files !")
continue
try:
__CONTENT_PROJECTS[project_id] = ContentProject(
id=project_id,
metadata=ContentMetadata(**yaml.safe_load(open(project_item_path))),
# strings=json.load(open(project_strings_path)) # Deprecated
)
print(f"Loaded project '{project_id}'")
except Exception as e:
print(f"Unable to load project '{project_id}' due to an exception !")
print(e)"""
# Loading tools definition files
for tools_file in os.listdir(os.path.join(os.getcwd(), "data/tools")):
tools_file_path = os.path.join(os.getcwd(), "data/tools", tools_file)
if not os.path.isfile(tools_file_path) or tools_file.startswith("."):

View File

@@ -43,7 +43,7 @@ def reload_strings(strings_root: str) -> None:
domain_key = str(Path(lang_domain).with_suffix(''))
if lang_domain.endswith(".json"):
print(f"Loading JSON lang data from '{lang_domain_path}'...")
#print(f"Loading JSON lang data from '{lang_domain_path}'...")
L10N.add_domain(
lang_dir,
domain_key,
@@ -51,7 +51,7 @@ def reload_strings(strings_root: str) -> None:
)
if lang_domain.endswith(".yml"):
print(f"Loading YAML lang data from '{lang_domain_path}'...")
#print(f"Loading YAML lang data from '{lang_domain_path}'...")
L10N.add_domain(
lang_dir,
domain_key,

View File

@@ -5,6 +5,8 @@ from flask import url_for
from website.content import ContentApplet
# FIXME: Implement preload support !
def render_applet_head(applet_data: ContentApplet, is_standalone: bool = False) -> str:
applet_style_html = ""