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

Widget:KnowHowComputer: Unterschied zwischen den Versionen

Aus ZUM-Unterrichten
KKeine Bearbeitungszusammenfassung
KKeine Bearbeitungszusammenfassung
 
(17 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 1: Zeile 1:
<includeonly>   <script type="text/JavaScript">
<noinclude>{{#tag:pre|{{msgnw:Widget:KnowHowComputer}}}}</noinclude><includeonly><script type="text/JavaScript">


         (function () {
         (function () {
Zeile 7: Zeile 7:
                 // Define the KnowHowComputer Web Component
                 // Define the KnowHowComputer Web Component
                 class KnowHowComputerElement extends HTMLElement {
                 class KnowHowComputerElement extends HTMLElement {
                     static observedAttributes = ['no-help'];
                     static observedAttributes = ['no-help', 'memory-size'];
 
                    /**
                    * @typedef {object} MemoryConfiguration
                    * @property {string} name
                    * @property {Record<int,string>} program
                    */
 
                    /**  @type {MemoryConfiguration[]} */
                    #memoryConfigurations = [];
 
                    /** @type {?MutationObserver} */
                    #observer;
                    /** @type {int} */
                    #selectedMemory;


                     constructor() {
                     constructor() {
Zeile 14: Zeile 28:
                         // Add the HTML template and styles to the shadow DOM
                         // Add the HTML template and styles to the shadow DOM
                         this.shadowRoot.innerHTML = `<style>
                         this.shadowRoot.innerHTML = `<style>
     :host {
     :host {
         display: block;
         display: block;
         margin-block:1rem;
         margin-block: 1rem;
    }
 
    * {
        box-sizing: border-box;
     }
     }


Zeile 23: Zeile 43:
     }
     }


     :host > div {
     :host > section {
         display: grid;
         display: flex;
        flex-direction: column;
         gap: 1rem;
         gap: 1rem;
         grid-template-areas:
    }
            "nav  nav  "
 
            "main side  ";
    .mw-ui-button.khc-help {
         grid-template-columns: 30ch auto;
         min-height: auto;
        min-width: auto;
        aspect-ratio: 1;
         margin-inline-start: auto;
     }
     }


     #memory-selector:not(:has(> button)) {
     #memory-selector:not(:has(> button)) {
         display: none;
         display: none;
    }
    #speicheranzeigefeld {
        overflow-y: scroll;
        overflow-block: scroll;
     }
     }


Zeile 40: Zeile 69:
     }
     }


     nav {
     label {
         grid-area: nav;
         font-weight: bold;
    }
 
    .steueritem {
         display: flex;
         display: flex;
         gap: 0.5em;
        justify-content: space-between;
         gap: 0.5rem;


         > input {
         > input {
Zeile 50: Zeile 83:
     }
     }


     main {
     figure {


         grid-area: main;
         grid-area: main;
         display: flex;
         display: flex;
         flex-direction: column;
         flex-direction: column;
        margin: 0;


         fieldset {
         fieldset {
             display: flex;
             display: flex;
             flex-direction: column;
             flex-direction: column;
             gap:0.5rem;
             gap: 0.5rem;
         }
         }
     }
     }


     aside {
     dialog#hilfe[open] {
         grid-area: side;
         display: flex;
 
    }
 
    dialog#hilfe {
        flex-direction: column;
 
        form {
            margin-block-start: 0.5rem;
            margin-inline-start: auto;
        }
     }
     }


Zeile 85: Zeile 129:
             flex-direction: column;
             flex-direction: column;
             grid-template-areas: "title" "nav" "main" "side";
             grid-template-areas: "title" "nav" "main" "side";
        }
        nav {
            flex-direction: column;
         }
         }
     }
     }
</style>
</style>


<div>
<section>
     <nav class="steueritem">
     <div class="steueritem">
         <button class="mw-ui-button"
         <button class="mw-ui-button"
                 type="button"
                 type="button"
              id="neustartschalter"
                id="neustartschalter"
              aria-describedby="descr-neustartschalter">Neustart F8</button>
                aria-describedby="descr-neustartschalter"
                aria-details="details-neustartschalter">Neustart&thinsp;<kbd>F8</kbd></button>
         <button class="mw-ui-button"
         <button class="mw-ui-button"
                 type="button"
                 type="button"
              id="ausfuehrenschalter"
                id="ausfuehrenschalter"
              aria-describedby="descr-ausfuehrenschalter">Ausf&uuml;hren F9</button>
                aria-describedby="descr-ausfuehrenschalter"
     </nav>
                aria-details="details-ausfuehrenschalter">Ausf&uuml;hren&thinsp;<kbd>F9</kbd></button>
     <main>
        <button class="mw-ui-button khc-help"
                type="button"
                id="hilfeschalter"
                title="Hilfe">ℹ︎
        </button>
     </div>
     <figure aria-describedby="details-khc">
         <label for="programmzaehleranzeigefeld">Programmz&auml;hler</label>
         <label for="programmzaehleranzeigefeld">Programmz&auml;hler</label>
         <input type="text"
         <input type="text"
               id="programmzaehleranzeigefeld"
               id="programmzaehleranzeigefeld"
               aria-describedby="descr-programmzaehleranzeigefeld"
               aria-describedby="descr-programmzaehleranzeigefeld"
              aria-details="details-programmzaehleranzeigefeld"
               readonly
               readonly
               disabled/>
               disabled/>
Zeile 113: Zeile 162:
         <label for="speicheranzeigefeld">Hauptspeicher</label>
         <label for="speicheranzeigefeld">Hauptspeicher</label>
         <textarea id="speicheranzeigefeld"
         <textarea id="speicheranzeigefeld"
                   aria-describedby="descr-speicheranzeigefeld">
                   aria-describedby="descr-speicheranzeigefeld"
                  aria-details="details-speicheranzeigefeld">
         </textarea>
         </textarea>
         <fieldset id="memory-selector">
         <fieldset id="memory-selector">
             <legend>Speicherkonfiguration laden:</legend>
             <legend>Speicherkonfiguration laden:</legend>
         </fieldset>
         </fieldset>
     </main>
     </figure>
     <aside class="khc-help">
 
     <dialog id="hilfe" class="oo-ui-window-frame">
         <details open>
         <details open>
             <summary><span is="h2">Bedeutung der KHC Befehle</span></summary>
             <summary><span is="h2">Bedeutung der KHC Befehle</span></summary>
