Nachricht für neue Nutzer.
Nachricht für engagierte Nutzer.

Widget:KnowHowComputer: Unterschied zwischen den Versionen

Aus ZUM-Unterrichten
KKeine Bearbeitungszusammenfassung
KKeine Bearbeitungszusammenfassung
Zeile 1: Zeile 1:
<includeonly><script type="text/JavaScript">
<noinclude>{{msgnw:Widget:KnowHowComputer}}</noinclude><includeonly><script type="text/JavaScript">


         (function () {
         (function () {

Version vom 8. Juni 2025, 21:41 Uhr

<noinclude>{{msgnw:Widget:KnowHowComputer}}</noinclude><includeonly><script type="text/JavaScript"> (function () { const STYLESHEET_HREFS = Array.from(document.styleSheets).filter(cssss => cssss.href).map(cssss => cssss.href); if (!customElements.get('know-how-computer')) { // Define the KnowHowComputer Web Component class KnowHowComputerElement extends HTMLElement { static observedAttributes = ['no-help']; constructor() { super(); this.attachShadow({mode: 'open'}); // Add the HTML template and styles to the shadow DOM this.shadowRoot.innerHTML = `<style> :host { display: block; margin-block:1rem; } .no-help .khc-help { display: none; } :host > div { display: grid; gap: 1rem; grid-template-areas: "nav nav " "main side "; grid-template-columns: 30ch auto; } #memory-selector:not(:has(> button)) { display: none; } code { font-weight: bolder; } nav { grid-area: nav; display: flex; gap: 0.5em; > input { max-height: 2rem; } } main { grid-area: main; display: flex; flex-direction: column; fieldset { display: flex; flex-direction: column; gap:0.5rem; } } aside { grid-area: side; } details { font-size: 0.95rem; } details, details > summary { display: revert; } details > summary { font-weight: bolder; font-size: 1.15rem; } @media (max-width: 600px) { :host > div { display: flex; flex-direction: column; grid-template-areas: "title" "nav" "main" "side"; } nav { flex-direction: column; } } </style> <div> <nav class="steueritem"> <button class="mw-ui-button" type="button" id="neustartschalter" aria-describedby="descr-neustartschalter">Neustart F8</button> <button class="mw-ui-button" type="button" id="ausfuehrenschalter" aria-describedby="descr-ausfuehrenschalter">Ausf&uuml;hren F9</button> </nav> <main> <label for="programmzaehleranzeigefeld">Programmz&auml;hler</label> <input type="text" id="programmzaehleranzeigefeld" aria-describedby="descr-programmzaehleranzeigefeld" readonly disabled/> <label for="speicheranzeigefeld">Hauptspeicher</label> <textarea id="speicheranzeigefeld" aria-describedby="descr-speicheranzeigefeld"> </textarea> <fieldset id="memory-selector"> <legend>Speicherkonfiguration laden:</legend> </fieldset> </main> <aside class="khc-help"> <details open> <summary><span is="h2">Bedeutung der KHC Befehle</span></summary> <dl> <dt><code>inc&nbsp;x</code></dt> <dd>Erh&ouml;he den Wert in Zelle x um 1 <br/> und erh&ouml;he den Programmz&auml;hler um 1 </dd> <dt><code>dec&nbsp;x</code></dt> <dd>Verringere den Wert in Zelle x um 1 <br/> und erh&ouml;he den Programmz&auml;hler um 1 </dd> <dt><code> isz&nbsp;x</code></dt> <dd>Wenn Zelle x den Wert 0 (<span lang="en">zero</span>) enth&auml;lt,<br/> dann erh&ouml;he den Programmz&auml;hler um 2,<br/> sonst erh&ouml;he den Programmz&auml;hler um 1 </dd> <dt><code>jmp&nbsp;x</code></dt> <dd>Setze den Programmz&auml;hler auf den Wert x</dd> <dt><code>stp&nbsp;&nbsp;</code></dt> <dd>Beende das Programm</dd> </dl> </details> <details> <summary>&Uuml;ber den Know-How-Computer</summary> <p>Die Idee zum Know-How-Computer wurde im Jahr 1983 von Wolfgang Back (WDR) und Ulrich Rohde (PC-Magazin) entwickelt. Ver&ouml;ffentlicht wurde das Konzept des KHC u.a. in den Computerzeitschriften MC und PC-Magazin. Weitere Informationen findet man auf <a href="https://de.wikipedia.org/wiki/Know-how-Computer" target="_blank">de.wikipedia.org/wiki/Know-how-Computer</a> </p> <p>Eine leicht abgewandelte Form des Know-How-Computers ist der so genannte Murmelrechner, der auf der Seite <a href="https://www.inf-schule.de/rechner/bonsai/murmelrechner" target="_blank"> www.inf-schule.de/rechner/bonsai/murmelrechner</a> vorgestellt wird. </p> <p>Diese JavaScript-Implementierung des Know-How-Computers ist auch f&uuml;r Menschen mit Seheinschr&auml;nkungen barrierefrei zugänglich. Sie wurde 2025 von Ulrich Kalina erstellt. </p> </details> <details> <summary>Kurzanleitung zu dieser JavaScript Implementierung des KHC</summary> <p>Die Bedienoberfl&auml;che des KHC besteht in dieser Implementierung aus folgenden f&uuml;nf Elementen, die mit TAB bzw. UMSCHALT+TAB fokussiert (angew&auml;hlt) werden k&ouml;nnen.</p> <h3>Mehrzeiliges Textfeld Hauptspeicher</h3> <p id="descr-speicheranzeigefeld">bildet den Hauptspeicher des Know-How-Computers ab. Jede Zeile des Textfeldes enth&auml;lt die Nummer (Adresse) einer Speicherzelle und deren Inhalt. Dabei geh&ouml;rt die Adresse (Zahl vor dem Doppelpunkt) selbst nicht zum Inhalt der Speicherzelle, sondern dient lediglich ihrer eindeutigen Bezeichnung. Die Zelleninhalte (hinter dem Doppelpunkt) k&ouml;nnen entweder KHC-Befehle oder Daten (Zahlenwerte von 0 bis 99) sein.<br> Der gesamte Inhalt des Textfeldes kann &uuml;ber Tastatureingaben ver&auml;ndert werden. Dabei muss in jeder Zeile die Abfolge Adresse, Doppelpunkt, Befehl oder Datenwert eingehalten werden. Da es sich um ein normales Textfeld handelt, kann sein gesamter Inhalt, also ein ganzes KHC-Programm einfach durch Kopieren und Einf&uuml;gen ("Drag and Drop") aus einem Texteditor in den KHC-Speicher importiert oder umgekehrt in einen Editor exportiert und in einer Textdatei gespeichert werden.<br>Der importierte Programmtext darf Kommentare enthalten. Diese beginnen mit einem Semikolon ; und reichen bis zum Zeilenende. Die Kommentare werden automatisch entfernt, wenn der Fokus das mehrzeilige Textfeld verl&auml;sst.<br> Wenn in diesem Textfeld Programmzeilen ver&auml;ndert wurden, sollte vor der n&auml;chsten Programmausf&uuml;hrung ein Neustart mit dem Neustart-Schalter (F8) durchgef&uuml;hrt werden. </p> <h3>Einzeiliges Textfeld Programmz&auml;hler</h3> <p id="descr-programmzaehleranzeigefeld">enth&auml;lt eine Kombination aus Programmz&auml;hler und Befehlsregister. Im Befehlsregister (hinter dem Doppelpunkt) steht immer der aktuelle Befehl, der im n&auml;chsten Schritt ausgef&uuml;hrt werden soll. Der Programmz&auml;hler (vor dem Doppelpunkt) gibt die Adresse der Speicherzelle an, von der dieser Befehl ins Befehlsregister geladen wurde. </p> <h3>Neustart-Schalter (F8)</h3> <p id="descr-neustartschalter">setzt den Programmz&auml;hler auf den Wert 1 zur&uuml;ck und l&auml;dt den Befehl aus der Zelle 1 des Hauptspeichers in das Befehlsregister. Der Inhalt des Hauptspeichers wird durch einen Neustart nicht ver&auml;ndert. Der Neustart-Schalter kann auch mit der F8-Taste aktiviert werden. </p> <h3>Ausf&uuml;hren-Schalter (F9)</h3> <p id="descr-ausfuehrenschalter">f&uuml;hrt den aktuellen Befehl im Befehlsregisters aus und l&auml;dt anschlie&szlig;end den n&auml;chsten Befehl aus dem Hauptspeicher dorthin. Der Ausf&uuml;hren-Schalter kann auch mit der F9-Taste aktiviert werden. </p> </details> </aside> </div>`; STYLESHEET_HREFS.map(function (href) { const styleElem = document.createElement("link"); styleElem.setAttribute("href", href); styleElem.setAttribute("rel", "stylesheet"); return styleElem; }).forEach(linkElem => this.shadowRoot.prepend(linkElem)); // Create the KnowHowComputer instance this.khc = new KnowHowComputer(this.shadowRoot); } refreshMemoryConfigurations() { this.memoryConfigurations = [ ...this.parseMemoryElements() ]; this.refreshMemorySelector(this.memoryConfigurations); } refreshHelp() { this.shadowRoot.querySelector(':host > div') .classList .toggle('no-help', this.hasAttribute('no-help')); } attributeChangedCallback(name, oldValue, newValue) { this.refreshHelp() } // Lifecycle callbacks connectedCallback() { // Component is now in the DOM // Parse khc-memory child elements this.refreshMemoryConfigurations(); this.observer = new MutationObserver(mutations => { this.refreshMemoryConfigurations(); }); this.observer.observe(this, {childList: true}); // Add event listener for memory selector const memorySelector = this.shadowRoot.getElementById('memory-selector'); memorySelector.addEventListener('click', (e) => { const selectedValue = e.target.value; // Load program from khc-memory elements const index = parseInt(selectedValue); if (index >= 0 && index < this.memoryConfigurations.length) { this.loadMemoryConfiguration(this.memoryConfigurations[index]); } e.preventDefault(); }); this.refreshMemoryConfigurations(); this.refreshHelp(); } disconnectedCallback() { // Component is removed from the DOM // Clean up any event listeners if needed if (this.observer) { this.observer.disconnect(); } } // Parse khc-memory child elements parseMemoryElements() { // Use Array.from to convert HTMLCollection to Array return Array.from(this.querySelectorAll('khc-memory')) .map((element, idx) => { const programText = element.textContent.trim(); const [name, program] = this.parseProgram(programText); return { name: name ?? `Program ${idx + 1}`, program: program }; }); } // Parse program text into a program object parseProgram(programText) { const lines = programText.split('\n') .map(line => line.trim()) .filter(line => line !== ''); let name = lines.slice(0, 1) .filter(l => l.startsWith('#')) .map(l => l.replace(/^#*/, '')) .map(l => l.trim()) .find(l => l !== '') ?? null; return [ name, Object.fromEntries( lines .filter(l => !l.startsWith('#')) // filter out comments .map(l => l.split(':', 2).map(s => s.trim())) // split and trim parts .filter(([address, instruction]) => (address && instruction))) // filter incomplete lines; ]; } // Populate memory selector dropdown refreshMemorySelector(memoryConfigurations) { const memorySelector = this.shadowRoot.getElementById('memory-selector'); memorySelector.querySelectorAll('button').forEach(button => memorySelector.removeChild(button)); // Add options for each memory configuration memoryConfigurations.forEach((config, index) => { const button = document.createElement('button'); button.value = (index).toString(); button.textContent = config.name; button.type = 'button'; button.classList.add('mw-ui-button'); memorySelector.appendChild(button); }); } // Load memory configuration loadMemoryConfiguration(config) { if (this.khc) { this.khc.loadProgram(config.program); } } } // KnowHowComputer class that handles the emulator logic class KnowHowComputer { constructor(containerNode) { // Store the container node this.container = containerNode; // Configuration variables this.speicheranzahl = 25; // Standardwert ist 25 für Adr. 1-25, kann aber geändert werden. Adr. 0 wird nicht benutzt this.maxwert = 99; this.spaltenanzahlinspeicheranzeigefeld = 10; this.zeilenanzahlinspeicheranzeigefeld = this.speicheranzahl; // kann auch abweichend von speicheranzahl gewählt werden // State variables this.mem = new Array(this.speicheranzahl + 1); // mem[0] existiert, wird aber nicht benutzt this.pz = 1; // Programmzähler this.br = ""; // Befehlsregister // Constants and utility variables this.ziffern = "0123456789"; this.lueckenzeichen = " \r\n\t"; this.khcbuchstaben = "iszjmpincdecstp"; this.doppelpunkt = ":"; this.kommentarzeichen = ";"; this.khcbefehle = new Array("isz", "jmp", "inc", "dec", "stp"); // Lexer/parser state this.symtypen = new Array(); this.symwerte = new Array(); this.symindex = -1; // Error messages this.fehlermeldungen = new Array(); this.fehlermeldungen[0] = "Keinen Fehler gefunden"; this.fehlermeldungen[1] = "Unerlaubtes Zeichen"; this.fehlermeldungen[2] = "Zahl erwartet"; this.fehlermeldungen[3] = "Adresse nicht im gültigen Bereich"; this.fehlermeldungen[4] = "Wert nicht im gültigen Bereich"; this.fehlermeldungen[5] = "Ungültiger Befehl"; this.fehlermeldungen[6] = "Syntaxfehler: Doppelpunkt erwartet"; this.fehlermeldungen[7] = "Syntaxfehler: Zahl oder Befehl erwartet"; this.fehlermeldungen[8] = "Syntaxfehler: stp ohne Operandenadresse "; this.fehlermeldungen[9] = "Syntaxfehler: Operandenadresse erwartet"; this.fehlermeldungen[10] = "Laufzeitfehler: Kein ausführbarer Befehl"; this.fehlermeldungen[11] = "Laufzeitfehler: Operand würde zu klein"; this.fehlermeldungen[12] = "Laufzeitfehler: Operand würde zu groß"; // Initialize the emulator this.init(); } // Helper methods meldung(index) { alert(this.fehlermeldungen[index]); } stringmitleerzeichenauffuellen(str, bislaenge) { // neu 24.01.2024 return str.trimEnd().padEnd(bislaenge); } rechtsbuendig(s) { return String(s).trimStart().padStart(2) } zahltrimmen(zahlstr, min, max) { var test = parseInt(zahlstr); if (isNaN(test)) { test = min; } else { if (test < min) { test = min; this.meldung(11); } if (test > max) { test = max; this.meldung(12); } } return test; } zeichenistinstring(z, s) { return s.indexOf(z) !== -1; } arrayleeren(a) { a.splice(0) return a; } // Lexer and parser khclexer(eingabe) { var z; // einzelnes Zeichen aus eingabe var i; this.symtypen = this.arrayleeren(this.symtypen); this.symwerte = this.arrayleeren(this.symwerte); this.symindex = -1; var symwert = ""; var symtyp = "l"; // z =zahl, b =buchstabenkette, l =luecke, : =doppelpunkt eingabe = " " + eingabe + " "; var ztyp = "l"; for (i = 0; i < eingabe.length; i++) { z = eingabe.charAt(i); if (this.zeichenistinstring(z, this.lueckenzeichen)) ztyp = "l"; // z ist ein Lückenzeichen else if (this.zeichenistinstring(z, this.ziffern)) ztyp = "z"; // z ist eine Ziffer else if (this.zeichenistinstring(z, this.doppelpunkt)) ztyp = ":"; // z ist ein Doppelpunkt else if (this.zeichenistinstring(z, this.khcbuchstaben)) ztyp = "b"; // z kommt in einem der KHC-Befehle vor else if (this.zeichenistinstring(z, this.kommentarzeichen)) ztyp = ";"; // z ist Kommentarzeichen else return false; if (symtyp == ztyp) { // aktueller symtyp stimmt mit dem ztyp des gerade gelesenen Zeichens z überein symwert = symwert + z; } else { // neues Symbol: if (symtyp != "l") { // bisheriges Symbol in Arrays speichern this.symindex++; this.symwerte[this.symindex] = symwert; this.symtypen[this.symindex] = symtyp; } if (ztyp != ";") { symtyp = ztyp; // Anfang des neuen Symbols in sym-Vars speichern symwert = z; } else { // Kommentarzeichen, also aufhören: return true; } } } return true; } istkhcbefehl(s) { var ok = false; for (let j = 0; j < this.khcbefehle.length; j++) { if (s == this.khcbefehle[j]) { ok = true; } } return ok; } istgueltigezahl(zahlstr, min, max) { var ok = true; var test = parseInt(zahlstr); if (isNaN(test)) ok = false; else { if (test < min || test > max) ok = false; } return ok; } khcparser(zeile) { // Rückgabewert: fehlerindex var ok = this.khclexer(zeile); if (this.symwerte.join("").length == 0) return -1; // Leerzeile liefert keine Fehlermeldung if (!ok) return 1; // "Unerlaubtes Zeichen" if (this.symtypen[0] != "z") return 2; // "Syntaxfehler - Zahl erwartet" if (!this.istgueltigezahl(this.symwerte[0], 1, this.speicheranzahl)) return 3; // "Adresse nicht im gültigen Bereich" if (this.symtypen[1] != ":") return 6; // "Syntaxfehler - Doppelpunkt erwartet" if (this.symtypen[2] != "z" && this.symtypen[2] != "b") return 7; // "Syntaxfehler - Zahl oder Befehl erwartet" if (this.symtypen[2] == "z") { if (!this.istgueltigezahl(this.symwerte[2], 0, this.maxwert)) return 4; // "Wert nicht im gültigen Bereich" } if (this.symtypen[2] == "b") { if (!this.istkhcbefehl(this.symwerte[2])) return 5; // "Ungültiger Befehl" if (this.symwerte[2] == "stp" && this.symtypen.length > 3) return 8; // "Syntaxfehler" if (this.symwerte[2] != "stp") { if (this.symtypen[3] != "z") return 9; // "Syntaxfehler" if (!this.istgueltigezahl(this.symwerte[3], 1, this.speicheranzahl)) return 3; // "Adresse nicht im gültigen Bereich" } } return 0; // keinen Fehler gefunden } // Initialization and control init() { for (let i = 0; i < this.speicheranzahl + 1; i++) { this.mem[i] = "0"; } /**@type HTMLInputElement */ const registeranzeigefeld = this.container.getElementById("programmzaehleranzeigefeld"); /**@type HTMLTextAreaElement */ const speicheranzeigefeld = this.container.getElementById("speicheranzeigefeld"); speicheranzeigefeld.rows = this.zeilenanzahlinspeicheranzeigefeld; speicheranzeigefeld.cols = this.spaltenanzahlinspeicheranzeigefeld; registeranzeigefeld.size = this.spaltenanzahlinspeicheranzeigefeld; this.neustart(); // Set up event listeners this.setupEventListeners(); } setupEventListeners() { const self = this; this.container.getElementById('ausfuehrenschalter').addEventListener('click', function () { self.befehlausfuehren(); }); this.container.getElementById('neustartschalter').addEventListener('click', function () { self.neustart(); }); this.container.getElementById('speicheranzeigefeld').addEventListener('change', function () { self.speicherfeldanzeigeinspeicheruebernehmen(); self.neustart(); }); this.container.getElementById('speicheranzeigefeld').addEventListener('focus', function () { self.zeileinspeicheranzeigefeldmarkieren(self.pz); }); this.container.addEventListener('keydown', function (event) { if (event.keyCode === 120) { // F9 self.registeranzeigefeldaktualisieren(); self.befehlausfuehren(); } else if (event.keyCode === 119) { // F8 self.speicherfeldanzeigeinspeicheruebernehmen(); self.neustart(); // 24.01.2024 } }); } // Load a program from a program object loadProgram(program) { // Reset memory for (let i = 0; i < this.speicheranzahl + 1; i++) { this.mem[i] = "0"; } this.mem[0] = "nicht verwendet"; // Load program into memory for (const [address, instruction] of Object.entries(program)) { if (parseInt(address) >= 1 && parseInt(address) <= this.speicheranzahl) { this.mem[address] = instruction; } } // Restart the emulator this.neustart(); } neustart() { this.pz = 1; // Programmzähler this.br = this.mem[this.pz]; // Befehlsregister this.anzeigeaktualisieren(); this.container.getElementById("speicheranzeigefeld").focus(); } registeranzeigefeldaktualisieren() { var str = this.rechtsbuendig(this.pz) + ": " + this.stringmitleerzeichenauffuellen(this.mem[this.pz], this.spaltenanzahlinspeicheranzeigefeld - 3); this.container.getElementById("programmzaehleranzeigefeld").value = str; } anzeigeaktualisieren() { // Anzeige des gesamten Speichers aktualisieren: const speicherfeld = this.container.getElementById("speicheranzeigefeld"); speicherfeld.value = this.mem.slice(1).map((speicher, idx) => this.rechtsbuendig(idx + 1) + ": " + speicher).join('\n'); this.registeranzeigefeldaktualisieren(); this.zeileinspeicheranzeigefeldmarkieren(this.pz); } befehlausfuehren() { var fehlerindex = 0; this.speicherfeldanzeigeinspeicheruebernehmen(); console.log("mem[pz] = " + this.mem[this.pz]); this.br = this.mem[this.pz]; var z = parseInt(this.br); console.log("Start befehlausfuehren mit z = parseint(br): " + z); console.log("in befehlausfuehren vor Test: pz = " + this.pz + " br = " + this.br); // Test: Ist Inhalt von br nur eine ganze Zahl, also ein Operand? Dann keine Ausführung möglich ... if (!isNaN(z)) { fehlerindex = 10; this.meldung(fehlerindex); } else { // sonst gehen wir von einem Befehl aus: var befehlscode = this.br.substr(0, 3).toLowerCase(); // Bei stp ist keine Adresse erforderlich und es wird auch nichts gemacht: if (befehlscode == "stp") return 0; // sonst muss eine ganze Zahl als Adresse folgen: var adresse = parseInt(this.br.substr(4)); // ab Pos. 4 bis zum Ende ausschneiden if (isNaN(adresse)) return 2; this.pz = parseInt(this.pz); switch (befehlscode) { case "jmp": this.pz = adresse; break; case "isz": if (this.mem[adresse] == 0) this.pz = this.pz + 2; else this.pz = this.pz + 1; break; case "inc": let wert = this.mem[adresse]; wert = parseInt(wert) + 1; wert = this.zahltrimmen(wert, 0, this.maxwert); this.mem[adresse] = wert; this.pz = this.pz + 1; break; case "dec": let wert2 = this.mem[adresse]; wert2 = parseInt(wert2) - 1; wert2 = this.zahltrimmen(wert2, 0, this.maxwert); this.mem[adresse] = wert2; this.pz = this.pz + 1; break; default: fehlerindex = 3; } this.pz = this.zahltrimmen(this.pz, 1, this.speicheranzahl); this.br = this.mem[this.pz]; this.anzeigeaktualisieren(); } this.container.getElementById("speicheranzeigefeld").focus(); this.zeileinspeicheranzeigefeldmarkieren(this.pz); return fehlerindex; } einezeileinspeicheruebernehmen(zeile) { zeile = zeile.toLowerCase(); var fehlerindex = 0; // vorläufig fehlerindex = this.khcparser(zeile); if (fehlerindex == 0) { var pz = parseInt(this.symwerte[0]); var befehlszeile = this.symwerte[2]; if (this.symwerte.length > 3) befehlszeile = befehlszeile + " " + this.symwerte[3]; befehlszeile = this.stringmitleerzeichenauffuellen(befehlszeile, this.spaltenanzahlinspeicheranzeigefeld - 3); this.mem[pz] = befehlszeile; } else { if (fehlerindex > 0) { // -1 wäre Leerzeile alert(this.fehlermeldungen[fehlerindex] + " in\n" + zeile); } } } speicherfeldanzeigeinspeicheruebernehmen() { var zeilen = this.container.getElementById("speicheranzeigefeld").value.split("\n"); for (let i = 0; i < zeilen.length; i++) { this.einezeileinspeicheruebernehmen(zeilen[i]); } this.anzeigeaktualisieren(); } setzeMarkierungInTextFeld(domElemSelector, posStart, posEnde) { const domElem = this.container.querySelector(domElemSelector); if ('selectionStart' in domElem) { domElem.selectionStart = posStart; domElem.selectionEnd = posEnde; domElem.focus(); } else { // Internet Explorer vor Version 9 var inputRange = domElem.createTextRange(); inputRange.moveStart("character", posStart); inputRange.collapse(); inputRange.moveEnd("character", posEnde - posStart); inputRange.select(); } } zeileinspeicheranzeigefeldmarkieren(pz) { let pzstring = this.rechtsbuendig(pz) + ": "; let startpos = this.container.getElementById("speicheranzeigefeld").value.indexOf(pzstring); let endepos = startpos + this.mem[pz].length + 4; this.setzeMarkierungInTextFeld("#speicheranzeigefeld", startpos, endepos); } getCaretPos(domElemSelector) { var pos = 0; const domElem = this.container.querySelector(domElemSelector); if (document.selection) { domElem.focus(); var sel = document.selection.createRange(); sel.moveStart('character', -domElem.value.length); pos = sel.text.length; } else if (domElem.selectionStart || domElem.selectionStart == '0') { pos = domElem.selectionStart; } return pos; } setzeCaretAnZeilenanfang(domElemSelector, zeilennr) { const domElem = this.container.querySelector(domElemSelector); let pos = domElem.value.indexOf(zeilennr + ": "); this.setzeMarkierungInTextFeld(domElemSelector, pos, pos); } } // Register the custom elements customElements.define('know-how-computer', KnowHowComputerElement); } if (!customElements.get('khc-memory')) { // Define the KhcMemoryElement custom element class KhcMemoryElement extends HTMLPreElement { constructor() { super(); // No need for shadow DOM as this is just a data container } // Lifecycle callbacks connectedCallback() { // Hide the element by default as it's just a data container this.style.display = 'none'; } } customElements.define('khc-memory', KhcMemoryElement, {extends: 'pre'}); } // Initialize when the DOM is loaded document.addEventListener('DOMContentLoaded', function () { // For MediaWiki gadget, this would be the initialization code // No need to do anything here as the custom element will initialize itself when added to the DOM }); })(); </script> <know-how-computer <!--{if $noHelp|validate:'boolean'|default:'false' == 'true'}--> no-help <!--{/if}--> > <!--{if $Programm1|default:'' != ''}--> <khc-memory><!--{$Programm1|escape:'htmlall'}--></khc-memory> <!--{/if}--> <!--{if $Programm2|default:'' != ''}--> <khc-memory><!--{$Programm2|escape:'htmlall'}--></khc-memory> <!--{/if}--> <!--{if $Programm3|default:'' != ''}--> <khc-memory><!--{$Programm3|escape:'htmlall'}--></khc-memory> <!--{/if}--> <!--{if $Programm4|default:'' != ''}--> <khc-memory><!--{$Programm4|escape:'htmlall'}--></khc-memory> <!--{/if}--> <!--{if $Programm5|default:'' != ''}--> <khc-memory><!--{$Programm5|escape:'htmlall'}--></khc-memory> <!--{/if}--> </know-how-computer> </includeonly>