define('application/components/details/details', [
'application/components/modals/abstract',
'application/widgets/detail/tablist',
'application/widgets/detail/series/header',
'application/widgets/detail/vod/header',
'application/widgets/detail/header',
'application/widgets/player/warningbox',
'rofl/widgets/verticallist',
'rofl/widgets/label',
'rofl/widgets/container',
'rofl/lib/l10n',
'rofl/analytics/web/google',
'application/managers/api',
'rofl/lib/utils',
'antie/runtimecontext',
'antie/events/keyevent',
'rofl/lib/promise'
], function (
AbstractModal,
TabList,
HeaderSeries,
HeaderVod,
HeaderEpg,
WarningBox,
VerticalList,
Label,
Container,
L10N,
GoogleAnalytics,
ApiManager,
Utils,
RuntimeContext,
KeyEvent,
Promise
) {
'use strict';
var api = ApiManager.getKPNAPI(),
application = RuntimeContext.getCurrentApplication(),
device = RuntimeContext.getDevice(),
l10n = L10N.getInstance(),
GA = GoogleAnalytics.getInstance(),
DETAIL_TYPES = {
vod: 'voddetail',
series: 'seriesdetail',
epg: 'epgdetail'
};
return AbstractModal.extend({
/**
* Initialises the component.
*
* @param {string} detail - Detail type to create.
*/
init: function init (detail) {
var detailType = this._detailType = DETAIL_TYPES[detail];
init.base.call(this, detailType);
this._onKeyDownBound = Utils.bind(this._onKeyDown, this);
this._onSelectBound = Utils.bind(this._onSelect, this);
this._lockBound = Utils.bind(this._lock, this);
},
/**
* Builds the component.
*
* @private
*/
_createModal: function () {
var isSeriesDetail = this._detailType === DETAIL_TYPES.series,
isEpgDetail = this._detailType === DETAIL_TYPES.epg,
list = this._list = new VerticalList(),
tabList = this._tabList = new TabList(),
warningBox = this._warningBox = new WarningBox(),
headerComponent = HeaderVod,
header,
detailslistbox;
if (isSeriesDetail) {
headerComponent = HeaderSeries;
}
if (isEpgDetail) {
headerComponent = HeaderEpg;
}
header = this._header = new headerComponent();
list.addClass('detail-content');
list.appendChildWidget(header);
if (this._detailType === DETAIL_TYPES.series || this._detailType === DETAIL_TYPES.vod) {
list.appendChildWidget(tabList);
}
if (this._detailType === DETAIL_TYPES.epg) {
detailslistbox = this._detailslistbox = new VerticalList();
detailslistbox.addClass('details-listing');
list.appendChildWidget(detailslistbox);
}
this.appendChildWidget(warningBox);
this.appendChildWidget(list);
if (this._detailType === DETAIL_TYPES.vod) {
this._createPurchaseMessage();
}
},
/**
* BeforeRender event.
*/
onBeforeRender: function () {
if (this._onBingeWatchEventBound) {
application.addEventListener('$bingewatch', this._onBingeWatchEventBound);
}
if (this._onNextEpisodeEventBound) {
application.addEventListener('$nextepisode', this._onNextEpisodeEventBound);
}
if (this._onRecordingChangeBound) {
application.addEventListener('$record', this._onRecordingChangeBound);
}
if (this._onSelectedItemChangeBound) {
this.addEventListener('selecteditemchange', this._onSelectedItemChangeBound);
}
if (this._onRecordDeletedBound) {
this.addEventListener('recorddeleted', this._onRecordDeletedBound);
}
application.addEventListener('$locked', this._lockBound);
this.addEventListener('keydown', this._onKeyDownBound);
this.addEventListener('select', this._onSelectBound);
},
/**
* BeforeShow event.
*
* @param {Object} e - The event data.
*/
onBeforeShow: function (e) {
var data = e.args.data,
isEpgSeries = e.args.series;
this._isEpg = this._detailType === DETAIL_TYPES.epg;
this._isEpgSeries = this._isEpg && isEpgSeries;
this.parentWidget.setStyleTo('display', 'block');
e.stopPropagation();
e.preventDefault();
// Clear Details.
this.clear();
this._callback = e.args.callback || null;
this._callingPage = e.args.callingPage || null;
this._userCanWatch = false;
this.setDataItem(data);
if (this._warningBox && this._warningBox.isRendered()) {
this._warningBox.hide({skipAnim: true});
}
switch (this._detailType) {
case DETAIL_TYPES.vod:
this._getDetails(data)
.then(Utils.bind(this._getUserData, this, data))
.then(Utils.bind(this._onDataReady, this));
break;
case DETAIL_TYPES.series:
this._getDetails(data)
.then(Utils.bind(this._onDataReady, this));
break;
case DETAIL_TYPES.epg:
if (isEpgSeries) {
this._getDetails(data.getItems()[0])
.then(Utils.bind(this._onDataReady, this));
} else {
this._getDetails(data)
.then(Utils.bind(this._loadRelatedContent, this))
.then(Utils.bind(this._onDataReady, this));
}
GA.onPageView('detail');
break;
// No default.
}
application.showLoader();
},
/**
* Requests userdata for the given item.
*
* @param {Object} data - Content to get it's detail.
* @returns {Promise} - Content detail promise.
* @private
*/
_getDetails: function (data) {
var detailsAction = data.getDetailsAction();
return api
.read('detail', {
params: {
endpoint: detailsAction
},
withCredentials: true
})
.then(Utils.bind(this._setContentDetail, this, data))
['catch'](Utils.bind(function () {
// No content detail data, reject promise.
this._onError(Utils.bind(this._onBack, this));
return Promise.reject();
}, this));
},
/**
* Requests userdata for the given item.
*
* @param {Object} data - Content to get it's userdata.
* @param {Object} contentDetail - The data's content detail result.
* @returns {Promise} - Userdata promise.
* @private
*/
_getUserData: function (data, contentDetail) {
return api.read('userdata', {
params: {
data: data
},
withCredentials: true
})
.then(Utils.bind(this._processUserData, this, contentDetail[0]));
},
/**
* Sets content detail.
*
* @param {Object} data - The content data.
* @param {Object} contentDetail - The data's content detail result.
* @returns {Array} - The content detail.
* @private
*/
_setContentDetail: function (data, contentDetail) {
var dataItems;
if (this._isEpgSeries) {
dataItems = this.getDataItem().getItems();
Utils.each(dataItems, function (item) {
item._description = contentDetail.getDescription();
});
return [contentDetail, dataItems];
}
return [contentDetail];
},
/**
* Loads the related content.
*
* @param {Object} detail - The detail data.
* @returns {Array} - Returns the content detail and/or relatedContent.
* @private
*/
_loadRelatedContent: function (detail) {
var detailData = detail[0] || {},
related = detailData.getRelatedContent && detailData.getRelatedContent();
if (related) {
return api.read(related)
.then(function (relatedContent) {
return [detailData, relatedContent];
})
['catch'](function () {
return detailData;
});
}
return [detailData];
},
/**
* BeforeHide event.
*/
onBeforeHide: function () {
if (this._onBingeWatchEventBound) {
application.removeEventListener('$bingewatch', this._onBingeWatchEventBound);
}
if (this._onNextEpisodeEventBound) {
application.removeEventListener('$nextepisode', this._onNextEpisodeEventBound);
}
if (this._onRecordingChangeBound) {
application.removeEventListener('$record', this._onRecordingChangeBound);
}
if (this._onSelectedItemChangeBound) {
this.removeEventListener('selecteditemchange', this._onSelectedItemChangeBound);
}
if (this._onRecordDeletedBound) {
this.removeEventListener('recorddeleted', this._onRecordDeletedBound);
}
application.removeEventListener('$locked', this._lockBound);
this.removeEventListener('keydown', this._onKeyDownBound);
this.removeEventListener('select', this._onSelectBound);
},
/**
* Creates purchase message.
*
* @private
*/
_createPurchaseMessage: function () {
var messageContainer = this._purchaseMessage = new Container('purchase-message'),
purchaseMessage = new Label({ text: l10n.get('movies.puchase_message')}),
icon = new Label({ text: '', classNames: ['icon-info', 'icon'] });
messageContainer.appendChildWidget(icon);
messageContainer.appendChildWidget(purchaseMessage);
this._list.appendChildWidget(messageContainer);
},
/**
* DataReady.
*
* @param {Array} results - Data results for details and userdata.
* @private
*/
_onDataReady: function (results) {
var contentDetail = results[0],
epgSeriesEpisodes = results[1] || [],
data = {
data: this.getDataItem(),
contentDetail: contentDetail,
epgSeriesEpisodes: epgSeriesEpisodes
};
this._contentDetail = contentDetail;
application.hideLoader();
this._setHeaderData(data);
this._setDetailData(data);
this.show();
},
/**
* Processes user data.
*
* @param {Object} contentDetail - The data's content detail result.
* @param {Object} userData - User data.
* @returns {Array} - Returns the previously requested content detail.
* @private
*/
_processUserData: function (contentDetail, userData) {
var containers = Utils.getNested(userData, 'resultObj', 'containers'),
assets,
asset,
packages,
i;
if (!containers.length) {
return [contentDetail];
}
assets = Utils.getNested(containers[0], 'entitlement', 'assets');
if (!assets.length) {
return [contentDetail];
}
for (i = 0; i < assets.length; i++) {
if (assets[i].assetType === 'MASTER') {
asset = assets[i];
break;
}
}
if (asset) {
if (Utils.getNested(asset, 'rights') === 'watch') {
this._userCanWatch = true;
} else {
packages = Utils.getNested(asset, 'commercialPackages');
if (packages && packages.length) {
this._price = Utils.getNested(packages[0], 'notRecurring', 'price');
}
}
}
return [contentDetail];
},
/**
* KeyDown Event.
*
* @param {Object} e - The event data.
* @private
*/
_onKeyDown: function (e) {
var samsungChannelKey = this._handleChannelKey(e);
if (!samsungChannelKey) {
e.preventDefault();
e.stopPropagation();
switch (e.keyCode) {
case KeyEvent.VK_BACK:
this._onBack();
break;
}
}
},
/**
* Determines if Samsung's channel key will be handled by the main app or the component.
*
* @param {Object} e - The keyEvent data.
* @returns {boolean} - True if the channel key will be handled by main app instead of component.
*/
_handleChannelKey: function (e) {
var deviceBrand = device.getBrand();
return (e.keyCode === KeyEvent.VK_CHANNEL_UP || e.keyCode === KeyEvent.VK_CHANNEL_DOWN) &&
deviceBrand === 'samsung';
},
/**
* Routes to the dropdown.
*
* @param {string} seasonTitle - The season title.
* @private
*/
_routeToDropdown: function (seasonTitle) {
var options = [],
values = [];
Utils.each(this._contentDetail.getSeasons(), function (season) {
options.push(season.getTitle());
values.push({
name: season.getTitle(),
season: season
});
});
application.route('dropdown', {
dataOptions: options,
dataValues: values,
prevValue: seasonTitle,
filter: 'genre',
showGoBackButton: true,
callback: Utils.bind(this._onDropdownCallback, this)
});
},
/**
* The Dropdown Callback.
*
* @param {Object} e - The callback event.
* @private
*/
_onDropdownCallback: function (e) {
var season = e.target.getItemValueObject().season;
season.prepare()
.then(Utils.bind(function (data) {
this._season = data;
this._tabList.updateTabData(data);
this._tabList.getTab(0).alignCarousel();
}, this));
this.focus();
},
/**
* Show the player on top of this component.
*
* @private
*/
_showPlayer: function () {
var main = application.getComponent('main'),
player = application.getComponent('player');
main.setStyleTo('display', 'none');
player.setStyleTo('display', 'block');
player.getChildWidget('player').setStyleTo('display', 'block');
this.parentWidget.setStyleTo('display', 'none');
},
/**
* Bingewatch event.
*
* @param {Object} e - The bingewatch event data.
* @private
*/
_onBingeWatchEvent: function (e) {
var episode = e.episode,
index = this._season.getItems().indexOf(episode);
if (index >= 0) {
this._tabList.getTab(0).alignCarousel(index, true);
}
},
/**
* NextEpisode event.
*
* @param {Object} e - The next episode event data.
* @private
*/
_onNextEpisodeEvent: function (e) {
// Use the same process for bingewatch.
this._onBingeWatchEvent(e);
},
/**
* Clears the old elements.
*/
clear: function () {
if (this._header) {
this._header.dispose();
}
if (this._detailsList) {
this._detailsList.dispose();
}
},
/**
* Locks the widget again.
*
* @private
*/
_lock: function () {
if (this.parentWidget && this.parentWidget.outputElement && this.parentWidget.outputElement.style.display === 'block') {
this._onBack();
} else {
this._header.clear();
this.parentWidget.back();
}
},
/**
* Go back.
*
* @private
*/
_onBack: function () {
this._header.clear();
this._list.unsetActiveChildWidget();
this.parentWidget.back();
application.getComponent('main').focus();
if (this._callback) {
this._callback();
}
}
});
});