bitmovin-player-ui
Version:
Bitmovin Player UI Framework
991 lines (883 loc) • 34.5 kB
HTML
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Bitmovin Player UI Demo</title>
<link
rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/css/bootstrap.min.css"
integrity="sha384-AysaV+vQoT3kOAXZkl02PThvDr8HYKPZhNT5h/CXfBThSRXQ6jW5DO2ekP5ViFdi"
crossorigin="anonymous"
/>
<link rel="stylesheet" href="css/demo.css" />
<link rel="stylesheet" href="css/bitmovinplayer-ui.css" />
</head>
<body>
<div class="container">
<div class="player-wrapper">
<div class="player" id="player"></div>
</div>
<br />
<div class="card">
<div class="card-header">
<h3 class="float-sm-left">Playground</h3>
<a class="float-sm-right" href="simple.html">Test plain CSS</a>
</div>
<div class="card-block">
<h4 class="card-title">Player Config</h4>
<div class="form-group row">
<label for="config-source" class="col-sm-4 form-control-label">Video Source</label>
<div class="col-sm-4">
<select class="form-control" id="config-source"></select>
</div>
<span class="col-sm-4 form-text">The video source to load into the player.</span>
</div>
<div class="form-group row">
<label for="config-ui" class="col-sm-4 form-control-label">UI</label>
<div class="col-sm-4">
<select class="form-control" id="config-ui"></select>
</div>
<span class="col-sm-4 form-text">The UI type to display.</span>
</div>
<div class="form-group form-check row">
<label for="config-ads" class="col-sm-4 form-check-label">Ads</label>
<div class="col-sm-4 form-check">
<input type="checkbox" id="config-ads" />
</div>
<span class="col-sm-4 form-text">Schedule client-side ads.</span>
</div>
<div class="form-group form-check row">
<label for="display-portrait" class="col-sm-4 form-check-label">Simulate portrait phone layout</label>
<div class="col-sm-4 form-check">
<input type="checkbox" id="display-portrait" />
</div>
</div>
</div>
<div class="card-block">
<h4 class="card-title">Playback Control</h4>
<p class="card-text" id="playback-controls"></p>
<h5 class="card-title">Actions</h5>
<p class="card-text" id="actions"></p>
<h5 class="card-title">Output</h5>
<div class="card card-block"><samp id="function-call-output"></samp></div>
</div>
<div class="card-block">
<h4 class="card-title">
Events
<small class="text-muted">Log events to the console</small>
</h4>
<p class="card-text" id="events">
<label>
<input type="checkbox" id="config-events-all" />
All
</label>
</p>
</div>
<div class="card-block">
<h4 class="card-title">
Native <video> events
<small class="text-muted">Log events to the console</small>
</h4>
<p class="card-text" id="native-events">
<label>
<input type="checkbox" id="config-native-events-all" />
All
</label>
</p>
</div>
<div class="card-block">
<h4 class="card-title">Stats</h4>
<p class="card-text" id="userAgent"></p>
</div>
</div>
</div>
<script src="https://cdn.bitmovin.com/player/web/8/bitmovinplayer.js"></script>
<script src="https://cdn.bitmovin.com/player/web/8/modules/bitmovinplayer-advertising-bitmovin.js"></script>
<script src="js/bitmovinplayer-ui.js"></script>
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
<script type="text/javascript">
var sources = {
fullyFeatured: {
dash: 'https://cdn.bitmovin.com/content/assets/sintel/sintel.mpd',
hls: 'https://cdn.bitmovin.com/content/assets/sintel/hls/playlist.m3u8',
progressive: [
{ url: 'https://cdn.bitmovin.com/content/assets/sintel/Sintel.mp4', type: 'video/mp4' },
{ url: 'https://cdn.bitmovin.com/content/assets/sintel/Sintel.webm', type: 'video/webm' },
],
poster: 'https://cdn.bitmovin.com/content/assets/sintel/poster.png',
thumbnailTrack: {
url: 'https://cdn.bitmovin.com/content/assets/sintel/sprite/sprite.vtt',
},
title: 'Sintel',
description:
"A woman, Sintel, is attacked while traveling through a wintry mountainside. After defeating her attacker and taking his spear, she finds refuge in a shaman's hut...",
markers: [
{ time: 0, title: 'Intro' },
{ time: 102, title: 'Old Guy', duration: 30 },
{ time: 150, title: 'City', cssClasses: ['class1', 'class2'] },
{ time: 200, title: 'Dragon' },
{ time: 370, title: 'Desert' },
{ time: 385, title: 'Bamboo Forest' },
{ time: 410, title: 'Winter again' },
{ time: 755, title: 'Credits' },
],
recommendations: [
{
title: 'Recommendation 1: The best video ever',
resource: {
dash: 'https://cdn.bitmovin.com/content/assets/sintel/sintel.mpd',
poster: 'https://cdn.bitmovin.com/content/assets/sintel/poster.png',
},
duration: 10.4,
},
{
title: 'Recommendation 2: The second best video',
resource: { url: 'http://bitmovin.com', thumbnail: 'https://placehold.co/300x300/2e2e2e/2e2e2e' },
duration: 64,
},
{
title: 'Recommendation 3: The third best video',
resource: { url: 'http://bitmovin.com' },
},
],
},
basicProgressive: {
progressive: 'https://cdn.bitmovin.com/content/assets/sintel/Sintel.mp4',
poster: 'https://cdn.bitmovin.com/content/assets/sintel/poster.png',
},
basicMultiProgressive: {
progressive: [
{ url: 'https://cdn.bitmovin.com/content/assets/sintel/Sintel.mp4', type: 'video/mp4', label: 'quality1' },
{ url: 'https://cdn.bitmovin.com/content/assets/sintel/Sintel.mp4', type: 'video/mp4', label: 'quality2' },
],
},
basicHls: {
hls: '//cdn.bitmovin.com/content/assets/sintel/hls/playlist.m3u8',
poster: 'https://cdn.bitmovin.com/content/assets/sintel/poster.png',
},
basicDash: {
dash: 'https://cdn.bitmovin.com/content/assets/sintel/sintel.mpd',
},
basicDashAudioOnly: {
dash: 'https://cdn.bitmovin.com/content/assets/sintel/sintel-audio.mpd',
},
basicDashVideoOnly: {
dash: 'https://cdn.bitmovin.com/content/assets/sintel/sintel-video.mpd',
},
errorInvalidHost: {
dash: 'http://invalid/url/to/nonexistent/mpd.mpd',
},
errorMissingMpd: {
dash: 'https://cdn.bitmovin.com/content/assets/sintel/sintellll.mpd',
},
vr: {
dash: 'https://cdn.bitmovin.com/content/assets/playhouse-vr/mpds/105560.mpd',
vr: {
contentType: 'single',
},
},
posterAndThumbnails: {
dash: 'https://cdn.bitmovin.com/content/assets/MI201109210084/mpds/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.mpd',
hls: 'https://cdn.bitmovin.com/content/assets/MI201109210084/m3u8s/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8',
progressive:
'https://cdn.bitmovin.com/content/assets/MI201109210084/MI201109210084_mpeg-4_hd_high_1080p25_10mbits.mp4',
poster: 'https://cdn.bitmovin.com/content/assets/art-of-motion_drm/art-of-motion_poster.jpg',
thumbnailTrack: {
url: 'https://cdn.bitmovin.com/content/assets/MI201109210084/thumbnails/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.vtt',
},
userId: 'demo_html5_vts',
videoId: 'art-of-motion',
},
live: {
dash: 'https://bitcdn-kronehit.bitmovin.com/v2/dash/manifest.mpd',
},
liveWithTimeshift: {
dash: 'http://demo-dash-live.zahs.tv/hd/manifest.mpd?timeshift=100',
},
};
var adBreaks = [
{
tag: {
// skippable
url: 'https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/single_preroll_skippable&sz=640x480&ciu_szs=300x250%2C728x90&gdfp_req=1&output=vast&unviewed_position_start=1&env=vp&impl=s&correlator=[random]',
type: 'vast',
},
position: 'pre',
},
{
tag: {
// nonskippable
url: 'https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/single_ad_samples&sz=640x480&cust_params=sample_ct%3Dlinear&ciu_szs=300x250%2C728x90&gdfp_req=1&output=vast&unviewed_position_start=1&env=vp&impl=s&correlator=[random]',
type: 'vast',
},
position: '10',
},
];
var config = {
key: 'YOUR KEY HERE',
ui: false,
remotecontrol: {
type: 'googlecast',
},
advertising: {},
playback: {
muted: true,
},
};
var uiConfig = {
metadata: {
// title: 'UI Demo Video Title',
// description: 'Bacon ipsum dolor amet t-bone prosciutto cupim kevin tongue, venison tail pork beef ribs boudin beef shoulder bacon jerky. Pork tail spare ribs jowl jerky andouille corned beef frankfurter. Burgdoggen meatloaf short ribs andouille tail tri-tip cow kevin doner.',
// markers: [
// { time: 50, title: 'Chapter 1' },
// { time: 80, title: 'Chapter Two' },
// { time: 200 },
// { time: 300, title: 'Again a chapter with a title' },
// ],
// recommendations: [
// {
// title: 'Recommendation 1: The best video ever',
// resource: {
// dash: 'https://cdn.bitmovin.com/content/assets/sintel/sintel.mpd',
// poster: 'https://cdn.bitmovin.com/content/assets/sintel/poster.png',
// },
// duration: 10.4
// },
// {
// title: 'Recommendation 2: The second best video',
// resource: { url: 'http://bitmovin.com', thumbnail: 'https://placehold.co/300x300/222/222' },
// duration: 64
// },
// {
// title: 'Recommendation 2: The second best video',
// resource: { url: 'http://bitmovin.com' },
// },
// ],
},
// seekbarSnappingRange: 0,
includeWatermark: true,
};
var player;
var uiManager;
bitmovin.playerui.UIManager.setLocalizationConfig({ language: 'en', browserLanguageDetection: false });
bitmovin.player.Player.addModule(bitmovin.player['advertising-bitmovin'].default);
var playerSetup = function (config) {
player = new bitmovin.player.Player(document.getElementById('player'), config);
// Update API methods when a module namespace becomes available
player.on(bitmovin.player.PlayerEvent.ModuleReady, function () {
updateApiMethods();
});
const playgroundConfig = getConfigFromStorage();
// Add UI
if (playgroundConfig.uiOption != null && bitmovin.playerui.UIFactory[playgroundConfig.uiOption] != null) {
uiManager = bitmovin.playerui.UIFactory[playgroundConfig.uiOption](player, uiConfig);
} else {
localStorage.clear();
uiManager = bitmovin.playerui.UIFactory.buildUI(player, uiConfig);
}
player.load(sources[playgroundConfig.source || 'fullyFeatured']).then(
function () {
console.log('source successfully loaded');
if (playgroundConfig.adsEnabled) {
scheduleAds();
}
},
function (errorEvent) {
console.log('error while loading source', errorEvent);
},
);
};
playerSetup(config);
$.each(sources, function (key, value) {
$('#config-source').append($('<option></option>').attr('value', key).text(key));
});
$('#config-source').change(function () {
player.unload();
player.load(sources[$(this).val()]);
storeConfigInStorage();
});
var printResult = function (result, method) {
console.log(method, result);
$('#function-call-output').html(JSON.stringify(result, null, 2));
};
function enumerateApiMethods() {
var enumerate = function (target) {
var members = [];
for (var member in target) {
if (typeof target[member] == 'function') {
members.push(member);
}
}
return members;
};
// Collect all API methods of the player object
var apiMethods = enumerate(player);
var namespaces = ['subtitles', 'ads', 'vr', 'buffer'];
namespaces
.filter(function (namespaceName) {
return player[namespaceName] != null;
})
.forEach(function (namespaceName) {
enumerate(player[namespaceName]).forEach(function (namespaceMember) {
apiMethods.push(namespaceName + '.' + namespaceMember);
});
});
return apiMethods.sort();
}
// Define custom parameters for testing API methods that take parameters
var apiMethodParameters = {
setVolume: [
[, [0]],
[, [50]],
[, [100]],
[
'+10',
function () {
return [player.getVolume() + 10];
},
],
],
seek: [
[, [0]],
[
'current+60s',
function () {
return [player.getCurrentTime() + 60];
},
],
[
'current+5min',
function () {
return [player.getCurrentTime() + 60 * 5];
},
],
[
'current-60s',
function () {
return [player.getCurrentTime() - 60];
},
],
[
'current-5min',
function () {
return [player.getCurrentTime() - 60 * 5];
},
],
[
'duration-1s',
function () {
return [player.getDuration() - 1];
},
],
],
setPlaybackSpeed: [
[, [0]],
[, [0.1]],
[, [0.25]],
[, [0.5]],
[, [1]],
[, [2]],
[, [4]],
[, [10]],
],
};
if (player.buffer /* added in player 8.1.0 */) {
apiMethodParameters['buffer.getLevel'] = [
[, [bitmovin.player.BufferType.ForwardDuration, bitmovin.player.MediaType.Video]],
[, [bitmovin.player.BufferType.ForwardDuration, bitmovin.player.MediaType.Audio]],
[, [bitmovin.player.BufferType.BackwardDuration, bitmovin.player.MediaType.Video]],
[, [bitmovin.player.BufferType.BackwardDuration, bitmovin.player.MediaType.Audio]],
];
}
function updateApiMethods() {
$('#playback-controls').empty(); // Remove old buttons
var addApiMethodButton = function (methodName, parametersName, parametersGeneratorFn) {
parametersGeneratorFn =
parametersGeneratorFn ||
function () {
return [];
};
parametersName = parametersName || '';
var button = $(
'<button class="btn btn-primary btn-sm">' + methodName + '(' + parametersName + ')' + '</button>',
);
$('#playback-controls').append(button).append(' ');
button.click(function () {
// Execute method with parameter
var levels = methodName.split('.');
var method = player[methodName];
if (levels.length === 2) {
// Method lies within a namespace
method = player[levels[0]][levels[1]];
}
var evaluatedParameters = parametersGeneratorFn();
printResult(method.apply(player, evaluatedParameters), methodName);
});
};
enumerateApiMethods().forEach(function (apiMethod) {
if (apiMethodParameters[apiMethod]) {
apiMethodParameters[apiMethod].forEach(function (apiMethodCallDefinition) {
var name = apiMethodCallDefinition[0];
var parameters = apiMethodCallDefinition[1];
var parametersGeneratorFn = null;
if (typeof parameters === 'function') {
parametersGeneratorFn = parameters;
} else if (Array.isArray(parameters)) {
name = name || String(parameters);
// Convert static parameters to generator function
parametersGeneratorFn = function () {
return parameters;
};
} else {
throw new Error(
'invalid method call definition - parameters must be defined as array or function returning an array',
);
}
addApiMethodButton(apiMethod, name, parametersGeneratorFn);
});
} else {
// No parameters defined, add a simple parameterless method call
addApiMethodButton(apiMethod);
}
});
}
updateApiMethods();
var actions = {
'Reload source': function () {
player.load(player.getSource());
},
'Recreate player': function () {
var config = player.getConfig();
player.destroy().then(function () {
playerSetup(config);
});
},
'Add subtitle': function () {
return player.subtitles.add({
url: 'https://cdn.bitmovin.com/content/assets/sintel/subtitles/subtitles_en.vtt',
id: 'test',
lang: 'ru',
kind: 'subtitle',
label: 'Test Subtitle',
});
},
'Remove subtitle': function () {
return player.subtitles.remove('test');
},
'Schedule skippable VAST preroll ad': function () {
return player.ads.schedule({
tag: {
url: 'https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/32573358/skippable_ad_unit&impl=s&gdfp_req=1&env=vp&output=xml_vast2&unviewed_position_start=1&url=http%3A%2F%2Freleasetest.dash-player.com%2Fads%2F&description_url=[description_url]&correlator=[random]',
type: 'vast',
},
position: 'pre',
});
},
'Schedule skippable VAST ad in 2 secs': function () {
return player.ads.schedule({
tag: {
url: 'https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/32573358/skippable_ad_unit&impl=s&gdfp_req=1&env=vp&output=xml_vast2&unviewed_position_start=1&url=http%3A%2F%2Freleasetest.dash-player.com%2Fads%2F&description_url=[description_url]&correlator=[random]',
type: 'vast',
},
position: String(player.getCurrentTime() + 2),
});
},
'Schedule nonskippable VAST ad in 2 secs': function () {
return player.ads.schedule({
tag: {
url: 'https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/32573358/2nd_test_ad_unit&ciu_szs=300x100&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&url=[referrer_url]&description_url=[description_url]&correlator=[random]',
type: 'vast',
},
position: String(player.getCurrentTime() + 2),
});
},
'Schedule skippable VAST postroll ad': function () {
return player.ads.schedule({
tag: {
url: 'https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/32573358/skippable_ad_unit&impl=s&gdfp_req=1&env=vp&output=xml_vast2&unviewed_position_start=1&url=http%3A%2F%2Freleasetest.dash-player.com%2Fads%2F&description_url=[description_url]&correlator=[random]',
type: 'vast',
},
position: 'post',
});
},
'CEA-608 subtitle test pattern (60 secs)': function () {
// Get UIManager's internal player wrapper with fireEventInUI method
// NOTE: this is a hack that accesses a private property which can change any time, do not use in production!
var player = uiManager.currentUi.playerWrapper.getPlayer();
var cueEnter = function (text, row, column) {
var cue = {
text: text,
position: {
row: row,
column: column,
},
};
player.fireEventInUI(bitmovin.player.PlayerEvent.CueEnter, cue);
return cue;
};
var cueExit = function (cue) {
player.fireEventInUI(bitmovin.player.PlayerEvent.CueExit, cue);
};
// Create list of cues and display them
var cues = [];
for (var row = 0; row < 15; row++) {
var text = 'Test' + (row + 1);
cues.push(cueEnter(text, row, 0));
cues.push(cueEnter(text, row, 15 - text.length / 2 + 1));
cues.push(cueEnter(text, row, 31 - text.length + 1));
}
// Remove cues after some time
setInterval(function () {
cues.forEach(function (cue) {
cueExit(cue);
});
}, 60000);
},
'CEA-608 subtitle test pattern 2': function () {
// Get UIManager's internal player wrapper with fireEventInUI method
// NOTE: this is a hack that accesses a private property which can change any time, do not use in production!
var player = uiManager.currentUi.playerWrapper.getPlayer();
var cueEnter = function (text, row, column) {
var cue = {
text: text,
position: {
row: row,
column: column,
},
};
player.fireEventInUI(bitmovin.player.PlayerEvent.CueEnter, cue);
return cue;
};
var cueExit = function (cue) {
player.fireEventInUI(bitmovin.player.PlayerEvent.CueExit, cue);
};
// Create list of cues and display them
var cues = [];
var column = 0;
var text = 'XOXOX XOXO OXOX XOXOXOXOXOX XOXO';
var enqueue = function () {
for (var row = 0; row < 15; row++) {
cues.push(cueEnter(text.substr(0, 32 - column), row, column));
if (column >= 2) {
cues.push(cueEnter(text.substr(0, column - 1), row, 0));
}
}
// Remove cues after some time
setTimeout(function () {
cues.forEach(function (cue) {
cueExit(cue);
});
cues = [];
if (column < 32) {
enqueue();
}
column++;
}, 500);
};
enqueue();
},
'subtitle test pattern': function () {
// Get UIManager's internal player wrapper with fireEventInUI method
// NOTE: this is a hack that accesses a private property which can change any time, do not use in production!
var player = uiManager.currentUi.playerWrapper.getPlayer();
var cueEnter = function (text) {
var cue = {
text: text,
};
player.fireEventInUI(bitmovin.player.PlayerEvent.CueEnter, cue);
return cue;
};
var cueExit = function (cue) {
player.fireEventInUI(bitmovin.player.PlayerEvent.CueExit, cue);
};
var cues = [];
var allowedChars = 'xoxoxoxoxoxo ';
var enqueue = function () {
// Create array of random length
var chars = new Array(Math.floor(Math.random() * 70)).fill(0);
// Populate array with random chars
chars = chars.map(function () {
var charIndex = Math.floor(Math.random() * allowedChars.length);
return allowedChars.substr(charIndex, 1);
});
// Join array chars to string
var text = chars.join('');
// Add new cue
cues.push(cueEnter(text));
// Remove oldest cue
if (cues.length > 4) {
cueExit(cues[0]);
cues = cues.slice(1);
}
// Remove cues after some time
setTimeout(function () {
enqueue();
}, 200);
};
enqueue();
},
'Animate player size/height': function () {
var playerContainer = player.getContainer();
var initialHeight = playerContainer.offsetHeight;
var minHeight = 300;
var maxHeight = 1400;
$(playerContainer)
.animate({ height: minHeight }, 1000)
.animate({ height: maxHeight }, 1000)
.animate({ height: initialHeight }, 1000);
},
'Show Buffering overlay': function () {
const player = uiManager.currentUi.playerWrapper.getPlayer();
player.fireEventInUI(bitmovin.player.PlayerEvent.StallStarted, {});
},
'Enter Fullscreen': function () {
const player = uiManager.currentUi.playerWrapper.getPlayer();
player.setViewMode(bitmovin.player.ViewMode.Fullscreen);
},
};
$.each(actions, function (title, handler) {
$('#actions')
.append(
$('<button type="button" class="btn btn-primary btn-sm">' + title + '</button>').click(function () {
printResult(handler(), title);
}),
)
.append(' ');
});
function generateEventCheckboxes(
rootElementSelector,
checkboxIdSelectorPrefix,
eventNames,
eventNameToValueFn,
subscribeFn,
unsubscribeFn,
loggingPrefix,
) {
$(checkboxIdSelectorPrefix + 'all').change(function () {
var checked = $(this).prop('checked');
$(rootElementSelector + ' input').each(function (index) {
// Skip first checkbox (the 'all' checkbox)
if (index === 0) {
return;
}
$(this).prop('checked', checked);
$(this).trigger('change');
});
});
// Add a checkbox for each event to toggle logging to console
eventNames.sort().forEach(function (eventName, eventIndex) {
var event = eventNameToValueFn(eventName);
var checkbox = $('<input>', {
type: 'checkbox',
id: checkboxIdSelectorPrefix + eventIndex++,
});
var eventConsoleLogger = function (event) {
console.log(loggingPrefix + eventName, event);
};
checkbox.change(function () {
var checkbox = $(this);
var checked = checkbox.prop('checked');
if (checked) {
subscribeFn(event, eventConsoleLogger);
} else {
unsubscribeFn(event, eventConsoleLogger);
}
});
$(rootElementSelector).append($('<label>').append(checkbox).append(' ').append(eventName).append(' '));
});
}
generateEventCheckboxes(
'#events',
'#config-events-',
Object.keys(bitmovin.player.PlayerEvent),
function (eventName) {
return bitmovin.player.PlayerEvent[eventName];
},
function (event, handler) {
player.on(event, handler);
},
function (event, handler) {
player.off(event, handler);
},
'PlayerEvent.',
);
var nativeVideoEvents = [
'abort',
'canplay',
'canplaythrough',
'durationchange',
'emptied',
'encrypted ',
'ended',
'error',
'interruptbegin',
'interruptend',
'loadeddata',
'loadedmetadata',
'loadstart',
'mozaudioavailable',
'pause',
'play',
'playing',
'progress',
'ratechange',
'seeked',
'seeking',
'stalled',
'suspend',
'timeupdate',
'volumechange',
'waiting',
'audioTracks.addtrack',
'audioTracks.removetrack',
'videoTracks.addtrack',
'videoTracks.removetrack',
'textTracks.addtrack',
'textTracks.removetrack',
];
generateEventCheckboxes(
'#native-events',
'#config-native-events-',
nativeVideoEvents,
function (eventName) {
return eventName;
},
function (event, handler) {
var target = player.getVideoElement();
var splitEvent = event.split('.');
if (splitEvent.length === 2) {
target = target[splitEvent[0]];
event = splitEvent[1];
}
if (target) {
target.addEventListener(event, handler);
}
},
function (event, handler) {
var target = player.getVideoElement();
var splitEvent = event.split('.');
if (splitEvent.length === 2) {
target = target[splitEvent[0]];
event = splitEvent[1];
}
if (target) {
target.removeEventListener(event, handler);
}
},
'video.',
);
// Collect all UI factory methods which are basically the different built-in types
const configUISelect = $('#config-ui');
const collectUIMethods = function (clazz, filter, label) {
const convertToOption = function (value) {
return $('<option></option>').attr('value', value).text(value);
};
let demoGroup = $(`<optgroup label="${label}" />`);
Object.keys(clazz)
.filter(member => {
return typeof clazz[member] === 'function';
})
.filter(filter)
.map(convertToOption)
.forEach(o => {
demoGroup.append(o);
});
demoGroup.appendTo(configUISelect);
};
const uiFactoryData = [
{
className: bitmovin.playerui.UIFactory,
filter: member => {
return member.indexOf('build') === 0;
},
label: 'UIs',
},
];
uiFactoryData.forEach(data => {
collectUIMethods(data.className, data.filter, data.label);
});
// Refresh UI when a factory is selected
configUISelect.change(function () {
uiManager.release();
var factoryMethod = $(this).val();
uiManager = bitmovin.playerui.UIFactory[factoryMethod](player, uiConfig);
storeConfigInStorage();
});
var scheduleAdsCheckbox = $('#config-ads');
scheduleAdsCheckbox.change(function () {
const isChecked = $(this).is(':checked');
if (isChecked) {
scheduleAds();
} else {
const ads = player.ads.list();
ads.forEach(function (ad) {
player.ads.discardAdBreak(ad.id);
});
}
storeConfigInStorage();
});
var displayPortraitCheckbox = $('#display-portrait');
displayPortraitCheckbox.change(function () {
const isChecked = $(this).is(':checked');
const playerWrapperContainer = $('.player-wrapper');
if (isChecked) {
playerWrapperContainer.addClass('portrait');
} else {
playerWrapperContainer.removeClass('portrait');
}
storeConfigInStorage();
});
function scheduleAds() {
adBreaks.forEach(function (adBreak) {
player.ads.schedule(adBreak);
});
}
// Populate stats
$('#userAgent').html(navigator.userAgent);
function storeConfigInStorage() {
var data = {
adsEnabled: $('#config-ads').is(':checked'),
source: $('#config-source').val(),
uiOption: $('#config-ui').val(),
portraitEnabled: $('#display-portrait').is(':checked'),
};
try {
window.localStorage.setItem('bmpui-playground-config', JSON.stringify(data));
} catch (e) {
console.error('Local storage access denied', e);
}
}
function getConfigFromStorage() {
let config = {};
try {
const loadedEntry = window.localStorage.getItem('bmpui-playground-config');
if (!loadedEntry) {
console.log('No local storage entry found');
return {};
}
config = JSON.parse(loadedEntry);
} catch (e) {
console.error('Problem loading playground config from localStorage', e);
}
return config || {};
}
(function applyInitialConfiguration() {
const config = getConfigFromStorage();
if ($('#config-source') && config.source) {
$('#config-source').val(config.source);
}
if ($('#config-ui') && config.uiOption) {
$('#config-ui').val(config.uiOption);
}
if ($('#config-ads') && config.adsEnabled !== undefined) {
$('#config-ads').prop('checked', config.adsEnabled);
}
if ($('#display-portrait') && config.portraitEnabled !== undefined) {
$('#display-portrait').prop('checked', config.portraitEnabled);
$('#display-portrait').trigger('change');
}
})();
</script>
</body>
</html>