define('application/components/details/epg', [
'application/components/details/details',
'rofl/widgets/container',
'rofl/events/keyevent',
'rofl/lib/utils',
'application/widgets/detail/header',
'application/widgets/detail/detailslist',
'rofl/widgets/verticallist',
'rofl/lib/l10n',
'antie/runtimecontext',
'rofl/analytics/web/google',
'rofl/widgets/label',
'rofl/lib/promise',
'application/widgets/pointerbutton',
'application/managers/recording',
'application/managers/feature',
'application/models/production/recordings/record',
'application/constants'
], function (
BaseDetail,
Container,
KeyEvent,
Utils,
Header,
DetailsList,
VerticalList,
L10N,
RuntimeContext,
GoogleAnalytics,
Label,
Promise,
PointerButton,
RecordingManager,
FeatureManager,
Record,
Constants
) {
'use strict';
var l10n = L10N.getInstance(),
application = RuntimeContext.getCurrentApplication(),
GA = GoogleAnalytics.getInstance(),
recordingManager = RecordingManager.getInstance(),
layout = application.getLayout(),
device = RuntimeContext.getDevice(),
featureManager = FeatureManager.getInstance();
return BaseDetail.extend({
/**
* Initialises the component.
*/
init: function init () {
this._onSelectedItemChangeBound = Utils.bind(this._onSelectedItemChange, this);
this._onRecordDeletedBound = Utils.bind(this._onRecordDeleted, this);
this._onRecordingChangeBound = Utils.bind(this._onRecordingChange, this);
init.base.call(this, 'epg');
},
/**
* Sets the header data.
*
* @param {Object} data - Contains detail data and series episodes if any.
* @private
*/
_setHeaderData: function (data) {
var contentDetail = data.contentDetail,
seriesItem = this._isEpgSeries ? data.data : null;
this._header.setDataItem(contentDetail, seriesItem);
},
/**
* Sets the content's details.
*
* @param {Object} data - Contains detail data and series episodes if any.
* @private
*/
_setDetailData: function (data) {
var contentDetail = data.contentDetail,
episodes = data.epgSeriesEpisodes;
if (episodes.length) {
this._list.removeClass('minimalistic');
this._buildEpisodes(episodes);
this._header.setSeriesData(contentDetail);
} else {
this._list.addClass('minimalistic');
this._detailslistbox.removeChildWidgets();
this._header.setData(contentDetail);
}
this._header.focus();
if (!this._header.getButtonListLength()) {
this._backButton.focus();
}
},
/**
* Gets executed when the recording state changes.
*
* @param {Object} e - The event data.
* @private
*/
_onRecordingChange: function (e) {
if (e.target === 'series' && e.id === this.getDataItem().getSeriesId()) {
this._header.updateRecordingStatus();
} else if (e.target === 'episode' && e.id === this.getDataItem().getId()) {
this._header.updateRecordingStatus();
}
},
/**
* Builds the episodes.
*
* @param {Array} episodes - The episodes.
* @private
*/
_buildEpisodes: function (episodes) {
var detailsList;
this._createDetailsList(episodes);
detailsList = this._detailsList;
detailsList.setStylesTo({
'top': layout.detailsHeader.bigheader.top + 'px'
});
},
/**
* Creates modal content list based on passed options and data.
*
* @param {Array} episodes - The episodes to append.
*/
_createDetailsList: function (episodes) {
var detailsList = this._detailsList = new DetailsList(episodes),
detailsListContainer = this._detailslistbox;
detailsListContainer.removeChildWidgets();
this._buildPointerButtons();
detailsListContainer.appendChildWidget(detailsList);
},
/**
* Builds the pointer buttons.
*
* @private
*/
_buildPointerButtons: function () {
var upButton = this._upButton = new PointerButton(PointerButton.DIRECTIONS.UP),
downButton = this._downButton = new PointerButton(PointerButton.DIRECTIONS.DOWN);
this._downButton.enable();
this._upButton.disable();
this._detailslistbox.appendChildWidget(upButton);
this._detailslistbox.appendChildWidget(downButton);
},
/**
* On Selected item change event.
*
* @param {Event} e - The event data.
*/
_onSelectedItemChange: function (e) {
var target = e.target,
item = e.item,
header = this._header,
toggle;
if (target.hasClass('episodes-list')) {
header.addClass('shortheader');
this.align(target.getActiveChildWidget());
} else if (item.hasClass('episodes-list')) {
toggle = item.getActiveChildWidget().getToggle();
header.addClass('shortheader');
this.align(item.getActiveChildWidget());
toggle.focus();
} else if (item.hasClass('details-page-header') || item.hasClass('back-button')) {
if (!this._detailsList) {
return;
}
device.moveElementTo({
el: this._detailsList.outputElement,
skipAnim: false,
duration: 200,
fps: 60,
easing: 'easeInOut',
to: {
top: layout.detailsHeader.bigheader.top
}
});
header.removeClass('shortheader');
}
if (target.hasClass('detail-content') || item.hasClass('back-button')) {
if (!this._upButton) {
return;
}
if (e.index) {
this._upButton.enable();
} else {
this._upButton.disable();
}
}
},
/**
* On Select event.
*
* @param {Event} e - The event data.
*/
_onSelect: function (e) {
var target = e.target,
id = target.id;
if (id === 'up' || id === 'down') {
this._focusItem(id);
return;
}
target.focus();
switch (id) {
case 'playnow':
if (target.getDataItem().isRecording() && target.getDataItem().isLive()) {
this._onRestart(target.getDataItem());
} else {
this._onPlay(target.getDataItem());
}
GA.onEvent('player', 'started', {eventLabel: this._callingPage});
break;
case 'restart':
this._onRestart(target.getDataItem());
break;
case 'record':
// Falls through.
case 'delete':
this._onRecord(target);
break;
case 'close':
if (target.getDataItem() !== this._contentDetail) {
this._previousEpisodeItem.disable();
this._previousEpisodeItem.getToggle().focus();
this._previousEpisodeItem = null;
} else {
this._onBack();
}
break;
case 'back-button':
this._onBack();
break;
default:
if (target.hasClass('toggle') && target.hasClass('focus')) {
this._onSelectToggle(target);
}
}
},
/**
* Attempts to play the video.
*
* @param {Object} dataItem - The data item.
* @private
* @returns {boolean} False if user input is blocked.
*/
_onPlay: function (dataItem) {
var data = dataItem || this._contentDetail;
if (application.blockUserInput) {
return false;
}
if (data.canPlay()) {
application.showLoader(true, true);
if (data.isLive()) {
application.route('liveplayer', {
data: data,
channelId: data.getChannelId(),
type: Constants.LIVE_VIDEO_TYPE,
callingPage: 'detail'
});
} else if (data.isRecording()) {
application.route('recordingplayer', {
data: data,
type: 'RECORDING',
callingPage: 'detail'
});
} else {
application.route('catchupplayer', {
data: data,
type: 'VOD',
callingPage: 'detail'
});
}
}
},
/**
* Starts video from beginning.
*
* @param {Object} dataItem - The data item.
* @private
*/
_onRestart: function (dataItem) {
var data = dataItem || this._contentDetail;
if (data.canPlay()) {
application.route('liveplayer', {
data: data,
channelId: data.getChannelId(),
type: Constants.RESTART_VIDEO_TYPE,
callingPage: 'detail'
});
}
},
/**
* Starts video recording.
*
* @param {Object} target - The record button target.
* @private
*/
_onRecord: function (target) {
var data = target.getDataItem() || this._contentDetail;
if (featureManager.isRecordingEnabled()) {
application.route('record', {
item: data,
callingComponent: 'detail',
callback: Utils.bind(this._onRecordCallback, this, target),
flow: (data instanceof Record || data.getContentType() === 'RECORDING')
&& data.getStartTime() <= application.getDate() / 1000 ? 'delete' : 'default'
});
} else {
this._showRecordingDisabled();
}
},
/**
* Is called when the select toggle gets selected.
*
* @param {Object} target - The target.
* @private
*/
_onSelectToggle: function (target) {
var episodeItem = target.parentWidget,
description = episodeItem.getDescription();
if (this._previousEpisodeItem) {
this._previousEpisodeItem.disable();
}
episodeItem.enable();
this.align(target.parentWidget);
description.focus();
this._previousEpisodeItem = episodeItem;
},
/**
* 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);
},
/**
* Aligns the component based on the given item.
*
* @param {Object} item - The item.
*/
align: function (item) {
var listElement = this._detailsList.outputElement,
listTop = listElement.getBoundingClientRect().top,
windowHeight = layout.requiredScreenSize.height,
dimensions,
perfectTop,
difference,
newTop;
dimensions = item.outputElement.getBoundingClientRect();
perfectTop = (windowHeight - dimensions.height) / 2;
difference = dimensions.top - perfectTop;
if (difference > 0) {
newTop = listTop - difference;
} else {
newTop = listTop + -difference;
}
// height of smaller header
if (newTop > 0) {
if (this._header.hasClass('shortheader')) {
newTop = layout.detailsHeader.shortheader.top;
} else {
newTop = layout.detailsHeader.bigheader.top;
}
}
device.moveElementTo({
el: listElement,
skipAnim: false,
duration: 200,
fps: 60,
easing: 'easeInOut',
to: {
top: newTop
}
});
},
/**
* Gets executed when the error triggers.
*
* @param {Function} callback - The callback.
* @private
*/
_onError: function (callback) {
application.hideLoader();
application.route('error', {
type: 'fullscreen',
title: L10N.getInstance().get('errors.cannot_load_page'),
button: {id: 'error-close-button', label: L10N.getInstance().get('errors.close')},
imgUrl: 'src/assets/images/error-icon.png',
callback: Utils.bind(this._onErrorCallback, this, callback)
});
},
/**
* Focuses a specific item based on the direction.
*
* @param {string} direction - The direction.
* @private
*/
_focusItem: function (direction) {
var episodes = this._detailsList.getChildWidgetByIndex(0),
current = episodes.getActiveChildWidgetIndex(),
length = episodes.getChildWidgetCount();
if (!episodes.isFocussed()) {
this._upButton.enable();
episodes.focus();
return;
}
if (direction === 'up') {
current--;
} else {
current++;
}
if (current < 0) {
this._header.focus();
this._upButton.disable();
if (!this._header.getButtonListLength()) {
this._backButton.focus();
}
return;
}
this._downButton.enable();
episodes.setActiveChildIndex(current);
if (current > length - 2) {
this._downButton.disable();
}
},
/**
* Error callback.
*
* @param {Function|null} callback - The function callback, or null if not defined.
* @private
*/
_onErrorCallback: function (callback) {
if (Utils.isFunction(callback)) {
callback();
}
},
/**
* The record callback.
*
* @param {Object} target - The target.
* @param {Object} e - The record callback.
* @private
*/
_onRecordCallback: function (target, e) {
var config = {
happy: true,
icon: 'icon-check-v2'
},
hasRecording = recordingManager.hasRecording(e.item.getId(), e.item.getSeriesId());
if (e.removed) {
if (e.item.getEndTime() * 1000 > application.getDate()) {
// Future item gets canceled.
config.text = l10n.get('recordings.notification.cancelled', {
item: e.item.getTitle()
});
} else {
// Old items get removed.
config.text = l10n.get('recordings.notification.removed', {
item: e.item.getTitle()
});
}
debugger;
if (target.id === 'record') {
if (hasRecording.episode || hasRecording.series) {
target.recording();
} else {
target.record();
}
}
} else {
config.text = l10n.get('recordings.notification.successful.' + e.type, { series: e.item.getTitle() });
target.recording();
}
this.showWarningBox(config);
},
/**
* Shows the program is already recorded message.
*
* @param {Object} item - The item.
* @private
*/
_showProgramAlreadyRecorded: function (item) {
var config = {
icon: 'icon-alert-v2',
text: l10n.get('recordings.notification.recording', {
series: item.getTitle()
})
};
this.showWarningBox(config);
},
/**
* Shows the program can not be recorded message.
*
* @param {Object} item - The item.
* @private
*/
_showProgramNotRecordable: function (item) {
var config = {
icon: 'icon-alert-v2',
text: l10n.get('recordings.notification.not_recordable', {
series: item.getTitle()
})
};
this.showWarningBox(config);
},
/**
* Shows recording disabled message.
*
* @private
*/
_showRecordingDisabled: function () {
var config = {
text: l10n.get('player.warningbox.recording'),
icon: 'icon-alert-v2'
};
this.showWarningBox(config);
},
/**
* Gets executed when a record gets deleted.
*
* @param {Object} e - The event.
* @private
*/
_onRecordDeleted: function (e) {
var episodeItem,
episodeList,
activeIndex;
e.stopPropagation();
if (e.target === this._contentDetail) {
this._onBack();
} else {
episodeItem = e.target;
episodeList = this._detailsList.getEpisodeList();
activeIndex = episodeList.getActiveChildWidgetIndex();
episodeList.removeChildWidget(episodeItem);
activeIndex = activeIndex - 1;
if (activeIndex < 0) {
activeIndex = 0;
}
if (episodeList.getChildWidgetCount()) {
episodeList.setActiveChildIndex(activeIndex);
} else {
this._onBack();
}
}
}
});
});