Sascha Schulz
2025-03-18 8f4523cd263d173cc70ccea1a7858b66eaa87cfe
index.html
@@ -36,9 +36,16 @@
                  <ul>
                     <li>JavaScript</li>
                     <li>Responsive Design</li>
                     <li>NodeJS</li>
                     <li>WebComponents / lit</li>
                     <li>NodeJS</li>
                     <li>Datenbank</li>
                  </ul>
               </section>
               <section>
                  <h3>Betrieb</h3>
                  <ul>
                     <li>Server einrichten</li>
                     <li>SSL- / TLS-Zertifikate</li>
                  </ul>
               </section>
               <section>
@@ -66,13 +73,6 @@
                     <li>Progressive Web Application</li>
                     <li>CEF / Electron</li>
                     <li>WebAssembly</li>
                  </ul>
               </section>
               <section>
                  <h3>Betrieb</h3>
                  <ul>
                     <li>Server einrichten</li>
                     <li>SSL- / TLS-Zertifikate</li>
                  </ul>
               </section>
               <section>
@@ -2386,6 +2386,2585 @@
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Map</h3>
                  <ul>
                     <li>auch als "assoziatives Array" bezeichnet</li>
                     <li>iterierbare Liste</li>
                     <li>ideal zur Assoziation von z.B. Hashes zu Objekt-Instanzen o.ä.</li>
                     <li>nutzbar ab es6 / es2015</li>
                     <li>löst herkömmliche Objekte als Hash-Map ab</li>
                  </ul>
               </section>
               <section>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        const m = new Map();
                        m.set("a", 0);
                        m.set("a", 1);
                        m.set("b", 2);
                        m.has("c"); // => false
                        m.delete("b");
                        console.log(m.get("a")); // => 1
                     </code>
                  </pre>
               </section>
               <section>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        m.forEach((k, v) => console.log(k, v)) // => "a" 1 ...
                        for (const entry of m.entries()) {
                           console.log(entry); // => ["a", 1] ...
                        }
                        for (const key of m.keys()) {
                           console.log(key); // => "a" ...
                        }
                        for (const value of m.values()) {
                           console.log(value); // => "1" ...
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Set</h3>
                  <ul>
                     <li>enthält nur einzigartige Werte</li>
                     <li>iterierbare Liste</li>
                     <li>ideale Struktur, wenn einzelne Werte einzigartig sein sollen</li>
                     <li>nutzbar ab es6 / es2015</li>
                     <li>löst individuelle Lösungen mittels Arrays und Array.prototype.indexOf o.ä. ab</li>
                  </ul>
               </section>
               <section>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        const s = new Set();
                        s.add("a");
                        s.add("a");
                        s.has("c"); // => false
                        s.delete("a");
                        console.log(s.size); // => 0
                     </code>
                  </pre>
               </section>
               <section>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        s.forEach((k, v) => console.log(k, v)) // => "a" "a" ...
                        for (const entry of m.entries()) {
                           console.log(entry); // => ["a", "a"] ...
                        }
                        for (const key of m.keys()) {
                           console.log(key); // => "a"...
                        }
                        for (const value of s.values()) {
                           console.log(value); // => "a" ...
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>DOM-Events</h3>
                  <ul>
                     <li>funktionale Ausführung von Code</li>
                     <li>Trigger können Klicks, Scrollen, Tastendrücke, Änderungen der Fenstergröße usw. sein</li>
                  </ul>
               </section>
               <section>
                  <img data-src="/assets/images/dom-events.svg">
               </section>
               <section>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        function doSomething(e) {
                           console.log("capture");
                        }
                        function click(e) {
                           console.log("bubble");
                        }
                        div.addEventListener("click", doSomething, true);
                        button.addEventListener("click", click, false /* default */);
                     </code>
                  </pre>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        capturing
                        bubble
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Aufgabe: Erweitere das vorherige Beispiel so, dass das div-Element einen weiteren Click-Handler bekommt,
                  der den Text "bubble2" anzeigt, nachdem "bubble" in der Konsole geloggt wurde.</p>
                  <p>Erwartete Ausgabe:</p>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        capturing
                        bubble
                        bubble2
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Lösung:</p>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        function doSomething(e) {
                           console.log("capture");
                        }
                        // neu
                        function doSomething2(e) {
                           console.log("bubble2");
                        }
                        function click(e) {
                           console.log("bubble");
                        }
                        div.addEventListener("click", doSomething, true);
                        div.addEventListener("click", doSomething2, false); // neu
                        button.addEventListener("click", click, false /* default */);
                     </code>
                  </pre>
               </section>
               <section>
                  Die Bubble-Phase stoppen
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        function click(e) {
                           e.stopPropagation()
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Aufgabe: Ändere das Ergebnis des vorherigen Beispiels, sodass die Bubble-Phase auf Ebene des button-Elements gestoppt wird. Was ändert sich in der Ausgabe?</p>
               </section>
               <section>
                  <p>Lösung:</p>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        function doSomething(e) {
                           console.log("capture");
                        }
                        function doSomething2(e) {
                           console.log("bubble2");
                        }
                        function click(e) {
                           e.stopPropagation();
                           console.log("bubble");
                        }
                        div.addEventListener("click", doSomething, true);
                        div.addEventListener("click", doSomething2, false);
                        button.addEventListener("click", click, false /* default */);
                     </code>
                  </pre>
               </section>
               <section>
                  Events programatisch triggern:
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        button.dispatchEvent(new Event("click"));
                     </code>
                  </pre>
               </section>
               <section>
                  Events sind synchron:
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        doSomethingElse();
                        button.dispatchEvent(new Event("click"));
                        doAnotherThing(); // wird erst ausgeführt, wenn zuvor alle Event-Handler fertig sind
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Strings</h3>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        const empty1 = "";
                        const empty1 = '';
                        const s1 = 'abc';
                        const s2 = "abc";
                        const s3 = 'a"b"c';
                        const s4 = "a\"b\"c";
                        const s5 = 'a\'b\'c';
                        const s6 = "a'b'c";
                        const s7 = `a'b'"c"`;
                     </code>
                  </pre>
               </section>
               <section>
                  <h5>Strings zusammenfügen</h5>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        const age = 10;
                        // Konkatenation
                        const s = "My age is " + number + "!";
                        // Interpolation
                        const s = `My age is ${number}!`;
                        // => "My age is 10!"
                     </code>
                  </pre>
               </section>
               <section>
                  <h5>Template Strings</h5>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        function sayHello() {
                           return "Hello";
                        }
                        let interpolation = `${sayHello()}`;
                        // => "Hello"
                        interpolation = `5 + 5 = ${5+5}`;
                        // => "5 + 5 = 10"
                     </code>
                  </pre>
               </section>
               <section>
                  lit
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        return html`<div>hello world!</div>`
                     </code>
                  </pre>
               </section>
               <section>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        function myTag() {
                           console.log(arguments);
                           // return 5;
                           // return true;
                           return "abc";
                        }
                        let interpolation = myTag`a ${5} b ${6} c`;
                        // => "abc"
                     </code>
                  </pre>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        Arguments(3) [Array(3), 5, 6]
                           0 : (3) ['a ', ' b ', ' c', raw: Array(3)]
                           1 : 5
                           2 : 6
                     </code>
                  </pre>
               </section>
               <section>
                  <h5>Aufgabe</h5>
                  <p>Schreibe ein Tag, welches die Zahlen aus den dynamischen Ausdrücken zusammenrechnet (<code>strings.js</code>)</p>
               </section>
               <section>
                  <h5>Lösung</h5>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        function add(parts, ...numbers) {
                           return numbers.reduce((p, c) => p + c, 0);
                        }
                        let sum = add`a ${5} b ${6} c`;
                        console.log(sum);
                        // => 11
                     </code>
                  </pre>
               </section>
               <section>
                  <h5>Rest-Operator</h5>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        function add(...numbers) {
                           return numbers.reduce((p, c) => p + c, 0);
                        }
                        add(1, 2, 3); // => 6
                     </code>
                  </pre>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        const o1 = {a: 1, b: 2};
                        const o2 = {x: 5, y: 6, ...o1};
                     </code>
                  </pre>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        const a1 = [4, 5, 6];
                        const a2 = [1, 2, 3, ...a1];
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Relevante Browser-APIs</h3>
               </section>
               <section>
                  <h5>window</h5>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        window.innerWidth; // innere Auflösung des sichtbaren Fensters
                        window.innerHeight;
                        window.outerWidth; // äußere Auflösung des Browserfensters inklusive Adresszeile usw.
                        window.outerHeight;
                        window.addEventListener(); // Events an das äußerste "Dach"-Element hängen
                     </code>
                  </pre>
               </section>
               <section>
                  <h5>location</h5>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        location.href; // aktuelle URL
                        location.host; // aktuelle Domain, z.B. "www.google.de"
                        location.protocol; // aktuelles Protokoll, z.B. "https:"
                     </code>
                  </pre>
               </section>
               <section>
                  <h5>Navigator</h5>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        navigator.language; // bevorzugte Browsersprache, z.B. "de"
                        navigator.userAgent; // z.B. "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) [...]"
                        navigator
                           .mediaDevices
                           .getDisplayMedia(); // Fordere Erlaubis zum Streamen von Anwendungen oder Bildschirmen an
                     </code>
                  </pre>
               </section>
               <section>
                  <h5>Document</h5>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        document.body; // &lt;body>-Element der Seite
                        document.activeElement; // aktives Element der Seite, welche gerade den Fokus besitzt
                        document.cookie; // für die aktuelle Seite gesetzte Cookies
                        document.execCommand("copy"); // Texte in die Zwischenablage zu schreiben
                        document.createElement("div"); // erzeuge neues DOM-Element mit angegebenen Tag
                     </code>
                  </pre>
               </section>
               <section>
                  <h5>LocalStorage</h5>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        // auf 5MB Daten pro Domain begrenzt
                        localStorage.getItem(key); // Element holen
                        localStorage.setItem(key, value); // Element setzen
                        localStorage.removeItem(key); // Element entfernen
                     </code>
                  </pre>
               </section>
            </section>
            <section>
               <section>
                  <h2>CSS</h2>
                  <img data-src="/assets/images/css-sucks.png">
               </section>
               <section>
                  <h3>Warum CSS?</h3>
                  <ul>
                     <li>Erhöhte Zugänglichkeit des Inhalts</li>
                     <li>Wiederverwendbarkeit</li>
                  </ul>
               </section>
               <section>
                  <h3>Syntax</h3>
                  <pre>
                     <code class="css" data-trim data-line-numbers>
                        selector [, selector2, selector3, ...] {
                           property1: value;
                           property2: value;
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Beispiel</h3>
                  <pre>
                     <code class="css" data-trim data-line-numbers>
                        html, body {
                           width: 100%;
                           height: 100%;
                           margin: 0;
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Selektoren</h3>
                  <pre>
                     <code class="css" data-trim data-line-numbers>
                        /* Elemente */
                        html, body {...}
                        /* IDs */
                        #my-id {...}
                        /* Klassen */
                        .my-class {...}
                        /* Pseudo-Klassen */
                        div:hover {...}
                        /* Attribute */
                        div[my-attribut="abc"] {...}
                        /* Kombinationen */
                        div.my-class#my-id {...}
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Variablen</h3>
                  <pre>
                     <code class="css" data-trim data-line-numbers>
                        html {
                           --my-background-color: #abcdef;
                        }
                        html div {
                           background-color: var(--my-background-color, white);
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Das <code>display</code>-Attribut</h3>
               </section>
               <section>
                  <p>block</p>
                  <img data-src="/assets/images/css-display-block.svg">
                  <ul>
                     <li>untereinander angeordnet</li>
                     <li>nehmen sich die volle Breite</li>
                     <li><code>width</code> und <code>height</code> möglich</li>
                  </ul>
               </section>
               <section>
                  <p>inline</p>
                  <img data-src="/assets/images/css-display-inline.svg">
                  <ul>
                     <li>Standard bei <code>Custom Elements</code></li>
                     <li>nur so groß wie der content</li>
                     <li><code>width</code> und <code>height</code> nicht möglich</li>
                     <li>nebeneinander angeordnet</li>
                  </ul>
               </section>
               <section>
                  <p>inline-block</p>
                  <img data-src="/assets/images/css-display-inline-block.svg">
                  <ul>
                     <li>Mischform von <code>block</code> und <code>inline</code></li>
                     <li>nur so groß wie der content</li>
                     <li><code>width</code> und <code>height</code> möglich</li>
                     <li>nebeneinander angeordnet</li>
                  </ul>
               </section>
               <section>
                  <p>none</p>
                  <img data-src="/assets/images/css-display-none.svg">
                  <ul>
                     <li>unsichtbar</li>
                     <li>Platz wird nicht länger beansprucht, so als wenn das Element nicht im DOM wäre</li>
                  </ul>
               </section>
               <section>
                  <h3>Das <code>position</code>-Attribut</h3>
               </section>
               <section>
                  <p>static</p>
                  <img data-src="/assets/images/css-position-static.svg">
                  <ul>
                     <li>Standard</li>
                     <li>Element wird am Ort der Verankerung im HTML dargestellt</li>
                  </ul>
               </section>
               <section>
                  <p>relative</p>
                  <img data-src="/assets/images/css-position-relative.svg">
                  <ul>
                     <li>Verschiebung zur eigentlichen <code>static</code>-Position, z.B. mittels <code>left: 20px</code></li>
                  </ul>
               </section>
               <section>
                  <p>absolut</p>
                  <img data-src="/assets/images/css-position-absolute.svg">
                  <ul>
                     <li>relative Verschiebung zum nächsten nicht-<code>static</code>-Parent (sonst <code>window</code>)</li>
                  </ul>
               </section>
               <section>
                  <h3>Aufgabe</h3>
                  <p>Implementiere folgendes Beispiel-Menü</p>
                  <iframe data-src="/assets/html/css-menu.html"></iframe>
               </section>
               <section>
                  <h3>Lösung</h3>
                  <pre>
                     <code class="css" data-trim data-line-numbers>
                        .menu-item {
                           display: inline-block;
                           position: relative;
                        }
                        .menu-item .children {
                           display: none;
                           position: absolute;
                        }
                        .menu-item:hover .children {
                           display: block;
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <pre>
                     <code class="html" data-trim data-line-numbers>
                        <script type="text/template">
                           <div class="menu">
                              <div class="menu-item">Eins</div>
                              <div class="menu-item">
                                 Hover Me!
                                 <div class="children">
                                    <div>Item 1</div>
                                    <div>Item 2</div>
                                 </div>
                              </div>
                              <div class="menu-item">Drei</div>
                           </div>
                        </script>
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Viewport</h3>
                  <pre>
                     <code class="html" data-trim data-line-numbers>
                        &lt;!DOCTYPE html>
                        &lt;html>
                           &lt;head>
                              &lt;meta charset="utf-8">&lt;/meta>
                              &lt;meta
                                 name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
                              >
                           &lt;/head>
                           &lt;body>
                              ...
                           &lt;/body>
                        &lt;/html>
                     </code>
                  </pre>
                  <p>Ein Muss für alle responsiven Seiten</p>
               </section>
               <section>
                  <pre>
                     <code class="bash" data-trim data-line-numbers="">
                        npm install -g http-server
                        cd pfad/zum/projekt
                        http-server --port 8080
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Einheiten</h3>
                  <pre>
                     <code class="css" data-trim data-line-numbers="">
                        div {
                           width: 1px; /* Pixel */
                           width: 1pt; /* Points */
                           width: 1em; /* Breite des "M" in der Schriftgröße des Elements */
                           width: 1rem; /* Breite des "M" in der Schriftgröße des HTML-Elements */
                           width: 1vw; /* Prozent der View Width */
                           width: 1%; /* Prozent des Elternelements */
                           width: 5fr; /* Fraction (nur im CSS Grid verfügbar) */
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Aufgabe</h3>
                  <p>Größeneinheiten in einer HTML-Datei ausprobieren</p>
               </section>
               <section>
                  <h3>CSS Flexbox</h3>
                  <pre>
                     <code class="css" data-trim data-line-numbers="">
                        .container {
                           display: flex; /* soll ein flexibler Container sein */
                           display: inline-flex;
                           flex-direction: column; /* Kinder spaltenweise anordnen */
                           flex-direction: row; /* Kinder zeilenweise anordnen */
                           align-items: center;
                           justify-content: center;
                           gap: 20px; /* Abstände zwischen den Achsen */
                        }
                        .child {
                           flex-grow: 1; /* Anteiliger Platz, wenn mehr als nötig da ist */
                           flex-shrink: 1; /* Anteiliger Platz, wenn weniger als nötig da ist */
                           flex-basis: 20px; /* Grundlage zur Berechnung der finalen Größe */
                           order: 1; /* Erlaubt die Darstellung in anderer Reihenfolge als durch das HTML vorgegeben */
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <img data-src="/assets/images/css-flexbox-axes.drawio.svg">
               </section>
               <section>
                  <h3>Aufgabe</h3>
                  <p>Mit CSS Flexbox rumspielen</p>
               </section>
               <section>
                  <h3>CSS Grid</h3>
               </section>
               <section>
                  <h3>Terminologie</h3>
                  <img data-src="/assets/images/css-grid-terminology.drawio.svg">
               </section>
               <section>
                  <pre>
                     <code class="css" data-trim data-line-numbers="">
                        .grid {
                           display: grid; /* inline-grid; */
                           grid-template-columns: 1fr 1fr 1fr;
                           grid-template-rows: 1fr 1fr;
                           gap: 5px 10px;
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <pre>
                     <code class="html" data-trim data-line-numbers="">
                        &lt;div class="grid"&gt;
                           &lt;span&gt;A&lt;/span&gt;
                           &lt;span&gt;B&lt;/span&gt;
                           &lt;span&gt;C&lt;/span&gt;
                           &lt;span&gt;D&lt;/span&gt;
                           &lt;span&gt;E&lt;/span&gt;
                           &lt;span&gt;F&lt;/span&gt;
                        &lt;/div&gt;
                     </code>
                  </pre>
               </section>
               <section>
                  <iframe data-src="/assets/html/css-grid-example-1.html"></iframe>
               </section>
               <section>
                  <pre>
                     <code class="css" data-trim data-line-numbers="">
                        .grid {
                           grid-template-columns: [left] 1fr [column1] 1fr [column2] 1fr [right];
                           grid-template-rows: [top] 1fr [row1] 1fr [bottom];
                        }
                        .a {
                           grid-column: 2 / span 2;
                           grid-row: 1;
                        }
                        .b {
                           grid-column: left / column2;
                           grid-row: 2;
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <pre>
                     <code class="html" data-trim data-line-numbers="">
                        &lt;div class="grid"&gt;
                           &lt;span class="a"&gt;A&lt;/span&gt;
                           &lt;span class="b"&gt;B&lt;/span&gt;
                        &lt;/div&gt;
                     </code>
                  </pre>
               </section>
               <section>
                  <iframe data-src="/assets/html/css-grid-example-2.html"></iframe>
               </section>
               <section>
                  <pre>
                     <code class="css" data-trim data-line-numbers="">
                        .grid {
                           grid-template-areas:
                              "header header header"
                              "content content content";
                        }
                        .a {
                           grid-area: header;
                        }
                        .b {
                           grid-area: 2 / 1 / 2 / 3; /* start-row / start-column / end-row / end-column */
                              /* alternativ auch Namen der Lines möglich */
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <iframe data-src="/assets/html/css-grid-example-3.html"></iframe>
               </section>
               <section>
                  <h3>Aufgabe</h3>
                  <p>Implementiere folgendes Design:</p>
                  <iframe data-src="/assets/html/css-grid-excercise.html"></iframe>
               </section>
               <section>
                  <h3>Lösung</h3>
                  <pre>
                     <code class="css" data-trim data-line-numbers="">
                        .grid {
                           display: grid;
                           grid-template-rows: 1fr 5fr 1fr;
                           gap: 2px;
                           grid-template-areas:
                              "navigation header header header"
                              "navigation content content content"
                              "footer footer footer footer"
                        }
                        .navigation {
                           grid-area: navigation;
                        }
                        .header {
                           grid-area: header;
                        }
                        .content {
                           grid-area: content;
                        }
                        .footer {
                           grid-area: footer;
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <pre>
                     <code class="html" data-trim data-line-numbers="">
                        &lt;div class="grid"&gt;
                           &lt;div class="area navigation"&gt;Navigation&lt;/div&gt;
                           &lt;div class="area header"&gt;Header&lt;/div&gt;
                           &lt;div class="area content"&gt;Content&lt;/div&gt;
                           &lt;div class="area footer"&gt;Footer&lt;/div&gt;
                        &lt;/div&gt;
                     </code>
                  </pre>
               </section>
               <section>
                  <h4>Platzierug des Inhalts</h4>
                  <pre>
                     <code class="css" data-trim data-line-numbers="">
                        .grid {
                           align-items: center;
                           justify-items: center;
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <iframe data-src="/assets/html/css-grid-example-4.html"></iframe>
               </section>
               <section>
                  <h3>Media Queries</h3>
                  <ul>
                     <li>"Medienabfragen"</li>
                     <li>Styles auf Basis von Eigenschaften des Ausgabemediums</li>
                     <li>Große Bildschirme, kleine Bildschirme, Printmedien...</li>
                     <li>Hinweis: Sollten immer als letztes (unten) in einem Stylesheet sein</li>
                  </ul>
               </section>
               <section>
                  <h3>Syntax</h3>
                  <pre>
                     <code class="css" data-trim data-line-numbers>
                        @media screen and (min-width: 640px) and (max-width: 1024px) {
                           .navigation {
                              display: none;
                           }
                        }
                     </code>
                  </pre>
                  <p>Bedeutung: Wenn die Seite auf einem Display angezeigt wird, welches zwischen 640px und 1024px in der Breite anzeigen kann, dann blende die Navigation aus</p>
               </section>
               <section>
                  <h3>Werte für die Ausgabemedien (optional, Standard ist "all")</h3>
                  <ul>
                     <li>all</li>
                     <li>screen</li>
                     <li>print</li>
                  </ul>
               </section>
               <section>
                  <h3>Werte für die Eigenschaften mit Typ und Erklärung (kleiner Auszug)</h3>
                  <ul>
                     <li>width (Number, Breite)</li>
                     <li>height (Number, Höhe)</li>
                     <li>orientation (portrait | landscape, Quer- oder Hochformat)</li>
                     <li>hover (none | hover, Hat Zeiger zum Hovern)</li>
                     <li>forced-colors (none | active, erzwungene Farbpalette z.B. bei Sehbehinderung)</li>
                  </ul>
               </section>
               <section>
                  <h3>Aufgabe</h3>
                  <p>Entwerfe eine kleine Seite mit @media und zeige ein beliebiges Element ab einer bestimmten Höhe oder Breite an (die Größe des Displays kann dynamisch mit den Entwicklertools im Gerätesimulator geändert werden)</p>
               </section>
               <section>
                  <h3>Aufgabe</h3>
                  <p>Passe die Vorlage "media-queries-responsive-layout.html" mit @media-Queries so an, dass das Menü links ab einer maximalen Breite von 480 Pixel und kleiner nach links oben in die Ecke minimiert wird und nur per Hover wieder 50% Breite und 100% Höhe bekommt</p>
               </section>
               <section>
                  <img data-src="/assets/images/media-queries-excercise.png">
               </section>
               <section>
                  <h3>Lösung</h3>
                  <pre>
                     <code class="css" data-trim data-line-numbers>
                        @media screen and (max-width: 480px) {
                           .menu {
                              grid-area: unset;
                              position: absolute;
                           }
                           .menu:hover {
                              height: 100%;
                              width: 50%;
                           }
                           .grid {
                              grid-template-columns: 0fr 5fr;
                           }
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Transform</h3>
                  <ul>
                     <li>rein optisch, d.h. "physische" Größe bleibt erhalten</li>
                     <li>eine Art "Shader" auf Grafikebene</li>
                     <li>Effekte wie Drehung, Vergrößerung, Verschiebung usw.</li>
                  </ul>
               </section>
               <section>
                  <h3>Syntax</h3>
                  <pre>
                     <code class="css" data-trim data-line-numbers>
                        .foo {
                           transform: translateX(20px) translateY(20px);
                           transform-origin: left;
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Mögliche Effekte</h3>
                  <ul>
                     <li>translate</li>
                     <li>rotate</li>
                     <li>scale</li>
                     <li>scew</li>
                  </ul>
                  <p>Die meisten besitzen zusätzlich spezifischere X-, Y- oder 3D-Versionen</p>
               </section>
               <section>
                  <h3>Aufgabe</h3>
                  <p>Probiere die genannten Transform-Effekte in einer beliebigen Seite aus</p>
               </section>
               <section>
                  <h3>Transition</h3>
                  <ul>
                     <li>Elemente können "weich" verändert werden</li>
                     <li>Mögliche Attribute, deren Änderungen animiert werden können sind z.B. <code>width</code>, <code>height</code>, <code>transform</code>, <code>color</code> und viele weitere</li>
                     <li>Das Attribut <code>display</code> lässt sich jedoch nicht animieren</li>
                  </ul>
               </section>
               <section>
                  <h3>Syntax</h3>
                  <pre>
                     <code class="css" data-trim data-line-numbers>
                        .foo {
                           width: 100%;
                           background-color: red;
                           /* transition: [Attribut] [Dauer] [Zeitfunktion] */
                           transition: width 1s ease-out;
                        }
                        .foo:hover {
                           width: 50%;
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Beispiel</h3>
                  <iframe data-src="/assets/html/css-transition.html"></iframe>
               </section>
               <section>
                  <h3>Animation</h3>
                  <ul>
                     <li>Es können zusätzlich eigene Animationen entworfen werden</li>
                     <li>Definition per <code>Keyframes</code></li>
                  </ul>
               </section>
               <section>
                  <h3>Syntax</h3>
                  <pre>
                     <code class="css" data-trim data-line-numbers>
                        .foo {
                          animation-duration: 3s;
                          animation-name: slidein;
                        }
                        @keyframes slidein {
                          from {
                           margin-left: 100%;
                           width: 300%;
                          }
                          to {
                           margin-left: 0%;
                           width: 100%;
                          }
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Alternative Syntax</h3>
                  <pre>
                     <code class="css" data-trim data-line-numbers>
                        .foo {
                          /* ... */
                        }
                        @keyframes slidein {
                          1% {
                           margin-left: 100%;
                          }
                          100% {
                           margin-left: 0%;
                          }
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Beispiel</h3>
                  <iframe data-src="/assets/html/css-animation.html"></iframe>
               </section>
               <section>
                  <h3>3D Transform</h3>
                  <ul>
                     <li>Neben den gewohnten X- und Y-Achsen (Breite und Höhe) gibt es auch noch die Z-Achse</li>
                     <li>Bewegung entlang der Sichtachse, d.h. zum bzw. vom Betrachter weg</li>
                     <li>Perspektivische Effekte möglich</li>
                  </ul>
               </section>
               <section>
                  <h3>Beispiel</h3>
                  <iframe data-src="/assets/html/css-transform-3d.html"></iframe>
               </section>
               <section>
                  <h3>Bedingungen / Optionen</h3>
                  <pre>
                     <code class="css" data-trim data-line-numbers>
                        .container {
                           perspective: 1000px; /* "Entfernung" des Betrachters */
                           transform-style: preserve-3d; /* oder "flat" */
                        }
                        .element {
                           backface-visibility: hidden; /* oder "visible" */
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Beispiel</h3>
                  <iframe data-src="/assets/html/css-transform-3d-cube.html"></iframe>
               </section>
               <section>
                  <h3>Aufgabe</h3>
                  <p>Implementiere einen drehenden Würfel (Vorlage "templates/008-css/css-3d-cube.html")</p>
               </section>
            </section>
            <section>
               <section>
                  <h2>Web Components</h2>
               </section>
               <section>
                  <p>Was sind Web Components?</p>
                  <img data-src="/assets/images/web-components.svg">
               </section>
               <section>
                  <p>Vorteile</p>
                  <ul>
                     <li>Entwicklung eigener HTML-Elemente, welche sich nahtlos neben nativen Elementen nutzen lassen</li>
                     <li>Hohe Wiederverwendbarkeit in mehreren Projekten</li>
                     <li>Keine Libraries oder Frameworks notwendig, um eigene Elemente zu entwickeln</li>
                     <li>Kapselung des Scopes sowohl von JS (Variablen...) als auch CSS (id, class...)</li>
                  </ul>
               </section>
               <section>
                  <h3>Ansätze in der Vergangenheit und Alternativen heute</h3>
               </section>
               <section>
                  <h4>jQuery</h4>
                  <a href="https://api.jqueryui.com/accordion/#entry-examples" target="_blank">https://api.jqueryui.com/accordion/#entry-examples</a>
               </section>
               <section>
                  <pre>
                     <code class="html" data-trim data-line-numbers>
                        <script type="text/template">
                           <div id="accordion" class="ui-accordion ui-widget ui-helper-reset" role="tablist">
                              <h3 class="ui-accordion-header ui-corner-top ui-state-default ui-accordion-icons ui-accordion-header-active ui-state-active" role="tab" id="ui-id-1" aria-controls="ui-id-2" aria-selected="true" aria-expanded="true" tabindex="0">
                                 <span class="ui-accordion-header-icon ui-icon ui-icon-triangle-1-s"></span>Section 1</h3>
                              <div class="ui-accordion-content ui-corner-bottom ui-helper-reset ui-widget-content ui-accordion-content-active" id="ui-id-2" aria-labelledby="ui-id-1" role="tabpanel" aria-hidden="false" style="display: block; height: 166.8px;">
                                 <p>Mauris mauris ante, blandit et, ultrices a, suscipit eget.
                                    Integer ut neque. Vivamus nisi metus, molestie vel, gravida in,
                                    condimentum sit amet, nunc. Nam a nibh. Donec suscipit eros.
                                    Nam mi. Proin viverra leo ut odio.</p>
                              </div>
                              <h3 class="ui-accordion-header ui-corner-top ui-state-default ui-accordion-icons ui-accordion-header-collapsed ui-corner-all" role="tab" id="ui-id-3" aria-controls="ui-id-4" aria-selected="false" aria-expanded="false" tabindex="-1"><span class="ui-accordion-header-icon ui-icon ui-icon-triangle-1-e"></span>Section 2</h3>
                              <div class="ui-accordion-content ui-corner-bottom ui-helper-reset ui-widget-content" id="ui-id-4" aria-labelledby="ui-id-3" role="tabpanel" aria-hidden="true" style="display: none; height: 166.8px;">
                                 <p>Sed non urna. Phasellus eu ligula. Vestibulum sit amet purus.
                                    Vivamus hendrerit, dolor aliquet laoreet, mauris turpis velit,
                                    faucibus interdum tellus libero ac justo.</p>
                              </div>
                              <h3 class="ui-accordion-header ui-corner-top ui-state-default ui-accordion-icons ui-accordion-header-collapsed ui-corner-all" role="tab" id="ui-id-5" aria-controls="ui-id-6" aria-selected="false" aria-expanded="false" tabindex="-1"><span class="ui-accordion-header-icon ui-icon ui-icon-triangle-1-e"></span>Section 3</h3>
                              <div class="ui-accordion-content ui-corner-bottom ui-helper-reset ui-widget-content" id="ui-id-6" aria-labelledby="ui-id-5" role="tabpanel" aria-hidden="true" style="display: none; height: 166.8px;">
                                 <p>Nam enim risus, molestie et, porta ac, aliquam ac, risus.
                                    Quisque lobortis.Phasellus pellentesque purus in massa.</p>
                                 <ul>
                                    <li>List item one</li>
                                    <li>List item two</li>
                                    <li>List item three</li>
                                 </ul>
                              </div>
                           </div>
                        </script>
                     </code>
                  </pre>
               </section>
               <section>
                  <h4>angular</h4>
                  <img data-src="/assets/images/web-components-angular.png">
               </section>
               <section>
                  <pre>
                     <code class="html" data-trim data-line-numbers>
                        <script type="text/template">
                           <app-root _nghost-ng-c3715250071="" ng-version="17.0.0"><app-top-bar _ngcontent-ng-c3715250071="" _nghost-ng-c3918259376=""><a _ngcontent-ng-c3918259376="" routerlink="/" ng-reflect-router-link="/" href="/"><h1 _ngcontent-ng-c3918259376="">My Store</h1></a><a _ngcontent-ng-c3918259376="" routerlink="/cart" class="button fancy-button" ng-reflect-router-link="/cart" href="/cart"><em _ngcontent-ng-c3918259376="" class="material-icons">shopping_cart</em>Checkout
                              </a></app-top-bar><div _ngcontent-ng-c3715250071="" class="container"><router-outlet _ngcontent-ng-c3715250071=""></router-outlet><app-product-list _nghost-ng-c2837874410=""><h2 _ngcontent-ng-c2837874410="">Products</h2><div _ngcontent-ng-c2837874410=""><h3 _ngcontent-ng-c2837874410=""><a _ngcontent-ng-c2837874410="" title="Phone XL details" ng-reflect-router-link="/products,1" href="/products/1"> Phone XL </a></h3><p _ngcontent-ng-c2837874410=""> Description: A large phone with one of the best screens </p><!--bindings={
                                "ng-reflect-ng-if": "A large phone with one of the "
                              }--><button _ngcontent-ng-c2837874410="" type="button"> Share </button><app-product-alerts _ngcontent-ng-c2837874410="" _nghost-ng-c869072018="" ng-reflect-product="[object Object]"><p _ngcontent-ng-c869072018=""><button _ngcontent-ng-c869072018="" type="button">Notify Me</button></p><!--bindings={
                                "ng-reflect-ng-if": "true"
                              }--></app-product-alerts></div><div _ngcontent-ng-c2837874410=""><h3 _ngcontent-ng-c2837874410=""><a _ngcontent-ng-c2837874410="" title="Phone Mini details" ng-reflect-router-link="/products,2" href="/products/2"> Phone Mini </a></h3><p _ngcontent-ng-c2837874410=""> Description: A great phone with one of the best cameras </p><!--bindings={
                                "ng-reflect-ng-if": "A great phone with one of the "
                              }--><button _ngcontent-ng-c2837874410="" type="button"> Share </button><app-product-alerts _ngcontent-ng-c2837874410="" _nghost-ng-c869072018="" ng-reflect-product="[object Object]"><!--bindings={
                                "ng-reflect-ng-if": "false"
                              }--></app-product-alerts></div><div _ngcontent-ng-c2837874410=""><h3 _ngcontent-ng-c2837874410=""><a _ngcontent-ng-c2837874410="" title="Phone Standard details" ng-reflect-router-link="/products,3" href="/products/3"> Phone Standard </a></h3><!--bindings={
                                "ng-reflect-ng-if": ""
                              }--><button _ngcontent-ng-c2837874410="" type="button"> Share </button><app-product-alerts _ngcontent-ng-c2837874410="" _nghost-ng-c869072018="" ng-reflect-product="[object Object]"><!--bindings={
                                "ng-reflect-ng-if": "false"
                              }--></app-product-alerts></div><!--bindings={
                                "ng-reflect-ng-for-of": "[object Object],[object Object"
                              }--></app-product-list><!--container--></div>
                           </app-root>
                        </script>
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Aufbau einer Web Component</h3>
                  <pre>
                     <code class="html" data-trim data-line-numbers>
                        <script type="text/template">
                           <!--
                              Hinweis: Der Name bzw. der Selektor muss immer einen Bindestrich enthalten,
                              damit keine Namens-Kollisionen mit nativen HTML-Tags entstehen können
                           -->
                           <hello-world></hello-world>
                        </script>
                     </code>
                  </pre>
               </section>
               <section>
                  <pre>
                     <code class="js" data-trim data-line-numbers="1-16|19">
                        <script type="text/template">
                           class HelloWorldComponent extends HTMLElement {
                              constructor() {
                                 super();
                                 const shadowRoot = this.attachShadow({mode: "open"});
                                 shadowRoot.innerHTML = `
                                    <style>
                                       div {
                                          background-color: coral;
                                          color: white;
                                       }
                                    </style>
                                    <div>Hello World!</div>
                                 `;
                              }
                           }
                           window.customElements.define("hello-world", HelloWorldComponent);
                        </script>
                     </code>
                  </pre>
               </section>
               <section>
                  <iframe data-src="/assets/html/hello-world.html"></iframe>
               </section>
               <section>
                  <h4>Light DOM vs Shadow DOM</h4>
                  <img data-src="/assets/images/web-components-shadow-root.png" width="500px">
               </section>
               <section>
                  <h4>Suche von Elementen im Light- und Shadow DOM</h4>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        <script type="text/template">
                           // Light DOM
                           const span = document.querySelector("span"); // Hi!
                           const divs = document.querySelectorAll("div");
                           console.log(divs.length); // => 1
                           // Shadow DOM
                           const helloWorld = document.querySelector("hello-world");
                           helloWorld.shadowRoot.querySelector("div"); // Hello World!
                        </script>
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Auf Attribute reagieren</h3>
                  <pre>
                     <code class="html" data-trim data-line-numbers>
                        <script type="text/template">
                           <hello-world my-attribute="a"></hello-world>
                        </script>
                     </code>
                  </pre>
                  <pre>
                     <code class="js" data-trim data-line-numbers="2|8-10">
                        <script type="text/template">
                           class HelloWorldComponent extends HTMLElement {
                              static observedAttributes = ["my-attribute"];
                              constructor() {
                                 // ...
                              }
                              attributeChangedCallback(name, oldValue, newValue) {
                                 console.log(`${name}: changed from ${oldValue} to ${newValue}`);
                              }
                           }
                        </script>
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Aufgabe</h3>
                  <p>Lasse die eigene Komponente "hello-sayer" auf eine Änderung des "name"-Attributs reagieren, indem der dort eingetragene Name in der Komponente angezeigt wird (Beispiel: name="Joe" => Hello Joe!)</p>
                  <p>(/examples/templates/web-components/hello-sayer.html)</p>
               </section>
               <section>
                  <h3>Musterlösung</h3>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        <script type="text/template">
                           class HelloSayerComponent extends HTMLElement {
                              static observedAttributes = ["name"];
                              constructor() {
                                 super();
                                 this.root = this.attachShadow({mode: "open"});
                                 this.root.innerHTML = `
                                    <style>
                                       div {
                                          background-color: coral;
                                          color: white;
                                       }
                                    </style>
                                    <div>Hello!</div>
                                 `;
                              }
                              attributeChangedCallback(name, oldValue, newValue) {
                                 this.root.querySelector("div").innerHTML = `Hello ${newValue}!`;
                              }
                           }
                           window.customElements.define("hello-sayer", HelloSayerComponent);
                           const names = ["Joe", "Allie"];
                           let counter = 0;
                           setInterval(() => document.querySelector("hello-sayer").setAttribute("name", names[counter++ % 2]), 2000);
                        </script>
                     </code>
                  </pre>
               </section>
               <section>
                  <iframe data-src="/assets/html/hello-sayer.html"></iframe>
               </section>
               <section>
                  <h3>Elemente aus dem LightDOM platzieren mit <code>&lt;slot></code></h3>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        <script type="text/template">
                           this.root.innerHTML = `
                              <span>Hello <slot></slot>!</span>
                           `;
                        </script>
                     </code>
                  </pre>
               </section>
               <section>
                  <h4>Beispiel</h4>
                  <pre>
                     <code class="html" data-trim data-line-numbers>
                        <script type="text/template">
                           <hello-sayer>Joe</hello-sayer>
                           <hello-sayer>Allie</hello-sayer>
                        </script>
                     </code>
                  </pre>
                  <iframe data-src="/assets/html/hello-sayer-2.html"></iframe>
               </section>
               <section>
                  <h3>Mehrfache benannte <code>&lt;slot></code>s</h3>
                  <pre>
                     <code class=js data-trim data-line-numbers>
                        <script type="text/template">
                           this.root.innerHTML = `
                              <span>
                                 first name: <slot name="first"></slot> |
                                 last name: <slot name="last"></slot>
                              </span>
                           `;
                        </script>
                     </code>
                  </pre>
               </section>
               <section>
                  <h4>Beispiel</h4>
                  <pre>
                     <code class="html" data-trim data-line-numbers>
                        <script type="text/template">
                           <name-field>
                              <span slot="first">Leia</span>
                              <span slot="last">Organa</span>
                           </name-field>
                        </script>
                     </code>
                  </pre>
                  <iframe data-src="/assets/html/name-field.html"></iframe>
               </section>
               <section>
                  <h3>Aufgabe</h3>
                  <p>Entwerfe eine <code>CardComponent</code> (Vorlage "card-component.html") in folgendem Stil und nutze dabei sinnvolle <code>&lt;slot></code>s:</p>
                  <pre>
                     <code class="html" data-trim data-line-numbers>
                        <script type="text/template">
                           <card-component>
                              <span slot="title">This is a title</span>
                              <span slot="content">...Lorem ipsum dolor sit amet, consetetur sadipscing elitr...</span>
                           </card-component>
                        </script>
                     </code>
                  </pre>
                  <iframe data-src="/assets/html/exercise-card-component.html"></iframe>
               </section>
               <section>
                  <h3>Styling einer Web Component</h3>
                  <p>Ein <code>:host</code>-Block mit einem <code>display</code> gehört zum guten Ton!</p>
                  <pre>
                     <code class="css" data-trim data-line-numbers>
                        <script type="text/template">
                           :host {
                              display: block; /* inline, inline-block, flex, grid etc... */
                              color: red;
                              font-family: Arial, Helvetica, sans-serif;
                           }
                        </script>
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Innerhalb einer Komponente können beliebige Styles für verschiedene Selektoren verwendet werden</p>
                  <pre>
                     <code class="css" data-trim data-line-numbers>
                        <script type="text/template">
                           :host {
                              display: block;
                           }
                           .info {
                              background-color: lightblue;
                           }
                           .warning {
                              background-color: yellow;
                           }
                        </script>
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Aufgabe</h3>
                  <p>Welche Wechselwirkungen haben Styles zueinander, die innerhalb und außerhalb einer Komponente definiert sind? (Vorlage "web-component-styles.html")</p>
               </section>
               <section>
                  <p>Einige globale Styles werden dennoch vererbt:</p>
                  <ul>
                     <li>Sämtliche Styles, die standardmäßig einen Wert von <code>inherit</code> besitzen (z.B. <code>color</code> oder <code>font-family</code>)</li>
                     <li>Eigene CSS-Variablen wie z.B. <code>--my-custom-color</code></li>
                  </ul>
               </section>
               <section>
                  <h3>Aufgabe</h3>
                  <p>Passe das vorherige Ergebnis (Vorlage "web-component-styles.html") so an, dass ein beliebiger Style per CSS-Variable von außen gesetzt werden kann (z.B. <code>color</code> oder <code>background-color</code>)</p>
                  <p>Erinnerung CSS-Variablen:</p>
                  <pre>
                     <code class="css" data-trim data-line-numbers>
                        /* Definition */
                        html {
                           --my-variable: #abcdef;
                        }
                        /* Usage */
                        .my-class {
                           background-color: var(--my-variable, white);
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <p>In der Anfangszeit von Web Components wurde oft folgendes Muster zur Individualisierung verwendet:</p>
                  <pre>
                     <code class="css" data-trim data-line-numbers>
                        /* Global Scope */
                        html {
                           --my-border-color: #abcdef;
                           --my-border-width: 1px;
                           --my-margin-top: 1rem;
                           --my-margin-bottom: 1rem;
                        }
                        /* Inside Web Component */
                        .container {
                           border-color: var(--my-border-color, white);
                           border-width: var(--my-border-width, 1px);
                           margin-top: var(--my-margin-top, 0.5rem);
                           margin-bottom: var(--my-margin-bottom, 0.5rem);
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Eleganteres Stylen von Web Components mittels <code>part</code></p>
                  <pre>
                     <code class="css" data-trim data-line-numbers>
                        /* Global Scope */
                        my-component::part(inner) {
                           border-color: yellow;
                           border-width: 2px;
                        }
                        /* Inside Web Component */
                        <div part="inner"></div>
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Aufgabe</h3>
                  <p>Passe die <code>CardComponent</code> aus einer vorherigen Übung an (Vorlage "card-component.html"), sodass man Titel und Inhalt einfach von außen per <code>part</code>s stylen kann.</p>
               </section>
               <section>
                  <p><code>part</code>s von eingebetteten Web Components exportieren und verfügbar machen</p>
                  <pre>
                     <code class="html" data-trim data-line-numbers>
                        <!-- Inner Component Html -->
                        <div part="inner">Inner Text</div>
                        <!-- Outer Component Html -->
                        <inner-component exportparts="inner"></inner-component>
                        <!-- Global Html -->
                        <outer-component></outer-component>
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Aufgabe</h3>
                  <p>Passe das Beispiel (Vorlage "exportparts.html") entsprechend an, sodass von außen sowohl der <code>Inner Text</code> als auch der <code>Outer Text</code> stylebar sind</p>
               </section>
               <section>
                  <h3>Stylen von LightDOM-Kindern</h3>
                  <p>Web Components können direkte Kinder, die via <code>slot</code> eingefügt werden, stylen. Hierfür gibt es den Pseudo-Elemente-Selektor <code>::slotted([selector])</code>, welcher Elemente selektiert, die in einem Slot stecken:</p>
                  <pre>
                     <code class="css" data-trim data-line-numbers>
                        /* Inside Web Component */
                        ::slotted(*) { /* Selectors like elements, #id or .class */
                           font-style: italic;
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Aufgabe</h3>
                  <p>Definiere mit dem Pseudo-Elemente-Selektor <code>::slotted()</code> Styles, sodass per <code>slot</code> eingefügte Elemente mit der Klasse <code>red</code> einen roten und Elemente mit der Klasse <code>blue</code> einen blauen Hintergrund haben. Die Schriftart soll jeweils weiß sein (Vorlage "slotted.html")</p>
               </section>
               <section>
                  <h3>Events in Web Components</h3>
                  <img data-src="/assets/images/web-components-event-bubbling-shadow-dom.png">
                  <ul>
                     <li>Events können an einer Web Component selbst oder innerhalb des Shadow Dom <code>dispatcht</code> werden</li>
                     <li>Das Shadow Root der Komponente stellt eine Art "gläserne Decke" dar</li>
                     <li>Standard-Events der UI wie <code>click</code> bubbeln durch das Shadow Dom nach oben durch</li>
                     <li>Ein <code>CustomEvent</code> muss entsprechend konfiguriert werden</li>
                  </ul>
               </section>
               <section>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        button.dispatchEvent(new CustomEvent(
                           "tabactivated", // Name des Events, frei wählbar
                           {
                              detail: "any data", // beliebige Daten wie Strings, Zahlen, Booleans usw.
                              bubbles: true, // bestimmt, ob das Event durch alle Eltern nach oben steigt
                              cancelable: true, // Abbrechbar per event.preventDefault()
                              composed: true // durch eventuelle Shadow Root steigen, falls vorhanden.
                           }
                        ));
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Aufgabe</h3>
                  <p>Entwickle eine Tab-Komponente (Vorlage "tab-componenmt.html"), welche beim Click auf einen Registerreite / Tab ein Event feuert, welches die Außenwelt über das gerade aktivierte Tab (tab-id) informiert</p>
               </section>
               <section>
                  <h3>Lösung</h3>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        this.root.innerHTML = `
                           &lt;style>
                              ::slotted(div) {
                                 display: none;
                              }
                              ::slotted(div.active) {
                                 display: block;
                              }
                           &lt;/style>
                           &lt;div>&lt;slot name="tab">&lt;/slot>&lt;/div>
                           &lt;div>&lt;slot name="content">&lt;/slot>&lt;/div>
                        `;
                     </code>
                  </pre>
               </section>
               <section>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        connectedCallback() {
                           const firstContent = this.querySelector("div");
                           firstContent.classList.add("active");
                           this.addEventListener("click", (e) => {
                              if (e.target.tagName === "SPAN" && e.target.hasAttribute("tab-id")) {
                                 const oldContent = this.querySelector("div.active");
                                 const newContent = this.querySelector(`[tab-id="${e.target.getAttribute("tab-id")}"]`);
                                 oldContent.classList.remove("active");
                                 newContent.classList.add("active");
                                 this.dispatchEvent(new CustomEvent("tabclick", {composed: true, bubbles: true, detail: e.target.getAttribute("tab-id"), cancelable: true}));
                              }
                           });
                        }
                     </code>
                  </pre>
               </section>
            </section>
            <section>
               <section>
                  <h2>NodeJS</h2>
               </section>
               <section>
                  <h3>Was ist NodeJS?</h3>
                  <ul>
                     <li class="fragment">"JavaScript auf dem Server"</li>
                     <li class="fragment">basiert auf der V8-Engine von Google</li>
                     <li class="fragment">Laufzeitumgebung für JavaScript mit eigener API</li>
                  </ul>
               </section>
               <section>
                  <img data-src="/assets/images/node-js.svg">
               </section>
               <section>
                  <h3>Überblick über die Node-API</h3>
                  <p>Die API ist in viele verschiedene Module aufgeteilt</p>
                  <table>
                     <tr><td>fs</td><td>Dateisystem</td></tr>
                     <tr><td>crypto</td><td>Kryptografie</td></tr>
                     <tr><td>zlib</td><td>Datenkompression</td></tr>
                     <tr><td>http</td><td>Webserver</td></tr>
                     <tr><td>net</td><td>Netzwerk (TCP)</td></tr>
                     <tr><td>tls</td><td>Verschlüsseltes Netzwerk (TCP)</td></tr>
                  </table>
               </section>
               <section>
                  <p>Aufruf eines Programmes in der Kommandozeile</p>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        node [Node-Parameter] path/to/script.js [Anwendungsparameter]
                     </code>
                  </pre>
                  <p>z.B.</p>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        node --inspect index.js --port 3000
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Einbinden von Modulen</p>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        const fs = require("fs");
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Aufbau eines Moduls</p>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        module.exports = {
                           foo: 5,
                           sayHello: function(name) {
                              return "Hello " + name + "!";
                           }
                        }
                     </code>
                  </pre>
                  <p>oder</p>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        exports.foo = 5;
                        exports.sayHello = function(name) {
                           return "Hello " + name + "!";
                        };
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Aufgabe</p>
                  <p>Schreibe ein Modul, welches eine <code>Square</code>-Klasse zur Verfügung stellt, mit welchem man den Umfang und den Flächeninhalt eines Rechtecks berechnen kann:</p>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        const Square = require("./square");
                        const square = new Square(2, 3);
                        console.log(square.getArea()); // => 6
                        console.log(square.getPerimeter()); // => 10
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Module für die Kommandozeile</h3>
                  <p>package.json</p>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        npm init -y
                     </code>
                  </pre>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        {
                           ...
                           "bin": {
                              "my-command": "./index.js"
                           }
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <p>index.js</p>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        #! /usr/bin/env node
                        console.log("Hello world from the command line!");
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Installation</p>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        npm install -g path/to/project
                     </code>
                  </pre>
                  z.B.
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        npm install -g .
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Ausführung</p>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        my-command
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Kommandozeilenparameter</p>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        console.log(process.argv);
                     </code>
                  </pre>
                  <p>angelehnt an die Parameter aus der Programmiersprache <code>c</code>:</p>
                  <pre>
                     <code class="c" data-trim data-line-numbers>
                        int main(int argc, char *argv[])
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Aufgabe</p>
                  <p>Mache das vorherige Programm für die Konsole aufrufbar, sodass die Länge und Breite für das Rechteck per Konsolenargumente übergeben werden können und der Flächeninhalt ausgegeben wird:</p>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        my-command 2 3
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Buffer</h3>
                  <p>Array-artige Struktur für rohe Bytes</p>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        const b1 = Buffer.from("abc"); // bevorzugt
                        const b2 = Buffer.alloc(100, 0); // Länge mit initialen Daten
                        const b3 = Buffer.allocUnsafe(100); // Länge ohne initiale Daten
                        const b4 = new Buffer("abc"); //  offiziell nicht empfohlen
                        console.log(b1); // => &lt;Buffer 61 62 63>
                        console.log(b1.toString("hex")); // => "616263"
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Mit Buffern arbeiten:</p>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        const a = Buffer.from("a") + Buffer.from("b");
                        const b = Buffer.concat([Buffer.from("a"), Buffer.from([98])]);
                        const c = b.toString()
                        console.log(a); // => ab
                        console.log(b); // => &lt;Buffer 61 62>
                        console.log(c); // => ab
                        for (const byte of a) {
                           console.log(byte); // => 97 98
                        }
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Aufgabe</p>
                  <p>Implementiere eine XOR-Verschlüsselung (<code>^</code>-Operator), sodass jedes Byte einer Original-Nachricht der Reihe nach mit dem entsprechenden Byte eines Schlüsselwortes verschlüsselt wird und gebe die verschlüsselte Nachricht als Text aus.</p>
               </section>
               <section>
                  <p>Aufgabe</p>
                  <p>Implementiere zur vorherigen Verschlüsselung eine Entschlüsselung.</p>
               </section>
               <section>
                  <p>Lösung</p>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        const word = Buffer.from("abcdef");
                        const key = Buffer.from("xyz");
                        const encrypted = Buffer.alloc(word.length, 0);
                        // encrypt
                        for (let i = 0; i &lt; word.length; i++) {
                           encrypted[i] = word[i] ^ key[i % key.length];
                        }
                        // decrypt
                        for (let i = 0; i &lt; word.length; i++) {
                           word[i] = encrypted[i] ^ key[i % key.length];
                        }
                        console.log(word.toString()); // => "abcdef"
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Streams</h3>
                  <p>Konstanter Fluss von (meist) großen Datenmengen in kleinen Paketen</p>
               </section>
               <section>
                  <p>Gängige Typen von Streams</p>
                  <ul>
                     <li>Standard In / Out</li>
                     <li>HTTP-Requests / -Responses</li>
                     <li>TCP-Sockets</li>
                     <li>Lese- und Schreib-Streams auf Dateien</li>
                     <li>Große Datenbank-Ergebnisse mittels Cursor</li>
                  </ul>
               </section>
               <section>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        const readableStream = fs.createReadStream("./source.txt");
                        const writableStream = fs.createWriteStream("./target.txt");
                        readableStream.on("data", (data /* Buffer */) =&gt; {
                           // process data here
                           writableStream.write(data)
                        });
                        readableStream.on("end", (data) =&gt; {
                           // stream has finished, e.g. EOF (end of file)
                        });
                        // alternatively pipe content
                        readableStream.pipe(writableStream);
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Aufgabe</p>
                  <p>Lese eine beliebige Datei als Readable Stream, verschlüssele den Inhalt mit der XOR-Methode und schreibe die verschlüsselten Bytes in eine Zieldatei</p>
               </section>
               <section>
                  <p>Aufgabe</p>
                  <p>Ändere das Programm, sodass auf Konsolenebene ein Pipe (|) ermöglicht wird:</p>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        echo "Hello World!" | node index.js
                     </code>
                  </pre>
                  <p>Nutze dafür die implizit vorhandenen Streams von Standard In / Out:</p>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        process.stdin; // Readable Stream
                        process.stdout; // Writable Stream
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Kryptographie</h3>
               </section>
               <section>
                  Mithilfe des nativen <code>crypto</code>-Moduls ist es möglich, moderne sichere kryptografische Verfahren zu verwenden:
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        const crypto = require("crypto");
                     </code>
                  </pre>
                  Hierzu nutzt NodeJS selber die weit verbreitete OpenSSL-Bibliothek
               </section>
               <section>
                  Einen MD5-Hash erzeugen:
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        const crypto = require("crypto");
                        const hash = crypto.createHash("md5");
                        const md5 = hash.update("abcdefg")
                           .digest(); // Buffer (default)
                           // .digest("hex") // Hexadezimal
                           // .digest("utf8") // Bytes forciert als Chars
                        console.log(md5);
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Aufgabe</p>
                  <p>Erzeuge jeweils den md5- und den sha256-Hash als Hex-Wert der beiden Texte in der Datei <code>text-examples.txt</code> und vergleiche jeweils ihren md5- und sha256-Hash miteinander</p>
               </section>
               <section>
                  <h3>Zippen</h3>
               </section>
               <section>
                  Mit dem nativen Modul <code>zlib</code> ist es möglich, Kompression in seinen Anwendungen zu nutzen.
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        const zlib = require("zlib");
                        // Komprimieren
                        zlib.deflate("abc" /* buffer | string */, (err, compressed) => {});
                        // Dekomprimieren
                        zlib.inflate(compressed /* buffer */, (err, decompressed) => {});
                        // Als Stream
                        const zipper = zlib.createDeflate();
                        const unzipper = zlib.createInflate();
                        source.pipe(zipper).pipe(target);
                        source.pipe(unzipper).pipe(target);
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Aufgabe</p>
                  <p>Nutze die einfachen Varianten <code>zlib.deflate</code> und <code>zlib.inflate</code>, um einen beliebigen Text zu komprimieren und anschließend wieder zu dekomprimieren</p>
               </section>
               <section>
                  <p>Aufgabe</p>
                  <p>Nutze die Stream-Varianten <code>zlib.createDeflate</code> und <code>zlib.createInflate</code>, um jeweils ein getrenntes Programm für die Kompression und Dekompression mit <code>process.stdin</code> und <code>process.stdout</code> zu machen, sodass in der Konsole folgende Nutzung möglich ist:</p>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        echo "abc" | node compress.js | node decompress.js
                        cat file.txt | node compress.js > file.zipped
                        cat file.zipped | node decompress.js > file.txt
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>TCP-Verbindungen</h3>
               </section>
               <section>
                  <ul>
                     <li>Bidirektionale Verbindung</li>
                     <li>Duplexfähig</li>
                     <li>Besteht immer aus Server- und Client-Socket</li>
                  </ul>
               </section>
               <section>
                  Mit dem nativen Modul <code>net</code> ist es möglich, eine TCP-Verbindung aufzubauen
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        /* Server */
                        const net = require("net");
                        const server = net.createServer((socket) => {
                           // new incoming connection
                           socket.on("data", (data) => {
                              // data is a buffer
                           });
                           socket.write("Hello World!");
                        });
                        server.listen(3000);
                     </code>
                  </pre>
               </section>
               <section>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        /* Client */
                        const net = require("net");
                        const socket = net.connect(3000, () => {
                           // 'connect' listener
                        });
                        socket.on("data", (data) => {
                           // data is a buffer
                        });
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Aufgabe</p>
                  <p>Implementiere die Clientlogik in der <code>tcp-client.js</code>, um dem Server ein <code>"Hello Server!"</code> zurück zu senden</p>
               </section>
               <section>
                  <p>Aufgabe</p>
                  <p>Sende eine festgelegte Datei vom Server zum Client. Der Server liest die Datei ein und sendet die Inhalte zum Client, welcher diese wiederum in eine Zieldatei schreibt. Eventuell darf auch noch eine Kompression mittels Zip / Deflate implementiert werden.</p>
               </section>
               <section>
                  <p>Kurze Wiederholung des HTTP-Protokolls</p>
                  <p>Request</p>
                  <pre>
                     <code class="http" data-trim data-line-numbers>
                        GET / HTTP/1.1
                        Host: localhost:3456
                     </code>
                  </pre>
                  <p>Response</p>
                  <pre>
                     <code class="http" data-trim data-line-numbers>
                        HTTP/1.1 200
                        Content-Type: text/plain
                        Content-Length: 12
                        Hello World!
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Aufgabe</p>
                  <p>Implementiere mithilfe des TCP-Sockets einen minimalen HTTP-Server, welcher <code>Hello &lt;name></code> als Text zurück gibt, wobei <code>&lt;name></code> aus dem Query-Parameter gelesen werden soll</p>
               </section>
               <section>
                  <h3>HTTP-Server</h3>
               </section>
               <section>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        const http = require("http");
                        const server = http.createServer((req, res) => {
                           res.writeHeader(200, {
                              "Content-Type": "text/plain", /* Put Content-Type here */
                           });
                           console.log(req.rawHeaders);
                           console.log(req.method);
                           console.log(req.url);
                           res.write(/* response content */);
                           res.end(); /* important if no Content-Length is specified */
                        });
                        server.listen(3456);
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Aufgabe</p>
                  <p>Experimentiert mit verschiedenen Möglichkeiten von <code>Content-Length</code> und dem Vorhandensein von <code>res.end()</code>, mit längerer oder kürzerer <code>Content-Length</code> als der tatsächliche Body oder teilt den Body in zwei <code>res.write()</code> und verzögert künstlich mit einem setTimeout() den zweiten Teil und schaut euch im Browser bei den Entwickler-Tools die Timings der Response an.</p>
               </section>
               <section>
                  <h3>Worker Threads</h3>
               </section>
               <section>
                  <p>Was sind Worker Threads?</p>
                  <p>Eine bidirektionale nachrichten-basierte Implementierung von Multithreading in NodeJS</p>
               </section>
               <section>
                  <p>Beispiel:</p>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        // Main Thread
                        const { Worker } = require('worker_threads');
                        const data = 'some data';
                        const worker = new Worker("path/to/worker.js", { workerData: data });
                        worker.on('message', message => console.log('Reply from Thread:', message));
                        // Worker Thread
                        const { workerData, parentPort } = require('worker_threads');
                        const result = workerData.toUpperCase();
                        parentPort.postMessage(result);
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Weitere nützliche Funktionen:</p>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        // Main Thread
                        worker.on('message', message => processMessage(message));
                        worker.on('error', error => console.error(error));
                        worker.postMessage({a: 1}); // send any data to the worker
                        worker.terminate(); // manually terminate worker thread
                        // Worker Thread
                        parentPort.on('message', message => doSomething(message)); // receive messages from the main thread
                        process.exit(0); // terminate self, optionally with exit code
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Aufgabe</p>
                  <p>
                     Nutze die in den <code>examples</code> vorhandene Funktion <code>isPrime</code> (010-nodejs/mt-main.js und 010-nodejs/mt-worker.js) und implementiere mit Hilfe der <code>Worker Threads</code> die Suche nach Primzahlen.
                     Die gewünschte maximale Zahl, bis zu der die Primzahlen gesucht werden sollen als auch die Anzahl der Threads, mit der gleichzeitig gesucht werden soll, sollen möglichst einfach parametrisierbar sein (einfache Variable im Code genügt).
                     Probiert verschiedene Thread-Anzahlen aus und vergleicht einmal die benötigten Zeiten.
                  </p>
               </section>
               <section>
                  <p>Hinweise:</p>
                  <ul>
                     <li>Bei der Suche bis zu 1.000.000 und 8 Threads soll der erste Thread von 0 bis 125.000 suchen, der zweite von 125.000 bis 250.000 usw.</li>
                     <li>Nutzt Promises, um auf alle Ergebnisse warten zu können. (s. <code>Promise.all</code>)</li>
                     <li>per <code>workerData</code> können auch Objekte übergeben werden</li>
                     <li>Die gefundenen Primzahlen können ausgegeben werden, müssen aber nicht. In erster Linie geht es darum, die Arbeit auf mehrere Threads aufzuteilen.</li>
                  </ul>
               </section>
               <section>
                  <h3>Multi Page Applications</h3>
               </section>
               <section>
                  <p>Im Gegensatz zu einer SPA ("Single Page Application") teilt sich eine Multi Page Application in mehrere Unterseiten auf, die sich per URL z.B. im Browser adressieren lassen.</p>
               </section>
               <section>
                  <p>In einem neuen Ordner / Projekt:</p>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        npm init -y
                        npm install express pug
                        mkdir public
                     </code>
                  </pre>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        // index.js (s. examples 010-nodejs/mpa.js)
                        const express = require("express");
                        const app = express();
                        app.use(express.static("public"));
                        app.listen(3002);
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Aufgabe</h3>
                  <p>Fülle das Verzeichnis "public" mit jeweis einer HTML-Datei für eine "Home"- und eine "About Us"-Seite, die beide eine einfache Navigations-Leiste enthalten (simple <code>span</code> mit Link (href)), um zwischen diesen beiden Seiten navigieren zu können.
                     Unterscheiden sollen sich diese beiden Seiten durch ihren "Inhalt", indem dort einfach "Home" und "About Us" enthalten ist.</p>
               </section>
               <section>
                  <h3>Serverseitiges Rendern</h3>
               </section>
               <section>
                  <p>Template-Engine "pug"</p>
                  <pre>
                     <code class="css" data-trim data-line-numbers>
                        html
                           head
                           body
                              div This is a div with some text
                              #main.my-class This is also a div with short-hands for id and class
                              a(href="/") This is a link with href
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Conditionals</p>
                  <pre>
                     <code class="css" data-trim data-line-numbers>
                        html
                           head
                           body
                              if name
                                 div Hello #{name}!
                              else
                                 div Hello Anonymous"
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Extension / Inheritance</p>
                  <pre>
                     <code class="css" data-trim data-line-numbers>
                        // layout.pug
                        html
                           head
                           body
                              div Some static content
                              block content
                        // page.pug
                        extends layout.pug
                        block content
                           div This is my home content
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Pug in Express</p>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        mkdir views
                     </code>
                  </pre>
                  <pre>
                     <code class="js" data-trim data-line-numbers>
                        // ...
                        app.set('view engine', 'pug');
                        app.get('/', (req, res) => {
                           // file name without .pug and some data passed to the template
                           res.render('index', { title: 'Hey', message: 'Hello there!' })
                        });
                        app.listen(3002);
                     </code>
                  </pre>
                  <pre>
                     <code class="css" data-trim data-line-numbers>
                        // views/index.pug
                        html
                           head
                              title #{title}
                           body #{message}
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Aufgabe</h3>
                  <p>Implementiere die Seiten aus dem vorherigen Beispiel mit Pug (nutze Vererbung, um die Navigationsleiste nicht doppelt implementieren zu müssen) und
                     baue einen Gruß im Seitentext mit dynamischen Namen ein, den man z.B. in der URL als Query-Parameter "?name=Joe" angibt und der im Server ausgewertet wird.</p>
               </section>
            </section>
            <section>
               <section>
                  <h2>Betrieb von Anwendungen</h2>
               </section>
               <section>
                  <p>Wichtige Aspekte beim produktiven Betrieb von Anwendungen</p>
                  <ul>
                     <li>Stabilität</li>
                     <li>Zugriffsschutz</li>
                     <li>Sicherheitsupdates</li>
                     <li>Ausfallsicherheit</li>
                  </ul>
               </section>
               <section>
                  <p>Typische Werkzeuge beim Betrieb</p>
                  <ul>
                     <li>Webserver</li>
                     <li>Proxies</li>
                     <li>Laufzeitumgebungen</li>
                     <li>Application-Server / Prozess-Manager</li>
                     <li>Remote-Zugänge</li>
                  </ul>
               </section>
               <section>
                  <h3>Beispielhafte Produktivumgebung mit lokaler VM</h3>
               </section>
               <section>
                  <p>Vorbereitungen</p>
                  <ul>
                     <li>Oracle VirtualBox</li>
                     <li>Ubuntu Server 24.04</li>
                  </ul>
               </section>
               <section>
                  <p>Eckdaten für die VM</p>
                  <ul>
                     <li>2048 MB Ram</li>
                     <li>Netzwerktyp "Brücke" / "Bridge"</li>
                     <li>Linux / Ubuntu (64-Bit)</li>
                     <li>20GB HDD</li>
                  </ul>
               </section>
               <section>
                  <img data-src="/assets/images/ubuntu-installation.png">
               </section>
               <section>
                  <p>Nach der Installation in der VM mit den bekannten Credentials anmelden und die IP in Erfahrung bringen:</p>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        ip a
                     </code>
                  </pre>
               </section>
               <section>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        2: enp0s3: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc pfifo_fast state UP [...]
                           link/ether 08:00:27:43:e5:2c brd ff:ff:ff:ff:ff:ff
                           inet 10.0.0.166/16 metric 100 brd 10.0.255.255 scope global dynamic enp0s3
                              valid_lft 863191sec preferred_lft 863191sec
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Anmeldung am Server</h3>
                  <p>Nach der Installation am Server anmelden</p>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        ssh user@domain [-p 12345]
                     </code>
                  </pre>
                  <p>Beispiele für eine Domain: localhost, www.example.com, 127.0.0.1, [0:0:0:0:0:0:0:1], [::1]</p>
               </section>
               <section>
                  <p>Es kann auch der bei der Installation des Servers angegebene Host-Name verwendet werden, z.B.</p>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        # "vm-ubuntu-server"
                        ssh user@vm-ubuntu-server
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Pakete aktualisieren</h3>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        # Paketlisten updaten
                        sudo apt update
                        # Pakete updaten
                        sudo apt upgrade
                     </code>
                  </pre>
                  <p>sonstige Werkzeuge installieren</p>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        sudo apt install vim git curl build-essential
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Ordner-Navigation</h3>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        # In einen Ordner gehen
                        cd [Ordner]
                        # Zum vorherigen Ordner gehen
                        cd -
                        # Längere Ordnerpfade sind auch möglich
                        # realtiv
                        cd a/b/c/d
                        # absolut
                        cd /var/www/html
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Ordner erstellen und löschen</h3>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        # Einen Ordner erstellen
                        mkdir my-folder
                        # Mehrere Hierarchien
                        mkdir -p a/b/c/d
                        # Mehrere Ordner
                        mkdir a b c
                        # Ordner rekrusiv löschen
                        rm -rf [Ordner]
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Ordner und Dateien kopieren und verschieben</h3>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        # Ordner kopieren
                        cp -r quelle ziel
                        # Datei kopieren
                        cp quelle ziel
                        # Ordner und Dateien verschieben
                        mv quelle ziel
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Eigenen User anlegen</h3>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        # User anlegen
                        # -m => Home Ordner anlegen
                        # -G => zur angegeben Gruppe hinzufügen
                        # -s => Pfad zur Shell, in diesem Fall /bin/bash
                        useradd [user] -m -G sudo -s /bin/bash
                        # Passwort setzen
                        passwd [user]
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Authentifizierung per SSH-Key-Pair</h3>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        # Schlüsselpaar lokal erzeugen, falls nicht vorhanden
                        ssh-keygen
                        # Inhalt aus Datei kopieren:
                        cat ~/.ssh/id_rsa.pub
                     </code>
                  </pre>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        # Auf dem Server in folgenden Datei einfügen (Public Key pro Zeile):
                        ~/.ssh/authorized_keys
                     </code>
                  </pre>
                  <p>anschließend wird kein Passwort mehr beim Login benötigt</p>
               </section>
               <section>
                  <h3>Apache einrichten</h3>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        sudo apt install apache2
                     </code>
                  </pre>
                  <p>Einrichtung überprüfen</p>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        # Oder im Browser aufrufen:
                        curl http://vm-ubuntu-server
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Aufbau der Apache-Konfiguration</p>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        # Hauptdatei, selten gebraucht
                        /etc/apache2/apache2.conf
                        # Verfügbare vhosts
                        /etc/apache2/sites-available/*.conf
                        # Aktivierte vhosts, Symlinks auf obige verfügbare
                        /etc/apache2/sites-enabled/*.conf
                        # Standard HTML Pfad bei Apache
                        /var/www/html
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>vhost (virtual host) einrichten</h3>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        cd /etc/apache/sites-available
                        # 000-default.conf als Vorlage nutzen
                        # vm-ubuntu-server durch eigenen Host ersetzen
                        sudo cp 000-default.conf www.vm-ubuntu-server.conf
                        # 'ServerName' einkommentieren und mit eigenem Host anpassen
                        # Weitere Optionen je nach Fall anpassen, z.B. 'DocumentRoot'
                        sudo vim www.vm-ubuntu-server.conf
                        # vhost / Site aktivieren
                        sudo a2ensite www.vm-ubuntu-server.conf
                     </code>
                  </pre>
               </section>
               <section>
                  <p>Domains lokal auf dem Entwicklungssystem bekannt machen:</p>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        # Windows
                        # z.B. mit Notepadd++ editieren. Benötigt Administrator-Rechte
                        C:\Windows\System32\drivers\etc\hosts
                        # Unix
                        # z.B. mit vim editieren. Benötigt Administrator-Rechte
                        /etc/hosts
                     </code>
                  </pre>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        # unten hinzufügen
                        [IP des Servers] www.vm-ubuntu-server
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Dateien zum Server kopieren</h3>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        # scp [Quelle] [Ziel], z.B.
                        scp index.html user@domain:~/
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Aufgabe</h3>
                  <p>Richte die Sub-Domain <code>www.vm-ubuntu-server</code> ein und liefere unter dieser Adresse eine beliebige HTML-Seite aus. Dies kann eine minimale selbstgeschriebene oder eine beliebige komplexere aus den vergangenen Veranstaltungen sein, z.B. aus den CSS-Themen (jedoch keine NodeJS-Projekte).</p>
               </section>
               <section>
                  <h3>NodeJS und PM2 einrichten</h3>
               </section>
               <section>
                  <p>Installation mittels fnm (<code>fast node manager</code>)</p>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        # Abhängigkeit 'unzip' installieren
                        sudo apt install unzip
                        # https://github.com/Schniz/fnm
                        curl -fsSL https://fnm.vercel.app/install | bash
                        # Anschließend die .bashrc neuladen
                        . ~/.bashrc
                        # Aktuelle NodeJS LTS installieren
                        fnm install --lts
                        # Test
                        node -v
                     </code>
                  </pre>
               </section>
               <section>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        # PM2
                        npm install -g pm2
                     </code>
                  </pre>
               </section>
               <section>
                  <h3>Aufgabe</h3>
                  <p>Richte auf dem Server ein kleines NodeJS-Projekt ein, welches eine beliebige Website zur Verfügung stellt (selber implementieren oder ein bestehendes kopieren). Kopiere das Projekt mit den bekannten Befehlen auf den Server an einen beliebigen Ort. Teste anschließend die Lauffähigkeit im Browser per IP und Port der Anwendung.</p>
               </section>
               <section>
                  <h3>Einrichten eines Reverse-Proxy</h3>
               </section>
               <section>
                  <img data-src="/assets/images/proxy-types.svg">
               </section>
               <section>
                  <p>Aktivieren des benötigten Moduls</p>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        # Apache 2 enable module
                        sudo a2enmod proxy
                     </code>
                  </pre>
                  <p>Neue Direktiven</p>
                  <ul>
                     <li>ProxyPass</li>
                     <li>ProxyPassReverse</li>
                     <li>ProxyRequests</li>
                  </ul>
               </section>
               <section>
                  <p>Notwendige Anpassungen am vhost</p>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        # DocumentRoot entfernen / auskommentieren
                        #DocumentRoot /var/www/html
                        # Aus Sicherheitsgründen Forward Proxy deaktivieren
                        ProxyRequests Off
                        # ProxyPass [Quelle] [Ziel] (auf Port achten) => Weiterleitung
                        ProxyPass / http://127.0.0.1:9000
                        # ProxyPassReverse [Quelle] [Ziel] (auf Port achten) => URL-Rewrite bei Redirects
                        ProxyPassReverse / http://127.0.0.1:9000
                     </code>
                  </pre>
                  <p>Apache neu starten, um die Änderungen am vhost zu übernehmen</p>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        sudo systemctl reload apache2
                     </code>
                  </pre>
               </section>
               <section>
                  <p>In das Verzeichnis der NodeJS-Anwendung wechseln und per PM2 starten</p>
                  <pre>
                     <code class="bash" data-trim data-line-numbers>
                        pm2 start --name my-super-app index.js
                     </code>
                  </pre>
                  <p>Anschließend sollte die NodeJS-Anwendung unter der bereits bekannten Domain erreichbar sein.</p>
               </section>
            </section>
         </div>
      </div>
@@ -2402,7 +4981,7 @@
            hash: true,
            slideNumber: "c/t",
            transition: "fade",
            // Learn about plugins: https://revealjs.com/plugins/
            plugins: [ RevealMarkdown, RevealHighlight, RevealNotes ]
         });