Fix overlay templates not showing data in web player

- Created overlay-web-adapter.js to bridge postMessage to WebChannel interface
- Updated all web templates to use conditional Qt script loading
- Updated all Qt player templates with same pattern for consistency
- Prevents browser console errors from Qt URL schemes (qrc:///, overlay://)
- Templates now work in both Qt player and web player environments
parent c9a35c3d
......@@ -29,3 +29,6 @@ there are 2 differenc interfaces.
- Web dashboard: this is the application administration interface plus the interface for cashier, it is in mbetterclient/web_dashboard/, and it is server directly by the application, and eventually proxied by nginx. admin, users, and cashier all uses the web dashboard, with some differences for admin, users or cashier.
- Wensite: this is the public website used when running in --headless mode, it serves brokers and players users. it is served directly by nginx and uses the API from our application.
At the end of every task in a tasklist or modifications you do, commit and push to git the changes
......@@ -3,6 +3,14 @@
<head>
<meta charset="utf-8">
<title>MBetther system</title>
<!-- Web adapter for browser compatibility - detects environment and loads appropriate scripts -->
<script src="/js/overlay-web-adapter.js"></script>
<script>
// Conditionally load Qt scripts only in Qt environment
if (window.loadQtScripts) {
window.loadQtScripts();
}
</script>
<style>
* {
margin: 0;
......@@ -459,8 +467,8 @@
<!--
IMPORTANT: When creating or editing custom templates, always maintain these script tags:
1. qrc:///qtwebchannel/qwebchannel.js - Required for Qt WebChannel communication
2. overlay://overlay.js - Required for overlay functionality and data updates
1. /js/overlay-web-adapter.js - Required for web browser compatibility (bridges postMessage to WebChannel interface)
2. Qt scripts are loaded conditionally via window.loadQtScripts() in Qt environment only
These scripts enable communication between the Qt application and the overlay template.
Without them, the template will not receive data updates or function properly.
......@@ -468,7 +476,5 @@
NOTE: When editing this template or creating new ones, never remove these script sources!
The overlay:// custom scheme ensures JavaScript files work for both built-in and uploaded templates.
-->
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
<script src="overlay://overlay.js"></script>
</body>
</html>
......@@ -3,7 +3,15 @@
<head>
<meta charset="utf-8">
<title>Fixtures Overlay</title>
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
<!-- Web adapter for browser compatibility - detects environment and loads appropriate scripts -->
<script src="/js/overlay-web-adapter.js"></script>
<script>
// Conditionally load Qt scripts only in Qt environment
// In web browser, this does nothing (web adapter handles communication)
if (window.loadQtScripts) {
window.loadQtScripts();
}
</script>
<style>
* {
margin: 0;
......
......@@ -3,7 +3,14 @@
<head>
<meta charset="utf-8">
<title>Match Overlay</title>
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
<!-- Web adapter for browser compatibility - must be loaded first -->
<script src="/js/overlay-web-adapter.js"></script>
<script>
// Conditionally load Qt scripts only in Qt environment
if (window.loadQtScripts) {
window.loadQtScripts();
}
</script>
<style>
* {
margin: 0;
......@@ -1390,9 +1397,9 @@
</script>
<!--
IMPORTANT: When creating or editing custom templates, always maintain these two script tags:
1. qrc:///qtwebchannel/qwebchannel.js - Required for Qt WebChannel communication
2. overlay://overlay.js - Required for overlay functionality and data updates
IMPORTANT: When creating or editing custom templates, always maintain these script tags:
1. /js/overlay-web-adapter.js - Required for web browser compatibility (bridges postMessage to WebChannel interface)
2. Qt scripts are loaded conditionally via window.loadQtScripts() - only in Qt environment
These scripts enable communication between the Qt application and the overlay template.
Without them, the template will not receive data updates or function properly.
......@@ -1400,7 +1407,5 @@
NOTE: When editing this template or creating new ones, never remove these script sources!
The overlay:// custom scheme ensures JavaScript files work for both built-in and uploaded templates.
-->
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
<script src="overlay://overlay.js"></script>
</body>
</html>
\ No newline at end of file
......@@ -3,6 +3,14 @@
<head>
<meta charset="utf-8">
<title>Match Video Overlay</title>
<!-- Web adapter for browser compatibility - detects environment and loads appropriate scripts -->
<script src="/js/overlay-web-adapter.js"></script>
<script>
// Conditionally load Qt scripts only in Qt environment
if (window.loadQtScripts) {
window.loadQtScripts();
}
</script>
<style>
* {
margin: 0;
......@@ -666,8 +674,16 @@
});
</script>
<!-- Required scripts for Qt WebChannel communication -->
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
<script src="overlay://overlay.js"></script>
<!--
IMPORTANT: When creating or editing custom templates, always maintain these script tags:
1. /js/overlay-web-adapter.js - Required for web browser compatibility (bridges postMessage to WebChannel interface)
2. Qt scripts are loaded conditionally via window.loadQtScripts() in Qt environment only
These scripts enable communication between the Qt application and the overlay template.
Without them, the template will not receive data updates or function properly.
NOTE: When editing this template or creating new ones, never remove these script sources!
The overlay:// custom scheme ensures JavaScript files work for both built-in and uploaded templates.
-->
</body>
</html>
\ No newline at end of file
......@@ -3,7 +3,14 @@
<head>
<meta charset="utf-8">
<title>Results Overlay</title>
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
<!-- Web adapter for browser compatibility - must be loaded first -->
<script src="/js/overlay-web-adapter.js"></script>
<script>
// Conditionally load Qt scripts only in Qt environment
if (window.loadQtScripts) {
window.loadQtScripts();
}
</script>
<style>
* {
margin: 0;
......@@ -1305,14 +1312,15 @@
</script>
<!--
IMPORTANT: When creating or editing custom templates, always maintain this script tag:
1. qrc:///qtwebchannel/qwebchannel.js - Required for Qt WebChannel communication
IMPORTANT: When creating or editing custom templates, always maintain these script tags:
1. /js/overlay-web-adapter.js - Required for web browser compatibility (bridges postMessage to WebChannel interface)
2. Qt scripts are loaded conditionally via window.loadQtScripts() - only in Qt environment
This script enables communication between the Qt application and the overlay template.
The results.html template uses inline WebChannel setup with client-initiated data fetching,
similar to fixtures.html, and includes custom dataUpdated signal handling.
NOTE: When editing this template, never remove the qrc:///qtwebchannel/qwebchannel.js script source!
NOTE: When editing this template, never remove the web adapter script source!
The template implements its own WebChannel setup and data polling mechanism.
-->
</body>
......
......@@ -459,8 +459,8 @@
<!--
IMPORTANT: When creating or editing custom templates, always maintain these script tags:
1. qrc:///qtwebchannel/qwebchannel.js - Required for Qt WebChannel communication
2. overlay://overlay.js - Required for overlay functionality and data updates
1. /js/overlay-web-adapter.js - Required for web browser compatibility (bridges postMessage to WebChannel interface)
2. Qt scripts are loaded conditionally via window.loadQtScripts() - only in Qt environment
These scripts enable communication between the Qt application and the overlay template.
Without them, the template will not receive data updates or function properly.
......@@ -468,7 +468,12 @@
NOTE: When editing this template or creating new ones, never remove these script sources!
The overlay:// custom scheme ensures JavaScript files work for both built-in and uploaded templates.
-->
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
<script src="overlay://overlay.js"></script>
<script src="/js/overlay-web-adapter.js"></script>
<script>
// Conditionally load Qt scripts only in Qt environment
if (window.loadQtScripts) {
window.loadQtScripts();
}
</script>
</body>
</html>
\ No newline at end of file
/**
* Web Overlay Adapter for MbetterClient
*
* This script bridges the gap between Qt WebChannel and Web postMessage communication.
* It provides a compatible interface for overlay templates when running in a web browser
* instead of the Qt player.
*
* When included in a template, it will:
* 1. Detect if running in Qt WebChannel or Web environment
* 2. Set up appropriate communication channels
* 3. Provide a unified interface for data access
* 4. Prevent Qt-specific script loading errors in web browser
*
* Usage: Include this script as the FIRST script in templates:
* <script src="/js/overlay-web-adapter.js"></script>
* <script>
* // Conditionally load Qt scripts only in Qt environment
* if (window.loadQtScripts) {
* window.loadQtScripts();
* }
* </script>
*/
(function() {
console.log('[WebAdapter] Loading overlay web adapter...');
// Check if we're running in Qt WebChannel environment
const isQtEnvironment = typeof qt !== 'undefined' && qt.webChannelTransport;
console.log('[WebAdapter] Environment detected:', isQtEnvironment ? 'Qt WebChannel' : 'Web Browser');
// Provide function to load Qt scripts dynamically (only works in Qt environment)
window.loadQtScripts = function() {
if (!isQtEnvironment) {
console.log('[WebAdapter] Not loading Qt scripts - not in Qt environment');
return false;
}
console.log('[WebAdapter] Loading Qt scripts for Qt environment');
// Load qwebchannel.js
const qwebchannelScript = document.createElement('script');
qwebchannelScript.src = 'qrc:///qtwebchannel/qwebchannel.js';
document.head.appendChild(qwebchannelScript);
// Load overlay.js
const overlayScript = document.createElement('script');
overlayScript.src = 'overlay://overlay.js';
document.head.appendChild(overlayScript);
return true;
};
// If already in Qt environment, load Qt scripts and exit
if (isQtEnvironment) {
console.log('[WebAdapter] Qt environment detected, loading Qt scripts');
// Load Qt scripts when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', window.loadQtScripts);
} else {
window.loadQtScripts();
}
return;
}
// Web environment - prevent Qt script loading errors
// Create dummy QWebChannel for browsers (prevents errors from qrc:/// URLs)
window.QWebChannel = function(transport, callback) {
console.log('[WebAdapter] QWebChannel called in web environment - using adapter instead');
// The adapter already set up window.overlay, so just call callback if provided
if (callback && window.overlay) {
callback({ objects: { overlay: window.overlay } });
}
};
// Global error handler to suppress Qt URL scheme errors
window.addEventListener('error', function(event) {
if (event.target && (event.target.tagName === 'SCRIPT' || event.target.tagName === 'LINK')) {
const src = event.target.src || event.target.href || '';
if (src.startsWith('qrc:///') || src.startsWith('overlay://')) {
console.log('[WebAdapter] Suppressed Qt URL error:', src);
event.preventDefault();
event.stopPropagation();
return true;
}
}
}, true); // Use capture phase to catch errors early
// Also handle uncaught errors from Qt URL fetches
window.addEventListener('unhandledrejection', function(event) {
if (event.reason && event.reason.message) {
const msg = event.reason.message;
if (msg.includes('qrc:///') || msg.includes('overlay://')) {
console.log('[WebAdapter] Suppressed Qt URL promise rejection:', msg);
event.preventDefault();
return true;
}
}
});
// Prevent fetch/XMLHttpRequest errors for Qt URLs
const originalFetch = window.fetch;
window.fetch = function(url, options) {
if (typeof url === 'string' && (url.startsWith('qrc:///') || url.startsWith('overlay://'))) {
console.log('[WebAdapter] Blocking fetch to Qt URL:', url);
return Promise.resolve(new Response('', { status: 200 }));
}
return originalFetch.apply(this, arguments);
};
// Override XMLHttpRequest to prevent Qt URL errors
const originalOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url) {
if (typeof url === 'string' && (url.startsWith('qrc:///') || url.startsWith('overlay://'))) {
console.log('[WebAdapter] Blocking XHR to Qt URL:', url);
// Return without actually opening - will fail silently
return;
}
return originalOpen.apply(this, arguments);
};
// Override document.write to prevent Qt scripts from being written
const originalWrite = document.write;
document.write = function(content) {
if (typeof content === 'string' && (content.includes('qrc:///') || content.includes('overlay://'))) {
console.log('[WebAdapter] Blocking document.write with Qt URLs');
return;
}
return originalWrite.apply(this, arguments);
};
// Web environment - set up postMessage bridge
console.log('[WebAdapter] Setting up postMessage bridge for web environment');
// Data storage
let fixtureData = null;
let timerState = { running: false, remaining_seconds: 0 };
let licenseText = '';
let completedMatches = [];
let overlayData = {};
let winningOutcomes = [];
// Signal emulation - simple event system
class Signal {
constructor() {
this.callbacks = [];
}
connect(callback) {
this.callbacks.push(callback);
}
disconnect(callback) {
const index = this.callbacks.indexOf(callback);
if (index > -1) {
this.callbacks.splice(index, 1);
}
}
emit(data) {
this.callbacks.forEach(callback => {
try {
callback(data);
} catch (e) {
console.error('[WebAdapter] Signal callback error:', e);
}
});
}
}
// Create overlay object that mimics Qt WebChannel interface
window.overlay = {
// Signals
dataUpdated: new Signal(),
// Log function
log: function(message) {
console.log('[Overlay]', message);
},
// Get fixture data - returns Promise (async like Qt)
getFixtureData: function() {
return new Promise((resolve) => {
if (fixtureData) {
resolve(JSON.stringify(fixtureData));
} else {
// Request data from parent
requestFixtureData();
// Wait for response with timeout
let attempts = 0;
const checkData = setInterval(() => {
attempts++;
if (fixtureData) {
clearInterval(checkData);
resolve(JSON.stringify(fixtureData));
} else if (attempts > 50) {
clearInterval(checkData);
resolve(JSON.stringify([]));
}
}, 100);
}
});
},
// Get timer state - returns Promise
getTimerState: function() {
return new Promise((resolve) => {
resolve(JSON.stringify(timerState));
});
},
// Get license text - returns Promise
getLicenseText: function() {
return new Promise((resolve) => {
if (licenseText) {
resolve(JSON.stringify({ license_text: licenseText }));
} else {
// Request data from parent
requestLicenseText();
let attempts = 0;
const checkData = setInterval(() => {
attempts++;
if (licenseText) {
clearInterval(checkData);
resolve(JSON.stringify({ license_text: licenseText }));
} else if (attempts > 50) {
clearInterval(checkData);
resolve(JSON.stringify({ license_text: '' }));
}
}, 100);
}
});
},
// Get completed matches - returns Promise
getCompletedMatches: function() {
return new Promise((resolve) => {
if (completedMatches && completedMatches.length > 0) {
resolve(JSON.stringify(completedMatches));
} else {
// Request data from parent
requestCompletedMatches();
let attempts = 0;
const checkData = setInterval(() => {
attempts++;
if (completedMatches && completedMatches.length > 0) {
clearInterval(checkData);
resolve(JSON.stringify(completedMatches));
} else if (attempts > 50) {
clearInterval(checkData);
resolve(JSON.stringify([]));
}
}, 100);
}
});
},
// Get winning outcomes - returns Promise
getWinningOutcomes: function(matchId) {
return new Promise((resolve) => {
// Request data from parent
requestWinningOutcomes(matchId);
let attempts = 0;
const checkData = setInterval(() => {
attempts++;
if (winningOutcomes && winningOutcomes.length > 0) {
clearInterval(checkData);
resolve(JSON.stringify(winningOutcomes));
} else if (attempts > 30) {
clearInterval(checkData);
resolve(JSON.stringify([]));
}
}, 100);
});
}
};
// Request functions - send postMessage to parent
function requestFixtureData() {
if (window.parent !== window) {
window.parent.postMessage({ type: 'requestFixtureData' }, '*');
}
}
function requestTimerState() {
if (window.parent !== window) {
window.parent.postMessage({ type: 'requestTimerState' }, '*');
}
}
function requestLicenseText() {
if (window.parent !== window) {
window.parent.postMessage({ type: 'requestLicenseText' }, '*');
}
}
function requestCompletedMatches() {
if (window.parent !== window) {
window.parent.postMessage({ type: 'requestCompletedMatches' }, '*');
}
}
function requestWinningOutcomes(matchId) {
if (window.parent !== window) {
window.parent.postMessage({ type: 'requestWinningOutcomes', matchId: matchId }, '*');
}
}
// Listen for messages from parent window
window.addEventListener('message', function(event) {
// Accept messages from same origin or any origin (for iframe embedding)
const message = event.data;
if (!message || typeof message !== 'object') {
return;
}
console.log('[WebAdapter] Received message:', message.type);
switch (message.type) {
case 'fixtureData':
fixtureData = message.fixtures || [];
console.log('[WebAdapter] Fixture data updated:', fixtureData.length, 'fixtures');
break;
case 'timerState':
timerState = message.running !== undefined ? {
running: message.running,
remaining_seconds: message.remaining_seconds || 0
} : message;
console.log('[WebAdapter] Timer state updated:', timerState);
// Emit signal for templates listening to dataUpdated
window.overlay.dataUpdated.emit({ timer_update: timerState });
break;
case 'timerUpdate':
timerState = message.running !== undefined ? {
running: message.running,
remaining_seconds: message.remaining_seconds || 0
} : message;
console.log('[WebAdapter] Timer update received:', timerState);
window.overlay.dataUpdated.emit({ timer_update: timerState });
break;
case 'licenseText':
licenseText = message.licenseText || '';
console.log('[WebAdapter] License text updated');
break;
case 'completedMatches':
completedMatches = message.matches || [];
console.log('[WebAdapter] Completed matches updated:', completedMatches.length, 'matches');
break;
case 'winningOutcomes':
winningOutcomes = message.outcomes || [];
console.log('[WebAdapter] Winning outcomes updated:', winningOutcomes.length, 'outcomes');
break;
case 'initialData':
// Initial data bundle sent when template loads
if (message.overlayData) {
overlayData = message.overlayData;
}
if (message.timerState) {
timerState = message.timerState;
}
if (message.licenseText) {
licenseText = message.licenseText;
}
console.log('[WebAdapter] Initial data received');
// Emit signal for templates
window.overlay.dataUpdated.emit(overlayData);
break;
case 'dataUpdated':
// Overlay data update
overlayData = { ...overlayData, ...message };
console.log('[WebAdapter] Overlay data updated:', Object.keys(message));
// Emit signal for templates
window.overlay.dataUpdated.emit(message);
break;
case 'matchUpdate':
console.log('[WebAdapter] Match update received');
window.overlay.dataUpdated.emit({ match_update: message });
break;
case 'resultUpdate':
console.log('[WebAdapter] Result update received');
window.overlay.dataUpdated.emit({ result_update: message });
break;
case 'videoStateChanged':
console.log('[WebAdapter] Video state changed:', message.state);
break;
case 'streamStateChanged':
console.log('[WebAdapter] Stream state changed:', message.state);
break;
case 'positionChanged':
// Video position update - can be used for time-based overlays
break;
default:
console.log('[WebAdapter] Unknown message type:', message.type);
}
});
// Request initial data when DOM is ready
function requestInitialData() {
console.log('[WebAdapter] Requesting initial data from parent');
if (window.parent !== window) {
window.parent.postMessage({ type: 'requestInitialData' }, '*');
}
// Also request specific data
requestFixtureData();
requestTimerState();
requestLicenseText();
requestCompletedMatches();
}
// Wait for DOM to be ready, then request initial data
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
// Small delay to ensure parent is ready
setTimeout(requestInitialData, 100);
});
} else {
setTimeout(requestInitialData, 100);
}
console.log('[WebAdapter] Web overlay adapter initialized');
// Flush any buffered console logs
if (typeof window.flushConsoleBuffer === 'function') {
setTimeout(window.flushConsoleBuffer, 200);
}
})();
\ No newline at end of file
/**
* Web Overlay Adapter for MbetterClient
*
* This script bridges the gap between Qt WebChannel and Web postMessage communication.
* It provides a compatible interface for overlay templates when running in a web browser
* instead of the Qt player.
*
* When included in a template, it will:
* 1. Detect if running in Qt WebChannel or Web environment
* 2. Set up appropriate communication channels
* 3. Provide a unified interface for data access
* 4. Prevent Qt-specific script loading errors in web browser
*
* Usage: Include this script BEFORE the Qt WebChannel scripts in templates:
* <script src="/js/overlay-web-adapter.js"></script>
* <script src="qrc:///qtwebchannel/qwebchannel.js"></script>
* <script src="overlay://overlay.js"></script>
*/
(function() {
console.log('[WebAdapter] Loading overlay web adapter...');
// Check if we're running in Qt WebChannel environment
const isQtEnvironment = typeof qt !== 'undefined' && qt.webChannelTransport;
console.log('[WebAdapter] Environment detected:', isQtEnvironment ? 'Qt WebChannel' : 'Web Browser');
// If already in Qt environment, no need for adapter
if (isQtEnvironment) {
console.log('[WebAdapter] Qt environment detected, adapter not needed');
return;
}
// Web environment - prevent Qt script loading errors
// Create dummy QWebChannel for browsers (prevents errors from qrc:/// URLs)
window.QWebChannel = function(transport, callback) {
console.log('[WebAdapter] QWebChannel called in web environment - using adapter instead');
// The adapter already set up window.overlay, so just call callback if provided
if (callback && window.overlay) {
callback({ objects: { overlay: window.overlay } });
}
};
// Prevent fetch/XMLHttpRequest errors for Qt URLs
const originalFetch = window.fetch;
window.fetch = function(url, options) {
if (typeof url === 'string' && (url.startsWith('qrc:///') || url.startsWith('overlay://'))) {
console.log('[WebAdapter] Blocking fetch to Qt URL:', url);
return Promise.resolve(new Response('', { status: 200 }));
}
return originalFetch.apply(this, arguments);
};
// Override XMLHttpRequest to prevent Qt URL errors
const originalOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url) {
if (typeof url === 'string' && (url.startsWith('qrc:///') || url.startsWith('overlay://'))) {
console.log('[WebAdapter] Blocking XHR to Qt URL:', url);
// Return without actually opening - will fail silently
return;
}
return originalOpen.apply(this, arguments);
};
// Prevent script errors from Qt URLs by creating a MutationObserver
// that intercepts script elements with Qt URLs
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
mutation.addedNodes.forEach(function(node) {
if (node.tagName === 'SCRIPT') {
const src = node.getAttribute('src') || '';
if (src.startsWith('qrc:///') || src.startsWith('overlay://')) {
console.log('[WebAdapter] Preventing load of Qt script:', src);
node.removeAttribute('src');
// Set empty content to prevent further errors
node.textContent = '// Qt script blocked by web adapter';
}
}
});
});
});
// Start observing immediately
observer.observe(document.documentElement || document, {
childList: true,
subtree: true
});
// Web environment - set up postMessage bridge
console.log('[WebAdapter] Setting up postMessage bridge for web environment');
// Data storage
let fixtureData = null;
let timerState = { running: false, remaining_seconds: 0 };
let licenseText = '';
let completedMatches = [];
let overlayData = {};
let winningOutcomes = [];
// Signal emulation - simple event system
class Signal {
constructor() {
this.callbacks = [];
}
connect(callback) {
this.callbacks.push(callback);
}
disconnect(callback) {
const index = this.callbacks.indexOf(callback);
if (index > -1) {
this.callbacks.splice(index, 1);
}
}
emit(data) {
this.callbacks.forEach(callback => {
try {
callback(data);
} catch (e) {
console.error('[WebAdapter] Signal callback error:', e);
}
});
}
}
// Create overlay object that mimics Qt WebChannel interface
window.overlay = {
// Signals
dataUpdated: new Signal(),
// Log function
log: function(message) {
console.log('[Overlay]', message);
},
// Get fixture data - returns Promise (async like Qt)
getFixtureData: function() {
return new Promise((resolve) => {
if (fixtureData) {
resolve(JSON.stringify(fixtureData));
} else {
// Request data from parent
requestFixtureData();
// Wait for response with timeout
let attempts = 0;
const checkData = setInterval(() => {
attempts++;
if (fixtureData) {
clearInterval(checkData);
resolve(JSON.stringify(fixtureData));
} else if (attempts > 50) {
clearInterval(checkData);
resolve(JSON.stringify([]));
}
}, 100);
}
});
},
// Get timer state - returns Promise
getTimerState: function() {
return new Promise((resolve) => {
resolve(JSON.stringify(timerState));
});
},
// Get license text - returns Promise
getLicenseText: function() {
return new Promise((resolve) => {
if (licenseText) {
resolve(JSON.stringify({ license_text: licenseText }));
} else {
// Request data from parent
requestLicenseText();
let attempts = 0;
const checkData = setInterval(() => {
attempts++;
if (licenseText) {
clearInterval(checkData);
resolve(JSON.stringify({ license_text: licenseText }));
} else if (attempts > 50) {
clearInterval(checkData);
resolve(JSON.stringify({ license_text: '' }));
}
}, 100);
}
});
},
// Get completed matches - returns Promise
getCompletedMatches: function() {
return new Promise((resolve) => {
if (completedMatches && completedMatches.length > 0) {
resolve(JSON.stringify(completedMatches));
} else {
// Request data from parent
requestCompletedMatches();
let attempts = 0;
const checkData = setInterval(() => {
attempts++;
if (completedMatches && completedMatches.length > 0) {
clearInterval(checkData);
resolve(JSON.stringify(completedMatches));
} else if (attempts > 50) {
clearInterval(checkData);
resolve(JSON.stringify([]));
}
}, 100);
}
});
},
// Get winning outcomes - returns Promise
getWinningOutcomes: function(matchId) {
return new Promise((resolve) => {
// Request data from parent
requestWinningOutcomes(matchId);
let attempts = 0;
const checkData = setInterval(() => {
attempts++;
if (winningOutcomes && winningOutcomes.length > 0) {
clearInterval(checkData);
resolve(JSON.stringify(winningOutcomes));
} else if (attempts > 30) {
clearInterval(checkData);
resolve(JSON.stringify([]));
}
}, 100);
});
}
};
// Request functions - send postMessage to parent
function requestFixtureData() {
if (window.parent !== window) {
window.parent.postMessage({ type: 'requestFixtureData' }, '*');
}
}
function requestTimerState() {
if (window.parent !== window) {
window.parent.postMessage({ type: 'requestTimerState' }, '*');
}
}
function requestLicenseText() {
if (window.parent !== window) {
window.parent.postMessage({ type: 'requestLicenseText' }, '*');
}
}
function requestCompletedMatches() {
if (window.parent !== window) {
window.parent.postMessage({ type: 'requestCompletedMatches' }, '*');
}
}
function requestWinningOutcomes(matchId) {
if (window.parent !== window) {
window.parent.postMessage({ type: 'requestWinningOutcomes', matchId: matchId }, '*');
}
}
// Listen for messages from parent window
window.addEventListener('message', function(event) {
// Accept messages from same origin or any origin (for iframe embedding)
const message = event.data;
if (!message || typeof message !== 'object') {
return;
}
console.log('[WebAdapter] Received message:', message.type);
switch (message.type) {
case 'fixtureData':
fixtureData = message.fixtures || [];
console.log('[WebAdapter] Fixture data updated:', fixtureData.length, 'fixtures');
break;
case 'timerState':
timerState = message.running !== undefined ? {
running: message.running,
remaining_seconds: message.remaining_seconds || 0
} : message;
console.log('[WebAdapter] Timer state updated:', timerState);
// Emit signal for templates listening to dataUpdated
window.overlay.dataUpdated.emit({ timer_update: timerState });
break;
case 'timerUpdate':
timerState = message.running !== undefined ? {
running: message.running,
remaining_seconds: message.remaining_seconds || 0
} : message;
console.log('[WebAdapter] Timer update received:', timerState);
window.overlay.dataUpdated.emit({ timer_update: timerState });
break;
case 'licenseText':
licenseText = message.licenseText || '';
console.log('[WebAdapter] License text updated');
break;
case 'completedMatches':
completedMatches = message.matches || [];
console.log('[WebAdapter] Completed matches updated:', completedMatches.length, 'matches');
break;
case 'winningOutcomes':
winningOutcomes = message.outcomes || [];
console.log('[WebAdapter] Winning outcomes updated:', winningOutcomes.length, 'outcomes');
break;
case 'initialData':
// Initial data bundle sent when template loads
if (message.overlayData) {
overlayData = message.overlayData;
}
if (message.timerState) {
timerState = message.timerState;
}
if (message.licenseText) {
licenseText = message.licenseText;
}
console.log('[WebAdapter] Initial data received');
// Emit signal for templates
window.overlay.dataUpdated.emit(overlayData);
break;
case 'dataUpdated':
// Overlay data update
overlayData = { ...overlayData, ...message };
console.log('[WebAdapter] Overlay data updated:', Object.keys(message));
// Emit signal for templates
window.overlay.dataUpdated.emit(message);
break;
case 'matchUpdate':
console.log('[WebAdapter] Match update received');
window.overlay.dataUpdated.emit({ match_update: message });
break;
case 'resultUpdate':
console.log('[WebAdapter] Result update received');
window.overlay.dataUpdated.emit({ result_update: message });
break;
case 'videoStateChanged':
console.log('[WebAdapter] Video state changed:', message.state);
break;
case 'streamStateChanged':
console.log('[WebAdapter] Stream state changed:', message.state);
break;
case 'positionChanged':
// Video position update - can be used for time-based overlays
break;
default:
console.log('[WebAdapter] Unknown message type:', message.type);
}
});
// Request initial data when DOM is ready
function requestInitialData() {
console.log('[WebAdapter] Requesting initial data from parent');
if (window.parent !== window) {
window.parent.postMessage({ type: 'requestInitialData' }, '*');
}
// Also request specific data
requestFixtureData();
requestTimerState();
requestLicenseText();
requestCompletedMatches();
}
// Wait for DOM to be ready, then request initial data
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
// Small delay to ensure parent is ready
setTimeout(requestInitialData, 100);
});
} else {
setTimeout(requestInitialData, 100);
}
console.log('[WebAdapter] Web overlay adapter initialized');
// Flush any buffered console logs
if (typeof window.flushConsoleBuffer === 'function') {
setTimeout(window.flushConsoleBuffer, 200);
}
})();
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment