define('application/views/player', [
'rofl/widgets/container',
'application/widgets/player/channelswitcher',
'application/widgets/player/warningbox',
'application/widgets/clock',
'application/widgets/detail/iconbutton',
'rofl/widgets/label',
'application/widgets/player/zappbanner',
'application/widgets/gradient',
'application/widgets/player/programdetails',
'rofl/lib/utils',
'rofl/devices/input/pointer',
'antie/runtimecontext',
'application/widgets/player/controls',
'application/widgets/pointerfocusablebutton',
'application/managers/recording',
'application/managers/session',
'rofl/lib/l10n',
'antie/storageprovider',
'rofl/devices/mediaplayer/mediaplayer',
'antie/events/keyevent',
'application/managers/feature',
'application/utils',
'application/helpers/parentalHelper',
'application/widgets/player/miniepg',
'rofl/analytics/web/google'
], function (
Container,
ChannelSwitcher,
WarningBox,
Clock,
IconButton,
Label,
ZappBanner,
Gradient,
ProgramDetails,
Utils,
PointerManager,
RuntimeContext,
Controls,
PointerButton,
RecordingManager,
SessionManager,
L10N,
StorageProvider,
MediaPlayer,
KeyEvent,
FeatureManager,
AppUtils,
ParentalHelper,
MiniEPG,
GoogleAnalytics
) {
'use strict';
var device = RuntimeContext.getDevice(),
pointerManager,
application = RuntimeContext.getCurrentApplication(),
configuration = application.getConfiguration(),
recordingManager = RecordingManager.getInstance(),
sessionManager = SessionManager.getInstance(),
mediaPlayer = device.getMediaPlayer(),
l10n = L10N.getInstance(),
storage = device.getStorage(StorageProvider.STORAGE_TYPE_SESSION),
GA = GoogleAnalytics.getInstance(),
layout = application.getLayout(),
descriptionLayout = layout.player.description;
return Container.extend({
/**
* Initialises the player view.
*/
init: function init () {
if (!pointerManager) {
pointerManager = PointerManager.getInstance();
this._pointerSupport = true;
}
this._handleParental = false;
this._currentProgram = null;
init.base.call(this);
this._build();
this.addClass(['view', 'player-view']);
},
/**
* Builds the player view.
*
* @private
*/
_build: function () {
this._buildGradients();
this._buildMainContainer();
this._buildZapControlsContainer();
this._buildZappBanner();
this._buildControls();
this._buildProgamInfo();
this._buildClock();
this._buildChannelSwitcher();
this._buildWarningBox();
this._buildBackButton();
this._buildMiniEpg();
},
/**
* Builds WarningBox.
*
* @private
*/
_buildWarningBox: function () {
var warningBox = this._warningBox = new WarningBox();
this.appendChildWidget(warningBox);
},
/**
* Builds the back button.
*
* @private
*/
_buildBackButton: function () {
var btn = this._backButton = new IconButton('back-button', '', ['icon', 'icon-back-v2']);
this._backButton.addClass('back-button');
this.appendChildWidget(btn);
},
/**
* Builds the main container that will include zappingbanner/controls, miniEPG and info.
*
* @private
*/
_buildMainContainer: function () {
var mainContainer = this._mainContainer = new Container();
mainContainer.addClass('main-content');
this.appendChildWidget(mainContainer);
},
/**
* Builds the main container that will include zappBanner & controls.
*
* @private
*/
_buildZapControlsContainer: function () {
var zapControlsContainer = this._zapControlsContainer = new Container();
zapControlsContainer.addClass('zapping-controls-content');
zapControlsContainer.appendChildWidget(this._bottomGradient);
this._mainContainer.appendChildWidget(zapControlsContainer);
},
/**
* Builds the video controls.
*
* @private
*/
_buildControls: function () {
var controls = this._controls = new Controls();
this._controls.addEventListener('focus', Utils.bind(this._onFocus, this));
this._zapControlsContainer.appendChildWidget(controls);
},
/**
* Builds the ZappBanner.
*
* @private
*/
_buildZappBanner: function () {
var zappBanner = this._zappBanner = new ZappBanner();
this._zapControlsContainer.appendChildWidget(zappBanner);
},
/**
* Builds the channel switcher.
*
* @private
*/
_buildChannelSwitcher: function () {
var channelSwitcher = this._channelSwitcher = new ChannelSwitcher();
this.appendChildWidget(channelSwitcher);
},
/**
* Builds the gradients.
*
* @private
*/
_buildGradients: function () {
var topDownGradient = this._topGradient = new Gradient(Gradient.DIRECTIONS.TOPDOWN);
this._bottomGradient = new Gradient(Gradient.DIRECTIONS.BOTTOMUP);
this.appendChildWidget(topDownGradient);
},
/**
* Builds the clock.
*
* @private
*/
_buildClock: function () {
var clock = this._clock = new Clock();
this.appendChildWidget(clock);
},
/**
* Builds the mini epg.
*
* @private
*/
_buildMiniEpg: function () {
this._miniEPG = this._mainContainer.appendChildWidget(new MiniEPG());
},
/**
* Builds the program detail info.
*/
_buildProgamInfo: function () {
var programDetails = this._programDetails = new ProgramDetails();
this._mainContainer.appendChildWidget(programDetails);
},
/**
* Show progress bar.
*
*/
showProgress: function () {
this._zappBanner.getProgress().show();
},
/**
* Hide progress bar.
*
* @param {Object} [options] - Pass animation options if required.
*
*/
hideProgress: function (options) {
this._zappBanner.getProgress().hide(options);
},
/**
* Shows the ui.
*
* @param {boolean} [hide] - True if it should hide after 3 seconds.
* @param {boolean} [hideClock] - True if clock should be hidden.
*/
showUI: function (hide, hideClock) {
var zappbannerDissapearTime = configuration.ZAPPBANNER_DISSAPPEAR_TIME,
playbackStatus = this.parentWidget.getPlaybackStatus(),
isProgressVisible = this._zappBanner.getProgress().isVisible();
clearTimeout(this._hideUITimeout);
if (!this.isUIVisible()) {
this._mainContainer.show();
this._backButton.show();
this._topGradient.show();
this._controls.show();
if (!hideClock) {
this._clock.show();
}
}
if (!playbackStatus.active && !isProgressVisible) {
playbackStatus.callback = Utils.bind(this.showProgress, this);
} else {
if (!isProgressVisible && !playbackStatus.callback) {
playbackStatus.callback = null;
this.showProgress();
}
}
/*
* Hide recording button for user without such a functionality,
* to prevent any unwanted showings.
*/
if (application.getUser() && !application.getUser().canRecord()) {
this._controls.hideRecord();
}
if (hide) {
this._hideUITimeout = setTimeout(Utils.bind(this.hideUI, this), zappbannerDissapearTime);
}
},
/**
* Hides the ui.
*
* @param {Object} [opts] - Options.
*/
hideUI: function (opts) {
if (this.isContentExpanded()) {
return;
}
if (this.isUIVisible()) {
this._mainContainer.hide(opts);
this._backButton.hide(opts);
this._clock.hide(opts);
this._topGradient.hide(opts);
this._controls.hide(opts);
this.closeExpandedContents();
}
},
/**
* Returns true if the UI is visible.
*
* @returns {boolean} - True if the UI is visible.
*/
isUIVisible: function () {
// Use the zappbanner as the ui default visibility.
return this._mainContainer.isVisible();
},
/**
* Enable pointer support.
*/
enablePointerSupport: function () {
this._pointerSupport = true;
this._zappBanner.getProgress().setPointerSupport(true);
Utils.each(this._controls._childWidgets, function (button) {
if (button instanceof PointerButton) {
button.setPointerSupport(true);
}
});
if (!application.getUser().canRecord()) {
this._controls.hideRecord();
}
},
/**
* Disable pointer support.
*/
disablePointerSupport: function () {
this._pointerSupport = false;
this._zappBanner.getProgress().setPointerSupport(false);
Utils.each(this._controls._childWidgets, function (button) {
if (button instanceof PointerButton) {
button.setPointerSupport(false);
}
});
},
/**
* Hides the warning box.
*
* @param {boolean} [skipAnim] - True if should skip the animations.
*/
hideWarningBox: function (skipAnim) {
this._warningBox.hide({
skipAnim: skipAnim
});
},
/**
* Shows the warning box.
*
* @param {Object} config - The config.
*/
showWarningBox: function (config) {
this._warningBox.show(config);
},
/**
* Sets the progress.
*
* @param {Object} data - The data.
*/
setLiveProgress: function (data) {
this._zappBanner.setLiveProgress(data);
if (data.behind < 0) {
this._controls.disableLiveButton(false);
} else {
this.resetLiveLabels();
}
},
/**
* Prevents playing for locked asset.
*
* @param {Object} event - Event.
* @private
*/
_preventPlayWhenPlaying: function (event) {
if (event.type === 'playing') {
mediaPlayer.pause();
}
},
/**
* Hides player under black overlay.
*
* @private
*/
_hidePlayer: function () {
mediaPlayer.addEventCallback(this, this._preventPlayWhenPlaying);
if (!this.hasClass('player-hidden')) {
this.addClass('player-hidden');
}
this._controls.setPlayerHiddenFlag(true);
},
/**
* Shows player after parental is passed.
*
* @private
*/
_showPlayer: function () {
mediaPlayer.removeEventCallback(this, this._preventPlayWhenPlaying);
if (this.hasClass('player-hidden')) {
this.removeClass('player-hidden');
}
if (mediaPlayer.getState() === MediaPlayer.STATE.PAUSED) {
mediaPlayer.resume();
}
this._controls.setPlayerHiddenFlag(false);
},
/**
* Adds limitation error to black overlay.
*
* @private
*/
_addLimitWarning: function () {
var warningContainer = new Container('limit-warning'),
warningContent = new Container('limit-warning-content'),
warningTitle = new Label({ text: l10n.get('parental.player_warning.title'), classNames: ['warning-title'] }),
description = new Label({ text: l10n.get('parental.player_warning.description'), classNames: ['warning-description'], enableHTML: true });
warningContainer.appendChildWidget(warningContent);
warningContent.appendChildWidget(warningTitle);
warningContent.appendChildWidget(description);
this.appendChildWidget(warningContainer);
},
/**
* Sets the VOD progress.
*
* @param {Object} data - The data.
*/
setVODProgress: function (data) {
this._zappBanner.setVODProgress(data);
},
/**
* Stores timestamp to session storage that represents when parental was locked.
*
* @private
*/
_storeLockTime: function () {
storage.setItem('parentalLockTimestamp', application.getDate().getTime());
},
/**
* Checks whether still locked.
*
* @returns {boolean} - True if locked.
* @private
*/
_isLockedFor24H: function () {
var storedTimestamp = parseInt(storage.getItem('parentalLockTimestamp'));
if (!storedTimestamp) {
return false;
}
return (application.getDate().getTime() - storedTimestamp) < 1000 * 60 * 60 * 24;
},
/**
* Starts parental control flow.
*
* @private
*/
_startParentalFlow: function () {
if (!this._currentProgram.getParentalWhitelisted() && !sessionManager.getUserPin()) {
this._hidePlayer();
if (!this._isLockedFor24H()) {
if (mediaPlayer.getState() === MediaPlayer.STATE.PLAYING) {
mediaPlayer.pause();
}
application.route('parentalpin', {
successCallback: Utils.bind(function () {
this._showPlayer();
this.onPlay();
this._currentProgram.setParentalWhitelisted();
}, this),
escapeCallback: Utils.bind(ParentalHelper.parentaleEscapeCallback, this.parentWidget, true),
errorCallback: Utils.bind(function (error) {
// in case of 5 times wrong pin
if (Utils.getNested(error, 'errorDescription') === '403-8117') {
this._addLimitWarning();
this._storeLockTime();
}
}, this)
});
}
}
},
/**
* Checks whether asset is child protected.
*
* @returns {boolean} - True if is child protected.
*/
_isLocked: function () {
var parentalControlParams = SessionManager.getInstance().getUserPCParams(),
parentalControlLevel = parseInt(Utils.getNested(parentalControlParams, 'parentalControlLevel')),
parentalControlGenres = Utils.getNested(parentalControlParams, 'parentalGenres'),
genres = this._currentProgram.getParentalGenres(),
isGenreProtected,
VODContentTypes = ['VOD', 'GROUP_OF_BUNDLES', 'BUNDLE'];
if (VODContentTypes.indexOf(this.getContentType()) >= 0) {
parentalControlLevel = parseInt(Utils.getNested(parentalControlParams, 'parentalVODControlLevel'));
}
isGenreProtected = function () {
var i;
if (parentalControlGenres.length) {
for (i = 0; i < parentalControlGenres.length; i++) {
if (genres.indexOf(parentalControlGenres[i]) > -1) {
return true;
}
}
}
return false;
};
return !(parentalControlLevel >= this._currentProgram.getPCLevel()) || isGenreProtected();
},
/**
* Handles onFocus.
*
* @private
*/
_onFocus: function () {
this._handleParental = false;
},
/**
* Attempts to set the program.
*
* @param {Object} programParams - Contains the params for setting the program.
* @param {Object} programParams.program - The program data to set.
* @param {string} programParams.type - The type.
* @param {boolean|null} [programParams.hasNextEpisode] - Flag whether it has next episode or not.
*/
setProgram: function (programParams) {
var controls = this._controls,
data = programParams.program,
type = programParams.type,
hasNextEpisode = programParams.hasNextEpisode,
hasRecording,
hasInfo = !!data.getDetailsAction();
this._currentProgram = data;
type = type.toLowerCase();
this._showPlayer();
if (type === 'vod' || type === 'vod_movie') {
// TODO: Fix this at some point.
type = type.toUpperCase();
}
if (type === 'VOD' || type === 'VOD_MOVIE' || type === 'RECORDING') {
if (hasInfo) {
controls.showInfo();
} else {
controls.hideInfo();
}
controls.hideNowNext();
controls.hideLive();
controls.hideRecord();
if (hasNextEpisode) {
controls.showNextEpisode();
} else {
controls.hideNextEpisode();
}
controls.setStyleTo('display', 'block');
} else if (type === 'live' || type === 'restart') {
this._miniEPG.setChannel(data.getChannel().getId());
controls.showInfo();
controls.showReplay();
controls.showNowNext();
controls.disableLiveButton(type !== 'restart');
controls.showLive();
controls.hideNextEpisode();
if (application.getUser().canRecord()) {
controls.showRecord();
} else {
controls.hideRecord();
}
controls.setStyleTo('display', 'block');
} else {
controls.setStyleTo('display', 'none');
}
hasRecording = recordingManager.hasRecording(data.getId(), data.getSeriesId());
if (!hasRecording.episode && !hasRecording.series) {
controls.onRecord();
} else {
controls.onRecording();
}
this._zappBanner.setProgram(data, type);
},
/**
* Resets live button and labels to default live state.
*/
resetLiveLabels: function () {
this._zappBanner.resetPauseLabel();
this._controls.disableLiveButton(true);
},
/**
* Attempts to set the full promo video.
*
* @param {Object} data - The data to set.
* @param {string} type - The type.
*/
setPromo: function (data, type) {
var controls = this._controls;
this._currentProgram = data;
type = type.toLowerCase();
this._showPlayer();
controls.hideReplay();
controls.hideRecord();
controls.hideInfo();
controls.hideNowNext();
controls.hideNextEpisode();
controls.hideLive();
controls.hideMenu();
controls.focus(); // Focus the pause button.
controls.setStyleTo('display', 'block');
this._zappBanner.setProgram(data, type);
},
/**
* Returns the channel switcher.
*
* @returns {Object} - The channel switcher.
*/
getChannelSwitcher: function () {
return this._channelSwitcher;
},
/**
* Returns the zappbanner.
*
* @returns {Object} - The zappbanner.
*/
getZappbanner: function () {
return this._zappBanner;
},
/**
* Gets the back button.
*
* @returns {Object} - The back button.
*/
getBackButton: function () {
return this._backButton;
},
/**
* Returns the controls.
*
* @returns {Object} - The controls.
*/
getControls: function () {
return this._controls;
},
/**
* Removes the mouse over handler.
*/
removeMouseOverHandler: function removeMouseOverHandlerFn () {
if (this.outputElement) {
this.outputElement.onmouseover = null;
}
},
/**
* Attempts to reset the controls.
*/
resetControls: function () {
this._controls.reset();
},
/**
* Updates the seek speed.
*
* @param {number} speed - The seek speed.
*/
updateSeekSpeed: function (speed) {
this._controls.updateSeekSpeed(speed);
},
/**
* Handles the mouse over event.
*/
mouseOverHandler: function () {
if (pointerManager.pointerIsOn() && this._pointerSupport) {
this.showUI(device.getMediaPlayer().getState() !== 'paused');
}
},
/**
* Adds the mouse over handler.
*/
addMouseOverHandler: function () {
if (this.outputElement) {
this.outputElement.onmouseover = Utils
.bind(this.mouseOverHandler, this);
}
},
/**
* Should be called when the video player plays.
*/
onPlay: function () {
this._controls.onPlay();
},
/**
* Should be called when the video player pauses.
*/
onPause: function () {
this._controls.onPause();
},
/**
* Sets the record state.
*/
onRecord: function () {
this._controls.onRecord();
},
/**
* Sets the recording state.
*/
onRecording: function () {
this._controls.onRecording();
},
/**
* Renders the button.
*
* @returns {Object} DOM node.
*/
render: function render () {
var element = render.base.call(this, device);
this.addMouseOverHandler();
return element;
},
/**
* Disposes of the button.
*/
dispose: function dispose () {
this.removeMouseOverHandler();
dispose.base.call(this);
},
/**
* Focuses the view.
*/
focus: function focus () {
focus.base.call(this);
this._controls.focus();
},
/**
* Show/hide the program detail for the current live program.
*
* @param {boolean} show - True to show info detail, false to hide it.
* @param {Object} programDetails - The program containing the details to show.
**/
showProgramInfo: function (show, programDetails) {
var infoContainerHeight = 0,
metadataOutputElement,
descriptionLayoutHeight;
this._controls.onProgramInfo(!show);
if (show) {
this.showMiniEPG(true);
this._programDetails.setDataItem(programDetails);
// Retrieve metadata widget and apply dynamic height to the info container.
metadataOutputElement = this._programDetails.getChildWidgetByIndex(1).outputElement;
descriptionLayoutHeight = descriptionLayout.height;
infoContainerHeight = metadataOutputElement.getBoundingClientRect().height;
infoContainerHeight = (infoContainerHeight < descriptionLayoutHeight ? descriptionLayoutHeight : infoContainerHeight) + 'px';
this._programDetails.setStyleTo('height', infoContainerHeight);
this._programDetails.addClass('expand');
} else {
this._programDetails.setStyleTo('height', infoContainerHeight);
this._programDetails.removeClass('expand');
}
},
/**
* Returns the program details container.
*
* @returns {Object} - The program details container.
*/
getProgramDetails: function () {
return this._programDetails;
},
/**
* Returns the mini epg.
*
* @returns {Object} - The mini epg.
*/
getMiniEPG: function () {
return this._miniEPG;
},
/**
* Shows the mini epg.
*
* @param {boolean} hide - True if the miniEPG should be hidden.
*/
showMiniEPG: function (hide) {
this._controls.onMiniEPG(hide);
if (this._miniEPG.isExpanded() || hide) {
this._miniEPG.removeClass('expand');
} else {
this.showProgramInfo(false);
GA.onEvent(
'Action',
'MiniEPGOpened',
{
eventLabel: 'LiveTV'
}
);
this._miniEPG.addClass('expand');
}
},
/**
* Checks if there's come content currently expanded.
*
* @returns {boolean} - True if there's expanded content, false if not.
*/
isContentExpanded: function () {
return this._programDetails.isExpanded() || this._miniEPG.isExpanded();
},
/**
* Closes any expanded content (i.e. miniEPG or program's detail).
*/
closeExpandedContents: function () {
if (this._programDetails.isExpanded()) {
this.showProgramInfo(false);
}
this.showMiniEPG(true);
}
});
});