Zeile 159: Zeile 210:
             </p>
             </p>
         </details>
         </details>
         <details>
         <details id="details-khc">
             <summary>Kurzanleitung zu dieser JavaScript Implementierung des KHC</summary>
             <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
             <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>
                 Elementen, die mit TAB bzw. UMSCHALT+TAB fokussiert (angew&auml;hlt) werden k&ouml;nnen.</p>
             <h3>Mehrzeiliges Textfeld Hauptspeicher</h3>
             <h3 id="descr-speicheranzeigefeld">Mehrzeiliges Textfeld Hauptspeicher</h3>
             <p id="descr-speicheranzeigefeld">bildet den Hauptspeicher des Know-How-Computers ab. Jede Zeile des
             <p id="details-speicheranzeigefeld">bildet den Hauptspeicher des Know-How-Computers ab. Jede Zeile des
                 Textfeldes enth&auml;lt
                 Textfeldes enth&auml;lt
                 die
                 die
Zeile 187: Zeile 238:
                 ein Neustart mit dem Neustart-Schalter (F8) durchgef&uuml;hrt werden.
                 ein Neustart mit dem Neustart-Schalter (F8) durchgef&uuml;hrt werden.
             </p>
             </p>
             <h3>Einzeiliges Textfeld Programmz&auml;hler</h3>
             <h3 id="descr-programmzaehleranzeigefeld">Einzeiliges Textfeld Programmz&auml;hler</h3>
             <p id="descr-programmzaehleranzeigefeld">enth&auml;lt eine Kombination aus Programmz&auml;hler und
             <p id="details-programmzaehleranzeigefeld">enth&auml;lt eine Kombination aus Programmz&auml;hler und
                 Befehlsregister. Im
                 Befehlsregister. Im
                 Befehlsregister
                 Befehlsregister
Zeile 196: Zeile 247:
                 dieser Befehl ins Befehlsregister geladen wurde.
                 dieser Befehl ins Befehlsregister geladen wurde.
             </p>
             </p>
             <h3>Neustart-Schalter (F8)</h3>
             <h3 id="descr-neustartschalter">Neustart-Schalter <kbd>F8</kbd></h3>
             <p id="descr-neustartschalter">setzt den Programmz&auml;hler auf den Wert 1 zur&uuml;ck und l&auml;dt
             <p id="details-neustartschalter">setzt den Programmz&auml;hler auf den Wert 1 zur&uuml;ck und l&auml;dt
                 den Befehl aus
                 den Befehl aus
                 der Zelle
                 der Zelle
Zeile 204: Zeile 255:
                 ver&auml;ndert. Der Neustart-Schalter kann auch mit der F8-Taste aktiviert werden.
                 ver&auml;ndert. Der Neustart-Schalter kann auch mit der F8-Taste aktiviert werden.
             </p>
             </p>
             <h3>Ausf&uuml;hren-Schalter (F9)</h3>
             <h3 id="descr-ausfuehrenschalter">Ausf&uuml;hren-Schalter <kbd>F9</kbd></h3>
             <p id="descr-ausfuehrenschalter">f&uuml;hrt den aktuellen Befehl im Befehlsregisters aus und l&auml;dt
             <p id="details-ausfuehrenschalter">f&uuml;hrt den aktuellen Befehl im Befehlsregisters aus und l&auml;dt
                 anschlie&szlig;end
                 anschlie&szlig;end
                 den n&auml;chsten
                 den n&auml;chsten
Zeile 213: Zeile 264:
             </p>
             </p>
         </details>
         </details>
     </aside>
        <form method="dialog">
</div>`;
            <button autofocus>Hilfe schlie&szlig;en</button>
        </form>
     </dialog>
</section>`;


                         STYLESHEET_HREFS.map(function (href) {
                         STYLESHEET_HREFS.map(function (href) {
Zeile 222: Zeile 276:
                             return styleElem;
                             return styleElem;
                         }).forEach(linkElem => this.shadowRoot.prepend(linkElem));
                         }).forEach(linkElem => this.shadowRoot.prepend(linkElem));
                        this.shadowRoot.getElementById("hilfeschalter").addEventListener("click", (event) => {
                            const dialog = this.shadowRoot.getElementById("hilfe");
                            if (dialog instanceof HTMLDialogElement) {
                                if (dialog.open) {
                                    dialog.close();
                                } else {
                                    dialog.show();
                                }
                            }
                        });
                         // Create the KnowHowComputer instance
                         // Create the KnowHowComputer instance
                         this.khc = new KnowHowComputer(this.shadowRoot);
                         this.khc = new KnowHowComputer(this.shadowRoot, {});
                     }
                     }


                     refreshMemoryConfigurations() {
                     refreshMemoryConfigurations() {
                         this.memoryConfigurations = [
                         this.#memoryConfigurations = [
                             ...this.parseMemoryElements()
                             ...this.parseMemoryElements()
                         ];
                         ];
                         this.refreshMemorySelector(this.memoryConfigurations);
                         this.refreshMemorySelector(this.#memoryConfigurations);
                        if (!this.#selectedMemory) {
                            if (this.khc && this.#memoryConfigurations[0]
                            ) {
                                this.khc.loadProgram(this.#memoryConfigurations[0].program);
                            }
                        }
                     }
                     }


                     refreshHelp() {
                     refreshHelp() {
                         this.shadowRoot.querySelector(':host > div')
                         this.shadowRoot.querySelector(':host > *')
                             .classList
                             .classList
                             .toggle('no-help', this.hasAttribute('no-help'));
                             .toggle('no-help', this.hasAttribute('no-help'));
                     }
                     }


                     attributeChangedCallback(name, oldValue, newValue) {
                    refreshKhcConfiguration() {
                        let memSize;
 
                        if (this.hasAttribute('memory-size')) {
                            memSize = Number.parseInt(this.getAttribute('memory-size'), 10);
                        }
 
                        if (!Number.isFinite(memSize) || !Number.isSafeInteger(memSize)) {
                            memSize = 15;
                        }
                        this.khc.setMemorySize(Math.max(memSize, 1));
                    }
 
                    // noinspection JSUnusedGlobalSymbols
                     attributeChangedCallback(_name, _oldValue, _newValue) {
                         this.refreshHelp()
                         this.refreshHelp()
                        this.refreshKhcConfiguration()
                     }
                     }


                     // Lifecycle callbacks
                     // Lifecycle callbacks
                    // noinspection JSUnusedGlobalSymbols
                     connectedCallback() {
                     connectedCallback() {
                         // Component is now in the DOM
                         // Component is now in the DOM
Zeile 249: Zeile 337:
                         // Parse khc-memory child elements
                         // Parse khc-memory child elements
                         this.refreshMemoryConfigurations();
                         this.refreshMemoryConfigurations();
                         this.observer = new MutationObserver(mutations => {
                         this.#observer = new MutationObserver(_ => {
                             this.refreshMemoryConfigurations();
                             this.refreshMemoryConfigurations();
                         });
                         });
                         this.observer.observe(this, {childList: true});
                         this.#observer.observe(this, {childList: true});




Zeile 261: Zeile 349:
                             // Load program from khc-memory elements
                             // Load program from khc-memory elements
                             const index = parseInt(selectedValue);
                             const index = parseInt(selectedValue);
                             if (index >= 0 && index < this.memoryConfigurations.length) {
                             if (index >= 0 && index < this.#memoryConfigurations.length) {
                                 this.loadMemoryConfiguration(this.memoryConfigurations[index]);
                                this.#selectedMemory = index;
                                 this.loadMemoryConfiguration(this.#memoryConfigurations[index]);
                             }
                             }
                             e.preventDefault();
                             e.preventDefault();
Zeile 269: Zeile 358:
                         this.refreshMemoryConfigurations();
                         this.refreshMemoryConfigurations();
                         this.refreshHelp();
                         this.refreshHelp();
                        this.refreshKhcConfiguration();
                     }
                     }


                    // noinspection JSUnusedGlobalSymbols
                     disconnectedCallback() {
                     disconnectedCallback() {
                         // Component is removed from the DOM
                         // Component is removed from the DOM
                         // Clean up any event listeners if needed
                         // Clean up any event listeners if needed
                         if (this.observer) {
                         this.#observer?.disconnect();
                            this.observer.disconnect();
                        }
                     }
                     }


