2430d65e3a
Replace monkey-patching of WidgetManager.close/minimize/openWidget with WidgetManager.on() event listeners. Eliminates 3-deep closure chain.
752 lines
19 KiB
JavaScript
752 lines
19 KiB
JavaScript
/* =============================================
|
|
HELLION NEWTAB — timer.js
|
|
Timer / Countdown Widget: Presets, Alarm,
|
|
Tab-Titel-Blink
|
|
============================================= */
|
|
|
|
const Timer = {
|
|
WIDGET_ID: 'widget_timer',
|
|
STORAGE_KEY: 'widgetStates',
|
|
MAX_PRESETS: 5,
|
|
|
|
/** @type {Array<{name: string, seconds: number}>} */
|
|
_presets: [],
|
|
_isOpen: false,
|
|
_seconds: 0,
|
|
_remaining: 0,
|
|
_intervalId: null,
|
|
_running: false,
|
|
_finished: false,
|
|
_blinkIntervalId: null,
|
|
_originalTitle: '',
|
|
_keydownHandler: null,
|
|
_muted: false,
|
|
|
|
// UI-Referenzen
|
|
_timeEl: null,
|
|
_muteBtn: null,
|
|
_inputEl: null,
|
|
_inputRow: null,
|
|
_btnStart: null,
|
|
_btnPause: null,
|
|
_btnReset: null,
|
|
|
|
// ---- STORAGE ----
|
|
|
|
/**
|
|
* Timer-State aus Storage laden
|
|
*/
|
|
async load() {
|
|
const data = await Store.get(this.STORAGE_KEY);
|
|
if (data && data.timer) {
|
|
this._presets = Array.isArray(data.timer.presets) ? data.timer.presets : [];
|
|
if (typeof data.timer.muted === 'boolean') this._muted = data.timer.muted;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Timer-State in Storage speichern
|
|
* Bestehende Notes + Calculator bleiben erhalten
|
|
*/
|
|
async save() {
|
|
const data = await Store.get(this.STORAGE_KEY) || {};
|
|
if (data.notes === undefined) data.notes = [];
|
|
|
|
const widgetState = WidgetManager.getState(this.WIDGET_ID);
|
|
data.timer = {
|
|
x: widgetState ? widgetState.x : 600,
|
|
y: widgetState ? widgetState.y : 80,
|
|
width: widgetState ? widgetState.width : 260,
|
|
height: widgetState ? widgetState.height : 360,
|
|
open: this._isOpen,
|
|
presets: this._presets.slice(0, this.MAX_PRESETS),
|
|
muted: this._muted
|
|
};
|
|
|
|
await Store.set(this.STORAGE_KEY, data);
|
|
},
|
|
|
|
// ---- WIDGET LIFECYCLE ----
|
|
|
|
/**
|
|
* Timer-Widget oeffnen oder in Vordergrund bringen
|
|
*/
|
|
async open() {
|
|
if (this._isOpen) {
|
|
WidgetManager.bringToFront(this.WIDGET_ID);
|
|
return;
|
|
}
|
|
|
|
const data = await Store.get(this.STORAGE_KEY);
|
|
const saved = (data && data.timer) ? data.timer : {};
|
|
|
|
WidgetManager.create('timer', {
|
|
id: this.WIDGET_ID,
|
|
title: t('timer.title'),
|
|
x: saved.x || 600,
|
|
y: saved.y || 80,
|
|
width: saved.width || 260,
|
|
height: saved.height || 360,
|
|
open: true
|
|
});
|
|
|
|
const body = WidgetManager.getBody(this.WIDGET_ID);
|
|
if (body) this.renderBody(body);
|
|
|
|
this._isOpen = true;
|
|
|
|
const entry = WidgetManager._widgets.get(this.WIDGET_ID);
|
|
if (entry) this._bindKeyboard(entry.el);
|
|
|
|
await this.save();
|
|
},
|
|
|
|
/**
|
|
* Timer 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() {
|
|
this._isOpen = false;
|
|
this._unbindKeyboard();
|
|
this._stopCountdown();
|
|
this._stopAlarm();
|
|
this._timeEl = null;
|
|
this._inputEl = null;
|
|
this._inputRow = null;
|
|
this._btnStart = null;
|
|
this._btnPause = null;
|
|
this._btnReset = null;
|
|
this._muteBtn = null;
|
|
await this.save();
|
|
},
|
|
|
|
// ---- UI RENDERING ----
|
|
|
|
/**
|
|
* Timer-Body rendern
|
|
* @param {HTMLElement} bodyEl
|
|
*/
|
|
renderBody(bodyEl) {
|
|
bodyEl.textContent = '';
|
|
bodyEl.style.padding = '8px';
|
|
bodyEl.style.display = 'flex';
|
|
bodyEl.style.flexDirection = 'column';
|
|
|
|
// Display
|
|
const display = document.createElement('div');
|
|
display.className = 'timer-display';
|
|
|
|
const timeEl = document.createElement('div');
|
|
timeEl.className = 'timer-time';
|
|
timeEl.textContent = '00:00';
|
|
this._timeEl = timeEl;
|
|
display.appendChild(timeEl);
|
|
|
|
// Input
|
|
const inputRow = document.createElement('div');
|
|
inputRow.className = 'timer-input-row';
|
|
this._inputRow = inputRow;
|
|
|
|
const input = document.createElement('input');
|
|
input.className = 'timer-input';
|
|
input.type = 'text';
|
|
input.placeholder = 'mm:ss';
|
|
input.maxLength = 8;
|
|
this._inputEl = input;
|
|
|
|
input.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Enter') {
|
|
e.preventDefault();
|
|
this._applyInput();
|
|
this._start();
|
|
}
|
|
});
|
|
|
|
inputRow.appendChild(input);
|
|
|
|
// Controls
|
|
const controls = document.createElement('div');
|
|
controls.className = 'timer-controls';
|
|
|
|
const btnStart = document.createElement('button');
|
|
btnStart.className = 'timer-ctrl-btn primary';
|
|
btnStart.type = 'button';
|
|
btnStart.textContent = t('timer.start');
|
|
btnStart.addEventListener('click', () => {
|
|
if (!this._running && this._remaining === 0) {
|
|
this._applyInput();
|
|
}
|
|
this._start();
|
|
});
|
|
this._btnStart = btnStart;
|
|
|
|
const btnPause = document.createElement('button');
|
|
btnPause.className = 'timer-ctrl-btn';
|
|
btnPause.type = 'button';
|
|
btnPause.textContent = t('timer.pause');
|
|
btnPause.disabled = true;
|
|
btnPause.addEventListener('click', () => this._pause());
|
|
this._btnPause = btnPause;
|
|
|
|
const btnReset = document.createElement('button');
|
|
btnReset.className = 'timer-ctrl-btn danger';
|
|
btnReset.type = 'button';
|
|
btnReset.textContent = t('timer.reset');
|
|
btnReset.addEventListener('click', () => this._reset());
|
|
this._btnReset = btnReset;
|
|
|
|
controls.append(btnStart, btnPause, btnReset);
|
|
|
|
// Mute Toggle (in Controls-Zeile)
|
|
const muteBtn = document.createElement('button');
|
|
muteBtn.className = 'timer-mute-btn';
|
|
muteBtn.type = 'button';
|
|
this._muteBtn = muteBtn;
|
|
this._updateMuteBtn();
|
|
muteBtn.addEventListener('click', async () => {
|
|
this._muted = !this._muted;
|
|
this._updateMuteBtn();
|
|
await this.save();
|
|
});
|
|
controls.appendChild(muteBtn);
|
|
|
|
// Presets
|
|
const presetsEl = this._createPresetsPanel();
|
|
|
|
bodyEl.append(display, inputRow, controls, presetsEl);
|
|
|
|
// State wiederherstellen
|
|
this._updateDisplay();
|
|
this._updateControls();
|
|
},
|
|
|
|
/**
|
|
* Presets-Panel erstellen
|
|
* @returns {HTMLElement}
|
|
*/
|
|
_createPresetsPanel() {
|
|
const container = document.createElement('div');
|
|
container.className = 'timer-presets';
|
|
container.id = 'timerPresetsPanel';
|
|
|
|
const header = document.createElement('div');
|
|
header.className = 'timer-presets-header';
|
|
|
|
const title = document.createElement('span');
|
|
title.className = 'timer-presets-title';
|
|
title.textContent = t('timer.presets');
|
|
|
|
const addBtn = document.createElement('button');
|
|
addBtn.className = 'timer-preset-add';
|
|
addBtn.type = 'button';
|
|
addBtn.textContent = '+';
|
|
addBtn.title = t('timer.save_preset');
|
|
addBtn.addEventListener('click', () => this._showAddPreset(container));
|
|
|
|
header.append(title, addBtn);
|
|
container.appendChild(header);
|
|
|
|
this._renderPresetItems(container);
|
|
|
|
return container;
|
|
},
|
|
|
|
/**
|
|
* Preset-Items rendern
|
|
* @param {HTMLElement} container
|
|
*/
|
|
_renderPresetItems(container) {
|
|
// Alte Items entfernen
|
|
const oldItems = container.querySelectorAll('.timer-preset-item, .timer-add-row');
|
|
oldItems.forEach(item => item.remove());
|
|
|
|
this._presets.forEach((preset, idx) => {
|
|
const item = document.createElement('div');
|
|
item.className = 'timer-preset-item';
|
|
|
|
const name = document.createElement('span');
|
|
name.className = 'timer-preset-name';
|
|
name.textContent = preset.name;
|
|
|
|
const time = document.createElement('span');
|
|
time.className = 'timer-preset-time';
|
|
time.textContent = this._formatTime(preset.seconds);
|
|
|
|
const del = document.createElement('button');
|
|
del.className = 'timer-preset-del';
|
|
del.type = 'button';
|
|
del.textContent = '\u2715';
|
|
del.addEventListener('click', async (e) => {
|
|
e.stopPropagation();
|
|
await this._deletePreset(idx);
|
|
this._renderPresetItems(container);
|
|
});
|
|
|
|
item.append(name, time, del);
|
|
|
|
// Klick laedt Preset
|
|
item.addEventListener('click', () => {
|
|
this._loadPreset(preset);
|
|
});
|
|
|
|
container.appendChild(item);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Add-Preset UI anzeigen
|
|
* @param {HTMLElement} container
|
|
*/
|
|
_showAddPreset(container) {
|
|
// Nur einmal anzeigen
|
|
if (container.querySelector('.timer-add-row')) return;
|
|
|
|
if (this._presets.length >= this.MAX_PRESETS) {
|
|
HellionDialog.alert(
|
|
t('timer.limit_message', { max: this.MAX_PRESETS }),
|
|
{ type: 'warning', title: t('timer.limit_title') }
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Aktuelle Zeit als Vorlage
|
|
const currentSeconds = this._remaining > 0 ? this._seconds : 0;
|
|
if (currentSeconds === 0 && this._inputEl) {
|
|
const parsed = this._parseTimeInput(this._inputEl.value);
|
|
if (parsed === 0) {
|
|
HellionDialog.alert(
|
|
t('timer.no_time_message'),
|
|
{ type: 'info', title: t('timer.no_time_title') }
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
const row = document.createElement('div');
|
|
row.className = 'timer-add-row';
|
|
|
|
const nameInput = document.createElement('input');
|
|
nameInput.className = 'timer-add-input';
|
|
nameInput.type = 'text';
|
|
nameInput.placeholder = t('timer.preset_name_placeholder');
|
|
nameInput.maxLength = 20;
|
|
|
|
const confirmBtn = document.createElement('button');
|
|
confirmBtn.className = 'timer-add-confirm';
|
|
confirmBtn.type = 'button';
|
|
confirmBtn.textContent = t('timer.ok');
|
|
|
|
const doAdd = async () => {
|
|
const name = nameInput.value.trim();
|
|
if (!name) return;
|
|
|
|
let secs = this._seconds;
|
|
if (secs === 0 && this._inputEl) {
|
|
secs = this._parseTimeInput(this._inputEl.value);
|
|
}
|
|
if (secs === 0) return;
|
|
|
|
await this._addPreset(name, secs);
|
|
this._renderPresetItems(container);
|
|
};
|
|
|
|
confirmBtn.addEventListener('click', doAdd);
|
|
nameInput.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Enter') doAdd();
|
|
if (e.key === 'Escape') row.remove();
|
|
});
|
|
|
|
row.append(nameInput, confirmBtn);
|
|
container.appendChild(row);
|
|
nameInput.focus();
|
|
},
|
|
|
|
// ---- TIMER LOGIC ----
|
|
|
|
/**
|
|
* Input-Feld auslesen und als Sekunden setzen
|
|
*/
|
|
_applyInput() {
|
|
if (!this._inputEl) return;
|
|
const secs = this._parseTimeInput(this._inputEl.value);
|
|
if (secs > 0) {
|
|
this._seconds = secs;
|
|
this._remaining = secs;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Timer starten
|
|
*/
|
|
_start() {
|
|
if (this._running) return;
|
|
if (this._remaining <= 0) return;
|
|
|
|
// Falls gerade Alarm laeuft, stoppen
|
|
if (this._finished) {
|
|
this._stopAlarm();
|
|
this._finished = false;
|
|
}
|
|
|
|
this._running = true;
|
|
this._updateControls();
|
|
|
|
// Input verstecken
|
|
if (this._inputRow) this._inputRow.style.display = 'none';
|
|
|
|
this._intervalId = setInterval(() => this._tick(), 1000);
|
|
},
|
|
|
|
/**
|
|
* Timer pausieren
|
|
*/
|
|
_pause() {
|
|
if (!this._running) return;
|
|
this._running = false;
|
|
this._stopCountdown();
|
|
this._updateControls();
|
|
},
|
|
|
|
/**
|
|
* Timer zuruecksetzen
|
|
*/
|
|
_reset() {
|
|
this._stopCountdown();
|
|
this._stopAlarm();
|
|
this._running = false;
|
|
this._finished = false;
|
|
this._remaining = 0;
|
|
this._seconds = 0;
|
|
|
|
// Input wieder anzeigen
|
|
if (this._inputRow) this._inputRow.style.display = 'flex';
|
|
if (this._inputEl) this._inputEl.value = '';
|
|
|
|
this._updateDisplay();
|
|
this._updateControls();
|
|
},
|
|
|
|
/**
|
|
* Jede Sekunde: remaining verringern, Display aktualisieren
|
|
*/
|
|
_tick() {
|
|
this._remaining--;
|
|
|
|
if (this._remaining <= 0) {
|
|
this._remaining = 0;
|
|
this._stopCountdown();
|
|
this._running = false;
|
|
this._finished = true;
|
|
this._onFinish();
|
|
}
|
|
|
|
this._updateDisplay();
|
|
this._updateControls();
|
|
},
|
|
|
|
/**
|
|
* Interval stoppen
|
|
*/
|
|
_stopCountdown() {
|
|
if (this._intervalId) {
|
|
clearInterval(this._intervalId);
|
|
this._intervalId = null;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Timer abgelaufen — Alarm + Tab-Blink
|
|
*/
|
|
_onFinish() {
|
|
if (!this._muted) this._playAlarm();
|
|
this._startTitleBlink();
|
|
},
|
|
|
|
/**
|
|
* Akustisches Signal (Browser Audio API, kein externer Request)
|
|
*/
|
|
_playAlarm() {
|
|
try {
|
|
const ctx = new AudioContext();
|
|
[0, 0.3, 0.6].forEach(delay => {
|
|
const osc = ctx.createOscillator();
|
|
const gain = ctx.createGain();
|
|
osc.connect(gain);
|
|
gain.connect(ctx.destination);
|
|
osc.frequency.value = 800;
|
|
gain.gain.value = 0.07;
|
|
osc.start(ctx.currentTime + delay);
|
|
osc.stop(ctx.currentTime + delay + 0.2);
|
|
});
|
|
} catch (e) {
|
|
console.warn('Timer: Audio nicht verfuegbar', e);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Tab-Titel blinken lassen
|
|
*/
|
|
_startTitleBlink() {
|
|
this._originalTitle = document.title;
|
|
this._blinkIntervalId = setInterval(() => {
|
|
document.title = document.title === t('timer.finished_title')
|
|
? this._originalTitle
|
|
: t('timer.finished_title');
|
|
}, 1000);
|
|
},
|
|
|
|
/**
|
|
* Tab-Titel Blink und Alarm stoppen
|
|
*/
|
|
_stopAlarm() {
|
|
if (this._blinkIntervalId) {
|
|
clearInterval(this._blinkIntervalId);
|
|
this._blinkIntervalId = null;
|
|
document.title = this._originalTitle || t('timer.default_page_title');
|
|
}
|
|
this._finished = false;
|
|
this._updateDisplay();
|
|
this._updateControls();
|
|
},
|
|
|
|
/**
|
|
* Mute-Button Text/Titel aktualisieren
|
|
*/
|
|
_updateMuteBtn() {
|
|
if (!this._muteBtn) return;
|
|
this._muteBtn.textContent = this._muted ? '\uD83D\uDD07' : '\uD83D\uDD0A';
|
|
this._muteBtn.title = this._muted ? t('timer.unmute') : t('timer.mute');
|
|
this._muteBtn.classList.toggle('muted', this._muted);
|
|
},
|
|
|
|
// ---- DISPLAY ----
|
|
|
|
/**
|
|
* Zeitanzeige aktualisieren
|
|
*/
|
|
_updateDisplay() {
|
|
if (!this._timeEl) return;
|
|
this._timeEl.textContent = this._formatTime(this._remaining);
|
|
this._timeEl.classList.toggle('finished', this._finished);
|
|
},
|
|
|
|
/**
|
|
* Button-States aktualisieren
|
|
*/
|
|
_updateControls() {
|
|
if (this._btnStart) {
|
|
this._btnStart.disabled = this._running;
|
|
this._btnStart.textContent = this._finished ? t('timer.restart') : t('timer.start');
|
|
}
|
|
if (this._btnPause) {
|
|
this._btnPause.disabled = !this._running;
|
|
}
|
|
},
|
|
|
|
// ---- PRESETS ----
|
|
|
|
/**
|
|
* Preset hinzufuegen
|
|
* @param {string} name
|
|
* @param {number} seconds
|
|
*/
|
|
async _addPreset(name, seconds) {
|
|
if (this._presets.length >= this.MAX_PRESETS) return;
|
|
this._presets.push({ name, seconds });
|
|
await this.save();
|
|
},
|
|
|
|
/**
|
|
* Preset loeschen
|
|
* @param {number} index
|
|
*/
|
|
async _deletePreset(index) {
|
|
this._presets.splice(index, 1);
|
|
await this.save();
|
|
},
|
|
|
|
/**
|
|
* Preset laden (Zeit setzen)
|
|
* @param {Object} preset - { name, seconds }
|
|
*/
|
|
_loadPreset(preset) {
|
|
// Falls laufend, erst stoppen
|
|
this._stopCountdown();
|
|
this._stopAlarm();
|
|
this._running = false;
|
|
this._finished = false;
|
|
|
|
this._seconds = preset.seconds;
|
|
this._remaining = preset.seconds;
|
|
|
|
if (this._inputRow) this._inputRow.style.display = 'none';
|
|
|
|
this._updateDisplay();
|
|
this._updateControls();
|
|
},
|
|
|
|
// ---- FORMATTING ----
|
|
|
|
/**
|
|
* Sekunden in Zeitformat umwandeln
|
|
* @param {number} totalSeconds
|
|
* @returns {string} "05:30" oder "1:05:30"
|
|
*/
|
|
_formatTime(totalSeconds) {
|
|
const h = Math.floor(totalSeconds / 3600);
|
|
const m = Math.floor((totalSeconds % 3600) / 60);
|
|
const s = totalSeconds % 60;
|
|
|
|
const mm = String(m).padStart(2, '0');
|
|
const ss = String(s).padStart(2, '0');
|
|
|
|
if (h > 0) {
|
|
return h + ':' + mm + ':' + ss;
|
|
}
|
|
return mm + ':' + ss;
|
|
},
|
|
|
|
/**
|
|
* Zeit-String in Sekunden parsen
|
|
* Akzeptiert: "5:30", "05:30", "1:05:30", "90" (Sekunden)
|
|
* @param {string} str
|
|
* @returns {number}
|
|
*/
|
|
_parseTimeInput(str) {
|
|
const trimmed = (str || '').trim();
|
|
if (!trimmed) return 0;
|
|
|
|
const parts = trimmed.split(':');
|
|
|
|
if (parts.length === 1) {
|
|
// Nur Zahl = Sekunden
|
|
const secs = parseInt(parts[0], 10);
|
|
return isNaN(secs) ? 0 : Math.max(0, secs);
|
|
}
|
|
|
|
if (parts.length === 2) {
|
|
// mm:ss
|
|
const m = parseInt(parts[0], 10);
|
|
const s = parseInt(parts[1], 10);
|
|
if (isNaN(m) || isNaN(s)) return 0;
|
|
return Math.max(0, m * 60 + s);
|
|
}
|
|
|
|
if (parts.length === 3) {
|
|
// hh:mm:ss
|
|
const h = parseInt(parts[0], 10);
|
|
const m = parseInt(parts[1], 10);
|
|
const s = parseInt(parts[2], 10);
|
|
if (isNaN(h) || isNaN(m) || isNaN(s)) return 0;
|
|
return Math.max(0, h * 3600 + m * 60 + s);
|
|
}
|
|
|
|
return 0;
|
|
},
|
|
|
|
// ---- KEYBOARD ----
|
|
|
|
/**
|
|
* Tastatur-Events binden
|
|
* @param {HTMLElement} widgetEl
|
|
*/
|
|
_bindKeyboard(widgetEl) {
|
|
this._unbindKeyboard();
|
|
|
|
this._keydownHandler = (e) => {
|
|
// Nicht reagieren wenn User in Input tippt
|
|
if (e.target.tagName === 'INPUT') return;
|
|
|
|
if (e.key === ' ' || e.key === 'Enter') {
|
|
e.preventDefault();
|
|
if (this._running) {
|
|
this._pause();
|
|
} else if (this._remaining > 0) {
|
|
this._start();
|
|
}
|
|
} else if (e.key === 'Escape' || e.key === 'r' || e.key === 'R') {
|
|
e.preventDefault();
|
|
this._reset();
|
|
}
|
|
};
|
|
|
|
widgetEl.addEventListener('keydown', this._keydownHandler);
|
|
widgetEl.tabIndex = 0;
|
|
},
|
|
|
|
/**
|
|
* 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 ----
|
|
|
|
/**
|
|
* Timer initialisieren (aus app.js aufgerufen)
|
|
*/
|
|
async init() {
|
|
await this.load();
|
|
|
|
// Wenn Timer beim letzten Mal offen war, wiederherstellen
|
|
const data = await Store.get(this.STORAGE_KEY);
|
|
if (data && data.timer && data.timer.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();
|
|
}
|
|
});
|
|
}
|
|
};
|