161 Commits
Author SHA1 Message Date
Klagarge 7b34f5a589 feat(cs): add initial lazygit doc
Build site / build (push) Successful in 2m50s
Build site / deploy (push) Skipped
2026-10-12 14:06:04 +02:00
Klagarge 6436f52889 feat(cs): add initial git doc 2026-10-12 14:05:52 +02:00
Klagarge 49e6820eae feat(cs): add tools section 2026-10-12 14:05:28 +02:00
Klagarge f8350ba235 ops: adapt script to work on windows 2026-10-12 13:32:43 +02:00
Klagarge 09f0dc42e4 chore(home): add metadatas
Build site / build (push) Successful in 2m44s
Build site / deploy (push) Successful in 7s
2026-10-11 23:46:05 +02:00
Klagarge 8e2303fcdf refactor: change main categories
Build site / build (push) Successful in 2m48s
Build site / deploy (push) Successful in 6s
2026-10-11 23:32:36 +02:00
Klagarge 4917abe9e6 ops: add toc on side 2026-10-11 23:26:28 +02:00
Klagarge b52629d1f6 feat(home): add gardening with yuca and dracaena 2026-10-11 23:23:48 +02:00
Klagarge b9159581f3 ops: add strip-exif function in justfile 2026-10-11 23:23:14 +02:00
Klagarge 9e9163f01b chore: add IDE in gitignore 2026-10-11 23:22:57 +02:00
Klagarge 54a69b51ae ops: add lib with tag function 2026-10-11 23:22:42 +02:00
Klagarge 975ba4f312 fix: remove act check
Build site / build (push) Successful in 50s
Build site / deploy (push) Successful in 3s
2026-10-11 20:09:47 +02:00
Klagarge 6cd5ed61c4 Merge branch 'refactor: migrate to calepin'
Build site / build (push) Successful in 51s
Build site / deploy (push) Successful in 2s
Reviewed-on: #1
2026-10-11 18:06:17 +00:00
Klagarge 1adca36802 ci: publish to the pages branch on main pushes
Build site / build (push) Successful in 52s
Build site / deploy (push) Skipped
Assisted-by: Delta:zai-glm-5.3
2026-10-11 20:00:54 +02:00
Klagarge 3b1ef34bce feat: enable PDF output for pages
Assisted-by: Delta:zai-glm-5.3
2026-10-11 18:56:04 +02:00
Klagarge 6b33be6956 chore: add a recipe for local act runs
Assisted-by: Delta:zai-glm-5.3
2026-10-11 00:22:23 +02:00
Klagarge f94a5cbe17 ci: use upload-artifact v3 for gitea
Assisted-by: Delta:zai-glm-5.3
2026-10-11 00:22:23 +02:00
Klagarge af31b6165c chore: remove unused placeholder
Build site / build (push) Failing after 11s
2026-10-11 00:09:52 +02:00
Klagarge 946177a0b9 feat: style the landing page with the academic theme
Assisted-by: Delta:zai-glm-5.3
2026-10-11 00:00:42 +02:00
Klagarge 3361c9ee3d feat: scope the sidebar to the current category
Assisted-by: Delta:zai-glm-5.3
2026-10-10 23:53:55 +02:00
Klagarge ab802968ca chore: add custom placeholder and fine tune calepin-toml 2026-10-10 23:51:22 +02:00
Klagarge 096026a964 docs: describe the site menu in README
Assisted-by: Delta:zai-glm-5.3
2026-10-10 22:05:21 +02:00
Klagarge 6478741950 feat: add master categories to the site menu
Assisted-by: Delta:zai-glm-5.3
2026-10-10 22:05:08 +02:00
Klagarge eb995c7b69 feat: restructure content into master categories
Assisted-by: Delta:zai-glm-5.3
2026-10-10 22:04:31 +02:00
Klagarge 7207cfdbd0 docs: update README and AGENT.md for the scripts layout
Assisted-by: Delta:zai-glm-5.3
2026-10-10 21:02:59 +02:00
Klagarge 36b2fc4188 ci: build site via the toolchain scripts
Assisted-by: Delta:zai-glm-5.3
2026-10-10 21:02:41 +02:00
Klagarge 62c4619c5e docs: add example sub-pages
Assisted-by: Delta:zai-glm-5.3
2026-10-10 21:02:41 +02:00
Klagarge f7beeb5e15 fix: align site scaffold with calepin conventions
Assisted-by: Delta:zai-glm-5.3
2026-10-10 21:02:20 +02:00
Klagarge 99ceb89e48 refactor: move toolchain logic into scripts/
Assisted-by: Delta:zai-glm-5.3
2026-10-10 21:01:20 +02:00
Klagarge 7217702068 chore: ignore generated outputs and IDE files
Assisted-by: Delta:zai-glm-5.3
2026-10-10 20:39:55 +02:00
Klagarge 11bf721f21 fix: adjust Justfile and typst templates for calepin 0.0.61
Assisted-by: Delta:zai-glm-5.3
2026-10-10 20:39:46 +02:00
Klagarge 446c156338 docs: write README and AGENT.md
Assisted-by: Delta:zai-glm-5.3
2026-10-10 20:30:15 +02:00
Klagarge 909ea17f2f ci: add Gitea Actions workflow
Assisted-by: Delta:zai-glm-5.3
2026-10-10 20:30:08 +02:00
Klagarge 7a0df30ca1 docs: add example categories
Assisted-by: Delta:zai-glm-5.3
2026-10-10 20:30:00 +02:00
Klagarge a5f95bb6d7 feat: scaffold calepin website
Assisted-by: Delta:zai-glm-5.3
2026-10-10 20:29:53 +02:00
Klagarge f047389da9 chore: bootstrap pinned toolchain
Assisted-by: Delta:zai-glm-5.3
2026-10-10 20:29:36 +02:00
Klagarge b78f79e12e docs: move legacy docs to legacy_docs/
Assisted-by: Delta:zai-glm-5.3
2026-10-10 20:28:25 +02:00
Klagarge d607e5df4d add MSE/SRE/tools (#29) 2025-03-16 16:47:18 +01:00
Klagarge 4199eb0e42 add MSE/SRE/tools 2025-03-16 16:44:48 +01:00
Fastium 0f4556a8a0 PTR/PCO modifications (#28) 2024-05-07 11:44:31 +02:00
Fastium dc62de5b6b Merge pull request #21
Infotro/s em/ind
2024-05-07 11:39:06 +02:00
Ranium 48296ab45c fix todo and and the file into the right readMe 2024-05-07 11:30:08 +02:00
Ranium de2deb15ad add pco modification 2024-05-07 11:23:23 +02:00
Rémi Heredero c3d3479327 add end of rf chapters (#27) 2024-05-05 18:33:59 +02:00
Klagarge 1bf4eeb242 add end of rf chapters 2024-05-05 18:32:59 +02:00
Rémi Heredero 6139748849 add SDi semester 6 (#26) 2024-05-04 17:55:50 +02:00
Klagarge 5637b4b14a add security information 2024-05-04 15:15:01 +02:00
Klagarge 79d0f08273 add databases 2024-05-04 10:43:02 +02:00
Rémi Heredero c7aabc7560 add Teams (#25) 2024-04-16 09:08:41 +02:00
Klagarge c16086c8dc add Teams 2024-04-16 09:07:50 +02:00
Rémi Heredero 525d5d466c Sdi lab ssl (#24) 2024-04-05 14:18:12 +02:00
Klagarge 0149f59c82 add description to a certificate 2024-04-05 14:14:23 +02:00
Klagarge 35ee5b72df add ssl-lab 2024-04-05 13:26:58 +02:00
Klagarge 47b6eb808e add switching contexte explanation 2024-03-25 22:18:55 +01:00
Rémi Heredero bdaa3e1ce4 Infotro/ptr (#23) 2024-03-24 18:09:50 +01:00
Klagarge ac64daca7f finish summary about PTR 2024-03-24 18:09:04 +01:00
Klagarge 5a1314a97a add PCR, ALP 2024-03-24 16:45:01 +01:00
Rémi Heredero 7d45d9d8e4 add os & concurrent programmation (#22) 2024-03-23 17:06:51 +01:00
Klagarge 991f49776d add os & concurrent programmation 2024-03-23 17:06:24 +01:00
Klagarge 19acbe1a74 typos 2024-03-23 13:23:30 +01:00
Rémi Heredero 76ae3150a0 add transmission ligne & smith chart (#20) 2024-03-23 10:29:17 +01:00
Klagarge 9c298cba2c fix nRF 2024-03-20 19:13:21 +01:00
Klagarge f0784cfef6 add transmission ligne & smith chart 2024-03-20 17:38:42 +01:00
Rémi Heredero 60e881a27f move files (#19) 2024-03-20 15:55:43 +01:00
Klagarge 06afb2e4d1 move files 2024-03-20 15:54:39 +01:00
Rémi Heredero 0c421a159c add nordic tutorial for developing environment installation (#18) 2024-03-20 15:44:09 +01:00
Klagarge 228da72eb1 add nordic tutorial for developing environment installation 2024-03-20 15:37:06 +01:00
Rémi Heredero 0f8fcb4fe6 add RF (#17) 2024-03-19 10:43:39 +01:00
Klagarge dbb7c8f33b add RF 2024-03-16 18:54:01 +01:00
Rémi Heredero c7d7b6ae8c add HES-SO OneDrive-Linux (#16) 2024-03-16 12:21:20 +01:00
Klagarge 343f7c7e3a add HES-SO OneDrive-Linux 2024-03-16 12:20:46 +01:00
Rémi Heredero ae4d5b8f33 fix display lab (#15) 2024-02-29 15:27:08 +01:00
Klagarge fbef6eceee fix display lab 2024-02-29 15:26:04 +01:00
Rémi Heredero 9a919151cc Infotro/sdi/labo (#14) 2024-02-29 15:14:21 +01:00
Klagarge f52207eed3 finish lab 2024-02-29 15:01:47 +01:00
Rémi Heredero 7a6970f89e add totp in security/ssh (#13) 2024-02-28 07:34:51 +01:00
Klagarge 5228190c95 add totp in security/ssh 2024-02-28 07:33:22 +01:00
Klagarge 2035387730 update lab-database 2024-02-27 14:26:22 +01:00
Klagarge 04f2ca9aba add lab-database 2024-02-22 15:08:55 +01:00
Fastium b4ac04880a fix problems 2024-02-21 17:00:40 +01:00
Fastium 766013fd3c update index 2024-02-21 16:12:13 +01:00
Fastium a9039fccc5 add content IND 2024-02-21 16:04:10 +01:00
Rémi Heredero 91e02ba46a Plugins (#12) 2024-02-18 10:52:03 +01:00
Klagarge 1c40e87061 add plugin progress, accordion and last time updated 2024-02-18 10:51:11 +01:00
Klagarge d3e4ab7876 add plugin count 2024-02-17 14:53:18 +01:00
Fastium a8a085b5aa Infotro/sem/int (#11) 2024-02-06 15:15:56 +01:00
Fastium 9bcc548c9e add interrupt in/out description 2024-02-06 12:40:33 +01:00
Klagarge c6cfe50a94 add cpsr 2024-02-05 17:04:02 +01:00
Klagarge d0c08a35a2 add interruption 2024-02-05 15:07:44 +01:00
Klagarge f716270ab6 add some rows 2024-02-04 19:39:21 +01:00
Rémi Heredero 587fe17f36 Infotro/eln/examens (#10) 2024-02-04 19:13:19 +01:00
Klagarge f08600e8bc add bilinear conversion w0 w0' 2024-02-04 19:10:14 +01:00
Klagarge 938af714d1 typo 2024-02-04 19:09:45 +01:00
Klagarge 15639751af add filter summary 2024-02-04 19:09:35 +01:00
Rémi Heredero b2b6cf5c14 fix access files (#9) 2024-02-04 17:06:33 +01:00
Klagarge 779a20386f fix access files 2024-02-04 17:05:51 +01:00
Rémi Heredero 959f3397f6 refactor and add some miss equations (#8) 2024-02-04 16:51:23 +01:00
Klagarge e5a00441e4 refactor and add some miss equations 2024-02-04 16:49:33 +01:00
Klagarge 6f558c0dab fix code highlighter 2024-02-04 09:34:19 +01:00
Klagarge bee1139aff Merge branch 'infotro/Eln/signaux_echantillonnage' 2024-02-04 09:30:38 +01:00
Klagarge c7a1f438d2 add lab6-filters 2024-02-03 23:43:11 +01:00
Klagarge aa399dcc93 add lab6-filters 2024-02-03 23:35:38 +01:00
Klagarge 4d5693aa1d clean matlab 2024-02-03 23:35:38 +01:00
Klagarge 1e5a337f94 add pictures & MATLAB 2024-02-03 23:35:38 +01:00
Klagarge 841c7082c9 add beginning of lab6-filter 2024-02-03 23:35:38 +01:00
Klagarge 9dec00c656 fix row on table 2024-02-03 18:38:05 +01:00
Klagarge ecc766fb53 add heaviside discret explanation 2024-02-03 18:36:30 +01:00
Rémi Heredero 7cb0973700 Infotro/eln/signaux echantillonnage (#6)
* add IIR filters

* fix signaux et echant.

* typo

* add filtres numériques
2024-02-03 17:33:54 +01:00
Klagarge ec57b65794 add filtres numériques 2024-02-03 17:32:48 +01:00
Klagarge e10ab79b06 typo 2024-02-03 17:30:39 +01:00
Klagarge f55f01c352 fix signaux et echant. 2024-02-03 12:20:48 +01:00
Klagarge f436b9de25 add IIR filters 2024-02-03 12:20:32 +01:00
Rémi Heredero e03787ac7b Merge pull request from infotro/Eln/signaux_echantillonnage
update pictures in signaux et echant.
2024-02-02 20:46:53 +01:00
Klagarge 85cf4d28da update pictures in signaux et echant. 2024-02-02 20:45:54 +01:00
Rémi Heredero f0d9cff028 Merge pull request from /infotro/Eln/signaux_echantillonnage
Infotro/eln/signaux echantillonnage
2024-02-02 18:03:49 +01:00
Klagarge 1092fb86cc add signaux et échantillonnage 2024-02-02 18:01:26 +01:00
Klagarge 9d6794a04e add signaux et échantiollnage 2024-02-02 12:39:20 +01:00
Rémi Heredero 9ce3d6939c Merge pull request from infotro/SDi/TransmissionProcess
- delete paper_exercice
- update transmission process
2024-02-01 18:55:14 +01:00
Klagarge d9c1dfa147 update transmission process 2024-02-01 18:54:13 +01:00
Klagarge 60e2650707 delete paper_exercise 2024-02-01 17:14:41 +01:00
Rémi Heredero 415f7786ba Merge pull request from /infotro/SDi/TransmissionProcess
add medias in transmission process
2024-02-01 17:05:56 +01:00
Klagarge 80373fdd08 add medias in transmission process 2024-02-01 17:05:04 +01:00
Klagarge d7242688df add collaborateurs 2024-02-01 16:17:38 +01:00
Klagarge 285c888b8d update bluetooth 2024-02-01 16:17:19 +01:00
Klagarge 33c1ffb7d2 update bluetooth 2024-02-01 15:52:14 +01:00
Klagarge 95a5a19943 add exercice on protocol development 2024-02-01 15:50:12 +01:00
Rémi Heredero 2bffbf4a60 typo & configuration for ocal running
from Zimo fait des modificatio de zigoto
2024-02-01 15:31:10 +01:00
Klagarge 1bb25ec1c1 add umlet diagram 2024-02-01 15:03:44 +01:00
SimoDonn fb6958da1e run configuration 2024-02-01 14:27:14 +01:00
SimoDonn 559e8bcc4d typo de ses morts 2024-02-01 12:09:50 +01:00
Klagarge a4058a2882 add bluetooth theory form Yann 2024-02-01 10:41:12 +01:00
Klagarge 813f64d922 typo in iot_technologies 2024-02-01 10:40:51 +01:00
Klagarge 3aea1a47b9 add pictures and glossary in protocol development 2024-02-01 10:40:14 +01:00
Klagarge 3d08888842 add protocol development 2024-02-01 10:16:02 +01:00
Klagarge 9a811a2d3d fix xml_example.png 2024-01-31 22:50:03 +01:00
Klagarge ec4a16209b fix kroki 2024-01-31 22:48:25 +01:00
Klagarge 54c40613e7 move medias 2024-01-31 19:19:59 +01:00
Klagarge 5e713e5151 add example SDi/IOT_Technologies/MQTT 2024-01-31 18:44:39 +01:00
Klagarge 12854429d1 add example SDi/IOT_Technologies/http/URI-IRI-URL 2024-01-31 16:51:51 +01:00
Klagarge 8a7ce06fe6 add SDi/IOT_Technologies/http/URI-IRI-URL 2024-01-31 16:33:02 +01:00
Klagarge 295a0844be add SDi/IOT_Technologies/serialisation 2024-01-31 15:23:12 +01:00
Klagarge 5da923e68b add SDi/IOT_Technologies/serialisation 2024-01-31 15:20:48 +01:00
Klagarge 649c2c2f58 add SDi/IOT_Technologies 2024-01-31 13:54:26 +01:00
Klagarge 3446d70a59 add PTR summary 2024-01-30 17:39:21 +01:00
Klagarge a02898f301 add local script for drawio 2024-01-29 09:15:43 +01:00
Klagarge c49ee22234 remove package json 2024-01-28 19:26:25 +01:00
Klagarge 49c891377b refactor for common usage 2024-01-28 19:13:10 +01:00
Klagarge d4dbc07c20 Merge remote-tracking branch 'origin/main' 2024-01-28 12:55:50 +01:00
Klagarge 00b5dfea65 first week of revision 2024-01-28 12:55:21 +01:00
Klagarge 20b294a769 first week of revision 2024-01-27 20:22:46 +01:00
Rémi Heredero 504472e7cc Create CNAME 2024-01-25 20:36:47 +01:00
Rémi Heredero 0d16b6fcaf Delete CNAME 2024-01-25 20:36:43 +01:00
Rémi Heredero 9b0110b8d1 Update CNAME 2024-01-24 11:50:13 +01:00
Rémi Heredero eaf4ed24fd Create CNAME 2024-01-24 11:49:57 +01:00
Klagarge b32065ca9c create Yggdrasil 2024-01-24 11:42:14 +01:00
Klagarge 5feafd8877 move files 2023-11-21 22:16:42 +01:00
Klagarge a0a1aab660 add drawio 2023-11-21 17:20:35 +01:00
Klagarge c67bf20145 update some docs + add navbar 2023-11-21 17:10:27 +01:00
Klagarge 01d9d71ff5 add some docs 2023-11-20 21:38:23 +01:00
Klagarge 8c4bdd9e41 update for STM32 2023-11-02 14:07:09 +01:00
Klagarge f13f4faa02 Initial commit 2023-10-30 16:09:37 +01:00
392 changed files with 17106 additions and 3822 deletions

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
# Shell scripts and the Justfile must stay LF so they run in Git Bash on Windows.
*.sh text eol=lf
Justfile text eol=lf
+70
View File
@@ -0,0 +1,70 @@
name: Build site
on:
- push
- workflow_dispatch
jobs:
build:
# NOTE: must match your act_runner labels (e.g. ubuntu-latest, ubuntu-24.04).
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Bootstrap pinned toolchain
run: ./scripts/bootstrap.sh
- name: Build site
run: ./scripts/calepin.sh compile site public
# The official upload-artifact@v4 refuses to run on Gitea (it detects a
# non-GitHub server and aborts with GHESNotSupportedError). v3 uses the
# artifact API that Gitea implements. On Gitea >= 1.22 with a current
# act_runner, christopherhx/gitea-upload-artifact@v4 is a drop-in
# replacement if you want the v4 API.
- name: Upload site artifact
uses: actions/upload-artifact@v3
with:
name: public
path: public/
deploy:
needs: build
# Publish only from main; move the gate to publish from another branch.
if: github.ref == 'refs/heads/main'
# Write access for the automatic Actions token to push the pages branch.
permissions:
contents: write
# NOTE: must match your act_runner labels (e.g. ubuntu-latest, ubuntu-24.04).
runs-on: ubuntu-latest
steps:
- name: Download site artifact
uses: actions/download-artifact@v3
with:
name: public
path: public
- name: Check artifact layout
run: test -f public/index.html
# Publishing interfaces with prod; it never runs under local act tests
# (act sets env.ACT, the Gitea runner does not). The force-push to the
# `pages` branch is what triggers the git-pages webhook.
- name: Publish to pages branch
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
run: |
if [ -z "$GITEA_TOKEN" ]; then
echo "::error::Missing GITEA_TOKEN (the automatic Actions token)."
exit 1
fi
cd public
git init -q -b pages .
git config user.name "Yggdrasil CI"
git config user.email "ci@noreply.kb28.ch"
git add -A
git commit -q -m "Publish site from ${GITHUB_SHA}"
repo_host="${GITHUB_SERVER_URL#*://}"
repo_host="${repo_host%/}"
git push -q --force "https://${GITHUB_REPOSITORY_OWNER}:${GITEA_TOKEN}@${repo_host}/${GITHUB_REPOSITORY}.git" pages
+12
View File
@@ -0,0 +1,12 @@
# Generated site and pinned toolchain
public/
/tools/
# Calepin build artifacts
.calepin/
.calepin-entry.*
# IDE
.idea/
.zed/
.vscode/
+17
View File
@@ -0,0 +1,17 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Docsify" type="ShConfigurationType">
<option name="SCRIPT_TEXT" value="start http://localhost:3000 ; docsify serve " />
<option name="INDEPENDENT_SCRIPT_PATH" value="false" />
<option name="SCRIPT_PATH" value="$PROJECT_DIR$/.idea/runConfigurations/run.sh" />
<option name="SCRIPT_OPTIONS" value="" />
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
<option name="INTERPRETER_PATH" value="powershell.exe" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="EXECUTE_IN_TERMINAL" value="true" />
<option name="EXECUTE_SCRIPT_FILE" value="false" />
<envs />
<method v="2" />
</configuration>
</component>
-29
View File
@@ -1,29 +0,0 @@
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>Not found</title><meta content="The requested page does not exist." name=description><meta content="The requested page does not exist." property=og:description><meta content="The requested page does not exist." name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content="Not found | Yggdrasil" property=og:title><meta content="Not found | Yggdrasil" name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/404.html rel=canonical><meta content=https://yggdrasil.kb28.ch/404.html property=og:url><link href=/assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-search:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke=Line truncated
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
*
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
* search bridge.
*
* The Rust theme loader links this file near the end of the page body, so
* the DOM already exists when it runs and no DOMContentLoaded guard is
* needed. */
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
// ----------------------------------------------------------- nav drawer
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
// ------------------------------------------------------ sidebar sections
// Only one section is open at a time, but "section" means siblings: opening a
// nested subsection must not fold the parent it lives in, and must only fold
// its own siblings.
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&&sections.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
// ------------------------------------------------------------- inline svg
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
// --------------------------------------------------------- link state
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
// ----------------------------------------------------- dialog lightbox
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
// Clicking the dialog backdrop (the dialog element itself) closes it.
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
// --------------------------------------------------------------- bootstrap
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
+20
View File
@@ -0,0 +1,20 @@
# Agent Rules for Yggdrasil Migration
* **Commit Policy**: Small conventional commits. One logical change per
commit, committed only once that step is verified and working. End every
commit message with a blank line followed by an
`Assisted-by: Delta:zai-glm-5.3` footer.
* **Layout**: Sources live in `site/` (`calepin.toml` + `.typ` pages),
generated output in `public/` (gitignored), pinned binaries in `tools/`
(gitignored), helper scripts in `scripts/`, and the old Docsify
documentation stays untouched in `legacy_docs/`.
* **Toolchain**: `calepin` (0.0.61) and `typst` (0.15.1) are pinned in
`scripts/bootstrap.sh` and downloaded into `tools/` with checksum
verification. The build must never depend on global installs. CI installs
the same pinned versions by calling the same scripts.
* **Verification**: Before committing, run a fresh cycle: `rm -rf tools`,
then `just clean`, then `just build` — the build must succeed with no
calepin on `PATH`. Verify the served site (`just preview`): home 200,
category page 200, sub-page 200, missing page shows the 404 page,
`sitemap.xml` present. After every test/clean cycle `git status` must stay
clean.
+31
View File
@@ -0,0 +1,31 @@
# Show the available recipes
default:
@just --list
# Bootstrap the pinned calepin + typst binaries into tools/
tools:
./scripts/bootstrap.sh
# Compile the site into public/
build: tools
./scripts/calepin.sh compile site public
# Build, then serve the site locally
preview: build
./scripts/calepin.sh serve public
# Rebuild on change and serve the result
watch: tools
./scripts/calepin.sh watch site public --serve
# Remove generated output and build caches
clean:
./scripts/clean.sh
# Remove all EXIF metadata from a file
strip-exif file:
exiftool -all= -overwrite_original {{ quote(file) }}
# Run the CI workflow locally with act (requires act + docker)
ci:
act push -W .gitea/workflows/build.yml -j build --artifact-server-path /tmp/act-artifacts
+88
View File
@@ -0,0 +1,88 @@
# Yggdrasil
Personal knowledge base built as a static website with
[Calepin](https://vincentarelbundock.github.io/calepin/), a Typst-based static
site generator.
## Layout
- `site/` — website sources: `calepin.toml` + `.typ` pages
- `site/themes/yggdrasil/` — local theme extending the bundled `calepin` theme (scoped sidebars, academic-styled landing)
- `public/` — generated site (gitignored)
- `tools/` — pinned `calepin` and `typst` binaries (gitignored, downloaded on demand)
- `scripts/` — helper scripts used by the Justfile and CI
- `legacy_docs/` — the old Docsify documentation, kept unmodified for reference
## Quick start
`just` is the only prerequisite — the toolchain is downloaded and
checksum-verified into `tools/` on first use (no global installs, no cargo):
just # list recipes
just tools # download the pinned toolchain into tools/
just build # compile site/ into public/
just preview # build, then serve the site locally
just watch # rebuild on change and serve
just clean # remove public/ and build caches
just ci # run the CI workflow locally with act (needs act + docker)
## Adding a page
Create `site/<category>/<page>.typ`:
```typst
#set document(title: [Page title])
#metadata((title: "Page title", translation_key: "category-page")) <website-metadata>
#title()
= Page title
Content…
```
and reference it in `site/calepin.toml`:
```toml
[[sidebar.section.item]]
target = "<category>/<page>.typ"
```
Sidebar labels come from the page's `#metadata(...)` line, not from the config.
Every page also renders a printable PDF twin (`pdf = true` in
`calepin.toml`, selectable in the page's view picker). Add `pdf: false` to a
page's `#metadata(...)` to skip its PDF, as `404.typ` does.
## Adding a category
1. Create `site/<category>/` with an `index.typ`, using
`#metadata((title: "Overview", ...))` for its sidebar label.
2. Add a `[[sidebar.section]]` named after the category in `calepin.toml`,
listing the index page and its sub-pages.
3. Add a `[[menus.main]]` entry with the category's `index.typ` as `target`
and the category name as `label` (e.g. `Home`, `IT`), so it appears in the
top navigation bar. The `label` is required here because the index page's
metadata title is `Overview`.
Pages inside a category show only that category's sidebar section — the
local theme (`site/themes/yggdrasil/partials/site-nav-sidebar.html`) filters
the sidebar to the section containing the current page. Pages outside every
category (landing page, `404`, standalone pages) fall back to the full
sidebar.
## Deployment
`just build` produces a fully static site in `public/`: HTML pages with
printable PDF twins, plus `sitemap.xml`, `robots.txt`, and `llms.txt`. The
Gitea Actions workflow in
`.gitea/workflows/build.yml` builds the site on every push with the same
pinned toolchain and uploads `public/` as an artifact.
Pushes to `main` are also published: the `deploy` job force-pushes `public/`
to the repo's `pages` branch using the automatic Actions token (no secrets to
configure). A Gitea webhook on the repo then notifies the self-hosted
[git-pages](https://codeberg.org/git-pages/git-pages) server, which clones
the `pages` branch and serves the site. The push step is gated with
`if: ${{ !env.ACT }}` so local `act` runs never publish, and
`workflow_dispatch` enables manual publishes from the Gitea UI.
-29
View File
@@ -1,29 +0,0 @@
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>Computer Science</title><meta content="Placeholder page for the Computer Science category. The real content comes later." name=description><meta content="Placeholder page for the Computer Science category. The real content comes later." property=og:description><meta content="Placeholder page for the Computer Science category. The real content comes later." name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content="Computer Science | Yggdrasil" property=og:title><meta content="Computer Science | Yggdrasil" name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/cs/ rel=canonical><meta content=https://yggdrasil.kb28.ch/cs/ property=og:url><link href=../assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svLine truncated
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
*
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
* search bridge.
*
* The Rust theme loader links this file near the end of the page body, so
* the DOM already exists when it runs and no DOMContentLoaded guard is
* needed. */
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
// ----------------------------------------------------------- nav drawer
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
// ------------------------------------------------------ sidebar sections
// Only one section is open at a time, but "section" means siblings: opening a
// nested subsection must not fold the parent it lives in, and must only fold
// its own siblings.
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&&sections.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
// ------------------------------------------------------------- inline svg
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
// --------------------------------------------------------- link state
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
// ----------------------------------------------------- dialog lightbox
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
// Clicking the dialog backdrop (the dialog element itself) closes it.
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
// --------------------------------------------------------------- bootstrap
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
BIN
View File
Binary file not shown.
-29
View File
@@ -1,29 +0,0 @@
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>Cooking</title><meta content="Placeholder page — the real content comes later." name=description><meta content="Placeholder page — the real content comes later." property=og:description><meta content="Placeholder page — the real content comes later." name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content="Cooking | Yggdrasil" property=og:title><meta content="Cooking | Yggdrasil" name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/home/cooking.html rel=canonical><meta content=https://yggdrasil.kb28.ch/home/cooking.html property=og:url><link href=../assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-search:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' Line truncated
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
*
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
* search bridge.
*
* The Rust theme loader links this file near the end of the page body, so
* the DOM already exists when it runs and no DOMContentLoaded guard is
* needed. */
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
// ----------------------------------------------------------- nav drawer
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
// ------------------------------------------------------ sidebar sections
// Only one section is open at a time, but "section" means siblings: opening a
// nested subsection must not fold the parent it lives in, and must only fold
// its own siblings.
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&&sections.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
// ------------------------------------------------------------- inline svg
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
// --------------------------------------------------------- link state
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
// ----------------------------------------------------- dialog lightbox
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
// Clicking the dialog backdrop (the dialog element itself) closes it.
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
// --------------------------------------------------------------- bootstrap
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
BIN
View File
Binary file not shown.
-29
View File
@@ -1,29 +0,0 @@
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>Gardening</title><meta content="Some plants description and advices for keep them alive" name=description><meta content="Some plants description and advices for keep them alive" property=og:description><meta content="Some plants description and advices for keep them alive" name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content="Gardening | Yggdrasil" property=og:title><meta content="Gardening | Yggdrasil" name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/home/gardening.html rel=canonical><meta content=https://yggdrasil.kb28.ch/home/gardening.html property=og:url><link href=../assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-search:url("data:image/svg+xml,%3Csvg xmlns='httLine truncated
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
*
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
* search bridge.
*
* The Rust theme loader links this file near the end of the page body, so
* the DOM already exists when it runs and no DOMContentLoaded guard is
* needed. */
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
// ----------------------------------------------------------- nav drawer
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
// ------------------------------------------------------ sidebar sections
// Only one section is open at a time, but "section" means siblings: opening a
// nested subsection must not fold the parent it lives in, and must only fold
// its own siblings.
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&&sections.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
// ------------------------------------------------------------- inline svg
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
// --------------------------------------------------------- link state
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
// ----------------------------------------------------- dialog lightbox
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
// Clicking the dialog backdrop (the dialog element itself) closes it.
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
// --------------------------------------------------------------- bootstrap
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
-29
View File
@@ -1,29 +0,0 @@
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>Home</title><meta content="Placeholder page for the home category: cooking, washing clothes, gardening. The real content comes later." name=description><meta content="Placeholder page for the home category: cooking, washing clothes, gardening. The real content comes later." property=og:description><meta content="Placeholder page for the home category: cooking, washing clothes, gardening. The real content comes later." name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content="Home | Yggdrasil" property=og:title><meta content="Home | Yggdrasil" name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/home/ rel=canonical><meta content=https://yggdrasil.kb28.ch/home/ property=og:url><link href=../assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline pointsLine truncated
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
*
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
* search bridge.
*
* The Rust theme loader links this file near the end of the page body, so
* the DOM already exists when it runs and no DOMContentLoaded guard is
* needed. */
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
// ----------------------------------------------------------- nav drawer
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
// ------------------------------------------------------ sidebar sections
// Only one section is open at a time, but "section" means siblings: opening a
// nested subsection must not fold the parent it lives in, and must only fold
// its own siblings.
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&&sections.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
// ------------------------------------------------------------- inline svg
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
// --------------------------------------------------------- link state
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
// ----------------------------------------------------- dialog lightbox
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
// Clicking the dialog backdrop (the dialog element itself) closes it.
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
// --------------------------------------------------------------- bootstrap
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
BIN
View File
Binary file not shown.
-29
View File
@@ -1,29 +0,0 @@
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>Backup</title><meta content="Placeholder page — the real content comes later." name=description><meta content="Placeholder page — the real content comes later." property=og:description><meta content="Placeholder page — the real content comes later." name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content="Backup | Yggdrasil" property=og:title><meta content="Backup | Yggdrasil" name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/homelab/backup.html rel=canonical><meta content=https://yggdrasil.kb28.ch/homelab/backup.html property=og:url><link href=../assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-search:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'Line truncated
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
*
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
* search bridge.
*
* The Rust theme loader links this file near the end of the page body, so
* the DOM already exists when it runs and no DOMContentLoaded guard is
* needed. */
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
// ----------------------------------------------------------- nav drawer
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
// ------------------------------------------------------ sidebar sections
// Only one section is open at a time, but "section" means siblings: opening a
// nested subsection must not fold the parent it lives in, and must only fold
// its own siblings.
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&&sections.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
// ------------------------------------------------------------- inline svg
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
// --------------------------------------------------------- link state
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
// ----------------------------------------------------- dialog lightbox
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
// Clicking the dialog backdrop (the dialog element itself) closes it.
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
// --------------------------------------------------------------- bootstrap
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
Binary file not shown.
-29
View File
@@ -1,29 +0,0 @@
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>HomeLab</title><meta content="Placeholder page for the homelab category. The real content comes later." name=description><meta content="Placeholder page for the homelab category. The real content comes later." property=og:description><meta content="Placeholder page for the homelab category. The real content comes later." name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content="HomeLab | Yggdrasil" property=og:title><meta content="HomeLab | Yggdrasil" name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/homelab/ rel=canonical><meta content=https://yggdrasil.kb28.ch/homelab/ property=og:url><link href=../assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-search:url("data:image/svLine truncated
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
*
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
* search bridge.
*
* The Rust theme loader links this file near the end of the page body, so
* the DOM already exists when it runs and no DOMContentLoaded guard is
* needed. */
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
// ----------------------------------------------------------- nav drawer
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
// ------------------------------------------------------ sidebar sections
// Only one section is open at a time, but "section" means siblings: opening a
// nested subsection must not fold the parent it lives in, and must only fold
// its own siblings.
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&&sections.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
// ------------------------------------------------------------- inline svg
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
// --------------------------------------------------------- link state
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
// ----------------------------------------------------- dialog lightbox
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
// Clicking the dialog backdrop (the dialog element itself) closes it.
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
// --------------------------------------------------------------- bootstrap
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
BIN
View File
Binary file not shown.
-29
View File
@@ -1,29 +0,0 @@
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>Yggdrasil</title><meta content="Welcome to Yggdrasil, my personal knowledge base. Pick a master category from the menu" name=description><meta content="Welcome to Yggdrasil, my personal knowledge base. Pick a master category from the menu" property=og:description><meta content="Welcome to Yggdrasil, my personal knowledge base. Pick a master category from the menu" name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content=Yggdrasil property=og:title><meta content=Yggdrasil name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/ rel=canonical><meta content=https://yggdrasil.kb28.ch/ property=og:url><link href=assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-search:url("data:image/sLine truncated
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
*
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
* search bridge.
*
* The Rust theme loader links this file near the end of the page body, so
* the DOM already exists when it runs and no DOMContentLoaded guard is
* needed. */
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
// ----------------------------------------------------------- nav drawer
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
// ------------------------------------------------------ sidebar sections
// Only one section is open at a time, but "section" means siblings: opening a
// nested subsection must not fold the parent it lives in, and must only fold
// its own siblings.
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&&sections.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
// ------------------------------------------------------------- inline svg
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
// --------------------------------------------------------- link state
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
// ----------------------------------------------------- dialog lightbox
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
// Clicking the dialog backdrop (the dialog element itself) closes it.
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
// --------------------------------------------------------------- bootstrap
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
BIN
View File
Binary file not shown.
View File
Whitespace-only changes.
+6
View File
@@ -0,0 +1,6 @@
<header><h1>
Embedded Systems
</h1></header>
----------
+57
View File
@@ -0,0 +1,57 @@
# Work with STM32 on CLion
----------
## Setup on Windows
## Install ARM Toolchain
[source](https://mynewt.apache.org/latest/get_started/native_install/cross_tools.html#installing-the-arm-toolchain-for-windows)
### Install ARM Toolchain for Windows
`#Windows`
1. Download the [binarie zip file for Windows](https://developer.arm.com/-/media/Files/downloads/gnu-rm/10-2020q4/gcc-arm-none-eabi-10-2020-q4-major-win32.exec)
2. Extract on `C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2020-q4-major`
3. Add `C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2020-q4-major\bin` to the `PATH` environment variable
4. Reboot the computer
### Install ARM Toolchain for Linux
`#Linux` `#WSL`
1. Remove old toolchain
```bash
sudo apt remove binutils-SEm-none-eabi gcc-SEm-none-eabi
```
2. Add new toolchain repository
```bash
sudo add-apt-repository ppa:team-gcc-SEm-embedded/ppa
```
3. Update
```bash
sudo apt update
```
4. Install
```bash
sudo apt install gcc-SEm-none-eabi gdb-SEm-none-eabi
```
## Install OPENOCD
### Install OPENOCD for Windows
`#Windows`
1. Download the [binary zip file for Windows](https://github.com/runtimeco/openocd-binaries/raw/master/openocd-0.10.0.zip)
2. Extract the zip file to `C:\openocd`
3. Add `C:\openocd\bin` to the `PATH` environment variable
4. Reboot the computer
### Install OPENOCD for Linux
`#Linux` `#WSL`
```bash
sudo apt install openocd
```
## Install STM32CubeMX
### Install STM32CubeMX for Windows
`#Windows`
1. Download the [binary zip file for Windows](https://www.st.com/en/development-tools/stm32cubemx.html)
2. Run the installer
+7
View File
@@ -0,0 +1,7 @@
<header><h1>
Nordic - nRF
</h1></header>
----------
[Install development environment](/Embed/nRF/development_environment.md)
@@ -0,0 +1,260 @@
<header><h1>
nRF Development Environment
</h1></header>
----------
Here you'll find some information for setting up the nRF development environment.
We'll see more on detail how to set up the environment for Linux and program the nRF52 with Zephyr on CLion.
# 1. Installing the nRF Connect SDK & Toolchain
<!-- tabs:start -->
#### **Linux**
1. Install the `libfuse2` library
```bash
sudo apt install libfuse2
```
2. Download the [nRF Connect for Desktop](https://www.nordicsemi.com/Products/Development-tools/nrf-connect-for-desktop)
3. Make the file executable
```bash
chmod a+x nrfconnect-*.appimage
```
4. Run the appimage (double-click on nrfconnect)
- If it asks for update, click "yes"
- If it asks is you want to help them improve the app, I recommend declining it.
- If it asks is you want to start the "Quick Start" app, click "Close"
5. Install necessary tools
- Toolchain Manager
- Programmer
6. Open the toolchain manager and install "nRF Connect SDK v2.6.0"
- Confirm the installation directory by clicking "Continue installation"
- Wait for the toolchain to download and install. (this may take a while)
- You can close the toolchain manager now
7. Download [nRF-udev](https://github.com/NordicSemiconductor/nrf-udev/releases/download/v1.0.1/nrf-udev_1.0.1-all.deb)
and install the nRF udev
```bash
sudo dpkg -i nrf-udev_1.0.1-all.deb
```
8. Install [JLink](https://www.segger.com/downloads/jlink/JLink_Linux_x86_64.deb) and install it
```bash
sudo dpkg -i JLink_Linux_*_x86_64.deb
```
#### **Windows**
Use Linux or WSL
#### **WSL**
Cry
<!-- tabs:end -->
# 2. Installing Zephyr [↗](https://builds.zephyrproject.io/zephyr/pr/60204/docs/develop/getting_started/index.html)
Zephyr is an open-source real-time operating system (RTOS) that is designed to be small, scalable, and secure for use
on resource-constrained systems.
It is a Linux Foundation project and is governed by a Technical Steering Committee (TSC).
## Install dependencies
| Tool | Min. Version |
|---------------------|--------------|
| CMake | 3.20.5 |
| Python | 3.8 |
| Devicetree compiler | 1.4.6 |
<!-- tabs:start -->
#### **Linux**
1. You'll probably need to update your package list
```bash
wget https://apt.kitware.com/kitware-archive.sh
sudo bash kitware-archive.sh
sudo apt update
```
2. Use apt to install the required dependencies:
```bash
sudo apt install --no-install-recommends git cmake ninja-build gperf \
ccache dfu-util device-tree-compiler wget \
python3-dev python3-pip python3-setuptools python3-tk python3-wheel xz-utils file \
make gcc gcc-multilib g++-multilib libsdl2-dev libmagic1
```
3. Verify the versions of the main dependencies installed on your system
```bash
cmake --version
python3 --version
dtc --version
```
#### **Windows**
Use Linux or WSL
#### **WSL**
Cry
<!-- tabs:end -->
## Get Zephyr and install Python dependencies
We'll see here only how to install within virtual environment because for CLion we'll need to use the virtual environment.
<!-- tabs:start -->
#### **Linux**
1. Install Python venv package
```bash
sudo apt install python3-venv
```
2. Create a new virtual environment
```bash
python3 -m venv ~/zephyrproject/.venv
```
3. Activate the virtual environment
```bash
source ~/zephyrproject/.venv/bin/activate
```
Once activated your shell will be prefixed with `(.venv)`. The virtual environment can be deactivated at any time by running `deactivate`.
4. Install west
```bash
pip install west
```
5. Get the Zephyr source code
```bash
west init ~/zephyrproject
cd ~/zephyrproject
west update
```
6. Export a [Zephyr CMake package](https://builds.zephyrproject.io/zephyr/pr/60204/docs/build/zephyr_cmake_package.html#cmake-pkg).
This allows CMake to automatically load boilerplate code required for building Zephyr applications.
```bash
west zephyr-export
```
7. Zephyr’s `scripts/requirements.txt` file declares additional Python dependencies.
```bash
pip install -r ~/zephyrproject/zephyr/scripts/requirements.txt
```
#### **Windows**
Use Linux or WSL
#### **WSL**
Cry
<!-- tabs:end -->
## Install the Zephyr SDK
<!-- tabs:start -->
#### **Linux**
1. Download and verify the [Zephyr SDK bundle](https://github.com/zephyrproject-rtos/sdk-ng/releases/tag/v0.16.5-1)
```bash
cd ~
wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.5-1/zephyr-sdk-0.16.5-1_linux-x86_64.tar.xz
wget -O - https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.5-1/sha256.sum | shasum --check --ignore-missing
```
2. Extract the Zephyr SDK bundle archive:
```bash
tar xf zephyr-sdk-0.16.5-1_linux-x86_64.tar.xz
```
3. Run the Zephyr SDK bundle setup script
```bash
~/zephyr-sdk-0.16.5-1/setup.sh
```
You only need to run the setup script once after extracting the Zephyr SDK bundle.
You must rerun the setup script if you relocate the Zephyr SDK bundle directory after the initial setup.
4. Install udev rules, which allow you to flash most Zephyr boards as a regular user.
```bash
sudo cp ~/zephyr-sdk-0.16.5-1/sysroots/x86_64-pokysdk-linux/usr/share/openocd/contrib/60-openocd.rules /etc/udev/rules.d
sudo udevadm control --reload
```
#### **Windows**
Use Linux or WSL
#### **WSL**
Cry
<!-- tabs:end -->
# 3. Configure CLion for Zephyr [↗](https://builds.zephyrproject.io/zephyr/pr/60204/docs/develop/tools/clion.html#clion)
<!-- tabs:start -->
#### **Linux**
1. Create new Toolchain
1. Open CLion, go to `File` > `Settings` > `Build, Execution, Deployment` > `Toolchain`
2. Create a new `Zephyr` toolchain
3. `Add environment ‣ From file` and select `~/zephyrproject/.venv/bin/activate`.
2. Create a new project in `~/zephyrproject/zephyr/myApps/<my_project_name>`
3. On CMake profile setting dialog, set the following:
- `Name`: `Zephyr`
- `Toolchain`: `Zephyr`
- `CMake options`: `-DBOARD=nrf52840dk_nrf52840` (or other board you want to use)
4. Create the project configuration file (`prj.conf`):
```
CONFIG_PRINTK=y
CONFIG_GPIO=y
CONFIG_DEBUG_THREAD_INFO=y
CONFIG_THREAD_RUNTIME_STATS=y
CONFIG_DEBUG_OPTIMIZATIONS=y
```
5. Create a new `main.c` file in the `src` directory. For example:
```c
#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#define LED0_NODE DT_ALIAS(led0)
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
int main(void){
int ret;
bool led_state = true;
if (!gpio_is_ready_dt(&led)) return 0;
ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
if (ret < 0) return 0;
while (1) {
ret = gpio_pin_toggle_dt(&led);
if (ret < 0) return 0;
led_state = !led_state;
printf("LED state: %s\n", led_state ? "ON" : "OFF");
k_msleep(1000);
}
}
```
6. Create the `CMakeLists.txt` file in the root of the project
```cmake
cmake_minimum_required(VERSION 3.13.1)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(my_project_name)
target_sources(app PRIVATE src/main.c)
```
7. Enable RTOS integration
1. Go to `Settings ‣ Build, Execution, Deployment ‣ Embedded Development ‣ RTOS Integration`.
2. Set the Enable RTOS Integration checkbox. You can leave the option set to `Auto`. CLion will detect Zephyr automatically.
8. Configure the debugger
1. Go to `Run ‣ New Embedded Configuration`.
2. Set the GDB settings: <br>
![GDB settings](medias/GDB_settings.png)
3. Click on `Next`, select your board and click `Create`.
9. Build and debug the project
#### **Windows**
Use Linux or WSL
#### **WSL**
Cry
<!-- tabs:end -->
Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

+95
View File
@@ -0,0 +1,95 @@
<header><h1>
OneDrive - Linux
</h1></header>
It's possible to have files from OneDrive on Linux by two ways:
----------
# 1. Using rClone (Mounting)
1. Update the system:
2. Install rClone:
``` bash
sudo apt install rclone
```
3. Configure rClone:
``` bash
rclone config
```
4. Follow the instructions to configure rClone
5. Create a directory to mount the OneDrive:
``` bash
mkdir ~/OneDrive
```
6. Mount OneDrive folder:
``` bash
rclone --vfs-cache-mode writes mount [rCloneProfilName]: ~/OneDrive
```
7. Automount OneDrive folder on Startup <br>
On `Startup Applications` add a new entry with the following command:
``` bash
sh -c "rclone --vfs-cache-mode writes mount [rCloneProfilName]: ~/OneDrive"
```
----------
# 2. Using OneDrive for Linux (Synchronization)
This tutorial work for Ubuntu 22.04 Jammy
## 2.1 Remove any configured PPA and associated 'onedrive' package and systemd service files
Remove the PPA repository and the older client, run the following commands:
``` bash
sudo apt remove onedrive
sudo add-apt-repository --remove ppa:yann1ck/onedrive
```
If no previous installation, the following command will return an error message, which can be ignored.
Remove the symbolic link to the systemd service file:
``` bash
sudo rm /etc/systemd/user/default.target.wants/onedrive.service
```
## 2.2 Ensure the system is up to date and verify you system version
1. Update the system:
2. Reboot the system:
3. Verify the system version:
``` bash
lsb_release -a
```
The output should be similar to:
``` bash
remi@marvin:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 22.04.4 LTS
Release: 22.04
Codename: jammy
```
## 2.3 Install OneDrive for Linux
1. Add the OpenSuSE Build Service repository release key:
``` bash
wget -qO - https://download.opensuse.org/repositories/home:/npreining:/debian-ubuntu-onedrive/xUbuntu_22.04/Release.key | gpg --dearmor | sudo tee /usr/share/keyrings/obs-onedrive.gpg > /dev/null
```
2. Add the OpenSuSE Build Service repository:
``` bash
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/obs-onedrive.gpg] https://download.opensuse.org/repositories/home:/npreining:/debian-ubuntu-onedrive/xUbuntu_22.04/ ./" | sudo tee /etc/apt/sources.list.d/onedrive.list
```
3. Update the package list:
``` bash
sudo apt update
```
4. Install the OneDrive for Linux client:
``` bash
sudo apt install --no-install-recommends --no-install-suggests onedrive
```
5. Autorun OneDrive for linux on Startup <br>
On `Startup Applications` add a new entry with the following command:
``` bash
sh -c "onedrive --monitor"
```
+6
View File
@@ -0,0 +1,6 @@
<header><h1>
Working environment at HES-SO
</h1></header>
- [OneDrive - Linux](HES-SO/OneDrive.md)
- [Teams](HES-SO/Teams.md)
+79
View File
@@ -0,0 +1,79 @@
<header><h1>
Teams - Linux
</h1></header>
It's possible to have Microsoft Teams on Linux and to mount teams folder.
----------
# Install Microsoft Teams on Linux
<details>
<summary>Install Snap</summary>
``` bash
sudo apt update
sudo apt install snapd
```
</details>
``` bash
sudo snap install teams-for-linux
```
# Mount Teams folder
The various software for backing up One Drive require a "Drive ID" for copying documents.
For the personal drive of the account this is done automatically but when you want the ID of a Teams group,
you need the address of the Sharepoint drive. Here's how to get it.
## Find drive ID
1. Open the [Microsoft Graph](https://developer.microsoft.com/en-us/graph/graph-explorer) site and go to Graph Explorer
2. Sign in with your Microsoft account and make sure the tenant is HESSO
3. Insert the command: `https://graph.microsoft.com/v1.0/sites?search={Name_of_your_team}` then execute it. The text to the right of the symbol = acts as a search engine.
Here we want all sites related to FS. <br>
![microsoft_graph-search_bar.png](medias/microsoft_graph-search_bar.png) <br>
![microsoft_graph-result_research.png](medias/microsoft_graph-result_research.png)
4. Retrieve the site identification (id).
We also check that the site name is correct, here the first one is "ProjetFS" which is indeed the name of the Sharepoint. <br>
Here the site id is: "hessoit.sharepoint.com,65c4ef54-f1dd-40f2-af2d-6831f7157a0c,9f964f78-7045-481f-8f08-bb22d56de14e"
5. Now taper the command: `https://graph.microsoft.com/v1.0/sites/{site-id}/drive` by replacing `{site-id}` with the result obtained previously. <br>
![microsoft_graph-search_site_id.png](medias/microsoft_graph-search_site_id.png) <br>
![microsoft_graph-result_id.png](medias/microsoft_graph-result_id.png)
6. Retrieve the drive identification (id). <BR>
Here the drive id is: "b!VO_EZd3x8kCvLWgx9xV6DHhPlp9FcB9Ijwi7ItVt4U66KjVZebmAQZO2GymljnCS"
## Mount Teams folder with rClone
<details>
<summary>Install rClone</summary>
``` bash
sudo apt update
sudo apt install rclone
```
</details>
1. Configure rClone:
``` bash
rclone config
```
2. Follow the instructions to configure rClone
1. It's a "OneDrive" configuration
2. Leve blank the "client_id" and "client_secret"
3. It's on Microsoft Cloud Global
4. Choose "Type in driveID"
3. Create a directory to mount the Teams:
``` bash
mkdir ~/Teams/{Team_name}
```
4. Mount Teams folder:
``` bash
rclone --vfs-cache-mode writes mount {team_name}: ~/Teams/{Team_name}
```
5. Automount Teams folder on Startup <br>
6. On `Startup Applications` add a new entry with the following command:
``` bash
sh -c "rclone --vfs-cache-mode writes mount {team_name}: ~/Teams/{Team_name}"
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

+4
View File
@@ -0,0 +1,4 @@
# Master of science in engineering in embedded system
## [SRE - Software Reverse Engineering](/MSE/SRE/)
- [Tools](/MSE/SRE/tools.md)
+5
View File
@@ -0,0 +1,5 @@
<header><h1>
SRE - Software Reverse Engineering
</h1></header>
- [Tools](/MSE/SRE/tools.md)
+129
View File
@@ -0,0 +1,129 @@
<header>
<h1>Tools</h1>
</header>
# Utilities (Linux)
## strings
For each file given, `strings` prints the printable character sequences that are at least 4 characters long (or the
number given with the options below) and are followed by an unprintable character. By default, it only prints the
strings from the initialized and loaded sections of object files. For other types of files, it prints the strings
from the whole file. The main use of `strings` is to find the contents of non-text files.
```terminal
$|strings code/a.out
The sum of 1 to 63 is %d.
```
## file
Determine file type
```terminal
$|file /bin/ls
/bin/ls: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),
dynamically linked (uses shared libs), for GNU/Linux 2.6.18, stripped
$|file /bin/ls
/bin/ls: Mach-O 64-bit executable x86_64
```
## nm
List the symbols in a object file
## readelf
Displays information about ELF files
## ldd
Displays the shared objects dependencies
## lsof
Lists files and other system ressources opened by a process
## ltrace
Traces the calls of a process to functions in dynamic libraries
## strace
Traces the systems calls of a process
# Disassemblers
## objdump, otool on macOS
Open-source tools for showing different types of information about object files on Unix-based operating systems.
## disassemble
Internal command of gdb
## [radare2](https://rada.re/n/radare2.html)
Open source portable reversing framework
## [Capstone](https://www.capstone-engine.org/)
Open source lightweight multi-platform, multi-architecture disassembly framework
## [Ghidra](https://ghidra-sre.org/)
Free and open source reverse engineering toolset, released by the NSA
## [Binary Ninja](https://binary.ninja/)
Commercial, cloud and limited trial versions
## [IDA Pro](https://hex-rays.com/ida-pro/)
Free and commercial versions
## [Hopper](https://www.hopperapp.com/)
Commercial, free trial versions
# Decompilers
Hopper, Binary Ninja and Ghidra have embedded decompilers
## [Hexrays](https://hex-rays.com/decompiler/)
Commercial plugin for IDA Pro
## [.NET Reflector](https://www.red-gate.com/products/dotnet-development/reflector/)
Commercial, free trial versions
## [JD](http://java-decompiler.github.io/)
Free and open source Java decompiler
## [uncompyle6](https://pypi.org/project/uncompyle6/)
Free and open source Python bytecode decompiler
# Debuggers
## gdb
open source GNU debugger
## [LLDB](https://lldb.llvm.org/)
open source debugger by the LLVM project; the default one in Apple environments
## [WinDbg](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-download-tools)
multipurpose debugger for the Microsoft Windows computer operating system, distributed by Microsoft
## [x64dbg](https://x64dbg.com/)
open source debugger for Microsoft Windows
# Emulators
## [qemu](https://www.qemu.org/)
generic and open source machine emulator and virtualizer
## [Bochs](https://bochs.sourceforge.io/)
highly portable open source IA-32 emulator
# Binary Instrumentation Frameworks
Various compilers offer instrumentation capabilities.
## [PIN](https://www.intel.com/content/www/us/en/developer/articles/tool/pin-a-dynamic-binary-instrumentation-tool.html)
free, closed-source instrumentation framework provided by Intel
## [Triton](https://github.com/JonathanSalwan/Triton)
open-source dynamic binary analysis framework
## [Frida](https://frida.re/)
open-source dynamic instrumentation toolkit for developers, reverse-engineers, and security researchers
## [QBDI](https://qbdi.quarkslab.com/)
modular, cross-platform and cross-architecture DBI framework
```terminal
```
+2
View File
@@ -0,0 +1,2 @@
* [SRE - Software Reverse Engineering](/MSE/SRE/)
* [Tools](/MSE/SRE/tools.md)
+3
View File
@@ -0,0 +1,3 @@
<header><h1>
Security
</h1></header>
+66
View File
@@ -0,0 +1,66 @@
<header><h1>
SSH
</h1></header>
# Add TOTP on ssh connection
For a more secure ssh connection, you can add TOTP (Time-based One-Time Password) to your ssh connection.
This will require you to enter a password and a TOTP code to connect to your server.
This is a good way to add an extra layer of security to your server.
## Installing Google Authenticator PAM
```bash
sudo apt install libpam-google-authenticator
```
## Configuring SSHD for Google PAM
1. Edit the sshd configuration file
```bash
sudo nano /etc/pam.d/sshd
```
2. Modified the file to look like this
```bash
# PAM configuration for the Secure Shell service
# Standard Un*x authentication.
#@include common-auth
. . .
# Standard Un*x password updating.
@include common-password
auth required pam_google_authenticator.so nullok
```
Comment out the `@include common-auth` for disable password authentication.
`nullok` allows users without a TOTP code to connect without one.
3. Edit the sshd configuration file
```bash
sudo nano /etc/ssh/sshd_config
```
4. Add the following line to the bottom of the file
```bash
. . .
# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)
ChallengeResponseAuthentication yes
. . .
UsePAM yes
AuthenticationMethods publickey,password publickey,keyboard-interactive
. . .
```
5. Restart the sshd service
```bash
sudo systemctl restart sshd
```
## Generating the Google Authenticator QR Code
1. Run the following command
```bash
google-authenticator
```
2. Answer the following questions
3. Scan the QR code with your Google Authenticator app
4. Save the emergency scratch codes in a safe place
5. Restart the sshd service
```bash
sudo systemctl restart sshd
```
View File
Whitespace-only changes.
+13
View File
@@ -0,0 +1,13 @@
* [Bachelor Infotro](/infotro/)
* [PTR - Programmation Temps Réel](/infotro/PTR/)
* [SDi - Systèmes Distribués](/infotro/SDi/)
* [Eln - Électronique](/infotro/Eln/)
* [SEm - Systèmes Embarqués](/infotro/SEm/)
* [MSE](/MSE/)
* [SRE](/MSE/SRE/)
* [Embed](/Embed/)
* [STM32](/Embed/STM32/)
* [nRF](/Embed/nRF/)
* [HES-SO](/HES-SO/)
* [OneDrive - Linux](/HES-SO/OneDrive.md)
* [Teams - Linux](/HES-SO/Teams.md)
+312
View File
@@ -0,0 +1,312 @@
#Demo
$$
E=mc^2
$$
-----------
[panel.md](divers/panel.md)
-----------
# FAQ Section
Introduction text for the FAQ page.
+ Question 1? +
Answer 1
+ Question 2? +
Answer 2
-----------
```mermaid
zenuml
title Declare participant (optional)
Bob
Alice
Alice->Bob: Hi Bob
Bob->Alice: Hi Alice
```
-----------
```mermaid
%%{init: { 'logLevel': 'debug', 'theme': 'forest' } }%%
sequenceDiagram
Alice->>John: Hello John, how are you?
John-->>Alice: Great!
Alice-)John: See you later!
Alice->>John: See you later!
```
-----------
```plantuml
@startuml
Alice ->> "Bob()" : Hello
"Bob()" -> "This is very\nlong" as Long
' You can also declare:
' "Bob()" -> Long as "This is very\nlong"
Long --> "Bob()" : ok
@enduml
```
-----------
![example4_v3.png](example4_v3.png)
-----------
[transmission_process.md](../infotro/SDi/transmission_process.md)
-----------
## embedding it directly
Theme valid: base, forest,
```mermaid
%%{init: { 'logLevel': 'debug', 'theme': 'base' } }%%
gitGraph
commit
commit
branch develop
checkout develop
commit
commit
checkout main
merge develop
commit
commit
```
-----------
```mermaid
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
```
-----------
```plantuml
@startuml
Alice -> Bob: Authentication Request
Bob --> Alice: Authentication Response
Alice -> Bob: Another authentication Request
Alice <-- Bob: another authentication Response
@enduml
```
-----------
## load from external files
![kroki-excalidraw](excalidraw.json)
-----------
## Wavedrom
```wavedrom
{ "signal": [
{ "name": "pclk", "wave": 'p.......' },
{ "name": "Pclk", "wave": 'P.......' },
{ "name": "nclk", "wave": 'n.......' },
{ "name": "Nclk", "wave": 'N.......' },
{},
{ "name": 'clk0', "wave": 'phnlPHNL' },
{ "name": 'clk1', "wave": 'xhlhLHl.' },
{ "name": 'clk2', "wave": 'hpHplnLn' },
{ "name": 'clk3', "wave": 'nhNhplPl' },
{ "name": 'clk4', "wave": 'xlh.L.Hx' },
]}
```
-----------
## Wavedrom register
```wavedrom
{"reg": [
{bits: 7, name: 'opcode', attr: ['LUI', 'AUIPC']},
{bits: 5, name: 'rd', attr: 'dest'},
{bits: 20, name: 'imm[31:12]', attr: 'U-immediate[31:12]', type: 3}
], config: {hspace: width}}
}
```
# Code exemple
``` cpp
void foo(uint8_t bar){
bar *= 4;
(void*) bar;
}
```
## drawio
```drawio
<?xml version="1.0" encoding="UTF-8"?>
<mxfile host="embed.diagrams.net" modified="2023-11-21T16:13:24.061Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0" etag="uH8AfpMK1L6zxq501sk9" version="22.1.3" type="embed">
<diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">
<mxGraphModel dx="1410" dy="1899" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="WIyWlLk6GJQsqaUBKTNV-1" parent="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-7" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--0" target="HSGzQKdNW0ipQrgMzVfQ-1" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--0" value="Person" style="swimlane;fontStyle=2;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="220" y="120" width="160" height="138" as="geometry">
<mxRectangle x="230" y="140" width="160" height="26" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--1" value="Name" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
<mxGeometry y="26" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--2" value="Phone Number" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
<mxGeometry y="52" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--3" value="Email Address" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
<mxGeometry y="78" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--4" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
<mxGeometry y="104" width="160" height="8" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--5" value="Purchase Parking Pass" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
<mxGeometry y="112" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--6" value="Student" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="120" y="360" width="160" height="142" as="geometry">
<mxRectangle x="120" y="360" width="160" height="26" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--7" value="Student Number" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--6" vertex="1">
<mxGeometry y="30" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--8" value="Average Mark" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--6" vertex="1">
<mxGeometry y="56" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--9" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--6" vertex="1">
<mxGeometry y="82" width="160" height="8" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--10" value="Is Eligible To Enroll" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontStyle=4" parent="zkfFHV4jXpPFQw0GAbJ--6" vertex="1">
<mxGeometry y="90" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--11" value="Get Seminars Taken" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--6" vertex="1">
<mxGeometry y="116" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--12" value="" style="endArrow=block;endSize=10;endFill=0;shadow=0;strokeWidth=1;rounded=0;edgeStyle=elbowEdgeStyle;elbow=vertical;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--6" target="zkfFHV4jXpPFQw0GAbJ--0" edge="1">
<mxGeometry width="160" relative="1" as="geometry">
<mxPoint x="200" y="203" as="sourcePoint" />
<mxPoint x="200" y="203" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--13" value="Professor" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="330" y="360" width="160" height="70" as="geometry">
<mxRectangle x="340" y="380" width="170" height="26" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--14" value="Salary" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--13" vertex="1">
<mxGeometry y="26" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--15" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--13" vertex="1">
<mxGeometry y="52" width="160" height="8" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--16" value="" style="endArrow=block;endSize=10;endFill=0;shadow=0;strokeWidth=1;rounded=0;edgeStyle=elbowEdgeStyle;elbow=vertical;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--13" target="zkfFHV4jXpPFQw0GAbJ--0" edge="1">
<mxGeometry width="160" relative="1" as="geometry">
<mxPoint x="210" y="373" as="sourcePoint" />
<mxPoint x="310" y="271" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--17" value="Address" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="508" y="120" width="160" height="216" as="geometry">
<mxRectangle x="550" y="140" width="160" height="26" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--18" value="Street" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="26" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--19" value="City" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="52" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--20" value="State" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="78" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--21" value="Postal Code" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="104" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--22" value="Country" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="130" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--23" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="156" width="160" height="8" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--24" value="Validate" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="164" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--25" value="Output As Label" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="190" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--26" value="" style="endArrow=open;shadow=0;strokeWidth=1;rounded=0;endFill=1;edgeStyle=elbowEdgeStyle;elbow=vertical;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--0" target="zkfFHV4jXpPFQw0GAbJ--17" edge="1">
<mxGeometry x="0.5" y="41" relative="1" as="geometry">
<mxPoint x="380" y="192" as="sourcePoint" />
<mxPoint x="540" y="192" as="targetPoint" />
<mxPoint x="-40" y="32" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--27" value="0..1" style="resizable=0;align=left;verticalAlign=bottom;labelBackgroundColor=none;fontSize=12;" parent="zkfFHV4jXpPFQw0GAbJ--26" connectable="0" vertex="1">
<mxGeometry x="-1" relative="1" as="geometry">
<mxPoint y="4" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--28" value="1" style="resizable=0;align=right;verticalAlign=bottom;labelBackgroundColor=none;fontSize=12;" parent="zkfFHV4jXpPFQw0GAbJ--26" connectable="0" vertex="1">
<mxGeometry x="1" relative="1" as="geometry">
<mxPoint x="-7" y="4" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--29" value="lives at" style="text;html=1;resizable=0;points=[];;align=center;verticalAlign=middle;labelBackgroundColor=none;rounded=0;shadow=0;strokeWidth=1;fontSize=12;" parent="zkfFHV4jXpPFQw0GAbJ--26" vertex="1" connectable="0">
<mxGeometry x="0.5" y="49" relative="1" as="geometry">
<mxPoint x="-38" y="40" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-0" value="Template1 signature" style="shape=partialRectangle;html=1;top=1;align=left;dashed=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="320" y="110" width="130" height="20" as="geometry" />
</mxCell>
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-1" value="Person" style="swimlane;fontStyle=2;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="220" y="-98" width="160" height="138" as="geometry">
<mxRectangle x="230" y="140" width="160" height="26" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-2" value="Name" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="HSGzQKdNW0ipQrgMzVfQ-1" vertex="1">
<mxGeometry y="26" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-3" value="Phone Number" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="HSGzQKdNW0ipQrgMzVfQ-1" vertex="1">
<mxGeometry y="52" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-4" value="Email Address" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="HSGzQKdNW0ipQrgMzVfQ-1" vertex="1">
<mxGeometry y="78" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-5" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="HSGzQKdNW0ipQrgMzVfQ-1" vertex="1">
<mxGeometry y="104" width="160" height="8" as="geometry" />
</mxCell>
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-6" value="Purchase Parking Pass" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="HSGzQKdNW0ipQrgMzVfQ-1" vertex="1">
<mxGeometry y="112" width="160" height="26" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

+605
View File
@@ -0,0 +1,605 @@
{
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": [
{
"type": "rectangle",
"version": 175,
"versionNonce": 279344008,
"isDeleted": false,
"id": "2ZYh24ed28FJ0yE-S3YNY",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 580,
"y": 140,
"strokeColor": "#000000",
"backgroundColor": "#15aabf",
"width": 80,
"height": 19.999999999999996,
"seed": 521916552,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["Be1y2yzhV3Zd4nwCro__8"]
},
{
"type": "rectangle",
"version": 180,
"versionNonce": 164784376,
"isDeleted": false,
"id": "bO0OVt6m7LowYpq22ePCA",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 660,
"y": 140,
"strokeColor": "#000000",
"backgroundColor": "#4c6ef5",
"width": 120,
"height": 19.999999999999996,
"seed": 1303206904,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["KaCO9-QjUenSyCuuanoTo"]
},
{
"type": "rectangle",
"version": 183,
"versionNonce": 27181704,
"isDeleted": false,
"id": "jz0Huq9-s6pNxDw0RqHcR",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 780,
"y": 140,
"strokeColor": "#000000",
"backgroundColor": "#fab005",
"width": 180,
"height": 19.999999999999996,
"seed": 861962120,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["74ifmqmu0vN0NK0_0FwPm"]
},
{
"type": "rectangle",
"version": 192,
"versionNonce": 2123008504,
"isDeleted": false,
"id": "UnmNTmwJtm6moubcGtSgB",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 960,
"y": 140,
"strokeColor": "#000000",
"backgroundColor": "#fa5252",
"width": 80,
"height": 19.999999999999996,
"seed": 277814520,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["1v60NED2criGG-wo9-oQL"]
},
{
"type": "rectangle",
"version": 202,
"versionNonce": 1823814024,
"isDeleted": false,
"id": "of76J4WOJHnHi0L61Vst_",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1040,
"y": 140,
"strokeColor": "#000000",
"backgroundColor": "#be4bdb",
"width": 180,
"height": 19.999999999999996,
"seed": 1496796808,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["jjuPzyRneMv3f65lps_6a"]
},
{
"type": "rectangle",
"version": 193,
"versionNonce": 1234602744,
"isDeleted": false,
"id": "SlvbjeV-9lXbcrlKib-hj",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1220,
"y": 140,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 60,
"height": 19.999999999999996,
"seed": 1938865656,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["5QQzhw_uqk_rBaW2wMriT"]
},
{
"type": "text",
"version": 81,
"versionNonce": 1188901129,
"isDeleted": false,
"id": "vrdt3JfbD2Xwz4K4TWScI",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 840,
"y": -60,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 190,
"height": 45,
"seed": 1499217288,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": [],
"fontSize": 36,
"fontFamily": 1,
"text": "JavaScript",
"baseline": 32,
"textAlign": "left",
"verticalAlign": "top"
},
{
"type": "arrow",
"version": 343,
"versionNonce": 1369065096,
"isDeleted": false,
"id": "Be1y2yzhV3Zd4nwCro__8",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 597.5075333823274,
"y": 299,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 40,
"height": 139,
"seed": 666255096,
"groupIds": [],
"strokeSharpness": "round",
"boundElementIds": [],
"startBinding": {
"focus": -0.41953339688473495,
"gap": 1,
"elementId": "UxgtvUBaIPnDWJZ9kUQH8"
},
"endBinding": {
"focus": -0.11111111111111113,
"gap": 1,
"elementId": "2ZYh24ed28FJ0yE-S3YNY"
},
"points": [
[0, 0],
[-17.507533382327438, -59],
[22.492466617672562, -139]
],
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow"
},
{
"type": "text",
"version": 81,
"versionNonce": 690339976,
"isDeleted": false,
"id": "UxgtvUBaIPnDWJZ9kUQH8",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 580,
"y": 300,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 94,
"height": 45,
"seed": 84626568,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["Be1y2yzhV3Zd4nwCro__8"],
"fontSize": 36,
"fontFamily": 1,
"text": "Fetch",
"baseline": 32,
"textAlign": "left",
"verticalAlign": "top"
},
{
"type": "rectangle",
"version": 60,
"versionNonce": 897215480,
"isDeleted": false,
"id": "-Lq0agjWQ31TR_Av5Z4HW",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 520,
"y": -60,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 820,
"height": 540,
"seed": 495165432,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["jjuPzyRneMv3f65lps_6a"]
},
{
"type": "arrow",
"version": 537,
"versionNonce": 1626949112,
"isDeleted": false,
"id": "KaCO9-QjUenSyCuuanoTo",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 721.0588599991052,
"y": 60.17790458606555,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 1.0588599991051524,
"height": 79.82209541393445,
"seed": 637565832,
"groupIds": [],
"strokeSharpness": "round",
"boundElementIds": [],
"startBinding": null,
"endBinding": {
"focus": 0,
"gap": 1,
"elementId": "bO0OVt6m7LowYpq22ePCA"
},
"points": [
[0, 0],
[-1.0588599991051524, 39.82209541393445],
[-1.0588599991051524, 79.82209541393445]
],
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow"
},
{
"type": "text",
"version": 112,
"versionNonce": 358083143,
"isDeleted": false,
"id": "4hEOdlcwK6AHyVhjc-MXS",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 660,
"y": 20,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 103,
"height": 45,
"seed": 352116984,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": [],
"fontSize": 36,
"fontFamily": 1,
"text": "Parse",
"baseline": 32,
"textAlign": "left",
"verticalAlign": "top"
},
{
"type": "arrow",
"version": 534,
"versionNonce": 983577992,
"isDeleted": false,
"id": "74ifmqmu0vN0NK0_0FwPm",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 841.6574209245741,
"y": 219,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 43.15128973100309,
"height": 59.174989629909305,
"seed": 1853344392,
"groupIds": [],
"strokeSharpness": "round",
"boundElementIds": [],
"startBinding": {
"focus": 0.09211398277003865,
"gap": 1,
"elementId": "K4so-arfr0JX0NJx8vd7T"
},
"endBinding": {
"focus": -0.2163077865936296,
"gap": 1,
"elementId": "jz0Huq9-s6pNxDw0RqHcR"
},
"points": [
[0, 0],
[-1.6574209245741258, 1],
[41.493868806428964, -58.174989629909305]
],
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow"
},
{
"type": "text",
"version": 118,
"versionNonce": 1185705864,
"isDeleted": false,
"id": "K4so-arfr0JX0NJx8vd7T",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 640,
"y": 220,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 366,
"height": 45,
"seed": 765854200,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["74ifmqmu0vN0NK0_0FwPm"],
"fontSize": 36,
"fontFamily": 1,
"text": "Compile and Optimize",
"baseline": 32,
"textAlign": "left",
"verticalAlign": "top"
},
{
"type": "arrow",
"version": 791,
"versionNonce": 1724761848,
"isDeleted": false,
"id": "1v60NED2criGG-wo9-oQL",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 960,
"y": 320,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 80,
"height": 160,
"seed": 1764571528,
"groupIds": [],
"strokeSharpness": "round",
"boundElementIds": [],
"startBinding": {
"focus": -0.1637630662020906,
"gap": 1,
"elementId": "dviXudWNxiHYQMZfqHWsH"
},
"endBinding": {
"focus": 0.07692307692307691,
"gap": 1,
"elementId": "UnmNTmwJtm6moubcGtSgB"
},
"points": [[0, 0], [80, -40], [40, -160]],
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow"
},
{
"type": "text",
"version": 194,
"versionNonce": 473574648,
"isDeleted": false,
"id": "dviXudWNxiHYQMZfqHWsH",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 720,
"y": 320,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 484,
"height": 45,
"seed": 1988297464,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["1v60NED2criGG-wo9-oQL"],
"fontSize": 36,
"fontFamily": 1,
"text": "Re-optimize and Deoptimize",
"baseline": 32,
"textAlign": "left",
"verticalAlign": "top"
},
{
"type": "arrow",
"version": 708,
"versionNonce": 185615496,
"isDeleted": false,
"id": "jjuPzyRneMv3f65lps_6a",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1140,
"y": 80,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 20,
"height": 60,
"seed": 1767688328,
"groupIds": [],
"strokeSharpness": "round",
"boundElementIds": [],
"startBinding": {
"focus": -0.3021784319542362,
"gap": 14.800415739789742,
"elementId": "qhkjvI1VmWZdnKvU5QKZK"
},
"endBinding": {
"focus": 0.15789473684210528,
"gap": 1,
"elementId": "of76J4WOJHnHi0L61Vst_"
},
"points": [[0, 0], [-20, 20], [0, 60]],
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow"
},
{
"type": "text",
"version": 213,
"versionNonce": 2105884296,
"isDeleted": false,
"id": "qhkjvI1VmWZdnKvU5QKZK",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1080,
"y": 20.19958426021026,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 139,
"height": 45,
"seed": 2115494904,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["jjuPzyRneMv3f65lps_6a"],
"fontSize": 36,
"fontFamily": 1,
"text": "Execute",
"baseline": 32,
"textAlign": "left",
"verticalAlign": "top"
},
{
"type": "arrow",
"version": 707,
"versionNonce": 543827960,
"isDeleted": false,
"id": "5QQzhw_uqk_rBaW2wMriT",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1220,
"y": 240,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 20,
"height": 80,
"seed": 2059564936,
"groupIds": [],
"strokeSharpness": "round",
"boundElementIds": [],
"startBinding": {
"focus": 0.7391304347826086,
"gap": 2,
"elementId": "C6fyzTg2FHAmrRYfC_THm"
},
"endBinding": {
"focus": 0.3333333333333333,
"gap": 1,
"elementId": "SlvbjeV-9lXbcrlKib-hj"
},
"points": [[0, 0], [20, -40], [20, -80]],
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow"
},
{
"type": "text",
"version": 227,
"versionNonce": 2002374136,
"isDeleted": false,
"id": "C6fyzTg2FHAmrRYfC_THm",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1160,
"y": 220,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 58,
"height": 45,
"seed": 1651025144,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["5QQzhw_uqk_rBaW2wMriT"],
"fontSize": 36,
"fontFamily": 1,
"text": "GC😃棒",
"baseline": 32,
"textAlign": "left",
"verticalAlign": "top"
}
],
"appState": {
"viewBackgroundColor": "#ffffff",
"gridSize": 20
}
}
+365
View File
@@ -0,0 +1,365 @@
# docsify-example-panels
[![NPM](https://img.shields.io/npm/v/docsify-example-panels.svg?style=flat-square)](https://www.npmjs.com/package/docsify-example-panels)
[![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/docsifyjs/awesome-docsify#plugins)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/9f47a225f5044aa491cc77ed7361a983)](https://app.codacy.com/app/VagnerDomingues/docsify-example-panels?utm_source=github.com&utm_medium=referral&utm_content=VagnerDomingues/docsify-example-panels&utm_campaign=Badge_Grade_Dashboard)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://github.com/VagnerDomingues/docsify-example-panels/blob/master/LICENSE)
[![](https://data.jsdelivr.com/v1/package/npm/docsify-example-panels/badge)](https://www.jsdelivr.com/package/npm/docsify-example-panels)
[![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?url=https%3A%2F%2Fgithub.com%2FVagnerDomingues%2Fdocsify-example-panels&hashtags=docsify,developers,frontend,plugin,documentation)
<a class="github-button" href="https://github.com/VagnerDomingues/docsify-example-panels" data-icon="octicon-star" data-show-count="true" aria-label="Star VagnerDomingues/docsify-example-panels on GitHub">Star</a>
A [docsify.js](https://docsify.js.org) plugin for rendering example panels content from markdown.
<!-- panels:start -->
<!-- div:title-panel -->
## Demo
<!-- div:left-panel -->
If you are on widescreen, checkout the *right* panel, *right* there →
<!-- div:right-panel -->
This is an example panel.
You can see it's usage in practice in the docs listed below:
- [Fairlay API](https://fairlay.com/api)
- [FLAP services](https://docs.flap.cloud/#/create_new_service?id=special-files)
<small>please contact me if you use docsify-example-panels. i would like to display it here too.</small>
<!-- div:title-panel -->
## Features
<!-- div:left-panel -->
**Advantages**
- Create div panels really fast and anywhere in your .md file.
- Choose the classnames for your divs and stylize them.
- Use CSS custom properties to change it's structure.
- Prefab CSS classes for "left-panel", "right-panel" and "title-panel".
**Compatibility**
- Fully compatible with any markdown or html features:
<details>
<summary>code snippets </summary>
```html
<body>
<img src="http://www.pudim.com.br/pudim.jpg">
</body>
```
</details>
<details>
<summary>quotes</summary>
> just a quote
?> a cooler quote... <small> (at least i think it is)</small>
</details>
<details>
<summary>images <small>(memorable)</small></summary>
<br/>
<img src="https://avatars0.githubusercontent.com/u/5666881?s=400&u=d94729bdf16611396a720b338c115ec0be656ba6&v=4" width="64" height="64">
</details>
- Fully compatible with major docsify plugins such as:
> [docsify-themeable](https://jhildenbiddle.github.io/docsify-themeable/)
>
> [docsify-tabs](https://jhildenbiddle.github.io/docsify-tabs/)
>
> [docsify-copy-code](https://github.com/jperasmus/docsify-copy-code)
>
> [docsify-pagination](https://github.com/imyelo/docsify-pagination)
**Limitations**
*Nesting* (i.e panels within panels) all pannels are rendered as siblings, Nesting only avaiable throught post-render javascript code.
<!-- div:right-panel -->
For multi-language documentation you can use it along with [docsify-tabs](https://jhildenbiddle.github.io/docsify-tabs/):
<!-- tabs:start -->
### **HTML**
```html
<!-- HTML -->
<h1>Heading</h1>
<p>This is a paragraph.</p>
```
### **JS**
```js
// JavaScript
function add(a, b) {
return Number(a + b);
}
```
### **CSS**
```css
/* CSS */
body {
background: white;
}
```
<!-- tabs:end -->
<!-- div:title-panel -->
## Installation
<!-- div:left-panel -->
Just add the docsify-example-panels plugin to your `HTML` after docsify and you'll be ready to go.
<!-- div:right-panel -->
```html
<!-- docsify (latest v4.x.x)-->
<script src="https://cdn.jsdelivr.net/npm/docsify@4"></script>
<!-- docsify-example-panels (latest) -->
<script src="https://cdn.jsdelivr.net/npm/docsify-example-panels"></script>
```
<!-- div:title-panel -->
## Usage
<!-- div:left-panel -->
1. Define a panel in markdown file using `panels:start` and `panels:end` HTML comments.
HTML comments are used to mark the start and end of a panel. The use of HTML comments prevents any markup from being displayed when markdown is rendered as HTML outside of your docsify site (e.g. Github, GitLab, etc).
<!-- div:right-panel -->
```markdown
<!--&nbsp;panels:start&nbsp;-->
(...) - Awesome div wrapper
<!--&nbsp;panels:end&nbsp;-->
```
<!-- div:left-panel -->
2. Define divs within a panel in markdown file using HTML comments.
The character sequence after double dot : will be rendered as the classname of the div content. all proceeding content will be associated with that div up to start of the next div `div:class` or end of panel `panels:end` comments.
<!-- div:right-panel -->
```markdown
<!--&nbsp;panels:start&nbsp;-->
<!--&nbsp;div:title-panel&nbsp;-->
(...) - Awesome title
<!--&nbsp;div:left-panel&nbsp;-->
(...) - Awesome explanation
<!--&nbsp;div:right-panel&nbsp;-->
(...) - Awesome example
<!--&nbsp;panels:end&nbsp;-->
```
<!-- div:left-panel -->
3. You can add a custom class aswell.
perphaps you want something like a "floating-cat" class
<!-- div:right-panel -->
```markdown
<!--&nbsp;panels:start&nbsp;-->
<!--&nbsp;div:your-custom-class&nbsp;-->
...
<!--&nbsp;panels:end&nbsp;-->
```
<!-- div:title-panel -->
## Customization
<!-- div:left-panel -->
### Properties
Allows you to customize panels styles without rewriting any CSS rule.
<!-- div:right-panel -->
```css
:root{
/* Document */
--docsify-example-panels-document-width: 90%;
/* Wrapper */
--docsify-example-panels-wrapper-width: 100%;
/* Standards */
--docsify-example-panels-padding-inner : 8px 16px;
--docsify-example-panels-padding-surroundings : 0px;
/* Panel Left */
--docsify-example-panels-left-panel-background : transparent;
--docsify-example-panels-left-panel-width : 60%;
/* Panel Right */
--docsify-example-panels-right-panel-background : transparent;
--docsify-example-panels-right-panel-width : 40%;
/* Titles */
--docsify-example-panels-title-background: transparent;
}
```
<!-- div:left-panel -->
### Custom Properties
You can change properties by adding a `<style>` tag or `<link>` reference to your `index.html` file after all other stylesheets, just remember to set the properties within a `:root` selector.
<!-- div:right-panel -->
```html
<style>
:root {
--docsify-example-panels-document-width: 100%; /* Remove margins */
--docsify-example-panels-left-panel-width : 70%; /* bit more for left */
--docsify-example-panels-right-panel-width : 30%; /* bit less for right */
}
</style>
```
<br/>
<br/>
<!-- div:title-panel -->
### Custom Styles
Custom styles can be created using the classname you choosed in markdown
The main purpose of this plugin is to ease and rush the production of any markdown written documentation using the right panel as code examples.
But let's suppose you indeed wants to add a floating cat in your documentation, like i did.
<small> In case you insisted to read this in github url the cat image won't be so floating.</small>
<!-- div:floating-cat -->
<img src="assets/imgs/floating-cat.png">
<!-- div:title-panel -->
<hr>
<!-- div:left-panel -->
1. Add this to your markdown:
<!-- div:right-panel -->
```markdown
<!--&nbsp;panels:start&nbsp;-->
<!--&nbsp;div:floating-cat&nbsp;-->
<img src="path/to/floating-cat.png">
<!--&nbsp;panels:end&nbsp;-->
```
<!-- div:left-panel -->
2. will be rendered like this:
<!-- div:right-panel -->
```html
<div class="docsify-example-panels">
<div class="docsify-example-panels floating-cat">
<img src="path/to/floating-cat.png">
</div>
</div>
```
<!-- div:left-panel -->
3. Then you can play with css animations:
<!-- div:right-panel -->
```css
.floating-cat{
position:absolute;
right: 0px;
width: 200px;
height: 360px;
z-index: 1000;
-webkit-filter: drop-shadow(0px 5px 15px rgba(0,0,0,0.6));
filter: drop-shadow(0px 5px 15px rgba(0,0,0,0.6));
transform: translatey(0px);
animation: float 6s ease-in-out infinite;
}
@keyframes float {
0% {
-webkit-filter: drop-shadow(0px 5px 5px rgba(0,0,0,0.8));
filter: drop-shadow(0px 5px 5px rgba(0,0,0,0.6));
transform: translatey(0px);
}
50% {
box-shadow: drop-shadow(0px 25px 10px rgba(0,0,0,0.4));
filter: drop-shadow(0px 25px 10px rgba(0,0,0,0.2));
transform: translatey(-20px);
}
100% {
box-shadow: drop-shadow(0px 5px 5px rgba(0,0,0,0.8));
filter: drop-shadow(0px 5px 5px rgba(0,0,0,0.6));
transform: translatey(0px);
}
}
```
<!-- panels:end -->
## Contact & Support
- Create a [GitHub issue](https://github.com/VagnerDomingues/docsify-example-panels/issues) for bug reports, feature requests, or questions
- Add a ⭐️ [star on GitHub](https://github.com/VagnerDomingues/docsify-example-panels) or ❤️ [tweet](https://twitter.com/intent/tweet?url=https%3A%2F%2Fgithub.com%2FVagnerDomingues%2Fdocsify-example-panels&hashtags=docsify,developers,frontend,plugin,documentation) to support the project!
## License
This project is licensed under the [MIT license](https://github.com/VagnerDomingues/docsify-example-panels/blob/master/LICENSE).
Plugin created by [Vagner Domingues Madeira](https://github.com/VagnerDomingues/)
<!-- GitHub Buttons -->
<script async defer src="https://buttons.github.io/buttons.js"></script>
+209
View File
@@ -0,0 +1,209 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Yggdrasil is a collections of notes around IT, OT and other geeks stuff">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<link rel="icon" href="style/yggdrasil.ico">
<link
rel="stylesheet"
href="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/style.min.css"
title="docsify-darklight-theme"
type="text/css"
/>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet">
</head>
<body>
<div id="app">Yggdrasil</div>
<footer id="mb-footer"></footer>
<script>
window.$docsify = {
name: 'Yggdrasil',
repo: 'https://github.com/Klagarge/Yggdrasil.git',
maxLevel: 5,
coverpage: 'style/_coverpage.md',
loadNavbar: true, // Nav bar on top
subMaxLevel: 5, // Add table of contents (TOC) in custom sidebar.
auto2top: true, // Scrolls to the top of the screen when the route is changed.
mergeNavbar: true, // Navbar will be merged with the sidebar on smaller screens.
latex: {
inlineMath : [['$', '$'], ['\\(', '\\)']], // default
displayMath : [['$$', '$$']], // default
customOptions: {} // default
},
select: {
detectOperatingSystem: {
enabled: false,
menuId: "operating-system"
}
},
flexibleAlerts: {
style: 'callout'
},
markdown: {
renderer: {
code: function (code, lang) {
if (lang === 'drawio') {
if (window.drawioConverter) {
console.log('drawio')
return window.drawioConverter(code)
} else {
return `<div class='drawio-code'>${code}</div>`
}
} else {
return this.origin.code.apply(this, arguments);
}
}
}
},
darklightTheme: {
siteFont : "Ubuntu",
defaultTheme : 'light',
codeFontFamily : 'Roboto Mono, Monaco, courier, monospace',
bodyFontSize : '17px',
dark: {
coverBackground : 'linear-gradient(to left bottom, hsl(118, 18%, 34%) 0%,hsl(207, 63%, 10%) 100%)',
},
light: {
coverBackground : 'linear-gradient(to left bottom, hsl(118, 100%, 85%) 0%,hsl(181, 100%, 100%) 100%)',
}
},
kroki: {
langs: [
"actdiag",
"blockdiag",
"bpmn",
"bytefield",
"c4plantuml",
"d2",
"dbml",
"ditaa",
"erd",
"excalidraw",
"graphviz",
"nomnoml",
"nwdiag",
"packetdiag",
"pikchr",
//"plantuml",
"rackdiag",
"seqdiag",
"structurizr",
"svgbob",
"symbolator",
"tikz",
"vega",
"vegalite",
"wavedrom",
"wireviz",
"umlet",
],
},
plantuml: {
skin: "classic",
renderAsObject: false,
},
count:{
position: 'bottom',
float: 'left',
isExpected: false
},
timeUpdater: {
text: "<div align='center' width='200px' style='color:gray;font-size:10px' >last update time: {docsify-updated}</div>",
formatUpdated: "{YYYY}-{MM}-{DD} {HH}:{mm}",
whereToPlace: "top",
},
tabs: {
persist: true, // default
sync: true, // default
theme: 'classic', // default
tabComments: true, // default
tabHeadings: true // default
},
};
</script>
<!----- Docsify v4 ----->
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
<!----- Render PlantUML ----->
<script src="//unpkg.com/docsify-puml/dist/docsify-puml.min.js"></script>
<!----- Render graph (UML/Mermaid/...) ----->
<script src="//unpkg.com/docsify-kroki"></script>
<!----- Mermaid (use this one for Light and Dark theme ----->
<script type="module">
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
mermaid.initialize({ startOnLoad: true });
window.mermaid = mermaid;
</script>
<script src="//unpkg.com/docsify-mermaid@2.0.1/dist/docsify-mermaid.js"></script>
<!----- Copy code button ----->
<script src="//cdn.jsdelivr.net/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>
<!----- LateX ----->
<script src="//cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
<script src="//cdn.jsdelivr.net/npm/docsify-latex@0"></script>
<!----- Flexible alert ----->
<script src="https://unpkg.com/docsify-plugin-flexible-alerts"></script>
<!----- Code highlights ----->
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-armasm.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-matlab.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-c.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-cpp.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-java.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-xml.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-json.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-sql.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-pascal.js"></script>
<!----- Dark Theme ----->
<script src="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/index.min.js"> </script>
<!----- docsify-select (latest v1.x.x) ----->
<script src="https://cdn.jsdelivr.net/npm/docsify-select@1"></script>
<!----- Draw.io ----->
<script src="scripts/viewer.min.js"></script>
<script src='scripts/drawio.js'></script>
<!----- Glossary ----->
<script src="//unpkg.com/docsify-glossary/dist/docsify-glossary.min.js"></script>
<!----- Count word ----->
<script src="https://cdn.jsdelivr.net/npm/docsify-count@latest/dist/countable.min.js"></script>
<!----- Progress ----->
<script src="https://cdn.jsdelivr.net/npm/docsify-progress@latest/dist/progress.min.js"></script>
<!----- Accordion ----->
<link rel="stylesheet" href="//unpkg.com/docsify-accordion/src/style.css">
<script src="//unpkg.com/docsify-accordion/src/index.js"></script>
<!----- Terminal ----->
<script src="https://cdn.jsdelivr.net/npm/docsify-terminal-block@1.0.3"></script>
<!----- Last time updated ----->
<script src="https://cdn.jsdelivr.net/npm/docsify-updated/src/time-updater.js"></script>
<!-- docsify-tabs (latest v1.x.x) -->
<script src="https://cdn.jsdelivr.net/npm/docsify-tabs@1"></script>
<style>
:root {
--docsifytabs-border-color: #ededed;
--docsifytabs-tab-highlight-color: purple;
}
</style>
</body>
</html>
+158
View File
@@ -0,0 +1,158 @@
<header><h1>
AD/DA
</h1></header>
Il existe 4 méthodes principales pour une conversion Analogique vers Digital:
- Parallèle
- Par pesée
- Par comptage
- Sigma-delta
| | + (Avantage) | - (Inconvénient) |
|-------------------------|------------------------|-----------------------------|
| **Parallèle<br>Unaire** | Simplicité<br>Monotone | Taille du circuit ~$2^{n}$ |
| **Pesée** | Taille du circuit ~$n$ | |
| **Par comptage** | 1 seul switch | Nécessite d'aller très vite |
$$SNR [dB]=6,02\cdot n+1,78$$
$n$ : nombre de bits du convertisseur
$$OSR = \frac{f_{s}}{2f_{0}}$$
$OSR$ : OverSampling Ratio <br>
$f_{s}$ : Sampling Frequency <br>
$f_{0}$ : Entry Fréquency <br>
$$n=\frac{SNR}{20\log_{10}(2\cdot OSR)}$$
$n$ : Ordre du filtre <br>
$SNR$ : Signal sur bruit <br>
$OSR$ : Oversampling ratio <br>
# AD - Parallèle
![AD Parallèle](medias/parallele.png ':size=200')
----------
# AD - Par comptage
Un seul switch mais avec une fréquence de commutation très rapide
$$
V_{a} = \frac{t_{on}}{T}\cdot V_{ref}
$$
![AD par comptage](medias/comptage.png ':size=300')
![Graph AD par comptage](medias/comptage-graph.png ':size=200')
----------
# AD - Par pesée
Un switch par bit.
![AD par pesée](medias/pesee.png ':size=300')
Pour un $V_{red} = 1[V]$ on a:
| z2 | z1 | z0 | Va |
|----|----|----|-------|
| 0 | 0 | 0 | 0,000 |
| 0 | 0 | 1 | 0,125 |
| 0 | 1 | 0 | 0,250 |
| 0 | 1 | 1 | 0,375 |
| 1 | 0 | 0 | 0,500 |
| 1 | 0 | 1 | 0,625 |
| 1 | 1 | 0 | 0,750 |
| 1 | 1 | 1 | 0,875 |
----------
# DA - Échantillonneur avec maintien
# Caractéristique de convertisseurs
## Digits et affichage
$$
nBits = Ceiling\left[log_2\cdot \frac{plage}{pas}\right]
$$
----------
# Sigma / Delta
[sigma-delta.drawio](medias/sigma-delta.drawio ':include :type=code')
## Principe
Un convertisseur analogique-numérique (AD) ou numérique-analogique (DA) delta-sigma se compose toujours d'un modulateur
delta-sigma produisant un "flux binaire" ou "signal binaire séquentiel" et d'un filtre passe-bas.
Le modulateur peut être mis en œuvre comme un circuit numérique pour un signal numérique ou comme un circuit analogique
pour un signal analogique. De même, le filtre passe-bas peut être analogique pour générer un signal de sortie
analogique ou numérique, réalisé soit par un circuit numérique soit par un algorithme dans un processeur de signal.
Cette structure permet d'explorer le flux binaire et le filtre passe-bas avant d'approfondir l'étude du
modulateur delta-sigma.
### Le flux binaire
Le flux binaire peut être interprété comme un signal numérique ou analogique.
Il s'agit d'un signal séquentiel à un bit avec une fréquence d'horloge significativement plus élevée que le débit des
données numériques de l'ADC ou du DAC. Sa caractéristique essentielle est que sa moyenne correspond à la moyenne du
signal d'entrée. Un niveau logique "haut" représente la valeur maximale de sortie, tandis qu'un niveau logique "bas"
représente la valeur minimale de sortie.
- **Sortie analogique :** Le flux binaire est converti par un DA à un bit en un signal analogique.
Les valeurs d'entrée "bas" et "haut" se traduisent respectivement par deux tensions analogiques précises, par exemple,
1V et +1V.
- **Sortie numérique :** Dans le cas d'une sortie numérique, un niveau "haut" (ou "bas") dans le flux binaire représente
la valeur maximale (ou minimale) de la plage de valeurs numériques. Par exemple, dans un système à 8 bits, un niveau
"haut" pourrait correspondre à FF en hexadécimal, tandis qu'un niveau "bas" pourrait correspondre à 00 en hexadécimal.
Un flux binaire similaire existe dans un signal modulé en largeur d‘impulsions (PWM), mais il présente quelques
désavantages par rapport au flux binaire d'un convertisseur delta-sigma, également appelé signal modulé en rapport
d’impulsions (PPM). La transmission en série d'une valeur numérique, telle que la sortie en série d'un ADC conventionnel
par pesée, est quant à elle appelée modulation par codage d'impulsions (PCM).
----------
#
Un convertisseur sigma-delta est un type de convertisseur
analogique-numérique (CAN) qui utilise une boucle de contre-réaction pour
échantillonner et quantifier un signal analogique en un signal numérique.
Le principe de fonctionnement d'un convertisseur sigma-delta peut être
expliqué en plusieurs étapes :
1. Échantillonnage : Tout d'abord, le signal analogique est échantillonné
à une fréquence d'échantillonnage beaucoup plus élevée que la bande
passante du signal. Cette haute fréquence d'échantillonnage permet de
réduire considérablement les effets des distorsions et du bruit dans le
domaine fréquentiel.
2. Quantification : Le signal échantillonné est ensuite quantifié en un
signal numérique à l'aide d'un comparateur qui compare le signal avec une
référence de tension fixe. La sortie du comparateur est une suite de bits
représentant la valeur du signal analogique à chaque instant.
3. Contre-réaction : Le signal quantifié est ensuite utilisé pour régler
un oscillateur numérique qui produit un signal d'erreur. Ce signal
d'erreur est ajouté au signal d'entrée dans la boucle de contre-réaction,
ce qui permet de compenser les erreurs de quantification et de réduire le
bruit dans le signal quantifié.
4. Suréchantillonnage : Le signal numérique produit par le convertisseur
sigma-delta est suréchantillonné à une fréquence beaucoup plus élevée que
la fréquence d'échantillonnage initiale, ce qui permet de réduire encore
davantage les effets du bruit dans le signal.
5. Décimation : Enfin, le signal suréchantillonné est décimé à la
fréquence d'échantillonnage désirée en utilisant un filtre passe-bas
numérique qui élimine les composantes de hautes fréquences indésirables.
Le résultat final est un signal numérique représentant fidèlement le
signal analogique d'origine, avec une faible distorsion et un faible
bruit. La boucle de contre-réaction permet au convertisseur sigma-delta
d'atteindre des performances supérieures à celles des autres types de
convertisseurs analogiques-numérique en termes de résolution et de
précision.
![img.png](medias/snr_by_osr.png)
----------
+339
View File
@@ -0,0 +1,339 @@
<header><h1>
Filtres
</h1></header>
-----------
# Diagramme de Bode
## H0 - Gain statique
$$H_{0}(s)=A$$
![filters-h0.png](medias%2Ffilters-h0.png)
## H1 - Coupe bas, amplis haut
$$H_{1}(s)=\frac{s}{\omega_{0}}$$
![filters-h1.png](medias%2Ffilters-h1.png)
## H2 - Ampli haut, coupe bas
$$H_{2}(s)=\frac{\omega_{0}}{s}$$
![filters-h2.png](medias%2Ffilters-h2.png)
## H3 - Ampli haut
$$H_{3}(s)=\frac{\omega_{0}+1}{\omega_{0}} = 1+ \frac{s}{\omega_{0}}$$
![filters-h3.png](medias%2Ffilters-h3.png)
## H4 - Coupe haut
$$H_{4}(s)=\frac{\omega_{0}}{\omega_{0}+s} = \frac{1}{1+\frac{s}{\omega_{0}}}$$
![filters-h4.png](medias%2Ffilters-h4.png)
## Résonance
![filters-h5-h6.png](medias%2Ffilters-h5-h6.png)
----------
# Filtres numériques
![filters-summary.png](medias%2Ffilters-summary.png)
## Filtres numériques récursifs (IIR)
Les filtres numériques récursifs (IIR) sont des filtres numériques qui utilisent des rétroactions.
Ils sont basés sur des équations aux différences.
Les filtres récursifs sont utilisés pour des applications où la phase du filtre n'est pas critique.
Ils sont utilisés dans les télécommunications et la régulation.
Les intérêts des filtres récursifs :
- Leur faible coût en calcul
- Faible retard (télécommunication et la régulation)
Les inconvénients des filtres récursifs :
- Ils possèdent une phase non-linéaire
- Ils peuvent avoir une instabilité numérique
***Fonction de transfert en $z$ :***
$$F(z) = \frac{Y(z)}{U(z)} = \frac{b_{0}+b_{1}z^{-1}+\dots+b_{n}z^{-n}}{1+a_{1}z^{-1}+\dots+a_{n}z^{-n}}$$
Numérisation par conversion DA et AD d'un filtre analogique :
$$F(z) = (1-z^{-1})Z \left\{ L^{-1}\left[ \frac{F(s)}{s} \right] \right\} $$
La fonction de transfert en *z* permet ensuite de tirer l'équation aux différences qui servira à réaliser le filtre sous forme d'un algorithme récursif
$$y[k]= b_{n}u[k-n]+ \dots + b_{1}u[k-1]+b_{0}u[k]-a_{n}y[k-n]-\dots-a_{1}y[k-1]$$
Si on souhaite connaître la réponse fréquentielle d'un filtre, il suffit de remplacer l'opérateur *z* par sa transformée de Fourier :
$$a\cdot z^{n}=a\cdot \exp(n\cdot s\cdot T_{s})=a\cdot \exp(n\cdot \mathbf{j}\omega T_{s})$$
$T_{s}$ : La période d'échantillonnage
Comme l’exponentielle imaginaire est périodique, la réponse fréquentielle d’un filtre numérique est également périodique
de fréquence, et sa représentation se fait dans le domaine de fréquences allant de $0$ à $fs/2$. <br>
Le gain statique d’un filtre peut être calculé selon :
$$\text{Gain statique d'un filtre} = \lim_{ s \to 0 } F(s) = \lim_{ z \to 1 } F(z)$$
> [!NOTE|label:Passage pôle et zéro s ↔ z]
> $$\begin{align}
z&=\exp(s\cdot T_{s}) \\
s&=\frac{1}{T_{s}\ln(z)}
\end{align}$$
![Filtre numérique passe bas ordre 2 - Réponse impulsionnel](medias/filtre_numerique_passe_bas_2-reponse.png ':size=40%')
![Filtre numérique passe bas ordre 2 - Bode](medias/filtre_numerique_passe_bas_2-bode.png ':size=40%')
### Transformation analogique-numérique
La transformation bilinéaire : transformation de Tustin ou approximation de Padé de premier ordre
$$s = \frac{2}{T_{s}} \frac{z-1}{z+1} \qquad \qquad z = \frac{1+\frac{1}{2}sT_{s}}{1-\frac{1}{2}sT_{s}}$$
***Pour un filtre analogique de premier ordre :***
$$F(s)=\frac{d_{0}+d_{1}s}{c_{0}+c_{1}s} \to F(z) = \frac{b_{1}z^{-1}+b_{0}}{a_{1}z^{-1}+1} $$
$$
a_{1} = \frac{c_{0}-\frac{2c_{1}}{T_{s}}}{c_{0}+\frac{2c_{1}}{T_{s}}}
\qquad \qquad
b_{0} = \frac{d_{0}+\frac{2d_{1}}{T_{s}}}{c_{0}+\frac{2c_{1}}{T_{s}}}
\qquad \qquad
b_{1} = \frac{d_{0}-\frac{2d_{1}}{T_{s}}}{c_{0}+\frac{2c_{1}}{T_{s}}}
$$
***Pour un filtre analogique de second ordre :***
$$F(s)=\frac{Y(s)}{U(s)}=\frac{d_{0}+d_{1}s+d_{2}s^{2}}{c_{0}+c_{1}s+c_{2}s^{2}}
\to F(z) = \frac{Y(z)}{U(z)}=\frac{b_{2}z^{-2}+b_{1}z^{-1}+b_{0}}{a_{2}z^{-2+}a_{1}z^{-1}+1} $$
$$\begin{gather}
C = c_{0}+\frac{2c_{1}}{T_{s}}+\frac{4c_{2}}{T_{s}^{2}} \\
\\
a_{1} = \frac{2\left( c_{0}-\frac{4c_{2}}{T_{s}^{2}} \right)}{C}
\qquad \qquad
a_{2} = \frac{ c_{0}-\frac{2c_{1}}{T_{s}}+\frac{4c_{2}}{T_{s}^{2}} }{C}
\qquad \qquad
b_{0} = \frac{d_{0}+\frac{2d_{1}}{T_{s}}+\frac{4d_{2}}{T_{s}^{2}}}{C} \\
\\
b_{1} = \frac{2\left( d_{0}-\frac{4d_{2}}{T_{s}^{2}} \right)}{C}
\qquad \qquad
b_{2} = \frac{d_{0}-\frac{2d_{1}}{T_{s}}+\frac{4d_{2}}{T_{s}^{2}}}{C}
\end{gather}$$
## Filtre numérique non-récursif (FIR)
Les filtres numériques non-récursifs (FIR) sont des filtres numériques qui n'utilisent pas de rétroactions.
Ils sont basés sur des équations aux différences.
Les filtres non-récursifs sont utilisés pour des applications où la phase du filtre est critique.
Ils sont utilisés dans le traitement du signal et la conception de filtres.
Les intérêts des filtres non-récursifs :
- Les fonctions de transfert ne possèdent que des coefficients $b_i$. ($\forall a_{i} = 0$)
- Ils possèdent une phase linéaire exacte
- Ils sont stables
Les inconvénients des filtres non-récursifs :
- Ils sont plus coûteux en calcul
- Ils nécessitent plus de matériel
Coefficients symétrique: pairs $b_{n-i} = b_{i}$, impairs $b_{n-i} = -b_{i}$, $b_{n/2}=0$ <br>
**Symétrie paire:**
$$H(\mathbf{j}\omega)
= \sum^{n}_{i=o}b_{i}e^{-\frac{\mathbf{j}i\omega}{f_{s}}}
= e^{\frac{-\mathbf{j}n\omega}{2f_{s}}} \sum^{n/2}_{i=0}b_{i}\cos\left[ \frac{\left( i-\frac{n}{2} \right)\omega}{f_{s}} \right]$$
**Symétrie impaire:**
$$H(\mathbf{j}\omega)=\mathbf{j}e^{\frac{-\mathbf{j}n\omega}{2f_{s}}}\sum^{n/2}_{i=0}b_{i}\sin\left( \frac{(i-\frac{n}{2})\omega}{f_{s}} \right)$$
Les filtres FIR à coefficients symétriques ont une phase linéaire.
$$
\text{Symétrie paire : }\varphi(\omega)=-\frac{n\omega}{2f_{s}}
\qquad \qquad
\text{Symétrie impaire : } \varphi(\omega)= -\frac{n\omega}{2f_{s}}+\frac{\pi}{2}
$$
$$\text{Temps de propagation : }t_{gr}=-\frac{d\varphi}{d\omega}=\frac{n}{2f_{s}}=\frac{n}{2}T_{s}$$
Il y a donc 4 types de filtres FIR à phase linéaire : n resp. symétrie paire/impaire
***Fonction de transfert en $z$ :***
$$F(z) = \frac{Y(z)}{U(z)} = b_{0}+b_{1}z^{-1}+\dots+b_{n}z^{-n}$$
***Équation aux différences :***
$$y[k]= b_{n}u[k-n]+ \dots + b_{1}u[k-1]+b_{0}u[k]$$
***Réponse en fréquence :***
$$z=\exp(\mathbf{j}\omega\cdot T_{s})$$
### Filtres FIR à symétrie paire :
$$b_{n-k} = b_{k}$$
Exemple : FIR à symétrie paire d'ordre 6
$$\begin{align}
F(z) &= \frac{Y(z)}{U(z)} \\
&= 1+2z^{-1}+3z^{-2}+4z^{-3}+3z^{-4}+2z^{-5}+z^{-6} \\
&= \frac{z^{6}+2z^{5}+3z^{4}+4z^{3}+3z^{2}+2z^{1}+1}{z^{6}}
\end{align}$$
Les filtres FIR à symétrie paire sont toujours de type passe-bas ou coupe-bande. <br>
Ils possèdent autant de pôles que l'order du filtre. Ces derniers sont à (0,0) sur le cercle unité. <br>
Ils possèdent autant de zéros que l'ordre du filtre. Ces derniers sont sur le cercle unité. <br>
### Filtres FIR à symétrie impaire :
$$b_{n-k} = -b_{k}$$
Exemple : FIR à symétrie impaire d'ordre 6
$$\begin{align}
F(z) &= \frac{Y(z)}{U(z)} \\
&= -1-2z^{-1}-3z^{-2}+0z^{-3}+3z^{-4}+2z^{-5}+z^{-6} \\
&= \frac{z^{6}+2z^{5}+3z^{4}+0z^{3}-3z^{2}-2z^{1}-1}{z^{8}}
\end{align}$$
Les filtres FIR à symétrie paire sont toujours de type passe-haut ou passe-bande. <br>
Ils possèdent autant de pôles que l'ordre du filtre. Ces derniers sont à (0,0) sur le cercle unité. <br>
Ils possèdent autant de zéros que l'ordre du filtre. <br>
## Filtres numériques FIR idéaux
$$\omega_{c_{d}} = 2\pi \frac{f_{c_{a}}}{f_{s}}$$
### Filtre passe-bas idéal
$$H(\mathbf{j}\omega)=\begin{cases}
1 &\text{ si } |\omega| \leq \omega_{c} \\
0 &\text{ si } |\omega| > \omega_{c}
\end{cases} $$
La transformée de Fourier inverse donne la réponse impulsionnelle d’un filtre passe-bas idéal.
$$h[n] = \begin{cases}
\frac{\omega_{c}}{\pi} \frac{\sin((n-N_{s})\omega_{c})}{(n-N_{s})\omega_{c}} &\text{ si } 0\leq n \leq N \\
\\
\frac{\omega_{c}}{\pi} &\text{ si } n=N_{s} \\
\\
0 &\text{ sinon}
\end{cases}$$
On tronque à $N=ordre$ du filtre. $N_{s}=\frac{N}{2}$ <br>
Ces coefficients ne sont rien d’autre que les coefficients de la fonction de transfert en *z* du filtre passe-bas idéal.
### Filtre passe-haut idéal
$$H(\mathbf{j}\omega)=\begin{cases}
0 &\text{ si } |\omega| \leq \omega_{c} \\
1 &\text{ si } |\omega| > \omega_{c}
\end{cases}$$
La transformée de Fourier inverse donne la réponse impulsionnelle d’un filtre passe-bas idéal.
$$h[n] = \begin{cases}
-\frac{\omega_{c}}{\pi} \frac{\sin((n-N_{s})\omega_{c})}{(n-N_{s})\omega_{c}} &\text{ si } 0\leq n \leq N \\
\\
1-\frac{\omega_{c}}{\pi} &\text{ si } n=N_{s} \\
\\
0 &\text{ sinon}
\end{cases}$$
On tronque à $N=ordre$ du filtre. $N_{s}=\frac{N}{2}$ <br>
Ces coefficients ne sont rien d’autre que les coefficients de la fonction de transfert en *z* du filtre passe-haut idéal.
### Filtre passe-bande idéal
$$H(\mathbf{j}\omega)=\begin{cases}
1 &\text{ si } \omega_{c_{1}} \leq \omega \leq \omega_{c_{2}} \\
0 &\text{ si } \omega \leq \omega_{c_{1}} \quad et \quad \omega\geq \omega_{c_{2}}
\end{cases}$$
La transformée de Fourrier inverse donne la réponse impulsionnelle d’un filtre passe-bande idéal.
$$h[n] = \begin{cases}
\frac{\omega_{c_{2}}}{\pi} \frac{\sin((n-N_{s})\omega_{c_{2}})}{(n-N_{s})\omega_{c_{2}}}-\frac{\omega_{c_{1}}}{\pi} \frac{\sin((n-N_{s})\omega_{c_{1}})}{(n-N_{s})\omega_{c_{1}}} &\text{ si } 0\leq n \leq N \\
\\
\frac{\omega_{c_{2}}-\omega_{c_{1}}}{\pi} &\text{ si } n=N_{s}\\
\\
0 &\text{ sinon}
\end{cases}$$
On tronque à $N=ordre$ du filtre. $N_{s}=\frac{N}{2}$ <br>
Ces coefficients ne sont rien d’autre que les coefficients de la fonction de transfert en *z* du filtre passe-bande idéal.
### Filtre coupe-bande idéal
$$H(\mathbf{j}\omega)=\begin{cases}
0 &\text{ si } \omega_{c_{1}} \leq \omega \leq \omega_{c_{2}} \\
1 &\text{ si } \omega \leq \omega_{c_{1}} \quad et \quad \omega\geq \omega_{c_{2}}
\end{cases}$$
La transformée de Fourrier inverse donne la réponse impulsionnelle d’un filtre coupe-bande idéal.
$$h[n] = \begin{cases}
\frac{\omega_{c_{1}}}{\pi} \frac{\sin((n-N_{s})\omega_{c_{1}})}{(n-N_{s})\omega_{c_{1}}}
-\frac{\omega_{c_{2}}}{\pi} \frac{\sin((n-N_{s})\omega_{c_{2}})}{(n-N_{s})\omega_{c_{2}}}
&\text{ si } 0\leq n \leq N \\
\\
1-\frac{\omega_{c_{2}}-\omega_{c_{1}}}{\pi} &\text{ si } n=N_{s}\\
\\
0 &\text{ sinon}
\end{cases}$$
On tronque à $N=ordre$ du filtre. $N_{s}=\frac{N}{2}$ <br>
Ces coefficients ne sont rien d’autre que les coefficients de la fonction de transfert en *z* du filtre coupe-bande idéal.
## Synthèse par fenêtrage
Le point de départ de la synthèse des filtres FIR est donné par la considération des réponses impulsionnelles des filtres idéaux.
Comme celles-ci sont infiniment longues et donc non réalisables, on va se contenter d’une approximation de leurs réponses.
### Méthode de fenêtrage
- **Fenêtre rectangulaire :** $$W[n] = \begin{cases}
1 &\text{ si } 0\leq n \leq N \\
0 &\text{ sinon}
\end{cases}$$
- **Fenêtre triangulaire (Bartlett) :** $$W[n] = \begin{cases}
2 \frac{n}{N} &\text{ si } 0\leq n \leq \frac{N}{2} \\
2-2 \frac{n}{N} &\text{ si } \frac{N}{2}\leq n \leq N \\
0 &\text{ sinon}
\end{cases}$$
- **Fenêtre sinusoïdale (Hann) :** $$W[n] = \begin{cases}
0,5\left( 1-\cos\left( \frac{2\pi n}{N} \right) \right) &\text{ si } 0\leq n\leq N \\
0 &\text{ sinon}
\end{cases}$$
- **Fenêtre de Hamming :** $$W[n] = \begin{cases}
0,54-0,46\cos\left( \frac{2\pi n}{N} \right) &\text{ si } 0\leq n\leq N \\
0 &\text{ sinon}
\end{cases}$$
- **Fenêtre de Blackman :** $$W[n] = \begin{cases}
0,42-0,5\cos\left( \frac{2\pi n}{N} \right)+0,08\cos\left( \frac{4\pi n}{N} \right) &\text{ si } 0\leq n\leq N \\
0 &\text{ sinon}
\end{cases}$$
- **Fenêtre de Kaiser-Bessel :** $$W[n] = \begin{cases}
\frac{I_{0}\left( \beta\sqrt{1-\left( \frac{1-n}{N_{s}} \right)^{2}} \right)}{I_{0}(\beta)} &\text{ si } 0\leq n\leq N \\
0 &\text{ sinon}
\end{cases}$$
$I_{0}$ : Fonction de Bessel modifiée d’ordre 0 <br>
$\beta$ : Paramètre de forme
***Caractéristiques spectrales de quelques fenêtres***
| Fenêtres | Largeur du 1er lobe | Atténuation du 1er lobe [dB] | Décroissance des lobes suivants | Ondulation Rp [dB] | Atténuation max [dB] | Bande de transition<br>[rad/sec]<br>$\Delta\omega$ |
|---------------------|---------------------|------------------------------|---------------------------------|--------------------|----------------------|----------------------------------------------------|
| Rectangulaire | $4\pi/N$ | $-13$ | $20 dB/dec$ | $0,74$ | $21$ | $1,8\pi/N$ |
| Triangulaire | $8\pi/N$ | $-27$ | $40 dB/dec$ | $0$ | $25$ | $6,1\pi/N$ |
| Cosinus | $8\pi/N$ | $-32$ | $60 dB/dec$ | $0,055$ | $44$ | $6,2\pi/N$ |
| Hamming | $8\pi/N$ | $-43$ | $20 dB/dec$ | $0,014$ | $53$ | $6,6\pi/N$ |
| Blackman | $12\pi/N$ | $-58$ | $60 dB/dec$ | $0,0017$ | $74$ | $11\pi/N$ |
| Kaiser $\beta=4,54$ | $7,2\pi/N$ | $-30$ | $20 dB/dec$ | $0,025$ | $50$ | $5,8\pi/N$ |
| Kaiser $\beta=5,66$ | $8,4\pi/N$ | $-42$ | $20 dB/dec$ | $0,009$ | $60$ | $7,2\pi/N$ |
### Comment calculer son filtre
![specification_de_filtre.svg](medias%2Fspecification_de_filtre.svg ':size=50%')
$H(\mathbf{j}\omega)$ : Gain <br>
$\delta_{1}$ : L'erreur de gain tolérable <br>
$\delta_{2}$ : L'erreur d'atténuation tolérable <br>
$\omega_{p}$ : Fréquence de pulsation de passage <br>
$\omega_{a}$ : Fréquence de pulsation d'arrêt <br>
1. En fonction du gabarit, déterminer le type de filtre désiré (`passe-bas`, `passe-haut`, `passe-bande`, `coupe-bande`)
2. Calculer $f_{c} \to \omega_{c}$ et $\Delta f \to \Delta\omega$
$$\begin{gather}
f_{c} = \frac{f_{a}+f_{p}}{2} \qquad & \qquad \Delta f = f_{a}-f_{p} \\
\omega_{c} = 2\pi \frac{f_{c}}{f_{s}} \quad & \quad \Delta\omega = \frac{2\pi\Delta f}{f_{s}}
\end{gather}$$
3. Choisir une fenêtre $W[n]$ satisfaisante du point de vue de l’atténuation et du reste du cahier des charges.(voir tableau ci-dessus)
4. Calculer l'ordre du filtre à l'aide de la bande de transition du filtre choisit. Arrondir au nombre pair supérieur.
5. Calculer la réponse impulsionnelle $h_{f}[n]$ du filtre avec $h[n]*W[n]$
6. Calculer les coefficients $b_{n}$ du filtre avec $h_{f}[n]$
<details>
<summary>Démarche selon les slides</summary>
1. Connaissant le gabarit du filtre désiré, choisir le filtre idéal correspondant
2. Calculer les pulsations caractéristiques se situant au centre des bandes de transitions
3. Rechercher la réponse impulsionnelle $h[n]$ du filtre idéal, si celle-ci n’est pas connue, on peut la calculer par transformation de Fourier inverse
4. Choisir une fenêtre $W[n]$ satisfaisante du point de vue de l’atténuation
5. Connaissant la largeur de la bande de transition , calculer l’ordre du filtre $N$
6. Calculer les coefficients du filtre en multipliant la réponse impulsionnelle par la fenêtre choisie
</details>
----------
+9
View File
@@ -0,0 +1,9 @@
<header>
<h1>Électronique</h1>
</header>
# Convertisseurs
- [AD/DA](/infotro/Eln/ADDA)
- [Signaux et échantillonnage](/infotro/Eln/SignauxEtEchantillonnage)
- [Filtres](/infotro/Eln/Filtres)
- [RF](/infotro/Eln/RF)
+145
View File
@@ -0,0 +1,145 @@
<header><h1>
Radio Fréquence
</h1></header>
----------
# Skin effect
Le courant électrique circule principalement à la surface des conducteurs.
Pour un fil de cuivre rond, la résistance en continue est donnée par la formule suivante:
$$R_{DC} = \rho\frac{l}{\pi r²}$$
avec:
- $R_{DC}$: résistance en continue
- $\rho$: résistivité du cuivre
- $l$: longueur du fil
- $r$: rayon du fil
La résistance en courant alternatif est donnée par la formule suivante:
$$R_{AC} = R_{DC} \frac{r}{2\delta}$$
avec:
- $R_{AC}$: résistance en courant alternatif
- $R_{DC}$: résistance en continue
- $r$: rayon du fil
- $\delta$: profondeur de pénétration du courant
La profondeur de pénétration du courant est donnée par la formule suivante:
$$\delta = \sqrt{\frac{\rho}{\pi f \mu_0}}$$
avec:
- $\delta$: profondeur de pénétration du courant
- $\rho$: résistivité du cuivre
- $f$: fréquence
- $\mu_0$: perméabilité magnétique du vide $= 4\pi 10^{-7} H/m$
On peut en déduire la résistance en courant alternatif:
$$R_{AC} = \frac{l\sqrt{\rho\pi\mu_0}}{2\pi r} \sqrt{f} \qquad \qquad R_{AC(f_{2})} = R_{AC(f_{1})} \cdot \sqrt{ \frac{f_{2}}{f_{1}} }$$
Pour calculer l'inductance et la capacité d'un fil, on peut utiliser les formules suivantes :
$$\begin{align}
L&=\frac{\mu_{0}l}{\pi}acosh\left( \frac{d}{2r} \right) \\
C&=\frac{\pi\epsilon_{0}l}{acosh\left( \frac{d}{2r} \right)}
\end{align}$$
avec :
- $\mu_{0}$: perméabilité magnétique du vide $=4\pi\cdot 10^{-7}\left[ \frac{H}{m} \right]$
- $\epsilon_{0}$: permittivité du vide $=8.854\cdot 10^{-12}\left[ \frac{F}{m} \right]$
# Model à haute fréquence
## Inductance
![rf-inductor.png](medias%2Frf-inductor.png ':size=50%')
<u>Facteur de qualité :</u>
$$Q(f) = \frac{X_{L}(f)}{R(f)} \qquad \qquad R_{AC}(f)= \frac{l\sqrt{ \rho \pi \mu_{0} }}{2\pi r}\sqrt{ f}$$
<u>Réactance & Impédance:</u>
$$X_{L}=\omega L=2\pi fL \qquad \qquad Z_{L}=jX_{L}=j\omega L$$
<u>Résonance :</u> $\omega_{r} ≃\omega_{0}=\frac{1}{\sqrt{ LC }}$ <br>
SRF: Self Resonant Frequency = $\frac{\omega_{r}}{2\pi}$
$$Z_{L,tot} = (j\omega L+R) \mid \mid \frac{1}{j\omega C} =\frac{sL+R}{1+sRC+s^{2}LC}$$
![rf-inductor.png](medias/rf-inductor-graph.png)
# Capacité
![rf-capacitor.png](medias%2Frf-capacitor.png ':size=50%')
<u>Facteur de qualité :</u>
$$Q(f) = \frac{X_{C}(f)}{R} \qquad \qquad \omega_{r} ≃\omega_{0}=\frac{1}{\sqrt{ LC }}$$
<u>Réactance & Impédance:</u>
$$X_{C}=\frac{1}{\omega C} \qquad \qquad Z_{C}=\frac{1}{j\omega C}$$
![rf-capacitor-graph.png](medias/rf-capacitor-graph.png)
# Résistance
![rf-resistor.png](medias%2Frf-resistor.png ':size=35%')
# Transmission line
$$ k = \frac{2\pi}{\lambda} \qquad \qquad \lambda = \frac{c_{mat}}{f} $$
$\Gamma$ (Gamma) : coefficient de réflexion
# Smith Chart
## Ajouter une ligne de réflexion
1. Placer $Z1$
1. Normaliser ($\hat{Z_{1}}=\frac{Z_{1}}{Z_{0}}$)
2. Placer le point au croisement de l'axe réel et imaginaire (Impédance = bleu)
2. Chercher $\Gamma$ (indice de reflexion)
1. Tracer l'angle ("Angle of reflexion coefficient in degrees")
2. Tracer le module (cercle au compas + rapport sur les échelles du bas)
3. Ajouter la ligne selon le sens
- $\circlearrowright$ : toward generator
- $\circlearrowleft$ : toward load
4. Lire les nouvelles valeures
1. Tracer le nouvel angle $\to \angle(\Gamma_{2})$
2. Tracer et lire $\hat{Z_{2}}$ puis $Z_{2}$
![add_line_on_smith_chart.png](medias/add_line_on_smith_chart.png)
# Impedance Matching
$Z_{LD} \to Z_S^*$, sans résistance
- Capacité + inductance
- ligne de transmission + capacité / inductance + ligne de transmission
- ligne de transmission + stub + ligne de transmission
-
![impedance_matching.png](medias%2Fimpedance_matching.png)
## Stub
Pour trouver la longueur du stub :
1. Pour un short stub, placer une inductance parallèle et pour un open stub un condensateur parallèle
2. Trouver la valeur normalisée de cette admittance.
3. Pour un short stub, placer le delta dans les négatifs (parti du dessus) et pour un open stub dans les positifs (partie du dessous).
4. Regarder la longueur d'onde. Partir du côté open circuit pour un open stub et du côté short circuit pour un short stub.
# Filtres
Filter components <br>
![Filters prototype](medias/rf-filters_prototype.png) <br>
![Filters components](medias/rf-filter_components.png)
Normalized value for Butterworth order 1 to 10 <br>
![Normalized value for Butterworth order 1 to 10](medias/rf-normalized_value-butterworth_1_10.png)
Normalized value for Chebyshev 0.5dB Ripple <br>
![Normalize value for Chebyshev 0.5 Ripple](medias/rf-normalized_value_chebyshev_ripple_0_5.png)
Normalized value for Chebyshev 3dB Ripple <br>
![Normalize value for Chebyshev 3 Ripple](medias/rf-normalized_value_chebyshev_ripple_3.png)
## Filtres passe-bas en microstrip
$l$ : longueur physique en mm <br>
$\theta$ : longueur électrique / degré <br>
$$ l_l = g_k * \frac{\lambda_{gl} * Z_0}{2\pi *Z_{0l}} $$
$$ l_c = g_k * \frac{\lambda_{gc} * Z_{0c}}{2\pi *Z_0} $$
$$ \theta_l = g_k * \frac{360 * Z_0}{2\pi *Z_{0l}} $$
$$ \theta_c = g_k * \frac{360* Z_{0c}}{2\pi *Z_0} $$
![rf-kuroda_identities.png](medias%2Frf-kuroda_identities.png)
@@ -0,0 +1,256 @@
<header><h1>
Signaux et échantillonnage
</h1></header>
-----------
# Fourier
Pour comprendre les signaux, il est important de comprendre la transformée de Fourier.
La transformée de Fourier est une technique de traitement du signal utilisée pour analyser et manipuler les signaux continus.
Elle permet d'exprimer un signal dans le temps en domaine fréquentiel.
## Transformée de Fourier
La transformée de Fourier est une technique de traitement du signal utilisée pour analyser et manipuler les signaux continus.
Elle permet d'exprimer un signal dans le temps en domaine fréquentiel.
La transformée de Fourier est définie par la formule suivante :
Soit $f(x)$ une fonction non périodique définie sur $\mathbb{R}$.
$$\begin{align}
\text{Transformée de Fourier de }f(x) &: \qquad \widehat{f}(\omega) = \int^{\infty}_{-\infty}f(x)e^{-\mathbf{i}\omega x}dx \qquad (\omega\in \mathbb{R})\\
\text{Intégrale de Fourier de } f(x) &: \qquad \frac{1}{2\pi}\int_{-\infty}^{\infty} \widehat{f}(\omega) e^{\mathbf{i}x\omega}\, d\omega \qquad(x \in\mathbb{R}).
\end{align}$$
**Théorème** : Si $\int_{-\infty}^{\infty} |f(x) \, dx < \infty$ et si $f$ est une fonction de variation bornée sur tout $\mathbb{R}$, alors
$$
\frac{1}{2\pi}\int_{-\infty}^{\infty} \widehat{f}(\omega)e^{\mathbf{i}x\omega} \, d\omega = \begin{cases}
f(x) &\text{si }f \text{ est continue au point }x \\
\text{milieu du saut de } f(x) &\text{si }f \text{ a un saut en }x
\end{cases}
$$
## Transformée de Fourier discrète
La transformée de Fourier discrète (DFT - Discret Fourier Transform) est une transformation discrète utilisée pour analyser le spectre fréquentiel
d'un signal discret.
Elle est l'équivalent discret de la transformée de Fourier, qui est utilisée pour analyser les signaux continus.
On pose: $\Omega := \omega \cdot T_{s}$
La transformation de Fourier de $x[k]$ est donnée par la formule suivante :
$$\mathcal{F}\{x[k]\} = X(\Omega) \quad := \quad\sum^{+\infty}_{k=-\infty}x[k]\cdot e^{-jk\Omega}= \mathcal{F}\{x_{s}(t)\}$$
$$\mathcal{F\{x[k]\} = \mathcal{Z}\{x[k]\}}|_{z=e^{j\Omega}}$$
![fourier_cerlce_unitaire.svg](medias/fourier_cerlce_unitaire.svg)
$$x[k]=\frac{1}{2\pi} \int^{+\pi}_{-\pi}X(\Omega)e^{jk\Omega} d\Omega$$
La DFT est utilisée dans de nombreux domaines, notamment en traitement du signal, en télécommunications, en traitement d'images
et en compression de données.
> [!NOTE|label:FFT]
> La transformée de Fourier rapide (FFT) est un algorithme efficace pour calculer la DFT. Elle est largement utilisée dans
> de nombreuses applications, notamment en traitement du signal et en compression de données.
----------
# Réponse impulsionnelle
La réponse impulsionnelle $h[k]$ d'un système discret LIT est sa réponse à l'impulsion unitaire à l'origine $δ[k]$.
![Réponse impulsionelle - Continu/discret](medias/reponse_impulsionelle-continue_discret.png)
Avant l'impulsion, la réponse impulsionnelle $h$ est nulle.
Après l'impulsion, la réponse impulsionnelle $h$ est la réponse du système à l'impulsion unitaire à l'origine.
*Causal* = le futur n'a pas d'influence sur le passé.
$$ h[k] = \mathcal{H} \{ δ[k] \} $$
$\mathcal{H}$ : Heaviside (peut aussi se noter $\varepsilon$ pour un heaviside en discret) <br>
$y[k]$ : réponse du système <br>
$x[k]$ : signal d'entrée <br>
$h[k]$ : réponse impulsionnelle <br>
$$
\forall x \in \mathbb{R}, \mathcal{H}(x)=\begin{cases}
0 &\text{ si } x<0 \\
1 &\text{ si } x \geq 0
\end{cases}
$$
Pour un système causal
$$\begin{align}
y[k] &= \sum^{k}_{n=-\infty}(x[n]\cdot h[k-n]) \\
\\
y[k] &= \sum^{+\infty}_{n=0}(x[k-n]\cdot h[n])
\end{align}$$
La réponse impulsionnelle caractérise <u>ENTIÈREMENT</u> un système LIT.
## Système LIT stable
Un système LIT (Linéaire et Invariant dans le Temps) est stable si et seulement si sa réponse impulsionnelle est sommable.
> [!NOTE]
> $\sum^{+\infty}_{k=-\infty}|h[k]|<+\infty$
## Système LIT causal à réponse impulsionnelle finie
Un système LIT (Linéaire et Invariant dans le Temps) est causal à réponse impulsionnelle finie si et seulement si sa
réponse impulsionnelle est nulle pour $k<0$ et sommable pour $k\geq0$.
> [!NOTE]
> fini: $h(k) |_{k<\infty} = 0$
$$\begin{align}
y[k] &= \sum^{N}_{n=0}x[k-n]\cdot h[n] \\
&= b_{0}x[k]+b_{1}x[k-1]+\dots+b_{N}x[k-N]
\end{align}$$
$N$ : Limite de notre réponse impulsionnelle.
![schema_bloc_fir.png](medias/schema_bloc_fir.svg ':size=75%')
En faisant la somme, la réponse impulsionnelle se transforme en facteurs $(b_0, b_1, b_2, ..., b_n)$.
On peut retrouver la réponse impulsionnelle à partir de ces facteurs.
La somme décomposée est appelée *équation aux différences*.
Pour obtenir la réponse indicielle depuis la réponse impulsionnelle, on utilise la somme cumulée de la réponse impulsionnelle.
On peut aussi mathématiquement obtenir la réponse impulsionnelle en multipliant la réponse indicielle par Heaviside ($F(z)\cdot \varepsilon(z)$).
----------
# Transformée en Z
La transformée en Z, également connue sous le nom de transformée en fréquence Z, est une technique de traitement du
signal utilisée pour analyser et manipuler les signaux numériques.
Elle permet de décomposer un signal en fonction de la fréquence et de fournir des informations sur la nature du signal.
La principale différence entre la transformée en Z et la transformation de Laplace (ou S) est que la transformée en Z
est adaptée aux signaux discrets, c'est-à-dire aux valeurs qui ne varient pas continuellement dans le temps.
En revanche, la transformation de Laplace est utilisée pour les signaux continus et permet d'étudier leur réponse en
fréquence et en phase.
En résumé, la transformée en Z est conçue pour les signaux discrets et les systèmes numériques, tandis que la
transformation de Laplace s'applique aux signaux continus et aux systèmes analogiques.
[Tableau des transformées en Z](/infotro/Eln/Ztransform)
> [!NOTE|label:S]
> Domaine fréquentiel continue avec Laplace.
> [!NOTE|label:Z]
> Domaine fréquentiel discret.
Avec $z:=e^{sT_{s}}$ on a
$$\mathcal{Z}\{x[k]\} = X(z) \quad := \quad \sum^{+\infty}_{k=-\infty}(x[k]\cdot z^{-k})=\mathcal{L\{x_{s}(t)\}}$$
qui est la transformée en Z de $x[k]$.<br>
$T_{s}$ : Période d'échantillonnage. <br>
$x[k]$ : Fonction du [Tableau des transformées en Z](/infotro/Eln/Ztransform) ou valeur du graphe des réponses impulsionnelle<br>
On a aussi la propriété suivante :
$$\mathcal{Z}\{x[k-n]\}=z^{-n}\cdot X(z)$$
## Marche à suivre - Transformée en *Z*
1. Avoir une équation aux différences.
2. Pour chaque terme de l'équation, appliquer la transformée en Z.
Avec :
- $f[k] => F(z)$
- $f[k-n] => z^{-n}\cdot F(z)$
3. Isoler $Y(z)$.
4. $H(z) = \frac{Y(z)}{X(z)}$ est la fonction de transfert.
> [!NOTE|label:Example]
> L'équation aux différences est donnée par $$\tag{1} y[k] = \frac{1}{2}(x[k]+y[k-1])$$
> devient
> $$\begin{align}
\tag{2} Y(z) &= \frac{1}{2}(X(z)+z^{-1}\cdot Y(z)) \\
Y(z)-\frac{1}{2}\cdot z^{-1}\cdot Y(z) &= \frac{1}{2}\cdot X(z) \\
\tag{3} Y(z) &= \frac{1}{2-z^{-1}}X(z) \\
\tag{4} H(z) &= \frac{Y(z)}{X(z)} = \frac{1}{2-z^{-1}}
\end{align}$$
> [!TIP]
> $h[k]\stackrel{\text{Z tabel}}{\longleftrightarrow}H(z)$
>
> Exemple: $h[k] = 2^{-(k+1)}$ devient
> $$\begin{align}
2^{-(k+1)} &= 2^{-k}\cdot 2^{-1} = \frac{1}{2}\cdot 2^{-k} = \frac{1}{2}\cdot (2^{-1})^k \\
&\to \frac{1}{2}\cdot \frac{z}{z-\frac{1}{2}} =\frac{z}{2z-1} = \frac{1}{2-\frac{1}{z}} = \frac{1}{2-z^{-1}}
\end{align}$$
> [!WARNING]
> Les propriétés de la transformée en Z se trouvent dans le [Tableau des transformées en Z](/infotro/Eln/Ztransform)
L'équation aux différences
$$y[k] + a_{1}y[k-1]+\dots+a_{M}y[k-M] \quad = \quad b_{0}x[k]+b_{1}x[k-1]+\dots+b_{N}x[k-N]$$
devient par la transformation en z
$$H(z) = \frac{Y(z)}{X(z)} = \frac{b_{0}+b_{1}z^{-1}+\dots+b_{N}z^{-1}}{1+a_{1}z^{-1}+\dots+a_{M}z^{-M}} \quad a_{m}, b_{n} \space \text{réels}$$
Les zéros et les pôles de la fonction de transfert donnent donc :
$$H(z) = K \frac{(z-o_{1})\cdot (z-o_{2})\cdot \space \dots \space \cdot (z-o_{N})}{(z-p_{1})\cdot (z-p_{2})\cdot \space \dots \space \cdot (z-p_{M}) }\quad o_{n}, p_{n} \space \text{complexes}$$
## Marche à suivre - Transformée en *Z* inverse
1. Avoir une fonction de transfert : $H(z) = \frac{Y(z)}{X(z)}$.
2. Effectuer le numérateur et le dénominateur.
3. Mettre les $z$ en puissance négative : $\cdot \frac{z^{-1}}{z^{-1}}$
4. Effectuer le produit en croix.
5. Pour chaque terme, appliquer la transformée en Z inverse.
- $F(z) \implies f[k]$
- $F(z)\cdot z^{-n} \implies f[k-n]$
6. Isoler $y[k]$. Et on a notre équation aux différences.
----------
# Approximation bilinéaire
L'approximation bilinéaire est une technique utilisée pour convertir une fonction de transfert continue en une fonction
de transfert discrète.
Elle est utilisée dans le domaine du traitement du signal pour convertir un système continu en un système discret.
Développement en série de la fonction $ln$ autour de 1 ($x=0$) :
$$\ln\left( \frac{1+x}{1-x} \right) = 2\left(x + \frac{x^{3}}{3} + \frac{x^{5}}{5} + \dots \right)$$
donne l'approximation
$\ln\left( \frac{1+x}{1-x} \right) \approx 2x$
## Approximation bilinéaire - Pôles et zéros
$$p_{di} = e^{p_{ai} \cdot T} \qquad z_{di} = e^{z_{ai} \cdot T} $$
- $k_a$, $p_{ai}$ and $z_{ai}$ sont le gain, les pôles et les zéros de notre filtre analogique
Attention, $k_a$ n'est pas le gain DC
- $k_d$, $p_{di}$ and $z_{di}$ sont le gain, des pôles et les zéros de notre filtre numérique.
Pour un filtre passe bas sans zéros, le gain numérique est:
$$k_{d} = k_{a}\cdot \prod_{i} \frac{1-e^{P_{ai}\cdot T}}{P_{ai}}$$
On peut appliquer la transformation bilinéaire aux pôles et zéros du filtre analogique pour obtenir les pôles et zéros du filtre numérique.
$$
p_{di} = \frac{1+p_{ai}\cdot \frac{T}{2}}{1-p_{ai}\cdot \frac{T}{2}}
\qquad \qquad
z_{di} = \frac{1+z_{ai}\cdot \frac{T}{2}}{1-z_{ai}\cdot \frac{T}{2}}
$$
Pour un filtre passe bas, le gain "bilinear" est :
$$k_{d} = k_{a}\cdot \frac{1}{\prod_{i}\left( \frac{2}{T}-p_{ai} \right)}$$
Pour passer de $\omega_o'$ à $\omega_o$ et vice-versa, on utilise la relation suivante :
$$\begin{align}
\omega_{0} &= 2f_{s}\tan\left( \frac{\omega_{0}'}{2f_{s}} \right) \\
\\
\omega_{0}'&=2f_{S}\cdot \arctan\left( \frac{\omega_{0}}{2fs} \right)
\end{align}$$
## Autres méthodes d'approximation d'un système continu LIT par un syst. discret
On détermine la réponse impulsionnelle du système discret LIT à partir de celle du système continu en appliquant la relation :
$$h[k] = T_{s}\cdot(kT_{s})$$
$h(kT_s)$ : Réponse impulsionnelle en continu <br>
On peut du coup approximer l'équation différentielle par une équation aux différences.
Par exemple pour la dérivée première de $x(t)$, on a
$$x'[k]= \frac{x[k]-x[k-1]}{T_{s}}$$
----------
+30
View File
@@ -0,0 +1,30 @@
<header><h1>
Tableau des transformées en Z
</h1></header>
-----------
$$f[k] =\varepsilon[x]=\begin{cases}
1^{x} &\text{ si } x\geq 0 \\
0 &\text{ si } x<0
\end{cases} \qquad \forall x=k,k-n
$$
$\varepsilon$ : fonction de Heaviside en discret
----------
| Nr. | $f[k]$ | $F(z)$ | $f(t)$ | $F(s)$ |
|------|-------------------|----------------------------------------|-------------|--------------------|
| 1 | $\delta_{k}, 0^k$ | 1 | $\delta(t)$ | 1 |
| 2 | $1^k$ | $\frac{z}{z-1}$ | $1$ | $\frac{1}{s}$ |
| 3 | $kT$ | $\frac{Tz}{z-1}^{2}$ | $t$ | $\frac{1}{s^{2}}$ |
| 4 | $(kT)^{2}$ | $\frac{T^{2}z(z+1)}{(z-1)^{3}}$ | $t^{2}$ | $\frac{2!}{s^{3}}$ |
| 5 | $(kT)^{3}$ | $\frac{T^{3}z(z^{2}+4z+1)}{(z-1)^{4}}$ | $t^{3}$ | $\frac{3!}{s^{4}}$ |
| 6 | $e^{akT}$ | $\frac{z}{z-e^{aT}}$ | $e^{at}$ | $\frac{1}{s-a}$ |
![ztabelle01.png](ztabelle01.png)
![ztabelle02.png](ztabelle02.png)
![ztabelle03.png](ztabelle03.png)
Dans la théorie Eln la partie transformée en Z commence à la slide 23 de "Eln2_LTI_0910_f.pdf".
@@ -0,0 +1,133 @@
<header><h1>
Lab 06 - Filters
</h1></header>
----------
In this laboratory on digital filters, the main objective is to understand how digital filters work.
We will use MATLAB to simulate and implement these filters on a digital signal processor (DSP).
# Analog filter design
We must design Butterworth low-pass filter that cuts at 1kHz as shown with the following equation :
$$F(s) = \frac{1}{\frac{s^{2}}{\omega_c^{2}} + s\cdot\frac{1.414}{\omega_c} + 1} $$
In according to the sample theory, we have to choose at least 2 times the maximum frequency of the signal.
In this case, the maximum frequency is 1 kHz, so we have to choose a sample frequency at least at 2 kHz.
We take 10 time $f_c$ for the sample frequency, so we choose 20 kHz.
## Transfer function
For calculate the transfer function, we have to calculate the poles and zeros of the filter.
$$p_{di} = e^{p_{ai} \cdot T} \qquad z_{di} = e^{z_{ai} \cdot T} $$
- $k_a$, $p_{ai}$ and $z_{ai}$ are the gain, poles and zeros of the analog filter that the representation *zpk*.
Be careful, $k_a$ is not the DC gain.
- $k_d$, $p_{di}$ and $z_{di}$ are the gain, poles and zeros of the digital filter.
For a low pass filter without zeros, the "digital" gain is :
$$k_{d} = k_{a}\cdot \prod_{i} \frac{1-e^{P_{ai}\cdot T}}{P_{ai}}$$
We can apply the bilinear transformation to the poles and zeros of the analog filter to obtain the poles and zeros of the digital filter.
$$
p_{di} = \frac{1+p_{ai}\cdot \frac{T}{2}}{1-p_{ai}\cdot \frac{T}{2}}
\qquad \qquad
z_{di} = \frac{1+z_{ai}\cdot \frac{T}{2}}{1-z_{ai}\cdot \frac{T}{2}}
$$
And for the low pass filter, the "bilinear" gain is :
$$k_{d} = k_{a}\cdot \frac{1}{\prod_{i}\left( \frac{2}{T}-p_{ai} \right)}$$
On the following diagram, we can see the bode diagram of the analog, digital filter and the filter with the bilinear transformation.
![bode_design_filter.svg](bode_design_filter.svg)
# IIR filter design
We must design an IIR filter with the following specifications :
- $H_0 = 0 [dB]$
- $H_1 = -3 [dB]$
- $H_2 = -40 [dB]$
- $f_{pass} = 7 [kHz]$
- $f_{stop} = 9 [kHz]$
- $f_s = 48 [kHz]$
We first calculate the analog filter for search the order with the function *ellipord*.
Then, we calculate the poles and zeros with the function *ellip*.
We apply the bilinear transformation to the poles and zeros to obtain the poles and zeros of the digital filter.
Finally, we can plot the bode diagram of the digital filter and the filter with the bilinear transformation.
![iir_filter.svg](iir_filter.svg)
# DSP implementation
We will implement the previously designed digital filter using a C program that will run in real-time on a fixed-point DSP.
We will employ a cascade structure, consisting of 2 second-order stages.
We will use the TMS320C6211 DSK from Texas Instruments.
The following equation represent an universal transfer function of a second-order filter :
$$H(z) = \frac{b_{0}+b_{1}\cdot z^{-1}+b_{2}\cdot z^{-2}}{a_{0}+a_{1}\cdot z^{-1}+a_{2}\cdot z^{-2}}$$
The following figure depicts the block diagram of this implementation in the case where a0 = 1
This structure is commonly referred to as "Direct Form I." It requires the storage of past values of both the input and output.
![direct_form_1.png](direct_form_1.svg)
So we have the difference equation :
$$y(n) = b_{0}\cdot x(n)+b_{1}\cdot x(n-1)+b_{2}\cdot x(n-2)-a_{1}\cdot y(n-1)-a_{2}\cdot y(n-2)$$
It exists a second structure called "Direct Form II" which is more efficient in terms of computation and memory usage.
![direct_form_2.png](direct_form_2.svg)
The difference equation is :
$$\begin{align}
y(n) &= tmp\cdot b_{0}+w[0]\cdot b_{1}+w[1]\cdot b_{2} \\
\\
tmp &= x(n)-a_{1}\cdot w[0]-a_{2}\cdot w[1] \\
\\
w[0] &= tmp_{prev} \\
\\
w[1] &= w[0]_{prev}
\end{align}$$
The variables can be translated into code by shifting the coefficients 14 positions to the left to enhance precision
after the decimal point, as the DSP only works with integers.
Structures for the coefficients and the state variables are defined as follows :
```c
typedef struct {
short b0;
short b1;
short b2;
short a0;
short a1;
short a2;
} coeff_sec_order;
short w3[2];
short w4[2];
coeff_sec_order stage1 = { 469, 349, 469, 16384, -23699, 10692};
coeff_sec_order stage2 = {16384, -11187, 16384, 16384, -19657, 14890};
```
The following code is the implementation of the Direct Form II function :
```c
void iir_sec_order_dir_s(coeff_sec_order Coeff, short*w, short* Input, short* Output) {
register short tmp;
tmp = (long)* Input - (( (Long)Coeff.a1 * (long)w[0]) >> SHIFT) - (( (long)Coeff.a2 * (long)w[1]) >> SHIFT);
*Output = (( (long)Coeff.b0 * (long)tmp >> SHIFT) + (( (long)Coeff.b1 * (long)w[0]) >> SHIFT) + (( (long)Coeff.b2 * (long)w[1]) >> SHIFT);
w[1] = w[0];
w[0] = tmp;
}
```
For a better effect of the filter, the function can be called several times.
File diff suppressed because it is too large. Load diff

After

Width:  |  Height:  |  Size: 78 KiB

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Do not edit this file with editors other than draw.io -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="840px" height="286px" viewBox="-0.5 -0.5 840 286" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2024-02-03T22:03:26.442Z&quot; agent=&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0&quot; etag=&quot;Da41eLOf3AT8AQXfy7Nj&quot; version=&quot;22.1.22&quot;&gt;&lt;diagram name=&quot;Page-1&quot; id=&quot;CkdF-7UHB_tb0CRjxC_V&quot;&gt;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&lt;/diagram&gt;&lt;/mxfile&gt;"><defs><style type="text/css">@import url(https://fonts.googleapis.com/css2?family=Architects+Daughter:wght@400;500);&#xa;</style><style xmlns="http://www.w3.org/1999/xhtml" id="MJX-SVG-styles">&#xa;mjx-container[jax="SVG"] {&#xa; direction: ltr;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"] &gt; svg {&#xa; overflow: visible;&#xa; min-height: 1px;&#xa; min-width: 1px;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"] &gt; svg a {&#xa; fill: blue;&#xa; stroke: blue;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"][display="true"] {&#xa; display: block;&#xa; text-align: center;&#xa; margin: 1em 0;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"][display="true"][width="full"] {&#xa; display: flex;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"][justify="left"] {&#xa; text-align: left;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"][justify="right"] {&#xa; text-align: right;&#xa;}&#xa;&#xa;g[data-mml-node="merror"] &gt; g {&#xa; fill: red;&#xa; stroke: red;&#xa;}&#xa;&#xa;g[data-mml-node="merror"] &gt; rect[data-background] {&#xa; fill: yellow;&#xa; stroke: none;&#xa;}&#xa;&#xa;g[data-mml-node="mtable"] &gt; line[data-line], svg[data-table] &gt; g &gt; line[data-line] {&#xa; stroke-width: 70px;&#xa; fill: none;&#xa;}&#xa;&#xa;g[data-mml-node="mtable"] &gt; rect[data-frame], svg[data-table] &gt; g &gt; rect[data-frame] {&#xa; stroke-width: 70px;&#xa; fill: none;&#xa;}&#xa;&#xa;g[data-mml-node="mtable"] &gt; .mjx-dashed, svg[data-table] &gt; g &gt; .mjx-dashed {&#xa; stroke-dasharray: 140;&#xa;}&#xa;&#xa;g[data-mml-node="mtable"] &gt; .mjx-dotted, svg[data-table] &gt; g &gt; .mjx-dotted {&#xa; stroke-linecap: round;&#xa; stroke-dasharray: 0,140;&#xa;}&#xa;&#xa;g[data-mml-node="mtable"] &gt; g &gt; svg {&#xa; overflow: visible;&#xa;}&#xa;&#xa;[jax="SVG"] mjx-tool {&#xa; display: inline-block;&#xa; position: relative;&#xa; width: 0;&#xa; height: 0;&#xa;}&#xa;&#xa;[jax="SVG"] mjx-tool &gt; mjx-tip {&#xa; position: absolute;&#xa; top: 0;&#xa; left: 0;&#xa;}&#xa;&#xa;mjx-tool &gt; mjx-tip {&#xa; display: inline-block;&#xa; padding: .2em;&#xa; border: 1px solid #888;&#xa; font-size: 70%;&#xa; background-color: #F8F8F8;&#xa; color: black;&#xa; box-shadow: 2px 2px 5px #AAAAAA;&#xa;}&#xa;&#xa;g[data-mml-node="maction"][data-toggle] {&#xa; cursor: pLine truncated

After

Width:  |  Height:  |  Size: 82 KiB

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Do not edit this file with editors other than draw.io -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="520px" height="366px" viewBox="-0.5 -0.5 520 366" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2024-02-03T22:16:47.416Z&quot; agent=&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0&quot; etag=&quot;gL6vpL3MQfkpyT-y1ADy&quot; version=&quot;22.1.22&quot;&gt;&lt;diagram name=&quot;Page-1&quot; id=&quot;CkdF-7UHB_tb0CRjxC_V&quot;&gt;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&lt;/diagram&gt;&lt;/mxfile&gt;"><defs><style type="text/css">@import url(https://fonts.googleapis.com/css2?family=Architects+Daughter:wght@400;500);&#xa;</style><style xmlns="http://www.w3.org/1999/xhtml" id="MJX-SVG-styles">&#xa;mjx-container[jax="SVG"] {&#xa; direction: ltr;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"] &gt; svg {&#xa; overflow: visible;&#xa; min-height: 1px;&#xa; min-width: 1px;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"] &gt; svg a {&#xa; fill: blue;&#xa; stroke: blue;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"][display="true"] {&#xa; display: block;&#xa; text-align: center;&#xa; margin: 1em 0;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"][display="true"][width="full"] {&#xa; display: flex;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"][justify="left"] {&#xa; text-align: left;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"][justify="right"] {&#xa; text-align: right;&#xa;}&#xa;&#xa;g[data-mml-node="merror"] &gt; g {&#xa; fill: red;&#xa; stroke: red;&#xa;}&#xa;&#xa;g[data-mml-node="merror"] &gt; rect[data-background] {&#xa; fill: yellow;&#xa; stroke: none;&#xa;}&#xa;&#xa;g[data-mml-node="mtable"] &gt; line[data-line], svg[data-table] &gt; g &gt; line[data-line] {&#xa; stroke-width: 70px;&#xa; fill: none;&#xa;}&#xa;&#xa;g[data-mml-node="mtable"] &gt; rect[data-frame], svg[data-table] &gt; g &gt; rect[data-frame] {&#xa; stroke-width: 70px;&#xa; fill: none;&#xa;}&#xa;&#xa;g[data-mml-node="mtable"] &gt; .mjx-dashed, svg[data-table] &gt; g &gt; .mjx-dashed {&#xa; stroke-dasharray: 140;&#xa;}&#xa;&#xa;g[data-mml-node="mtable"] &gt; .mjx-dotted, svg[data-table] &gt; g &gt; .mjx-dotted {&#xa; stroke-linecap: round;&#xa; stroke-dasharray: 0,140;&#xa;}&#xa;&#xa;g[data-mml-node="mtable"] &gt; g &gt; svg {&#xa; overflow: visible;&#xa;}&#xa;&#xa;[jax="SVG"] mjx-tool {&#xa; display: inline-block;&#xa; position: relative;&#xa; width: 0;&#xa; height: 0;&#xa;}&#xa;&#xa;[jax="SVG"] mjx-tool &gt; mjx-tip {&#xa; position: absolute;&#xa; top: 0;&#xa; left: 0;&#xa;}&#xa;&#xa;mjx-tool &gt; mjx-tip {&#xa; display: inline-block;&#xa; padding: .2em;&#xa; border: 1px solid #888;&#xa; font-size: 70%;&#xa; background-color: #F8F8F8;&#xa; color: black;&#xa; box-shadow: 2px 2px 5px #AAAAAA;&#xa;}&#xa;&#xa;g[data-mml-node="maction"][data-toggle] {&#xa; cursor: pLine truncated

After

Width:  |  Height:  |  Size: 79 KiB

File diff suppressed because it is too large. Load diff

After

Width:  |  Height:  |  Size: 72 KiB

Whitespace-only changes.
Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Do not edit this file with editors other than draw.io -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="289px" height="262px" viewBox="-0.5 -0.5 289 262" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2024-02-02T17:56:41.416Z&quot; agent=&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0&quot; etag=&quot;i95P2xGId2tSeVqMwiar&quot; version=&quot;23.1.1&quot; scale=&quot;1&quot; border=&quot;0&quot;&gt;&#xA; &lt;diagram name=&quot;Page-1&quot; id=&quot;ASSYr2psEjrnozlwjsYB&quot;&gt;&#xA; &lt;mxGraphModel dx=&quot;1050&quot; dy=&quot;626&quot; grid=&quot;1&quot; gridSize=&quot;10&quot; guides=&quot;1&quot; tooltips=&quot;1&quot; connect=&quot;1&quot; arrows=&quot;1&quot; fold=&quot;1&quot; page=&quot;1&quot; pageScale=&quot;1&quot; pageWidth=&quot;827&quot; pageHeight=&quot;1169&quot; math=&quot;1&quot; shadow=&quot;0&quot;&gt;&#xA; &lt;root&gt;&#xA; &lt;mxCell id=&quot;0&quot; /&gt;&#xA; &lt;mxCell id=&quot;1&quot; parent=&quot;0&quot; /&gt;&#xA; &lt;mxCell id=&quot;hvOSe9N11qCTzQnM01ZF-1&quot; value=&quot;&quot; style=&quot;ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=none;strokeColor=#808080;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#xA; &lt;mxGeometry x=&quot;360&quot; y=&quot;280&quot; width=&quot;160&quot; height=&quot;160&quot; as=&quot;geometry&quot; /&gt;&#xA; &lt;/mxCell&gt;&#xA; &lt;mxCell id=&quot;hvOSe9N11qCTzQnM01ZF-2&quot; value=&quot;&quot; style=&quot;endArrow=classic;html=1;rounded=0;strokeColor=#808080;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&#xA; &lt;mxGeometry width=&quot;50&quot; height=&quot;50&quot; relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#xA; &lt;mxPoint x=&quot;439.5&quot; y=&quot;480&quot; as=&quot;sourcePoint&quot; /&gt;&#xA; &lt;mxPoint x=&quot;440&quot; y=&quot;240&quot; as=&quot;targetPoint&quot; /&gt;&#xA; &lt;/mxGeometry&gt;&#xA; &lt;/mxCell&gt;&#xA; &lt;mxCell id=&quot;hvOSe9N11qCTzQnM01ZF-3&quot; value=&quot;&quot; style=&quot;endArrow=classic;html=1;rounded=0;strokeColor=#808080;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&#xA; &lt;mxGeometry width=&quot;50&quot; height=&quot;50&quot; relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#xA; &lt;mxPoint x=&quot;320&quot; y=&quot;360&quot; as=&quot;sourcePoint&quot; /&gt;&#xA; &lt;mxPoint x=&quot;560&quot; y=&quot;360&quot; as=&quot;targetPoint&quot; /&gt;&#xA; &lt;/mxGeometry&gt;&#xA; &lt;/mxCell&gt;&#xA; &lt;mxCell id=&quot;hvOSe9N11qCTzQnM01ZF-4&quot; value=&quot;1&quot; style=&quot;text;strokeColor=none;fillColor=none;html=1;fontSize=20;fontStyle=1;verticalAlign=bottom;align=left;fontColor=#808080;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#xA; &lt;mxGeometry x=&quot;520&quot; y=&quot;320&quot; width=&quot;40&quot; height=&quot;40&quot; as=&quot;geometry&quot; /&gt;&#xA; &lt;/mxCell&gt;&#xA; &lt;mxCell id=&quot;hvOSe9N11qCTzQnM01ZF-5&quot; value=&quot;1&quot; style=&quot;text;strokeColor=none;fillColor=none;html=1;fontSize=20;fontStyle=1;verticalAlign=bottom;align=left;fontColor=#808080;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#xA; &lt;mxGeometry x=&quot;440&quot; y=&quot;240&quot; width=&quot;40&quot; height=&quot;40&quot; as=&quot;geometry&quot; /&gt;&#xA; &lt;/mxCell&gt;&#xA; &lt;mxCell id=&quot;hvOSe9N11qCTzQnM01ZF-7&quot; value=&quot;&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Z&amp;lt;/div&amp;gt;&quot; style=&quot;text;strokeColor=#808080;fillColor=none;html=1;fontSize=20;fontStyle=1;verticalAlign=middle;align=center;fontColor=#808080;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#xA; &lt;mxGeometry x=&quot;530&quot; y=&quot;240&quot; width=&quot;30&quot; height=&quot;40&quot; as=&quot;geometry&quot; /&gt;&#xA; &lt;/mxCell&gt;&#xA; &lt;mxCell id=&quot;hvOSe9N11qCTzQnM01ZF-8&quot; value=&quot;$$\mathcal{Z} = e^{\mathbf{j}\omega}$$&quot; style=&quot;text;strokeColor=none;fillColor=none;fontSize=20;fontStyle=1;verticalAlign=middle;align=center;fontColor=#808080;html=1;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#xA; &lt;mxGeometry x=&quot;280&quot; y=&quot;240&quot; width=&quot;120&quot; height=&quot;40&quot; as=&quot;geometry&quot; /&gt;&#xA; &lt;/mxCell&gt;&#xA; &lt;/root&gt;&#xA; &lt;/mxGraphModel&gt;&#xA; &lt;/diagram&gt;&#xA;&lt;/mxfile&gt;&#xA;"><defs><style xmlns="http://www.w3.org/1999/xhtml" id="MJX-SVG-styles">&#xa;mjx-container[jax="SVG"] {&#xa; direction: ltr;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"] &gt; svg {&#xa; overflow: visible;&#xa; min-height: 1px;&#xa; min-width: 1px;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"] &gt; svg a {&#xa; fill: blue;&#xa; stroke: blue;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"][display="true"] {&#xa; display: block;&#xa; text-align: center;&#xa; margin: 1em 0;&#xa;}&#xa;&#xa;Line truncated

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

@@ -0,0 +1,606 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
style=""
version="1.1"
width="1220px"
height="361px"
viewBox="-0.5 -0.5 1220 361"
content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2024-02-02T14:30:19.773Z&quot; agent=&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0&quot; etag=&quot;Fj5xVG2gLKINAW_kfnPL&quot; version=&quot;23.0.0&quot; scale=&quot;1&quot; border=&quot;0&quot;&gt;&#10; &lt;diagram name=&quot;Page-1&quot; id=&quot;iDfkVSNYGaoFjq7dBcwx&quot;&gt;&#10; &lt;mxGraphModel dx=&quot;1500&quot; dy=&quot;894&quot; grid=&quot;1&quot; gridSize=&quot;10&quot; guides=&quot;1&quot; tooltips=&quot;1&quot; connect=&quot;1&quot; arrows=&quot;1&quot; fold=&quot;1&quot; page=&quot;1&quot; pageScale=&quot;1&quot; pageWidth=&quot;827&quot; pageHeight=&quot;1169&quot; math=&quot;0&quot; shadow=&quot;0&quot;&gt;&#10; &lt;root&gt;&#10; &lt;mxCell id=&quot;0&quot; /&gt;&#10; &lt;mxCell id=&quot;1&quot; parent=&quot;0&quot; /&gt;&#10; &lt;mxCell id=&quot;Gu1WY5u3SJpsm1HDf4T9-3&quot; value=&quot;&quot; style=&quot;endArrow=classic;html=1;rounded=0;strokeColor=#808080;strokeWidth=4;entryX=0;entryY=0.5;entryDx=0;entryDy=0;&quot; edge=&quot;1&quot; parent=&quot;1&quot; target=&quot;Gu1WY5u3SJpsm1HDf4T9-4&quot;&gt;&#10; &lt;mxGeometry width=&quot;50&quot; height=&quot;50&quot; relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#10; &lt;mxPoint x=&quot;80&quot; y=&quot;320&quot; as=&quot;sourcePoint&quot; /&gt;&#10; &lt;mxPoint x=&quot;180&quot; y=&quot;160&quot; as=&quot;targetPoint&quot; /&gt;&#10; &lt;/mxGeometry&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;Gu1WY5u3SJpsm1HDf4T9-4&quot; value=&quot;D&quot; style=&quot;text;strokeColor=#808080;fillColor=#DDE1CC;html=1;fontSize=24;fontStyle=1;verticalAlign=middle;align=center;rounded=0;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;280&quot; y=&quot;280&quot; width=&quot;120&quot; height=&quot;80&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;Gu1WY5u3SJpsm1HDf4T9-5&quot; value=&quot;b&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;&quot; style=&quot;text;strokeColor=#808080;fillColor=#DDE1CC;html=1;fontSize=24;fontStyle=1;verticalAlign=middle;align=center;rounded=1;arcSize=50;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;440&quot; y=&quot;400&quot; width=&quot;80&quot; height=&quot;80&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;Gu1WY5u3SJpsm1HDf4T9-6&quot; value=&quot;b&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt;&quot; style=&quot;text;strokeColor=#808080;fillColor=#DDE1CC;html=1;fontSize=24;fontStyle=1;verticalAlign=middle;align=center;rounded=1;arcSize=50;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;160&quot; y=&quot;400&quot; width=&quot;80&quot; height=&quot;80&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;Gu1WY5u3SJpsm1HDf4T9-7&quot; value=&quot;b&amp;lt;sub&amp;gt;n&amp;lt;/sub&amp;gt;&quot; style=&quot;text;strokeColor=#808080;fillColor=#DDE1CC;html=1;fontSize=24;fontStyle=1;verticalAlign=middle;align=center;rounded=1;arcSize=50;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;1000&quot; y=&quot;400&quot; width=&quot;80&quot; height=&quot;80&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;Gu1WY5u3SJpsm1HDf4T9-9&quot; value=&quot;D&quot; style=&quot;text;strokeColor=#808080;fillColor=#DDE1CC;html=1;fontSize=24;fontStyle=1;verticalAlign=middle;align=center;rounded=0;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;560&quot; y=&quot;280&quot; width=&quot;120&quot; height=&quot;80&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;Gu1WY5u3SJpsm1HDf4T9-10&quot; value=&quot;D&quot; style=&quot;text;strokeColor=#808080;fillColor=#DDE1CC;html=1;fontSize=24;fontStyle=1;verticalAlign=middle;align=center;rounded=0;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;840&quot; y=&quot;280&quot; width=&quot;120&quot; height=&quot;80&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;Gu1WY5u3SJpsm1HDf4T9-11&quot; value=&quot;&amp;lt;font style=&amp;quot;font-size: 80px;&amp;quot;&amp;gt;∑&amp;lt;/font&amp;gt;&quot; style=&quot;text;strokeColor=#808080;fillColor=#DDE1CC;html=1;fontSize=24;fontStyle=1;verticalAlign=middle;align=center;rounded=1;arcSize=50;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;80&quot; y=&quot;520&quot; width=&quot;1080&quot; height=&quot;120&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;Gu1WY5u3SJpsm1HDf4T9-12&quot; value=&quot;&quot; style=&quot;endArrow=classic;html=1;rounded=0;strokeColor=#808080;strokeWidth=4;entryX=0;entryYLine truncated
id="svg29"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<defs
id="defs1" />
<g
id="layer1">
<path
d="M 9,40 H 197.03"
fill="none"
stroke="#808080"
stroke-width="4"
stroke-miterlimit="10"
pointer-events="stroke"
id="path1"
style="background-color:rgb(255, 255, 255)" />
<path
d="m 204.53,40 -10,5 2.5,-5 -2.5,-5 z"
fill="#808080"
stroke="#808080"
stroke-width="4"
stroke-miterlimit="10"
pointer-events="all"
id="path2"
style="background-color:rgb(255, 255, 255)" />
<rect
x="209"
y="0"
width="120"
height="80"
fill="#dde1cc"
stroke="#808080"
pointer-events="all"
id="rect2"
style="background-color:rgb(255, 255, 255)" />
<g
id="g2"
style="background-color:rgb(255, 255, 255)"
transform="translate(-0.5,-0.5)">
<switch
id="switch2">
<foreignObject
style="overflow: visible; text-align: left;"
pointer-events="none"
width="100%"
height="100%"
requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<xhtml:div
style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 40px; margin-left: 269px;">
<xhtml:div
style="box-sizing: border-box; font-size: 0px; text-align: center;"
data-drawio-colors="color: rgb(0, 0, 0); ">
<xhtml:div
style="display: inline-block; font-size: 24px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap;">D</xhtml:div>
</xhtml:div>
</xhtml:div>
</foreignObject>
<image
x="260.5"
y="26"
width="17"
height="47"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEQAAAC8CAYAAADB0gjGAAAEXklEQVR4Xu2cvY8NURiH7V9AQYlGaIREgQSdwkerQJQKJHQSOh2NFoVIKFDofJcSEhQiaIiKkkT8Bbxv3Nncmdk7c3/nzLV73vvc5M1udu/M3nnu7znvmblndmEVjxqBBXjUCQCkkQiAAKR7kCAhJISESI0UZVAGZVBGIoAyKCMFhi6DMiiDMhIBlEEZKTB0GZRBmUGV+SPtbbgnv7Fd/Rrt7r19/W31weqj1ffh/kx7T31jyHIB6Trmn/bLB1YPrZ4MDadEIOMMHM5Vq+ujFGXzKR1IBaACcyWXSBQgFQcfe05afUoFEw1IxeG4fXMvBUpUIM7C9bmoQokMxFncsDqtQIkOxFlI+swDEIdyeNo5y7wA8ba8aZq5yrwA8ZQ8szrYN57MCkjffpuva739YMPoh6vt6zar7Vb7rdb2HYTw+151+l546rlM336FY1i1x558wuqUstGE536xn2/p2k/fC18JQKrXv9W+uWm1KxOMz00mTvFLAlJx8BO5nLR0pqREIA4mF8rEuUmpQBzKU6sDifpM7DglA/HO9C6jC3lXa119KxmIh+OC1eXElJwZqVfbvHQgPmf5mpiSJbUpHUjuALvGduAXsBcfEYD4xO1lojZ7bbtX0YD48fxI1KY1SYuQkJwWfN82PhYxIandxi9K744IxN/lu4njSM2SKMrkDKy1ThMFiM9avyUmpNZpogBxFqmXKgDSSFLtKhoJ+fdh1uIFI4AApDX0kpAGEoAApHuCQkJICAmRJvEhJ2acyzQykHO2G/Jc5pABeiyJMuG6cpSpe+oVM19Isy7iFTNfgnk0ISGtz2aiJOSzwdicAKS1dDMCkJwO01pRFAGIr0O9lpAO36T1gXcEIK/twFJWFbU+gnBCpQPxZVZ+U1HKY8ml36UDyVlJFG59SM7sdEldSlbG14W8TWy1ftyh1pg5jOeJA6nDaM1OS56puia3MpLhxx5inaqfvJ21Sl11WIXA16jutKqtGlrJCRlf877PXuhGqyNWQ6137713ZlZtN2VeMOttlvVuiFkfnLp/H0h3WPXeFT4vCem9LaQiPA9AOrtKM2rRgXBX5tg7LsOY5dRdHfSGfL4PoOesuLPbIHhrPW819/f++wz0Umoq/sdMdUgFuvblibgzBIiS265fy7ht9WiaiZb6zqzUtjv+P4he2EFV/4OodueCerDTPL8PyDT7CPUcgDTeToAApNtwEkJCSIjUBVEGZVAGZSQCKIMyUmDoMiiDMigjEUAZlJECQ5dBGZRBGYkAyqCMFBi6DMqgDMpIBFAGZaTA0GVQBmVQRiKAMigjBYYugzIogzISAZRBGSkwdBmUQRmUkQigDMpIgaHLoAzKoIxEAGVQRgoMXQZlUAZlJAIogzJSYOgyKIMyKCMRQBmUkQJDl0EZlEEZiQDKoIwUGLoMyqAMykgEUAZlpMDQZVAGZVBGIoAyKCMFhi6DMiiDMhIBlEEZKTB0GZRBGZSRCKBMN66/z87NveQ2FjYAAAAASUVORK5CYII="
id="image2" />
</switch>
</g>
<rect
x="369"
y="120"
width="80"
height="80"
rx="40"
ry="40"
fill="#dde1cc"
stroke="#808080"
pointer-events="all"
id="rect3"
style="background-color:rgb(255, 255, 255)" />
<g
id="g3"
style="background-color:rgb(255, 255, 255)"
transform="translate(-0.5,-0.5)">
<switch
id="switch3">
<foreignObject
style="overflow: visible; text-align: left;"
pointer-events="none"
width="100%"
height="100%"
requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<xhtml:div
style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 160px; margin-left: 409px;">
<xhtml:div
style="box-sizing: border-box; font-size: 0px; text-align: center;"
data-drawio-colors="color: rgb(0, 0, 0); ">
<xhtml:div
style="display: inline-block; font-size: 24px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap;">b<xhtml:sub>1</xhtml:sub>
</xhtml:div>
</xhtml:div>
</xhtml:div>
</foreignObject>
<image
x="396"
y="144.5"
width="26"
height="38"
xlink:href="data:image/png;base64,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"
id="image3" />
</switch>
</g>
<rect
x="89"
y="120"
width="80"
height="80"
rx="40"
ry="40"
fill="#dde1cc"
stroke="#808080"
pointer-events="all"
id="rect4"
style="background-color:rgb(255, 255, 255)" />
<g
id="g4"
style="background-color:rgb(255, 255, 255)"
transform="translate(-0.5,-0.5)">
<switch
id="switch4">
<foreignObject
style="overflow: visible; text-align: left;"
pointer-events="none"
width="100%"
height="100%"
requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<xhtml:div
style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 160px; margin-left: 129px;">
<xhtml:div
style="box-sizing: border-box; font-size: 0px; text-align: center;"
data-drawio-colors="color: rgb(0, 0, 0); ">
<xhtml:div
style="display: inline-block; font-size: 24px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap;">b<xhtml:sub>0</xhtml:sub>
</xhtml:div>
</xhtml:div>
</xhtml:div>
</foreignObject>
<image
x="116"
y="144.5"
width="26"
height="38"
xlink:href="data:image/png;base64,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"
id="image4" />
</switch>
</g>
<rect
x="929"
y="120"
width="80"
height="80"
rx="40"
ry="40"
fill="#dde1cc"
stroke="#808080"
pointer-events="all"
id="rect5"
style="background-color:rgb(255, 255, 255)" />
<g
id="g5"
style="background-color:rgb(255, 255, 255)"
transform="translate(-0.5,-0.5)">
<switch
id="switch5">
<foreignObject
style="overflow: visible; text-align: left;"
pointer-events="none"
width="100%"
height="100%"
requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<xhtml:div
style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 160px; margin-left: 969px;">
<xhtml:div
style="box-sizing: border-box; font-size: 0px; text-align: center;"
data-drawio-colors="color: rgb(0, 0, 0); ">
<xhtml:div
style="display: inline-block; font-size: 24px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap;">b<xhtml:sub>n</xhtml:sub>
</xhtml:div>
</xhtml:div>
</xhtml:div>
</foreignObject>
<image
x="955.5"
y="144.5"
width="27"
height="38"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGwAAACYCAYAAAABZQzCAAAG40lEQVR4Xu2cO6smRRCGd3+BCqaKgmiisIGo4CX1GhgYrIIKBosKYiSokYGgiZmogQgauIKYLV4iEXdBE1nQRBEDTQ30F2i9cAY+hvlmuqqrqrvnvAMdnPN1V3W/z1TfpmfOnuE1lAJnh6otK3uGwAa7CQiMwAZTYLDqMsIIbDAFBqsuI4zABlNgsOoywghsMAUGqy4jjMAGU2Cw6jLCCGwwBQarLiOMwAZTYLDqMsIIbDAFBqsuI4zABlNgsOoywghsMAUGq+5ahH0lbXlI2Z6vJf/DyjLMrlCAwBRi9ZCVwHqgoKgDgSnE6iErgfVAQVEHAlOI1UNWAuuBgqIOBKYQq4esBNYDBUUdCEwhVg9ZCawHCoo6EJhCrB6yElgPFBR1IDCFWD1kJbAeKCjqQGAKsXrISmA9UFDUgcAUYvWQtQWwG6Thj0l6QNLNku6eCfGb/P2HpKuSvpd0RdK/PYjVQx0ygT0pDX55AVCJDp9JpndP4JXk322eDGD3inofSbrVQUWcGXnzNIOLBHaNCPu2pOcdQM1NwO5rAXa7NxkFDLC+MXZ/paL9KBkfPG3jWwSwV0TEL5y6wC14pw6aNzAIeF0SrAnmqYLmDWwrIqJ+B7R7ooz3ZHcvwKApJiGYjOz62hMwgLpD0i97JpYJDGuoTyT9PBP1dvn7fknPOswqd3+2PwMYtpqek4Qtpq3rEcnwsaTrtzKu/H5foa8KF+2KRgPDlhIWzpq9wNo1HHxiG2yXVySwmplbLbQbhdZfeyQWBexvEesWZWTN9cXYhvHOcr0ohd63FOy9TBSwp6ThFx0aD9Ete5G77RYjgGGScZsDLJjAs7M/jbbW2mY02b5YBDDvnXRE6nmDVLtck0UA8xbqBYH1ngGYV7dscB1XJAKYd1dknXzscqvKG1jUTsN/hnt2lxMPb2AfiLDowryvH8Tg/LDOlo+om2fLb+jv3sCiuiF+M+TkNtgzMDTRezwNjZ4S46MAe1Ua81ZJg2Z5CGxDtKgukcAG6xIJjMAMHex6ETxhwKFZPIw9J+lOSYfP9aYj6N/J/y9JMj0ZH2UMs2wCl07rS6P3mD3sd74u6YkZoK07AgDfkKTaJPcGVirSVmPmv0dO62uAlZZda6/mifzqtDdSpNGB1T5gXWp/0d6nd4RFrX0sW1OlM9bSKJl6D+xtfqvs/kpv0Ecl45drmSOAee/WW5+JlT511gC7IGL+FAQLnDaf1EcAKxWq9K7DgZpPSzMf5Cs9PVUKDGdUcGn3NLVVX92PjQBWc/hmqXGWGaKmay4FphW+Jv+1UnjxpFkEMFS09O7eahQG93+2Mi38rrlpaoGhG/tQEtZW09nLaU32kvxP+6FrNOfoBCQKmNf03ipm6YQD4lh9oCyOQyCtnbu0dOlHn+VFAUNjascyTDasA7xm4mMFVjQNP4l+7TGHoweZIoHVdI016xxNd2iNMMtBo1/FmeY970U20cDQv+Mlh9W1xWwMqoG12v8fGQu1EYa7/66NbnDJldZPE2BTxUv6euSt/eLA5jpmQUmtkJbomtp2+chNs/TvxYlbdIQdVuRwNoWd6mmgxliFyj0jyTKjOvShGVemclpg1hmw9vRXc2CKm8uUVTt2WYHVPMXWbLGFA0ME1bzXZaJ0UEgzMzz0pYmw2mPoXQHD2udxSdFbN0tgLV2hJcJq15fdAcM20u/JkWadBBCYKADxIh8/zKPL4+CqpkvcXYRNn13AzO/z4O6xdifFEmGaLa+lrru7LvHwOxlYAOPuRfK8MLnBWFnyknuJX02E7RrYJBYWw+84RVvpArwEFCPsZAw7JhY+6fC0pPMaNSXvtODGO2IRL5rvJsKUuhZnR1cJePiE7E2S5rsbh+f3pk/IFhs3ZCQwg2gtixBYS/UNvgnMIFrLIgTWUn2DbwIziNayCIG1VN/gm8AMorUsQmAt1Tf4JjCDaC2LEFhL9Q2+CcwgWssiBNZSfYNvAjOI1rIIgbVU3+CbwAyitSxCYC3VN/gmMINoLYsQWEv1Db4JzCAaixQqUHOwv9AFs3kqQGCeaibYIrAEkT1dEJinmgm2CCxBZE8XBOapZoItAksQ2dMFgXmqmWCLwBJE9nRBYJ5qJtgisASRPV0QmKeaCbYILEFkTxcE5qlmgi0CSxDZ0wWBeaqZYIvAEkT2dEFgnmom2CKwBJE9XRCYp5oJtggsQWRPFwTmqWaCLQJLENnTBYF5qplgi8ASRPZ0QWCeaibYIrAEkT1dEJinmgm2CCxBZE8XBOapZoItAksQ2dMFgXmqmWCLwBJE9nRBYJ5qJtgisASRPV0QmKeaCbYILEFkTxcE5qlmgi0CSxDZ0wWBeaqZYOt/n+2nqD3bxkAAAAAASUVORK5CYII="
id="image5" />
</switch>
</g>
<rect
x="489"
y="0"
width="120"
height="80"
fill="#dde1cc"
stroke="#808080"
pointer-events="all"
id="rect6"
style="background-color:rgb(255, 255, 255)" />
<g
id="g6"
style="background-color:rgb(255, 255, 255)"
transform="translate(-0.5,-0.5)">
<switch
id="switch6">
<foreignObject
style="overflow: visible; text-align: left;"
pointer-events="none"
width="100%"
height="100%"
requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<xhtml:div
style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 40px; margin-left: 549px;">
<xhtml:div
style="box-sizing: border-box; font-size: 0px; text-align: center;"
data-drawio-colors="color: rgb(0, 0, 0); ">
<xhtml:div
style="display: inline-block; font-size: 24px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap;">D</xhtml:div>
</xhtml:div>
</xhtml:div>
</foreignObject>
<image
x="540.5"
y="23"
width="17"
height="47"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEQAAAC8CAYAAADB0gjGAAAEXklEQVR4Xu2cvY8NURiH7V9AQYlGaIREgQSdwkerQJQKJHQSOh2NFoVIKFDofJcSEhQiaIiKkkT8Bbxv3Nncmdk7c3/nzLV73vvc5M1udu/M3nnu7znvmblndmEVjxqBBXjUCQCkkQiAAKR7kCAhJISESI0UZVAGZVBGIoAyKCMFhi6DMiiDMhIBlEEZKTB0GZRBmUGV+SPtbbgnv7Fd/Rrt7r19/W31weqj1ffh/kx7T31jyHIB6Trmn/bLB1YPrZ4MDadEIOMMHM5Vq+ujFGXzKR1IBaACcyWXSBQgFQcfe05afUoFEw1IxeG4fXMvBUpUIM7C9bmoQokMxFncsDqtQIkOxFlI+swDEIdyeNo5y7wA8ba8aZq5yrwA8ZQ8szrYN57MCkjffpuva739YMPoh6vt6zar7Vb7rdb2HYTw+151+l546rlM336FY1i1x558wuqUstGE536xn2/p2k/fC18JQKrXv9W+uWm1KxOMz00mTvFLAlJx8BO5nLR0pqREIA4mF8rEuUmpQBzKU6sDifpM7DglA/HO9C6jC3lXa119KxmIh+OC1eXElJwZqVfbvHQgPmf5mpiSJbUpHUjuALvGduAXsBcfEYD4xO1lojZ7bbtX0YD48fxI1KY1SYuQkJwWfN82PhYxIandxi9K744IxN/lu4njSM2SKMrkDKy1ThMFiM9avyUmpNZpogBxFqmXKgDSSFLtKhoJ+fdh1uIFI4AApDX0kpAGEoAApHuCQkJICAmRJvEhJ2acyzQykHO2G/Jc5pABeiyJMuG6cpSpe+oVM19Isy7iFTNfgnk0ISGtz2aiJOSzwdicAKS1dDMCkJwO01pRFAGIr0O9lpAO36T1gXcEIK/twFJWFbU+gnBCpQPxZVZ+U1HKY8ml36UDyVlJFG59SM7sdEldSlbG14W8TWy1ftyh1pg5jOeJA6nDaM1OS56puia3MpLhxx5inaqfvJ21Sl11WIXA16jutKqtGlrJCRlf877PXuhGqyNWQ6137713ZlZtN2VeMOttlvVuiFkfnLp/H0h3WPXeFT4vCem9LaQiPA9AOrtKM2rRgXBX5tg7LsOY5dRdHfSGfL4PoOesuLPbIHhrPW819/f++wz0Umoq/sdMdUgFuvblibgzBIiS265fy7ht9WiaiZb6zqzUtjv+P4he2EFV/4OodueCerDTPL8PyDT7CPUcgDTeToAApNtwEkJCSIjUBVEGZVAGZSQCKIMyUmDoMiiDMigjEUAZlJECQ5dBGZRBGYkAyqCMFBi6DMqgDMpIBFAGZaTA0GVQBmVQRiKAMigjBYYugzIogzISAZRBGSkwdBmUQRmUkQigDMpIgaHLoAzKoIxEAGVQRgoMXQZlUAZlJAIogzJSYOgyKIMyKCMRQBmUkQJDl0EZlEEZiQDKoIwUGLoMyqAMykgEUAZlpMDQZVAGZVBGIoAyKCMFhi6DMiiDMhIBlEEZKTB0GZRBGZSRCKBMN66/z87NveQ2FjYAAAAASUVORK5CYII="
id="image6" />
</switch>
</g>
<rect
x="769"
y="0"
width="120"
height="80"
fill="#dde1cc"
stroke="#808080"
pointer-events="all"
id="rect7"
style="background-color:rgb(255, 255, 255)" />
<g
id="g7"
style="background-color:rgb(255, 255, 255)"
transform="translate(-0.5,-0.5)">
<switch
id="switch7">
<foreignObject
style="overflow: visible; text-align: left;"
pointer-events="none"
width="100%"
height="100%"
requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<xhtml:div
style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 40px; margin-left: 829px;">
<xhtml:div
style="box-sizing: border-box; font-size: 0px; text-align: center;"
data-drawio-colors="color: rgb(0, 0, 0); ">
<xhtml:div
style="display: inline-block; font-size: 24px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap;">D</xhtml:div>
</xhtml:div>
</xhtml:div>
</foreignObject>
<image
x="820.5"
y="20"
width="17"
height="47"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEQAAAC8CAYAAADB0gjGAAAEXklEQVR4Xu2cvY8NURiH7V9AQYlGaIREgQSdwkerQJQKJHQSOh2NFoVIKFDofJcSEhQiaIiKkkT8Bbxv3Nncmdk7c3/nzLV73vvc5M1udu/M3nnu7znvmblndmEVjxqBBXjUCQCkkQiAAKR7kCAhJISESI0UZVAGZVBGIoAyKCMFhi6DMiiDMhIBlEEZKTB0GZRBmUGV+SPtbbgnv7Fd/Rrt7r19/W31weqj1ffh/kx7T31jyHIB6Trmn/bLB1YPrZ4MDadEIOMMHM5Vq+ujFGXzKR1IBaACcyWXSBQgFQcfe05afUoFEw1IxeG4fXMvBUpUIM7C9bmoQokMxFncsDqtQIkOxFlI+swDEIdyeNo5y7wA8ba8aZq5yrwA8ZQ8szrYN57MCkjffpuva739YMPoh6vt6zar7Vb7rdb2HYTw+151+l546rlM336FY1i1x558wuqUstGE536xn2/p2k/fC18JQKrXv9W+uWm1KxOMz00mTvFLAlJx8BO5nLR0pqREIA4mF8rEuUmpQBzKU6sDifpM7DglA/HO9C6jC3lXa119KxmIh+OC1eXElJwZqVfbvHQgPmf5mpiSJbUpHUjuALvGduAXsBcfEYD4xO1lojZ7bbtX0YD48fxI1KY1SYuQkJwWfN82PhYxIandxi9K744IxN/lu4njSM2SKMrkDKy1ThMFiM9avyUmpNZpogBxFqmXKgDSSFLtKhoJ+fdh1uIFI4AApDX0kpAGEoAApHuCQkJICAmRJvEhJ2acyzQykHO2G/Jc5pABeiyJMuG6cpSpe+oVM19Isy7iFTNfgnk0ISGtz2aiJOSzwdicAKS1dDMCkJwO01pRFAGIr0O9lpAO36T1gXcEIK/twFJWFbU+gnBCpQPxZVZ+U1HKY8ml36UDyVlJFG59SM7sdEldSlbG14W8TWy1ftyh1pg5jOeJA6nDaM1OS56puia3MpLhxx5inaqfvJ21Sl11WIXA16jutKqtGlrJCRlf877PXuhGqyNWQ6137713ZlZtN2VeMOttlvVuiFkfnLp/H0h3WPXeFT4vCem9LaQiPA9AOrtKM2rRgXBX5tg7LsOY5dRdHfSGfL4PoOesuLPbIHhrPW819/f++wz0Umoq/sdMdUgFuvblibgzBIiS265fy7ht9WiaiZb6zqzUtjv+P4he2EFV/4OodueCerDTPL8PyDT7CPUcgDTeToAApNtwEkJCSIjUBVEGZVAGZSQCKIMyUmDoMiiDMigjEUAZlJECQ5dBGZRBGYkAyqCMFBi6DMqgDMpIBFAGZaTA0GVQBmVQRiKAMigjBYYugzIogzISAZRBGSkwdBmUQRmUkQigDMpIgaHLoAzKoIxEAGVQRgoMXQZlUAZlJAIogzJSYOgyKIMyKCMRQBmUkQJDl0EZlEEZiQDKoIwUGLoMyqAMykgEUAZlpMDQZVAGZVBGIoAyKCMFhi6DMiiDMhIBlEEZKTB0GZRBGZSRCKBMN66/z87NveQ2FjYAAAAASUVORK5CYII="
id="image7" />
</switch>
</g>
<rect
x="9"
y="240"
width="1080"
height="120"
rx="60"
ry="60"
fill="#dde1cc"
stroke="#808080"
pointer-events="all"
id="rect8"
style="background-color:rgb(255, 255, 255)" />
<g
id="g8"
style="background-color:rgb(255, 255, 255)"
transform="translate(-0.5,-0.5)">
<switch
id="switch8">
<foreignObject
style="overflow: visible; text-align: left;"
pointer-events="none"
width="100%"
height="100%"
requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<xhtml:div
style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 300px; margin-left: 549px;">
<xhtml:div
style="box-sizing: border-box; font-size: 0px; text-align: center;"
data-drawio-colors="color: rgb(0, 0, 0); ">
<xhtml:div
style="display: inline-block; font-size: 24px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap;">
<xhtml:font
style="font-size: 80px;">∑</xhtml:font>
</xhtml:div>
</xhtml:div>
</xhtml:div>
</foreignObject>
<image
x="520.5"
y="252.5"
width="57"
height="102"
xlink:href="data:image/png;base64,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 truncated
id="image8" />
</switch>
</g>
<path
d="M 329,40 H 477.03"
fill="none"
stroke="#808080"
stroke-width="4"
stroke-miterlimit="10"
pointer-events="stroke"
id="path8"
style="background-color:rgb(255, 255, 255)" />
<path
d="m 484.53,40 -10,5 2.5,-5 -2.5,-5 z"
fill="#808080"
stroke="#808080"
stroke-width="4"
stroke-miterlimit="10"
pointer-events="all"
id="path9"
style="background-color:rgb(255, 255, 255)" />
<path
d="m 609,40 h 20"
fill="none"
stroke="#808080"
stroke-width="4"
stroke-miterlimit="10"
pointer-events="stroke"
id="path10"
style="background-color:rgb(255, 255, 255)" />
<path
d="M 889,39.29 969,39 v 69.03"
fill="none"
stroke="#808080"
stroke-width="4"
stroke-miterlimit="10"
pointer-events="stroke"
id="path11"
style="background-color:rgb(255, 255, 255)" />
<path
d="m 969,115.53 -5,-10 5,2.5 5,-2.5 z"
fill="#808080"
stroke="#808080"
stroke-width="4"
stroke-miterlimit="10"
pointer-events="all"
id="path12"
style="background-color:rgb(255, 255, 255)" />
<path
d="m 729,39.95 28.03,-0.2"
fill="none"
stroke="#808080"
stroke-width="4"
stroke-miterlimit="10"
pointer-events="stroke"
id="path13"
style="background-color:rgb(255, 255, 255)" />
<path
d="m 764.53,39.69 -9.97,5.07 2.47,-5.01 -2.54,-4.98 z"
fill="#808080"
stroke="#808080"
stroke-width="4"
stroke-miterlimit="10"
pointer-events="all"
id="path14"
style="background-color:rgb(255, 255, 255)" />
<path
d="M 629,40 H 729"
fill="none"
stroke="#808080"
stroke-width="4"
stroke-miterlimit="10"
stroke-dasharray="12, 12"
pointer-events="stroke"
id="path15"
style="background-color:rgb(255, 255, 255)" />
<path
d="m 329,40 h 80 v 68.03"
fill="none"
stroke="#808080"
stroke-width="4"
stroke-miterlimit="10"
pointer-events="stroke"
id="path16"
style="background-color:rgb(255, 255, 255)" />
<path
d="m 409,115.53 -5,-10 5,2.5 5,-2.5 z"
fill="#808080"
stroke="#808080"
stroke-width="4"
stroke-miterlimit="10"
pointer-events="all"
id="path17"
style="background-color:rgb(255, 255, 255)" />
<path
d="m 129,40 v 68.03"
fill="none"
stroke="#808080"
stroke-width="4"
stroke-miterlimit="10"
pointer-events="stroke"
id="path18"
style="background-color:rgb(255, 255, 255)" />
<path
d="m 129,115.53 -5,-10 5,2.5 5,-2.5 z"
fill="#808080"
stroke="#808080"
stroke-width="4"
stroke-miterlimit="10"
pointer-events="all"
id="path19"
style="background-color:rgb(255, 255, 255)" />
<path
d="m 129,200 -0.08,27.79"
fill="none"
stroke="#808080"
stroke-width="4"
stroke-miterlimit="10"
pointer-events="stroke"
id="path20"
style="background-color:rgb(255, 255, 255)" />
<path
d="m 128.89,235.29 -4.97,-10.02 5,2.52 5,-2.49 z"
fill="#808080"
stroke="#808080"
stroke-width="4"
stroke-miterlimit="10"
pointer-events="all"
id="path21"
style="background-color:rgb(255, 255, 255)" />
<path
d="m 408.66,200 v 28.03"
fill="none"
stroke="#808080"
stroke-width="4"
stroke-miterlimit="10"
pointer-events="stroke"
id="path22"
style="background-color:rgb(255, 255, 255)" />
<path
d="m 408.66,235.53 -5,-10 5,2.5 5,-2.5 z"
fill="#808080"
stroke="#808080"
stroke-width="4"
stroke-miterlimit="10"
pointer-events="all"
id="path23"
style="background-color:rgb(255, 255, 255)" />
<path
d="m 968.66,200 v 28.03"
fill="none"
stroke="#808080"
stroke-width="4"
stroke-miterlimit="10"
pointer-events="stroke"
id="path24"
style="background-color:rgb(255, 255, 255)" />
<path
d="m 968.66,235.53 -5,-10 5,2.5 5,-2.5 z"
fill="#808080"
stroke="#808080"
stroke-width="4"
stroke-miterlimit="10"
pointer-events="all"
id="path25"
style="background-color:rgb(255, 255, 255)" />
<path
d="m 1089,300 h 108.58"
fill="none"
stroke="#808080"
stroke-width="4"
stroke-miterlimit="10"
pointer-events="stroke"
id="path26"
style="background-color:rgb(255, 255, 255)" />
<path
d="m 1205.08,300 -10,5 2.5,-5 -2.5,-5 z"
fill="#808080"
stroke="#808080"
stroke-width="4"
stroke-miterlimit="10"
pointer-events="all"
id="path27"
style="background-color:rgb(255, 255, 255)" />
<rect
x="1109"
y="261"
width="80"
height="44"
rx="22"
ry="22"
fill="none"
stroke="none"
pointer-events="all"
id="rect27"
style="background-color:rgb(255, 255, 255)" />
<g
id="g27"
style="background-color:rgb(255, 255, 255)"
transform="translate(-0.5,-0.5)">
<switch
id="switch27">
<foreignObject
style="overflow: visible; text-align: left;"
pointer-events="none"
width="100%"
height="100%"
requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<xhtml:div
style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 283px; margin-left: 1149px;">
<xhtml:div
style="box-sizing: border-box; font-size: 0px; text-align: center;"
data-drawio-colors="color: #808080; ">
<xhtml:div
style="display: inline-block; font-size: 24px; font-family: Helvetica; color: rgb(128, 128, 128); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap;">y[k]</xhtml:div>
</xhtml:div>
</xhtml:div>
</foreignObject>
<image
x="1127.5"
y="269"
width="43"
height="35"
xlink:href="data:image/png;base64,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"
id="image27" />
</switch>
</g>
<rect
x="9"
y="0"
width="80"
height="40"
rx="20"
ry="20"
fill="none"
stroke="none"
pointer-events="all"
id="rect28"
style="background-color:rgb(255, 255, 255)" />
<g
id="g28"
style="background-color:rgb(255, 255, 255)"
transform="translate(-0.5,-0.5)">
<switch
id="switch28">
<foreignObject
style="overflow: visible; text-align: left;"
pointer-events="none"
width="100%"
height="100%"
requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
<xhtml:div
style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 20px; margin-left: 49px;">
<xhtml:div
style="box-sizing: border-box; font-size: 0px; text-align: center;"
data-drawio-colors="color: #808080; ">
<xhtml:div
style="display: inline-block; font-size: 24px; font-family: Helvetica; color: rgb(128, 128, 128); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap;">x[k]</xhtml:div>
</xhtml:div>
</xhtml:div>
</foreignObject>
<image
x="27.5"
y="6"
width="43"
height="35"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKwAAACMCAYAAADhnULuAAAJTklEQVR4Xu2dMYwdNRCGcz20CGoEBRRIUNAgQo9AghoqqKDlGiiSFCAhaEkFDdDQgASiTxAUFESigIIoNRF1+mP25Bf59t579sw/tnd2f6SnE3pr7/ifb8fjsffl5Ar/owKBFDgJZCtNpQJXCCwhCKUAgQ3lLhpLYMlAKAUIbCh30VgCSwZCKUBgQ7mLxhJYMhBKAQIbyl00lsCSgVAKENhQ7qKxBJYMhFKAwIZyF40lsGQglAIENpS7aCyBJQOhFCCwodxFYwksGQilAIEN5S4aS2DJQCgFCGwod9FYAksGQimwOWCvX7/+hXjovUZeuiP9v3Csb/n+THvvk5OTN69du/aDtl2L60frR2B9vUpgMT2L+hFYTOB566LgjLBHBS/qR2AJrEoBpgQqufCLRwvOCMsIq6KYwKrkunTxaP2YEmD+Yw7bWT8C21lwpgRMCVTIjZ7SCCyBJbAqBbCLRz/wTAkw/zGH7awfge0sOFMCpgQq5CxTmrRxe7CjA6sRW8b6h1z/vKINd7rmYhFYBT7gpQQWFHBqTmAdRKzsgsBWCnXsMgLrIGJlFwS2UigC6yCUQxcE1kdE9QFuLrpswhNYm24XWjElcBCxsotmwN64ceOds7OzLyvt2HfZbTHuFaD9tBh6TNrfk88jxn5OpY/PS223BKyM9RnR43dA0wfS9lXp55eSrvu+bwZsWj1/I3/fshg2tZH3jt6V946+sraXwd2StleN7asfmK0AmwLAr6LnU0ZNPXzarg7rMMD7Isxz0s9/WoHACK+674aA1cIyd1vVjHXM100jbIqyL8vf21rgsuu/FSPf1rRHUwHtG6VbAFbG+KP44DWNH2bXwrAmnrQPjX6ny+LQfLAGgJBUwPKArLpKIP77WPzxIQDrTenjfaD9w6bNI+zuToYb5eO7L+2fqBkwmArclfs8XXOf/BrLAxmlrCV2fiBj/UyrSXb9T9LH60D7C00NHOkjbArl6Oqy+JSiqYDYedWyel0rsDKuKZ37WT7WKosrrN1Sgt0jAka/qZujQIFVgU+k/UeWSLBGYGVMaICprrJoNO8WYbPUAMkvD07Z4MNQnDaOibo2YB2qO3dFr5cs1Z0SvCOAnYr5f8rn8ZJxB76/FAnBVGAqZD+JiLtCYP8RTay11mawdk8JstTgDdkF+94I7ATYiwLJ3x5RG92cSCKupkoAlq+awjoMWKuTM8AfTuFgKuCyKFhLhLWMI/MJtOVaG7y6pwS5YXJzZOo5lb6+lo/1rIBqN2vtOSxYvuoC69AIm26OrEQnkaZdD+tZAVMJax+4lsi0pDpsKl9ZdyO7wToc2GQAuotSO5vk1xVruppOIwOL1lo91gBKrdtvzZYMAuunpe7n35t2s9aYEsjC9zcZF3L6yuV8gMaBQ3PYbJWPnlutHfOlCkNtw5UC+6mMy1q+6g7rIlKCHQiy2kdKXbXcmXez1gasjGcqQVlhdU2pap23KGCTMdCB78LAm2wVJrvD1WE1kMyufSDT8qNAe6jpIlKCWWqA5FSHxIB3s1YYYRFwmsxUNQYtCtgUrdAD35fGrT1PWyNcfk3EKoF2jPMoK/8PbWdb7784YBO0nqUu9YFsrZgbBHaSqLmu+/ywSGCNyfW+8TXf295gDpvr/Gx+pkP7oFuuXzKwcKmrdSqQ5d5bWnTlnEHHMlcFbIpct+Svdeu127S10ZTgnLdeQSELDuN3ug7kKui7RDtBod82qIkCWwZW9Kl+365Gy9I1i0wJxCjkUMx8zE1LWhvPYXdadytzLRVYbdgvPZjNNg1WCOx07FL7NkjzoLDYlECeIM+SVg5ysyiwopTgVHLSe8a3QbqUuRYVYcFzmaUo2+Tgy4oi7MPDLMDpObczxoecuTRgkTcQSsBO37sfLVwJsBdeFUpriL9qBJ1d07zMtRhgxZCWB19yXd1PGgVPCfa+12b1R+sD3YsAttPRwhxa16krMLAHX8KUMVk3bpqWuYYDm4RBfqfAMHOdnwV1+6GHoMAW3xi2jCs5w30WW0yVQERBfsYROYTstqq1OFbahPiH5cTOf5dU5hoaYcHfFLgpQn4nH+vbnm7biisH1rrjWIzglqlxGLBAjnRhtW+BJRPKpeBtsSFKhE1VEOtGjutawWhLsXJRNdUZnpSDiybpCymHwbtgawcWWBQXYdFGWQM3RRuKwMpNkd2sSwk9IOhOL+gN0LUDmyLbLfmrPjnnXebqDmwqSlv/2ZyDPy9krRsmYqFdsI0AOx1IsmwmuJa5RgBrnr6Pnb10KI8Vp44j24XhDnBbzrECQcGtzNUVWEskyiAplqEcUgOTsJZxRVp0ZTVQ62aCy+K266JLHIS8DVv9S4NAFNj5Rb2y3Qqw1nMTSViXMleXCItO15rpC72XiKveBdsSsAlay2bC1FQdDOZpWC9gkYMtxVRgPiiH1EB1zw0Ca91MMK8Tum3NgvBUpwJ7nkRky1e1C7Y1YI255LmL0DJX0wgL7mapoNkDrHWBsOuq+mHZIrBAIILKXK2BNRWbEzGqaXlfyQk8qzB1WbULtkVgkc0EaWuqxhgjezENOd/pAmGpjm6HaqNZzoM8NFVT2IaBtW4mmMtcTSIsuJsFpQINUoPiLthWgU0Rz7qgNpW5mgBbinpr+340sGvT89h4CKyDtwmsg4iVXRDYSqEKT/3QswQOQwjTBYF1cBUjrIOIlV0Q2EqhGGEdhHLogsD6iMiUwEHHmi4IbI1KhWuYEjiIWNkFga0UyjslUNy2uFOj6GuRl1oeeMVAivoV3+lS3CzEpaMFDyHSESNH60dgfQkqRgjf2/XvjcB21ny04J2H63670foxwvq6lBEW07OoH4HFBJ63Lgrue7v+vTHCdtZ8tOCdh+t+u9H6McL6upQRFtOzqB+BxQRmStBZPwLbWXDf2/XvjSlBZ81HC955uO63G60fI6yvS4s5mO/t+vdGYPtrzjsGVmBzETawr2i6KEBgiUEoBQhsKHfRWAJLBkIpQGBDuYvGElgyEEoBAhvKXTSWwJKBUAoQ2FDuorEElgyEUoDAhnIXjSWwZCCUAgQ2lLtoLIElA6EUILCh3EVjCSwZCKUAgQ3lLhpLYMlAKAUIbCh30VgCSwZCKUBgQ7mLxhJYMhBKAQIbyl00lsCSgVAKENhQ7qKxBJYMhFKAwIZyF40lsGQglAL/AzxRJvanxVBaAAAAAElFTkSuQmCC"
id="image28" />
</switch>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 59 KiB

@@ -0,0 +1,151 @@
<mxfile host="Electron" modified="2024-01-27T19:14:32.318Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/22.1.21 Chrome/120.0.6099.109 Electron/28.1.0 Safari/537.36" etag="_JxO4yykJQa9LzsHiU7_" version="22.1.21" type="device">
<diagram name="Page-1" id="ScnFzgmv18Yx9ZM-e3TZ">
<mxGraphModel dx="450" dy="363" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="z0oqqv8cwlupup-oO1Ry-11" value="" style="group;strokeColor=none;" parent="1" vertex="1" connectable="0">
<mxGeometry x="160" y="380" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-7" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=none;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-11" vertex="1">
<mxGeometry width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-8" value="" style="endArrow=none;html=1;rounded=0;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-11" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="15" y="30" as="sourcePoint" />
<mxPoint x="25" y="30" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-9" value="" style="endArrow=none;html=1;rounded=0;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-11" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="3" y="19.899999999999977" as="sourcePoint" />
<mxPoint x="13" y="19.899999999999977" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-10" value="" style="endArrow=none;html=1;rounded=0;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-11" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="8" y="25" as="sourcePoint" />
<mxPoint x="8" y="15" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-12" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#808080;" parent="1" source="z0oqqv8cwlupup-oO1Ry-7" target="z0oqqv8cwlupup-oO1Ry-3" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-13" value="" style="group;strokeColor=none;" parent="1" vertex="1" connectable="0">
<mxGeometry x="280" y="360" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-3" value="" style="triangle;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-13" vertex="1">
<mxGeometry width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-4" value="" style="curved=1;endArrow=none;html=1;rounded=0;endFill=0;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-13" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="7" y="53" as="sourcePoint" />
<mxPoint x="37" y="33" as="targetPoint" />
<Array as="points">
<mxPoint x="7" y="63" />
<mxPoint x="17" y="63" />
<mxPoint x="27" y="23" />
<mxPoint x="37" y="23" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-14" value="" style="group;strokeColor=none;" parent="1" vertex="1" connectable="0">
<mxGeometry x="440" y="360" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-5" value="" style="triangle;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-14" vertex="1">
<mxGeometry width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-6" value="" style="endArrow=none;html=1;rounded=0;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-14" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="8" y="61" as="sourcePoint" />
<mxPoint x="28" y="21" as="targetPoint" />
<Array as="points">
<mxPoint x="18" y="61" />
<mxPoint x="18" y="41" />
<mxPoint x="18" y="21" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-15" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#808080;" parent="1" source="z0oqqv8cwlupup-oO1Ry-3" target="z0oqqv8cwlupup-oO1Ry-5" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-16" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeColor=#808080;" parent="1" target="z0oqqv8cwlupup-oO1Ry-7" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="120" y="400" as="sourcePoint" />
<mxPoint x="150.00000000000003" y="395" as="targetPoint" />
<Array as="points">
<mxPoint x="130" y="400" />
<mxPoint x="130" y="400" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.25;entryDx=0;entryDy=0;endArrow=block;endFill=1;strokeColor=#808080;" parent="1" source="z0oqqv8cwlupup-oO1Ry-5" target="z0oqqv8cwlupup-oO1Ry-17" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-27" value="" style="group;strokeColor=none;fontColor=#808080;" parent="1" vertex="1" connectable="0">
<mxGeometry x="600" y="480" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-23" value="" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=none;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-27" vertex="1">
<mxGeometry width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-24" value="" style="endArrow=none;html=1;rounded=0;exitX=0;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0;entryDx=0;entryDy=0;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-27" source="z0oqqv8cwlupup-oO1Ry-23" target="z0oqqv8cwlupup-oO1Ry-23" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="60" y="-120" as="sourcePoint" />
<mxPoint x="110" y="-170" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-25" value="&lt;font style=&quot;font-size: 20px;&quot;&gt;&lt;b&gt;A&lt;/b&gt;&lt;/font&gt;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-27" vertex="1">
<mxGeometry width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-26" value="&lt;font style=&quot;font-size: 20px;&quot;&gt;&lt;b&gt;D&lt;/b&gt;&lt;/font&gt;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-27" vertex="1">
<mxGeometry x="40" y="40" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-28" value="" style="group;strokeColor=#808080;fontColor=#808080;" parent="1" vertex="1" connectable="0">
<mxGeometry x="600" y="380" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-17" value="" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=none;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-28" vertex="1">
<mxGeometry width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-19" value="" style="endArrow=none;html=1;rounded=0;exitX=0;exitY=0.804;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.004;entryY=0.68;entryDx=0;entryDy=0;entryPerimeter=0;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-28" source="z0oqqv8cwlupup-oO1Ry-17" target="z0oqqv8cwlupup-oO1Ry-17" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="-10" y="70" as="sourcePoint" />
<mxPoint x="40" y="20" as="targetPoint" />
<Array as="points">
<mxPoint x="10" y="60" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-20" value="D" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-28" vertex="1">
<mxGeometry y="10" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-22" value="Q" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-28" vertex="1">
<mxGeometry x="60" y="10" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-29" value="" style="endArrow=classic;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;strokeColor=#808080;" parent="1" source="z0oqqv8cwlupup-oO1Ry-22" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="660" y="370" as="sourcePoint" />
<mxPoint x="760" y="400" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-30" value="" style="endArrow=classic;html=1;rounded=0;entryX=1;entryY=0.25;entryDx=0;entryDy=0;startArrow=oval;startFill=1;strokeColor=#808080;" parent="1" target="z0oqqv8cwlupup-oO1Ry-23" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="720" y="400" as="sourcePoint" />
<mxPoint x="710" y="320" as="targetPoint" />
<Array as="points">
<mxPoint x="720" y="500" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="z0oqqv8cwlupup-oO1Ry-31" value="" style="endArrow=classic;html=1;rounded=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;strokeColor=#808080;" parent="1" source="z0oqqv8cwlupup-oO1Ry-25" target="z0oqqv8cwlupup-oO1Ry-7" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="330" y="450" as="sourcePoint" />
<mxPoint x="380" y="400" as="targetPoint" />
<Array as="points">
<mxPoint x="180" y="500" />
</Array>
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="162.60143mm"
height="145.49326mm"
viewBox="0 0 162.60143 145.49326"
version="1.1"
id="svg1"
xml:space="preserve"
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
sodipodi:docname="specification_de_filtre.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="0.76167929"
inkscape:cx="454.25943"
inkscape:cy="204.15416"
inkscape:window-width="1920"
inkscape:window-height="1111"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" /><defs
id="defs1" /><g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-24.16979,-27.981753)"><path
style="fill:#b3b3b3"
d="m 43.550519,173.33743 c -0.254574,-0.10272 -0.330729,-2.01702 -0.330729,-8.31348 v -8.18004 h -6.331101 c -5.402769,0 -6.392067,-0.061 -6.746875,-0.41577 -0.510872,-0.51087 -0.510872,-0.50966 0,-1.02054 0.354801,-0.3548 1.343921,-0.41577 6.744842,-0.41577 h 6.329068 l 0.0029,-60.35319 0.0029,-60.353191 -0.952606,0.8364 c -1.049253,0.921256 -1.784615,0.967164 -1.90637,0.119015 -0.09659,-0.672828 2.715832,-6.487427 3.294484,-6.811257 0.285899,-0.159997 0.561696,-0.07902 0.886584,0.260305 0.805275,0.841062 3.459054,6.349148 3.28169,6.811353 -0.253641,0.660977 -1.054436,0.509844 -2.08282,-0.393089 l -0.935248,-0.821159 v 7.963517 7.963516 l 24.14323,0.06754 24.14323,0.06754 -0.104749,47.781485 -0.104749,47.781485 30.46569,-0.12725 30.46569,-0.12725 v 4.66711 4.66712 h 7.70032 7.70033 l -0.8384,-0.95488 c -0.47736,-0.54368 -0.77401,-1.12269 -0.68887,-1.34456 0.0822,-0.21432 0.14953,-0.43947 0.14953,-0.50032 0,-0.7008 6.66518,2.43463 7.25239,3.41165 0.23452,0.39022 0.16497,0.58078 -0.37322,1.02256 -1.0994,0.90245 -5.83161,3.16392 -6.43871,3.077 -0.86373,-0.12368 -0.82489,-0.85236 0.10166,-1.90765 l 0.83562,-0.95172 h -62.2045 -62.204492 v 8.01688 c 0,8.59642 -0.05419,8.9619 -1.256771,8.47664 z m 49.146356,-22.71496 v -4.36936 l -17.462539,0.0699 c -9.604396,0.0384 -17.462521,-0.0194 -17.4625,-0.12856 2.1e-5,-0.10914 7.858163,-0.19843 17.462538,-0.19843 H 92.696875 V 109.08662 72.17725 H 68.752082 44.80729 v 37.05526 37.05526 l 3.83687,-0.0797 c 2.110279,-0.0439 3.836685,0.01 3.836459,0.1187 -2.27e-4,0.10914 -1.726818,0.19843 -3.83687,0.19843 H 44.80729 v 4.23334 4.23333 H 68.752082 92.696875 Z M 52.74479,146.26058 c 0,-0.14552 0.119063,-0.26458 0.264584,-0.26458 0.145521,0 0.264583,0.11906 0.264583,0.26458 0,0.14552 -0.119062,0.26458 -0.264583,0.26458 -0.145521,0 -0.264584,-0.11906 -0.264584,-0.26458 z m 2.116667,0 c 0,-0.14552 0.119063,-0.26458 0.264584,-0.26458 0.14552,0 0.264583,0.11906 0.264583,0.26458 0,0.14552 -0.119063,0.26458 -0.264583,0.26458 -0.145521,0 -0.264584,-0.11906 -0.264584,-0.26458 z m 2.116667,0 c 0,-0.14552 0.119062,-0.26458 0.264583,-0.26458 0.145521,0 0.264584,0.11906 0.264584,0.26458 0,0.14552 -0.119063,0.26458 -0.264584,0.26458 -0.145521,0 -0.264583,-0.11906 -0.264583,-0.26458 z m 47.889586,4.36563 v -4.36563 h -5.953127 -5.953125 v 4.36563 4.36562 h 5.953125 5.953127 z m -10.84938,14.55384 c 0.214329,-0.25825 0.290326,-0.98543 0.219399,-2.09933 l -0.108517,-1.70421 1.069769,0.0112 c 1.353594,0.0142 1.731227,0.32537 1.731227,1.42673 0,0.9449 -0.814558,1.77934 -1.485583,1.52184 -0.437085,-0.16772 -0.462927,0.0352 -0.09261,0.72712 0.247585,0.46262 0.18094,0.51177 -0.693855,0.51177 -0.828304,0 -0.920498,-0.0569 -0.639832,-0.39511 z m 1.853545,-2.39863 c 0,-0.92599 -0.421098,-1.23532 -0.698297,-0.51296 -0.212276,0.55319 0.128763,1.77538 0.44147,1.58212 0.141255,-0.0873 0.256827,-0.56842 0.256827,-1.06916 z m -57.770105,2.22951 c -1.118538,-0.65019 -1.581701,-3.28368 -0.832366,-4.73273 1.112465,-2.15127 3.369757,-1.39534 3.583821,1.20017 0.136745,1.65803 -0.02844,2.29917 -0.788082,3.05881 -0.830243,0.83024 -1.197598,0.91888 -1.963373,0.47375 z m 1.297599,-1.43486 c 0.18609,-1.24094 0.04661,-3.715 -0.224738,-3.98634 -0.08413,-0.0841 -0.279952,-0.0745 -0.435155,0.0214 -0.306929,0.18969 -0.402424,4.69724 -0.105797,4.99386 0.391137,0.39114 0.593518,0.11918 0.76569,-1.02895 z m 49.21576,0.52538 c -0.924423,-0.92442 -0.845786,-2.60334 0.166089,-3.54605 0.596077,-0.55533 1.79899,-0.73714 1.79899,-0.2719 0,0.14552 -0.110558,0.26458 -0.245685,0.26458 -0.597264,0 -1.077232,0.89257 -1.077232,2.00327 0,1.01947 0.07329,1.17173 0.564004,1.17173 0.504048,0 0.549109,-0.11106 0.423881,-1.0447 -0.153331,-1.14317 0.299988,-2.08213 0.843159,-1.74643 0.202259,0.125 0.291819,0.66162 0.238575,1.42949 -0.07498,1.08137 -0.02155,1.22935 0.443923,1.22935 0.420124,0 0.546042,-0.20369 0.611062,-0.98847 0.06414,-0.7741 -0.06497,-1.13532 -0.595312,-1.66567 -0.372465,-0.37246 -0.677208,-0.73133 -0.677208,-0.79747 0,-0.36153 1.130335,-0.0329 1.76517,0.51311 1.404889,1.20843 0.745415,3.86454 -0.959511,3.86454 -0.384904,0 -0.829263,-0.12944 -0.987463,-0.28764 -0.207127,-0.20712 -0.337395,-0.20712 -0.465407,0 -0.262976,0.42551 -1.370378,0.34892 -1.847035,-0.12774 z m 13.969861,-0.52505 c -0.86767,-1.32423 -0.25657,-3.37493 1.10776,-3.71735 0.95943,-0.2408 1.04239,0.004 0.26495,0.78123 -0.71748,0.71748 -0.86779,1.70368 -0.39446,2.58811 0.22279,0.41628 0.36178,0.46351 0.65293,0.22187 0.26169,-0.21718 0.32457,-0.63765 0.21495,-1.4374 -0.1058,-0.77188 -0.0484,-1.19627 0.18096,-1.33802 0.55599,-0.34362 1.0106,0.58073 0.85524,1.73896 -0.12522,0.93364 -0.0802,1.0447 0.42388,1.0447 0.49072,0 0.56401,-0.15227 0.56401,-1.17173 0,-1.1107 -0.47997,-2.00327 -1.07723,-2.00327 -0.13513,0 -0.24569,-0.11907 -0.24569,-0.26459 0,-0.29619 0.78976,-0.35104 1.44336,-0.10023 1.1331,0.43482 1.57065,2.90706 0.67331,3.8044 -0.55867,0.55866 -1.70818,0.71148 -1.98482,0.26387 -0.1096,-0.17735 -0.32831,-0.17735 -0.65968,0 -0.8326,0.44559 -1.55469,Line truncated
id="path2" /><path
style="fill:#808080"
d="m 43.396179,173.07169 c -0.09701,-0.097 -0.176389,-3.8454 -0.176389,-8.32975 v -8.15337 l -6.548437,-0.0708 c -6.34846,-0.0686 -6.548438,-0.0869 -6.548438,-0.59994 0,-0.51301 0.199978,-0.53133 6.548438,-0.59993 l 6.548437,-0.0708 V 94.608444 33.969858 l -1.059851,1.034917 c -0.989866,0.966578 -1.089016,1.005752 -1.501523,0.593245 -0.412506,-0.412506 -0.348751,-0.638909 0.9655,-3.428616 0.773944,-1.642819 1.523739,-3.135772 1.666211,-3.317673 0.66443,-0.84831 1.212028,-0.318737 2.831572,2.738364 2.132466,4.02531 2.023569,5.317937 -0.285463,3.388494 l -1.161237,-0.970338 -0.07018,7.902962 c -0.0386,4.34663 -0.01257,8.053092 0.05784,8.236584 0.10172,0.265076 5.103687,0.33362 24.345702,0.33362 H 93.22604 v 10.71424 10.71424 l -24.275521,0.06753 -24.275521,0.06753 v 41.539583 41.53958 l 30.228646,0.0673 30.228646,0.0673 v -4.76363 -4.76362 h 24.34167 24.34167 v 4.7625 4.7625 h 7.97119 7.9712 l -1.12206,-1.21729 c -1.03294,-1.1206 -1.08812,-1.25123 -0.69472,-1.64463 0.39339,-0.39339 0.68143,-0.30196 3.62632,1.15115 3.16955,1.56395 3.97437,2.22847 3.45442,2.85217 -0.14389,0.17259 -1.69977,1.02413 -3.45751,1.8923 -2.94205,1.4531 -3.22984,1.54454 -3.62323,1.15114 -0.3934,-0.39339 -0.34307,-0.51877 0.63362,-1.57848 l 1.06097,-1.15115 H 107.15541 44.674999 l -0.06992,8.26823 c -0.06589,7.79212 -0.09763,8.26823 -0.551215,8.26823 -0.264715,0 -0.560675,-0.0794 -0.657689,-0.17639 z M 153.02188,151.28766 v -4.7625 h -23.54792 -23.54792 v 4.7625 4.7625 h 23.54792 23.54792 z M 92.432291,66.621 V 61.8585 H 68.752082 45.071874 v 4.7625 4.7625 h 23.680208 23.680209 z m 0,-10.847916 v -4.7625 H 68.884374 45.336457 v 4.7625 4.7625 h 23.547917 23.547917 z m 1.805893,107.845426 0.0861,-1.95789 0.83994,-0.12443 c 0.461967,-0.0684 0.989796,-0.0675 1.172953,0.002 0.504048,0.19147 0.698654,1.34565 0.354316,2.10139 -0.241655,0.53037 -0.448452,0.64406 -0.984978,0.5415 -0.619793,-0.11848 -0.669339,-0.0633 -0.567281,0.63209 0.09256,0.63067 0.01718,0.76201 -0.437664,0.76252 -0.51456,5.8e-4 -0.544016,-0.12384 -0.463388,-1.95727 z m 1.789476,-0.29171 c 0.209982,-0.83664 -0.02154,-1.4558 -0.544356,-1.4558 -0.282814,0 -0.405179,0.26635 -0.405179,0.88194 0,1.35214 0.654867,1.74791 0.949535,0.57386 z m -58.198835,1.22649 c -1.825406,-2.01705 -0.386832,-6.4379 1.726836,-5.3067 1.953092,1.04526 1.523284,5.79941 -0.524308,5.79941 -0.416148,0 -0.957285,-0.22172 -1.202528,-0.49271 z m 1.631335,-2.24841 c 0.07507,-2.28472 -0.236707,-3.42255 -0.787021,-2.87223 -0.356239,0.35624 -0.568584,2.78034 -0.363404,4.14858 0.14354,0.95719 0.283809,1.21289 0.62916,1.14688 0.36906,-0.0705 0.457467,-0.48152 0.521265,-2.42323 z m 49.347786,1.81475 c -1.251277,-0.87643 -0.938041,-3.0842 0.537951,-3.79162 0.943422,-0.45218 1.225185,-0.20524 0.469995,0.41189 -0.73777,0.6029 -0.994555,2.14252 -0.473818,2.8409 0.331352,0.44439 0.449158,0.46769 0.817743,0.1618 0.314639,-0.26113 0.385782,-0.5964 0.264142,-1.24479 -0.230392,-1.2281 0.026,-1.85804 0.571851,-1.40502 0.297473,0.24688 0.364662,0.60417 0.245856,1.30737 -0.117722,0.6968 -0.05127,1.06078 0.237398,1.30036 0.604967,0.50208 0.953227,0.1116 0.953227,-1.06878 0,-1.23117 -0.382681,-2.08366 -0.93535,-2.08366 -0.213162,0 -0.387566,-0.1318 -0.387566,-0.29289 0,-0.16109 0.188331,-0.22062 0.418514,-0.13229 0.230183,0.0883 0.52784,0.1606 0.661459,0.1606 0.622599,0 1.301277,1.10394 1.301277,2.11666 0,1.59415 -1.411636,2.71587 -2.299928,1.82758 -0.209071,-0.20908 -0.438621,-0.20908 -0.829273,0 -0.70594,0.3778 -0.8767,0.36592 -1.553478,-0.10811 z m 13.953404,-0.44644 c -1.01162,-1.08584 -0.52023,-3.16017 0.86281,-3.6423 0.44011,-0.15342 0.87018,-0.20258 0.95572,-0.10924 0.0855,0.0933 0.005,0.22357 -0.17992,0.2894 -0.71026,0.25343 -1.11959,0.94217 -1.1157,1.87728 0.005,1.22215 0.33185,1.76553 0.92487,1.53796 0.37124,-0.14245 0.42207,-0.38121 0.28119,-1.32069 -0.12644,-0.84319 -0.0705,-1.23105 0.21144,-1.46501 0.52403,-0.4349 0.73201,-0.0253 0.65447,1.28888 -0.0798,1.35282 0.42932,1.96009 1.00338,1.19673 0.53175,-0.7071 0.28253,-2.24164 -0.463,-2.85088 -0.75519,-0.61714 -0.47343,-0.86407 0.46999,-0.4119 1.52434,0.7306 1.78707,2.96345 0.45165,3.83845 -0.53228,0.34877 -0.77381,0.36871 -1.34017,0.11066 -0.48162,-0.21944 -0.77484,-0.23081 -0.96812,-0.0375 -0.44595,0.44594 -1.16809,0.3213 -1.74861,-0.30181 z m 5.3917,0.0144 c -0.20172,-0.3264 0.32597,-0.95769 0.97188,-1.1627 0.56485,-0.17927 0.68647,-1.06583 0.17455,-1.2724 -0.22526,-0.0909 -0.20417,-0.13882 0.0661,-0.15028 0.71146,-0.0302 0.97962,0.3515 1.11522,1.58736 l 0.13337,1.21542 -1.15263,0.0174 c -0.63395,0.01 -1.22279,-0.0961 -1.30854,-0.23483 z m 1.47716,-0.64156 c 0,-0.2097 -0.11906,-0.38126 -0.26458,-0.38126 -0.14553,0 -0.26459,0.24515 -0.26459,0.54478 0,0.29963 0.11906,0.47119 0.26459,0.38126 0.14552,-0.0899 0.26458,-0.33509 0.26458,-0.54478 z m -1.32292,-1.1594 c 0,-0.44355 0.46925,-0.55338 0.62543,-0.14639 0.0922,0.24016 -8.3e-4,0.39536 -0.23686,0.39536 -0.21371,0 -0.38857,-0.11204 -0.38857,-0.24897 z m 73.63399,-3.58782 c -1.25128,-0.87643 -0.93804,-3.08419 0Line truncated
id="path1" /></g></svg>

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

+491
View File
@@ -0,0 +1,491 @@
<header><h1>
Programmation concurrente
</h1></header>
Un programme concurrent est un programme non séquentiel.
Soit l'expression arithmétique suivante : $(a+b) * (c+d)$.
***Programmation séquentiel***
1. $x = (a+b)$
2. $y = (c+d)$
3. $x*y$
***Programmation concurrente***
1. $x = (a+b) \qquad y = (c+d)$
2. $x*y$
La programmation concurrente sur 1 CPU permet d'exploiter pleinement les périphériques parallèlement au processeur.
Cela permet d'éviter de bloquer toutes les applications lorsqu'une seule est en attente d'une réponse.
La programmation concurrente sur plusieurs CPU permet en plus un gain de temps.
## Switching contexte
Lors d'un switching context, pour passer d'un processus P1 a un processus P2, les actions suivantes sont effectué :
1. Sauvegarde des registres de P1 sur la stack
2. Placement de P1 dans la WAITING queue
3. Sélectionne le premier processus de la READY queue (P2)
4. Charge les registres de P2
5. Restaure le contexte utilisateur
# Exclusion mutuelle
- un programme est une entité statique qui contient des processus
- un processus est une entité dynamique (il s'agit d'une tâche en cours d'exécution)
Les processus ne sont jamais totalement indépendants.
Ils partagent des ressources (mémoire, fichiers, périphériques, variable, etc.)
Ceci peut conduire à des erreurs et doit donc être controlé.
- ***Section critique :*** partie du code qui accède à une ressource partagée.
- ***Exclusion mutuelle :*** un seul processus à la fois peut accéder à la section critique.
- ***Ressource critique :*** ressource qui doit être accédé en exclusion mutuelle
On pourrait éviter les problèmes d'exclusion mutuelle si on pouvait assurer que deux processus n'exécute jamais en même temps une section
critique.
On peut résoudre ce problème à l'aide de **mutex** ou **sémaphore**.
## Exclusion mutuelle par attente active
```pascal
type processNum = 1..2;
module CS; /* Section critique */
defines Enter, Leave; /* Primitive */
var
request : array processNum of boolean;
tour : processNum;
procedure Enter(num: processNum);
begin
request[num] := true; /* Demande d'accès */
tour := 3 – num; /* Autre processus */
repeat
/* Attendre que l'autre processus ait fini avec "not request" */
/* Priorité si les deux processus demandent en même temps à la section critique
grâce à "tour = num" */
until (not request[3-num]) or (tour = num)
end Enter;
procedure Leave(num: processNum);
begin
request[num] := false
end Leave;
begin /* Initialisation */
request[1] := false;
request[2] := false
end CS;
/***********************************************************************/
process P1;
uses CS, Console;
begin
CS.Enter(1);
Console.Write(”Error on hard disk !”);
CS.Leave(1);
end P1;
/***********************************************************************/
process P2;
uses CS, Console;
begin
CS.Enter(2);
Console.Write(”Printer not ready !”);
CS.Leave(2);
end P2;
```
- si les processus *P1* et *P2* ne tentent pas de pénétrer simultanément dans la section
critique, la variable *request[3-num]* est fausse et l'entrée en section critique est
autorisée
- si par contre les processus *P1* et *P2* tentent de pénétrer simultanément dans la section
critique, la variable *tour* permet d'arbitrer entre les deux demandes. La variable
*tour* (à la ligne 15) indique le numéro du processus élu
Cette méthode peut facilement être généralisée à n processus. Il suffit de faire *tour := n+1 – num* et
*request[n+1-num]*.
Cette solution n'est pas satisfaisante, car elle consomme beaucoup de ressources CPU. En effet, un processus
qui attend l'accès à la section critique est en attente active. Il est donc en boucle constante et consomme
beaucoup de ressources CPU.
## Exclusion mutuelle par masquage des interruptions
Masquer les interruptions pour résoudre le problème de l'exclusion mutuelle.
Plus d'interruption $=>$ plus de commutation de processus $=>$ plus de risque de violer l'exclusion mutuelle.
Le danger avec cette solution est le risque de perte d'information provenant des périphériques. Cela ne peut être une bonne solution
que si la section critique est très courte. Il est dangereux de laisser la possibilité de masquer les interruptions à l'utilisateur.
## Instruction TAS (TestAndSet)
L'exclusion mutuelle par masquage des interruptions n'est pas possible dans une architecture multiprocesseur.
Il est possible de résoudre le problème de l'exclusion mutuelle en utilisant l'instruction *TAS*.
Elle permet de tester et de modifier une variable en **une seule instruction** assembleur.
# Mutex
Le mutex est utilisé pour bloquer l'accès simultané aux sections critiques avec les primitives suivantes :
- ***block() :*** Il laisse passer un seul processus à la fois. Si la section est déjà bloquée, le processus et mis dans
une file d'attente.
```pascal
procedure Block(var v:lock);
begin
if v.state = OPEN then
v.state := CLOSE
else
block the process in the list "v.waiting"
end if
end Block;
}
```
- ***release() :*** Il libère la section critique, si la file d'attente est vide. Sinon, il passe la main au processus
suivant.
```pascal
procedure Release(var v:lock);
begin
if queue "v.waiting" not empty then
unblock first process in the list "v.waiting"
else
v.state := OPEN
end if
end Release;
}
```
Ces procédures sont elles même des sections critiques. Elles sont cepandant très courte et ne contiennent aucunes entrées-sorties.
Elles peuvent donc être implémentées à l'aide d'une [Exclusion mutuelle par masquage des interruptions](#exclusion-mutuelle-par-masquage-des-interruptions)
ou d'une [instruction TAS](#instruction-tas-testandset).
Les Mutex restent un outil primitif. Ils ne s'appliquent pas sur des problèmes plus complexes.
# Sémaphore
Le sémaphore est une généralisation du mutex. Il permet de bloquer l'accès à une ressource partagée par plusieurs processus.
Il limite l'accès à plusieurs même ressources utilisées par plusieurs processus.
Par exemple : les processus ont besoin d'imprimer et il y a plusieurs imprimantes à disposition.
Le sémaphore va limiter l'accès au nombre d'imprimantes disponibles.
Les primitives de sémaphores sont :
- ***P*** (Passeren = passer) appelé aussi *wait*, *up*, *get* ou *lock*
```pascal
procedure P(var s:semaphore);
begin
s.num := s.num – 1;
if s.num < 0 then
block the process at the end of the list "s.waiting"
end if
end P;
```
- ***V*** (Vrijgeven = libérer) appelé aussi *signal*, *down*, *release* ou *unlock*
```pascal
procedure V(var s:semaphore);
begin
s.num := s.num + 1;
if s.num <= 0 then
unblock first process in the list "s.waiting"
end if
end V;
```
À l'initialisation, le sémaphore est initialisé au nombre de mêmes ressources disponibles.
Ou alors, il est initialisé à 1 pour un mutex.
Ou alors, il est initialisé à 0 pour un sémaphore binaire (file d'attente).
## Interblocage
L'interblocage est un problème qui peut survenir lorsqu'un ensemble de processus se bloquent mutuellement.
On appelle aussi cela un **DEADLOCK**.
## Résolution reader / writter
Le but est qu'il peut avoir plusieurs processus qui puisse lire la même ressource et aucun modifier la ressource.
Sinon, **un** processus peut modifier la ressource et aucun peut lire.
```cpp
void BeginRead(void) {
osSemaphoreAcquire(rw, osWaitForever);
osSemaphoreAcquire(mutexNbReader, osWaitForever); // ENTER CRITICAL SECTION
nbReader++;
if(nbReader == 1) osSemaphoreAcquire(w, osWaitForever);
osSemaphoreRelease(mutexNbReader); // EXIT CRITICAL SECTION
osSemaphoreRelease(rw);
}
void EndRead(void) {
osSemaphoreAcquire(mutexNbReader, osWaitForever); // ENTER CRITICAL SECTION
nbReader--;
if(nbReader == 0) osSemaphoreRelease(w);
osSemaphoreRelease(mutexNbReader); // EXIT CRITICAL SECTION
}
void BeginWrite(void) {
osSemaphoreAcquire(rw, osWaitForever);
osSemaphoreAcquire(w, osWaitForever);
}
void EndWrite(void) {
osSemaphoreRelease(w);
osSemaphoreRelease(rw);
}
```
# Coopération-Synchronisation
La coopération entre processus implique la synchronisation entre eux pour éviter de ressource.
## Évènement
L'évènement est un outil permettant de résoudre un problème de synchronisation comme les sémaphores ou les mutex.
On peut manipuler les évènements :
- ***Wait(event)*** : Attendre que l'évènement **event** se produise par la fonction signal(...)
- ***Signal(event)*** : Déclencher l'évènement **event**
- ***Init(event)*** : Reset l'évènement **event** (inverse de signal(event))
On peut aussi résoudre un problème de synchronisation entre processus avec un sémaphore initialisé à 0. On peut le comparer
à une barrière déclenchable depuis un autre processus.
Les sémaphores ne permettent pas de résoudre tous les problèmes de synchronisation. Trop grand risque de DEADLOCK.
Il faut utiliser d'autres outils plus performants.
## Le moniteur (monitor)
On peut l'imager avec une classe entièrement protégée par des sémaphores pour l'accès au variable et fonction (procédure).
Le moniteur possède comme primitive :
- ***x.wait*** : bloque le processus jusqu'à ce qu'un x.send soit émis.
- ***x.send*** : débloque le processus qui attend sur un x.wait.
```pascal
monitor M;
defines DepositeChar, TakeChar;
var
c : char;
chAvailable : boolean;
x : signal;
procedure TakeChar(var ch:char);
begin
if not chAvailable then x.wait end if;
ch := c;
chAvailable := FALSE
end TakeChar;
procedure DepositeChar(ch:char);
begin
c := ch;
chAvailable := TRUE;
x.send
end DepositeChar;
begin
chAvailable := FALSE
end M;
```
```pascal
process Keyboard;
uses M;
var cRead: char;
begin
...
Read a character on keyboard
Put it in the cRead variable
M.DepositeChar(cRead);
...
end Keyboard;
process Screen;
uses M;
var cWrite: char;
begin
...
M.TakeChar(cWrite);
Write the character cWrite on the screen
...
end Screen;
```
## Producteurs / consommateurs
Le problème survient lorsque le producteur envoie un message au consommateur via une queue.
[Example](https://git.kb28.ch/Klagarge/PTR-Queue/src/branch/master/main.c)
- ***Le producteur*** (Producer) doit être mis en attente s'il veut déposer un message alors que le tampon est plein
- ***Le consommateur*** (Consumer) doit être mis en attente s'il veut prélever un message alors que le tampon est vide
- ***Le producteur*** (Producer) en attente doit être débloqué dès que le consommateur (Consumer) prélève un message
- à ce moment le tampon n'est plus plein
- ***Le consommateur*** (Consumer) en attente doit être débloqué dès que le producteur (Producer) dépose un message
- à ce moment le tampon n'est plus vide
On peut résoudre aisément le problème producteurs-consommateurs avec un moniteur ainsi qu'avec un sémaphore.
<b><u>Solution à chier :</u></b><br>
Le producteur attend que le consommateur soit capable de réceptionner le message, ainsi les deux peuvent
continuer leurs exécutions (principe de rendez-vous). Ce concept ne possède pas de buffer.
# Allocation processeur
État d'un processus :
![img_1.png](img_1.png)
Le processeur possède une liste de descripteur caractérisant chaque processus. Ces descripteurs de processus qui
contiennent le registre de status et un pointeur sur sa stack. Ils sont utilisé pour faire des switching context :
1. Le noyau va sauvegarder les registres de travail du processus en cours sur la stack.
2. Il va sauvegarder aussi le registre de status et le pointeur de stack dans la liste de descripteurs.
3. Il va prendre le descripteur du prochain processus et utiliser ses registres.
4. Le processus continue jusqu'au nouveau switching context.
## Cycle processeur
Un processeur réalise alternativement une suite de :
- ***Cycles processeur*** (CPU burst cycle)
- ***Cycles d'entrée-sortie*** (I/O burst cycle)
Lorsque le temps de cycle processeur est petit et qu'il y a une attente engendrée par les entrées-sorties, il est pertinent de partager
le processeur entre plusieurs processus.
## Queues d'ordonnancement
Les processus en attente du processeur sont placés dans une liste appelée **queue de processus prêts** (ready queue).
Cette queue peut être gérée comme une file FIFO, une liste de priorité, un arbre, une pile, une liste non-ordonnée, etc.
Cette queue est sous forme d'une liste chaînée qui contient des pointeurs sur les descripteurs de processus.
Les processus qui ont besoins d'une ressource sont placés dans une liste d'attente correspondant à la ressource (mutex/sémaphore).
L'ensemble des queues peut être regroupé dans le diagramme de synthèse suivant :
![synthese_diagram_queue_process.png](medias/synthese_diagram_queue_process.png)
## Ordonnanceurs
![ordonnanceur.png](medias/ordonnanceur.png)
### Ordonnanceur à court terme
- Sélectionne un nouveau processus toutes les millisecondes.
- Doit être très rapide
- Est très simple
### Ordonnanceur à long terme
- Contrôle le degré de multiprogrammation, c'est-à-dire, le nombre de processus en mémoire.
- Assure que la charge globale du processeur soit répartie équitablement entre les processus réalisant beaucoup d'entrée-sorties et les
processus réalisant beaucoup de calculs.
- S'exécute peu fréquement
- La complexité de ce dernier augmente fortement.
### Le swapping
On peut augmenter la RAM avec de la mémoire virtuelle (swap) pour garder les processus et ne pas les mettre en stockage
long termes.
### Distributeur de tâches
Il distribue au processeur les processus sélectionnés par l'ordonnanceur court terme.
- Il charge les registres du processus sélectionné
- Il change en mode utilisateur depuis le superviseur. Ce dernier servant à modifier les registres.
- Il met à jour le PC
## Critères de performance
- ***Efficacité** (CPU utilisation) : Taux d'utilisation du processus
- ***Débit*** (troughtput) : Nombre de processus par unité de temps
- ***Temps global*** (turnaround time) : Temps qui s'écoule entre la soumission du processus et la fin de son exécution
- ***Temps d'attente*** (waiting time) : Temps d'attente d'un processus dans la queue des processus prêts
- ***Temps de réponse*** (response time) : intervalle de temps entre l'heure de soumission et l'heure de la première réponse
## Algorithme d'ordonnancement
L'allocateur du processeur doit sélectionner quels processus situés dans la queue des processeurs prêts doivent recevoir le processeur.
### FIFO
L'algorithme FIFO est le plus simple. Il sélectionne le premier processus de la queue des processus prêts. Mais, il n'est
pas forcément le plus efficace.
### Le plus court d'abord
Le processus avec la plus petite suite d'instruction est exécuté en premier. Si deux processus ont le même nombre d'instructions,
on peut utiliser un algorithme FIFO pour déterminer le vainqueur. Il est performant, mais c'est compliqué de connaître
le nombre d'instructions.
Il nécessite d'éstimer en temps réel la longueur du processus.
$$\tau_{n+1} = \alpha\cdot t_{n}+(1-\alpha)\cdot \tau_{n}$$ <br>
$\tau_{n+1}$ : prochaine valeur estimée <br>
$\alpha$ : coefficient de pondération $0\leq\alpha\leq 1$ <br>
$t_{n}$ : valeur mesurée à l'instant $n$ <br>
$\tau_{n}$ précédente valeur estimée pour l'instant $n$ <br>
### Priorité
Cet algorithme donne au processeur le processus qui a la plus haute priorité qui est défini par l'utilisateur.
On peut rencontrer plusieurs problèmes :
- ***Blocage indéfini*** (indefinite blocking) : on reste bloqué dans un processus de haute priorité.
- ***Famine*** (starvation) : Les processus de basse priorité ne sont jamais exécuté.
***Solution*** : On augmente la priorité dum processus en attente tous les quantums de temps.
### Ordonnancement avec réquisition (preemptive)
On réévalue l'ordre des processus dans la queue à chaque tick et le processus élu prend ou garde le processeur.
### Tourniquet (round-robin)
Il est basé sur une tranche de temps fixe soit quantum de temps. On donne à chacun des processus ce temps et on passe au
processus suivant une fois le temps écoulé.
Lorsque le temps est grand, on retrouve en quelque sorte une FIFO, car le temps d'exécution des processus est plus court que
le quantum de temps.
Si le temps est très petit, le tourniquet est appelé **processeur partagé** (processor sharing).
<b><u>Overhead</u></b> : temps perdu pour changer de processus.<br>
### Files multiples (multi-queue)
On utilise cette technique lorsqu'on peut facilement classer les processus en différents groupes. Chaque queue possède son
propre algorithme d'ordonnancement. Un ordonnancement entre les queues est réalisé soit par priorité et réquisition, soit
en round robin.
### Files multiples imbriquées (multi-level feedback queue)
Cette technique permet aux processus d'évoluer dans des queues en fonction du temps d'exécution écoulé.
- Un processus entrant dans la queue des processus prêts est placé dans la queue 0
- Si le quantum est écoulé et que la suite d’instruction processeur n’est pas terminée, le processus est placé dans
la queue de priorité inférieure
Plus on descend dans les queues, plus on est de priorité basse.
<u>Exemple :</u> Plus on descend dans les queues, plus le quantum devient grand.
# Interblocage (DEADLOCK)
Un processus attend indéfiniment pour accéder à une ressource bloquée.
L'interblocage apparaît si les 4 conditions apparaissent simultanément :
1. ***L'exclusion mutuelle*** (mutual exclusion) : Une ressource au minimum n'est pas partageable.
2. ***La détention et l'attente*** (hold and wait) : Un processus qui dispose déjà d'au moins une ressource et qui attend l'acquisistion
d'une nouvelle ressource déjà utilisée par un autre processus.
3. ***Pas de réquisition*** (no preemption) : Les ressources obtenues par un processus ne peuvent pas lui être retirées contre son gré
4. ***L'attente circulaire*** (circulare wait) : Il doit y avoir un cycle d'au moins deux processus, chacun d'entre eux attendant
une ressource détenue par un autre processus du cycle.
Les interblocages peuvent être décrits plus précisément en utilsant un graphe appelé **graphe
d'allocation des ressources** (ressource allocation graph).
![interblocage.png](medias/interblocage.png)
- Si le graphe d'allocation des ressources ne contient aucun cycle $=>$ Aucun processus n'est en interblocage
- Si le graphe d'allocation des ressources contient un cycle $=>$ <u>Un risque</u> d'interblocage existe !!
On peut faire une réduction de graphe pour observer l'état suivant du graphe :
- Un graphe peut être réduit par un processus Pi, si le processus n'a pas de demande de ressource en cours.
- Pour faire une réduction, on enlève toutes les allocations du processus.
***Complètement réductible*** : si on arrive à faire des réductions successives jusqu'à ce qu'il n'y a plus de flêches
dans le graphe.
On peut résoudre le problème d'interblocage :
- ***Prévention*** : On garantit qu'aucun interblocage ne peut se produire.
- ***Prévenir** : Empêcher une des 4 conditions de se produire
- ***Éviter** : Éviter dynamiquement en allouant les ressource avec précaution
- ***Détection ou guérison*** : Lorsqu'un interblocage est détecté, on ramène le système dans un état où les processus sont débloqués
En cas d'interblocage, on peut utiliser deux algorithmes pour résoudre ce problème.
L'algorithme du banquier pour la prévention et ensuite un algorithme de reprise après interblocage.
L'algorithme de reprise fait soit appel à l'aide d'un opérateur, soit par une séquence automatique
qui tue les processus impliqués dans l'interblocage, soit un processus après l'autre jusqu'à ce que l'interblocage soit résolu.
Mais la destruction de processus est complexe et dangereuse.
## Algorithme du banquier (banker's algorithm)
Cette technique impose de connaître pour chaque processus les ressources dont il aura besoin.
- Il vérifie avant d'allouer une ressource que le graphe reste complètement réductible.
- Il alloue une ressource uniquement s'il y a plus de ressources disponibles que demandées.
- Sinon la demande de ressource est mise en attente.
<b><u>Exemple :</u></b><br>
![algo_banquier1.png](medias/algo_banquier1.png)
![algo_banquier2.png](medias/algo_banquier2.png)
Une ressource n'est attribuée que si les ressources demandées sont inférieurs aux ressources disponibles.
$$ (N)Needed < (A)Available $$
Si aucune rangée (processus) ne satisfait la condition, il y a <u><b>interblocage</b></u>.
+9
View File
@@ -0,0 +1,9 @@
<header><h1>
PTR - Programmation Temps Réel
</h1></header>
- [XF](/infotro/PTR/XF.md)
- [UML](/infotro/PTR/UML)
- [RealTime](/infotro/PTR/RealTime)
- [SYS - Systèmes](/infotro/PTR/SYS)
- [PCO - Programmation Concurrente](/infotro/PTR/PCO)
+209
View File
@@ -0,0 +1,209 @@
<header><h1>
Real Time
</h1></header>
## Temps réel vs non - temps réel
Pour les systèmes non temps réel, seule l'exactitude du résultat importe, le traitement est principalement contrôlé par
des données massives, et ils ne dépendent pas d'un matériel spécifique.
En revanche, pour les systèmes temps réel, non seulement l'exactitude du résultat est importante, mais aussi le moment
où le résultat apparaît. Le traitement est contrôlé par des événements, les données sont simples,
les quantités sont modérées, le processeur est intensément utilisé, et ces systèmes dépendent d'un matériel spécifique.
**Système Déterministe:**
1. L'état futur du système peut être prédit de manière précise à partir de son état actuel et des entrées.
2. Chaque transition d'état est définie de manière univoque par les conditions actuelles, assurant une prévisibilité totale.
3. Les réponses du système à des signaux d'entrée sont constantes, sans dépendance de la qualité ou du nombre de ces signaux.
4. Idéal pour les applications où la précision du timing et la prédictibilité du comportement sont cruciales, comme dans les systèmes temps réel.
5. Le concept de déterminisme assure une fiabilité et une certitude dans le fonctionnement du système.
**Système Non-Déterministe:**
1. L'état futur du système ne peut pas être prédit de manière précise à partir de son état actuel et des entrées, introduisant une certaine incertitude.
2. Plusieurs transitions d'état peuvent résulter des mêmes conditions initiales, rendant le comportement du système moins prévisible.
3. Les réponses du système à des signaux d'entrée peuvent varier en fonction de différents facteurs, tels que la charge du système ou les conditions d'exécution.
4. Souvent utilisé dans des applications où une flexibilité est nécessaire, mais peut poser des défis dans les systèmes où la prédictibilité est cruciale.
5. La gestion de l'incertitude et l'adaptabilité aux conditions changeantes sont des caractéristiques clés des systèmes non-déterministes.
### Système temps réel
- ***Temps réel dur:*** Le systéme fait une erreur si le délai est dépassé.
- ***Temps réel souple:*** Le système accepte le dépassement du délai sans grave conséquence.
- ***Temps réel ferme:*** Le système accepte le dépassement du délai, mais le résultat n'est plus d'aucune utilité.
### Management du temps (Timing)
Le timing peut avoir principalement deux formes différentes :
- *Timing absolu:* Une tâche sera exécutée à un moment dans le temps bien déterminé.
- *Timing relatif:* Une tâche sera exécutée à la suite de l'apparition d'un événement.
Le timing peut par-contre aussi être classé comme suit :
- *Fixed Time:* Une tâche sera exécutée à un moment bien précis.
- *Fixed Time +/-:* L'exécution d'une tâche est démarrée dans un tout petit intervalle de temps avant ou après un moment bien précis.
- *Latest Time:* L'exécution d'une tâche est démarrée dans un intervalle de temps avant le moment le plus tard possible.
- *Earliest Time:* L'exécution d'une tâche est démarrée dans un intervalle de temps après le moment le plus tôt possible.
### Management du traitement
***Multitasking:***
Traitement en thread: pseudo-parallèle
***Multiprocessing:***
Traintement en parallèle sur plusieurs coeur / processeurs.
### Management des ressources
***Mutex:***
Une ressource peut être utilisée par un seul processus à la fois
***Semaphore:***
Une ressource peut être utilisée par un nombre limité de processus et sous des conditions bien précise
### Approches pour la programmation temps réel
***Synchrone:***
Tout est planifié à l'avance pendant la phase de programmation.
***Asynchrone:***
Rien est planifié à l'avance. Toutes les décisions sont faites pendant d'exécution.
#### Synchrone
Le timing des tâches cycliques est planifié précisément.
Ces dernières sont synchronisées (démarrées) par un signal d'une horloge.
La scéquence des tâches est déterminé à l'avance
Le temps cumulée de l'execution des tâches ne doit pas dépasser le temps d'un cycle.
![Programmation synchrone](medias/system_synchron.png)
<u>*Avantages*</u><br>
- Timing précis
- Comportement déterministe
<u>*Avantages*</u><br>
- Inadaptée aux Processus Sporadiques :
- Inflexibilité
#### Asynchrone
Le timing des tâches est contrôlé soit par un RTOS soit par un XF.
Les tâches sont contrôlées par des événements et des appels de méthodes.
Des tâches parallèles sont sérialisées.
Le séquencement des tâches n'est pas déterminé.
![Programmation asynchrone](medias/system_asynchron.png)
<u>Avantages</u><br>
- Flexibilité face aux changements de spécification du système.
- Implémentation possible au moyen de diagrammes d'états transitions.
<u>Inconvénients</u><br>
- Le RTOS ou le XF introduit des délais dans le traitement.
- Le timing n'est pas très précis, rendant les systèmes moins déterministes en temps.
- Systèmes toujours préemptifs, avec la possibilité d'interruptions dans le traitement
## Processus & Thread
**Processus :**
Un processus est un programme en cours d'exécution, avec sa propre mémoire et ses ressources allouées.
Dans le contexte des systèmes temps réel, un processus est souvent appelé une "task" (tâche).
Il peut être lié à un Real-Time Operating System (RTOS) ou un Executive Framework (XF).
Chaque processus a son propre espace mémoire, ce qui signifie qu'il ne partage pas directement ses données avec d'autres processus.
La communication entre les processus se fait généralement à travers des messages.
**Thread :**
- Un thread est une unité d'exécution plus légère au sein d'un processus.
- Contrairement à un processus, un thread partage le même espace mémoire que son processus parent.
- Les threads peuvent être exécutés de manière concurrente, ce qui permet un parallélisme au sein du processus.
- Les threads peuvent communiquer plus facilement entre eux, car ils ont un accès direct à la mémoire partagée.
**Différence :**
- La principale différence réside dans la gestion de la mémoire. Les processus ont leur propre espace mémoire distinct, tandis que les threads partagent la même mémoire avec leur processus parent.
- Les threads permettent une exécution concurrente au sein du même processus, tandis que les processus sont généralement des entités indépendantes.
- En résumé, un processus est un programme complet en cours d'exécution, tandis qu'un thread est une unité d'exécution plus petite qui partage la mémoire avec d'autres threads dans le même processus.
```plantuml
@startuml
binary "Red Task" as r
binary "Green Task" as g
binary "Blue Task" as b
concise "Task" as t
robust "Red Task" as rt
@0
r is low
g is low
b is low
t is {-}
rt is Sleep
@1
r is high
g is high
b is high
t is "" #blue
rt is ready
@2
r is low
g is low
b is low
t is "" #green
@4
t is "" #red
rt is running
@6
b is high
t is "" #blue
rt is interrupt
@7
b is low
t is "" #red
rt is running
@9
b is high
g is high
t is "" #blue
rt is interrupt
@10
b is low
g is low
t is "" #green
@12
t is "" #red
rt is running
@13
t is {-}
rt is Sleep
@enduml
```
## Scheduling
## OSAL, OXF, RTOS
**Différence entre OSAL, OXF et RTOS :**
1. **OSAL (Operating System Abstraction Layer) :**
- L'OSAL est une couche d'abstraction qui fournit une interface uniforme pour accéder aux services du système d'exploitation sous-jacent.
- Il permet de rendre le code portable entre différents systèmes d'exploitation en masquant les détails spécifiques de chaque système.
- L'OSAL est utile dans des environnements où le système d'exploitation peut varier, permettant ainsi d'adapter le logiciel à différentes plates-formes sans changer le code applicatif.
2. **OXF (Object Execution Framework) :**
- L'OXF est un framework qui fournit une infrastructure pour le développement d'applications temps réel orientées objet.
- Il facilite la modélisation d'objets réactifs, possédant souvent une machine à états, et offre une gestion efficace des événements.
- L'OXF peut être utilisé avec ou sans RTOS. Il fournit des fonctionnalités telles que la gestion des événements, la gestion des tâches, et la synchronisation entre les objets réactifs.
3. **RTOS (Real-Time Operating System) :**
- Un RTOS est un système d'exploitation conçu pour répondre aux exigences temporelles strictes des systèmes temps réel.
- Il offre des fonctionnalités telles que l'ordonnancement des tâches, la gestion du temps, la synchronisation, et la gestion des interruptions pour assurer des réponses prévisibles aux événements.
- Un RTOS peut être utilisé en conjonction avec l'OXF pour développer des systèmes temps réel orientés objet.
**Conclusion :**
- L'OSAL fournit une abstraction des services du système d'exploitation.
- L'OXF est un framework pour le développement d'applications temps réel orientées objet.
- Un RTOS est un système d'exploitation temps réel complet, offrant des services spécifiques pour répondre aux contraintes temporelles strictes.
+163
View File
@@ -0,0 +1,163 @@
<header><h1>
Introduction aux systèmes d'exploitation
</h1></header>
![operating_system.png](medias/operating_system.png)
Le système d'exploitation virtualise la partie hardware pour :
- Simplifie l'utilisation
- Gère les ressources physiques
- Processeur
- Mémoire principale
- Entrées-sorties (disques, interfaces réseaux, ...)
L'OS gère les ressources à l'aide de technique de gestion des mémoires et de partage de ressources
partageable entre plusieurs utilisateurs (ex : threads).
Pour ce faire, il
- connait à tout moment l'utilisateur d'une ressource
- gère les accès à une ressource
- accorde l'usage d'une ressource
- évite les conflits
Les premiers systèmes d'exploitation étaient des systèmes à traitement par lots.
Les programmes étaient regroupés en lots et exécutés les uns après les autres.
Les programmes étaient chargés en mémoire et exécutés sans intervention de l'utilisateur.
## Machine à transistors
- Coûtent cher
- Les utilisateurs ne peuvent pas interagir avec le système
- Les utilisateurs écrivent leurs programmes en Fortran et en assembleur
<b><u>Fonctionnement :</b></u>
1. Programmation d'une carte perforée en Fortran ou en assembleur
2. L'opérateur prend les cartes perforées et les charge dans le compilateur
3. L'opérateur récupère les cartes perforées compilées et les charge dans "l'ordinateur"
4. L'ordinateur exécute le programme
5. S'il y a une erreur, l'opérateur imprime l'image de la mémoire sur un listing et doit tout recommencer.
## Systèmes à traitement par lots
Pour accélérer les opérations, deux méthodes sont utilisées :
- ***Le traitement par lots (batch processing) :*** Regroupe et traite à la suite les travaux similaires
- ***Le moniteurs d'enchaînement (linking monitor) :*** Les travaux sont automatiquement enchaîné et
executé par un programme spécialisé (ancêtre des systèmes d'exploitation)
- Une place mémoire lui est réservée dans la mémoire centrale qui se divise donc en une zone moniteur
et une zone utilisateur
## Sysèmes avec spooling
Les systèmes avec spooling permettent d'accéder simultanément à plusieurs périphériques d'entrée/sortie.
Les travaux sont stockés dans une file d'attente (spool) et sont traités par un programme spécialisé.
Le spooling permet de traiter plusieurs travaux en même temps.
La mémoire est partagée en quatre programmes :
- Le programme d'entrée
- Le programme d'enchaînement
- Le programme de l'utilisateur
- Le programme de sortie
![spooling.png](medias/spooling.png)
## Système à multi-programmation
La multi-programmation permet de partager le processeur entre plusieurs programmes.
Chaque programme est chargé en mémoire et est exécuté en alternance.
Dès qu'un programme doit lire des données ou écrire des résultats, il est mis en attente et un autre
programme est exécuté afin d'optimiser l'utilisation du processeur.
![multiprogramming .png](medias/multiprogramming.png)
La commutation de l'exécution d'un programme à un autre est assuré par l'**ordonnanceur** (scheduler).
*Inconvénients :* Les programmes cours, sont pénalisés, car leur temps d'exécution dépend aussi des autres
programmes en cours d'exécution.
## Système à temps partagé
Le système à temps partagé permet à plusieurs utilisateurs d'accéder simultanément à l'ordinateur.
Chaque utilisateur dispose d'un temps d'utilisation du processeur (time slice) qui est très court (ex. 40 msec).
![time-sharing.png](medias/time-sharing.png)
## Système temps réel
Les systèmes temps réel doivent être capables réagire au rythme où les données arrivent et repartent.
Ils ont des contraîntes plus hautes ques les systèmes à temps partagé, qui peuvent se permettent d'attendre
alors qu'un système temps réel aurait des conséquences fâcheuses.
## Systèmes multi-processeur répartis
Dans un système multi-processeur répartis, chaque processeur dispose de son propre système d'exploitation.
Les systèmes d'exploitation communiquent entre eux pour partager les ressources.
# Mécanisme d'entrées-sorties
Pour accéder aux périphériques, le processeur utilise des mécanismes d'entrées sorties.
## Les registres d'un processeur
Les registres contenus dans le processeur sont regroupés selon leur fonctionnalités
- Registre généraux
- Registre spécialisés
- Le **registre d'instruction** IR (Instruction Register) contient l'instruction courante
- Le **compteur ordinal** PC (Program Counter) contient l'adresse de l'instruction courante
- Le **registre d'état** SR (Status Register) contient des informations sur l'état du processeur
Ces regstres caractérisent le contexte d'un programme. Il s'agit de la partie sauvegardée lors d'un
*switching context* (changement de contexte).
## Les registres d'un périphérique
Les périphériques disposent, pour comuniquer avec le processeur, d'un certain nombre de registres:
- **Registre de données** DR (Data Register) : Contient les données à lire ou à écrire
- **Registre d'état** SR (Status Register) : Contient des informations sur l'état du périphérique
- **Registre de commande** CR (Command Register) : Détermine les caractéristiques du périphérique
## Entrées-sorties en boucle d'attente
Dans ce mode, le processeur attend que le périphérique soit prêt pour effectuer une opération.
Le processeur effectue une boucle d'attente jusqu'à ce que le périphérique soit prêt.
1. Le processeur contrôle le registre d'état du périphérique
2. Si le périphérique n'est pas prêt, le processeur effectue une boucle d'attente. S'il est prêt :
3. Le processeur écrit les données dans le registre de données
4. Le processeur écrit la commande dans le registre de commande
5. Le périphérique effectue l'opération
6. Le périphérique met à jour le registre d'état
7. Le processeur lit les données dans le registre de données
Si le périphérique notifie le processeur :
1. Le périphérique va changer son statut
2. Le processeur va pouvoir aller chercher les données dans le registre de données
## Les interruptions
Les interruptions permettent de signaler au processeur qu'un événement s'est produit.
Le signal est la conséquence d'un évènement interne au programme et résultant de son exécution ou
externe et indépendant de son exécution. Toute l'information nécessaire pour traiter l'interruption
est regroupé en mémoire dans deux mots consécutis, appelés **vecteur d'interruption** (vector interrupt).
On y trouve :
- L'adresse de la routine de traitement de l'interruption
- Le registre d'état du processeur pour cette routine.
Lorsqu'une interruption se produit, le processeur effectue un switching context et exécute la routine.
On distingue en général au moins trois niveaux d'interruption :
- Les interruptions externes
- Les exceptions internes
- Les appels systèmes
![interruptions.png](medias/interruptions.png)
## La DMA
La [DMA](/infotro/SEm/DMA) (Direct Access Memory) permet de faire des transferts vers la mémoire rapidement.
Le processeur laisse le controller DMA accéder au périphérique pour amener les données directement dans la
mémoire et éviter de perdre du temps en passant par le processeur qui doit faire une copie des données.
Elle est très utile lorsqu'il y a un grand nombre de données.
Loaded 100 of 392 files, more files were not shown because too many files have changed in this diff. Show more