iTV.js Framework - Documentation
Introduction
Designing a TV interface is a little different to designing one for other types of platforms. The most obvious difference between a TV and a standard desktop or tablet/smartphone interface is the lack of a pointing device. Indeed, instead of using a mouse or touch screen, the user's only tool to navigate through the interface is a remote control. This difference entails a certain way of presenting information to the user and also how he may interact with it. This framework has been designed with this in mind.
The aim of this framework is to facilitate the creation of applications destined to run on browser based television interfaces. It adds functionality to an ordinary HTML document in ways that are specifically well suited to television interface requirements. A key aspect that makes this framework easy to use is that it doesn't assume you are running a particular flavor of browser with a specific feature set. Instead, it uses standard HTML and performs feature detection in order to perform specific HbbTV functionality if necessary. This means you can develop in the comfort of your desktop browser with all the debugging tools available (ex: Chrome DevTools, Firefox Firebug or Edge Developer Tools) and deploy the exact same code to your TV/STB.
This documentation assumes you have basic knowledge of HTML/CSS/JavaScript and will guide you through the functionalities of this framework in order to help you become a proficient interactive television web application developer.
Versioning
This framework follows the Semantic Versioning (SemVer) guidelines. The rules are pretty simple, given a version number MAJOR.MINOR.PATCH, increment the:
- MAJOR version when you make incompatible API changes,
- MINOR version when you add functionality in a backwards-compatible manner, and
- PATCH version when you make backwards-compatible bug fixes.
This means that the user of this framework can upgrade to a newer version without worrying about compatibility as long as the MAJOR version number isn't incremented. If it is, a migration guideline will be made available in order to make the upgrade process as smooth and painless as possible.
Hello World
The simplest application you may write is as follows:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="itv.js"></script>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
In this basic HTML page, the simple fact of including the iTV.js framework means that this is now a valid HbbTV application. Indeed, the framework checks to see if the browser is HbbTV compliant and if it is, adds the necessary <object> in order to run the OIPF plug-in to set the page visibility.
During the course of this documentation, we will take this simple example and add functionality concentrating on what is inside the <body> element.
Events
An essential part of creating a user interface is to perform actions when a specific event occurs. These events can be user generated (remote control key presses) or internal to the application (page loaded, index changed, video reached the end, etc.). That is why the framework enables you to easily perform actions associated with an event.
Events - Declaration
Inside an HTML element, you may associate an action with an event by creating an attribute that consists of the event name prefixed by "itv-event-". Then, you set the value of this attribute with whatever JavaScript you wish to perform when this event occurs.
Here is an Example:
<body itv-event-load="itv.log('Hello World')"></body>
The event "load" occurs when the HTML document is ready for you to interact with. This is most often the entry point of the application. When this event is triggered, it calls whatever JavaScript is associated with it. Here, it will call itv.log("Hello World") that will display the string "Hello World" on screen.
Events - Type
There are two types of events:
- User generated events which are triggered when the user presses a remote control key. These events are triggered on the element that is currently focused.
- Internally generated events such as the load event we have seen previously. The other internally generated events will be mentioned later on in order to better explain the context in which they occur.
Note: According to the HbbTV spec, you must explicitly request the capture of specific keys. This is done by calling itv.setKeys(mask) where mask is a combination of values that correspond to sets of keys.
Key Masks:
Type | Keys | Mask |
---|---|---|
Color | red | 0x1 |
Color | green | 0x2 |
Color | yellow | 0x4 |
Color | blue | 0x8 |
Navigation | left, up, right, down, enter, back | 0x10 |
VCR | play, pause, stop, next, prev, fast_fwd, rewind, play_pause | 0x20 |
Scroll | page_up, page_down | 0x40 |
Info | info | 0x80 |
Numeric | [0..9] | 0x100 |
Alpha | [a..z] | 0x200 |
Other | subtitle | 0x400 |
It is good practice to always set the key mask you wish to use just in case; non-HbbTV browsers are not affected by this in any way.
When running an app on a PC browser, you will find that most remote control keys have a direct keyboard counterpart. Those include navigational, numeric, alpha and scroll keys. For the rest, the following shortcuts are available:
Remote control key | Keyboard shortcut |
---|---|
red | Shift+R |
green | Shift+G |
yellow | Shift+Y |
blue | Shift+B |
play | Shift+P |
pause | Shift+O |
stop | Shift+S |
next | Shift+N |
prev | Shift+V |
fast_fwd | Shift+F |
rewind | Shift+W |
play_pause | Shift+A |
info | Shift+I |
Beyond these keyboard shortcuts, another accessibility feature is that clicking or touching an element sets focus to it and triggers the "enter" key event (i.e. itv-event-enter) on that element.
You may test this for yourself in the following example:
Events - Context
As explained previously, you may associate JavaScript with events are triggered by specific HTML elements. A result of this is that during the execution of the associated Javascript, the value of the this variable references the current HTML element that received the event. To demonstrate this, let's take the following example:
<div id="myButton" itv-event-enter="itv.log(this.id)">
If you trigger the "enter" event on that <div> element, this.id will have the value of "myButton".
On top of that, in certain circumstances, another variable that is added to the context in which the JavaScript is executed. This depends on the event that was triggered. Here is a list of the variables that are added to the context:
Event | Variable | Value | Range | Occurrence |
---|---|---|---|---|
itv-event-numeric | numeric | The value of the numeric key that was pressed | [0..9] | Any element that can receive a key event |
itv-event-alpha | alpha | The value of the alpha key that was pressed | [a..z] | Any element that can receive a key event |
itv-event-index | index | The value of the index that got selected | positive integer | Template - Mosaic elements |
To demonstrate this, let's take the following example:
<div id="myButton" itv-event-numeric="itv.log(this.id + ' : ' + numeric)">
If you press a numeric key while focused on this element, the output of the log will be "myButton : X", X being the numeric key you pressed.
You may test this for yourself in the following example:
Events - Propagation
Internally generated events do not bubble up, whereas user generated events do until they are captured.
What does this mean?
- When an internally generated event is triggered, if there is JavaScript that is associated with that event via an itv-event-xxxx attribute, then it will be executed, and that's it.
- When a user generated event is triggered, the same thing occurs, except when there is no itv-event-xxxx attribute associated with the element currently focused, the same event is triggered on its mother element. This bubbling up continues until it finds the corresponding itv-event-xxxx attribute associated or it reaches the top most element.
Why is it done this way?
This enables you to associate an action to be performed when a key is pressed:
- when the focus is on a specific element (ex: button functionality).
- wherever the focus is on a subset of elements (ex: navigation bar functionality).
You may test this for yourself in the following example:
Screen
In order for a web application to be reactive, a typical practice is to have the different screens of an interface be all part of a single page. This is commonly known as a single-page application. With the iTV.js Framework, it is easy to set up multiple screens that are displayed when needed.
Screen - Standard
To create a screen, all that is needed is to add an itv-screen attribute with no associated value to a container element. What this does is that this screen will be displayed as long as one of the elements it contains is focused. If none of the contained elements are focused, the screen disappears.
Here is an example:
<body itv-event-load="itv.setKeys(0x10);itv.setFocus('button1')" style="visibility:hidden">
<div itv-screen>
<h1>Screen 1</h1>
<div id="button1"
itv-focusable=1
itv-event-enter="itv.setFocus('button2')">
Go to Screen 2
</div>
</div>
<div itv-screen>
<h1>Screen 2</h1>
<div id="button2"
itv-focusable=1
itv-event-enter="itv.setFocus('button1')">
Go to Screen 1
</div>
</div>
</body>
In this example, we have 2 screens. Since we set the initial focus to "button1", only the first screen will be displayed. Then, if we press enter to go to screen 2, this first screen disappears and the second screen takes its place.
You may wonder why we have added style="visibility:hidden" to the <body> element? This is done to prevent the flickering that occurs during the lapse of time between when the HTML elements composing the page are ready and displayed and when the framework is up and running. The framework then automatically updates the visibility once an element is focused.
You may test this for yourself in the following example:
Screen - Group
Sometimes, you may want to be able to display a screen without necessarily having the focus set inside it. This typically occurs when you have a focusable element that is visible but outside a group of sub-screens. To do this, all you have to do is to add a label to your group of screens as follows: itv-screen="myGroupLabel". What this does is that all the screens that have the same label form a group of screens. Only one screen of a group is visible at a time. By default, the one that is visible is the first screen of this group. Otherwise it is the last one to have had a focused element inside it.
Here is an example:
<body itv-event-load="itv.setKeys(0x10);itv.setFocus('button1')" style="visibility:hidden">
<div itv-screen>
<h1>Screen 1</h1>
<div id="button1"
itv-focusable=1
itv-event-enter="itv.setFocus('button1-1')">
Go to Sub-Screen 1
</div>
<div itv-screen="myScreenLabel">
<h2>Sub-Screen 1</h2>
<div id="button1-1"
itv-focusable=1
itv-event-enter="itv.setFocus('button1-2')"
itv-event-back="itv.setFocus('button0')">
Go to Sub-Screen 2
</div>
</div>
<div itv-screen="myScreenLabel">
<h2>Sub-Screen 2</h2>
<div id="button1-2"
itv-focusable=1
itv-event-enter="itv.setFocus('button2')"
itv-event-back="itv.setFocus('button0')">
Go to Screen 2
</div>
</div>
</div>
<div itv-screen>
<h1>Screen 2</h1>
<div id="button2"
itv-focusable=1
itv-event-enter="itv.setFocus('button1')">
Go to Screen 1
</div>
</div>
</body>
In this example, we have 4 screens: 2 "main" screens and 2 "sub" screens inside the first "main" screen. We can navigate from one screen to the other by pressing enter on the various buttons. We start with the first main screen being visible because the focus is on one of its containing elements. The first sub screen is also visible because it is part of a group, even though no elements inside it are focused. When we move the focus to "button2" on the second main screen, the other main screen and the sub screens inside it disappear.
You may test this for yourself in the following example:
Templates
Now that you have the capacity of handling events and navigating inside screens, the next thing you probably would like to do is manipulate dynamic data and inject it inside widgets. This is where the itv-template attribute comes into play.
Templates - Text
The simplest template available is itv-template="text". Once you have added this attribute to an HTML element, you may use the double curly braces inside this element as place holders for dynamic data as such:
<span id="myTemplateID" itv-template="text">
Fruits : {{ ['apples', 'peaches', 'oranges'].join(' | ') + ', id : ' + this.id }}
</span>
Inside that element, the text will be rendered as follows : "Fruits : apples | peaches | oranges, id : myTemplateID".
What actually happens under the hood is that the content of that span element is parsed and what is inside the double curly braces is executed as JavaScript. This means that you can put whatever you want inside those braces as long as it evaluates to a String. You will have noticed that the this variable refers to the template element in which this JavaScript was called.
If you wish to update the template because data has changed, you may use itv.update("myTemplateID").
You may test this for yourself in the following example:
Templates - Mosaic
One of the most commonly used data structures that you need to represent on screen is a list. Whether it is a list of days, films, channels, images... you basically need to display items horizontally or vertically and be able to navigate through them and eventually select one. When you come to think of it, a grid or mosaic of items is actually just a list with a 2 dimensional layout. Another way to view this is that a one dimensional list is a flat mosaic. Furthermore, if there are many items, you may want to display them across multiple pages and be able to navigate inside them.
To fulfill all these needs, the framework offers a "mosaic" template. It is invoked by adding the attribute itv-template="mosaic" to an HTML element. What this template does is replicate whatever is inside it by applying certain rules. The number of replications depends on the amount of data you use. Then, you may want to change how many rows or columns this mosaic has, if the mosaic is populated from left to right or from top to bottom (one row or one column being a simple list). In case there are several pages, you may want to specify if the scrolling is done horizontally or vertically along with how the scrolling takes place.
All these features are available by specifying certain attributes to this template. Here is a list of them along with how they modify the template they are applied to:
- itv-data : Type Array, associate it to whatever data structure you wish as long as it is an Array. There is an exception that consists of directly specifying a Number instead in which case the replication is done accordingly. This is the only mandatory attribute.
- itv-index : Type Number, is the index of the item that is selected by default. If not specified, it will be set to 0 which corresponds to the first item.
- itv-rows : Type Number, specifies how many rows there are per page. The default is 1.
- itv-cols : Type Number, specifies how many columns there are per page. The default is 1.
- itv-select : Type String, if set to "manual", you must manually change the selected item index using itv.setIndex(). Any other value (or no value) will automatically update the currently selected index according to which item is focused.
- itv-fill : Type String, if set to "rows", the mosaic will be populated by items from left to right of a page which, when completed goes to the following row below. Any other value (or no value) sets this fill pattern from top to bottom of a page, then go to the following column to the right.
- itv-scroll : Type String, if set to "vertical", when the focus reaches the top or bottom edge of a page, the scrolling is done vertically. Any other value sets this to horizontally.
- itv-scroll-type : Type String, if set to "page", the focus will move from item to item until it reaches the end of a page. If you try to move further, a new page is displayed and the focus goes back to the opposite edge of the page. Any other value changes what happens when you reach the edge of a page. In this case, the position of the focus remains at the same position at the edge but all items move by one step.
The values of the attributes itv-data, itv-index, itv-rows and itv-cols are interpreted as JavaScript. This means that the following two examples will give you the same result:
<html>
<body itv-event-load="itv.setKeys(0x10);itv.setFocus('myMosaic')">
<div id="myMosaic"
itv-focusable=1
itv-template="mosaic"
itv-data="['A', 'B', 'C', 'D', 'E', 'F']"
itv-index="3"
itv-rows="4">
<div>{{this.id + " : " + (i + 1) + "/" + data.length + " : " + data[i]}}</div>
</div>
</body>
</html>
Is equivalent to:
<html>
<head>
<script>
var initialIndex = 3,
rowCount = 4;
function getData () {
// This could be data retrieved via an HTTP request.
return ['A', 'B', 'C', 'D', 'E', 'F'];
}
</script>
</head>
<body itv-event-load="itv.setKeys(0x10);itv.setFocus('myMosaic')">
<div id="myMosaic"
itv-focusable=1
itv-template="mosaic"
itv-data="getData()"
itv-index="initialIndex"
itv-rows="rowCount">
<div>{{this.id + " : " + (i + 1) + "/" + data.length + " : " + data[i]}}</div>
</div>
</body>
</html>
As you can see, the same curly braces described in the text template applies here. You have the same access to the this variable but also have other variables that have been added to the context. These variables are data which refers to the value of itv-data and i which is the Nth iteration of the index that goes from 0 to data.length - 1 during the creation process of the mosaic items.
You may test this for yourself in the following examples:
Video
There are different types of video streams to consider depending on the source. Indeed, you may access a video via multiple protocols such as "http://", "https://", "ftp://", "ftps://", "hls://", "file://", etc. or you may want to display a live satellite, terrestrial or cable feed. Unfortunately, the HbbTV committee didn't go with the now de facto HTML5 <video> element preferring to use a <object type="video/mpeg"></object> element to manipulate file streaming and a <object type="video/broadcast"></object> element to manipulate a live DVB feed.
For the moment, this framework does very little to specifically support video playback. An upcoming releases will introduce an intelligent way of handling the HTML5 <video> element and gracefully fall back to the HbbTV way of handling things if necessary.
In the mean time, you need to implement whatever technique is necessary depending on the target you are aiming at. Thankfully, this isn't too difficult.
Here is an example of how to playback a video using the <video> element:
Here is an example of how to manipulate the live DVB broadcast feed the HbbTV way:
API
The iTV.js framework exposes only one global variable: itv. It is the object through which you can access all functions that are available in this framework. You are free to use any third party JavaScript library/framework such as jQuery, Prototype, Mootools or any other that suits you best. That said, remember that you might not really need one. This is especially true since you probably do not need to support legacy Internet Explorer quirks.
Here is an exhaustive list of what is available in the itv object. It is purposefully short, concentrating on the bare essentials:
- itv.version : Current version of the framework.
- Returns : Type String.
- Example : console.log( itv.version ); // > "3.1.10"
- itv.log ( msg ) : Displays a log message on screen since the native console.log() isn't always available on a TV/STB. A timestamp is prepended to the message.
- msg : Type String, the text to be displayed.
- Returns : Type undefined.
- Example : itv.log( "Framework version " + itv.version ); // On screen display > "17:19:41.813 : Framework version 3.1.10"
- itv.setKeys ( mask ) : Enables you to declare keys you wish to capture when running in a HbbTV compliant browser.
- mask : Type Number.
- Returns : Type undefined.
- Example : itv.setKeys( 0x1 + 0x10 ); // This will capture the Red + Navigation keys. See Key Masks for an exhaustive list of possible values.
- itv.getFocus ( ) : Gets the currently focused HTML element.
- Returns : Type HTML element of the currently focused element.
- Example : var focus = itv.getFocus();
- itv.setFocus ( element ) : Sets the focus to a specific HTML element. If the target element is not visible, the visibility hierarchy is updated accordingly.
- element : Type String or HTML element you wish to set focus to.
- Returns : Type Boolean, true if the focus was set successfully, false otherwise.
- Example : itv.setFocus( "myElementID" );
- itv.getIndex ( element ) : Get the currently selected index of the specified Template - Mosaic element.
- element : Type String or HTML element of the Template - Mosaic element you wish to get the index of.
- Returns : Type Number, the index of the Template - Mosaic element.
- Example : itv.getIndex( "myMosaicID" );
- itv.setIndex ( element, index ) : Sets the index of a specific Template - Mosaic element.
- element : Type String or HTML element of the Template - Mosaic element you wish to set the index of.
- index : Type Number.
- Returns : Type undefined.
- Example : itv.setIndex( "myMosaicID", 4 );
- itv.getFocusedIndex ( ) : Get the index of the currently focused Template - Mosaic element.
- Returns : Type Number, the index if available, otherwise returns undefined if the focus isn't on a Template - Mosaic element.
- itv.getState ( element, state ) : Gets the state of a specific video element.
- element : Type String or HTML element of the video element you wish to get the state of.
- state : Type String, the possibilities are "currentTime" or "duration".
- Returns : Type Number, the value of the associated state in seconds.
- Example : itv.getState( "myVideoID", "currentTime" ); // > 500.4502
- itv.setState ( element, state, param ) : Sets the state of a specific video element.
- element : Type String or HTML element of the video element you wish to get the state of.
- state : Type String, the possibilities are "load", "loop", "play", "pause", "play_pause" or "stop".
- param : Only necessary when state is "load" (in which case you must specify a URL String) or "loop" (in which case you must specify a Boolean).
- Returns : Type undefined.
- Example : itv.setState( "myVideoID", "load", "videos/myVideo.mp4" );
- Example : itv.setState( "myVideoID", "loop", true );
- Example : itv.setState( "myVideoID", "play" );
- itv.update ( element_array ) : Force update a list of template elements.
- element_array : Multiple String or HTML element of element templates. If left empty, all templates will be updated.
- Returns : Type undefined.
- Example : itv.update("myTemplateID1", "myTemplateID2", "myTemplateID3");
- Example : itv.update(); // => updates all templates.
- itv.processEvent ( element, eventName, value, bubble ) : Execute the javascript that is associated to the event in the corresponding element.
- element : Type String or HTML element you wish to process.
- eventName : Type String, the name of the event.
- value : Only necessary if you wish to associate a value to the eventName.
- bubble : Type Boolean, set to true if you wish that the event bubbles up.
- Returns : Type undefined.
- Example : itv.processEvent( "myElementID", "enter" ); // This simulates the user pressing enter / OK on a specific element.
- itv.getStyle ( element, attribute ) : Get the on screen calculated value of a style attribute of an element.
- element : Type String or HTML element you wish to get the style value of.
- attribute : Type String, the style attribute you wish to get the value of.
- Returns : Type String, the on screen calculated value you are looking for.
- Example : itv.getStyle( "myElementID", "width" ); // => "100px".
- itv.setStyle ( element, cssRules ) : Set in-line style values of an element.
- element : Type String or HTML element you wish to set the style of.
- cssRules : Type Object, a collection of style attributes with their corresponding values.
- Returns : Type undefined.
- Example : itv.setStyle( "myElementID", {"width" : "100px", "height" : "50px", "z-index" : 5} );
- itv.addStyleValues ( element, attributes ) : Add the on screen calculated values of multiple style attributes of an element.
- element : Type String or HTML element you wish to get the sum of style values of.
- attributes : Type Array of String that can be any style attributes that are measured in px.
- Returns : Type Number, the sum of the style values.
- Example : itv.addStyleValues( "myElementID", ["width", "border-left-width", "border-right-width", "padding-left", "padding-right", "margin-left", "margin-right"] ); // => 126.
- Remark : All style attributes must return values of the same type (ex: "px").
- itv.getPos ( element ) : Get the on screen calculated position of an element.
- element : Type String or HTML element you wish to set the position of.
- Returns : Type Array of size 2 => [top, left].
- Example : itv.getPos( "myElementID" ); // => [45, 80].
- itv.hasClass ( element, aClass ) : Does the element have a specific class.
- element : Type String or HTML element you are inquiring about.
- aClass : Type String, the name of the class you are inquiring about.
- Returns : Type Boolean, true if the class is present, false otherwise.
- Example : itv.hasClass( "myElementID", "active" ); // => false.
- itv.addClass ( element, aClass ) : Add a class to an element.
- element : Type String or HTML element you wish to add a class to.
- aClass : Type String, the name of the class you wish to add.
- Returns : Type undefined.
- Example : itv.addClass( "myElementID", "active" );
- itv.removeClass ( element, aClass ) : Remove a class from an element.
- element : Type String or HTML element you wish to remove a class from.
- aClass : Type String, the name of the class you wish to remove.
- Returns : Type undefined.
- Example : itv.removeClass( "myElementID", "active" );
- itv.replaceClass ( element, aClasss, bClass ) : Replace a class by another one to an element.
- element : Type String or HTML element you wish to replace a class from.
- aClass : Type String, the name of the class you wish to remove.
- bClass : Type String, the name of the class you wish to add.
- Returns : Type undefined.
- Example : itv.replaceClass( "myElementID", "active", "disabled" );