Zeile 347: Zeile 436:


                         // Configuration variables
                         // Configuration variables
                         this.speicheranzahl = 25; // Standardwert ist 25 für Adr. 1-25, kann aber geändert werden. Adr. 0 wird nicht benutzt
                         this.speicheranzahl = 15; // Standardwert ist 15 für Adr. 1-15, kann aber geändert werden. Adr. 0 wird nicht benutzt
                         this.maxwert = 99;
                         this.maxwert = 99;
                         this.spaltenanzahlinspeicheranzeigefeld = 10;
                         this.spaltenanzahlinspeicheranzeigefeld = 10;
Zeile 353: Zeile 442:


                         // State variables
                         // State variables
                         this.mem = new Array(this.speicheranzahl + 1); // mem[0] existiert, wird aber nicht benutzt
                        /** @type {string[]} */
                         this.mem = this.getMemForProgramm({}, this.speicheranzahl);
                         this.pz = 1; // Programmzähler
                         this.pz = 1; // Programmzähler
                         this.br = ""; // Befehlsregister
                         this.br = ""; // Befehlsregister


                         // Constants and utility variables
                         // Constants and utility variables
                         this.ziffern = "0123456789";
                         this.ziffern = new Set("0123456789".split(''));
                         this.lueckenzeichen = " \r\n\t";
                         this.lueckenzeichen = new Set(" \r\n\t".split(''));
                        this.khcbuchstaben = "iszjmpincdecstp";
                         this.doppelpunkt = new Set(":".split(''));
                         this.doppelpunkt = ":";
                         this.kommentarzeichen = new Set(":".split(''));
                         this.kommentarzeichen = ";";
                         this.khcbefehle = new Set(["isz", "jmp", "inc", "dec", "stp"]);
                         this.khcbefehle = new Array("isz", "jmp", "inc", "dec", "stp");
                        this.khcbuchstaben = new Set(this.khcbefehle.values().flatMap(s => s.split('')));
 


                         // Lexer/parser state
                         // Lexer/parser state
                         this.symtypen = new Array();
                        /** @type {string[]} */
                         this.symwerte = new Array();
                         this.symtypen = [];
                        /** @type {string[]} */
                         this.symwerte = [];
                         this.symindex = -1;
                         this.symindex = -1;


                         // Error messages
                         // Error messages
                         this.fehlermeldungen = new Array();
                         this.fehlermeldungen = [];
                         this.fehlermeldungen[0] = "Keinen Fehler gefunden";
                         this.fehlermeldungen[0] = "Keinen Fehler gefunden";
                         this.fehlermeldungen[1] = "Unerlaubtes Zeichen";
                         this.fehlermeldungen[1] = "Unerlaubtes Zeichen";
Zeile 404: Zeile 497:


                     zahltrimmen(zahlstr, min, max) {
                     zahltrimmen(zahlstr, min, max) {
                         var test = parseInt(zahlstr);
                         let test = parseInt(zahlstr);
                         if (isNaN(test)) {
                         if (isNaN(test)) {
                             test = min;
                             test = min;
Zeile 418: Zeile 511:
                         }
                         }
                         return test;
                         return test;
                    }
                    zeichenistinstring(z, s) {
                        return s.indexOf(z) !== -1;
                     }
                     }


