Handling Updates
Here we will define what happens when the CasparCG Server calls the update command on our graphic.
In the last section we called out how the update
function looked different than the other play out commands.
const _graphic = (function() {
(function() {
// Runs after the rest of the parent function parses
window['update'] = (raw) => update(raw);
window['play'] = play;
window['next'] = next;
window['stop'] = stop;
window['remove'] = remove;
})();
// Other function definitions
}
We do this because the update
function is the only function that is passed data from the CasparCG server. We use an arrow function to capture the incoming string and pass it to our own update
function. An example explains it best.
// CasparCG Command
// CG 1-1 UPDATE 0 "Hello from Caspar"
const _graphic = (function() {
// Other function definitions
function update(raw) {
console.log(raw) // Output: Hello from Caspar
}
// Other play out command function definitions
}
Template Data Setup
Now that we have some data, let's put it to work! We will be using JSON data so we can easily convert it to a JS object and utilize it in our script. In your web browser's developer console, enter the following JS command to get the example JSON data stringified and escaped.
JSON.stringify({
style: {primaryColor: "lightblue", textColor: "black", position: "right"},
data: {title: "Title One", subtitle: "Subtitle One"}
}).replace(/"/g, '\\"');
// {\"style\":{\"primaryColor\":\"lightblue\",\"textColor\":\"black\"},\"data\":{\"title\":\"Title One\",\"subtitle\":\"Subtitle One\"}}
Feel free to change the colors or titles to different values. The colors an be a color keyword, HEX, RGB, or RGBA value.
You may also add multiple names by changing data
to an array of objects.
data: [
{title: "Title One", subtitle: "Subtitle One"},
{title: "Title Two", subtitle: "Subtitle Two"}
]
We can now call our update function either from CasparCG server or the browser's developer console.
// CasparCG Server
CG 1-1 ADD 0 http://localhost:8080/lower-third.1.html 0 "{\"style\":{\"primaryColor\":\"lightblue\",\"textColor\":\"black\"},\"data\":{\"title\":\"Title One\",\"subtitle\":\"Subtitle One\"}}"
// Broswer Developer Console
update("{\"style\":{\"primaryColor\":\"lightblue\",\"textColor\":\"black\"},\"data\":{\"title\":\"Title One\",\"subtitle\":\"Subtitle One\"}}")
Template Data w/ the Development Widget
If you are using the HTML Widget setup in the Development Setup section, then now is a good time to add our data into the widget. This will allow the widget to inject our template data when we run the update command.
Via Web Browser
If you downloaded a Chromium browser, then you can use your mouse to interact with the widget and add the data normally. Click the Data
button on the widget and then in the popup window, click the {}
icon on the top right. From there paste in the following JSON.
{
"data": [
{"title": "Title One", "subtitle": "Subtitle One"},
{"title": "Title Two", "subtitle": "Subtitle Two"}
], "style": {
"primaryColor": "lightblue",
"textColor": "black",
"position": "center"
}
}
In the text field at the bottom of the popup window, enter the name Load
and click the save icon on the bottom right. The name will appear on the right hand side under "Data Sets". Click on the {}
icon again and remove all the text from the text area. Then, paste in the following JSON snippet.
{
"data": {"title": "Title Three", "subtitle": "Subtitle Three"}
}
Change the name at the bottom from Load
to Add New Title
and click save icon again. You should be able to toggle between both data sets in the Data Sets list. Ensure that the Load
data set is first in the list so the widget can run the update
command with the correct data set.
When loading the graphic for the first time, click the Update button with the Load
option selected. To add a new name to the graphic ( Once the graphic is complete ), click the update function with the Add New Title
option selected.
Via CasparCG Server
If you are developing directly in the CasparCG server, then you will need to have remote debugging enabled for your HTML templates. Somewhere in your server's casparcg.config
file there should be the following lines to enable the remote debugging feature.
<html>
<remote-debugging-port>8081</remote-debugging-port>
</html>
We can now start the server and go to http://localhost:8081/
and when a template is added via the CG ADD command, a link will appear for the template's debugging session. If you do not see a link after running the CG ADD
command, refresh the debug page.
If you are using a modern version of Chrome to debug CasparCG server then you may get this error, TypeError: document.registerElement is not a function
. To fix this, download and use a version of Chromium listed in the CasparCG Server Setup section. Future version of CasparCG will not have this issue.
Once you have navigated to the remote development console, we can add the template's data to the widget. The downside to developing from within the server is none of Chromium's local storage is saved when the server stops. This mean, every time you begin developing, you would need to enter the following command to load both sets of data.
_widget.setTemplateData("{\"data\":{\"title\":\"Title Three\",\"subtitle\":\"Subtitle Three\"}}", 'Add New Title');
_widget.setTemplateData("{\"style\":{\"primaryColor\":\"lightblue\",\"textColor\":\"black\"},\"data\":[{\"title\":\"Title One\",\"subtitle\":\"Subtitle One\"},{\"title\":\"Title Two\",\"subtitle\":\"Subtitle Two\"}]}", 'Load');
The widget now has the two pieces of JSON data required by our graphic. To switch between each data set, we can run another function.
_widget.getDataOptions(); // Returns all the data sets
_widget.selectTemplateData('Add New Title'); // Selects a data set
Replace Add New Title
with the name of the data set you want to use. Finally, there is one additional CasparCG server trade off we have to address.
Every time the CG ADD
command is ran, a new debug session will be started for that template. This means you will need to go back to the original debug page, http://localhost:8081/
, and select the new debug session link. To avoid this, we will refresh the page from the console then re-enable our widget.
location.reload(); // Reloads the HTML page
// After the page has reloaded
// If using NRK version, re-enable the widget
_widget.enableWidget();
// Select the correct data and update the template
_widget.selectTemplateData('Load');
_widget.executePlayOutCommand('update');
The graphic's update
function will run with the data passed to it as if the CasparCG server was running it.
Updates in the JavaScript File
After all of that setup, we are finally ready to begin scripting. Inside the update
function, let's begin by parsing the string passed by the window's update function.
function update(raw) {
let parsed;
// Try and parse incoming string as JSON
try {
parsed = JSON.parse(raw);
if(!Object.keys(parsed).length)
throw new Error('Empty objects are invalid');
if(!parsed.style) {
if(!parsed.data)
throw new Error('Invalid data object');
}
} catch(e) { // Parse Failed
handleError(e);
return;
}
Array.isArray(parsed.data) // Save the text data
? data = data.concat(parsed.data)
: data.push(parsed.data);
style = parsed.style; // Save the style data
}
If the parsing fails or the object does not have the properties we are looking for, we will throw an error. If there was valid data, we will add it to our graphic's data and style proprieties.
Next, let's write two functions. One to handle our text data and the other to handle our style data. We will begin with the text data because it is simpler.
function update(raw) {
let parsed;
/* Parsing JSON Try Catch Block */
/* Save data and style */
// Is graphic being loaded?
if(state === 0) {
try {
applyData();
} catch (error) {
handleError(error);
return;
}
}
}
First we check and see if the graphic is still being loaded with the state
property. If it is, we will call the applyData
function inside of a try catch block. We can then write the applyData
function.
const _graphic = (function() {
/* Other Graphic Properties */
let data;
let style;
// Other function definitions
function applyData() {
const graphic = document.querySelector('.lt-style-one .graphic');
const title = graphic.querySelector('h1');
const subtitle = graphic.querySelector('p');
title.textContent = data[activeStep].title;
subtitle.textContent = data[activeStep].subtitle;
}
function update() { /* Update function code */}
}
The applyData
functions is pretty straight forwards. We setup a variable for each text element and then set it's text content to the current title and subtitle. Later on we will increment the activeStep
property to get the next title in the list.
Next we can add in our function to setup the graphic's styles. Let's start by adding a function call to applyStyles
.
function update(raw) {
let parsed;
/* Parsing JSON Try Catch Block */
if(this.state === 0) {
try {
applyData();
applyStyle();
} catch (error) {
handleError(e);
return;
}
}
}
We can then begin writing the applyStyle
function.
const _graphic = (function() {
/* Other Graphic Properties */
let data;
let style;
// Other function definitions
function applyData() { }
function applyStyle() {
const container = document.querySelector('.lt-style-one');
const graphic = container.querySelector('.graphic');
const [pathLeft, pathRight] = graphic.querySelectorAll('svg path');
const title = graphic.querySelector('h1');
const subtitle = graphic.querySelector('.subtitle');
// Set the elements CSS styles
pathLeft.style.stroke = style.primaryColor;
pathRight.style.stroke = style.primaryColor;
title.style.color = style.textColor;
subtitle.style.color = style.textColor;
subtitle.style.backgroundColor = style.primaryColor;
// Position the graphic on screen
switch(style.position) {
case 'left':
container.style.marginRight = 'auto';
break;
case 'center':
container.style.margin = '4vh auto';
break;
default:
container.style.marginLeft = 'auto';
break;
}
}
function update() { /* Update function code */}
}
The concept of the applyStyles
function is similar to the applyData
function. Get a reference to all the elements needed to be modified and then update the necessary properties. We use [pathLeft, pathRight]
when defining the svg
paths. This is a shorthand way of defining elements from within an array in the variable declaration. It is a form of deconstruction and a full breakdown on how it works can be found on MDN's website.
We also check the position
property to the style object. This is optional but, when used will move the graphic to either the left, center, or right section of the screen.
Wrapping Up
We need to increment the graphic's state
property. This will matter when we begin animating.
function update(raw) {
let parsed;
/* Parsing JSON Try Catch Block */
if(this.state === 0) {
try {
applyData();
applyStyle();
state = 1; // Graphic has been loaded
} catch (error) {
handleError(e);
return;
}
}
}
Our graphic can now take in data and setup the initial state of the graphic. The last thing we need to do is set an opacity
property on the .lt-style-one
element. This will ensure we can not see the graphic before it is loaded. We could set this value with SCSS but, on a slow network the CSS file may not load before the first paint which would display the graphic for a moment.
<body>
<main class="lt-style-one">
<div class="graphic" style="opacity: 0;">
<!-- SVG and text element definitions -->
</div>
</main>
<script src="../js/lower.1.js"></script>
</body>
We will use JS to reveal the graphic when the CasparCG server executes CG PLAY command.
Resources
Browser Snippets on Chrome's Developer site.
Deconstruction on MDN's website.
Last updated
Was this helpful?