define('application/components/replay', [
'rofl/widgets/component',
'application/widgets/masonrygrid',
'antie/runtimecontext',
'application/managers/search',
'application/models/configuration',
'rofl/lib/utils',
'application/utils',
'application/formatters/portraitasset',
'application/formatters/landscapeasset',
'rofl/events/keyevent',
'rofl/widgets/label',
'rofl/lib/l10n',
'application/widgets/clock',
'rofl/widgets/container',
'rofl/widgets/horizontallist',
'rofl/widgets/verticallist',
'rofl/lib/promise',
'application/widgets/replay/filterbutton',
'rofl/analytics/web/google'
], function (
Component,
MasonryGrid,
RuntimeContext,
SearchManager,
KPNConfiguration,
Utils,
AppUtils,
PortraitAssetFormatter,
LandscapeAssetFormatter,
KeyEvent,
Label,
L10N,
Clock,
Container,
HorizontalList,
VerticalList,
Promise,
FilterButton,
GoogleAnalytics
) {
'use strict';
var searchManager = SearchManager.getInstance(),
application = RuntimeContext.getCurrentApplication(),
layout = application.getLayout().replay,
requiredScreenSize = application.getLayout().requiredScreenSize,
l10n = L10N.getInstance(),
device = RuntimeContext.getDevice(),
GA = GoogleAnalytics.getInstance(),
MAX_ITEMS = 32,
filters = {
films: {
name: 'Films',
value: 'Films'
},
series: {
name: 'Series',
value: 'Series'
},
crime: {
name: 'Misdaad',
value: 'Misdaad'
},
kids: {
name: 'Kids',
value: 'Animatie'
},
sport: {
name: 'Sport',
value: ['Sport', 'Live voetbal']
},
news: {
name: 'Actualiteiten',
value: 'Nieuws'
},
info: {
name: 'Informatief',
value: ['Informatief', 'Documentaire', 'Wetenschap', 'Natuur']
},
culture: {
name: 'Cultuur & Religie',
value: ['Kunst/Cultuur', 'Religieus']
},
entertainment: {
name: 'Amusement',
value: 'Amusement'
},
music: {
name: 'Muziek',
value: 'Muziek'
},
erotic: {
name: 'Erotiek',
value: 'Erotiek'
}
};
return Component.extend({
/**
* Initialises the replay component.
*/
init: function init () {
init.base.call(this, 'replay');
this._build();
this._onKeyDownBound = Utils.bind(this._onKeyDown, this);
this._onSelectedItemChangeBound = Utils.bind(this._onSelectedItemChange, this);
this._onSelectBound = Utils.bind(this._onSelect, this);
this._onSelectAssetsBound = Utils.bind(this._onSelectAssets, this);
},
/**
* Builds the replay component.
*
* @private
*/
_build: function () {
this._buildPageContainer();
this._buildHeader();
this._buildTitle();
this._buildSubtitle();
this._buildClock();
this._buildFilters();
this._buildGrid();
},
/**
* Builds the filters.
*
* @private
*/
_buildFilters: function () {
this._buildFiltersList();
},
/**
* Builds the header.
*
* @private
*/
_buildPageContainer: function () {
var pageContainer = this._pageContainer = new VerticalList();
this.appendChildWidget(pageContainer);
},
/**
* Builds the header.
*
* @private
*/
_buildHeader: function () {
var header = this._header = new Container();
header.addClass('header');
this._pageContainer.appendChildWidget(header);
},
/**
* Builds the title.
*
* @private
*/
_buildTitle: function () {
var title = this._title = new Label({ text: l10n.get('replay.title'), classNames: ['top-title'] });
this._header.appendChildWidget(title);
},
/**
* Builds the subtitle.
*
* @private
*/
_buildSubtitle: function () {
var subtitle = new Label({ text: l10n.get('replay.subtitle'), classNames: ['main-subtitle'] });
this._header.appendChildWidget(subtitle);
},
/**
* Builds the clock.
*
* @private
*/
_buildClock: function () {
var clock = this._clock = new Clock();
clock.addClass('replay-clock');
this._header.appendChildWidget(clock);
},
/**
* Builds the filters list.
*
* @private
*/
_buildFiltersList: function () {
var genreFilters = this._genreFilters = new HorizontalList(),
genres = ['news', 'entertainment', 'series', 'films', 'kids', 'crime', 'sport', 'info', 'culture', 'music', 'erotic'],
i;
genreFilters.addClass('filters');
for (i = 0; i < genres.length; i++) {
// Generate buttons
genres[i] = new FilterButton(l10n.get('replay.filters.' + i), genres[i]);
genres[i].addClass('filterbtn');
this._genreFilters.appendChildWidget(genres[i]);
}
this._header.appendChildWidget(genreFilters);
},
/**
* Builds the grid.
*
* @private
*/
_buildGrid: function () {
var grid = this._grid = new MasonryGrid({
columns: 4,
types: [
{
class: 'landscape',
height: layout.asset.landscape,
percentage: 60,
formatter: new LandscapeAssetFormatter()
},
{
class: 'portrait',
height: layout.asset.portrait,
percentage: 40,
formatter: new PortraitAssetFormatter()
}
]
});
this._pageContainer.appendChildWidget(grid);
},
/**
* BeforeRender event.
*/
onBeforeRender: function () {
this._loadingTime = application.getDate();
},
/**
* BeforeShow event.
*
* @param {Object} e - The event data.
*/
onBeforeShow: function (e) {
var firstBtn = this._genreFilters.getChildWidget('news'),
grid = this._grid,
startTime = new Date(application.getDate() - 1000 * 60 * 60 * 24 * 7), // 7 days ago
endTime = application.getDate();
firstBtn.addClass('selected');
firstBtn.focus();
if (!e.fromBack) {
startTime.setHours(0);
startTime.setMinutes(0);
startTime.setSeconds(0);
startTime.setMilliseconds(0);
startTime = Date.parse(startTime);
endTime = Date.parse(endTime);
grid.empty();
grid.setStyleTo('left', layout.scrolling.left + 'px');
grid.setStyleTo('top', layout.scrolling.top + 'px');
if (this._newFilter) {
this._newFilter.removeClass('selected');
this._newFilter = firstBtn;
}
firstBtn.addClass('selected');
firstBtn.focus();
this._loadData('', startTime, endTime, filters.news.value)
.then(Utils.bind(this._setData, this))
.then(Utils.bind(this._setEventListeners, this));
}
this._startReplayPage = application.getDate();
GA.onPageView('replaytv');
},
/**
* BeforeHide event.
*/
onBeforeHide: function () {
var duration = ((application.getDate() - this._startReplayPage) / 60000).toFixed(2);
this._removeEventListeners();
GA.onEvent('page', 'time', {
eventLabel: 'replaytv',
eventValue: duration
});
},
/**
* Attempts to set the data.
*
* @param {Array} data - The data.
* @private
*/
_setData: function (data) {
var grid = this._grid,
value = ((application.getDate() - this._loadingTime) / 1000).toFixed(2);
grid.setStyleTo('top', layout.scrolling.top + 'px');
grid.setDataItem(data);
application.hideLoader();
if (!this._genreFilters.isFocussed()) {
this._grid.getChildWidgetByIndex(0).focus();
}
GA.onEvent('page', 'load', {
eventLabel: 'replay',
eventValue: value
});
},
/**
* Attempts to load the data for a given date.
*
* @param {string} query - Query.
* @param {Date} startTime - Start time.
* @param {Date} endTime - End time.
* @param {string} genre - The genre of the item.
* @returns {Promise} - Promise resolving with data.
* @private
*/
_loadData: function (query, startTime, endTime, genre) {
application.showLoader(true);
return searchManager.search(query, startTime, endTime, genre)
.then(Utils.bind(this._randomiseData, this, startTime, endTime, genre))
['catch'](function () {
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'
});
});
},
/**
* Attempts to load the data for a given genres if multiple for one category.
*
* @param {string} query - Query.
* @param {Date} startTime - Start time.
* @param {Date} endTime - End time.
* @param {Array} genres - The genre of the item.
* @returns {Array} - The randomised data.
* @private
*/
_connectDataForMultipleGenres: function (query, startTime, endTime, genres) {
var multipleData = [],
promises = [],
i;
application.showLoader(true);
for (i = 0; i < genres.length; i++) {
promises.push(searchManager.search(query, startTime, endTime, genres[i]));
}
return Promise.all(promises)
.then(function (results) {
Utils.each(results, function (result) {
Utils.each(result, function (item) {
multipleData.push(item);
});
});
return multipleData;
});
},
/**
* Attempts to load the data for a given date.
*
* @param {string} query - Query.
* @param {Date} startTime - Start time.
* @param {Date} endTime - End time.
* @param {Array} genres - The genre of the item.
* @private
*/
_getDataForMultipleGenres: function (query, startTime, endTime, genres) {
this._connectDataForMultipleGenres(query, startTime, endTime, genres)
.then(Utils.bind(this._randomiseData, this, startTime, endTime, genres))
.then(Utils.bind(this._setData, this));
},
/**
* Randomises the given data and only returns the maximum amount of items.
*
* @param {Date} startTime - The start time.
* @param {Date} endTime - The end time.
* @param {string} genre - The genre.
* @param {Array} data - The data.
* @returns {Array} - The randomised data.
* @private
*/
_randomiseData: function (startTime, endTime, genre, data) {
var combined = [],
finalArray = [],
i;
Utils.each(data, function (items) {
combined = combined.concat(items);
});
AppUtils.randomiseArray(combined);
for (i = 0; i < combined.length; i++) {
finalArray.push(combined[i]);
if (finalArray.length >= MAX_ITEMS) {
break;
}
}
return finalArray;
},
/**
* Sets the event listeners.
*
* @private
*/
_setEventListeners: function () {
this.addEventListener('keydown', this._onKeyDownBound);
this._genreFilters.addEventListener('select', this._onSelectBound);
this._grid.addEventListener('select', this._onSelectAssetsBound);
this._grid.addEventListener('selecteditemchange', this._onSelectedItemChangeBound);
},
/**
* Removes the event listeners.
*
* @private
*/
_removeEventListeners: function () {
this.removeEventListener('keydown', this._onKeyDownBound);
this._genreFilters.removeEventListener('select', this._onSelectBound);
this._grid.removeEventListener('select', this._onSelectAssetsBound);
this._grid.removeEventListener('selecteditemchange', this._onSelectedItemChangeBound);
},
/**
* KeyDown event.
*
* @param {Object} e - The event data.
* @private
*/
_onKeyDown: function (e) {
var activeAssetIndex = this._grid.getActiveChildIndex();
switch (e.keyCode) {
case KeyEvent.VK_LEFT:
e.stopPropagation();
e.preventDefault();
application.focusMenu('main');
break;
case KeyEvent.VK_RIGHT:
e.preventDefault();
e.stopPropagation();
break;
case KeyEvent.VK_BACK:
if (activeAssetIndex !== 0) {
this._grid.alignToTop();
} else {
this._onClose();
e.preventDefault();
e.stopPropagation();
}
break;
}
},
/**
* Gets executed when the.
*
* @private
*/
_onClose: function () {
this._grid.dispose();
// Go back and focus the player.
this.parentWidget.back();
application.getComponent('player').focus();
},
/**
* Get filtered data.
*
* @param {string} query - The genre of the item.
* @param {Date} startTime - Start time.
* @param {Date} endTime - End time.
* @param {string} genre - The genre of the item.
* @private
*/
_getFilteredData: function (query, startTime, endTime, genre) {
this._loadData(query, startTime, endTime, genre)
.then(Utils.bind(this._setData, this));
},
/**
* Select event.
*
* @param {Object} e - The event data.
* @private
*/
_onSelect: function (e) {
var id = e.target.id,
films = this._genreFilters.getChildWidgetByIndex(0),
startTime = new Date(application.getDate() - 1000 * 60 * 60 * 24 * 7), // 7 days ago
endTime = application.getDate(),
currentFilter = this._newFilter || films;
startTime.setHours(0);
startTime.setMinutes(0);
startTime.setSeconds(0);
startTime.setMilliseconds(0);
startTime = Date.parse(startTime);
endTime = Date.parse(endTime);
currentFilter.removeClass('selected');
e.target.addClass('selected');
switch (id) {
case 'films':
this._getFilteredData('', startTime, endTime, filters.films.value);
break;
case 'series':
this._getFilteredData('', startTime, endTime, filters.series.value);
break;
case 'crime':
this._getFilteredData('', startTime, endTime, filters.crime.value);
break;
case 'kids':
this._getFilteredData('', startTime, endTime, filters.kids.value);
break;
case 'sport':
this._getDataForMultipleGenres('', startTime, endTime, filters.sport.value);
break;
case 'news':
this._getFilteredData('', startTime, endTime, filters.news.value);
break;
case 'info':
this._getDataForMultipleGenres('', startTime, endTime, filters.info.value);
break;
case 'culture':
this._getDataForMultipleGenres('', startTime, endTime, filters.culture.value);
break;
case 'entertainment':
this._getFilteredData('', startTime, endTime, filters.entertainment.value);
break;
case 'music':
this._getFilteredData('', startTime, endTime, filters.music.value);
break;
case 'erotic':
this._getFilteredData('', startTime, endTime, filters.erotic.value);
break;
}
this._newFilter = e.target;
},
/**
* Select assets event.
*
* @param {Object} e - The event data.
* @private
*/
_onSelectAssets: function (e) {
var dataItem;
if (e.target.hasClass('asset')) {
dataItem = e.target.getDataItem();
application.route('epgdetail', {
data: dataItem,
callback: Utils.bind(self.focus, self),
callingPage: 'replaytv'
});
}
},
/**
* Change elements positon on selected item change.
*
* @param {Object} e - The selected item change event.
* @private
*/
_onSelectedItemChange: function (e) {
var target = e.target,
activeTarget = target.getActiveChildWidget();
// Verticallist is the grid that scrolls
if (target.hasClass('verticallist')) {
this.align(activeTarget);
} else if (target.hasClass('masonrygrid')) {
if (activeTarget) {
this.align(activeTarget.getActiveChildWidget());
}
}
},
/**
* Aligns the component based on the given item.
*
* @param {Object} item - The item.
*/
align: function (item) {
var listElement = this._grid.outputElement,
scrollingLayout = layout.scrolling,
windowHeight = requiredScreenSize.height,
listClientRect,
listTop,
left,
maxBottomPosition,
dimensions,
perfectTop,
difference,
newTop;
if (!item || !item.outputElement) {
return;
}
listClientRect = listElement.getBoundingClientRect();
listTop = listClientRect.top;
left = scrollingLayout.left;
maxBottomPosition = -(listClientRect.height) + layout.height - scrollingLayout.fromBottom;
dimensions = item.outputElement.getBoundingClientRect();
perfectTop = (windowHeight - dimensions.height) / 2;
difference = dimensions.top - perfectTop;
if (difference > 0) {
newTop = listTop - difference;
} else {
newTop = listTop + -difference;
}
// -23px - Value on which newTop should be settet as 186px,
// which is the header height(default grid top position)
if (newTop >= scrollingLayout.fromTop) {
newTop = scrollingLayout.top;
}
// Small differences are caused by the scaling of the widget, ignore movement.
if (newTop === listTop
|| newTop - listTop >= -1 && newTop - listTop <= 1) {
return;
}
// If we reached the bottom, stop scrolling.
if (newTop < maxBottomPosition) {
newTop = maxBottomPosition;
}
device.moveElementTo({
el: listElement,
skipAnim: false,
duration: 300,
fps: 60,
to: {
left: left,
top: newTop
}
});
}
});
});