'use strict'; const { WTVShared } = require("./WTVShared.js"); class WTVMinifyingProxy { constructor(minisrv_config) { this.minisrv_config = minisrv_config; this.wtvshared = new WTVShared(this.minisrv_config); // HTML 3.0/4.0 compatible tags and attributes this.allowedTags = [ 'html', 'head', 'title', 'meta', 'body', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'br', 'hr', 'div', 'span', 'a', 'img', 'ul', 'ol', 'li', 'table', 'tr', 'td', 'th', 'tbody', 'thead', 'tfoot', 'form', 'input', 'textarea', 'select', 'option', 'button', 'b', 'i', 'u', 'strong', 'em', 'center', 'font', 'big', 'small', 'sub', 'sup', 'pre', 'code', 'blockquote', 'dl', 'dt', 'dd' ]; this.allowedAttributes = [ 'href', 'src', 'alt', 'title', 'width', 'height', 'border', 'align', 'valign', 'bgcolor', 'color', 'size', 'face', 'target', 'name', 'value', 'type', 'action', 'method', 'cols', 'rows', 'cellpadding', 'cellspacing', 'nowrap', // JellyScript event handlers 'onclick', 'onload', 'onunload', 'onsubmit', 'onreset', 'onfocus', 'onblur', 'onchange', 'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup' ]; // CSS properties to convert to HTML attributes this.cssToHtml = { 'text-align': 'align', 'vertical-align': 'valign', 'background-color': 'bgcolor', 'color': 'color', 'font-size': 'size', 'font-family': 'face' }; // JellyScript (WebTV JavaScript) supported features this.jellyScriptSupported = { // Core JavaScript objects and methods objects: ['window', 'document', 'history', 'location', 'navigator'], domMethods: ['getElementById', 'getElementsByTagName', 'getElementsByName'], windowMethods: ['alert', 'confirm', 'prompt', 'open', 'close', 'focus', 'blur'], historyMethods: ['back', 'forward', 'go'], documentMethods: ['write', 'writeln', 'open', 'close'], events: ['onclick', 'onload', 'onunload', 'onsubmit', 'onreset', 'onfocus', 'onblur', 'onchange'], // Basic JavaScript features features: ['var', 'function', 'if', 'else', 'for', 'while', 'switch', 'case', 'break', 'continue', 'return'] }; // Modern JavaScript features not supported by JellyScript this.unsupportedJSFeatures = [ // Modern ES6+ features 'const', 'let', 'arrow functions', '=>', 'class', 'extends', 'import', 'export', // Modern APIs 'fetch', 'Promise', 'async', 'await', 'XMLHttpRequest', 'addEventListener', // jQuery and libraries '\\$\\(', 'jQuery', 'angular', 'react', 'vue', 'prototype\\.js', // Modern DOM methods 'querySelector', 'querySelectorAll', 'classList', 'dataset' ]; } /** * Transform modern HTML to HTML 3.0/4.0 compatible version * @param {string} html - The HTML content to transform * @param {string} url - The original URL (for fixing relative links) * @returns {string} - Transformed HTML */ transformHtml(html, url = '') { try { let transformed = html; // Step 1: Clean up the HTML structure transformed = this.cleanHtml(transformed); // Step 2: Convert modern tags to compatible ones transformed = this.convertModernTags(transformed); // Step 3: Extract and convert CSS to HTML attributes transformed = this.convertCssToAttributes(transformed); // Step 4: Fix links and images transformed = this.fixUrls(transformed, url); // Step 5: Remove unsupported content transformed = this.removeUnsupportedContent(transformed); // Step 6: Minify and optimize transformed = this.minifyHtml(transformed); // Step 7: Return the processed content (structure will be handled by transformForWebTV) return transformed; } catch (err) { throw new Error(`HTML transformation failed: ${err.message}`); } } /** * Clean HTML by removing comments, normalizing whitespace */ cleanHtml(html) { return html // Remove HTML comments .replace(//g, '') // Remove CDATA sections .replace(//g, '') // Remove XML declarations .replace(/<\?xml[^>]*\?>/g, '') // Normalize whitespace .replace(/\s+/g, ' ') .trim(); } /** * Convert modern HTML5/CSS3 tags to HTML 3.0/4.0 compatible versions */ convertModernTags(html) { // Convert semantic HTML5 tags to divs with classes const semanticTags = { 'header': 'div', 'footer': 'div', 'nav': 'div', 'section': 'div', 'article': 'div', 'aside': 'div', 'main': 'div', 'figure': 'div', 'figcaption': 'div' }; Object.entries(semanticTags).forEach(([modern, classic]) => { // Opening tags html = html.replace(new RegExp(`<${modern}\\b([^>]*)>`, 'gi'), `<${classic}$1>`); // Closing tags html = html.replace(new RegExp(``, 'gi'), ``); }); return html; } /** * Extract CSS styles and convert them to HTML attributes where possible */ convertCssToAttributes(html) { // Remove