LeTTo Doc

JavaScript-Schnittstelle der Plugins

Diese Beschreibung richtet sich an Entwickler von LeTTo-Plugins. Sie beschreibt, wie das JavaScript eines Plugins in der LeTTo-App aufgerufen wird, wie es die Eingabe an LeTTo zurückgibt und wie es auf die Einstellungen der App (Design, Prüfen-Buttons) zugreifen kann.

Als Vorlage dient das Demo-Plugin „Uhr“ des Referenz-Plugins (Java) (src/main/resources/plugins/uhr/uhrScript.js, https://github.com/LeTTo-GmbH/plugin-demo-java).


1. Einbinden der JavaScript-Dateien

Das Plugin gibt in seiner Java-Klasse an, welche JavaScript-Dateien LeTTo laden soll:

javascriptLibs = new String[]{"plugins/uhr/uhrScript.js", "plugins/uhr/uhrConfigScript.js", "plugins/plugintools.js"};

Die Dateien liefert das Plugin-Service selbst aus: LeTTo liest sie aus den Ressourcen des Plugin-Services (src/main/resources/<Pfad>). Das Demo-Plugin enthält dazu src/main/resources/plugins/plugintools.js mit allgemeinen Hilfsfunktionen (Zeichnen, Skalieren, Umgebung der App, siehe Abschnitt 4). Diese Datei in das eigene Plugin übernehmen und in javascriptLibs eintragen. Sie ist eine Kopie aus dem LeTTo-Repository letto-plugins – bei neuen LeTTo-Versionen kann sie neue Hilfsfunktionen enthalten. Eigene globale Funktionen dürfen nicht so heißen wie die Funktionen in plugintools.js.

2. Aufruf des Plugins

Nach dem Rendern der Frage ruft LeTTo eine globale Funktion mit dem Namen initPlugin<Typ> auf, z. B. initPluginUhr für den Plugin-Typ Uhr:

function initPluginUhr(dtoString, active) { ... }
Parameter Bedeutung
dtoString Plugin-DTO als JSON-String (JSON.parse(dtoString))
active true: der Schüler darf eingeben. false: nur anzeigen (z. B. Lösungsansicht, abgegebener Test)

Wichtige Felder des DTO:

Feld Bedeutung
tagName eindeutiger Name des Plugins in der Frage – Grundlage für die Element-Namen (siehe unten)
jsonData plugin-spezifische Daten als JSON-String (JSON.parse(dto.jsonData))
imageUrl URL eines vom Plugin-Service erzeugten Bildes
width, height Größe der Darstellung in Pixel

Regel: Alle Hilfsfunktionen des Plugins als innere Funktionen von initPlugin<Typ> anlegen. Die Funktion kann für mehrere Plugins einer Seite und mehrfach für dieselbe Frage aufgerufen werden (z. B. nach einem Neuladen).

3. Darstellung und Rückgabe der Eingabe

LeTTo gibt zwei Elemente vor:

Element Selektor Verwendung
Container "#" + dto.tagName + "_div" hier rendert das Plugin seine Darstellung (z. B. ein Canvas)
Eingabe "." + dto.tagName + "_inp" verstecktes Textfeld: hier steht die bisherige Antwort, hier schreibt das Plugin die neue Antwort hinein

Das Plugin liest beim Start die bisherige Antwort aus dem Textfeld und schreibt jede Änderung als Text (oder JSON) in dessen value. LeTTo übernimmt den Inhalt automatisch (Abfrage etwa alle 500 ms) und übergibt ihn zum Beurteilen an den Plugin-Service. Ist active false, darf das Plugin keine Eingaben annehmen.

const plugin_div = "#" + dto.tagName + "_div";
const plugin_inp = "." + dto.tagName + "_inp";
let eingabe = $(plugin_inp)[0];
let bisher = eingabe.value;          // bisherige Antwort
eingabe.value = neueAntwort;         // neue Antwort an LeTTo übergeben

4. Umgebung der App: Design und Prüfen-Buttons (window.LeTTo)

Die LeTTo-App stellt das Objekt window.LeTTo bereit. Darüber kann das Plugin z. B. seine Farben an das gewählte Design anpassen oder erkennen, ob der Schüler die Frage während des Tests prüfen lassen kann.

Feld Bedeutung
theme gewähltes Design: default, theme-soft-light, theme-soft-dark, theme-dark, theme-high-contrast
dunkel true bei dunklem Design (theme-dark, theme-soft-dark)
kontrast true bei hohem Kontrast (theme-high-contrast)
farben aktuelle Farben des Designs: text, hintergrund, primary, rahmen (CSS-Farbwerte, z. B. #ffffff)
pruefen true, wenn zur Frage ein Prüfen-Button angezeigt wird
pruefenButtons angezeigte Prüfen-Buttons: check (Prüfen), check_next (Prüfen und weiter), check_penalty (Prüfen mit Abzug), in der Beispielsammlung bp_score
modus Modus der Frage: test (Test/Übung), bp (Beispielsammlung, Vorschau), result (Ergebnisansicht) u. a.

Wann ist pruefen wahr?

Die Werte sind gesetzt, bevor initPlugin<Typ> aufgerufen wird. Das Objekt bleibt während der ganzen Sitzung dasselbe; seine Werte werden aktualisiert. Bei jeder Änderung (z. B. Wechsel des Designs) löst die App das Ereignis letto-umgebung auf window aus, event.detail enthält eine Kopie der Werte.

Hilfsfunktionen in plugintools.js

Statt direkt auf window.LeTTo zuzugreifen, sollten die Hilfsfunktionen verwendet werden. Sie funktionieren auch dort, wo es window.LeTTo nicht gibt (ältere LeTTo-Oberflächen, Plugin-Tester), und liefern dann Standardwerte – lettoPruefen() liefert in diesem Fall null (unbekannt).

Funktion Ergebnis
lettoUmgebung() alle Werte als Objekt (Felder wie oben)
lettoTheme() Name des Designs
lettoDunkel() true bei dunklem Design
lettoPruefen() true/false, null wenn unbekannt
lettoOnUmgebung(callback) ruft callback(umgebung) bei jeder Änderung auf, liefert eine Funktion zum Abmelden

Ohne plugintools.js

window.LeTTo fehlt in älteren LeTTo-Oberflächen und im Plugin-Tester. Deshalb immer mit Rückfallwerten lesen:

function umgebung() {
    const standard = {theme: 'default', dunkel: false, kontrast: false,
        farben: {text: '', hintergrund: '', primary: '', rahmen: ''},
        pruefen: null, pruefenButtons: [], modus: ''};
    return (window.LeTTo && typeof window.LeTTo === 'object') ? Object.assign({}, standard, window.LeTTo) : standard;
}
window.addEventListener('letto-umgebung', e => zeichne(e.detail));   // Änderungen

Beispiel (mit plugintools.js)

function initPluginBeispiel(dtoString, active) {
    const dto = JSON.parse(dtoString);
    const plugin_div = "#" + dto.tagName + "_div";

    function zeichne(u) {
        const farbe = u.dunkel ? (u.farben.text || '#ffffff') : '#000000';
        // ... Darstellung mit farbe in plugin_div zeichnen
    }

    zeichne(lettoUmgebung());

    // Bei Designwechsel neu zeichnen
    const abmelden = lettoOnUmgebung(u => zeichne(u));

    // Eigene Rückmeldung nur anbieten, wenn LeTTo keinen Prüfen-Button zeigt
    if (lettoPruefen() === false) {
        // ...
    }
}

5. Hinweise