Eine kurze Einführung/eine kurze Übersicht über JavaScript für erfahrene Programmierer.
michael.eichberg@dhbw.de, Raum 149B
2.0
Seit 2016 gibt es jährliche Updates (ECMAScript 2016, 2017, 2018, 2019, 2020, 2021, 2022, ...)
Objektorientiert
Protoypische Vererbung
Objekte erben von anderen Objekten
Objekte als allgemeine Container
(Im Grunde eine Vereinheitlichung von Objekten und Hashtabellen.)
seit ES6 werden auch Klassen unterstützt; diese sind aber nur syntaktischer Zucker
Skriptsprache
Loose Typing/Dynamische Typisierung
Load and go-delivery (Lieferung als Text/Quellcode)
Garbage Collected
Single-Threaded
Funktionen sind Objekte erster Klasse
Ein (globaler) Namespace
Syntaktisch eine Sprache der "C"-Familie (viele Ähnlichkeiten zu Java)
Standardisiert durch die ECMA (ECMAScript)
Verwendet ganz insbesondere in Browsern, aber auch Serverseitig (Node.js) oder in Desktop-Anwendungen (Electron)
Schlüsselworte:
function, async, await, return, yield
break, continue, case, default, do, else, for, if, instanceof, of, typeof, switch, while
throw, try, finally, catch
class, delete, extends, in, new, static, super, this
const, let, var
export, import
Nicht genutzte Schlüsselworte:
enum, implements, interface, package, private, protected, public, void, with (no longer)
(Sehr vergleichbar mit Java.)
Buchstaben (Unicode), Ziffern, Unterstriche, Dollarzeichen
Ein Identifier darf nicht mit einer Ziffer beginnen
Nameskonventionen:
Klassen beginnen mit einem Großbuchstaben (UpperCamelCase)
Variablen und Funktionen beginnen mit einem Kleinbuchstaben (lowerCamelCase)
Konstanten sind komplett in Großbuchstaben
console
Number, Boolean, Date, BigInt, Math, ...
window
document (bzw. window.document)
alert
navigator
location
module
exports
require
process
crypto
1// Der "Scope" ist auf den umgebenden Block begrenzt.
2// Eine Änderung des Wertes ist möglich.
3let y = "yyy";
45
// Der "Scope" ist auf den umgebenden Block begrenzt.
6// Eine Änderung des Wertes ist nicht möglich.
7const z = "zzz";
89
log("y, z:", y, z);
1011
function doIt() {
12const y = "---";
13log("y, z:", y, z);
14return "";
15}
1617
ilog('"doIt done"', doIt());
18log("y, z:", y, z);
Um diesen und den Code auf den folgenden Folien ggf. mit Hilfe von Node.js auszuführen, muss am Anfang der Datei:
import { ilog, log, done } from "./log.mjs";
und am Ende der Datei:
done();
hinzugefügt werden.
Den entsprechenden Code der Module (log.mjs und später Queue.mjs) finden Sie auf:
https://github.com/Delors/delors.github.io/tree/main/web-javascript/code
1console.log("Undefined --------------------------------------------------------------");
2let u = undefined;
3console.log("u", u);
45
console.log("Number -----------------------------------------------------------------");
6let i = 1; // double-precision 64-bit binary IEEE 754 value
7let f = 1.0; // double-precision 64-bit binary IEEE 754 value
8let l = 10_000;
9let binary = 0b1010;
10console.log("0b1010", binary);
11let octal = 0o12;
12console.log("0o12", octal );
13let hex = 0xA;
14console.log("0xA", hex);
15console.log(
16Number.MIN_VALUE,
17Number.MIN_SAFE_INTEGER,
18Number.MAX_SAFE_INTEGER,
19Number.MAX_VALUE,
20);
21let x = NaN;
22let y = Infinity;
23let z = -Infinity;
2425
// Standard Operatoren: +, - , *, /, %, ++, --, **
26// Bitwise Operatoren: &, |, ^, ~, <<, >>, >>> (operieren auf dem Ganzzahlwert der Bits)
27console.log("i =", i, "; i++ ", i++); // 1 oder 2?
28console.log("i =", i, "; ++i ", ++i); // 2 oder 3?
29console.log("2 ** 4 === 0 ", 2 ** 4);
30console.log("7 % 3 === ", 7 % 3);
31console.log("1 / 0 === ", 1 / 0);
3233
34
console.log("BigInt -----------------------------------------------------------------");
35let ib = 1n;
36console.log(100n === BigInt(100));
37console.log(Number.MAX_SAFE_INTEGER + 2102); // 9007199254743092
38console.log(BigInt(Number.MAX_SAFE_INTEGER) + 2102n); // 9007199254743093n
3940
41
console.log("Boolean ----------------------------------------------------------------");
42let b = true; // oder false
43console.log("Boolean(undefined)", Boolean(undefined)); // true oder false?
44console.log(null == true ? "true" : "false"); // true oder false?
4546
47
console.log("(Quasi-)Logische Operatoren -------------------------------------------");
48console.log('1 && "1": ', 1 && "1");
49console.log('null && "1": ', null && "1");
50console.log("null && true: ", null && true);
51console.log("true && null: ", true && null);
52console.log("null && false: ", null && false);
53console.log("{} && true: ", {} && true);
5455
// Neben den Standardoperatoren: ``&&``, ``||``, ``!`` gibt es auch noch ``??``
56// Der ``??``-Operator gibt den rechten Operanden zurück, wenn der linke Operand
57// ``null`` oder ``undefined`` ist. Andernfalls gibt er den linken Operanden zurück.
58// ``??`` ist der *nullish coalescing operator (??) (vergleichbar zu ||)*
59console.log('1 ?? "1": ', 1 ?? "1");
60console.log('null ?? "1": ', null ?? "1");
61console.log("null ?? true: ", null ?? true);
62console.log("true ?? null: ", true ?? null);
63console.log("null ?? false: ", null ?? false);
64console.log("{} ?? true: ", {} ?? true);
6566
console.log('undefined ?? "1": ', undefined ?? "1");
67console.log('undefined ?? "1": ', undefined ?? "1");
68console.log("undefined ?? true: ", undefined ?? true);
69console.log("true ?? undefined: ", true ?? undefined);
70console.log("undefined ?? false: ", undefined ?? false);
71console.log("undefined ?? undefined: ", undefined ?? undefined);
7273
74
console.log("Strings ----------------------------------------------------------------");
75let _s = "42";
76console.log("Die Antwort ist " + _s + "."); // String concatenation
77console.log(`Die Antwort ist ${_s}.`); // Template literals (Template strings)
78// multiline Strings
79console.log(`
80Die Antwort mag ${_s} sein,
81aber was ist die Frage?`);
8283
console.log(String(42)); // "42"
8485
86
console.log("Objekte ----------------------------------------------------------------");
87let emptyObject = null;
88let anonymousObj = {
89i: 1,
90u: { j: 2, v: { k: 3 } },
91toString: function () {
92return "anonymousObj";
93},
94"?" : "question mark"
95};
96// Zugriff auf die Eigenschaften eines Objekts
97anonymousObj.j = 2; // mittels Bezeichner ("j") (eng. Identifier)
98anonymousObj["j"] = 4; // mittels String ("j")
99anonymousObj["k"] = 3;
100console.log("anonymousObj: ", anonymousObj);
101console.log("anonymousObj.toString(): ", anonymousObj.toString());
102delete anonymousObj["?"]; // delete operator dient dem Löschen von Eigenschaften
103delete anonymousObj.toString; // delete operator dient dem Löschen von Eigenschaften
104console.log("anonymousObj.toString() [original]", anonymousObj.toString());
105// Der Chain-Operator kann verwendet werden, um auf Eigenschaften (Properties)
106// von Objekten zuzugreifen, ohne dass eine Fehlermeldung ausgegeben wird,
107// wenn eine (höher-liegende) Eigenschaft nicht definiert ist.
108// Besonders nützlich beim Verarbeiten von komplexen JSON-Daten.
109console.log("anonymousObj.u?.v.k", anonymousObj.u?.v.k);
110console.log("anonymousObj.u.v?.k", anonymousObj.u.v?.k);
111console.log("anonymousObj.u.v?.z", anonymousObj.u.v?.z);
112console.log("anonymousObj.u.q?.k", anonymousObj.u.q?.k);
113console.log("anonymousObj.p?.v.k", anonymousObj.p?.v.k);
114115
// Nützliche Zuweisungen, um den Fall undefined und null gemeinsam zu behandeln:
116anonymousObj.name ||= "Max Mustermann";
117118
119
120
console.log("Date -------------------------------------------------------------------");
121let date = new Date("8.6.2024"); // ACHTUNG: Locale-Settings
122console.log(date);
123124
125
console.log("Funktionen sind auch Objekte -------------------------------------------");
126let func = function () {
127return "Hello World";
128};
129console.log(func, func());
130131
132
console.log("Arrays -----------------------------------------------------------------");
133let temp = undefined;
134let $a = [1];
135console.log("let $a = [1]; $a, $a.length", $a, $a.length);
136$a.push(2); // append
137console.log("$a.push(2); $a", $a);
138temp = $a.unshift(0); // "prepend" -> return new length
139console.log("temp = $a.unshift(0); temp, $a", temp, $a);
140temp = $a.shift(); // remove first element -> return removed element
141console.log("temp = $a.shift(); temp, $a", temp, $a);
142// Um zu prüfen ob eine Datenstruktur ein Array ist:
143console.log("Array.isArray($a)", Array.isArray($a));
144console.log("Array.isArray({})", Array.isArray({}));
145console.log("Array.isArray(1)", Array.isArray(1));
146147
148
console.log("Symbols ---------------------------------------------------------------");
149let sym1 = Symbol("1"); // a unique and immutable primitive value
150let sym2 = Symbol("1");
151let obj1Values = { sym1: "value1", sym2: "value2" };
152console.log(obj1Values);
153console.log(`sym1 in ${JSON.stringify(obj1Values)}: `, sym1 in obj1Values);
154let obj2Values = { [sym1]: "value1", [sym2]: "value2" };
155console.log(obj2Values);
156console.log(`sym1 in ${JSON.stringify(obj2Values)}: `, sym1 in obj2Values);
157console.log(obj1Values, " vs. ", obj2Values);
158159
console.log( { sym1 : "this", sym1 : "that" }); // ??? { sym1: "that" }
160console.log("sym1 == sym2", sym1 == sym2);
1// Die Funktionsdeklaration der Funktion "hello" ist "hochgezogen" (eng. "hoisted")
2// und kann hier verwendet werden.
3hello("Michael");
45
function hello(person = "World" /* argument with default value */) {
6log(`fun: Hello ${person}!`);
7}
8hello();
910
waitOnInput();
1112
const helloExpr = function () { // Anonymer Funktionsausdruck
13log("expr: Hello World!");
14};
1516
// Arrow Functions
17const times3 = (x) => x * 3;
18log("times3(5)", times3(5)); // 15
1920
const helloArrow = () => log("arrow: Hello World!");
21const helloBigArrow = () => {
22const s = "Hello World!";
23log("arrow: " + s);
24return s;
25};
26helloExpr();
27helloArrow();
2829
var helloXXX = function helloYYY() { // benannter Funktionsausdruck
30// "helloYYY" ist _nur_ innerhalb der Funktion sichtbar und verwendbar
31// "arguments" ist ein Arrays-vergleichbares Objekt
32// und enthält alle Argumente der Funktion
33log(`Hello: `, ...arguments); // "..." ist der "Spread Operator"
34};
35helloXXX("Michael", "John", "Jane");
3637
waitOnInput();
3839
function sum(...args) {
40// rest parameter
41log("typeof args: " + typeof args + "; isArray: "+ Array.isArray(args));
42log("args: " + args);
43log("args:", ...args); // es werden alle Elemente des Arrays als einzelne Argumente übergeben
44return args.reduce((a, b) => a + b, 0); // function nesting
45}
46log(sum(1, 2, 3, 4, 5)); // 15
47log(sum());
4849
/* Generator Functions */
50function* fib() {
51// generator
52let a = 0,
53b = 1;
54while (true) {
55yield a;
56[a, b] = [b, a + b];
57}
58}
59const fibGen = fib();
60log(fibGen.next().value); // 0
61log(fibGen.next().value); // 1
62log(fibGen.next().value); // 1
63log(fibGen.next().value); // 2
64/* Will cause an infinite loop:
65for (const i of fib()) console.log(i);
66// 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 ... */
Voraussetzung: Installieren Sie Node.js (http://nodejs.org/).
Hello World in Node.js
Starten Sie die Konsole/Terminal und schreiben Sie ein einfaches JavaScript Programm, das "Hello World" ausgibt.
Hello World auf der JavaScript Console
Starten Sie einen Browser und aktivieren Sie die JavaScript Console in den Entwicklerwerkzeugen. Schreiben Sie ein einfaches JavaScript Programm, das "Hello World" ausgibt.
Prototyping mit der JavaScript Konsole
Schreiben Sie ein kurzes JavaScript Programm, das programmatisch zum Ende des Dokuments scrollt.
Hinweise:
das von document.body
referenziert HTML Element enthält den gesamten Inhalt des Dokuments
die aktuellen Abmaße des Dokuments können Sie mit der Funktion window.getComputedStyle(<HTML Element>).height
ermitteln; geben Sie den Wert auf der Konsole aus bevor Sie das Dokument scrollen; was fällt Ihnen auf?
um zu scrollen, können Sie window.scrollTo(x,y) verwenden
um den Integer Wert eines Wertes in Pixeln zu bestimmen, können Sie parseInt verwenden
1// Gleichheit == // mit Typumwandlung (auch bei <, >, <=, >=)
23
// strikt gleich === // ohne Typumwandlung
4// strike Ungleichheit !== // ohne Typumwandlung
56
log('1 == "1": ', 1 == "1");
7log('1 === "1": ', 1 === "1");
8log("1.0 == 1: ", 1 == 1.0);
9log("1.0 === 1: ", 1 === 1.0);
10log("1 === 1n: ", 1 === 1n);
11log("1 == 1n: ", 1 == 1n);
12log('1 < "1"', 1 < "1");
13log('0 < "1"', 0 < "1");
14log('0 <= "0"', 0 <= "0");
15log('"abc" <= "d"', "abc" <= "d");
1617
log('"asdf" === "as" + "df"', "asdf" === "as" + "df"); // unlike Java!
1819
log("NaN === NaN: ", NaN === NaN);
20log("NaN == NaN: ", NaN == NaN);
21log("null === NaN: ", null === NaN);
22log("null == NaN: ", null == NaN);
23log("null === null: ", null === null);
24log("null == null: ", null == null);
25log("undefined === undefined: ", undefined === undefined);
26log("undefined == undefined: ", undefined == undefined);
27log("null === undefined: ", null === undefined);
28log("null == undefined: ", (null == undefined)+ "!");
2930
31
const a1 = [1, 2, 3];
32const a2 = [1, 2, 3];
33log("const a1 = [1, 2, 3]; a1 == [1, 2, 3]: ", a1 == [1, 2, 3]);
34log("const a1 = [1, 2, 3]; a1 == a1: ", a1 == a1);
35log("const a1 = [1, 2, 3]; a1 === a1: ", a1 === a1);
36log("const a1 = [1, 2, 3]; const a2 = [1, 2, 3]; a1 === a2: ", a1 === a2);
37log("const a1 = [1, 2, 3]; const a2 = [1, 2, 3]; a1 == a2: ", a1 == a2);
38log(
39"flatEquals(a1,a2):",
40a1.length == a2.length && a1.every((v, i) => v === a2[i])
41);
4243
44
let firstJohn = { person: "John" };
45show('let firstJohn = { person: "John" };');
46let secondJohn = { person: "John" };
47show('let secondJohn = { person: "John" };');
48let basedOnFirstJohn = Object.create(firstJohn);
49show("let basedOnFirstJohn = Object.create(firstJohn)");
50log("firstJohn == firstJohn: ", firstJohn == firstJohn);
51log("firstJohn === secondJohn: ", firstJohn === secondJohn);
52log("firstJohn == secondJohn: ", firstJohn == secondJohn);
53log("firstJohn === basedOnFirstJohn: ", firstJohn === basedOnFirstJohn);
54log("firstJohn == basedOnFirstJohn: ", firstJohn == basedOnFirstJohn);
5556
57
{
58const obj = {
59name: "John",
60age: 30,
61city: "Berlin",
62};
63log("\nTyptests und Feststellung des Typs:");
64log("typeof obj", typeof obj);
65log("obj instanceof Object", obj instanceof Object);
66log("obj instanceof Array", obj instanceof Array);
67}
68{
69const obj = { a: "lkj" };
70const obj2 = Object.create(obj);
71log("obj2 instanceof obj.constructor", obj2 instanceof obj.constructor);
72}
7374
log("\n?-Operator/if condition and Truthy and Falsy Values:");
75log('""', "" ? "is truthy" : "is falsy");
76log("f()", (() => {}) ? "is truthy" : "is falsy");
77log("Array ", Array ? "is truthy" : "is falsy");
78log("obj ", {} ? "is truthy" : "is falsy");
79log("undefined ", undefined ? "is truthy" : "is falsy");
80log("null ", null ? "is truthy" : "is falsy");
81log("0", 0 ? "is truthy" : "is falsy");
82log("1", 1 ? "is truthy" : "is falsy");
NaN (Not a Number) repräsentiert das Ergebnis einer Operation die keinen sinnvollen Wert hat. Ein Vergleich mit NaN ist immer false
. Um zu überprüfen, ob ein Wert NaN ist muss isNaN(<Value>)
verwendet werden.
1const arr = [1, 3, 4, 7, 11, 18, 29];
23
log("if-else_if-else:");
4if (arr.length == 7) {
5ilog("arr.length == 7");
6} else if (arr.length < 7) {
7ilog("arr.length < 7");
8} else {
9ilog("arr.length > 7");
10}
1112
log("\nswitch (integer value):");
13switch (arr.length) {
14case 7:
15ilog("arr.length == 7");
16break;
17case 6:
18ilog("arr.length == 6");
19break;
20default:
21ilog("arr.length != 6 and != 7");
22}
2324
log("\nswitch (string value):");
25switch ("foo") {
26case "bar":
27ilog("it's bar");
28break;
29case "foo":
30ilog("it's foo");
31break;
32default:
33ilog("not foo, not bar");
34}
3536
log("\nswitch (integer - no type conversion):");
37switch (
381 // Vergleich auf strikte Gleichheit (===)
39) {
40case "1":
41ilog("string(1)");
42break;
43case 1:
44ilog("number(1)");
45break;
46}
4748
ilog("\nfor-continue:");
49for (let i = 0; i < arr.length; i++) {
50const v = arr[i];
51if (v % 2 == 0) continue;
52log(v);
53}
5455
ilog("\n(for)-break with label:");
56outer: for (let i = 0; i < arr.length; i++) {
57for (let j = 0; j < i; j++) {
58if (j == 3) break outer;
59log(arr[i], arr[j]);
60}
61}
6263
ilog("\nin (properties of Arrays; i.e. the indexes):");
64for (const key in arr) {
65log(key, arr[key]);
66}
6768
ilog("\nof (values of Arrays):");
69for (const value of arr) {
70log(value);
71}
7273
ilog("\nArray and Objects - instanceof:");
74log("arr instanceof Object", arr instanceof Object);
75log("arr instanceof Array", arr instanceof Array);
7677
const obj = {
78name: "John",
79age: 30,
80city: "Berlin",
81};
8283
ilog("\nin (properties of Objects):");
84for (const key in obj) {
85log(key, obj[key]);
86}
8788
/* TypeError: obj is not iterable
89for (const value of obj) {
90log(value);
91}
92*/
9394
{
95ilog("\nIteration über Iterables (here: Map):");
96const m = new Map();
97m.set("name", "Elisabeth");
98m.set("alter", 50);
99log("Properties of m: ");
100for (const key in m) {
101log(key, m[key]);
102}
103log("Values of m: ");
104for (const [key, value] of m) {
105log(key, value);
106}
107}
108109
{
110ilog("\nWhile Loop: ");
111let c = 0;
112while (c < arr.length) {
113const v = arr[c];
114if (v > 10) break;
115log(v);
116c++;
117}
118}
119120
{
121ilog("\nDo-While Loop: ");
122let c = 0;
123do {
124log(arr[c]);
125c++;
126} while (c < arr.length);
127}
Die Tatsache, dass insbesondere null als auch undefined falsy sind, wird of in Bedingungen ausgenutzt (z. B., if (!x)...
).
1console.log("try-catch-finally - Grundlagen -----------------------------------------");
23
try {
4let i = 1 / 0; // Berechnungen erzeugen nie eine Exception
5console.log("i", i);
6} catch {
7console.error("console.log failed");
8} finally {
9console.log("computation finished");
10}
1112
console.log("Programmierfehler behandeln --------------------------------------------");
13try {
14const obj = {};
15obj = { a: 1 };
16} catch ({ name, message }) {
17console.error(message);
18} finally {
19console.log("object access finished");
20}
2122
console.log("Handling of a specific error -------------------------------------------");
23try {
24throw new RangeError("out of range");
25} catch (error) {
26if (error instanceof RangeError) {
27const { name, message } = error;
28console.error("a RangeError:", name, message);
29} else {
30throw error;
31}
32} finally {
33console.log("error handling finished");
34}
In JavaScript können während der Laufzeit Fehler auftreten, die (z. B.) in Java während des kompilierens erkannt werden.
removeNthElement
Implementieren Sie eine Funktion, die ein Array übergeben bekommt und ein neues Array zurückgibt in dem jedes n-te Element nicht vorkommt.
Beispiel: removeNthElement([1,2,3,4,5,6,7], 2) \(\Rightarrow\) [1,3,5,7]
Schreiben Sie Ihren Code in eine JavaScript Datei und führen Sie diese mit Hilfe von Node.js aus.
Testen Sie Ihre Funktion mit verschiedenen Eingaben und lassen Sie sich das Ergebnis ausgeben (z. B. console.log(removeNthElement([1,2,3,4,5,6,7],2))
)!
removeNthElement mit Fehlerbehandlung
Erweitern Sie die Implementierung von removeNthElement so, dass die Funktion einen Fehler wirft, wenn das übergebene Array kein Array ist oder wenn der zweite Parameter kein positiver Integer ist.
Testen Sie alle Fehlerzustände und fangen Sie die entsprechenden Fehler ab (catch) und geben Sie die Nachrichten aus.
Einfacher RPN Calculator
Implementieren Sie einen einfachen RPN (Reverse Polish Notation) Calculator, der eine Liste von Zahlen und Operatoren (+, -, *, /) als Array entgegennimmt und das Ergebnis berechnet.
Nutzen Sie keine if oder switch Anweisung, um die Operatoren zu unterscheiden. Nutzen Sie stattdessen ein Objekt. Sollte der Operator unbekannt sein, dann geben Sie eine entsprechende Fehlermeldung aus.
Beispiel: eval([2,3,"+",4,"*"]) \(\Rightarrow\) 20
(Neuer Code sollte var nicht mehr verwenden!)
1let y = "yyy"; // wie zuvor
2const z = "zzz";
34
// Der Gültigkeitsbereich von var ist die umgebende Funktion oder der
5// globale Gültigkeitsbereich.
6// Die Definition ist hochgezogen (eng. "hoisted") (initialisiert mit undefined);
7var x = "xxx";
89
function sumIfDefined(a, b) {
10// ⚠️ Der folgende Code ist NICHT empfehlenswert!
11// Er dient der Visualisierung des Verhaltens von var.
12if (parseInt(a)) {
13var result = parseInt(a);
14} else {
15result = 0;
16}
17const bVal = parseFloat(b);
18if (bVal) {
19result += bVal;
20}
21return result;
22}
2324
ilog("sumIfDefined()", sumIfDefined()); // 0
25ilog("sumIfDefined(1)", sumIfDefined(1)); // 1
26ilog("sumIfDefined(1, 2)", sumIfDefined(1, 2)); // 3
27ilog('sumIfDefined(1, "2")', sumIfDefined(1, "2")); // 3
28ilog("undefined + 2", undefined + 2);
29ilog('sumIfDefined(undefined, "2")', sumIfDefined(undefined, "2")); // 2
3031
function global_x() {
32ilog("global_x():", x, y, z);
33}
3435
function local_var_x() {
36ilog("local_var_x(): erste Zeile (x)", x);
3738
var x = 1; // the declaration of var is hoisted, but not the initialization
39let y = 2;
40const z = 3;
4142
ilog("local_var_x(): letzte Zeile (x, y, z)", x, y, z); // 1 2 3
43}
4445
global_x();
46local_var_x();
4748
ilog("nach global_x() und local_var_x() - x, y, z:", x, y, z);
4950
51
// Hier, ist nur die Variablendeklaration (helloExpr) "hoisted", aber nicht
52// die Definition. Daher kann die Funktion nicht vorher im Code aufgerufen
53// werden!
54try {
55helloExpr();
56} catch ({error, message}) {
57log("calling helloExpr() failed:", error, "; message: ", message);
58}
59var helloExpr = function () {
60log("expr: Hello World!");
61};
62// ab jetzt funktioniert es
63helloExpr();
1log("Array Destructuring:");
23
let [val1, val2] = [1, 2, 3, 4];
4ilog("[val1, val2] = [1, 2, 3, 4]:", "val1:", val1, ", val2:", val2); // 1
56
log("Object Destructuring:");
78
let { a, b } = { a: "aaa", b: "bbb" };
9ilog('let { a, b } = { a: "aaa", b: "bbb" }: ', "a:", a, ", b:", b); // 1
1011
let { a: x, b: y } = { a: "aaa", b: "bbb" };
12ilog('let { a: x, b: y } = { a: "aaa", b: "bbb" }: ', "x:", x, ", y:", y); // 1
1314
let { a: u, b: v, ...w } = { a: "+", b: "-", c: "*", d: "/" };
15ilog(
16'let { a: u, b: v, ...w } = { a: "+", b: "-", c: "*", d: "/" }:',
17"u:",
18u,
19", v:",
20v,
21", w:",
22JSON.stringify(w), // just for better readability/comprehension
23);
2425
let { k1, k2 } = { a: "a", b: "b" };
26ilog('let { k1, k2 } = { a: "a", b: "b" }:', "k1:", k1, ", k2:", k2);
27// "undefined undefined", weder k1 noch k2 sind definiert
1const someJSON = `{
2"name": "John",
3"age": 30,
4"cars": {
5"American": ["Ford"],
6"German": ["BMW", "Mercedes", "Audi"],
7"Italian": ["Fiat","Alfa Romeo", "Ferrari"]
8}
9}
10`
1112
// JSON.parse(...) JSON String => JavaScript Object
13const someObject = JSON.parse(someJSON);
1415
someObject.age = 31;
16someObject.cars.German.push("Porsche");
17someObject.cars.Italian.pop();
18console.log(someObject);
1920
// JSON.stringify(...) JavaScript Object => JSON String
21console.log(JSON.stringify(someObject, null, 2));
JSON requires that keys must be strings and strings must be enclosed in double quotes.
Eingebaute Unterstützung basierend auf entsprechenden Literalen (Strings in "/") und einer API
inspiriert von der Perl Syntax
Methoden auf regulären Objekten: test
(e.g., RegExp.test(String)
).
Methoden auf Strings, die reguläre Ausdrücke verarbeiten: search
, match
, replace
, split
, ...
1{
2const p = /.*[1-9]+H/; // a regexp
3console.log(p.test("ad13H"));
4console.log(p.test("ad13"));
5console.log(p.test("13H"));
6}
7{
8const p = /[1-9]+H/g;
9const s = "1H, 2H, 3P, 4C";
10console.log(s.match(p));
11console.log(s.replace(p, "XX"));
12}
1class Figure {
2calcArea() {
3throw new Error("calcArea is not implemented");
4}
5}
6class Rectangle extends Figure {
7height;
8width;
910
constructor(height, width) {
11super();
12this.height = height;
13this.width = width;
14}
1516
calcArea() {
17return this.height * this.width;
18}
1920
get area() {
21return this.calcArea();
22}
2324
set area(value) {
25throw new Error("Area is read-only");
26}
27}
2829
const r = new Rectangle(10, 20);
30console.log("r instanceof Figure", r instanceof Figure); // true
31console.log(r.width);
32console.log(r.height);
33console.log(r.area); // 200
3435
try {
36r.area = 300; // Error: Area is read-only
37} catch (e) {
38console.error(e.message);
39}
Queue.mjs exportiert die Klasse Queue
1/* Modul für den Datentyp Warteschlange (Queue). */
2export class Queue {
3#last = null; // private field
4#first = null;
5constructor() {} // "default constructor"
6enqueue(elem) {
7if (this.#first === null) {
8const c = { e: elem, next: null };
9this.#first = c;
10this.#last = c;
11} else {
12const c = { e: elem, next: null };
13this.#last.next = c;
14this.#last = c;
15}
16}
17dequeue() {
18if (this.#first === null) {
19return null;
20} else {
21const c = this.#first;
22this.#first = c.next;
23return c.e;
24}
25}
26head() {
27if (this.#first === null) {
28throw new Error("Queue is empty");
29} else {
30return this.#first.e;
31}
32}
33last() {
34if (this.#first === null) {
35throw new Error("Queue is empty");
36} else {
37return this.#last.e;
38}
39}
40isEmpty() {
41return this.#first === null;
42}
43}
log.mjs verwendet (import
) die Klasse Queue und exportiert Funktionen zum Loggen
1import { Queue } from "./Queue.mjs"; // import des Moduls "Queue.mjs"
23
const messages = new Queue();
45
export function log(...message) {
6if (messages.isEmpty()) {
7messages.enqueue(message);
8} else {
9message.unshift("\n");
10messages.last().push(...message);
11}
12}
ECMAScript Module verwenden immer den strict mode.
Import Statements erlauben das selektierte importieren als auch das Umbenennen von importierten Elementen (z. B., import { Queue as Q } from "./Queue.mjs";
).
this ist ein "zusätzlicher" Parameter, dessen Wert von der aufrufenden Form abhängt
this ermöglicht den Methoden den Zugriff auf ihr Objekt
this wird zum Zeitpunkt des Aufrufs gebunden (außer bei Arrow-Funktionen)
1//"use strict";
23
function counter () {
4// console.log(this === globalThis); // true
5if(this.count) // this is the global object if we don't use strict mode
6this.count ++;
7else {
8this.count = 1;
9}
1011
return this.count;
12}
1314
const counterExpr = function () {
15if(this.count)
16this.count ++;
17else {
18this.count = 1;
19}
2021
return this.count;
22}
2324
const counterArrow = () => {
25console.log(this);
26console.log(this === globalThis);
27this.count = this.count ? this.count + 1 : 1;
28return this.count;
29}
3031
console.log("\nCounter");
32console.log(counter()); // 1
33console.log(counter()); // 2
34console.log(`Counter (${globalThis.count})`);
3536
console.log("\nCounterExpression");
37console.log(counterExpr()); // 3
38console.log(counterExpr()); // 4
3940
console.log("\nCounter");
41const obj = {};
42console.log(counter.apply(obj)); // 1 - we set a new "this" object!
43console.log(counterExpr.apply(obj)); // 2
4445
console.log(`\nCounterArrow (${this.count})`);
46console.log(counterArrow.apply(obj)); // 1
47console.log(counterArrow.apply(undefined)); // 2
48console.log(counterArrow.apply()); // 3
49console.log(counterArrow.apply(obj)); // 4
50console.log(counterArrow.apply({})); // 5
5152
console.log("\nCounter (global)");
53console.log(counter());
54console.log(counterExpr());
1function add(x, y) {
2return x + y;
3}
45
// Partial function application:
6const add2 = add.bind(null, 2); // "null" is the value of "this"
7console.log(add2(3));
89
10
function addToValue(b) {
11return this.x + b;
12}
13console.log(addToValue.call({x : 0}, -101));
Verwendung von Object.create
zur Initialisierung der Prototype Chain:
1const p = { s : "p" };
2const c = Object.create(p);
3const gc = Object.create(c);
Verwendung der Eigenschaften von Prototypen:
1const p = { s : "p" };
2const c = Object.create(p);
3const gc = Object.create(c);
4gc.t = "q";
5gc.s = "gc"
6console.log(gc.s); // gc
7delete gc.s;
8console.log(gc.s); // p
Pseudoclassical Inheritance
1function Person(name, title){ this.name = name; this.title = title; } // constructor
2Person.prototype.formOfAddress = function (){
3const foa = "Dear ";
4if(this.title){ foa += this.title+" "; }
5return foa + this.name;
6}
7function Student(name, title, id, email) { // constructor
8Person.call(this, name, title);
9this.id = id;
10this.email = email;
11}
12Student.prototype = Object.create(Person.prototype);
13Student.prototype.constructor = Student;
1415
const aStudent = new Student("Emilia Galotti", "Mrs.", 1224441, 'emilia@galotti.com');
Objektabhängigkeiten
1function Person(name, title){ … }
2Person.prototype.formOfAddress = function (){ … }
34
function Student(name, title, id, email) { … }
5Student.prototype = Object.create(Person.prototype);
6Student.prototype.constructor = Student;
78
const p = new Person(…); const s = new Student(…);
Die Eigenschaft prototype
einer Funktion (F) verweist auf das Objekt, dass als Prototype (__proto__
) verwendet wird, wenn die Funktion als Konstruktor verwendet wird. D. h. im Falle einer Instantiierung von F
(d. h. const newF = new F()
) wird das Objekt, das durch F.prototype
referenziert wird, als Prototype (newF.__proto__
) des neu erstellten Objekts (newF
) verwendet.
1// Prototypen
2console.log("{}.__proto__: ",{}.__proto__);
3console.log("Array.prototype: ",Array.prototype);
4console.log("Array.prototype.__proto__: ",Array.prototype.__proto__);
5console.log("Object.prototype: ",Object.prototype);
6console.log("Object.__proto__: ",Object.__proto__);
78
let o = { created: "long ago" };
9var p = Object.create(o);
10console.log("Object.getPrototypeOf(o): " + Object.getPrototypeOf(o));
11console.log("o.isPrototypeOf(p):" + o.isPrototypeOf(p));
12console.log("Object.prototype.isPrototypeOf(p): " + Object.prototype.isPrototypeOf(p));
1let a = [1, 10, 100, 1000];
2try { console.log(a.fold()); } catch (error) {
3console.log("error: ", error.message);
4}
56
// ADDING FUNCTIONS TO Array.prototpye IS NOT RECOMMENDED! IF ECMAScript EVENTUALLY
7// ADDS THIS METHOD (I.E. fold) TO THE PROTOTYPE OF ARRAY OBJECTS, IT MAY CAUSE HAVOC.
8Array.prototype.fold = function (f) {
9if (this.length === 0) {
10throw new Error("array is empty");
11} else if (this.length === 1) {
12return this[0];
13} else {
14let result = this[0];
15for (let i = 1; i < this.length; i++) {
16result = f(result, this[i]);
17}
18return result;
19}
20};
2122
console.log(a.fold((u, v) => u + v));
1<html lang="en">
2<head>
3<meta charset="utf-8" />
4<meta name="viewport" content="width=device-width, initial-scale=1.0" />
5<title>DOM Manipulation with JavaScript</title>
6<script>
7function makeScriptsEditable() {
8const scripts = document.getElementsByTagName("script");
9for (const scriptElement of scripts) {
10scriptElement.contentEditable = false;
11const style = scriptElement.style;
12style.display = "block";
13style.whiteSpace = "preserve";
14style.padding = "1em";
15style.backgroundColor = "yellow";
16}
17}
18</script>
19</head>
20<body>
21<h1>DOM Manipulation with JavaScript</h1>
22<p id="demo">This is a paragraph.</p>
23<button
24type="button"
25onclick="
26document.getElementById('demo').style.color = 'red';
27makeScriptsEditable();
28document.querySelector('button').style.display = 'none';"
29>
30Magic!
31</button>
3233
<script>
34const demoElement = document.getElementById("demo");
35const style = demoElement.style;
36demoElement.addEventListener(
37"mouseover",
38() => (style.color = "green"),
39);
40demoElement.addEventListener(
41"mouseout",
42() => (style.color = "unset"),
43);
44</script>
4546
<p>Position der Mouse: <span id="position"></span></p>
47<script>
48window.addEventListener("mousemove", () => {
49document.getElementById("position").innerHTML =
50`(${event.clientX}, ${event.clientY})`;
51});
52</script>
53</body>
54</html>
1// "express" and "cors" are CommonJS modules, which requires us to use the
2// "default import" syntax.
3import express from "express";
45
// Cross-Origin Resource Sharing (CORS); This is required to allow the browser
6// using a different domain to load the HTML to make requests to this server.
7// I. e., we can use the HTML file from the "web-javascript" project to make
8// requests to this server.
9import cors from "cors";
10const APP_PORT = 5080;
1112
const app = express();
1314
app.get("/users", cors(), function (req, res) {
15res.set("Content-Type", "application/json");
16res.end(`{
17"user1" : {
18"name" : "dingo",
19"password" : "1234",
20"profession" : "chef",
21"id": 1
22},
2324
"user2" : {
25"name" : "ringo",
26"password" : "asdf",
27"profession" : "boss",
28"id": 3
29}
30}`);
31});
3233
app.listen(APP_PORT, function () {
34console.log(`Users App @ http://127.0.0.1:${APP_PORT}`);
35});
Express ist ein minimalistisches Web-Framework für Node.js, das die Entwicklung von Webanwendungen vereinfacht. Die Installation kann über einen Packagemanager erfolgen.
Installieren Sie (z. B.) pnpm (https://pnpm.io/) und nutzen Sie danach pnpm, um die benötigten Module zu installieren:
$ pnpm init
$ pnpm install express
Danach starten Sie Ihren Server mit:
node --watch UsersServer.mjs
1<html lang="en">
2<head>
3<meta charset="utf-8" />
4<meta name="viewport" content="width=device-width, initial-scale=1.0" />
5<title>Eventhandling</title>
6</head>
7<body>
8<script>
9/* Using Promises:
10function getUsers() {
11fetch('http://127.0.0.1:4080/users')
12.then(response => response.json())
13.then(users => {
14const usersElement = document.getElementById('users');
15usersElement.innerText = JSON.stringify(users);
16});
17}
18*/
1920
/* Using async/await: */
21async function getUsers() {
22let response = await fetch("http://127.0.0.1:5080/users");
23let users = await response.json();
24const usersElement = document.getElementById("users");
25usersElement.innerText = JSON.stringify(users);
26}
27</script>
2829
<div id="users"></div>
30<button onclick="getUsers()">Get Users</button>
31</body>
32</html>
Im Folgenden verwenden wir zur Client-/Server-Kommunikation insbesondere Websockets.
Server
1const express = require('express');
2const app = express();
34
const expressWs = require('express-ws')(app);
56
let clients = 0;
7let playerWSs = [];
89
let adminWS = null;
10let answersCount = 0;
11let correctAnswersCount = 0;
1213
app.use(express.static('.')); // required to serve static files
1415
16
function sendCurrentPlayers() {
17if (adminWS && playerWSs.length > 0) {
18allPlayers = playerWSs
19.filter(player => player.name)
20.map(player => { return { "id": player.id, "name": player.name } })
21console.log("Sending current players: " + JSON.stringify(allPlayers));
22adminWS.send(JSON.stringify({ "type": "players", "players": allPlayers }));
23}
24}
2526
function sendNextQuestion() {
27answersCount = 0;
28correctAnswersCount = 0;
29const question = "What is the capital of France?";
30const answers = ["Paris", "London", "Berlin", "Madrid"];
31const correct = "Paris";
3233
const nextQuestion = JSON.stringify({
34"type": "question",
35"question": question,
36"answers": ["Paris", "London", "Berlin", "Madrid"]
37})
38playerWSs.forEach(player => player.ws.send(nextQuestion));
39adminWS.send(JSON.stringify({
40"type": "question",
41"question": question,
42"answers": answers,
43"correct": correct
44}));
45}
4647
function sendResults() {
48const results = playerWSs.map(player => {
49return { "id": player.id, "name": player.name, "wins": player.wins }
50});
51const sortedResults = results.sort((a, b) => b.wins - a.wins);
52const resultsMsg = JSON.stringify({
53"type": "results",
54"results": sortedResults
55});
56playerWSs.forEach(player => player.ws.send(resultsMsg));
57adminWS.send(resultsMsg);
5859
}
6061
62
function handleAnswer(clientId, answer) {
63const correct = answer.answer === "Paris";
64const player = playerWSs.find(player => player.id === clientId);
65if (correct) {
66if (correctAnswersCount === 0) {
67player.wins++;
68}
69correctAnswersCount++;
70}
71answersCount++;
72if (answersCount === playerWSs.length) {
73// sendNextQuestion();
74sendResults();
75} else {
76adminWS.send(JSON.stringify({
77"type": "answers",
78"count": answersCount,
79"correctAnswersCount": correctAnswersCount
80}));
81}
82}
8384
85
app.ws('/player', function (ws, request) {
86const clientId = clients++;
87const playerData = { "ws": ws, "id": clientId, "wins": 0 }
88playerWSs.push(playerData);
89ws.onmessage = function (event) {
90message = JSON.parse(event.data);
91switch (message.type) {
92case "registration":
93const name = message.name;
94console.log("Registration: " + clientId + "/" + name);
95playerData.name = name;
96sendCurrentPlayers();
97break;
9899
case "answer":
100const answer = message;
101handleAnswer(clientId, answer);
102break;
103104
default:
105console.log("Unknown message: " + message);
106break;
107}
108};
109ws.onclose = function () {
110console.log("Player disconnected: " + clientId);
111playerWSs = playerWSs.filter(player => player.id !== clientId);
112sendCurrentPlayers();
113};
114ws.onerror = function () {
115console.log("Player error: " + clientId);
116playerWSs = playerWSs.filter(player => player.id !== clientId);
117sendCurrentPlayers();
118};
119});
120121
app.ws('/admin', function (ws, req) {
122adminWS = ws;
123sendCurrentPlayers(); // when admin registers her/himself, send current players
124ws.onmessage = function (event) {
125message = JSON.parse(event.data);
126switch (message.type) {
127case "start":
128console.log("Start game");
129sendNextQuestion();
130break;
131default:
132console.log("Unknown message: " + message);
133break;
134}
135};
136137
ws.onclose = (event) => {
138console.log("Admin disconnected");
139adminWS = null;
140sendCurrentPlayers();
141};
142143
ws.onerror = (event) => {
144console.log("Admin error: " + event);
145sendCurrentPlayers();
146};
147148
});
149150
151
var server = app.listen(8800, function () {
152console.log("Quizzy running at http://127.0.0.1:8800/");
153})
Client - Players
1<!DOCTYPE html>
2<html lang="en">
34
<head>
5<script>
6const ws = new WebSocket("ws://localhost:8800/player");
7ws.onmessage = (event) => {
8const data = JSON.parse(event.data);
9switch (data.type) {
10case "question":
11console.log("Question: " + data.question);
12showQuestion(data);
13break;
14case "results":
15const main = document.getElementById("main")
16main.innerText = "Results: " + event.data;
17break;
18default:
19console.log("Unknown message: " + data);
20break;
21}
22};
23ws.onclose = (event) => {
24console.log("Connection closed: " + event);
25}
26ws.onerror = (event) => {
27console.error("Error: " + event);
28}
2930
function showQuestion(data) {
31const main = document.getElementById("main")
32main.innerHTML = `<h1>Question</h1><p>${data.question}</p>`;
3334
function createAnswerButton(answer) {
35const button = document.createElement("button");
36button.innerText = answer;
37button.onclick = submitAnswer(answer);
38return button;
39}
4041
for (answer of data.answers) {
42main.appendChild(createAnswerButton(answer));
43}
44}
4546
function submitAnswer(answer) {
47return () => {
48ws.send(JSON.stringify({
49"type": "answer",
50"answer": answer
51}));
52doWait();
53}
54}
5556
function submitUsername() {
57const name = document.getElementById("username").value;
58ws.send(JSON.stringify({
59"type": "registration",
60"name": name
61}));
6263
doWait();
64}
6566
function doWait() {
67const main = document.getElementById("main");
68main.innerHTML = "Waiting for other players...";
69}
70</script>
7172
<body>
7374
<main id="main">
75<form>
76<input type="text" id="username" placeholder="Username">
77<button type="button" onclick="submitUsername();">Submit</button>
78</form>
79</main>
80</body>
8182
</html>
Client - Admin
1<!DOCTYPE html>
2<html lang="en">
34
<head>
5<script>
6const ws = new WebSocket("ws://localhost:8800/admin");
78
ws.onmessage = (event) => {
9const data = JSON.parse(event.data);
10console.log("Received: " + event.data);
11switch (data.type) {
12case "players":
13const players = document.getElementById("players")
14players.innerText =
15"["+data.players.length + " players] " +
16data.players
17.map(player => player.id + ": " + player.name)
18.join(", ");
19break;
20case "question":
21showQuestion(data);
22break;
23case "results":
24const main = document.getElementById("main")
25main.innerText = "Result: " + event.data;
26break;
27default:
28console.log("unknown: " + event.data);
29break;
30}
31};
3233
ws.onclose = (event) => {
34console.log("Connection closed: " + event);
35const main = document.getElementById("main")
36main.innerText = "Connection closed - you need to restart.";
37};
38ws.onerror = (event) => {
39console.log("Connection error: " + event);
40};
4142
function startGame() {
43ws.send(JSON.stringify({"type": "start"}));
44}
4546
function showQuestion(data) {
47document.getElementById("main").innerText = `
48question: ${data.question}; correct answer: ${data.correct}
49`
50}
51</script>
52</head>
5354
<body>
55<main id="main">
56<h1>Players</h1>
57<p id="players"></p>
58<button type="button" onclick="startGame();">Start Game</button>
59</main>
60</body>
6162
</html>
Die Implementierung dient nur dazu die grundlegenden Konzepte zu verdeutlichen. Es fehlen viele Aspekte wie z. B., Sicherheit.
Im Folgenden wird primär die Verwendung eines JWTs zur Authentifizierung von Benutzern demonstriert.
Die initiale Authentifizierung, die im folgenden Beispiel über ein per get-Request übermittelten Benutzernamen und Passwort erfolgt, ist nicht sicher. In einer realen Anwendung sollte für die initiale Authentifizierung ein sicherer Mechanismus verwendet werden. Eine Möglichkeit wäre z. B. die Verwendung von DIGEST Authentication (nicht empfohlen bzw. nur für einfachste Fälle). Sinnvoll wäre Basic Authentication in Verbindung mit HTTPS oder zum Beispiel der Einsatz von OAuth.
Server
1import express from "express";
2import fs from "fs";
3import path from "node:path";
4import { fileURLToPath } from "url";
5import jwt from "jsonwebtoken";
6import crypto from "crypto";
7import bodyParser from "body-parser";
89
const app = express();
1011
const SERVER_SECRET = crypto.randomBytes(64).toString("hex");
12const users = JSON.parse(
13fs.readFileSync(
14path.resolve(path.dirname(fileURLToPath(import.meta.url)), "users.json"),
15"utf8",
16),
17);
18console.log("Users: " + JSON.stringify(users));
1920
app.use(express.static("."));
21app.use(express.json());
22app.use(bodyParser.text());
2324
const verifyToken = (req, res, next) => {
25console.log("Headers: " + JSON.stringify(req.headers));
2627
const token = req.headers["authorization"].split(" ")[1];
28if (!token) {
29return res.status(401).json({ error: "Unauthorized" });
30}
3132
jwt.verify(token, SERVER_SECRET, (err, decoded) => {
33console.log("Decoded: " + JSON.stringify(decoded));
34if (err) {
35return res.status(401).json({ error: "Unauthorized" });
36}
37req.userIndex = decoded.userIndex;
38next();
39});
40};
4142
app.get("/admin/login", function (req, res) {
43const name = req.query.name;
44const password = req.query.password; // in a real app use hashed passwords!
4546
if (!name || !password) {
47res.status(400).send("Missing name or password");
48return;
49}
5051
let userIndex = -1;
52for (let i = 0; i < users.length; i++) {
53if (users[i].name === name && users[i].password === password) {
54userIndex = i;
55break;
56}
57}
58if (userIndex === -1) {
59res.status(401).send("Credentials invalid.");
60return;
61}
62console.log(
63"Authenticated: " + users[userIndex].name + " " + users[userIndex].password,
64);
6566
// Here, we can use the userIndex to identify the user;
67// but his only works as long as the user list is fixed.
68// In a real app use, e.g., a users's email.
69const token = jwt.sign({ userIndex: userIndex }, SERVER_SECRET, {
70expiresIn: "2h",
71});
72res.status(200).json({ token });
73});
7475
app.post("/admin/question", verifyToken, function (req, res) {
76const userIndex = req.userIndex;
77const question = req.body;
78console.log("Received question: " + question + " from user: " + users[userIndex].name);
7980
res.status(200).send("Question stored. Preliminary answer: 42.");
81});
8283
// Attention: a port like 6666 will not work on (most?) browsers
84const port = 8080;
85var server = app.listen(port, function () {
86console.log(`Running at http://127.0.0.1:${port}/`);
87});
Client (JavaScript)
1/*
2Initializes the login interface.
3*/
4document
5.getElementsByTagName("main")[0]
6.replaceChildren(document.getElementById("log-in").content.cloneNode(true));
7document.getElementById("login-dialog").showModal();
8document.getElementById("login-button").addEventListener("click", login);
910
let jwt = null; // JSON Web Token for authentication
1112
async function login() {
13const name = document.getElementById("administrator").value;
14const password = document.getElementById("password").value;
15const urlEncodedName = encodeURIComponent(name);
16const urlEncodedPassword = encodeURIComponent(password);
17const response = await fetch(
18"http://" +
19location.host +
20"/admin/login?name=" +
21urlEncodedName +
22"&password=" +
23urlEncodedPassword,
24);
25if (response.status !== 200) {
26console.error("Login failed: " + response.status);
27return;
28}
29const responseJSON = await response.json();
30jwt = responseJSON.token;
31console.log("Received JWT: " + jwt);
3233
document.getElementById("login-dialog").close();
3435
document
36.getElementsByTagName("main")[0]
37.replaceChildren(document.getElementById("logged-in").content.cloneNode(true));
38document.getElementById("enter-question-dialog").showModal();
39document.getElementById("send-question").addEventListener("click", sendQuestion);
40}
4142
async function sendQuestion() {
43const question = document.getElementById("question").value;
4445
const response = await fetch("http://" + location.host + "/admin/question", {
46method: "POST",
47headers: {
48"Content-Type": "text/plain",
49Authorization: `Bearer ${jwt}`,
50},
51body: question,
52});
53const text = await response.text();
54showAnswer(text);
55}
5657
function showAnswer(text) {
58document.getElementById("answer-dialog").showModal(false);
59document.getElementById("answer-paragraph").textContent = text;
60}