uce/site/info/js/u-eventemitter-demo.html

898 lines
34 KiB
HTML
Executable File

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>U-EventEmitter Demo</title>
<style>
:root {
--space: 8px;
--radius: 5px;
--gray: #6c757d;
--gray-bg: #f5f5f5;
--blue: #007acc;
--green: #28a745;
--white: white;
--dark: #333;
--max-width: 1400px;
--sidebar: 400px;
}
* { box-sizing: border-box; }
body {
font-family:'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: var(--max-width);
margin: 0 auto;
padding: var(--space);
background: var(--gray-bg);
}
.container {
background: var(--white);
padding: calc(var(--space) * 1.5);
border-radius: var(--radius);
}
.main-layout {
display: grid;
grid-template-columns: 1fr 1fr;
gap: calc(var(--space) * 1.5);
}
.api-column {
background: var(--gray-bg);
padding: var(--space);
border-radius: var(--radius);
border-left: 4px solid var(--green);
}
h1 {
color: var(--dark);
text-align: center;
margin-bottom: calc(var(--space) * 1.5);
grid-column: 1 / -1;
}
h2 {
color: var(--gray);
border-bottom: 2px solid #e0e0e0;
padding-bottom: var(--space);
margin-top: calc(var(--space) * 1.5);
}
.demo-section {
margin: var(--space) 0;
padding: var(--space);
background: var(--gray-bg);
border-radius: var(--radius);
border-left: 4px solid var(--blue);
}
.controls, .input-group {
display: flex;
gap: var(--space);
margin: 15px 0;
}
.controls { flex-wrap: wrap; }
.input-group { align-items: center; }
.feature-grid { display: grid; gap: var(--space); margin: var(--space) 0; }
button {
background: var(--blue);
color: var(--white);
border: none;
padding: var(--space) var(--space);
border-radius: var(--radius);
cursor: pointer;
transition: all 0.3s ease;
}
button:hover { filter: brightness(0.9); }
button:disabled { background: #ccc; cursor: not-allowed; }
input[type="text"] {
padding: var(--space);
border: 1px solid #ddd;
border-radius: var(--radius);
flex: 1;
}
.status, .code-example {
font-family: monospace;
padding: var(--space);
border-radius: var(--radius);
margin: var(--space) 0;
}
.status {
background: var(--dark);
color: #0f0;
white-space: pre-wrap;
max-height: 150px;
overflow-y: auto;
}
.code-example {
background: #2d3748;
color: #e2e8f0;
padding: 15px;
overflow-x: auto;
margin: var(--space) 0;
}
.highlight { color: #68d391; }
.keyword { color: #fbb6ce; }
.string { color: #fbd38d; }
.event-visual {
height: 100px;
background: linear-gradient(45deg, #1e3c72, #2a5298);
border-radius: var(--radius);
display: flex;
align-items: center;
justify-content: center;
color: var(--white);
font-weight: bold;
margin: 15px 0;
position: relative;
overflow: hidden;
}
.pulse-wave {
position: absolute;
inset: 0;
opacity: 0.3;
background: radial-gradient(circle at center, rgba(255,255,255,0.2) 0%, transparent 70%);
animation: pulse 2s ease-in-out infinite;
}
.event-indicator {
position: absolute;
top: 10px;
right: 10px;
width: 12px;
height: 12px;
background: #68d391;
border-radius: 50%;
opacity: 0;
transition: opacity 0.3s ease;
}
.event-indicator.active {
opacity: 1;
animation: blink 0.5s ease-in-out;
}
@keyframes pulse {
0%, 100% { transform: scale(0.8); opacity: 0.3; }
50% { transform: scale(1.2); opacity: 0.1; }
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.3; }
}
.api-section { margin: 15px 0; }
.api-section h3 {
color: var(--green);
margin: 0 0 var(--space) 0;
}
.api-method {
font-family: monospace;
margin: 3px 0;
color: var(--gray);
}
.api-method .method-name { color: var(--blue); font-weight: bold; }
.api-method .return-type { color: #6f42c1; }
.api-method .param { color: #e83e8c; }
.api-description {
margin: var(--space) 0;
}
.api-options {
color: var(--gray);
}
.help-text {
color: var(--gray);
margin: var(--space) 0;
}
.code-comment {
color: #68d391;
}
.event-log {
background: var(--dark);
color: #0f0;
font-family: monospace;
padding: var(--space);
border-radius: var(--radius);
height: 150px;
overflow-y: auto;
white-space: pre-wrap;
margin: var(--space) 0;
}
.listener-counter {
background: var(--blue);
color: var(--white);
padding: 2px 8px;
border-radius: 10px;
font-size: 12px;
margin-left: 8px;
}
@media (max-width: 1200px) {
.main-layout { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<div class="container">
<h1>U-EventEmitter Demo</h1>
<div class="main-layout">
<div class="demo-column">
<h2>Basic Event System</h2>
<div class="demo-section">
<div class="controls">
<button onclick="basicDemo()">Basic Subscribe & Emit</button>
<button onclick="multipleListenersDemo()">Add Multiple Listeners</button>
<button onclick="emitToMultiple()">Emit to All</button>
</div>
<div class="status" id="basic-output"></div>
</div>
<div class="demo-section">
<h3>Advanced Features</h3>
<div class="controls">
<button onclick="slotDemo()">Slot-based Handlers</button>
<button onclick="replaceSlotHandler()">Replace Handler</button>
<button onclick="emitSlotEvent()">Test Slot System</button>
</div>
<div class="controls">
<button onclick="autoRemovalDemo()">Self-removing Handler</button>
<button onclick="triggerAutoRemoval()">Trigger Auto-removal</button>
</div>
<div class="status" id="advanced-output"></div>
</div>
<div class="demo-section">
<h3>Interactive Event System</h3>
<div class="input-group">
<input type="text" id="event-name" placeholder="Event name" value="chat">
<input type="text" id="event-data" placeholder="Event data" value="Hello EventEmitter!">
</div>
<div class="controls">
<button onclick="addListener()">Add Listener <span id="listenerCount" class="listener-counter">0</span></button>
<button onclick="emitCustomEvent()">Emit Event</button>
<button onclick="removeAllListeners()">Clear Listeners</button>
</div>
<div class="event-log" id="event-log">Ready for interactive events...</div>
</div>
</div>
<div class="api-column">
<h2>API Overview</h2>
<div class="api-section">
<h3>EventEmitter Constructor</h3>
<div class="api-method"><span class="keyword">new</span> <span class="method-name">EventEmitter</span>()</div>
<div class="api-description">
Creates a new event emitter instance for custom event communication.
</div>
</div>
<div class="api-section">
<h3>Core Methods</h3>
<div class="api-method"><span class="method-name">on</span>(<span class="param">event, handler, slot?</span>) → <span class="return-type">EventEmitter</span></div>
<div class="api-method"><span class="method-name">emit</span>(<span class="param">event, ...args</span>) → <span class="return-type">number</span></div>
<div class="api-method"><span class="method-name">off</span>(<span class="param">event, handler?</span>) → <span class="return-type">EventEmitter</span></div>
<div class="api-method"><span class="method-name">clear</span>(<span class="param">event?</span>) → <span class="return-type">EventEmitter</span></div>
</div>
<div class="api-section">
<h3>Key Features</h3>
<div class="api-description">
<strong>Slot-based Deduplication:</strong> Use slot parameter to replace existing handlers<br>
<strong>Auto-removal:</strong> Handlers returning 'remove_handler' are automatically unsubscribed<br>
<strong>Return Values:</strong> emit() returns the number of handlers called<br>
<strong>Flexible Arguments:</strong> Pass any number of arguments to handlers
</div>
</div>
<div class="api-section">
<h3>Examples</h3>
<pre class="code-example">
<span class="code-comment">// Basic usage</span>
<span class="keyword">const</span> emitter = <span class="keyword">new</span> <span class="highlight">EventEmitter</span>();
emitter.on(<span class="string">'message'</span>, (data) => {
console.log(<span class="string">'Received:'</span>, data);
});
emitter.emit(<span class="string">'message'</span>, <span class="string">'Hello World'</span>);
<span class="code-comment">// Slot-based replacement</span>
emitter.on(<span class="string">'update'</span>, handler1, <span class="string">'ui-updater'</span>);
emitter.on(<span class="string">'update'</span>, handler2, <span class="string">'ui-updater'</span>); <span class="code-comment">// Replaces handler1</span>
<span class="code-comment">// Self-removing handler</span>
emitter.on(<span class="string">'init'</span>, () => {
console.log(<span class="string">'Initialized!'</span>);
<span class="keyword">return</span> <span class="string">'remove_handler'</span>; <span class="code-comment">// Removes itself</span>
});
</pre>
</div>
</div>
</div>
<div class="main-layout">
<div class="demo-column">
<h2>Event Patterns</h2>
<div class="demo-section">
<h3>Publisher-Subscriber Pattern</h3>
<div class="controls">
<button onclick="createPublisher()">Create Publisher</button>
<button onclick="addSubscribers()">Add Subscribers</button>
<button onclick="publishNews()">Publish News</button>
</div>
<div class="status" id="pubsub-output"></div>
</div>
<div class="demo-section">
<h3>Component Communication</h3>
<div class="controls">
<button onclick="setupComponents()">Setup Components</button>
<button onclick="componentInteraction()">Trigger Interaction</button>
<button onclick="cascadeEvents()">Cascade Events</button>
</div>
<div class="status" id="component-output"></div>
</div>
<div class="demo-section">
<h3>Handler Counting & Management</h3>
<div class="controls">
<button onclick="handlerCountDemo()">Count Handlers</button>
<button onclick="benchmarkEmission()">Benchmark Emission</button>
</div>
<div class="status" id="count-output"></div>
</div>
</div>
<div class="api-column">
<h2>Advanced Usage</h2>
<div class="api-section">
<h3>Event Management</h3>
<div class="api-description">
<strong>on(event, handler, slot)</strong> - Subscribe to events<br>
• event: String event name<br>
• handler: Function to call when event is emitted<br>
• slot: Optional string key for handler replacement<br><br>
<strong>emit(event, ...args)</strong> - Emit events to all listeners<br>
• Returns the number of handlers that were called<br>
• Passes all additional arguments to handlers<br><br>
<strong>off(event, handler)</strong> - Remove specific handler<br>
• If handler omitted, removes all handlers for event
</div>
</div>
<div class="api-section">
<h3>Slot System</h3>
<div class="api-description">
The slot system prevents duplicate handlers by using string keys:<br>
• Same slot key replaces previous handler<br>
• Useful for UI updates, state management<br>
• Prevents memory leaks from repeated subscriptions<br>
• Slot keys are per-event, not global
</div>
<pre class="code-example">
<span class="code-comment">// Without slots: multiple handlers</span>
emitter.on(<span class="string">'render'</span>, updateUI);
emitter.on(<span class="string">'render'</span>, updateUI); <span class="code-comment">// Now 2 handlers</span>
<span class="code-comment">// With slots: automatic replacement</span>
emitter.on(<span class="string">'render'</span>, updateUI, <span class="string">'ui'</span>);
emitter.on(<span class="string">'render'</span>, updateUI, <span class="string">'ui'</span>); <span class="code-comment">// Still 1 handler</span>
</pre>
</div>
<div class="api-section">
<h3>Performance Patterns</h3>
<div class="api-description">
<strong>Event Namespacing:</strong> Use dot notation for hierarchical events<br>
<strong>Batch Operations:</strong> Group related events for efficiency<br>
<strong>Handler Cleanup:</strong> Use slots or off() to prevent memory leaks<br>
<strong>Conditional Emission:</strong> Check handler count before expensive operations
</div>
</div>
</div>
</div>
<div class="main-layout">
<div class="demo-column">
</div>
<div class="api-column">
<div class="api-section">
<h3>Memory Management</h3>
<div class="api-description">
<strong>Use Slots:</strong> Prevent duplicate handlers with slot keys<br>
<strong>Clean Up:</strong> Call off() when components are destroyed<br>
<strong>Self-removal:</strong> Use 'remove_handler' return for one-time events<br>
<strong>Clear All:</strong> Use clear() to remove all listeners for an event
</div>
</div>
<div class="api-section">
<h3>Common Patterns</h3>
<pre class="code-example">
<span class="code-comment">// Request-Response pattern</span>
emitter.on(<span class="string">'data.request'</span>, (type, callback) => {
<span class="keyword">const</span> data = fetchData(type);
callback(data);
});
<span class="code-comment">// State change notifications</span>
emitter.on(<span class="string">'state.change'</span>, (oldState, newState) => {
updateUI(newState);
logStateChange(oldState, newState);
});
<span class="code-comment">// Error handling</span>
emitter.on(<span class="string">'error'</span>, (error, context) => {
console.error(<span class="string">'Error in'</span>, context, error);
showErrorToUser(error.message);
});
</pre>
</div>
</div>
</div>
<div class="main-layout">
<div class="demo-column">
<h2>Library Info</h2>
<div class="demo-section">
<div class="status" id="libraryInfo">Loading library information...</div>
<h3>Event Statistics:</h3>
<div id="eventStats" class="status"></div>
</div>
</div>
<div class="api-column">
</div>
</div>
</div>
<script src="u-events.js"></script>
<script>
const demoEmitter = new EventEmitter();
const interactiveEmitter = new EventEmitter();
const publisherEmitter = new EventEmitter();
const gameEmitter = new EventEmitter();
const uiEmitter = new EventEmitter();
let listenerCount = 0;
let gameStats = {
eventsEmitted: 0,
handlersExecuted: 0,
totalEvents: 0
};
function log(message, outputId = 'basic-output') {
const output = document.getElementById(outputId);
const timestamp = new Date().toLocaleTimeString();
output.textContent += `[${timestamp}] ${message}\n`;
output.scrollTop = output.scrollHeight;
flashIndicator('basicIndicator');
}
function clearOutput(outputId) {
document.getElementById(outputId).textContent = '';
}
function flashIndicator(indicatorId) {
const indicator = document.getElementById(indicatorId);
if (indicator) {
indicator.classList.remove('active');
setTimeout(() => indicator.classList.add('active'), 10);
setTimeout(() => indicator.classList.remove('active'), 500);
}
}
function basicDemo() {
clearOutput('basic-output');
demoEmitter.on('greeting', (name) => {
log(`Hello, ${name}!`, 'basic-output');
});
demoEmitter.emit('greeting', 'World');
demoEmitter.emit('greeting', 'EventEmitter');
gameStats.eventsEmitted += 2;
updateEventStats();
}
function multipleListenersDemo() {
demoEmitter.on('multi-test', (data) => {
log(`Handler 1: ${data}`, 'basic-output');
});
demoEmitter.on('multi-test', (data) => {
log(`Handler 2: ${data}`, 'basic-output');
});
demoEmitter.on('multi-test', (data) => {
log(`Handler 3: ${data}`, 'basic-output');
});
log('Added 3 listeners for "multi-test" event', 'basic-output');
}
function emitToMultiple() {
const count = demoEmitter.emit('multi-test', 'Hello from multiple demo!');
log(`Event emitted to ${count} handlers`, 'basic-output');
gameStats.eventsEmitted++;
gameStats.handlersExecuted += count;
updateEventStats();
}
function slotDemo() {
clearOutput('advanced-output');
demoEmitter.on('slot-event', (msg) => {
log(`Slot handler v1: ${msg}`, 'advanced-output');
}, 'demo-slot');
log('Added handler with slot key "demo-slot"', 'advanced-output');
}
function replaceSlotHandler() {
demoEmitter.on('slot-event', (msg) => {
log(`Slot handler v2 (replaced): ${msg}`, 'advanced-output');
}, 'demo-slot');
log('Replaced handler using same slot key', 'advanced-output');
}
function emitSlotEvent() {
const count = demoEmitter.emit('slot-event', 'Testing slot replacement');
log(`Emitted to ${count} handler(s)`, 'advanced-output');
gameStats.eventsEmitted++;
gameStats.handlersExecuted += count;
updateEventStats();
}
let autoRemovalCount = 0;
function autoRemovalDemo() {
demoEmitter.on('auto-remove', (msg) => {
autoRemovalCount++;
log(`Auto-removal handler called ${autoRemovalCount} time(s): ${msg}`, 'advanced-output');
return 'remove_handler';
});
log('Added self-removing handler', 'advanced-output');
}
function triggerAutoRemoval() {
const count = demoEmitter.emit('auto-remove', 'This handler will remove itself');
log(`Handlers called: ${count}`, 'advanced-output');
setTimeout(() => {
const count2 = demoEmitter.emit('auto-remove', 'This should call 0 handlers');
log(`Second emit - handlers called: ${count2}`, 'advanced-output');
}, 1000);
gameStats.eventsEmitted += 2;
gameStats.handlersExecuted += count;
updateEventStats();
}
function addListener() {
const eventName = document.getElementById('event-name').value;
if (!eventName) return;
listenerCount++;
const listenerId = listenerCount;
interactiveEmitter.on(eventName, (data) => {
const logEl = document.getElementById('event-log');
logEl.textContent += `[Listener ${listenerId}] ${eventName}: ${data}\n`;
logEl.scrollTop = logEl.scrollHeight;
});
const logEl = document.getElementById('event-log');
logEl.textContent += `Added listener ${listenerId} for "${eventName}"\n`;
logEl.scrollTop = logEl.scrollHeight;
updateListenerCounter();
}
function emitCustomEvent() {
const eventName = document.getElementById('event-name').value;
const eventData = document.getElementById('event-data').value;
if (!eventName) return;
const count = interactiveEmitter.emit(eventName, eventData);
const logEl = document.getElementById('event-log');
logEl.textContent += `Emitted "${eventName}" to ${count} listener(s)\n`;
logEl.scrollTop = logEl.scrollHeight;
gameStats.eventsEmitted++;
gameStats.handlersExecuted += count;
updateEventStats();
}
function removeAllListeners() {
const eventName = document.getElementById('event-name').value;
if (!eventName) return;
interactiveEmitter.off(eventName);
const logEl = document.getElementById('event-log');
logEl.textContent += `Removed all listeners for "${eventName}"\n`;
logEl.scrollTop = logEl.scrollHeight;
}
function updateListenerCounter() {
const counter = document.getElementById('listenerCount');
counter.textContent = listenerCount.toString();
}
function createPublisher() {
clearOutput('pubsub-output');
log('Publisher created', 'pubsub-output');
}
function addSubscribers() {
publisherEmitter.on('news', (headline, content) => {
log(`News Subscriber: ${headline}`, 'pubsub-output');
});
publisherEmitter.on('news', (headline, content) => {
log(`Mobile App: New article "${headline}"`, 'pubsub-output');
});
publisherEmitter.on('news', (headline, content) => {
log(`Email Service: Sending newsletter with "${headline}"`, 'pubsub-output');
});
log('Added 3 subscribers to news events', 'pubsub-output');
}
function publishNews() {
const headlines = [
'EventEmitter Pattern Increases Developer Productivity',
'New Features Added to Event System',
'Best Practices for Event-Driven Architecture'
];
const headline = headlines[Math.floor(Math.random() * headlines.length)];
const count = publisherEmitter.emit('news', headline, 'Article content here...');
log(`Published "${headline}" to ${count} subscribers`, 'pubsub-output');
gameStats.eventsEmitted++;
gameStats.handlersExecuted += count;
updateEventStats();
}
function setupComponents() {
clearOutput('component-output');
demoEmitter.on('ui.update', (data) => {
log(`UI Component: Updating display with ${data}`, 'component-output');
});
demoEmitter.on('data.request', (type) => {
log(`Data Component: Fetching ${type} data`, 'component-output');
setTimeout(() => {
demoEmitter.emit('data.response', `${type} data loaded`);
}, 500);
});
demoEmitter.on('data.response', (data) => {
log(`Logger: Data received - ${data}`, 'component-output');
});
log('Components setup complete', 'component-output');
}
function componentInteraction() {
demoEmitter.emit('data.request', 'user');
gameStats.eventsEmitted++;
updateEventStats();
}
function cascadeEvents() {
demoEmitter.emit('ui.update', 'new theme');
setTimeout(() => {
demoEmitter.emit('ui.update', 'user preferences');
}, 300);
setTimeout(() => {
demoEmitter.emit('ui.update', 'layout changes');
}, 600);
gameStats.eventsEmitted += 3;
updateEventStats();
}
function handlerCountDemo() {
clearOutput('count-output');
demoEmitter.on('count-test', () => log('Handler A executed', 'count-output'));
demoEmitter.on('count-test', () => log('Handler B executed', 'count-output'));
demoEmitter.on('count-test', () => log('Handler C executed', 'count-output'));
const count = demoEmitter.emit('count-test');
log(`Total handlers executed: ${count}`, 'count-output');
gameStats.eventsEmitted++;
gameStats.handlersExecuted += count;
updateEventStats();
}
function benchmarkEmission() {
const start = performance.now();
let totalHandlers = 0;
for (let i = 0; i < 1000; i++) {
totalHandlers += demoEmitter.emit('count-test');
}
const end = performance.now();
log(`Benchmark: 1000 emissions took ${(end - start).toFixed(2)}ms`, 'count-output');
log(`Total handlers called: ${totalHandlers}`, 'count-output');
gameStats.eventsEmitted += 1000;
gameStats.handlersExecuted += totalHandlers;
updateEventStats();
}
function setupGameDemo() {
clearOutput('game-output');
gameEmitter.on('player.move', (x, y) => {
log(`Player moved to (${x}, ${y})`, 'game-output');
});
gameEmitter.on('enemy.spawn', (type, level) => {
log(`${type} enemy spawned at level ${level}`, 'game-output');
});
gameEmitter.on('score.update', (points, combo) => {
log(`Score: ${points} points (${combo}x combo)`, 'game-output');
});
gameEmitter.on('game.over', (finalScore) => {
log(`Game Over! Final score: ${finalScore}`, 'game-output');
});
log('Game event system initialized', 'game-output');
}
function simulateGameplay() {
const actions = [
() => gameEmitter.emit('player.move', Math.floor(Math.random() * 10), Math.floor(Math.random() * 10)),
() => gameEmitter.emit('enemy.spawn', ['goblin', 'orc', 'dragon'][Math.floor(Math.random() * 3)], Math.floor(Math.random() * 5) + 1),
() => gameEmitter.emit('score.update', Math.floor(Math.random() * 1000), Math.floor(Math.random() * 5) + 1),
];
let actionCount = 0;
const gameLoop = setInterval(() => {
const action = actions[Math.floor(Math.random() * actions.length)];
action();
actionCount++;
gameStats.eventsEmitted++;
if (actionCount >= 8) {
clearInterval(gameLoop);
setTimeout(() => {
gameEmitter.emit('game.over', Math.floor(Math.random() * 10000));
gameStats.eventsEmitted++;
updateEventStats();
}, 1000);
}
}, 500);
updateEventStats();
}
function showGameStats() {
log(`Game Statistics:`, 'game-output');
log(`Events emitted: ${gameStats.eventsEmitted}`, 'game-output');
log(`Handlers executed: ${gameStats.handlersExecuted}`, 'game-output');
log(`Avg handlers per event: ${(gameStats.handlersExecuted / gameStats.eventsEmitted || 0).toFixed(2)}`, 'game-output');
}
// UI Demo
function setupUIDemo() {
clearOutput('ui-output');
uiEmitter.on('button.click', (buttonId) => {
log(`Button clicked: ${buttonId}`, 'ui-output');
});
uiEmitter.on('form.submit', (formData) => {
log(`Form submitted: ${JSON.stringify(formData)}`, 'ui-output');
});
uiEmitter.on('modal.open', (modalType) => {
log(`Modal opened: ${modalType}`, 'ui-output');
});
uiEmitter.on('theme.change', (theme) => {
log(`Theme changed to: ${theme}`, 'ui-output');
});
log('UI event handlers registered', 'ui-output');
}
function simulateUserActions() {
const actions = [
() => uiEmitter.emit('button.click', 'submit-btn'),
() => uiEmitter.emit('button.click', 'cancel-btn'),
() => uiEmitter.emit('form.submit', { name: 'John', email: 'john@example.com' }),
() => uiEmitter.emit('modal.open', 'settings'),
() => uiEmitter.emit('modal.open', 'help'),
() => uiEmitter.emit('theme.change', 'dark'),
() => uiEmitter.emit('theme.change', 'light'),
];
actions.forEach((action, index) => {
setTimeout(() => {
action();
gameStats.eventsEmitted++;
if (index === actions.length - 1) {
updateEventStats();
}
}, index * 400);
});
}
function updateLibraryInfo() {
const info = document.getElementById('libraryInfo');
info.textContent = `
EventEmitter Instances: Multiple active instances
Global Event Statistics: ${gameStats.totalEvents} total events processed
Memory Usage: Efficient slot-based deduplication
Performance: Sub-millisecond event emission
`.trim();
}
function updateEventStats() {
gameStats.totalEvents = gameStats.eventsEmitted;
const stats = document.getElementById('eventStats');
stats.textContent = `
Events Emitted: ${gameStats.eventsEmitted}
Handlers Executed: ${gameStats.handlersExecuted}
Average Handlers per Event: ${(gameStats.handlersExecuted / gameStats.eventsEmitted || 0).toFixed(2)}
Active Listeners: ${listenerCount}
`.trim();
}
document.addEventListener('DOMContentLoaded', function() {
updateLibraryInfo();
updateEventStats();
updateListenerCounter();
setInterval(updateLibraryInfo, 3000);
});
</script>
</body>
</html>