Zeile 431: Zeile 520:
                     // Lexer and parser
                     // Lexer and parser
                     khclexer(eingabe) {
                     khclexer(eingabe) {
                         var z; // einzelnes Zeichen aus eingabe
                         let z; // einzelnes Zeichen aus eingabe
                         var i;
                         let i;
                         this.symtypen = this.arrayleeren(this.symtypen);
                         this.symtypen = this.arrayleeren(this.symtypen);
                         this.symwerte = this.arrayleeren(this.symwerte);
                         this.symwerte = this.arrayleeren(this.symwerte);
                         this.symindex = -1;
                         this.symindex = -1;


                         var symwert = "";
                         let symwert = "";
                         var symtyp = "l"; // z =zahl, b =buchstabenkette, l =luecke, : =doppelpunkt
                         let symtyp = "l"; // z =zahl, b =buchstabenkette, l =luecke, : =doppelpunkt
                         eingabe = " " + eingabe + " ";
                         eingabe = " " + eingabe + " ";
                         var ztyp = "l";
                         let ztyp = "l";
                         for (i = 0; i < eingabe.length; i++) {
                         for (i = 0; i < eingabe.length; i++) {
                             z = eingabe.charAt(i);
                             z = eingabe.charAt(i);
                             if (this.zeichenistinstring(z, this.lueckenzeichen)) ztyp = "l";      // z ist ein Lückenzeichen
                             if (this.lueckenzeichen.has(z)) ztyp = "l";      // z ist ein Lückenzeichen
                             else if (this.zeichenistinstring(z, this.ziffern)) ztyp = "z";      // z ist eine Ziffer
                             else if (this.ziffern.has(z)) ztyp = "z";      // z ist eine Ziffer
                             else if (this.zeichenistinstring(z, this.doppelpunkt)) ztyp = ":";  // z ist ein Doppelpunkt
                             else if (this.doppelpunkt.has(z)) 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.khcbuchstaben.has(z)) ztyp = "b"; // z kommt in einem der KHC-Befehle vor
                             else if (this.zeichenistinstring(z, this.kommentarzeichen)) ztyp = ";";  // z ist Kommentarzeichen
                             else if (this.kommentarzeichen.has(z)) ztyp = ";";  // z ist Kommentarzeichen
                             else return false;
                             else return false;


                             if (symtyp == ztyp) { // aktueller symtyp stimmt mit dem ztyp des gerade gelesenen Zeichens z überein
                             if (symtyp === ztyp) { // aktueller symtyp stimmt mit dem ztyp des gerade gelesenen Zeichens z überein
                                 symwert = symwert + z;
                                 symwert = symwert + z;
                             } else { // neues Symbol:
                             } else { // neues Symbol:
                                 if (symtyp != "l") { // bisheriges Symbol in Arrays speichern
                                 if (symtyp !== "l") { // bisheriges Symbol in Arrays speichern
                                     this.symindex++;
                                     this.symindex++;
                                     this.symwerte[this.symindex] = symwert;
                                     this.symwerte[this.symindex] = symwert;
                                     this.symtypen[this.symindex] = symtyp;
                                     this.symtypen[this.symindex] = symtyp;
                                 }
                                 }
                                 if (ztyp != ";") {
                                 if (ztyp !== ";") {
                                     symtyp = ztyp; // Anfang des neuen Symbols in sym-Vars speichern
                                     symtyp = ztyp; // Anfang des neuen Symbols in sym-Vars speichern
                                     symwert = z;
                                     symwert = z;
Zeile 470: Zeile 559:


                     istkhcbefehl(s) {
                     istkhcbefehl(s) {
                         var ok = false;
                         return this.khcbefehle.has(s);
                        for (let j = 0; j < this.khcbefehle.length; j++) {
                            if (s == this.khcbefehle[j]) {
                                ok = true;
                            }
                        }
                        return ok;
                     }
                     }


                     istgueltigezahl(zahlstr, min, max) {
                     istgueltigezahl(zahlstr, min, max) {
                         var ok = true;
                         let ok = true;
                         var test = parseInt(zahlstr);
                         const test = parseInt(zahlstr);
                         if (isNaN(test)) ok = false;
                         if (isNaN(test)) ok = false;
                         else {
                         else {
Zeile 491: Zeile 574:
                     khcparser(zeile) {
                     khcparser(zeile) {
                         // Rückgabewert: fehlerindex
                         // Rückgabewert: fehlerindex
                         var ok = this.khclexer(zeile);
                         const ok = this.khclexer(zeile);
                         if (this.symwerte.join("").length == 0) return -1; // Leerzeile liefert keine Fehlermeldung
                         if (this.symwerte.join("").length === 0) return -1; // Leerzeile liefert keine Fehlermeldung
                         if (!ok) return 1; // "Unerlaubtes Zeichen"
                         if (!ok) return 1; // "Unerlaubtes Zeichen"
                         if (this.symtypen[0] != "z") return 2; // "Syntaxfehler - Zahl erwartet"
                         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.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[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" && this.symtypen[2] !== "b") return 7; // "Syntaxfehler - Zahl oder Befehl erwartet"
                         if (this.symtypen[2] == "z") {
                         if (this.symtypen[2] === "z") {
                             if (!this.istgueltigezahl(this.symwerte[2], 0, this.maxwert)) return 4; // "Wert nicht im gültigen Bereich"
                             if (!this.istgueltigezahl(this.symwerte[2], 0, this.maxwert)) return 4; // "Wert nicht im gültigen Bereich"
                         }
                         }
                         if (this.symtypen[2] == "b") {
                         if (this.symtypen[2] === "b") {
                             if (!this.istkhcbefehl(this.symwerte[2])) return 5; // "Ungültiger Befehl"
                             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" && this.symtypen.length > 3) return 8; // "Syntaxfehler"
                             if (this.symwerte[2] != "stp") {
                             if (this.symwerte[2] !== "stp") {
                                 if (this.symtypen[3] != "z") return 9; // "Syntaxfehler"
                                 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"
                                 if (!this.istgueltigezahl(this.symwerte[3], 1, this.speicheranzahl)) return 3; // "Adresse nicht im gültigen Bereich"
                             }
                             }
Zeile 514: Zeile 597:
                     // Initialization and control
                     // Initialization and control
                     init() {
                     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();
                         this.loadProgram({});


                         // Set up event listeners
                         // Set up event listeners
Zeile 562: Zeile 633:
                             }
                             }
                         });
                         });
                    }
                    loadProgram(program) {
                        this.mem = this.getMemForProgramm(program ?? {}, this.speicheranzahl);
                        // Restart the emulator
                        this.neustart();
                     }
                     }


                     // Load a program from a program object
                     // Load a program from a program object
                     loadProgram(program) {
                     getMemForProgramm(program, speicheranzahl) {
                         // Reset memory
                         const newMem = new Array(speicheranzahl + 1);//expected size
                        for (let i = 0; i < this.speicheranzahl + 1; i++) {
                         newMem.fill(undefined);
                            this.mem[i] = "0";
                         }
                        this.mem[0] = "nicht verwendet";


                         // Load program into memory
                         // Load program into memory
                         for (const [address, instruction] of Object.entries(program)) {
                         for (const [address, instruction] of Object.entries(program)) {
                             if (parseInt(address) >= 1 && parseInt(address) <= this.speicheranzahl) {
                             if (parseInt(address) >= 1 && parseInt(address)) {
                                 this.mem[address] = instruction;
                                 newMem[address] = instruction;
                             }
                             }
                         }
                         }
 
                         // fill all empty memory cells with 0
                         // Restart the emulator
                         newMem.forEach((val, idx, self) => {
                         this.neustart();
                            if (val === undefined) {
                                self[idx] = '0';
                            }
                        });
                        return newMem;
                     }
                     }


Zeile 591: Zeile 669:


                     registeranzeigefeldaktualisieren() {
                     registeranzeigefeldaktualisieren() {
                         var str = this.rechtsbuendig(this.pz) + ": " + this.stringmitleerzeichenauffuellen(this.mem[this.pz], this.spaltenanzahlinspeicheranzeigefeld - 3);
                         this.container.getElementById("programmzaehleranzeigefeld").value = this.rechtsbuendig(this.pz) + ": " + this.stringmitleerzeichenauffuellen(this.mem[this.pz], this.spaltenanzahlinspeicheranzeigefeld - 3);
                        this.container.getElementById("programmzaehleranzeigefeld").value = str;
                     }
                     }


                     anzeigeaktualisieren() {
                     anzeigeaktualisieren() {
                         // Anzeige des gesamten Speichers aktualisieren:
                         // Anzeige des gesamten Speichers aktualisieren:
                         const speicherfeld = this.container.getElementById("speicheranzeigefeld");
                        /**@type HTMLTextAreaElement */
                         speicherfeld.value = this.mem.slice(1).map((speicher, idx) => this.rechtsbuendig(idx + 1) + ": " + speicher).join('\n');
                         const speicheranzeigefeld = this.container.getElementById("speicheranzeigefeld");
                         speicheranzeigefeld.value = this.mem.slice(1).map((speicher, idx) => this.rechtsbuendig(idx + 1) + ": " + speicher).join('\n');
 
                        /**@type HTMLInputElement */
                        const registeranzeigefeld = this.container.getElementById("programmzaehleranzeigefeld");
 
                        speicheranzeigefeld.rows = this.mem.length - 1
                        speicheranzeigefeld.cols = this.spaltenanzahlinspeicheranzeigefeld;
                        registeranzeigefeld.size = this.spaltenanzahlinspeicheranzeigefeld;
 
                         this.registeranzeigefeldaktualisieren();
                         this.registeranzeigefeldaktualisieren();
                         this.zeileinspeicheranzeigefeldmarkieren(this.pz);
                         this.zeileinspeicheranzeigefeldmarkieren(this.pz);
Zeile 604: Zeile 690:


                     befehlausfuehren() {
                     befehlausfuehren() {
                         var fehlerindex = 0;
                         let fehlerindex = 0;
                         this.speicherfeldanzeigeinspeicheruebernehmen();
                         this.speicherfeldanzeigeinspeicheruebernehmen();
                         console.log("mem[pz] = " + this.mem[this.pz]);
                         console.log("mem[pz] = " + this.mem[this.pz]);
                         this.br = this.mem[this.pz];
                         this.br = this.mem[this.pz];


                         var z = parseInt(this.br);
                         const z = parseInt(this.br);
                         console.log("Start befehlausfuehren mit z = parseint(br): " + z);
                         console.log("Start befehlausfuehren mit z = parseint(br): " + z);
                         console.log("in befehlausfuehren vor Test: pz = " + this.pz + " br = " + this.br);
                         console.log("in befehlausfuehren vor Test: pz = " + this.pz + " br = " + this.br);
Zeile 619: Zeile 705:
                         } else {
                         } else {
                             // sonst gehen wir von einem Befehl aus:
                             // sonst gehen wir von einem Befehl aus:
                             var befehlscode = this.br.substr(0, 3).toLowerCase();
                             const befehlscode = this.br.substr(0, 3).toLowerCase();
                             // Bei stp ist keine Adresse erforderlich und es wird auch nichts gemacht:
                             // Bei stp ist keine Adresse erforderlich und es wird auch nichts gemacht:
                             if (befehlscode == "stp") return 0;
                             if (befehlscode === "stp") return 0;
                             // sonst muss eine ganze Zahl als Adresse folgen:
                             // sonst muss eine ganze Zahl als Adresse folgen:
                             var adresse = parseInt(this.br.substr(4)); // ab Pos. 4 bis zum Ende ausschneiden
                             var adresse = parseInt(this.br.substr(4)); // ab Pos. 4 bis zum Ende ausschneiden
Zeile 631: Zeile 717:
                                     break;
                                     break;
                                 case "isz":
                                 case "isz":
                                     if (this.mem[adresse] == 0) this.pz = this.pz + 2; else this.pz = this.pz + 1;
                                     if (this.isZero(this.mem[adresse])) this.pz = this.pz + 2; else this.pz = this.pz + 1;
                                     break;
                                     break;
                                 case "inc":
                                 case "inc":
Zeile 637: Zeile 723:
                                     wert = parseInt(wert) + 1;
                                     wert = parseInt(wert) + 1;
                                     wert = this.zahltrimmen(wert, 0, this.maxwert);
                                     wert = this.zahltrimmen(wert, 0, this.maxwert);
                                     this.mem[adresse] = wert;
                                     this.mem[adresse] = String(wert);
                                     this.pz = this.pz + 1;
                                     this.pz = this.pz + 1;
                                     break;
                                     break;
Zeile 644: Zeile 730:
                                     wert2 = parseInt(wert2) - 1;
                                     wert2 = parseInt(wert2) - 1;
                                     wert2 = this.zahltrimmen(wert2, 0, this.maxwert);
                                     wert2 = this.zahltrimmen(wert2, 0, this.maxwert);
                                     this.mem[adresse] = wert2;
                                     this.mem[adresse] = String(wert2);
                                     this.pz = this.pz + 1;
                                     this.pz = this.pz + 1;
                                     break;
                                     break;
Zeile 657: Zeile 743:
                         this.zeileinspeicheranzeigefeldmarkieren(this.pz);
                         this.zeileinspeicheranzeigefeldmarkieren(this.pz);
                         return fehlerindex;
                         return fehlerindex;
                    }
                    isZero(zeile) {
                        return 0 === parseInt(zeile);
                     }
                     }


                     einezeileinspeicheruebernehmen(zeile) {
                     einezeileinspeicheruebernehmen(zeile) {
                         zeile = zeile.toLowerCase();
                         zeile = zeile.toLowerCase();
                         var fehlerindex = 0; // vorläufig
                         const fehlerindex = this.khcparser(zeile);
                        fehlerindex = this.khcparser(zeile);
                         if (fehlerindex === 0) {
                         if (fehlerindex == 0) {
                             const pz = parseInt(this.symwerte[0]);
                             var pz = parseInt(this.symwerte[0]);
                             let befehlszeile = this.symwerte[2];
                             var befehlszeile = this.symwerte[2];
                             if (this.symwerte.length > 3) {
                             if (this.symwerte.length > 3) befehlszeile = befehlszeile + " " + this.symwerte[3];
                                befehlszeile = befehlszeile + " " + this.symwerte[3];
                             befehlszeile = this.stringmitleerzeichenauffuellen(befehlszeile, this.spaltenanzahlinspeicheranzeigefeld - 3);
                             }
                             this.mem[pz] = befehlszeile;
                             this.mem[pz] = befehlszeile;
                         } else {
                         } else {
Zeile 689: Zeile 779:
                             domElem.selectionStart = posStart;
                             domElem.selectionStart = posStart;
                             domElem.selectionEnd = posEnde;
                             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();
                         }
                         }
                     }
                     }
Zeile 706: Zeile 789:
                     }
                     }


                     getCaretPos(domElemSelector) {
                     setMemorySize(memSize) {
                         var pos = 0;
                         this.speicheranzahl = memSize;
                         const domElem = this.container.querySelector(domElemSelector);
                         this.updateMemSize();
                        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) {
                     updateMemSize() {
                         const domElem = this.container.querySelector(domElemSelector);
                         const currentProgram = this.mem.reduce((p, v, idx) => {
                         let pos = domElem.value.indexOf(zeilennr + ": ");
                            if (!this.isZero(v)) {
                         this.setzeMarkierungInTextFeld(domElemSelector, pos, pos);
                                p[idx] = v;
                            }
                            return p;
                        }, {})
                         this.mem = this.getMemForProgramm(currentProgram, this.speicheranzahl);
                         this.anzeigeaktualisieren();
                     }
                     }
                 }
                 }
Zeile 741: Zeile 820:


                     // Lifecycle callbacks
                     // Lifecycle callbacks
                    // noinspection JSUnusedGlobalSymbols
                     connectedCallback() {
                     connectedCallback() {
                         // Hide the element by default as it's just a data container
                         // Hide the element by default as it's just a data container
Zeile 757: Zeile 837:
             });
             });
         })();
         })();
     </script>
     </script>


Zeile 767: Zeile 848:
   <!--{if $noHelp|validate:'boolean'|default:'false' == 'true'}-->
   <!--{if $noHelp|validate:'boolean'|default:'false' == 'true'}-->
   no-help
   no-help
  <!--{/if}-->
  <!--{if $Speichergroesse|validate:'int'|default:'-1' != '-1'}-->
  memory-size="<!--{$Speichergroesse|escape:'htmlall'}-->"
   <!--{/if}-->
   <!--{/if}-->
>
>
<!--{if $param1|default:'' != ''}-->
<!--{if $Speicherkonfiguration1|default:'' != ''}-->
<khc-memory><!--{$param1|escape:'htmlall'}--></khc-memory>
<khc-memory><!--{$Speicherkonfiguration1|escape:'htmlall'}--></khc-memory>
<!--{/if}-->
<!--{/if}-->
<!--{if $param2|default:'' != ''}-->
<!--{if $Speicherkonfiguration2|default:'' != ''}-->
<khc-memory><!--{$param2|escape:'htmlall'}--></khc-memory>
<khc-memory><!--{$Speicherkonfiguration2|escape:'htmlall'}--></khc-memory>
<!--{/if}-->
<!--{/if}-->
<!--{if $param3|default:'' != ''}-->
<!--{if $Speicherkonfiguration3|default:'' != ''}-->
<khc-memory><!--{$param3|escape:'htmlall'}--></khc-memory>
<khc-memory><!--{$Speicherkonfiguration3|escape:'htmlall'}--></khc-memory>
<!--{/if}-->
<!--{/if}-->
<!--{if $param4|default:'' != ''}-->
<!--{if $Speicherkonfiguration4|default:'' != ''}-->
<khc-memory><!--{$param4|escape:'htmlall'}--></khc-memory>
<khc-memory><!--{$Speicherkonfiguration4|escape:'htmlall'}--></khc-memory>
<!--{/if}-->
<!--{/if}-->
<!--{if $param5|default:'' != ''}-->
<!--{if $Speicherkonfiguration5|default:'' != ''}-->
<khc-memory><!--{$param5|escape:'htmlall'}--></khc-memory>
<khc-memory><!--{$Speicherkonfiguration5|escape:'htmlall'}--></khc-memory>
<!--{/if}-->
<!--{/if}-->
</know-how-computer>
</know-how-computer>
</includeonly>
</includeonly>

Aktuelle Version vom 29. Juni 2025, 20:16 Uhr

<noinclude>{{#tag:pre|{{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', 'memory-size'];

                    /**
                     * @typedef {object} MemoryConfiguration
                     * @property {string} name
                     * @property {Record<int,string>} program
                     */

                    /**  @type {MemoryConfiguration[]} */
                    #memoryConfigurations = [];

                    /** @type {?MutationObserver} */
                    #observer;
                    /** @type {int} */
                    #selectedMemory;

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

    * {
        box-sizing: border-box;
    }

    .no-help .khc-help {
        display: none;
    }

    :host > section {
        display: flex;
        flex-direction: column;
        gap: 1rem;
    }

    .mw-ui-button.khc-help {
        min-height: auto;
        min-width: auto;
        aspect-ratio: 1;
        margin-inline-start: auto;
    }

    #memory-selector:not(:has(> button)) {
        display: none;
    }

    #speicheranzeigefeld {
        overflow-y: scroll;
        overflow-block: scroll;
    }

    code {
        font-weight: bolder;
    }

    label {
        font-weight: bold;
    }

    .steueritem {
        display: flex;
        justify-content: space-between;
        gap: 0.5rem;

        > input {
            max-height: 2rem;
        }
    }

    figure {

        grid-area: main;
        display: flex;
        flex-direction: column;
        margin: 0;

        fieldset {
            display: flex;
            flex-direction: column;
            gap: 0.5rem;
        }
    }

    dialog#hilfe[open] {
        display: flex;

    }

    dialog#hilfe {
        flex-direction: column;

        form {
            margin-block-start: 0.5rem;
            margin-inline-start: auto;
        }
    }

    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";
        }
    }
