Presenter techdoku

 

Technische Dokumentation – WP Presentation Builder Viewer

Übersicht

Diese Dokumentation beschreibt die wichtigsten JavaScript-Funktionen des WP Presentation Builder Viewers, insbesondere die Next-Action-Funktionalität.


Hauptklasse: WPPresentationPlayer

Constructor

Funktion: constructor(container, options)

Aufgabe: Initialisiert den Presentation Player mit allen benötigten Einstellungen

Parameter:

  • container (DOM Element) – Container-Element für die Präsentation
  • options (Object) – Konfigurationsobjekt
    • options.slides (Array) – Array von Slide-Objekten
    • options.settings (Object) – Präsentations-Einstellungen
    • options.presentation_id (Number) – WordPress Post ID der Präsentation

Rückgabewert: Keine (Constructor)

Wichtige Properties:

  • this.slides – Array aller Slides
  • this.currentSlide – Index des aktuellen Slides (0-basiert)
  • this.isFullscreen – Boolean für Fullscreen-Status
  • this.$container – jQuery-Objekt des Containers

Message-Handling

Window Message Listener

Funktion: Event Listener für window.addEventListener('message', ...)

Aufgabe: Empfängt Messages von externen Slides (iframe) und verarbeitet diese

Message Format:

{
  source: 'wp_external_slide',  // Identifiziert WP-Slides
  type: 'show_subtitle',         // Message-Typ
  data: {                        // Payload
    text: 'Untertitel Text',
    next_action: 'fill_input',
    action_selector: 'person',
    input_value: 'Max Mustermann',
    // weitere Parameter...
  }
}

Verarbeitete Message-Typen:

  • next_slide – Nächster Slide
  • prev_slide – Vorheriger Slide
  • exit_presentation – Präsentation beenden
  • subtitle_update – Untertitel aktualisieren
  • show_subtitle – Untertitel anzeigen + Next-Action ausführen
  • external_slide_ready – Externer Slide geladen

Ablauf:

  1. Message empfangen
  2. Prüfen: event.data.source === 'wp_external_slide'
  3. Switch-Case basierend auf event.data.type
  4. Bei show_subtitle: executeNextAction() aufrufen

Next-Action System

executeNextAction()

Funktion: executeNextAction(actionData)

Aufgabe: Dispatcher-Funktion die basierend auf next_action die entsprechende Action ausführt

Parameter:

  • actionData (Object) – Daten aus subtitle_rules
    • next_action (String) – Action-Typ: ’next‘, ’slide‘, ‚click_link‘, ‚click_button‘, ‚fill_input‘, ’select_option‘, ’scroll‘, ‚mouse_trail‘
    • action_selector (String) – CSS-Selector oder Element-Name
    • input_value (String) – Wert für fill_input
    • select_value (String) – Wert für select_option
    • scroll_pixels (Number) – Y-Position für scroll
    • goto_slide (Number) – Slide-Nummer für ’slide‘ action
    • trail_from (String) – Start-Koordinaten für mouse_trail
    • trail_to (String) – End-Koordinaten für mouse_trail

Rückgabewert: Keine (void)

Verzögerung: 500ms setTimeout vor Ausführung

Action-Mapping:

  • 'next'nextSlide()
  • 'slide'goToSlide(goto_slide - 1)
  • 'click_link' / 'click_button'executeClickAction(selector)
  • 'fill_input'executeFillAction(selector, value)
  • 'select_option'executeSelectAction(selector, value)
  • 'scroll'executeScrollAction(pixels)
  • 'mouse_trail'executeMouseTrailAction(from, to)

Beispiel:

executeNextAction({
  next_action: 'fill_input',
  action_selector: 'person',
  input_value: 'Max Mustermann'
});

executeFillAction()

Funktion: executeFillAction(selector, value, retryCount = 0)

Aufgabe: Füllt ein Input- oder Textarea-Feld im iframe mit einem Wert aus

Parameter:

  • selector (String) – Element-Selector (name, id, CSS-Selector)
  • value (String) – Einzutragender Wert
  • retryCount (Number, optional) – Anzahl bisheriger Versuche (für Retry-Logik)

Rückgabewert: Keine (void)

Ablauf:

  1. Sucht iframe: .wp-slide-frame
  2. Holt iframe Document: iframe.contentDocument
  3. Sucht Element mit 3 Strategien:
    • [name="${selector}"] – Nach name-Attribut
    • querySelector(selector) – Als CSS-Selector
    • #${selector} – Als ID mit #-Prefix
  4. Prüft ob Element INPUT oder TEXTAREA ist
  5. Fokussiert Element: element.focus()
  6. Setzt Wert: element.value = value
  7. Triggert Events:
    • new Event('input', { bubbles: true })
    • new Event('change', { bubbles: true })

