more proxy work

This commit is contained in:
zefie
2025-08-08 19:45:51 -04:00
parent 702aaab591
commit 9809fac251

View File

@@ -8,6 +8,7 @@ class WTVMinifyingProxy {
// HTML 3.0/4.0 compatible tags and attributes // HTML 3.0/4.0 compatible tags and attributes
this.allowedTags = [ this.allowedTags = [
'audioscope', 'bgsound', 'marquee', 'wtvchattranscript', 'wtvchat',
'html', 'head', 'title', 'meta', 'body', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'html', 'head', 'title', 'meta', 'body', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
'p', 'br', 'hr', 'div', 'span', 'a', 'img', 'ul', 'ol', 'li', 'table', 'tr', 'p', 'br', 'hr', 'div', 'span', 'a', 'img', 'ul', 'ol', 'li', 'table', 'tr',
'td', 'th', 'tbody', 'thead', 'tfoot', 'form', 'input', 'textarea', 'select', 'td', 'th', 'tbody', 'thead', 'tfoot', 'form', 'input', 'textarea', 'select',
@@ -16,6 +17,9 @@ class WTVMinifyingProxy {
]; ];
this.allowedAttributes = [ this.allowedAttributes = [
'leftcolor', 'rightcolor', 'maxlevel', 'leftoffset', 'rightoffset',
'host', 'port', 'channel', 'borderimage', 'font', 'nohighlight', 'autoactivate',
'text', 'cursor',
'href', 'src', 'alt', 'title', 'width', 'height', 'border', 'align', 'valign', 'href', 'src', 'alt', 'title', 'width', 'height', 'border', 'align', 'valign',
'bgcolor', 'color', 'size', 'face', 'target', 'name', 'value', 'type', 'action', 'bgcolor', 'color', 'size', 'face', 'target', 'name', 'value', 'type', 'action',
'method', 'cols', 'rows', 'cellpadding', 'cellspacing', 'nowrap', 'method', 'cols', 'rows', 'cellpadding', 'cellspacing', 'nowrap',
@@ -171,8 +175,8 @@ class WTVMinifyingProxy {
html = html.replace(/<(\w+)([^>]*)\s+style\s*=\s*["']([^"']+)["']([^>]*?)(\s*\/?)>/gi, (match, tagName, beforeStyle, styles, afterStyle, selfClosing) => { html = html.replace(/<(\w+)([^>]*)\s+style\s*=\s*["']([^"']+)["']([^>]*?)(\s*\/?)>/gi, (match, tagName, beforeStyle, styles, afterStyle, selfClosing) => {
const result = this.parseStyleToAttributes(styles, tagName); const result = this.parseStyleToAttributes(styles, tagName);
if (result.fontSize && !/^(input|select|textarea)$/i.test(tagName)) { if (result.fontSize) {
// For non-form elements with font-size, wrap in font tag // For all elements with font-size, wrap in font tag
const elementWithAttributes = result.attributes ? const elementWithAttributes = result.attributes ?
`<${tagName}${beforeStyle} ${result.attributes}${afterStyle}${selfClosing}>` : `<${tagName}${beforeStyle} ${result.attributes}${afterStyle}${selfClosing}>` :
`<${tagName}${beforeStyle}${afterStyle}${selfClosing}>`; `<${tagName}${beforeStyle}${afterStyle}${selfClosing}>`;
@@ -217,31 +221,62 @@ class WTVMinifyingProxy {
styles.forEach(style => { styles.forEach(style => {
const [property, value] = style.split(':').map(s => s.trim()); const [property, value] = style.split(':').map(s => s.trim());
if (property && value && this.cssToHtml[property]) { if (property && value) {
let htmlValue = value; let htmlValue = value;
// Convert CSS values to HTML equivalents // Convert CSS values to HTML equivalents
if (property === 'font-size') { if (property === 'font-size') {
htmlValue = this.convertFontSize(value); htmlValue = this.convertFontSize(value);
// Handle font-size differently for form vs non-form elements // For all elements, font-size should use font tag wrapping, not size attribute
const isFormElement = /^(input|select|textarea)$/i.test(elementTag);
if (isFormElement) {
// For form elements, add size attribute directly
attributes.push(`${this.cssToHtml[property]}="${htmlValue}"`);
} else {
// For non-form elements, store font size for font tag wrapping
fontSize = htmlValue; fontSize = htmlValue;
return;
} else if (property === 'color') {
htmlValue = this.convertColor(value);
// WebTV-specific color handling based on element type
if (/^(input|textarea)$/i.test(elementTag)) {
// Forms support both 'color' and 'text' attributes for text color
attributes.push(`text="${htmlValue}"`);
} else if (/^(button)$/i.test(elementTag)) {
// Buttons support 'text' attribute but not bgcolor
attributes.push(`text="${htmlValue}"`);
} else if (/^(select)$/i.test(elementTag)) {
// Select menus don't support color attributes - skip
return;
} else {
// Other elements use standard 'color' attribute
attributes.push(`color="${htmlValue}"`);
} }
return; return;
} else if (property === 'color' || property === 'background-color') { } else if (property === 'background-color') {
htmlValue = this.convertColor(value); htmlValue = this.convertColor(value);
} else if (property === 'width' || property === 'height') {
// WebTV-specific background color handling
if (/^(input|textarea)$/i.test(elementTag)) {
// Forms support bgcolor attribute
attributes.push(`bgcolor="${htmlValue}"`);
} else if (/^(button|select)$/i.test(elementTag)) {
// Buttons and select menus don't support bgcolor - skip
return;
} else {
// Other elements use standard bgcolor
attributes.push(`bgcolor="${htmlValue}"`);
}
return;
} else if (property === 'caret-color' && /^(input|textarea)$/i.test(elementTag)) {
// WebTV cursor attribute for text cursor color in forms
htmlValue = this.convertColor(value);
attributes.push(`cursor="${htmlValue}"`);
return;
} else if (this.cssToHtml[property]) {
// Handle other standard CSS-to-HTML conversions
if (property === 'width' || property === 'height') {
htmlValue = this.convertDimension(value); htmlValue = this.convertDimension(value);
} }
attributes.push(`${this.cssToHtml[property]}="${htmlValue}"`); attributes.push(`${this.cssToHtml[property]}="${htmlValue}"`);
} }
}
}); });
return { return {
@@ -266,42 +301,30 @@ class WTVMinifyingProxy {
if (attributes) { if (attributes) {
let newTagContent = tagContent; let newTagContent = tagContent;
// Special handling for input elements: WebTV prioritizes size over width // Special handling for input elements: WebTV prioritizes width over size
if (attributes.includes('width=')) { if (attributes.includes('width=')) {
const existingSize = tagContent.match(/size\s*=\s*["']?(\d+)["']?/i); const existingSize = tagContent.match(/size\s*=\s*["']?(\d+)["']?/i);
const widthMatch = attributes.match(/width="(\d+)"/); const widthMatch = attributes.match(/width="(\d+)"/);
if (existingSize && widthMatch) { if (existingSize && widthMatch) {
// Both size and width exist - for WebTV, recalculate size based on CSS width // Both size and width exist - for WebTV, prioritize width, remove size
const cssWidth = parseInt(widthMatch[1]); newTagContent = tagContent.replace(/\s*size\s*=\s*["']?\d+["']?/i, '');
const currentSize = parseInt(existingSize[1]);
// Use larger of the two for better WebTV compatibility
const betterSize = Math.max(Math.round(cssWidth / 8), currentSize);
// Replace the existing size with the better calculated size // Add all attributes including width
newTagContent = tagContent.replace(/size\s*=\s*["']?\d+["']?/i, `size="${betterSize}"`); newTagContent = `${newTagContent} ${attributes}`;
// Add other attributes except width
const filteredAttributes = attributes.replace(/width="[^"]*"/, '').trim();
if (filteredAttributes) {
newTagContent = `${newTagContent} ${filteredAttributes}`;
}
} else if (existingSize) { } else if (existingSize) {
// If input already has size attribute, don't add width // If input already has size attribute, replace with width if available
const filteredAttributes = attributes.replace(/\s*width="[^"]*"/, ''); newTagContent = tagContent.replace(/\s*size\s*=\s*["']?\d+["']?/i, '');
newTagContent = `${tagContent} ${filteredAttributes}`; newTagContent = `${newTagContent} ${attributes}`;
} else { } else {
// Convert width to size if no existing size // Add width and other attributes normally
const pixelWidth = parseInt(widthMatch[1]); newTagContent = `${tagContent} ${attributes}`;
// Rough conversion: ~8-10 pixels per character for WebTV
const charSize = Math.round(pixelWidth / 8);
const sizeAttr = `size="${Math.min(charSize, 80)}"`; // Cap at 80 chars
const otherAttributes = attributes.replace(/width="[^"]*"/, '').trim();
const finalAttributes = otherAttributes ? `${sizeAttr} ${otherAttributes}` : sizeAttr;
newTagContent = `${tagContent} ${finalAttributes}`;
} }
} else if (attributes.includes('size=')) {
// Only font-size, no width - use size attribute
newTagContent = `${tagContent} ${attributes}`;
} else { } else {
// No width attribute, add all attributes normally // No width or size attributes, add all attributes normally
newTagContent = `${tagContent} ${attributes}`; newTagContent = `${tagContent} ${attributes}`;
} }
@@ -313,8 +336,19 @@ class WTVMinifyingProxy {
// Also handle non-input elements with this class // Also handle non-input elements with this class
const generalRegex = new RegExp(`<((?!input)[^>]+class\\s*=\\s*["'][^"']*\\b${className}\\b[^"']*["'][^>]*)>`, 'gi'); const generalRegex = new RegExp(`<((?!input)[^>]+class\\s*=\\s*["'][^"']*\\b${className}\\b[^"']*["'][^>]*)>`, 'gi');
html = html.replace(generalRegex, (match, tagContent) => { html = html.replace(generalRegex, (match, tagContent) => {
const attributes = this.parseStyleToAttributes(styles); // Extract tag name to properly handle attributes
if (attributes) { const tagMatch = tagContent.match(/^(\w+)/);
const tagName = tagMatch ? tagMatch[1] : '';
const result = this.parseStyleToAttributes(styles, tagName);
const attributes = result.attributes || '';
if (result.fontSize) {
// Wrap all elements with font-size in font tags
const elementWithAttributes = attributes ?
`<${tagContent} ${attributes}>` : `<${tagContent}>`;
return `<font size="${result.fontSize}">${elementWithAttributes}`;
} else if (attributes) {
return `<${tagContent} ${attributes}>`; return `<${tagContent} ${attributes}>`;
} }
return match; return match;
@@ -443,6 +477,9 @@ class WTVMinifyingProxy {
return `<input ${attributes}>`; return `<input ${attributes}>`;
}); });
// Add WebTV-specific enhancements for better layout
html = this.addWebTVLayoutEnhancements(html);
// Fix submit buttons to have better sizing for WebTV // Fix submit buttons to have better sizing for WebTV
html = html.replace(/<input([^>]*type="submit"[^>]*)>/gi, (match, attributes) => { html = html.replace(/<input([^>]*type="submit"[^>]*)>/gi, (match, attributes) => {
// Add width if not present to make buttons more visible // Add width if not present to make buttons more visible
@@ -794,6 +831,86 @@ ${bodyContent}
return `<img${attrs}>`; return `<img${attrs}>`;
}); });
} }
/**
* Add WebTV-specific layout enhancements
*/
addWebTVLayoutEnhancements(html) {
// Ensure input elements have minimum sizing for WebTV visibility
html = html.replace(/<input([^>]*type=["']?text["']?[^>]*)>/gi, (match, attributes) => {
let newAttributes = attributes;
// Check if width exists and ensure it's reasonable for WebTV
const widthMatch = attributes.match(/width\s*=\s*["']?(\d+)["']?/);
if (widthMatch) {
let width = parseInt(widthMatch[1]);
// Ensure minimum width of 200px for text inputs on WebTV
if (width < 200) {
newAttributes = attributes.replace(/width\s*=\s*["']?\d+["']?/, `width="200"`);
}
// Cap maximum width at 400px for WebTV compatibility
else if (width > 400) {
newAttributes = attributes.replace(/width\s*=\s*["']?\d+["']?/, `width="400"`);
}
} else {
// Add default width if none exists
newAttributes += ` width="250"`;
}
// Ensure minimum height for better visibility
if (!attributes.includes('height=')) {
newAttributes += ` height="25"`;
}
return `<input ${newAttributes}>`;
});
// Enhance table layouts for better WebTV rendering
html = html.replace(/<table([^>]*)>/gi, (match, attributes) => {
let newAttributes = attributes;
// Ensure tables have explicit widths for WebTV
if (!attributes.includes('width=')) {
newAttributes += ` width="100%"`;
}
// Add cellpadding and cellspacing if not present
if (!attributes.includes('cellpadding=')) {
newAttributes += ` cellpadding="4"`;
}
if (!attributes.includes('cellspacing=')) {
newAttributes += ` cellspacing="2"`;
}
return `<table${newAttributes}>`;
});
// Don't auto-wrap forms in tables - enhance existing structure instead
// The original HTML likely already has proper table structure
// Ensure submit buttons have minimum height for WebTV
html = html.replace(/<input([^>]*type=["']?submit["']?[^>]*)>/gi, (match, attributes) => {
let newAttributes = attributes;
// Ensure minimum height for buttons
if (!attributes.includes('height=')) {
newAttributes += ` height="30"`;
}
// Ensure minimum width for buttons
const widthMatch = attributes.match(/width\s*=\s*["']?(\d+)["']?/);
if (widthMatch) {
let width = parseInt(widthMatch[1]);
if (width < 80) {
newAttributes = attributes.replace(/width\s*=\s*["']?\d+["']?/, `width="80"`);
}
}
return `<input ${newAttributes}>`;
});
return html;
}
} }
module.exports = WTVMinifyingProxy; module.exports = WTVMinifyingProxy;