</style>

<section>
    <div class="steueritem">
        <button class="mw-ui-button"
                type="button"
                id="neustartschalter"
                aria-describedby="descr-neustartschalter"
                aria-details="details-neustartschalter">Neustart&thinsp;<kbd>F8</kbd></button>
        <button class="mw-ui-button"
                type="button"
                id="ausfuehrenschalter"
                aria-describedby="descr-ausfuehrenschalter"
                aria-details="details-ausfuehrenschalter">Ausf&uuml;hren&thinsp;<kbd>F9</kbd></button>
        <button class="mw-ui-button khc-help"
                type="button"
                id="hilfeschalter"
                title="Hilfe">ℹ︎
        </button>
    </div>
    <figure aria-describedby="details-khc">
        <label for="programmzaehleranzeigefeld">Programmz&auml;hler</label>
        <input type="text"
               id="programmzaehleranzeigefeld"
               aria-describedby="descr-programmzaehleranzeigefeld"
               aria-details="details-programmzaehleranzeigefeld"
               readonly
               disabled/>

        <label for="speicheranzeigefeld">Hauptspeicher</label>
        <textarea id="speicheranzeigefeld"
                  aria-describedby="descr-speicheranzeigefeld"
                  aria-details="details-speicheranzeigefeld">
        </textarea>
        <fieldset id="memory-selector">
            <legend>Speicherkonfiguration laden:</legend>
        </fieldset>
    </figure>

    <dialog id="hilfe" class="oo-ui-window-frame">
        <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 id="details-khc">
            <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 id="descr-speicheranzeigefeld">Mehrzeiliges Textfeld Hauptspeicher</h3>
            <p id="details-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 id="descr-programmzaehleranzeigefeld">Einzeiliges Textfeld Programmz&auml;hler</h3>
            <p id="details-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 id="descr-neustartschalter">Neustart-Schalter <kbd>F8</kbd></h3>
            <p id="details-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 id="descr-ausfuehrenschalter">Ausf&uuml;hren-Schalter <kbd>F9</kbd></h3>
            <p id="details-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>
        <form method="dialog">
            <button autofocus>Hilfe schlie&szlig;en</button>
        </form>
    </dialog>
