mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-11 18:33:53 +00:00
45e7373279
* chore: avoid shadow variable name * chore: map could just be foreach * clean uneeded template literal * lint
158 lines
4.9 KiB
JavaScript
158 lines
4.9 KiB
JavaScript
/*eslint-env browser*/
|
|
/**
|
|
* This is a very minimal example for a websocket client
|
|
* You could use this as a starting point to creating your own interfaces
|
|
*/
|
|
|
|
// Data that the user needs to provide depending on the Ontime URL
|
|
const isSecure = window.location.protocol === 'https:';
|
|
const userProvidedSocketUrl = `${isSecure ? 'wss' : 'ws'}://${window.location.host}${getStageHash()}/ws`;
|
|
|
|
connectSocket();
|
|
|
|
let reconnectTimeout;
|
|
const reconnectInterval = 1000;
|
|
let reconnectAttempts = 0;
|
|
|
|
/**
|
|
* Connects to the websocket server
|
|
* @param {string} socketUrl
|
|
*/
|
|
function connectSocket(socketUrl = userProvidedSocketUrl) {
|
|
const websocket = new WebSocket(socketUrl);
|
|
|
|
websocket.onopen = () => {
|
|
clearTimeout(reconnectTimeout);
|
|
reconnectAttempts = 0;
|
|
console.warn('WebSocket connected');
|
|
};
|
|
|
|
websocket.onclose = () => {
|
|
console.warn('WebSocket disconnected');
|
|
reconnectTimeout = setTimeout(() => {
|
|
console.warn(`WebSocket: attempting reconnect ${reconnectAttempts}`);
|
|
if (websocket && websocket.readyState === WebSocket.CLOSED) {
|
|
reconnectAttempts += 1;
|
|
connectSocket();
|
|
}
|
|
}, reconnectInterval);
|
|
};
|
|
websocket.onerror = (error) => {
|
|
console.error('WebSocket error:', error);
|
|
};
|
|
|
|
websocket.onmessage = (event) => {
|
|
// all objects from ontime are structured with tag and payload
|
|
const { tag, payload } = JSON.parse(event.data);
|
|
|
|
/**
|
|
* runtime-data is sent
|
|
* - on connect with the full state
|
|
* - and then on every update with a patch
|
|
*/
|
|
if (tag === 'runtime-data') {
|
|
handleOntimePayload(payload);
|
|
}
|
|
};
|
|
}
|
|
|
|
let localData = {};
|
|
/**
|
|
* Handles the ontime payload updates
|
|
* @param {object} payload - The payload object containing the updates
|
|
*/
|
|
function handleOntimePayload(payload) {
|
|
// 1. apply the patch into your local copy of the data
|
|
localData = { ...localData, ...payload };
|
|
|
|
// 2. update the UI with the new data
|
|
// ... timer data
|
|
if ('clock' in payload) updateDOM('clock', formatTimer(payload.clock));
|
|
if ('timer' in payload) updateDOM('timer', formatObject(payload.timer));
|
|
// ... rundown data
|
|
if ('rundown' in payload) updateDOM('rundown', formatObject(payload.rundown));
|
|
// ... runtime
|
|
if ('offset' in payload) updateDOM('offset', formatObject(payload.offset));
|
|
// ... relevant entries
|
|
if ('eventNow' in payload) updateDOM('eventNow', formatObject(payload.eventNow));
|
|
if ('eventNext' in payload) updateDOM('eventNext', formatObject(payload.eventNext));
|
|
if ('eventFlag' in payload) updateDOM('eventFlag', formatObject(payload.eventFlag));
|
|
if ('groupNow' in payload) updateDOM('groupNow', formatObject(payload.groupNow));
|
|
// ... messages service
|
|
if ('message' in payload) updateDOM('message', formatObject(payload.message));
|
|
// ... extra timers
|
|
if ('auxtimer1' in payload) updateDOM('auxtimer1', formatObject(payload.auxtimer1));
|
|
if ('auxtimer2' in payload) updateDOM('auxtimer2', formatObject(payload.auxtimer2));
|
|
if ('auxtimer3' in payload) updateDOM('auxtimer3', formatObject(payload.auxtimer3));
|
|
}
|
|
|
|
/**
|
|
* Updates the DOM with a given payload
|
|
* @param {string} field - The runtime data field
|
|
* @param {object} payload - The patch object for the field
|
|
*/
|
|
function updateDOM(field, payload) {
|
|
const domElement = document.getElementById(field);
|
|
if (domElement) {
|
|
domElement.innerText = payload;
|
|
}
|
|
}
|
|
|
|
// Time constants used for calculating times
|
|
const millisToSeconds = 1000;
|
|
const millisToMinutes = 1000 * 60;
|
|
const millisToHours = 1000 * 60 * 60;
|
|
|
|
/**
|
|
* Formats a timer value into a human-readable string
|
|
* @param {number} number - The timer value in milliseconds
|
|
* @returns {string} The formatted timer string
|
|
*/
|
|
function formatTimer(number) {
|
|
if (number == null) {
|
|
return '--:--:--';
|
|
}
|
|
const millis = Math.abs(number);
|
|
const isNegative = number < 0;
|
|
return `${isNegative ? '-' : ''}${leftPad(millis / millisToHours)}:${leftPad(
|
|
(millis % millisToHours) / millisToMinutes,
|
|
)}:${leftPad((millis % millisToMinutes) / millisToSeconds)}`;
|
|
|
|
/**
|
|
* Pads a number with leading zeros
|
|
* @param {number} number - The number to pad
|
|
* @returns {string} The padded number string
|
|
*/
|
|
function leftPad(val) {
|
|
return Math.floor(val).toString().padStart(2, '0');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stringifies an object into a pretty string
|
|
* @param {object} data - The data object to format
|
|
* @returns {string} The formatted data string
|
|
*/
|
|
function formatObject(data) {
|
|
return JSON.stringify(data, null, 2);
|
|
}
|
|
|
|
/**
|
|
* Utility to handle a demo deployed in an ontime stage
|
|
* You can likely ignore this in your app
|
|
*
|
|
* an url looks like
|
|
* https://cloud.getontime.no/stage-hash/external/demo/ -> /stage-hash
|
|
* @returns {string} - The stage hash if the app is running in an ontime stage
|
|
*/
|
|
function getStageHash() {
|
|
const href = window.location.href;
|
|
if (!href.includes('getontime.no')) {
|
|
return '';
|
|
}
|
|
|
|
const hash = href.split('/');
|
|
const stageHash = hash.at(3);
|
|
return stageHash ? `/${stageHash}` : '';
|
|
}
|