Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbc7e0cfd9 |
No files matched your search
@@ -1,3 +0,0 @@
|
|||||||
# Shell scripts and the Justfile must stay LF so they run in Git Bash on Windows.
|
|
||||||
*.sh text eol=lf
|
|
||||||
Justfile text eol=lf
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# Generated site and pinned toolchain
|
|
||||||
public/
|
|
||||||
/tools/
|
|
||||||
|
|
||||||
# Calepin build artifacts
|
|
||||||
.calepin/
|
|
||||||
.calepin-entry.*
|
|
||||||
|
|
||||||
# IDE
|
|
||||||
.idea/
|
|
||||||
.zed/
|
|
||||||
.vscode/
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<!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&§ions.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>
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# 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.
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
# 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
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
# 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.
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<!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&§ions.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>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<!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&§ions.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>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<!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&§ions.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>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<!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&§ions.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>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<!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&§ions.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>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<!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&§ions.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>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<!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&§ions.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>
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<header><h1>
|
|
||||||
Embedded Systems
|
|
||||||
</h1></header>
|
|
||||||
|
|
||||||
----------
|
|
||||||
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
# 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
|
|
||||||
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<header><h1>
|
|
||||||
Nordic - nRF
|
|
||||||
</h1></header>
|
|
||||||
|
|
||||||
----------
|
|
||||||
|
|
||||||
[Install development environment](/Embed/nRF/development_environment.md)
|
|
||||||
@@ -1,260 +0,0 @@
|
|||||||
<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>
|
|
||||||

|
|
||||||
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 -->
|
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 88 KiB |
@@ -1,95 +0,0 @@
|
|||||||
<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"
|
|
||||||
```
|
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<header><h1>
|
|
||||||
Working environment at HES-SO
|
|
||||||
</h1></header>
|
|
||||||
|
|
||||||
- [OneDrive - Linux](HES-SO/OneDrive.md)
|
|
||||||
- [Teams](HES-SO/Teams.md)
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
<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>
|
|
||||||
 <br>
|
|
||||||

|
|
||||||
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>
|
|
||||||
 <br>
|
|
||||||

|
|
||||||
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}"
|
|
||||||
```
|
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 5.7 KiB |
@@ -1,4 +0,0 @@
|
|||||||
# Master of science in engineering in embedded system
|
|
||||||
|
|
||||||
## [SRE - Software Reverse Engineering](/MSE/SRE/)
|
|
||||||
- [Tools](/MSE/SRE/tools.md)
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<header><h1>
|
|
||||||
SRE - Software Reverse Engineering
|
|
||||||
</h1></header>
|
|
||||||
|
|
||||||
- [Tools](/MSE/SRE/tools.md)
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
<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
|
|
||||||
|
|
||||||
```
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
* [SRE - Software Reverse Engineering](/MSE/SRE/)
|
|
||||||
* [Tools](/MSE/SRE/tools.md)
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<header><h1>
|
|
||||||
Security
|
|
||||||
</h1></header>
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
<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
|
|
||||||
```
|
|
||||||
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
* [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)
|
|
||||||
@@ -1,312 +0,0 @@
|
|||||||
#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
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
-----------
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
-----------
|
|
||||||
|
|
||||||
[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
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
-----------
|
|
||||||
|
|
||||||
## 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>
|
|
||||||
```
|
|
||||||
|
Before Width: | Height: | Size: 43 KiB |
@@ -1,605 +0,0 @@
|
|||||||
{
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,365 +0,0 @@
|
|||||||
# docsify-example-panels
|
|
||||||
|
|
||||||
[](https://www.npmjs.com/package/docsify-example-panels)
|
|
||||||
[](https://github.com/docsifyjs/awesome-docsify#plugins)
|
|
||||||
[](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)
|
|
||||||
[](https://github.com/VagnerDomingues/docsify-example-panels/blob/master/LICENSE)
|
|
||||||
[](https://www.jsdelivr.com/package/npm/docsify-example-panels)
|
|
||||||
[](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
|
|
||||||
<!-- panels:start -->
|
|
||||||
|
|
||||||
(...) - Awesome div wrapper
|
|
||||||
|
|
||||||
<!-- panels:end -->
|
|
||||||
```
|
|
||||||
|
|
||||||
<!-- 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
|
|
||||||
<!-- panels:start -->
|
|
||||||
<!-- div:title-panel -->
|
|
||||||
|
|
||||||
(...) - Awesome title
|
|
||||||
|
|
||||||
<!-- div:left-panel -->
|
|
||||||
|
|
||||||
(...) - Awesome explanation
|
|
||||||
|
|
||||||
<!-- div:right-panel -->
|
|
||||||
|
|
||||||
|
|
||||||
(...) - Awesome example
|
|
||||||
|
|
||||||
<!-- panels:end -->
|
|
||||||
```
|
|
||||||
|
|
||||||
<!-- div:left-panel -->
|
|
||||||
|
|
||||||
3. You can add a custom class aswell.
|
|
||||||
|
|
||||||
perphaps you want something like a "floating-cat" class
|
|
||||||
|
|
||||||
<!-- div:right-panel -->
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
<!-- panels:start -->
|
|
||||||
<!-- div:your-custom-class -->
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
<!-- panels:end -->
|
|
||||||
```
|
|
||||||
|
|
||||||
<!-- 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
|
|
||||||
<!-- panels:start -->
|
|
||||||
<!-- div:floating-cat -->
|
|
||||||
|
|
||||||
<img src="path/to/floating-cat.png">
|
|
||||||
|
|
||||||
<!-- panels:end -->
|
|
||||||
```
|
|
||||||
|
|
||||||
<!-- 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>
|
|
||||||
@@ -1,209 +0,0 @@
|
|||||||
<!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>
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
<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 - 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 pesée
|
|
||||||
Un switch par bit.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
----------
|
|
||||||
@@ -1,339 +0,0 @@
|
|||||||
<header><h1>
|
|
||||||
Filtres
|
|
||||||
</h1></header>
|
|
||||||
|
|
||||||
-----------
|
|
||||||
|
|
||||||
|
|
||||||
# Diagramme de Bode
|
|
||||||
|
|
||||||
## H0 - Gain statique
|
|
||||||
$$H_{0}(s)=A$$
|
|
||||||

|
|
||||||
|
|
||||||
## H1 - Coupe bas, amplis haut
|
|
||||||
$$H_{1}(s)=\frac{s}{\omega_{0}}$$
|
|
||||||

|
|
||||||
|
|
||||||
## H2 - Ampli haut, coupe bas
|
|
||||||
$$H_{2}(s)=\frac{\omega_{0}}{s}$$
|
|
||||||

|
|
||||||
|
|
||||||
## H3 - Ampli haut
|
|
||||||
$$H_{3}(s)=\frac{\omega_{0}+1}{\omega_{0}} = 1+ \frac{s}{\omega_{0}}$$
|
|
||||||

|
|
||||||
|
|
||||||
## H4 - Coupe haut
|
|
||||||
$$H_{4}(s)=\frac{\omega_{0}}{\omega_{0}+s} = \frac{1}{1+\frac{s}{\omega_{0}}}$$
|
|
||||||

|
|
||||||
|
|
||||||
## Résonance
|
|
||||||

|
|
||||||
|
|
||||||
----------
|
|
||||||
|
|
||||||
# Filtres numériques
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## 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}$$
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
### 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
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
$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>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----------
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<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)
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
<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
|
|
||||||

|
|
||||||
|
|
||||||
<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}$$
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
# Capacité
|
|
||||||

|
|
||||||
|
|
||||||
<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}$$
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
# Résistance
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
# 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}$
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
# 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
|
|
||||||
-
|
|
||||||

|
|
||||||
|
|
||||||
## 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>
|
|
||||||
 <br>
|
|
||||||

|
|
||||||
|
|
||||||
Normalized value for Butterworth order 1 to 10 <br>
|
|
||||||

|
|
||||||
|
|
||||||
Normalized value for Chebyshev 0.5dB Ripple <br>
|
|
||||||

|
|
||||||
|
|
||||||
Normalized value for Chebyshev 3dB Ripple <br>
|
|
||||||

|
|
||||||
|
|
||||||
## 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} $$
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
@@ -1,256 +0,0 @@
|
|||||||
<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}}$$
|
|
||||||

|
|
||||||
$$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]$.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
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}}$$
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----------
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<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}$ |
|
|
||||||
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
Dans la théorie Eln la partie transformée en Z commence à la slide 23 de "Eln2_LTI_0910_f.pdf".
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
<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.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
# 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.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
# 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.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 78 KiB |
@@ -1,4 +0,0 @@
|
|||||||
<?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="<mxfile host="app.diagrams.net" modified="2024-02-03T22:03:26.442Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0" etag="Da41eLOf3AT8AQXfy7Nj" version="22.1.22"><diagram name="Page-1" id="CkdF-7UHB_tb0CRjxC_V">7V1bc6M2FP41nmkfkkHcbB7XTtLtTDrTaWbb5mkHxzKmxcjFcmLvr68wkgGJq0EGOyQPQQddz/k4+nQkyEibrfe/BPZm9RtaQG+kKov9SHsYqSrQNZX8CSWHSDI2lEjgBO6CZooFL+4PSIUs285dwG0qI0bIw+4mLXxDvg/fcEpmBwH6SGdbIi/d6sZ2oCB4ebM9UfqXu8ArKjUVJb7xFbrOijZtsRtr+5Q5EmxX9gJ9RKJjHu1xpM0ChHB0td7PoBcqj+klqugp5+6pYwH0cZUCT8D2/dmvz1N4mKu+M3/5tvzzzqB9wwc2YLgg46dJFOAVcpBve4+xdBqgnb+AYa0KScV5nhHaECEgwn8gxgdqTHuHERGt8Nqjd7c4QP/CGfJQcGxTmyjh7+kOUzRR0TTqX9ip3GEz/aJd8AYLxkqRiO3Agbggn34yDkE1RGuIgwMpF0DPxu57uh82hZdzyhdbgFxQI9QwCO3ku+3taEsj1fRId6dLRAZMEGzTQZr/7ULoTL/Ndz7exWly5dC/x3LzWEBGpv8YGY+j8fQOjMYPkYRlJP2d84WJLGqXiTm0YLjHhRZdup7H5D7yQ/zYnuv4JPlGLAiJfPoOA+ySR+4LvbF2F4sj0j5WLoYvm2jEH8TBCOhLoCrsZzVMhTkpOIHB0nRMCk0/2WvXC30WVS8be9hXuC/GoogdWmDCnAN1hhp9ZJWP2LXoVLRKOBUmax1t+oC2z4M2Xe8YbVYG2kJIzL8rDBu8wQPX9p1c6yTtIVo+HyYCJnKtSdpzfYdakqb+oKoCRTNWI8tZatpyqtmx5RgbE0x3Z38H12i744PnuZuvjJJEt5/hUq5lwclGbAqYdG1aIFiuZxQQ+osvIZOP0UEkT244zGPzRPXB4e9k4jVM3Bss+bBP3nw40FR1allKGa2GlJEW/R25x1mXOXDeDUw4EESUl5ZKUn++IpOrSOMqigYoVETUbh8S2TZhhm1Bh/XsDuf2qzg/uYh6EKP7pNwGgFf7D3gG6BjDrykISwe0KgXQ47YAPba4iixJgM7psFyAai2y8nBq3v/k/yyQ7/pEmzrfgWMn5nNz0jemlrWki0j2VRK1y5Hs7plY74NxXTMx6QxLs6z7yXlTkgAnsSpJLCtuqSrPEkpImsjMXF+kDr6oyBcZSte+aJxhOs5WDvE9m9zB0w0Ze86yK3WVApS+eegsPPMe2/PczbYCfu3tJtqvWrr70H/XA/SZKCywtWiGrrRcAXriRJTQbHpKbHmz6VwNGhkaZLKGkxj/lJy5puKqAVw1OZNXW3PFZDB6LaNraWupZxodFFcj2ehgIhr5czBeuHdxIsJDUq+0d+F1XChM1A/vlG6FN93irsamDWDdj83zgCkwooy6JAV5jPRBCknc2Oo/9JtBGySAHcNcOrRZAKQ0xGlKeQasSQ5nrR2z5zy8blzWN7OH4hoAmg6Tg04B2hXuzPZ8r7DwquF8W4MfKCeEF1+Adh7dZUq4sRVonh06U3PWDky/VyMVVDgsQUusnrV1Mlg93+o3sQZVzVvnOek1ptISzykl4mZFPhQ9Za0TIqDwmx48ga6MT8Af2700FR9fD0T7s1aUTrHVcYsUO6sy2bCqEHOVTrFB385DqlmnlG+AYk9KmcJF1cy4wzWRrXIVDhS7xOoVVvWD1W+NYms3H+tW2uUvhdN1eQRckUJ/7njXYd2bphL/qMZ52NTrVSsbqxUibBcnRZ3HHbUqAbHrI0VaztKzMzVfXwSqggoHUlRidWOw+ickRVcUd+xPUKdCPDGaqyTEE9sM/4Dzwz91j7eIbZW8lVdWQM6BGC3r2Cd9xVi9xtPivXnFuPPD5NoVHPNruMUixdWVrvS0nCOojV0dd4ip0WESwPlN/cITLYP1DaNPwgZf+6ACRriyT0Hh7MNxmZUpF8ZV7z+c0KKn0dWK7Etvyr6afc8oK3ozfNAowYmu+WVroHFTSfffz2rzVf0Bb33HW+df0NLFNVDP5pybe0UcqPyJfiumHrV5i1ahsrbW/Rp/bCluK79/pWXkrP712/n2AagHbLlvgpVHyyQtIXmg62dTfb3CYrS1R4Zv6xKvgun9jwR3+0qXrIgu79nP/hyV6NYrxjgaA/QioVm99+dPu2MHeQZp7ueEmtrCkD4W9x6skr6VFZEEvKwTqk2+hHYYvoQmbbEk4FfeoRWSjL+bH4Et/u8D2uP/</diagram></mxfile>"><defs><style type="text/css">@import url(https://fonts.googleapis.com/css2?family=Architects+Daughter:wght@400;500);
</style><style xmlns="http://www.w3.org/1999/xhtml" id="MJX-SVG-styles">
mjx-container[jax="SVG"] {
 direction: ltr;
}

mjx-container[jax="SVG"] > svg {
 overflow: visible;
 min-height: 1px;
 min-width: 1px;
}

mjx-container[jax="SVG"] > svg a {
 fill: blue;
 stroke: blue;
}

mjx-container[jax="SVG"][display="true"] {
 display: block;
 text-align: center;
 margin: 1em 0;
}

mjx-container[jax="SVG"][display="true"][width="full"] {
 display: flex;
}

mjx-container[jax="SVG"][justify="left"] {
 text-align: left;
}

mjx-container[jax="SVG"][justify="right"] {
 text-align: right;
}

g[data-mml-node="merror"] > g {
 fill: red;
 stroke: red;
}

g[data-mml-node="merror"] > rect[data-background] {
 fill: yellow;
 stroke: none;
}

g[data-mml-node="mtable"] > line[data-line], svg[data-table] > g > line[data-line] {
 stroke-width: 70px;
 fill: none;
}

g[data-mml-node="mtable"] > rect[data-frame], svg[data-table] > g > rect[data-frame] {
 stroke-width: 70px;
 fill: none;
}

g[data-mml-node="mtable"] > .mjx-dashed, svg[data-table] > g > .mjx-dashed {
 stroke-dasharray: 140;
}

g[data-mml-node="mtable"] > .mjx-dotted, svg[data-table] > g > .mjx-dotted {
 stroke-linecap: round;
 stroke-dasharray: 0,140;
}

g[data-mml-node="mtable"] > g > svg {
 overflow: visible;
}

[jax="SVG"] mjx-tool {
 display: inline-block;
 position: relative;
 width: 0;
 height: 0;
}

[jax="SVG"] mjx-tool > mjx-tip {
 position: absolute;
 top: 0;
 left: 0;
}

mjx-tool > mjx-tip {
 display: inline-block;
 padding: .2em;
 border: 1px solid #888;
 font-size: 70%;
 background-color: #F8F8F8;
 color: black;
 box-shadow: 2px 2px 5px #AAAAAA;
}

g[data-mml-node="maction"][data-toggle] {
 cursor: pLine truncated
|
|
||||||
|
Before Width: | Height: | Size: 82 KiB |
@@ -1,4 +0,0 @@
|
|||||||
<?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="<mxfile host="app.diagrams.net" modified="2024-02-03T22:16:47.416Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0" etag="gL6vpL3MQfkpyT-y1ADy" version="22.1.22"><diagram name="Page-1" id="CkdF-7UHB_tb0CRjxC_V">7Vxtj6M2EP41SO2HXfFmEj5esru9Slup6up6t59OJPESWoJTQjbJ/fqa2A7BQDAsDpBDd7rDg23MPI/H4/EQxZiu9r+Fznr5B1pAX9HVxV4xHhRdt8dj/G8sOBAB0EZE4Ibegoi0RPDi/YBUqFLp1lvATapihJAfeeu0cI6CAM6jlMwJQ7RLV3tDfvqpa8eFGcHL3PGz0q/eIlpSqaWqyY3P0HOX9NE2u7FyTpWJYLN0FmhHRMc6xqNiTEOEInK12k+hH+uO6YV09FRw9zSwEAaRSIMnzQmC6e/PE3iY6YE7e/ny9vedTnp5d/wtfWFFt3zc3+QN4W6xwpw5uWH9t41HOvky2wbRNinjK5f+f2w3SwQm/vtDAY/KaHKnKaMHImEV8UhnfGMsI89l4qPiogNDI4L7WL6JQvQvnCIfhUe5MVbjP/GoPd9n8gAFuNnE8T03wMU51hPE8sk7DCMPI/yJ3lh5i0X8gMlu6UXwZU3eeIfpjGUh2gYLGKsw7n4ZrXx8qVH95I2AjI1xRac1KbE1wMr0nVRafnJWnh9PEape9u7xWOG+EHHtxCM8/yBawSg84CqsgUopodLJZ7DyLqGySUXLMxIzmUMnj3vqOuEXvqAUq0A3c6DbT0Q302qZbnYO3WJOzL6rjBw84qHnBG4hPOeAZKEv5kmGFIVw4ud5gUuhpKW/qKq0fMAbgW6chk5v21Kw9T+D3Z3zXesjeMep53vrz3QI9PYzfJMLrW1xa8C4bWS1DHBwgX0uWkRhtEQuChz/MZFydjGp84zQmir0HxhFB2r4nG2E0ogXw5vVOgwWn2LXMSEHljx58WseH481Hx6+nRde48I9YMWH/fnNhwMtkfeOX/YyltjFdUIXRmWWLYt5CH0n8t7T/echSJv+ibzjsnsyAyZnBngWbNA2nEPa7NzZzPRkpHvSbK4n8oqZnrDincNZtXVcYXNpyFb+kItHdrkBviBjSBh+UvAHSK93n/T1SA33XvSNPi++PmuFS0mjuFB9IhC+XVKsIThjdF3KlBnpDc2YMe+rWx+dMCkr3QSHjQa993gB3/8S/Jpx0qs75NRGD7742ao/6pw/l7f1I754L925K/ri7XtsViF2+oDdReyA2TZ2oxzsOLBcbOnWhW9PI6vOjFVXK2tF69oeJI/QvHvm483aRoDAzmZNAs9v3j5eLaoxuiYNL2CdhaEtLQtQL7vVOtNsegGursOLDmZdDYIcDTLZB31J3gOs6Uly3WhifmRTXuJ4AL0S6Nw+Wa8Juna5G8mgs9duc5UZd83lZTq4sVWmCIbW1Jy3Le22xRFQ4bDMlKCet58cUL/xdcYQMKnXXmda38wYIgawf+sMwbo764zRP4sjoMJhnSlBHQyo/4TrTF7ogmYf6H0MeXYl+6D1eKjZ/ewDeqCaHKK+KmdnqLkHqsnhbXJe+0qf3UxGQulBrNHyQaxm8H4Zf4AqbHr4nkyejZKtj5k1Ll3jqKQMmZaTCZjZL+UwORFrnsM6z2Hbvreshmic11ljWThc4hCwyrJwLjeQk4VjWt2fWPUM+WnR6PjEIauEhImjcv7iuCnjbwi6nlWnjK22MQFGnZ8ANVYA+fmYpRMA2IITwBxLmgCAmwC1MzeNsp5aT0Qz884YP5KIdhgS0aR9FKLz6bZtH8uZ9m3bwLTjkFg96TaQRcbKd4ByTKDKraiGWjv2xKfBj667AWQWuPsUTQcptM5TVHSVlsRQLjhqNsZQk4+uy2ZoThS8Ywy9zRAFi4uXhyjkcHjMx/dBTQpn0nX4jhoLTYD8SVc8ty43kLMzA9lD3J0CJqoCHgbns346NmcmRc/q+T1/Y84nyB4ixzBrA8xNwix6yiQP5uypcbRaDwg3hrDoLlIewv0JJZ9ix9UcnJZCycIOjqRQsj3imMZ/uCzq4PAdGdfeRHY/1lvLRRc+CqmwNW2Qv6YgfYGsPSaXOQN42onvMe2SnqQdHoKqh4fgGh66wJctV/+SrvVoKjv4uLHkU1BwTNPaB4uqgJq7lYYooMIh+bQEdYHPqgbUby351OpBbLWfsVNR30xSdpfGR6PqumY2f45wbbtk9Ieh9fKo2mKo1TJDDd6xtu9ZtkVlI6qNyjuTTdQeZNL20pSKpjsBST9Up/E/u1U3SpPdUQn+UF1jFAWdp2g/P0gQNaWSKGpzTqRRO47Ie6ONxRFxMfn5cVI9+Q134/F/</diagram></mxfile>"><defs><style type="text/css">@import url(https://fonts.googleapis.com/css2?family=Architects+Daughter:wght@400;500);
</style><style xmlns="http://www.w3.org/1999/xhtml" id="MJX-SVG-styles">
mjx-container[jax="SVG"] {
 direction: ltr;
}

mjx-container[jax="SVG"] > svg {
 overflow: visible;
 min-height: 1px;
 min-width: 1px;
}

mjx-container[jax="SVG"] > svg a {
 fill: blue;
 stroke: blue;
}

mjx-container[jax="SVG"][display="true"] {
 display: block;
 text-align: center;
 margin: 1em 0;
}

mjx-container[jax="SVG"][display="true"][width="full"] {
 display: flex;
}

mjx-container[jax="SVG"][justify="left"] {
 text-align: left;
}

mjx-container[jax="SVG"][justify="right"] {
 text-align: right;
}

g[data-mml-node="merror"] > g {
 fill: red;
 stroke: red;
}

g[data-mml-node="merror"] > rect[data-background] {
 fill: yellow;
 stroke: none;
}

g[data-mml-node="mtable"] > line[data-line], svg[data-table] > g > line[data-line] {
 stroke-width: 70px;
 fill: none;
}

g[data-mml-node="mtable"] > rect[data-frame], svg[data-table] > g > rect[data-frame] {
 stroke-width: 70px;
 fill: none;
}

g[data-mml-node="mtable"] > .mjx-dashed, svg[data-table] > g > .mjx-dashed {
 stroke-dasharray: 140;
}

g[data-mml-node="mtable"] > .mjx-dotted, svg[data-table] > g > .mjx-dotted {
 stroke-linecap: round;
 stroke-dasharray: 0,140;
}

g[data-mml-node="mtable"] > g > svg {
 overflow: visible;
}

[jax="SVG"] mjx-tool {
 display: inline-block;
 position: relative;
 width: 0;
 height: 0;
}

[jax="SVG"] mjx-tool > mjx-tip {
 position: absolute;
 top: 0;
 left: 0;
}

mjx-tool > mjx-tip {
 display: inline-block;
 padding: .2em;
 border: 1px solid #888;
 font-size: 70%;
 background-color: #F8F8F8;
 color: black;
 box-shadow: 2px 2px 5px #AAAAAA;
}

g[data-mml-node="maction"][data-toggle] {
 cursor: pLine truncated
|
|
||||||
|
Before Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 143 KiB |
|
Before Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 65 KiB |
@@ -1,4 +0,0 @@
|
|||||||
<?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="<mxfile host="app.diagrams.net" modified="2024-02-02T17:56:41.416Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0" etag="i95P2xGId2tSeVqMwiar" version="23.1.1" scale="1" border="0">
 <diagram name="Page-1" id="ASSYr2psEjrnozlwjsYB">
 <mxGraphModel dx="1050" dy="626" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="1" shadow="0">
 <root>
 <mxCell id="0" />
 <mxCell id="1" parent="0" />
 <mxCell id="hvOSe9N11qCTzQnM01ZF-1" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=none;strokeColor=#808080;" vertex="1" parent="1">
 <mxGeometry x="360" y="280" width="160" height="160" as="geometry" />
 </mxCell>
 <mxCell id="hvOSe9N11qCTzQnM01ZF-2" value="" style="endArrow=classic;html=1;rounded=0;strokeColor=#808080;" edge="1" parent="1">
 <mxGeometry width="50" height="50" relative="1" as="geometry">
 <mxPoint x="439.5" y="480" as="sourcePoint" />
 <mxPoint x="440" y="240" as="targetPoint" />
 </mxGeometry>
 </mxCell>
 <mxCell id="hvOSe9N11qCTzQnM01ZF-3" value="" style="endArrow=classic;html=1;rounded=0;strokeColor=#808080;" edge="1" parent="1">
 <mxGeometry width="50" height="50" relative="1" as="geometry">
 <mxPoint x="320" y="360" as="sourcePoint" />
 <mxPoint x="560" y="360" as="targetPoint" />
 </mxGeometry>
 </mxCell>
 <mxCell id="hvOSe9N11qCTzQnM01ZF-4" value="1" style="text;strokeColor=none;fillColor=none;html=1;fontSize=20;fontStyle=1;verticalAlign=bottom;align=left;fontColor=#808080;" vertex="1" parent="1">
 <mxGeometry x="520" y="320" width="40" height="40" as="geometry" />
 </mxCell>
 <mxCell id="hvOSe9N11qCTzQnM01ZF-5" value="1" style="text;strokeColor=none;fillColor=none;html=1;fontSize=20;fontStyle=1;verticalAlign=bottom;align=left;fontColor=#808080;" vertex="1" parent="1">
 <mxGeometry x="440" y="240" width="40" height="40" as="geometry" />
 </mxCell>
 <mxCell id="hvOSe9N11qCTzQnM01ZF-7" value="&lt;div align=&quot;center&quot;&gt;Z&lt;/div&gt;" style="text;strokeColor=#808080;fillColor=none;html=1;fontSize=20;fontStyle=1;verticalAlign=middle;align=center;fontColor=#808080;" vertex="1" parent="1">
 <mxGeometry x="530" y="240" width="30" height="40" as="geometry" />
 </mxCell>
 <mxCell id="hvOSe9N11qCTzQnM01ZF-8" value="$$\mathcal{Z} = e^{\mathbf{j}\omega}$$" style="text;strokeColor=none;fillColor=none;fontSize=20;fontStyle=1;verticalAlign=middle;align=center;fontColor=#808080;html=1;" vertex="1" parent="1">
 <mxGeometry x="280" y="240" width="120" height="40" as="geometry" />
 </mxCell>
 </root>
 </mxGraphModel>
 </diagram>
</mxfile>
"><defs><style xmlns="http://www.w3.org/1999/xhtml" id="MJX-SVG-styles">
mjx-container[jax="SVG"] {
 direction: ltr;
}

mjx-container[jax="SVG"] > svg {
 overflow: visible;
 min-height: 1px;
 min-width: 1px;
}

mjx-container[jax="SVG"] > svg a {
 fill: blue;
 stroke: blue;
}

mjx-container[jax="SVG"][display="true"] {
 display: block;
 text-align: center;
 margin: 1em 0;
}

Line truncated
|
|
||||||
|
Before Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 55 KiB |