</section>`;

                        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));

                        this.shadowRoot.getElementById("hilfeschalter").addEventListener("click", (event) => {

                            const dialog = this.shadowRoot.getElementById("hilfe");
                            if (dialog instanceof HTMLDialogElement) {
                                if (dialog.open) {
                                    dialog.close();
                                } else {
                                    dialog.show();
                                }
                            }
                        });
                        // Create the KnowHowComputer instance
                        this.khc = new KnowHowComputer(this.shadowRoot, {});
                    }

                    refreshMemoryConfigurations() {
                        this.#memoryConfigurations = [
                            ...this.parseMemoryElements()
                        ];
                        this.refreshMemorySelector(this.#memoryConfigurations);
                        if (!this.#selectedMemory) {
                            if (this.khc && this.#memoryConfigurations[0]
                            ) {
                                this.khc.loadProgram(this.#memoryConfigurations[0].program);
                            }
                        }
                    }

                    refreshHelp() {
                        this.shadowRoot.querySelector(':host > *')
                            .classList
                            .toggle('no-help', this.hasAttribute('no-help'));
                    }

                    refreshKhcConfiguration() {
                        let memSize;

                        if (this.hasAttribute('memory-size')) {
                            memSize = Number.parseInt(this.getAttribute('memory-size'), 10);
                        }

                        if (!Number.isFinite(memSize) || !Number.isSafeInteger(memSize)) {
                            memSize = 15;
                        }
                        this.khc.setMemorySize(Math.max(memSize, 1));
                    }

                    // noinspection JSUnusedGlobalSymbols
                    attributeChangedCallback(_name, _oldValue, _newValue) {
                        this.refreshHelp()
                        this.refreshKhcConfiguration()
                    }

                    // Lifecycle callbacks
                    // noinspection JSUnusedGlobalSymbols
                    connectedCallback() {
                        // Component is now in the DOM

                        // Parse khc-memory child elements
                        this.refreshMemoryConfigurations();
                        this.#observer = new MutationObserver(_ => {
                            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.#selectedMemory = index;
                                this.loadMemoryConfiguration(this.#memoryConfigurations[index]);
                            }
                            e.preventDefault();
                        });

                        this.refreshMemoryConfigurations();
                        this.refreshHelp();
                        this.refreshKhcConfiguration();
                    }

                    // noinspection JSUnusedGlobalSymbols
                    disconnectedCallback() {
                        // Component is removed from the DOM
                        // Clean up any event listeners if needed
                        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 = 15; // Standardwert ist 15 für Adr. 1-15, 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
                        /** @type {string[]} */
                        this.mem = this.getMemForProgramm({}, this.speicheranzahl);
                        this.pz = 1; // Programmzähler
                        this.br = ""; // Befehlsregister

                        // Constants and utility variables
                        this.ziffern = new Set("0123456789".split(''));
                        this.lueckenzeichen = new Set(" \r\n\t".split(''));
                        this.doppelpunkt = new Set(":".split(''));
                        this.kommentarzeichen = new Set(":".split(''));
                        this.khcbefehle = new Set(["isz", "jmp", "inc", "dec", "stp"]);
                        this.khcbuchstaben = new Set(this.khcbefehle.values().flatMap(s => s.split('')));


                        // Lexer/parser state
                        /** @type {string[]} */
                        this.symtypen = [];
                        /** @type {string[]} */
                        this.symwerte = [];
                        this.symindex = -1;

                        // Error messages
                        this.fehlermeldungen = [];
                        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) {
                        let 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;
                    }

                    arrayleeren(a) {
                        a.splice(0)
                        return a;
                    }

                    // Lexer and parser
                    khclexer(eingabe) {
                        let z; // einzelnes Zeichen aus eingabe
                        let i;
                        this.symtypen = this.arrayleeren(this.symtypen);
                        this.symwerte = this.arrayleeren(this.symwerte);
                        this.symindex = -1;

                        let symwert = "";
                        let symtyp = "l"; // z =zahl, b =buchstabenkette, l =luecke, : =doppelpunkt
                        eingabe = " " + eingabe + " ";
                        let ztyp = "l";
                        for (i = 0; i < eingabe.length; i++) {
                            z = eingabe.charAt(i);
                            if (this.lueckenzeichen.has(z)) ztyp = "l";      // z ist ein Lückenzeichen
                            else if (this.ziffern.has(z)) ztyp = "z";       // z ist eine Ziffer
                            else if (this.doppelpunkt.has(z)) ztyp = ":";   // z ist ein Doppelpunkt
                            else if (this.khcbuchstaben.has(z)) ztyp = "b"; // z kommt in einem der KHC-Befehle vor
                            else if (this.kommentarzeichen.has(z)) 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) {
                        return this.khcbefehle.has(s);
                    }

                    istgueltigezahl(zahlstr, min, max) {
                        let ok = true;
                        const test = parseInt(zahlstr);
                        if (isNaN(test)) ok = false;
                        else {
                            if (test < min || test > max) ok = false;
                        }
                        return ok;
                    }

                    khcparser(zeile) {
                        // Rückgabewert: fehlerindex
                        const 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() {

                        this.loadProgram({});

                        // 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
                            }
                        });
                    }

                    loadProgram(program) {
                        this.mem = this.getMemForProgramm(program ?? {}, this.speicheranzahl);
                        // Restart the emulator
                        this.neustart();
                    }

                    // Load a program from a program object
                    getMemForProgramm(program, speicheranzahl) {
                        const newMem = new Array(speicheranzahl + 1);//expected size
                        newMem.fill(undefined);

                        // Load program into memory
                        for (const [address, instruction] of Object.entries(program)) {
                            if (parseInt(address) >= 1 && parseInt(address)) {
                                newMem[address] = instruction;
                            }
                        }
                        // fill all empty memory cells with 0
                        newMem.forEach((val, idx, self) => {
                            if (val === undefined) {
                                self[idx] = '0';
                            }
                        });
                        return newMem;
                    }

                    neustart() {
                        this.pz = 1; // Programmzähler
                        this.br = this.mem[this.pz]; // Befehlsregister
                        this.anzeigeaktualisieren();
                        this.container.getElementById("speicheranzeigefeld").focus();
                    }

                    registeranzeigefeldaktualisieren() {
                        this.container.getElementById("programmzaehleranzeigefeld").value = this.rechtsbuendig(this.pz) + ": " + this.stringmitleerzeichenauffuellen(this.mem[this.pz], this.spaltenanzahlinspeicheranzeigefeld - 3);
                    }

                    anzeigeaktualisieren() {
                        // Anzeige des gesamten Speichers aktualisieren:
                        /**@type HTMLTextAreaElement */
                        const speicheranzeigefeld = this.container.getElementById("speicheranzeigefeld");
                        speicheranzeigefeld.value = this.mem.slice(1).map((speicher, idx) => this.rechtsbuendig(idx + 1) + ": " + speicher).join('\n');

                        /**@type HTMLInputElement */
                        const registeranzeigefeld = this.container.getElementById("programmzaehleranzeigefeld");

                        speicheranzeigefeld.rows = this.mem.length - 1
                        speicheranzeigefeld.cols = this.spaltenanzahlinspeicheranzeigefeld;
                        registeranzeigefeld.size = this.spaltenanzahlinspeicheranzeigefeld;

                        this.registeranzeigefeldaktualisieren();
                        this.zeileinspeicheranzeigefeldmarkieren(this.pz);
                    }

                    befehlausfuehren() {
                        let fehlerindex = 0;
                        this.speicherfeldanzeigeinspeicheruebernehmen();
                        console.log("mem[pz] = " + this.mem[this.pz]);
                        this.br = this.mem[this.pz];

                        const 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:
                            const 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.isZero(this.mem[adresse])) 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] = String(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] = String(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;
                    }

                    isZero(zeile) {
                        return 0 === parseInt(zeile);
                    }

                    einezeileinspeicheruebernehmen(zeile) {
                        zeile = zeile.toLowerCase();
                        const fehlerindex = this.khcparser(zeile);
                        if (fehlerindex === 0) {
                            const pz = parseInt(this.symwerte[0]);
                            let befehlszeile = this.symwerte[2];
                            if (this.symwerte.length > 3) {
                                befehlszeile = befehlszeile + " " + this.symwerte[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;
                        }
                    }

                    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);
                    }

                    setMemorySize(memSize) {
                        this.speicheranzahl = memSize;
                        this.updateMemSize();
                    }

                    updateMemSize() {
                        const currentProgram = this.mem.reduce((p, v, idx) => {
                            if (!this.isZero(v)) {
                                p[idx] = v;
                            }
                            return p;
                        }, {})
                        this.mem = this.getMemForProgramm(currentProgram, this.speicheranzahl);
                        this.anzeigeaktualisieren();
                    }
                }

                // 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
                    // noinspection JSUnusedGlobalSymbols
                    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 $Speichergroesse|validate:'int'|default:'-1' != '-1'}-->
  memory-size="<!--{$Speichergroesse|escape:'htmlall'}-->"
  <!--{/if}-->
>
<!--{if $Speicherkonfiguration1|default:'' != ''}-->
<khc-memory><!--{$Speicherkonfiguration1|escape:'htmlall'}--></khc-memory>
<!--{/if}-->
<!--{if $Speicherkonfiguration2|default:'' != ''}-->
<khc-memory><!--{$Speicherkonfiguration2|escape:'htmlall'}--></khc-memory>
<!--{/if}-->
<!--{if $Speicherkonfiguration3|default:'' != ''}-->
<khc-memory><!--{$Speicherkonfiguration3|escape:'htmlall'}--></khc-memory>
<!--{/if}-->
<!--{if $Speicherkonfiguration4|default:'' != ''}-->
<khc-memory><!--{$Speicherkonfiguration4|escape:'htmlall'}--></khc-memory>
<!--{/if}-->
<!--{if $Speicherkonfiguration5|default:'' != ''}-->
<khc-memory><!--{$Speicherkonfiguration5|escape:'htmlall'}--></khc-memory>
<!--{/if}-->
</know-how-computer>
</includeonly>