// @license © 2025 Google LLC. Licensed under the Apache License, Version 2.0. let featureDataPromise = null; export class WasmCompat extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }); this.featureId = this.getAttribute('wasm-feature'); this.hideHeader = this.hasAttribute('hide-header'); } static async fetchFeatureData() { if (!featureDataPromise) { const url = 'https://raw.githubusercontent.com/WebAssembly/website/main/features.json'; featureDataPromise = fetch(url) .then((response) => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }) .catch((error) => { console.error('Could not fetch Wasm feature data:', error); return null; }); } return featureDataPromise; } async connectedCallback() { this.renderLoading(); const { features, browsers } = await WasmCompat.fetchFeatureData(); if (features && this.featureId) { const feature = features[this.featureId]; if (feature) { this.render(feature, browsers); } else { this.renderError(`Feature "${this.featureId}" not found.`); } } else { this.renderError('Could not load WebAssembly feature data.'); } } renderLoading() { this.shadowRoot.innerHTML = `

Loading feature: ${this.featureId}

`; } renderError(message) { this.shadowRoot.innerHTML = `

${message}

`; } render(feature, browsers) { const browserSupport = browsers ? Object.entries(browsers) : []; this.shadowRoot.innerHTML = `
${Object.keys(browsers).map((engineName) => { const engine = browsers[engineName]; return ` `; }).join('')} ${browserSupport.length > 0 ? browserSupport.map(([, supportData]) => this.createCell(supportData.features[this.featureId])).join('') : this.createEmptyRow()}
${feature.description} (Phase ${feature.phase})
${engineName}
`; } createCell(version) { let remarks = ''; let versionNumber = version; if (Array.isArray(version)) { versionNumber = version[0] === 'flag' ? '' : version[0]; remarks = version[1]; remarks = remarks.replace(/`(.+?)`/, '$1'); } const isSupported = typeof versionNumber === 'string'; const versionText = isSupported ? `${versionNumber}${remarks ? ' ' + `
More
${remarks}
` : ''}` : 'N/A'; const supportClass = isSupported ? 'supported' : 'unsupported'; const supportIcon = isSupported ? `` : ``; return `
${supportIcon} ${versionText}
`; } createEmptyRow() { return ` No browser support information available for this feature. `; } getStyles() { return ` :host { display: block; margin-bottom: 2rem; } .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0; } .container { padding: 1.5rem; } .feature-title { text-align: left; font-size: 1.25rem; font-weight: 600; margin-bottom: 0.25rem; } .feature-title a { color: currentColor; text-decoration: none; } .feature-id { font-size: 0.875rem; margin-bottom: 1.5rem; } .feature-id code { padding: 0.2rem 0.4rem; border-radius: 0.25rem; font-family: monospace; } .table-wrapper { overflow-x: auto; } table { width: 100%; border-collapse: collapse; text-align: left; } th, td { padding: 0.75rem 1rem; } th { font-size: 0.75rem; font-weight: 600; letter-spacing: 0.05rem; } td { font-size: 0.95rem; } .engine-cell { display: flex; align-items: center; gap: 0.75rem; } .logo { width: auto; height: 24px; } .support-cell { white-space: pre; display: flex; align-items: center; gap: 0.5rem; font-weight: 500; } .version-info { display: flex; gap: 0.25rem; align-items: center; } .support-details { display: inline-block; font-size: 0.8rem; } .support-details summary { font-weight: bold; cursor: pointer; } .support-details div { white-space: normal; max-width: 40ch; } .support-details code { white-space: pre; } .supported { color: #10b981; } .unsupported { color: #ef4444; } .check-icon { color: #10b981; } .x-icon { color: #ef4444; } .empty-cell { text-align: center; color: #6b7280; padding: 2rem; } .loading-container, .error-container { display: flex; align-items: center; justify-content: center; padding: 2rem; color: #4b5563; gap: 0.75rem; } .error-container { color: #b91c1c; background-color: #fee2e2; } .spinner { width: 24px; height: 24px; border: 3px solid #e5e7eb; border-top-color: #3b82f6; border-radius: 50%; animation: spin 1s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } }`; } } customElements.define('wasm-compat', WasmCompat);