Retry-Mechanismus:

  • Max. 3 Versuche bei nicht gefundenem Element
  • Verzögerungen: 300ms, 600ms, 900ms
  • Nach 3 Fehlversuchen: Listet alle verfügbaren Inputs auf

Beispiel:

executeFillAction('person', 'Max Mustermann');
// Sucht: input[name="person"] oder #person
// Trägt ein: "Max Mustermann"

executeClickAction()

Funktion: executeClickAction(selector)

Aufgabe: Klickt ein Element (Link, Button) im iframe

Parameter:

  • selector (String) – CSS-Selector oder Text-Inhalt

Rückgabewert: Keine (void)

Suchstrategien:

  1. CSS-Selector: querySelector(selector)
  2. Text-Content: Durchsucht alle a, button nach Text

Beispiel:

executeClickAction('.submit-btn');
executeClickAction('Weiter');  // Klickt Button mit Text "Weiter"

executeSelectAction()

Funktion: executeSelectAction(selector, value)

Aufgabe: Wählt eine Option in einem Select-Dropdown

Parameter:

  • selector (String) – Select-Element (name, id, CSS-Selector)
  • value (String) – Wert oder Text der zu wählenden Option

Rückgabewert: Keine (void)

Suchstrategien für Element:

  1. select[name="${selector}"]
  2. select#${selector}
  3. CSS-Selector: querySelector(selector)

Suchstrategien für Option:

  1. Nach value: element.value = value
  2. Nach Text: Durchsucht <option>-Elemente

Triggert: change Event

Beispiel:

executeSelectAction('country', 'AT');  // Wählt value="AT"
executeSelectAction('country', 'Österreich');  // Wählt nach Text

executeScrollAction()

Funktion: executeScrollAction(pixels)

Aufgabe: Scrollt das iframe zu einer bestimmten Y-Position

Parameter:

  • pixels (Number) – Y-Position in Pixeln

Rückgabewert: Keine (void)

Scroll-Verhalten: Smooth scrolling aktiviert

Beispiel:

executeScrollAction(500);  // Scrollt zu Y=500px

executeMouseTrailAction()

Funktion: executeMouseTrailAction(from, to)

Aufgabe: Zeigt animierte Mausspur von Start- zu End-Position

Parameter:

  • from (String) – Start-Koordinaten „X,Y“ oder CSS-Selector
  • to (String) – End-Koordinaten „X,Y“ oder CSS-Selector

Rückgabewert: Keine (void)

Koordinaten-Formate:

  • Pixel: "100,200" = X:100px, Y:200px
  • Selector: ".my-button" = Zentrum des Elements

Animation:

  • Dauer: 2 Sekunden
  • Easing: ease-out
  • Cursor-Darstellung: Roter Kreis (20px)

Beispiel:

executeMouseTrailAction('100,50', '300,400');  // Von Pixel zu Pixel
executeMouseTrailAction('.start-btn', '.end-btn');  // Von Element zu Element

parseCoordinates()

Funktion: parseCoordinates(input, iframeDoc)

Aufgabe: Konvertiert String (Koordinaten oder Selector) zu X/Y-Koordinaten

Parameter:

  • input (String) – „X,Y“ oder CSS-Selector oder Text
  • iframeDoc (Document) – iframe Document-Objekt

Rückgabewert:

  • {x: Number, y: Number} – Koordinaten
  • null – Bei Fehler

Strategien:

  1. Regex-Match für „100,200“ Format
  2. CSS-Selector: querySelector(input)
  3. Text-Suche in typischen Elementen
  4. Berechnet Zentrum des Elements via getBoundingClientRect()

Beispiel:

parseCoordinates('100,200', iframeDoc);  // → {x: 100, y: 200}
parseCoordinates('.my-button', iframeDoc);  // → {x: 250, y: 150} (Zentrum)

Slide-Loading

loadSlide()

Funktion: loadSlide(index)

Aufgabe: Lädt einen Slide und registriert alle Event-Handler für subtitle_rules

Parameter:

  • index (Number) – Slide-Index (0-basiert)

Rückgabewert: Keine (void)

Ablauf:

  1. Validierung: index >= 0 && index < slides.length
  2. Stop AutoPlay Timer
  3. Fade-Out alter Slide (300ms)
  4. Warte 350ms
  5. Parse subtitle_rules (Array oder Object → Array)
  6. Bestimme Slide-Typ: content, post, admin, external
  7. Erstelle iframe oder Direct-Content
  8. Für externe Slides: Verwende Proxy mit subtitle_rules als Parameter
  9. Registriere Event-Handler
  10. Starte zeitbasierte subtitle_rules
  11. Update Counter & Progress

Slide-Typen:

  • content – Direkter HTML-Content
  • post – WordPress Post/CPT
  • admin – WordPress Admin-Bereich
  • external – Externe URL (mit Proxy)

