- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
1530 lines
55 KiB
PHP
1530 lines
55 KiB
PHP
<main class="demo-container">
|
||
<section class="hero-section">
|
||
<h1>🚀 API Manager Demo</h1>
|
||
<p class="hero-description">Zentrale Verwaltung aller Web APIs für moderne Browser-Features</p>
|
||
</section>
|
||
|
||
<div class="demo-grid">
|
||
<!-- API Manager Status -->
|
||
<section class="demo-card">
|
||
<h2>📊 API Manager Status</h2>
|
||
<button class="btn btn-primary" onclick="initializeAPIManager()">Initialize API Manager</button>
|
||
<button class="btn btn-secondary" onclick="getAPIManagerStatus()">Get Status</button>
|
||
<div id="api-manager-status" class="results-box"></div>
|
||
</section>
|
||
|
||
<!-- Observer APIs -->
|
||
<section class="demo-card">
|
||
<h2>👀 Observer APIs</h2>
|
||
|
||
<div class="button-group">
|
||
<h3>Intersection Observer</h3>
|
||
<button class="btn btn-primary" onclick="createIntersectionObserver()">Create Observer</button>
|
||
<button class="btn btn-secondary" onclick="addObserverTarget()">Add Target</button>
|
||
<div class="observer-target" id="intersection-target" style="height: 100px; background: #f0f0f0; margin: 10px 0; padding: 20px; text-align: center;">
|
||
Target Element - Scroll to observe
|
||
</div>
|
||
</div>
|
||
|
||
<div class="button-group">
|
||
<h3>Resize Observer</h3>
|
||
<button class="btn btn-primary" onclick="createResizeObserver()">Create Resize Observer</button>
|
||
<button class="btn btn-secondary" onclick="testResize()">Test Resize</button>
|
||
<div class="resizable-target" id="resize-target" style="width: 200px; height: 100px; background: #e0e0ff; margin: 10px 0; padding: 10px; resize: both; overflow: auto;">
|
||
Resize me to see the observer in action
|
||
</div>
|
||
</div>
|
||
|
||
<div class="button-group">
|
||
<h3>Mutation Observer</h3>
|
||
<button class="btn btn-primary" onclick="createMutationObserver()">Create Mutation Observer</button>
|
||
<button class="btn btn-secondary" onclick="triggerMutation()">Trigger Mutation</button>
|
||
<div id="mutation-target" style="background: #ffe0e0; margin: 10px 0; padding: 10px;">
|
||
Original content - will be modified
|
||
</div>
|
||
</div>
|
||
|
||
<div id="observer-results" class="results-box"></div>
|
||
</section>
|
||
|
||
<!-- Media APIs -->
|
||
<section class="demo-card">
|
||
<h2>📹 Media APIs</h2>
|
||
|
||
<div class="button-group">
|
||
<h3>Camera & Microphone</h3>
|
||
<button class="btn btn-primary" onclick="requestCameraAccess()">Request Camera</button>
|
||
<button class="btn btn-primary" onclick="requestMicrophoneAccess()">Request Microphone</button>
|
||
<button class="btn btn-success" onclick="startVideoCall()">Start Video Call</button>
|
||
<button class="btn btn-danger" onclick="stopMediaStreams()">Stop All Streams</button>
|
||
<video id="camera-preview" width="200" height="150" autoplay muted style="background: #000; display: none; margin: 10px 0;"></video>
|
||
</div>
|
||
|
||
<div class="button-group">
|
||
<h3>Screen Sharing</h3>
|
||
<button class="btn btn-primary" onclick="startScreenShare()">Start Screen Share</button>
|
||
<button class="btn btn-secondary" onclick="stopScreenShare()">Stop Screen Share</button>
|
||
<video id="screen-preview" width="300" height="200" autoplay muted style="background: #000; display: none; margin: 10px 0;"></video>
|
||
</div>
|
||
|
||
<div id="media-results" class="results-box"></div>
|
||
</section>
|
||
|
||
<!-- Storage APIs -->
|
||
<section class="demo-card">
|
||
<h2>💾 Storage APIs</h2>
|
||
|
||
<div class="button-group">
|
||
<h3>IndexedDB</h3>
|
||
<button class="btn btn-primary" onclick="initializeIndexedDB()">Initialize IndexedDB</button>
|
||
<button class="btn btn-secondary" onclick="storeDataIndexedDB()">Store Test Data</button>
|
||
<button class="btn btn-secondary" onclick="retrieveDataIndexedDB()">Retrieve Data</button>
|
||
<button class="btn btn-outline" onclick="clearIndexedDB()">Clear Database</button>
|
||
</div>
|
||
|
||
<div class="button-group">
|
||
<h3>Cache API</h3>
|
||
<button class="btn btn-primary" onclick="initializeCacheAPI()">Initialize Cache</button>
|
||
<button class="btn btn-secondary" onclick="cacheResources()">Cache Resources</button>
|
||
<button class="btn btn-secondary" onclick="retrieveCachedData()">Retrieve Cached</button>
|
||
<button class="btn btn-outline" onclick="clearCache()">Clear Cache</button>
|
||
</div>
|
||
|
||
<div class="button-group">
|
||
<h3>Local Storage</h3>
|
||
<button class="btn btn-primary" onclick="testLocalStorage()">Test Local Storage</button>
|
||
<button class="btn btn-secondary" onclick="testSessionStorage()">Test Session Storage</button>
|
||
<button class="btn btn-outline" onclick="getStorageInfo()">Get Storage Info</button>
|
||
</div>
|
||
|
||
<div id="storage-results" class="results-box"></div>
|
||
</section>
|
||
|
||
<!-- Device APIs -->
|
||
<section class="demo-card">
|
||
<h2>📱 Device APIs</h2>
|
||
|
||
<div class="button-group">
|
||
<h3>Geolocation</h3>
|
||
<button class="btn btn-primary" onclick="getCurrentLocation()">Get Current Location</button>
|
||
<button class="btn btn-secondary" onclick="watchPosition()">Watch Position</button>
|
||
<button class="btn btn-outline" onclick="stopWatchingPosition()">Stop Watching</button>
|
||
</div>
|
||
|
||
<div class="button-group">
|
||
<h3>Device Sensors</h3>
|
||
<button class="btn btn-primary" onclick="getDeviceMotion()">Device Motion</button>
|
||
<button class="btn btn-secondary" onclick="getDeviceOrientation()">Device Orientation</button>
|
||
<button class="btn btn-secondary" onclick="testVibration()">Test Vibration</button>
|
||
</div>
|
||
|
||
<div class="button-group">
|
||
<h3>Network Information</h3>
|
||
<button class="btn btn-primary" onclick="getNetworkInfo()">Get Network Info</button>
|
||
<button class="btn btn-secondary" onclick="monitorConnection()">Monitor Connection</button>
|
||
</div>
|
||
|
||
<div id="device-results" class="results-box"></div>
|
||
</section>
|
||
|
||
<!-- Web Animations API -->
|
||
<section class="demo-card">
|
||
<h2>✨ Web Animations API</h2>
|
||
|
||
<div class="button-group">
|
||
<h3>Basic Animations</h3>
|
||
<button class="btn btn-primary" onclick="createBasicAnimation()">Basic Animation</button>
|
||
<button class="btn btn-secondary" onclick="createKeyframeAnimation()">Keyframe Animation</button>
|
||
<button class="btn btn-secondary" onclick="createComplexAnimation()">Complex Animation</button>
|
||
<div class="animation-target" id="animation-box" style="width: 100px; height: 100px; background: #4CAF50; margin: 20px auto; border-radius: 8px;"></div>
|
||
</div>
|
||
|
||
<div class="button-group">
|
||
<h3>Animation Controls</h3>
|
||
<button class="btn btn-success" onclick="playAnimations()">Play All</button>
|
||
<button class="btn btn-warning" onclick="pauseAnimations()">Pause All</button>
|
||
<button class="btn btn-danger" onclick="cancelAnimations()">Cancel All</button>
|
||
<button class="btn btn-outline" onclick="getAnimationStatus()">Get Status</button>
|
||
</div>
|
||
|
||
<div id="animation-results" class="results-box"></div>
|
||
</section>
|
||
|
||
<!-- Worker APIs -->
|
||
<section class="demo-card">
|
||
<h2>⚙️ Worker APIs</h2>
|
||
|
||
<div class="button-group">
|
||
<h3>Web Worker</h3>
|
||
<button class="btn btn-primary" onclick="createWebWorker()">Create Web Worker</button>
|
||
<button class="btn btn-secondary" onclick="sendWorkerMessage()">Send Message</button>
|
||
<button class="btn btn-outline" onclick="terminateWorker()">Terminate Worker</button>
|
||
</div>
|
||
|
||
<div class="button-group">
|
||
<h3>Service Worker</h3>
|
||
<button class="btn btn-primary" onclick="registerServiceWorker()">Register Service Worker</button>
|
||
<button class="btn btn-secondary" onclick="updateServiceWorker()">Update Service Worker</button>
|
||
<button class="btn btn-outline" onclick="getServiceWorkerStatus()">Get SW Status</button>
|
||
</div>
|
||
|
||
<div id="worker-results" class="results-box"></div>
|
||
</section>
|
||
|
||
<!-- Performance APIs -->
|
||
<section class="demo-card">
|
||
<h2>⚡ Performance APIs</h2>
|
||
|
||
<div class="button-group">
|
||
<h3>Performance Monitoring</h3>
|
||
<button class="btn btn-primary" onclick="measurePerformance()">Measure Performance</button>
|
||
<button class="btn btn-secondary" onclick="getPerformanceEntries()">Get Performance Entries</button>
|
||
<button class="btn btn-secondary" onclick="createPerformanceMark()">Create Performance Mark</button>
|
||
</div>
|
||
|
||
<div class="button-group">
|
||
<h3>Resource Timing</h3>
|
||
<button class="btn btn-primary" onclick="getResourceTiming()">Get Resource Timing</button>
|
||
<button class="btn btn-secondary" onclick="measureNavigationTiming()">Navigation Timing</button>
|
||
<button class="btn btn-outline" onclick="clearPerformanceData()">Clear Performance Data</button>
|
||
</div>
|
||
|
||
<div id="performance-results" class="results-box"></div>
|
||
</section>
|
||
|
||
<!-- Comprehensive Test -->
|
||
<section class="demo-card full-width">
|
||
<h2>🧪 Comprehensive API Test</h2>
|
||
<div class="button-group">
|
||
<button class="btn btn-success" onclick="runFullAPITest()">Run Full API Test Suite</button>
|
||
<button class="btn btn-primary" onclick="createIntegratedWorkflow()">Create Integrated Workflow</button>
|
||
<button class="btn btn-outline" onclick="generateAPIReport()">Generate API Report</button>
|
||
</div>
|
||
<div id="comprehensive-results" class="results-box"></div>
|
||
</section>
|
||
</div>
|
||
</main>
|
||
|
||
<style>
|
||
.demo-container {
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
padding: 2rem 1rem;
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
}
|
||
|
||
.hero-section {
|
||
text-align: center;
|
||
margin-bottom: 3rem;
|
||
padding: 2rem;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
border-radius: 12px;
|
||
}
|
||
|
||
.hero-section h1 {
|
||
font-size: 2.5rem;
|
||
margin-bottom: 1rem;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.hero-description {
|
||
font-size: 1.2rem;
|
||
opacity: 0.9;
|
||
margin: 0;
|
||
}
|
||
|
||
.demo-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
||
gap: 2rem;
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
.demo-card {
|
||
background: white;
|
||
border-radius: 12px;
|
||
padding: 2rem;
|
||
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||
border: 1px solid #e0e0e0;
|
||
}
|
||
|
||
.demo-card.full-width {
|
||
grid-column: 1 / -1;
|
||
}
|
||
|
||
.demo-card h2 {
|
||
color: #333;
|
||
margin-bottom: 1.5rem;
|
||
font-size: 1.5rem;
|
||
font-weight: 600;
|
||
border-bottom: 2px solid #f0f0f0;
|
||
padding-bottom: 0.5rem;
|
||
}
|
||
|
||
.demo-card h3 {
|
||
color: #555;
|
||
margin: 1.5rem 0 1rem 0;
|
||
font-size: 1.1rem;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.button-group {
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.button-group:last-of-type {
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.btn {
|
||
display: inline-block;
|
||
padding: 0.75rem 1rem;
|
||
margin: 0.25rem 0.5rem 0.25rem 0;
|
||
border: none;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 0.9rem;
|
||
font-weight: 500;
|
||
text-decoration: none;
|
||
transition: all 0.2s ease;
|
||
min-width: 120px;
|
||
text-align: center;
|
||
}
|
||
|
||
.btn:hover {
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
|
||
}
|
||
|
||
.btn-primary {
|
||
background: #007bff;
|
||
color: white;
|
||
}
|
||
|
||
.btn-primary:hover {
|
||
background: #0056b3;
|
||
}
|
||
|
||
.btn-secondary {
|
||
background: #6c757d;
|
||
color: white;
|
||
}
|
||
|
||
.btn-secondary:hover {
|
||
background: #545b62;
|
||
}
|
||
|
||
.btn-success {
|
||
background: #28a745;
|
||
color: white;
|
||
}
|
||
|
||
.btn-success:hover {
|
||
background: #1e7e34;
|
||
}
|
||
|
||
.btn-warning {
|
||
background: #ffc107;
|
||
color: #212529;
|
||
}
|
||
|
||
.btn-warning:hover {
|
||
background: #d39e00;
|
||
}
|
||
|
||
.btn-danger {
|
||
background: #dc3545;
|
||
color: white;
|
||
}
|
||
|
||
.btn-danger:hover {
|
||
background: #bd2130;
|
||
}
|
||
|
||
.btn-info {
|
||
background: #17a2b8;
|
||
color: white;
|
||
}
|
||
|
||
.btn-info:hover {
|
||
background: #117a8b;
|
||
}
|
||
|
||
.btn-outline {
|
||
background: transparent;
|
||
color: #666;
|
||
border: 2px solid #ddd;
|
||
}
|
||
|
||
.btn-outline:hover {
|
||
background: #f8f9fa;
|
||
border-color: #aaa;
|
||
}
|
||
|
||
.results-box {
|
||
margin-top: 1rem;
|
||
padding: 1rem;
|
||
background: #f8f9fa;
|
||
border-radius: 6px;
|
||
border: 1px solid #dee2e6;
|
||
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
|
||
font-size: 0.85rem;
|
||
line-height: 1.5;
|
||
max-height: 300px;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.results-box:empty {
|
||
display: none;
|
||
}
|
||
|
||
.form-group {
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.input-group {
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.input-group label {
|
||
display: block;
|
||
margin-bottom: 0.5rem;
|
||
font-weight: 500;
|
||
color: #555;
|
||
}
|
||
|
||
.input-group input {
|
||
width: 100%;
|
||
padding: 0.75rem;
|
||
border: 2px solid #ddd;
|
||
border-radius: 6px;
|
||
font-size: 1rem;
|
||
}
|
||
|
||
.input-group input:focus {
|
||
outline: none;
|
||
border-color: #007bff;
|
||
box-shadow: 0 0 0 3px rgba(0,123,255,0.1);
|
||
}
|
||
|
||
.observer-target,
|
||
.resizable-target,
|
||
.animation-target {
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.observer-target.observed {
|
||
background: #d4edda !important;
|
||
border: 2px solid #28a745;
|
||
}
|
||
|
||
.resizable-target.resizing {
|
||
background: #d1ecf1 !important;
|
||
border: 2px solid #17a2b8;
|
||
}
|
||
|
||
.status-indicator {
|
||
display: inline-block;
|
||
padding: 0.25rem 0.5rem;
|
||
border-radius: 4px;
|
||
font-size: 0.8rem;
|
||
font-weight: bold;
|
||
margin-left: 0.5rem;
|
||
}
|
||
|
||
.status-supported {
|
||
background: #d4edda;
|
||
color: #155724;
|
||
}
|
||
|
||
.status-unsupported {
|
||
background: #f8d7da;
|
||
color: #721c24;
|
||
}
|
||
|
||
.status-granted {
|
||
background: #d1ecf1;
|
||
color: #0c5460;
|
||
}
|
||
|
||
/* Responsive Design */
|
||
@media (max-width: 768px) {
|
||
.demo-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.demo-container {
|
||
padding: 1rem 0.5rem;
|
||
}
|
||
|
||
.demo-card {
|
||
padding: 1.5rem;
|
||
}
|
||
|
||
.btn {
|
||
min-width: 100px;
|
||
font-size: 0.8rem;
|
||
padding: 0.6rem 0.8rem;
|
||
}
|
||
}
|
||
</style>
|
||
|
||
<script>
|
||
// Initialize variables
|
||
let apiManager = null;
|
||
let observers = {
|
||
intersection: null,
|
||
resize: null,
|
||
mutation: null
|
||
};
|
||
let mediaStreams = [];
|
||
let animations = [];
|
||
let workers = {
|
||
web: null,
|
||
service: null
|
||
};
|
||
let positionWatcher = null;
|
||
|
||
// Wait for DOM and framework to load
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// Check if API Manager is available
|
||
if (typeof window.API !== 'undefined') {
|
||
displayResult('api-manager-status', '✅ API Manager is available and ready to use');
|
||
} else {
|
||
displayResult('api-manager-status', '⚠️ API Manager not yet loaded. Click "Initialize" to try again.');
|
||
}
|
||
});
|
||
|
||
// Utility function to display results
|
||
function displayResult(containerId, message, type = 'info') {
|
||
const container = document.getElementById(containerId);
|
||
if (!container) return;
|
||
|
||
const timestamp = new Date().toLocaleTimeString();
|
||
const colorClass = type === 'error' ? 'color: #dc3545' :
|
||
type === 'success' ? 'color: #28a745' :
|
||
type === 'warning' ? 'color: #ffc107' : 'color: #17a2b8';
|
||
|
||
container.innerHTML += `<div style="${colorClass}; margin-bottom: 0.5rem;">
|
||
[${timestamp}] ${message}
|
||
</div>`;
|
||
container.scrollTop = container.scrollHeight;
|
||
}
|
||
|
||
function clearResults(containerId) {
|
||
const container = document.getElementById(containerId);
|
||
if (container) container.innerHTML = '';
|
||
}
|
||
|
||
// API Manager Functions
|
||
function initializeAPIManager() {
|
||
try {
|
||
if (window.API) {
|
||
apiManager = window.API;
|
||
displayResult('api-manager-status', '✅ API Manager initialized successfully', 'success');
|
||
displayResult('api-manager-status', JSON.stringify({
|
||
hasPermissions: !!apiManager.permissions,
|
||
hasBiometric: !!apiManager.biometric,
|
||
hasObserver: !!apiManager.observer,
|
||
hasMedia: !!apiManager.media,
|
||
hasStorage: !!apiManager.storage,
|
||
hasDevice: !!apiManager.device,
|
||
hasAnimation: !!apiManager.animation,
|
||
hasWorker: !!apiManager.worker,
|
||
hasPerformance: !!apiManager.performance
|
||
}, null, 2));
|
||
} else {
|
||
displayResult('api-manager-status', '❌ API Manager not available in window.API', 'error');
|
||
}
|
||
} catch (error) {
|
||
displayResult('api-manager-status', `❌ Error initializing API Manager: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function getAPIManagerStatus() {
|
||
try {
|
||
if (!apiManager) {
|
||
displayResult('api-manager-status', '⚠️ API Manager not initialized', 'warning');
|
||
return;
|
||
}
|
||
|
||
const status = {
|
||
initialized: true,
|
||
modules: Object.keys(apiManager).filter(key => typeof apiManager[key] === 'object'),
|
||
config: apiManager.config || 'No config available'
|
||
};
|
||
|
||
displayResult('api-manager-status', JSON.stringify(status, null, 2), 'success');
|
||
} catch (error) {
|
||
displayResult('api-manager-status', `❌ Error getting status: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
// Observer API Functions
|
||
function createIntersectionObserver() {
|
||
try {
|
||
if (!apiManager?.observer) {
|
||
displayResult('observer-results', '❌ Observer API not available', 'error');
|
||
return;
|
||
}
|
||
|
||
const target = document.getElementById('intersection-target');
|
||
if (!target) {
|
||
displayResult('observer-results', '❌ Target element not found', 'error');
|
||
return;
|
||
}
|
||
|
||
observers.intersection = apiManager.observer.intersection([target], (entries) => {
|
||
entries.forEach(entry => {
|
||
const isVisible = entry.isIntersecting;
|
||
entry.element.classList.toggle('observed', isVisible);
|
||
displayResult('observer-results', `📍 Intersection: ${isVisible ? 'Visible' : 'Hidden'} (ratio: ${entry.intersectionRatio.toFixed(2)})`);
|
||
});
|
||
}, {
|
||
threshold: [0, 0.5, 1.0],
|
||
rootMargin: '0px'
|
||
});
|
||
|
||
displayResult('observer-results', '✅ Intersection Observer created and observing target', 'success');
|
||
} catch (error) {
|
||
displayResult('observer-results', `❌ Error creating Intersection Observer: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function addObserverTarget() {
|
||
displayResult('observer-results', '📝 Scroll up/down to see the intersection observer in action');
|
||
}
|
||
|
||
function createResizeObserver() {
|
||
try {
|
||
if (!apiManager?.observer) {
|
||
displayResult('observer-results', '❌ Observer API not available', 'error');
|
||
return;
|
||
}
|
||
|
||
const target = document.getElementById('resize-target');
|
||
if (!target) {
|
||
displayResult('observer-results', '❌ Resize target not found', 'error');
|
||
return;
|
||
}
|
||
|
||
observers.resize = apiManager.observer.resize([target], (entries) => {
|
||
entries.forEach(entry => {
|
||
const { width, height } = entry.dimensions;
|
||
entry.element.classList.add('resizing');
|
||
setTimeout(() => entry.element.classList.remove('resizing'), 300);
|
||
displayResult('observer-results', `📐 Resize: ${Math.round(width)}x${Math.round(height)}px`);
|
||
});
|
||
});
|
||
|
||
displayResult('observer-results', '✅ Resize Observer created and observing target', 'success');
|
||
} catch (error) {
|
||
displayResult('observer-results', `❌ Error creating Resize Observer: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function testResize() {
|
||
const target = document.getElementById('resize-target');
|
||
if (target) {
|
||
const newWidth = Math.random() * 300 + 150;
|
||
const newHeight = Math.random() * 200 + 100;
|
||
target.style.width = `${newWidth}px`;
|
||
target.style.height = `${newHeight}px`;
|
||
displayResult('observer-results', `🔄 Programmatically resized to ${Math.round(newWidth)}x${Math.round(newHeight)}px`);
|
||
}
|
||
}
|
||
|
||
function createMutationObserver() {
|
||
try {
|
||
if (!apiManager?.observer) {
|
||
displayResult('observer-results', '❌ Observer API not available', 'error');
|
||
return;
|
||
}
|
||
|
||
const target = document.getElementById('mutation-target');
|
||
if (!target) {
|
||
displayResult('observer-results', '❌ Mutation target not found', 'error');
|
||
return;
|
||
}
|
||
|
||
observers.mutation = apiManager.observer.mutation(target, (mutations) => {
|
||
mutations.forEach(mutation => {
|
||
displayResult('observer-results', `🔄 Mutation: ${mutation.type} on ${mutation.target.tagName || 'text'}`);
|
||
});
|
||
}, {
|
||
childList: true,
|
||
attributes: true,
|
||
subtree: true,
|
||
characterData: true
|
||
});
|
||
|
||
displayResult('observer-results', '✅ Mutation Observer created and observing target', 'success');
|
||
} catch (error) {
|
||
displayResult('observer-results', `❌ Error creating Mutation Observer: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function triggerMutation() {
|
||
const target = document.getElementById('mutation-target');
|
||
if (target) {
|
||
const timestamp = new Date().toLocaleTimeString();
|
||
target.innerHTML = `Modified content at ${timestamp}`;
|
||
target.setAttribute('data-modified', timestamp);
|
||
}
|
||
}
|
||
|
||
// Media API Functions
|
||
async function requestCameraAccess() {
|
||
try {
|
||
if (!apiManager?.media) {
|
||
displayResult('media-results', '❌ Media API not available', 'error');
|
||
return;
|
||
}
|
||
|
||
const stream = await apiManager.media.getUserMedia({ video: true });
|
||
mediaStreams.push(stream);
|
||
|
||
const video = document.getElementById('camera-preview');
|
||
video.srcObject = stream;
|
||
video.style.display = 'block';
|
||
|
||
displayResult('media-results', '✅ Camera access granted and preview started', 'success');
|
||
} catch (error) {
|
||
displayResult('media-results', `❌ Camera access failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
async function requestMicrophoneAccess() {
|
||
try {
|
||
if (!apiManager?.media) {
|
||
displayResult('media-results', '❌ Media API not available', 'error');
|
||
return;
|
||
}
|
||
|
||
const stream = await apiManager.media.getUserMedia({ audio: true });
|
||
mediaStreams.push(stream);
|
||
|
||
displayResult('media-results', '✅ Microphone access granted', 'success');
|
||
} catch (error) {
|
||
displayResult('media-results', `❌ Microphone access failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
async function startVideoCall() {
|
||
try {
|
||
if (!apiManager?.media) {
|
||
displayResult('media-results', '❌ Media API not available', 'error');
|
||
return;
|
||
}
|
||
|
||
const stream = await apiManager.media.getUserMedia({
|
||
video: { width: 1280, height: 720 },
|
||
audio: true
|
||
});
|
||
mediaStreams.push(stream);
|
||
|
||
const video = document.getElementById('camera-preview');
|
||
video.srcObject = stream;
|
||
video.style.display = 'block';
|
||
|
||
displayResult('media-results', '✅ Video call stream started with audio/video', 'success');
|
||
} catch (error) {
|
||
displayResult('media-results', `❌ Video call failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
async function startScreenShare() {
|
||
try {
|
||
if (!apiManager?.media) {
|
||
displayResult('media-results', '❌ Media API not available', 'error');
|
||
return;
|
||
}
|
||
|
||
const stream = await apiManager.media.getDisplayMedia({ video: true });
|
||
mediaStreams.push(stream);
|
||
|
||
const video = document.getElementById('screen-preview');
|
||
video.srcObject = stream;
|
||
video.style.display = 'block';
|
||
|
||
displayResult('media-results', '✅ Screen sharing started', 'success');
|
||
} catch (error) {
|
||
displayResult('media-results', `❌ Screen sharing failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function stopScreenShare() {
|
||
const video = document.getElementById('screen-preview');
|
||
if (video.srcObject) {
|
||
video.srcObject.getTracks().forEach(track => track.stop());
|
||
video.srcObject = null;
|
||
video.style.display = 'none';
|
||
displayResult('media-results', '✅ Screen sharing stopped', 'success');
|
||
}
|
||
}
|
||
|
||
function stopMediaStreams() {
|
||
mediaStreams.forEach(stream => {
|
||
stream.getTracks().forEach(track => track.stop());
|
||
});
|
||
mediaStreams = [];
|
||
|
||
document.getElementById('camera-preview').style.display = 'none';
|
||
document.getElementById('screen-preview').style.display = 'none';
|
||
|
||
displayResult('media-results', '✅ All media streams stopped', 'success');
|
||
}
|
||
|
||
// Storage API Functions
|
||
async function initializeIndexedDB() {
|
||
try {
|
||
if (!apiManager?.storage) {
|
||
displayResult('storage-results', '❌ Storage API not available', 'error');
|
||
return;
|
||
}
|
||
|
||
await apiManager.storage.indexedDB.open('APITestDB', 1, (db, oldVersion) => {
|
||
if (oldVersion < 1) {
|
||
const store = db.createObjectStore('testData', { keyPath: 'id', autoIncrement: true });
|
||
store.createIndex('timestamp', 'timestamp', { unique: false });
|
||
}
|
||
});
|
||
|
||
displayResult('storage-results', '✅ IndexedDB initialized successfully', 'success');
|
||
} catch (error) {
|
||
displayResult('storage-results', `❌ IndexedDB initialization failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
async function storeDataIndexedDB() {
|
||
try {
|
||
if (!apiManager?.storage) {
|
||
displayResult('storage-results', '❌ Storage API not available', 'error');
|
||
return;
|
||
}
|
||
|
||
const testData = {
|
||
message: 'Hello from IndexedDB!',
|
||
timestamp: Date.now(),
|
||
randomValue: Math.random()
|
||
};
|
||
|
||
await apiManager.storage.indexedDB.set('testData', testData);
|
||
displayResult('storage-results', `✅ Data stored in IndexedDB: ${JSON.stringify(testData)}`, 'success');
|
||
} catch (error) {
|
||
displayResult('storage-results', `❌ IndexedDB store failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
async function retrieveDataIndexedDB() {
|
||
try {
|
||
if (!apiManager?.storage) {
|
||
displayResult('storage-results', '❌ Storage API not available', 'error');
|
||
return;
|
||
}
|
||
|
||
const data = await apiManager.storage.indexedDB.getAll('testData');
|
||
displayResult('storage-results', `✅ Retrieved from IndexedDB: ${JSON.stringify(data, null, 2)}`, 'success');
|
||
} catch (error) {
|
||
displayResult('storage-results', `❌ IndexedDB retrieval failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
async function clearIndexedDB() {
|
||
try {
|
||
if (!apiManager?.storage) {
|
||
displayResult('storage-results', '❌ Storage API not available', 'error');
|
||
return;
|
||
}
|
||
|
||
await apiManager.storage.indexedDB.clear('testData');
|
||
displayResult('storage-results', '✅ IndexedDB cleared', 'success');
|
||
} catch (error) {
|
||
displayResult('storage-results', `❌ IndexedDB clear failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
async function initializeCacheAPI() {
|
||
try {
|
||
if (!apiManager?.storage) {
|
||
displayResult('storage-results', '❌ Storage API not available', 'error');
|
||
return;
|
||
}
|
||
|
||
await apiManager.storage.cache.open('api-test-cache');
|
||
displayResult('storage-results', '✅ Cache API initialized', 'success');
|
||
} catch (error) {
|
||
displayResult('storage-results', `❌ Cache API initialization failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
async function cacheResources() {
|
||
try {
|
||
if (!apiManager?.storage) {
|
||
displayResult('storage-results', '❌ Storage API not available', 'error');
|
||
return;
|
||
}
|
||
|
||
const cache = await apiManager.storage.cache.open('api-test-cache');
|
||
await cache.addAll([
|
||
'/css/admin.css',
|
||
'/js/chat.js'
|
||
]);
|
||
|
||
displayResult('storage-results', '✅ Resources cached successfully', 'success');
|
||
} catch (error) {
|
||
displayResult('storage-results', `❌ Resource caching failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
async function retrieveCachedData() {
|
||
try {
|
||
if (!apiManager?.storage) {
|
||
displayResult('storage-results', '❌ Storage API not available', 'error');
|
||
return;
|
||
}
|
||
|
||
const cache = await apiManager.storage.cache.open('api-test-cache');
|
||
const keys = await cache.keys();
|
||
|
||
displayResult('storage-results', `✅ Cached resources: ${keys.map(req => req.url).join(', ')}`, 'success');
|
||
} catch (error) {
|
||
displayResult('storage-results', `❌ Cache retrieval failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
async function clearCache() {
|
||
try {
|
||
if (!apiManager?.storage) {
|
||
displayResult('storage-results', '❌ Storage API not available', 'error');
|
||
return;
|
||
}
|
||
|
||
await caches.delete('api-test-cache');
|
||
displayResult('storage-results', '✅ Cache cleared', 'success');
|
||
} catch (error) {
|
||
displayResult('storage-results', `❌ Cache clear failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function testLocalStorage() {
|
||
try {
|
||
const testData = { message: 'Hello from localStorage!', timestamp: Date.now() };
|
||
localStorage.setItem('apiTest', JSON.stringify(testData));
|
||
const retrieved = JSON.parse(localStorage.getItem('apiTest'));
|
||
displayResult('storage-results', `✅ LocalStorage test: ${JSON.stringify(retrieved)}`, 'success');
|
||
} catch (error) {
|
||
displayResult('storage-results', `❌ LocalStorage test failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function testSessionStorage() {
|
||
try {
|
||
const testData = { message: 'Hello from sessionStorage!', timestamp: Date.now() };
|
||
sessionStorage.setItem('apiTest', JSON.stringify(testData));
|
||
const retrieved = JSON.parse(sessionStorage.getItem('apiTest'));
|
||
displayResult('storage-results', `✅ SessionStorage test: ${JSON.stringify(retrieved)}`, 'success');
|
||
} catch (error) {
|
||
displayResult('storage-results', `❌ SessionStorage test failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
async function getStorageInfo() {
|
||
try {
|
||
if ('storage' in navigator && 'estimate' in navigator.storage) {
|
||
const estimate = await navigator.storage.estimate();
|
||
displayResult('storage-results', `📊 Storage: ${(estimate.usage / 1024 / 1024).toFixed(2)}MB used / ${(estimate.quota / 1024 / 1024).toFixed(2)}MB total`, 'success');
|
||
} else {
|
||
displayResult('storage-results', '❌ Storage estimation not supported', 'error');
|
||
}
|
||
} catch (error) {
|
||
displayResult('storage-results', `❌ Storage info failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
// Device API Functions
|
||
async function getCurrentLocation() {
|
||
try {
|
||
if (!apiManager?.device) {
|
||
displayResult('device-results', '❌ Device API not available', 'error');
|
||
return;
|
||
}
|
||
|
||
const position = await apiManager.device.geolocation.getCurrent();
|
||
const { latitude, longitude, accuracy } = position;
|
||
|
||
displayResult('device-results', `📍 Location: ${latitude.toFixed(6)}, ${longitude.toFixed(6)} (±${accuracy}m)`, 'success');
|
||
} catch (error) {
|
||
displayResult('device-results', `❌ Geolocation failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function watchPosition() {
|
||
try {
|
||
if (!apiManager?.device) {
|
||
displayResult('device-results', '❌ Device API not available', 'error');
|
||
return;
|
||
}
|
||
|
||
const watcher = apiManager.device.geolocation.watch(
|
||
(position) => {
|
||
if (position.error) {
|
||
displayResult('device-results', `❌ Position watch error: ${position.error.message}`, 'error');
|
||
} else {
|
||
displayResult('device-results', `👀 Watching: ${position.latitude.toFixed(6)}, ${position.longitude.toFixed(6)} (±${position.accuracy}m)`);
|
||
}
|
||
}
|
||
);
|
||
positionWatcher = watcher;
|
||
|
||
displayResult('device-results', '✅ Started watching position', 'success');
|
||
} catch (error) {
|
||
displayResult('device-results', `❌ Position watch failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function stopWatchingPosition() {
|
||
if (positionWatcher) {
|
||
positionWatcher.stop();
|
||
positionWatcher = null;
|
||
displayResult('device-results', '✅ Stopped watching position', 'success');
|
||
}
|
||
}
|
||
|
||
function getDeviceMotion() {
|
||
if ('DeviceMotionEvent' in window) {
|
||
const handler = (event) => {
|
||
const acc = event.accelerationIncludingGravity;
|
||
if (acc) {
|
||
displayResult('device-results', `📱 Motion: x:${acc.x?.toFixed(2)}, y:${acc.y?.toFixed(2)}, z:${acc.z?.toFixed(2)}`);
|
||
window.removeEventListener('devicemotion', handler);
|
||
}
|
||
};
|
||
|
||
window.addEventListener('devicemotion', handler);
|
||
setTimeout(() => window.removeEventListener('devicemotion', handler), 5000);
|
||
displayResult('device-results', '✅ Listening for device motion (5s)...', 'success');
|
||
} else {
|
||
displayResult('device-results', '❌ Device Motion not supported', 'error');
|
||
}
|
||
}
|
||
|
||
function getDeviceOrientation() {
|
||
if ('DeviceOrientationEvent' in window) {
|
||
const handler = (event) => {
|
||
displayResult('device-results', `🧭 Orientation: α:${event.alpha?.toFixed(1)}°, β:${event.beta?.toFixed(1)}°, γ:${event.gamma?.toFixed(1)}°`);
|
||
window.removeEventListener('deviceorientation', handler);
|
||
};
|
||
|
||
window.addEventListener('deviceorientation', handler);
|
||
setTimeout(() => window.removeEventListener('deviceorientation', handler), 5000);
|
||
displayResult('device-results', '✅ Listening for device orientation (5s)...', 'success');
|
||
} else {
|
||
displayResult('device-results', '❌ Device Orientation not supported', 'error');
|
||
}
|
||
}
|
||
|
||
function testVibration() {
|
||
if ('vibrate' in navigator) {
|
||
navigator.vibrate([200, 100, 200]);
|
||
displayResult('device-results', '✅ Vibration triggered', 'success');
|
||
} else {
|
||
displayResult('device-results', '❌ Vibration not supported', 'error');
|
||
}
|
||
}
|
||
|
||
function getNetworkInfo() {
|
||
if ('connection' in navigator) {
|
||
const conn = navigator.connection;
|
||
displayResult('device-results', `🌐 Network: ${conn.effectiveType}, ${conn.downlink}Mbps, rtt:${conn.rtt}ms`, 'success');
|
||
} else {
|
||
displayResult('device-results', '❌ Network Information API not supported', 'error');
|
||
}
|
||
}
|
||
|
||
function monitorConnection() {
|
||
const updateStatus = () => {
|
||
const status = navigator.onLine ? 'Online' : 'Offline';
|
||
displayResult('device-results', `🔌 Connection: ${status}`);
|
||
};
|
||
|
||
updateStatus();
|
||
window.addEventListener('online', updateStatus);
|
||
window.addEventListener('offline', updateStatus);
|
||
|
||
displayResult('device-results', '✅ Monitoring connection status', 'success');
|
||
}
|
||
|
||
// Web Animations API Functions
|
||
function createBasicAnimation() {
|
||
try {
|
||
if (!apiManager?.animation) {
|
||
displayResult('animation-results', '❌ Animation API not available', 'error');
|
||
return;
|
||
}
|
||
|
||
const target = document.getElementById('animation-box');
|
||
const animation = apiManager.animation.animate(target, [
|
||
{ transform: 'translateX(0px)', backgroundColor: '#4CAF50' },
|
||
{ transform: 'translateX(200px)', backgroundColor: '#2196F3' },
|
||
{ transform: 'translateX(0px)', backgroundColor: '#4CAF50' }
|
||
], {
|
||
duration: 2000,
|
||
easing: 'ease-in-out'
|
||
});
|
||
|
||
animations.push(animation);
|
||
displayResult('animation-results', '✅ Basic animation created and started', 'success');
|
||
} catch (error) {
|
||
displayResult('animation-results', `❌ Basic animation failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function createKeyframeAnimation() {
|
||
try {
|
||
if (!apiManager?.animation) {
|
||
displayResult('animation-results', '❌ Animation API not available', 'error');
|
||
return;
|
||
}
|
||
|
||
const target = document.getElementById('animation-box');
|
||
const animation = apiManager.animation.animate(target, [
|
||
{ transform: 'scale(1) rotate(0deg)', borderRadius: '8px' },
|
||
{ transform: 'scale(1.5) rotate(180deg)', borderRadius: '50%' },
|
||
{ transform: 'scale(1) rotate(360deg)', borderRadius: '8px' }
|
||
], {
|
||
duration: 3000,
|
||
easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
|
||
iterations: 2
|
||
});
|
||
|
||
animations.push(animation);
|
||
displayResult('animation-results', '✅ Keyframe animation created', 'success');
|
||
} catch (error) {
|
||
displayResult('animation-results', `❌ Keyframe animation failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function createComplexAnimation() {
|
||
try {
|
||
if (!apiManager?.animation) {
|
||
displayResult('animation-results', '❌ Animation API not available', 'error');
|
||
return;
|
||
}
|
||
|
||
const target = document.getElementById('animation-box');
|
||
|
||
// Multiple simultaneous animations
|
||
const moveAnimation = apiManager.animation.animate(target, [
|
||
{ transform: 'translateY(0px)' },
|
||
{ transform: 'translateY(-100px)' },
|
||
{ transform: 'translateY(0px)' }
|
||
], {
|
||
duration: 2000,
|
||
easing: 'ease-out'
|
||
});
|
||
|
||
const colorAnimation = apiManager.animation.animate(target, [
|
||
{ backgroundColor: '#4CAF50' },
|
||
{ backgroundColor: '#FF9800' },
|
||
{ backgroundColor: '#E91E63' },
|
||
{ backgroundColor: '#4CAF50' }
|
||
], {
|
||
duration: 2500,
|
||
easing: 'linear'
|
||
});
|
||
|
||
animations.push(moveAnimation, colorAnimation);
|
||
displayResult('animation-results', '✅ Complex multi-animation created', 'success');
|
||
} catch (error) {
|
||
displayResult('animation-results', `❌ Complex animation failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function playAnimations() {
|
||
try {
|
||
animations.forEach(animation => {
|
||
if (animation.playState === 'paused' || animation.playState === 'finished') {
|
||
animation.play();
|
||
}
|
||
});
|
||
displayResult('animation-results', `✅ Playing ${animations.length} animations`, 'success');
|
||
} catch (error) {
|
||
displayResult('animation-results', `❌ Play animations failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function pauseAnimations() {
|
||
try {
|
||
animations.forEach(animation => {
|
||
if (animation.playState === 'running') {
|
||
animation.pause();
|
||
}
|
||
});
|
||
displayResult('animation-results', `✅ Paused ${animations.length} animations`, 'success');
|
||
} catch (error) {
|
||
displayResult('animation-results', `❌ Pause animations failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function cancelAnimations() {
|
||
try {
|
||
animations.forEach(animation => animation.cancel());
|
||
animations = [];
|
||
displayResult('animation-results', '✅ All animations cancelled', 'success');
|
||
} catch (error) {
|
||
displayResult('animation-results', `❌ Cancel animations failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function getAnimationStatus() {
|
||
try {
|
||
const statuses = animations.map((anim, index) => `#${index + 1}: ${anim.playState}`);
|
||
displayResult('animation-results', `📊 Animation Status: ${statuses.join(', ')}`, 'success');
|
||
} catch (error) {
|
||
displayResult('animation-results', `❌ Get animation status failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
// Worker API Functions
|
||
function createWebWorker() {
|
||
try {
|
||
if (!apiManager?.worker) {
|
||
displayResult('worker-results', '❌ Worker API not available', 'error');
|
||
return;
|
||
}
|
||
|
||
// Create an inline worker for demonstration
|
||
const workerCode = `
|
||
self.onmessage = function(e) {
|
||
const result = e.data * e.data;
|
||
self.postMessage({ original: e.data, squared: result });
|
||
};
|
||
`;
|
||
|
||
const blob = new Blob([workerCode], { type: 'application/javascript' });
|
||
const workerUrl = URL.createObjectURL(blob);
|
||
|
||
workers.web = new Worker(workerUrl);
|
||
workers.web.onmessage = (e) => {
|
||
displayResult('worker-results', `📨 Worker response: ${e.data.original}² = ${e.data.squared}`);
|
||
};
|
||
|
||
displayResult('worker-results', '✅ Web Worker created successfully', 'success');
|
||
URL.revokeObjectURL(workerUrl);
|
||
} catch (error) {
|
||
displayResult('worker-results', `❌ Web Worker creation failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function sendWorkerMessage() {
|
||
try {
|
||
if (!workers.web) {
|
||
displayResult('worker-results', '❌ No Web Worker available', 'error');
|
||
return;
|
||
}
|
||
|
||
const randomNumber = Math.floor(Math.random() * 100);
|
||
workers.web.postMessage(randomNumber);
|
||
displayResult('worker-results', `📤 Sent to worker: ${randomNumber}`, 'success');
|
||
} catch (error) {
|
||
displayResult('worker-results', `❌ Send worker message failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function terminateWorker() {
|
||
try {
|
||
if (workers.web) {
|
||
workers.web.terminate();
|
||
workers.web = null;
|
||
displayResult('worker-results', '✅ Web Worker terminated', 'success');
|
||
} else {
|
||
displayResult('worker-results', '❌ No Web Worker to terminate', 'error');
|
||
}
|
||
} catch (error) {
|
||
displayResult('worker-results', `❌ Worker termination failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function registerServiceWorker() {
|
||
try {
|
||
if (!apiManager?.worker || !('serviceWorker' in navigator)) {
|
||
displayResult('worker-results', '❌ Service Worker not supported', 'error');
|
||
return;
|
||
}
|
||
|
||
navigator.serviceWorker.register('/sw.js')
|
||
.then(registration => {
|
||
displayResult('worker-results', '✅ Service Worker registered successfully', 'success');
|
||
workers.service = registration;
|
||
})
|
||
.catch(error => {
|
||
displayResult('worker-results', `❌ Service Worker registration failed: ${error.message}`, 'error');
|
||
});
|
||
} catch (error) {
|
||
displayResult('worker-results', `❌ Service Worker registration error: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function updateServiceWorker() {
|
||
try {
|
||
if (workers.service) {
|
||
workers.service.update().then(() => {
|
||
displayResult('worker-results', '✅ Service Worker update check completed', 'success');
|
||
});
|
||
} else {
|
||
displayResult('worker-results', '❌ No Service Worker registration found', 'error');
|
||
}
|
||
} catch (error) {
|
||
displayResult('worker-results', `❌ Service Worker update failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function getServiceWorkerStatus() {
|
||
try {
|
||
if ('serviceWorker' in navigator) {
|
||
navigator.serviceWorker.getRegistrations().then(registrations => {
|
||
displayResult('worker-results', `📊 Service Workers: ${registrations.length} registered`, 'success');
|
||
registrations.forEach((reg, index) => {
|
||
displayResult('worker-results', `#${index + 1}: ${reg.scope}, state: ${reg.active?.state || 'inactive'}`);
|
||
});
|
||
});
|
||
} else {
|
||
displayResult('worker-results', '❌ Service Worker not supported', 'error');
|
||
}
|
||
} catch (error) {
|
||
displayResult('worker-results', `❌ Service Worker status failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
// Performance API Functions
|
||
function measurePerformance() {
|
||
try {
|
||
if (!apiManager?.performance) {
|
||
displayResult('performance-results', '❌ Performance API not available', 'error');
|
||
return;
|
||
}
|
||
|
||
const startTime = performance.now();
|
||
|
||
// Simulate some work
|
||
let sum = 0;
|
||
for (let i = 0; i < 1000000; i++) {
|
||
sum += Math.sqrt(i);
|
||
}
|
||
|
||
const endTime = performance.now();
|
||
const duration = endTime - startTime;
|
||
|
||
displayResult('performance-results', `⚡ Performance: Operation took ${duration.toFixed(2)}ms`, 'success');
|
||
displayResult('performance-results', `📊 Result: ${sum.toFixed(2)}`);
|
||
} catch (error) {
|
||
displayResult('performance-results', `❌ Performance measurement failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function getPerformanceEntries() {
|
||
try {
|
||
const entries = performance.getEntriesByType('navigation');
|
||
if (entries.length > 0) {
|
||
const nav = entries[0];
|
||
displayResult('performance-results', `🚀 Navigation: ${nav.loadEventEnd - nav.navigationStart}ms total`, 'success');
|
||
displayResult('performance-results', `📡 DNS: ${nav.domainLookupEnd - nav.domainLookupStart}ms`);
|
||
displayResult('performance-results', `🔗 Connect: ${nav.connectEnd - nav.connectStart}ms`);
|
||
displayResult('performance-results', `📄 DOM: ${nav.domContentLoadedEventEnd - nav.domContentLoadedEventStart}ms`);
|
||
}
|
||
|
||
const resourceEntries = performance.getEntriesByType('resource').slice(0, 5);
|
||
displayResult('performance-results', `📚 Resources (first 5): ${resourceEntries.length} entries`);
|
||
resourceEntries.forEach(entry => {
|
||
displayResult('performance-results', `• ${entry.name.split('/').pop()}: ${entry.duration.toFixed(2)}ms`);
|
||
});
|
||
} catch (error) {
|
||
displayResult('performance-results', `❌ Performance entries failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function createPerformanceMark() {
|
||
try {
|
||
const markName = `api-test-mark-${Date.now()}`;
|
||
performance.mark(markName);
|
||
|
||
setTimeout(() => {
|
||
performance.mark(`${markName}-end`);
|
||
performance.measure(`${markName}-duration`, markName, `${markName}-end`);
|
||
|
||
const measure = performance.getEntriesByName(`${markName}-duration`)[0];
|
||
displayResult('performance-results', `🏷️ Performance Mark: ${measure.duration.toFixed(2)}ms`, 'success');
|
||
}, Math.random() * 1000);
|
||
|
||
displayResult('performance-results', `✅ Performance mark created: ${markName}`, 'success');
|
||
} catch (error) {
|
||
displayResult('performance-results', `❌ Performance mark failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function getResourceTiming() {
|
||
try {
|
||
const resources = performance.getEntriesByType('resource');
|
||
const totalResources = resources.length;
|
||
const totalTime = resources.reduce((sum, entry) => sum + entry.duration, 0);
|
||
const avgTime = totalTime / totalResources;
|
||
|
||
displayResult('performance-results', `📊 Resources: ${totalResources} total, ${avgTime.toFixed(2)}ms average`, 'success');
|
||
|
||
const slowResources = resources.filter(entry => entry.duration > 100);
|
||
if (slowResources.length > 0) {
|
||
displayResult('performance-results', `🐌 Slow resources (>100ms): ${slowResources.length}`);
|
||
slowResources.slice(0, 3).forEach(entry => {
|
||
displayResult('performance-results', `• ${entry.name.split('/').pop()}: ${entry.duration.toFixed(2)}ms`);
|
||
});
|
||
}
|
||
} catch (error) {
|
||
displayResult('performance-results', `❌ Resource timing failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function measureNavigationTiming() {
|
||
try {
|
||
const nav = performance.getEntriesByType('navigation')[0];
|
||
if (nav) {
|
||
const metrics = {
|
||
'DNS Lookup': nav.domainLookupEnd - nav.domainLookupStart,
|
||
'TCP Connect': nav.connectEnd - nav.connectStart,
|
||
'Request': nav.responseStart - nav.requestStart,
|
||
'Response': nav.responseEnd - nav.responseStart,
|
||
'DOM Processing': nav.domContentLoadedEventEnd - nav.responseEnd,
|
||
'Load Complete': nav.loadEventEnd - nav.loadEventStart
|
||
};
|
||
|
||
displayResult('performance-results', '🚀 Navigation Timing:', 'success');
|
||
Object.entries(metrics).forEach(([name, time]) => {
|
||
displayResult('performance-results', `• ${name}: ${time.toFixed(2)}ms`);
|
||
});
|
||
}
|
||
} catch (error) {
|
||
displayResult('performance-results', `❌ Navigation timing failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
function clearPerformanceData() {
|
||
try {
|
||
performance.clearMarks();
|
||
performance.clearMeasures();
|
||
displayResult('performance-results', '✅ Performance data cleared', 'success');
|
||
} catch (error) {
|
||
displayResult('performance-results', `❌ Clear performance data failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
// Comprehensive Test Functions
|
||
async function runFullAPITest() {
|
||
displayResult('comprehensive-results', '🧪 Starting comprehensive API test suite...', 'success');
|
||
|
||
const tests = [
|
||
{ name: 'API Manager', fn: () => getAPIManagerStatus() },
|
||
{ name: 'Intersection Observer', fn: () => createIntersectionObserver() },
|
||
{ name: 'Local Storage', fn: () => testLocalStorage() },
|
||
{ name: 'Performance Measurement', fn: () => measurePerformance() },
|
||
{ name: 'Network Info', fn: () => getNetworkInfo() },
|
||
{ name: 'Basic Animation', fn: () => createBasicAnimation() }
|
||
];
|
||
|
||
let passed = 0;
|
||
let failed = 0;
|
||
|
||
for (const test of tests) {
|
||
try {
|
||
await test.fn();
|
||
passed++;
|
||
displayResult('comprehensive-results', `✅ ${test.name}: PASSED`);
|
||
} catch (error) {
|
||
failed++;
|
||
displayResult('comprehensive-results', `❌ ${test.name}: FAILED - ${error.message}`, 'error');
|
||
}
|
||
|
||
// Small delay between tests
|
||
await new Promise(resolve => setTimeout(resolve, 100));
|
||
}
|
||
|
||
displayResult('comprehensive-results', `📊 Test Results: ${passed}/${tests.length} passed, ${failed} failed`, passed === tests.length ? 'success' : 'warning');
|
||
}
|
||
|
||
async function createIntegratedWorkflow() {
|
||
displayResult('comprehensive-results', '🔄 Creating integrated workflow...', 'success');
|
||
|
||
try {
|
||
// Step 1: Initialize core APIs
|
||
await initializeAPIManager();
|
||
displayResult('comprehensive-results', '1️⃣ API Manager initialized');
|
||
|
||
// Step 2: Set up observers
|
||
createIntersectionObserver();
|
||
createResizeObserver();
|
||
displayResult('comprehensive-results', '2️⃣ Observers configured');
|
||
|
||
// Step 3: Test storage capabilities
|
||
testLocalStorage();
|
||
testSessionStorage();
|
||
displayResult('comprehensive-results', '3️⃣ Storage tested');
|
||
|
||
// Step 4: Start monitoring
|
||
monitorConnection();
|
||
displayResult('comprehensive-results', '4️⃣ Monitoring started');
|
||
|
||
// Step 5: Create animations
|
||
createBasicAnimation();
|
||
displayResult('comprehensive-results', '5️⃣ Animations created');
|
||
|
||
displayResult('comprehensive-results', '✅ Integrated workflow completed successfully', 'success');
|
||
} catch (error) {
|
||
displayResult('comprehensive-results', `❌ Integrated workflow failed: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
async function generateAPIReport() {
|
||
displayResult('comprehensive-results', '📋 Generating comprehensive API report...', 'success');
|
||
|
||
const report = {
|
||
timestamp: new Date().toISOString(),
|
||
userAgent: navigator.userAgent,
|
||
apis: {}
|
||
};
|
||
|
||
// Test API availability
|
||
const apiTests = {
|
||
'Intersection Observer': () => 'IntersectionObserver' in window,
|
||
'Resize Observer': () => 'ResizeObserver' in window,
|
||
'Mutation Observer': () => 'MutationObserver' in window,
|
||
'getUserMedia': () => navigator.mediaDevices?.getUserMedia !== undefined,
|
||
'getDisplayMedia': () => navigator.mediaDevices?.getDisplayMedia !== undefined,
|
||
'IndexedDB': () => 'indexedDB' in window,
|
||
'Cache API': () => 'caches' in window,
|
||
'Service Worker': () => 'serviceWorker' in navigator,
|
||
'Web Worker': () => 'Worker' in window,
|
||
'Web Animations': () => 'animate' in document.createElement('div'),
|
||
'Geolocation': () => 'geolocation' in navigator,
|
||
'Vibration': () => 'vibrate' in navigator,
|
||
'Network Information': () => 'connection' in navigator,
|
||
'Performance API': () => 'performance' in window,
|
||
'Permissions API': () => 'permissions' in navigator
|
||
};
|
||
|
||
Object.entries(apiTests).forEach(([name, test]) => {
|
||
try {
|
||
report.apis[name] = test();
|
||
} catch (error) {
|
||
report.apis[name] = false;
|
||
}
|
||
});
|
||
|
||
// Display report
|
||
displayResult('comprehensive-results', '📊 API Support Report:', 'success');
|
||
displayResult('comprehensive-results', JSON.stringify(report, null, 2));
|
||
|
||
const supportedCount = Object.values(report.apis).filter(Boolean).length;
|
||
const totalCount = Object.keys(report.apis).length;
|
||
displayResult('comprehensive-results', `✅ Summary: ${supportedCount}/${totalCount} APIs supported (${Math.round(supportedCount/totalCount*100)}%)`, 'success');
|
||
}
|
||
</script> |