/* =============================================
   HELLION NEWTAB — calculator.js
   Taschenrechner Widget: Expression-Parsing,
   History, Tastatureingabe
   ============================================= */

const Calculator = {
  WIDGET_ID: 'widget_calculator',
  STORAGE_KEY: 'widgetStates',
  MAX_HISTORY: 10,

  /** @type {Array<{expr: string, result: string}>} */
  _history: [],
  _currentExpr: '',
  _lastResult: '',
  _isOpen: false,
  _displayExprEl: null,
  _displayResultEl: null,
  _keydownHandler: null,
  _modes: new Map(),
  _activeMode: 'standard',
  _tabBarEl: null,

  // ---- MODE REGISTRY ----

  /**
   * Modus registrieren (wird von externen Mode-Dateien aufgerufen)
   * @param {string} name - Eindeutiger Modus-Name
   * @param {Object} config - { label, shortName, titleKey, render(bodyEl), destroy() }
   */
  registerMode(name, config) {
    this._modes.set(name, config);
    // Tab-Bar aktualisieren falls Widget bereits offen
    if (this._tabBarEl) this._renderTabBar();
  },

  // ---- STORAGE ----

  /**
   * Calculator-State aus Storage laden
   */
  async load() {
    const data = await Store.get(this.STORAGE_KEY);
    if (data && data.calculator) {
      this._history = Array.isArray(data.calculator.history) ? data.calculator.history : [];
      if (data.calculator.activeMode) {
        this._activeMode = data.calculator.activeMode;
      }
    }
  },

  /**
   * Calculator-State in Storage speichern
   * Bestehende Notes-Daten bleiben erhalten
   */
  async save() {
    const data = await Store.get(this.STORAGE_KEY) || {};

    // Widget-Position aus WidgetManager holen
    const widgetState = WidgetManager.getState(this.WIDGET_ID);
    if (!data.calculator) data.calculator = {};
    data.calculator.x = widgetState ? widgetState.x : 400;
    data.calculator.y = widgetState ? widgetState.y : 120;
    data.calculator.width = widgetState ? widgetState.width : 280;
    data.calculator.height = widgetState ? widgetState.height : 400;
    data.calculator.open = this._isOpen;
    data.calculator.activeMode = this._activeMode;
    data.calculator.history = this._history.slice(0, this.MAX_HISTORY);

    await Store.set(this.STORAGE_KEY, data);
  },

  // ---- WIDGET LIFECYCLE ----

  /**
   * Calculator oeffnen oder in Vordergrund bringen
   */
  async open() {
    if (this._isOpen) {
      WidgetManager.bringToFront(this.WIDGET_ID);
      return;
    }

    // Gespeicherte Position laden
    const data = await Store.get(this.STORAGE_KEY);
    const saved = (data && data.calculator) ? data.calculator : {};

    const widgetId = WidgetManager.create('calculator', {
      id: this.WIDGET_ID,
      title: t('calculator.title'),
      x: saved.x || 400,
      y: saved.y || 120,
      width: saved.width || 280,
      height: saved.height || 400,
      open: true
    });

    const body = WidgetManager.getBody(widgetId);
    if (body) this.renderBody(body);

    this._isOpen = true;

    // Keyboard-Events binden
    const entry = WidgetManager._widgets.get(this.WIDGET_ID);
    if (entry) this._bindKeyboard(entry.el);

    await this.save();
  },

  /**
   * Calculator toggle: oeffnen oder minimieren
   */
  async toggle() {
    if (this._isOpen) {
      const entry = WidgetManager._widgets.get(this.WIDGET_ID);
      if (entry && entry.state.open) {
        await WidgetManager.minimize(this.WIDGET_ID);
        this._isOpen = false;
        await this.save();
      } else if (entry) {
        await WidgetManager.openWidget(this.WIDGET_ID);
        this._isOpen = true;
        await this.save();
      }
    } else {
      await this.open();
    }
  },

  /**
   * Wird aufgerufen wenn Widget geschlossen wird
   */
  async onClose() {
    // Aktiven Modus aufräumen
    const mode = this._modes.get(this._activeMode);
    if (mode && mode.destroy) mode.destroy();

    this._isOpen = false;
    this._unbindKeyboard();
    this._tabBarEl = null;
    this._displayExprEl = null;
    this._displayResultEl = null;
    await this.save();
  },

  // ---- UI RENDERING ----

  /**
   * Calculator-Body rendern: Tab-Bar + aktiver Modus
   * @param {HTMLElement} bodyEl
   */
  renderBody(bodyEl) {
    bodyEl.textContent = '';
    bodyEl.style.padding = '0';
    bodyEl.style.display = 'flex';
    bodyEl.style.flexDirection = 'column';
    bodyEl.style.height = '100%';

    // Tab-Bar
    const tabBar = document.createElement('div');
    tabBar.className = 'calc-tab-bar';
    this._tabBarEl = tabBar;
    this._renderTabBar();

    // Mode-Body Container
    const modeBody = document.createElement('div');
    modeBody.className = 'calc-mode-body';

    bodyEl.append(tabBar, modeBody);

    // Aktiven Modus rendern
    const mode = this._modes.get(this._activeMode);
    if (mode) {
      mode.render(modeBody);
    }
  },

  /**
   * Tab-Bar mit Buttons aus _modes Map befüllen
   */
  _renderTabBar() {
    if (!this._tabBarEl) return;
    while (this._tabBarEl.firstChild) {
      this._tabBarEl.removeChild(this._tabBarEl.firstChild);
    }

    this._modes.forEach((config, name) => {
      const tab = document.createElement('button');
      tab.type = 'button';
      tab.className = 'calc-tab' + (name === this._activeMode ? ' active' : '');
      tab.dataset.mode = name;

      const icon = document.createElement('span');
      icon.className = 'calc-tab-icon';
      icon.textContent = config.label;

      const label = document.createElement('span');
      label.className = 'calc-tab-label';
      label.textContent = config.shortName;

      tab.append(icon, label);
      tab.addEventListener('click', () => this.switchMode(name));
      this._tabBarEl.appendChild(tab);
    });
  },

  /**
   * Aktiven Tab visuell markieren (ohne Neuaufbau)
   */
  _updateTabBar() {
    if (!this._tabBarEl) return;
    const tabs = this._tabBarEl.querySelectorAll('.calc-tab');
    tabs.forEach(tab => {
      tab.classList.toggle('active', tab.dataset.mode === this._activeMode);
    });
  },

  /**
   * Modus wechseln
   * @param {string} name - Ziel-Modus
   */
  async switchMode(name) {
    if (name === this._activeMode) return;
    const mode = this._modes.get(name);
    if (!mode) return;

    // Alten Modus aufräumen
    const oldMode = this._modes.get(this._activeMode);
    if (oldMode && oldMode.destroy) oldMode.destroy();

    this._activeMode = name;

    // Mode-Body leeren und neu rendern
    const entry = WidgetManager._widgets.get(this.WIDGET_ID);
    if (!entry) return;
    const modeBody = entry.el.querySelector('.calc-mode-body');
    if (!modeBody) return;
    modeBody.textContent = '';
    mode.render(modeBody);

    // Tab-UI aktualisieren
    this._updateTabBar();

    // Auto-Resize für komplexe Modi
    const isComplex = name !== 'standard';
    if (isComplex && entry) {
      const state = entry.state;
      if (state) {
        const newW = Math.max(state.width, 320);
        const newH = Math.max(state.height, 480);
        if (newW !== state.width || newH !== state.height) {
          entry.el.style.width = newW + 'px';
          entry.el.style.height = newH + 'px';
          state.width = newW;
          state.height = newH;
        }
      }
    }

    // Keyboard neu binden
    this._unbindKeyboard();
    if (name === 'standard' || name === 'scientific') {
      if (entry) this._bindKeyboard(entry.el);
    }

    await this.save();
  },

  /**
   * Standard-Modus UI rendern
   * @param {HTMLElement} bodyEl
   */
  _renderStandardMode(bodyEl) {
    bodyEl.style.padding = '8px';
    bodyEl.style.display = 'flex';
    bodyEl.style.flexDirection = 'column';
    bodyEl.style.flex = '1';
    bodyEl.style.overflow = 'hidden';

    // Display
    const display = document.createElement('div');
    display.className = 'calc-display';

    const exprEl = document.createElement('div');
    exprEl.className = 'calc-expression';
    this._displayExprEl = exprEl;

    const resultEl = document.createElement('div');
    resultEl.className = 'calc-result';
    resultEl.textContent = '0';
    this._displayResultEl = resultEl;

    display.append(exprEl, resultEl);

    // Buttons
    const buttonsEl = this._createButtons();

    // History
    const historyEl = this._createHistoryPanel();

    bodyEl.append(display, buttonsEl, historyEl);

    // Aktuellen State anzeigen
    this._updateDisplay();
  },

  /**
   * Button-Grid erstellen (4x5)
   * @returns {HTMLElement}
   */
  _createButtons() {
    const grid = document.createElement('div');
    grid.className = 'calc-buttons';

    // Button-Layout: [label, value, cssClass]
    const buttons = [
      ['C',  'clear',     'clear'],
      ['()', 'paren',     'operator'],
      ['%',  '%',         'operator'],
      ['\u00F7', '/',     'operator'],
      ['7',  '7',         ''],
      ['8',  '8',         ''],
      ['9',  '9',         ''],
      ['\u00D7', '*',     'operator'],
      ['4',  '4',         ''],
      ['5',  '5',         ''],
      ['6',  '6',         ''],
      ['\u2212', '-',     'operator'],
      ['1',  '1',         ''],
      ['2',  '2',         ''],
      ['3',  '3',         ''],
      ['+',  '+',         'operator'],
      ['0',  '0',         ''],
      ['.',  '.',         ''],
      ['\u232B', 'backspace', ''],
      ['=',  '=',         'equals']
    ];

    buttons.forEach(([label, value, cls]) => {
      const btn = document.createElement('button');
      btn.className = 'calc-btn' + (cls ? ' ' + cls : '');
      btn.textContent = label;
      btn.type = 'button';
      btn.addEventListener('click', () => this._handleKey(value));
      grid.appendChild(btn);
    });

    return grid;
  },

  /**
   * History-Panel erstellen
   * @returns {HTMLElement}
   */
  _createHistoryPanel() {
    const container = document.createElement('div');
    container.className = 'calc-history';
    container.id = 'calcHistoryPanel';

    const title = document.createElement('div');
    title.className = 'calc-history-title';
    title.textContent = t('calculator.history');
    container.appendChild(title);

    this._renderHistoryItems(container);

    return container;
  },

  /**
   * History-Items rendern
   * @param {HTMLElement} container
   */
  _renderHistoryItems(container) {
    // Alte Items entfernen (nur die .calc-history-item Elemente)
    const oldItems = container.querySelectorAll('.calc-history-item');
    oldItems.forEach(item => item.remove());

    if (this._history.length === 0) return;

    // Neueste zuerst
    const reversed = [...this._history].reverse();
    reversed.forEach(entry => {
      const item = document.createElement('div');
      item.className = 'calc-history-item';

      const exprSpan = document.createElement('span');
      exprSpan.textContent = entry.expr;

      const resultSpan = document.createElement('span');
      resultSpan.className = 'calc-h-result';
      resultSpan.textContent = '= ' + entry.result;

      item.append(exprSpan, resultSpan);

      // Klick uebernimmt Ergebnis als neue Eingabe
      item.addEventListener('click', () => {
        this._currentExpr = entry.result;
        this._lastResult = '';
        this._updateDisplay();
      });

      container.appendChild(item);
    });
  },

  // ---- INPUT HANDLING ----

  /**
   * Taste verarbeiten
   * @param {string} key
   */
  _handleKey(key) {
    switch (key) {
      case 'clear':
        this._currentExpr = '';
        this._lastResult = '';
        break;

      case 'backspace':
        this._currentExpr = this._currentExpr.slice(0, -1);
        break;

      case '=':
        this._calculate();
        return;

      case 'paren': {
        // Smarte Klammern: oeffnende wenn noetig, sonst schliessende
        const openCount = (this._currentExpr.match(/\(/g) || []).length;
        const closeCount = (this._currentExpr.match(/\)/g) || []).length;
        const lastChar = this._currentExpr.slice(-1);
        if (openCount <= closeCount || /[+\-*/%(]$/.test(lastChar) || this._currentExpr === '') {
          this._currentExpr += '(';
        } else {
          this._currentExpr += ')';
        }
        break;
      }

      case '%':
      case '+':
      case '-':
      case '*':
      case '/':
      case '^': {
        // Wenn gerade ein Ergebnis angezeigt wird, damit weiterrechnen
        if (this._lastResult && this._currentExpr === '') {
          this._currentExpr = this._lastResult;
          this._lastResult = '';
        }
        // Doppelte Operatoren verhindern (letzten ersetzen)
        const last = this._currentExpr.slice(-1);
        if (/[+\-*/%^]/.test(last)) {
          this._currentExpr = this._currentExpr.slice(0, -1) + key;
        } else {
          this._currentExpr += key;
        }
        break;
      }

      case '.': {
        // Doppelten Dezimalpunkt im letzten Zahlenblock verhindern
        const parts = this._currentExpr.split(/[+\-*/%()^]/);
        const lastPart = parts[parts.length - 1];
        if (lastPart && lastPart.includes('.')) break;
        this._currentExpr += key;
        break;
      }

      default:
        // Ziffern 0-9
        if (/^[0-9]$/.test(key)) {
          // Wenn ein Ergebnis da ist und User eine Zahl tippt, neue Berechnung starten
          if (this._lastResult && this._currentExpr === '') {
            this._lastResult = '';
          }
          this._currentExpr += key;
        }
        break;
    }

    this._updateDisplay();
  },

  /**
   * Berechnung ausfuehren
   */
  async _calculate() {
    if (!this._currentExpr) return;

    const result = this._evaluate(this._currentExpr);
    if (result === null) {
      this._lastResult = t('calculator.error');
      this._updateDisplay();
      return;
    }

    const resultStr = this._formatResult(result);
    this._addHistory(this._currentExpr, resultStr);
    this._lastResult = resultStr;

    // Display aktualisieren
    if (this._displayExprEl) {
      this._displayExprEl.textContent = this._formatExpression(this._currentExpr) + ' =';
    }
    if (this._displayResultEl) {
      this._displayResultEl.textContent = resultStr;
    }

    this._currentExpr = '';

    // History-Panel aktualisieren
    const historyPanel = document.getElementById('calcHistoryPanel');
    if (historyPanel) this._renderHistoryItems(historyPanel);

    await this.save();
  },

  // ---- EXPRESSION PARSER (Shunting-Yard, KEIN eval!) ----

  /**
   * Expression sicher auswerten
   * @param {string} expr
   * @returns {number|null}
   */
  _evaluate(expr) {
    try {
      // Nur erlaubte Zeichen
      const sanitized = expr.replace(/[^0-9+\-*/.%()^a-z]/g, '');
      if (!sanitized) return null;

      const tokens = this._tokenize(sanitized);
      if (!tokens) return null;

      return this._parseExpression(tokens);
    } catch {
      return null;
    }
  },

  /**
   * Expression in Tokens aufteilen
   * @param {string} expr
   * @returns {Array|null}
   */
  _tokenize(expr) {
    const tokens = [];
    let i = 0;

    while (i < expr.length) {
      const ch = expr[i];

      // Funktion: sqrt
      if (expr.substring(i, i + 4) === 'sqrt') {
        tokens.push({ type: 'func', value: 'sqrt' });
        i += 4;
        continue;
      }

      // Zahl (inkl. Dezimal)
      if (/[0-9.]/.test(ch)) {
        let num = '';
        while (i < expr.length && /[0-9.]/.test(expr[i])) {
          num += expr[i];
          i++;
        }
        const parsed = parseFloat(num);
        if (isNaN(parsed)) return null;
        tokens.push({ type: 'number', value: parsed });
        continue;
      }

      // Operator
      if (/[+\-*/%]/.test(ch)) {
        // Negativer Vorzeichen-Check: am Anfang oder nach Operator/oeffnender Klammer
        if (ch === '-') {
          const prev = tokens[tokens.length - 1];
          if (!prev || prev.type === 'op' || (prev.type === 'paren' && prev.value === '(')) {
            // Negatives Vorzeichen → als Teil der naechsten Zahl lesen
            let num = '-';
            i++;
            while (i < expr.length && /[0-9.]/.test(expr[i])) {
              num += expr[i];
              i++;
            }
            if (num === '-') return null;
            const parsed = parseFloat(num);
            if (isNaN(parsed)) return null;
            tokens.push({ type: 'number', value: parsed });
            continue;
          }
        }
        tokens.push({ type: 'op', value: ch });
        i++;
        continue;
      }

      // Potenz-Operator
      if (ch === '^') {
        tokens.push({ type: 'op', value: '^' });
        i++;
        continue;
      }

      // Klammern
      if (ch === '(' || ch === ')') {
        tokens.push({ type: 'paren', value: ch });
        i++;
        continue;
      }

      // Unbekannte Buchstaben
      if (/[a-z]/.test(ch)) {
        return null;
      }

      // Unbekanntes Zeichen
      return null;
    }

    return tokens;
  },

  /**
   * Rekursiver Descent Parser mit Operator-Precedence
   * Hierarchie: parseExpr (+/-) → parseTerm (*\/%) → parsePower (^) → parseFactor
   * @param {Array} tokens
   * @returns {number|null}
   */
  _parseExpression(tokens) {
    let pos = 0;

    function peek() { return tokens[pos]; }
    function consume() { return tokens[pos++]; }

    function parseExpr() {
      let left = parseTerm();
      if (left === null) return null;
      while (pos < tokens.length) {
        const tk = peek();
        if (!tk || tk.type !== 'op' || (tk.value !== '+' && tk.value !== '-')) break;
        consume();
        const right = parseTerm();
        if (right === null) return null;
        left = tk.value === '+' ? left + right : left - right;
      }
      return left;
    }

    function parseTerm() {
      let left = parsePower();
      if (left === null) return null;
      while (pos < tokens.length) {
        const tk = peek();
        if (!tk || tk.type !== 'op' || (tk.value !== '*' && tk.value !== '/' && tk.value !== '%')) break;
        consume();
        const right = parsePower();
        if (right === null) return null;
        if (tk.value === '*') {
          left = left * right;
        } else if (tk.value === '/') {
          if (right === 0) return null;
          left = left / right;
        } else {
          left = left % right;
        }
      }
      return left;
    }

    // Power: Factor ('^' Power)?  — rechts-assoziativ via Rekursion
    function parsePower() {
      let base = parseFactor();
      if (base === null) return null;
      const tk = peek();
      if (tk && tk.type === 'op' && tk.value === '^') {
        consume();
        const exp = parsePower(); // Rechts-assoziativ!
        if (exp === null) return null;
        return Math.pow(base, exp);
      }
      return base;
    }

    // Factor: func '(' Expression ')' | Number | '(' Expression ')'
    function parseFactor() {
      const tk = peek();
      if (!tk) return null;

      // Funktion: sqrt(...)
      if (tk.type === 'func') {
        const funcName = tk.value;
        consume();
        const open = peek();
        if (!open || open.type !== 'paren' || open.value !== '(') return null;
        consume();
        const val = parseExpr();
        if (val === null) return null;
        const close = peek();
        if (close && close.type === 'paren' && close.value === ')') {
          consume();
        }
        if (funcName === 'sqrt') {
          if (val < 0) return null; // Negativer Radikand nicht erlaubt
          return Math.sqrt(val);
        }
        return null;
      }

      if (tk.type === 'number') {
        consume();
        return tk.value;
      }

      if (tk.type === 'paren' && tk.value === '(') {
        consume();
        const val = parseExpr();
        if (val === null) return null;
        const closing = peek();
        if (closing && closing.type === 'paren' && closing.value === ')') {
          consume();
        }
        return val;
      }

      return null;
    }

    const result = parseExpr();

    // Alle Tokens muessen verbraucht sein
    if (pos < tokens.length) return null;

    if (result === null || !isFinite(result)) return null;
    return result;
  },

  // ---- FORMATTING ----

  /**
   * Ergebnis formatieren (maximal 10 Dezimalstellen, trailing Nullen entfernen)
   * @param {number} num
   * @returns {string}
   */
  _formatResult(num) {
    if (Number.isInteger(num)) return num.toString();
    // Maximal 10 Dezimalstellen, trailing Nullen weg
    const str = num.toFixed(10).replace(/\.?0+$/, '');
    return str;
  },

  /**
   * Expression fuer Anzeige formatieren (× statt *, ÷ statt /)
   * @param {string} expr
   * @returns {string}
   */
  _formatExpression(expr) {
    return expr
      .replace(/\*/g, '\u00D7')
      .replace(/\//g, '\u00F7')
      .replace(/sqrt\(/g, '\u221A(');
  },

  // ---- DISPLAY ----

  /**
   * Display aktualisieren
   */
  _updateDisplay() {
    if (this._displayExprEl) {
      if (this._lastResult) {
        // Ergebnis-Modus: Expression oben, Ergebnis gross
        // (wird von _calculate() direkt gesetzt)
      } else {
        this._displayExprEl.textContent = '';
      }
    }
    if (this._displayResultEl) {
      if (this._lastResult && this._currentExpr === '') {
        this._displayResultEl.textContent = this._lastResult;
      } else {
        this._displayResultEl.textContent = this._formatExpression(this._currentExpr) || '0';
      }
    }
  },

  // ---- HISTORY ----

  /**
   * History-Eintrag hinzufuegen
   * @param {string} expr
   * @param {string} result
   */
  _addHistory(expr, result) {
    this._history.push({
      expr: this._formatExpression(expr),
      result: result
    });
    // Limit einhalten
    if (this._history.length > this.MAX_HISTORY) {
      this._history = this._history.slice(-this.MAX_HISTORY);
    }
  },

  // ---- KEYBOARD ----

  /**
   * Tastatur-Events binden
   * @param {HTMLElement} widgetEl
   */
  _bindKeyboard(widgetEl) {
    this._unbindKeyboard();

    this._keydownHandler = (e) => {
      // Nur reagieren wenn Calculator-Widget fokussiert ist
      // (d.h. nicht wenn User in Textarea/Input tippt)
      if (e.target.tagName === 'TEXTAREA' || e.target.tagName === 'INPUT') return;
      if (e.target.contentEditable === 'true') return;

      const key = e.key;
      let handled = false;

      if (/^[0-9]$/.test(key)) {
        this._handleKey(key);
        handled = true;
      } else if (key === '+' || key === '-' || key === '*' || key === '/') {
        this._handleKey(key);
        handled = true;
      } else if (key === '.') {
        this._handleKey('.');
        handled = true;
      } else if (key === '%') {
        this._handleKey('%');
        handled = true;
      } else if (key === '(' || key === ')') {
        this._handleKey('paren');
        handled = true;
      } else if (key === 'Enter' || key === '=') {
        this._handleKey('=');
        handled = true;
      } else if (key === 'Backspace') {
        this._handleKey('backspace');
        handled = true;
      } else if (key === 'Escape' || key === 'c' || key === 'C') {
        this._handleKey('clear');
        handled = true;
      }

      if (handled) {
        e.preventDefault();
        e.stopPropagation();
      }
    };

    widgetEl.addEventListener('keydown', this._keydownHandler);
    // Widget fokussierbar machen
    widgetEl.tabIndex = 0;
    widgetEl.focus();
  },

  /**
   * Keyboard-Events entfernen
   */
  _unbindKeyboard() {
    if (this._keydownHandler) {
      const entry = WidgetManager._widgets.get(this.WIDGET_ID);
      if (entry) {
        entry.el.removeEventListener('keydown', this._keydownHandler);
      }
      this._keydownHandler = null;
    }
  },

  // ---- INIT ----

  /**
   * Calculator initialisieren (aus app.js aufgerufen)
   */
  async init() {
    await this.load();

    // Standard-Modus ZUERST registrieren, bevor open() aufgerufen wird
    this._modes.set('standard', {
      label: '🔢',
      shortName: 'Std',
      titleKey: 'calculator.tab.standard',
      render: (bodyEl) => this._renderStandardMode(bodyEl),
      destroy: () => {
        this._displayExprEl = null;
        this._displayResultEl = null;
      }
    });

    // Wenn Calculator beim letzten Mal offen war, wiederherstellen
    const data = await Store.get(this.STORAGE_KEY);
    if (data && data.calculator && data.calculator.open) {
      await this.open();
    }

    // Widget-Lifecycle-Events
    const self = this;
    WidgetManager.on('widget:close', (e) => {
      if (e.detail.id === self.WIDGET_ID) {
        self.onClose();
      }
    });

    WidgetManager.on('widget:minimize', (e) => {
      if (e.detail.id === self.WIDGET_ID) {
        self._isOpen = false;
        self.save();
      }
    });

    WidgetManager.on('widget:open', (e) => {
      if (e.detail.id === self.WIDGET_ID) {
        self._isOpen = true;
        const body = WidgetManager.getBody(self.WIDGET_ID);
        if (body && body.children.length === 0) {
          self.renderBody(body);
        }
        const entry = WidgetManager._widgets.get(self.WIDGET_ID);
        if (entry) self._bindKeyboard(entry.el);
        self.save();
      }
    });
  }
};