Subtitle Rules Format:

[
  {
    type: 'input',      // Event-Typ
    action: 'person',   // Feld-Name oder Element-ID
    text: 'Name eingegeben',
    next_action: 'fill_input',
    action_selector: 'email',
    input_value: 'max@example.com'
  }
]

registerEventHandlers()

Funktion: registerEventHandlers($container, subtitleRules, $subtitleNav)

Aufgabe: Registriert alle Event-Handler für subtitle_rules im Slide-Container

Parameter:

  • $container (jQuery Object) – Container (Slide oder iframe body)
  • subtitleRules (Array) – Array von subtitle_rule Objekten
  • $subtitleNav (jQuery Object) – Subtitle-Element zum Updaten

Rückgabewert: Keine (void)

Registrierte Events:

1. Click auf [data-subtitle-action]

$container.find('[data-subtitle-action]').on('click', ...)

Matcht Rules mit type: 'click' und action === data-subtitle-action

2. Click auf Links (a[href])

$container.find('a[href]:not([data-subtitle-action])').on('click', ...)

Action-Key-Priorität: data-id > href > name > id > linkText

3. Input Events

$container.find('input[name], textarea[name], select[name]').on('input', ...)

Triggert bei Tippen

4. Change Events

$container.find('input[name], textarea[name], select[name]').on('change', ...)

Triggert bei Wert-Änderung + Feld verlassen

5. Focus Events

$container.find('input[name], textarea[name], select[name]').on('focus', ...)

Triggert wenn Feld aktiv wird

6. Blur Events

$container.find('input[name], textarea[name], select[name]').on('blur', ...)

Triggert wenn Feld verlassen wird

7. Button Clicks

$container.find('button, input[type=submit], input[type=button]').on('click', ...)

Action-Key: name oder Button-Text


processInputRule()

Funktion: processInputRule(eventType, $input, subtitleRules, $subtitleNav)

Aufgabe: Verarbeitet Input-Event und setzt Untertitel mit Platzhalter-Ersetzung

Parameter:

  • eventType (String) – Event-Typ: ‚input‘, ‚change‘, ‚focus‘, ‚blur‘
  • $input (jQuery Object) – Input-Element
  • subtitleRules (Array) – Array von Rules
  • $subtitleNav (jQuery Object) – Subtitle-Element

Rückgabewert: Keine (void)

Platzhalter:

  • {value} – Aktueller Wert
  • {fieldName} – name-Attribut
  • {fieldType} – Feld-Typ (input type oder ’select‘)
  • {fieldId} – id-Attribut
  • {placeholder} – placeholder-Text
  • {selectedValue} – Wert der gewählten Option (bei select)
  • {selectedText} – Text der gewählten Option (bei select)
  • {eventType} – Event-Typ

Beispiel:

// Rule:
{
  type: 'input',
  action: 'person',
  text: 'Name: {value}',
  next_action: 'next'
}
// User tippt "Max" in input[name="person"]
// → Subtitle: "Name: Max"

Navigation

nextSlide()

Funktion: nextSlide()

Aufgabe: Geht zum nächsten Slide

Parameter: Keine

Rückgabewert: Keine (void)

Verhalten:

  • Bei letztem Slide + Loop aktiviert: Springt zu Slide 0
  • Bei letztem Slide ohne Loop: Keine Aktion

previousSlide()

Funktion: previousSlide()

Aufgabe: Geht zum vorherigen Slide

Parameter: Keine

Rückgabewert: Keine (void)

Verhalten:

  • Bei erstem Slide + Loop aktiviert: Springt zu letztem Slide
  • Bei erstem Slide ohne Loop: Keine Aktion

goToSlide()

Funktion: goToSlide(index)

Aufgabe: Springt zu einem bestimmten Slide

Parameter:

  • index (Number) – Slide-Index (0-basiert)

Rückgabewert: Keine (void)

Zusatz: Synchronized mit Presenter-Ansicht via BroadcastChannel


Datenfluss: subtitle_rules → next_action

1. PHP Backend (wp-presentation-builder.php)

// Slide-Meta speichern
subtitle_rules[] = [
  'type' => 'input',
  'action' => 'person',
  'text' => 'Name eingegeben',
  'next_action' => 'fill_input',
  'action_selector' => 'email',
  'input_value' => 'test@example.com'
]

2. PHP → JavaScript (generate_interactive_script)

var subtitleRules = <?php echo json_encode($subtitle_rules); ?>;

// Bei Input-Event:
window.parent.postMessage({
  source: 'wp_external_slide',
  type: 'show_subtitle',
  data: {
    text: rule.text,
    next_action: rule.next_action,
    action_selector: rule.action_selector,
    input_value: rule.input_value
  }
}, '*');

3. Viewer empfängt Message

window.addEventListener('message', function(event) {
  if (event.data.source === 'wp_external_slide' && 
      event.data.type === 'show_subtitle') {
    
    // Setze Subtitle
    $subtitle.text(event.data.data.text);
    
    // Führe next_action aus
    executeNextAction(event.data.data);
  }
});

4. Next-Action Ausführung

executeNextAction(actionData) {
  setTimeout(() => {
    switch(actionData.next_action) {
      case 'fill_input':
        executeFillAction(
          actionData.action_selector,  // 'email'
          actionData.input_value       // 'test@example.com'
        );
        break;
    }
  }, 500);
}

5. Fill-Action im iframe

executeFillAction('email', 'test@example.com') {
  // Finde Element
  const element = iframeDoc.querySelector('[name="email"]');
  
  // Setze Wert
  element.value = 'test@example.com';
  
  // Trigger Events
  element.dispatchEvent(new Event('input'));
  element.dispatchEvent(new Event('change'));
}

Debugging

Console Logs Hierarchie

Level 1: Message Empfang

🔔 Message Event empfangen: {hasData, source, type, fullData}

Level 2: Message Verarbeitung

📨 Message von externem Slide empfangen: {complete event.data}
📨 Message Type: "show_subtitle"

Level 3: Case Execution

🎯 show_subtitle Case erreicht!
📦 subtitleData: {complete data object}

Level 4: Element Suche

🔍 Suche .wp-subtitle Element: {found, count, element}

Level 5: Action Dispatch

✅ Subtitle von externem Slide gesetzt: "Text"
🎬 next_action: "fill_input"
🚀 Führe next_action aus: "fill_input"

Level 6: Action Execution

✍️ Fill Input: {selector: "person", value: "Max"}
🔍 executeFillAction aufgerufen (Versuch 1): {selector, value}
📺 iframe gefunden: true
📄 iframeDoc: vorhanden
📄 iframeDoc.readyState: complete

Level 7: Element Finding

🔎 Element per name gesucht: [name="person"] gefunden ✓

Level 8: Value Setting

✅ Element gefunden! Details: {tagName, type, name, id, currentValue, disabled, readOnly}
📝 Wert gesetzt: "Max"
✅ Events getriggert, finaler Wert: "Max"

Häufige Probleme

Problem: next_action wird nicht ausgeführt

Mögliche Ursachen:

  1. Message kommt nicht an → Prüfe: 🔔 Message Event
  2. Falscher source → Prüfe: event.data.source === ‚wp_external_slide‘
  3. Falscher type → Prüfe: event.data.type === ’show_subtitle‘
  4. Element nicht gefunden → Prüfe: 🔍 Suche .wp-subtitle Element
  5. next_action leer → Prüfe: 🎬 next_action

Problem: Input wird nicht befüllt

Mögliche Ursachen:

  1. Falscher selector → Prüfe HTML: <input name="person">
  2. Element nicht im iframe → Prüfe: 📺 iframe gefunden
  3. Timing-Problem → Retry-Mechanismus sollte helfen
  4. Falsches Tag → Element muss INPUT oder TEXTAREA sein

Problem: Daten aus PHP kommen nicht an

Prüfpunkte:

  1. PHP: echo json_encode($subtitle_rules) in generate_interactive_script
  2. Browser-Quellcode: Suche „subtitleRules“ im Script
  3. Console: subtitleRules Variable ausgeben
  4. Message: Prüfe ob alle Felder übertragen werden

Best Practices

Selector Naming

// Gut - Name-Attribut
<input name="person">
action_selector: "person"

// Gut - ID mit #
<input id="email">
action_selector: "#email"

// Gut - CSS Class
<input class="user-name">
action_selector: ".user-name"

// Vermeiden - Komplexe Selectors
action_selector: "div > form input:nth-child(3)"

Timing

  • 500ms Delay bei executeNextAction → Zeit für Subtitle-Anzeige
  • 300/600/900ms Retries bei executeFillAction → Zeit für DOM-Aufbau
  • 2000ms Mouse Trail Animation → Sichtbar aber nicht zu langsam

Error Handling

  • Immer console.log für erfolgreiche Actions
  • console.warn für nicht gefundene Elemente
  • console.error für schwere Fehler (kein iframe)
  • Retry-Logik für Timing-Probleme
  • Fallback-Suche mit mehreren Strategien

Versionierung

Aktuelle Version: 0.13.1

Changelog Next-Action Feature:

  • v0.13.0: Basis next_action Implementierung (next, slide)
  • v0.13.1: Erweiterte Actions (fill_input, click, select, scroll, mouse_trail)
  • v0.13.1: Retry-Mechanismus für executeFillAction
  • v0.13.1: Umfassendes Debug-Logging