define('application/components/uibuilder', [
'rofl/widgets/component',
'rofl/lib/utils',
'rofl/lib/l10n',
'application/widgets/uibuilder/hero',
'application/widgets/uibuilder/carousel',
'application/widgets/uibuilder/filter',
'application/widgets/uibuilder/herocarousel',
'application/widgets/uibuilder/heroslider',
'application/widgets/clock',
'rofl/widgets/verticallist',
'antie/widgets/horizontallist',
'rofl/events/keyevent',
'antie/runtimecontext',
'rofl/widgets/label',
'rofl/widgets/container',
'application/formatters/vodasset',
'application/formatters/homeepgasset',
'application/formatters/heroslider',
'application/formatters/sports',
'application/formatters/editorials',
'application/formatters/bookmarkasset',
'rofl/analytics/web/google',
'application/managers/halo'
], function (
Component,
Utils,
L10N,
Hero,
Carousel,
Filter,
HeroCarousel,
HeroSlider,
Clock,
VerticalList,
HorizontalList,
KeyEvent,
RuntimeContext,
Label,
Container,
VodFormatter,
EpgFormatter,
HeroSliderFormatter,
SportsFormatter,
EditorialFormatter,
BookmarkFormatter,
GoogleAnalytics,
HaloManager
) {
'use strict';
var application = RuntimeContext.getCurrentApplication(),
l10n = L10N.getInstance(),
GA = GoogleAnalytics.getInstance(),
UPDATE_CAROUSEL_INTERVAL_TIME = 120000, // 2 minutes.
PROGRESS_INTERVAL_TIME = 5000,
UIBuilderComponent,
WIDGET_TYPES = {
EDITORIAL_BLOCK: 'editorial',
HERO: 'hero',
HERO_SLIDER: 'heroSlider',
CAROUSEL: 'carousel',
FILTERED_RESULT: 'filteredResult'
},
layout = application.getLayout(),
VOD_CAROUSEL_PARAMS = {
formatter: VodFormatter,
layout: layout.asset.vod,
numberOfVisibleItemsOnScreen: 8,
watchAll: {
classNames: []
}
},
EPG_CAROUSEL_PARAMS = {
formatter: EpgFormatter,
layout: layout.asset.epg,
numberOfVisibleItemsOnScreen: 4,
watchAll: {
classNames: ['landscape-watchall']
}
},
BOOKMARK_CAROUSEL_PARAMS = {
formatter: BookmarkFormatter,
layout: layout.asset.epg,
numberOfVisibleItemsOnScreen: 4,
watchAll: {
classNames: ['landscape-watchall']
}
},
VOD_GRID = {
grid: {
columns: 8,
width: layout.requiredScreenSize.width,
height: layout.watchall.gridHeight,
alignPoint: 0,
culling: true,
continuousListener: true,
navigateNext: true,
animOptions: {
easing: 'easeInOut',
fps: 60,
duration: 200,
skipAnim: false
}
},
asset: {
width: layout.asset.vod.width,
height: layout.asset.vod.height,
formatter: VodFormatter
}
},
EPG_GRID = {
grid: {
columns: 4,
width: layout.requiredScreenSize.width,
height: layout.watchall.gridHeight,
alignPoint: 0,
culling: true,
continuousListener: true,
navigateNext: true,
animOptions: {
easing: 'easeInOut',
fps: 60,
duration: 200,
skipAnim: false
}
},
asset: {
width: layout.asset.epg.width,
height: layout.asset.epg.height,
formatter: EpgFormatter
}
},
BOOKMARK_GRID = {
grid: {
columns: 4,
width: layout.requiredScreenSize.width,
height: layout.watchall.gridHeight,
alignPoint: 0,
culling: true,
continuousListener: true,
navigateNext: true,
animOptions: {
easing: 'easeInOut',
fps: 60,
duration: 200,
skipAnim: false
}
},
asset: {
width: layout.asset.epg.width,
height: layout.asset.epg.height,
formatter: BookmarkFormatter
}
},
WIDGETS_PARAMS = {
home: {
hero: {
id: 'hero',
formatter: HeroSliderFormatter,
numberOfVisibleItemsOnScreen: 4,
layout: layout.home.slider
},
carousels: {
vod: VOD_CAROUSEL_PARAMS,
epg: EPG_CAROUSEL_PARAMS,
bookmark: BOOKMARK_CAROUSEL_PARAMS
},
grid: {
vod: VOD_GRID,
epg: EPG_GRID,
bookmark: BOOKMARK_GRID
}
},
movies: {
hero: {
id: 'hero',
formatter: HeroSliderFormatter,
numberOfVisibleItemsOnScreen: 4,
layout: layout.home.slider
},
carousels: {
vod: VOD_CAROUSEL_PARAMS
},
grid: {
vod: VOD_GRID
}
},
series: {
hero: {
id: 'hero',
formatter: HeroSliderFormatter,
numberOfVisibleItemsOnScreen: 4,
layout: layout.home.slider
},
carousels: {
vod: VOD_CAROUSEL_PARAMS
},
grid: {
vod: VOD_GRID
}
},
sports: {
editorialBlocks: {
id: 'hero',
formatter: EditorialFormatter,
numberOfVisibleItemsOnScreen: 4,
layout: layout.home.slider
},
carousels: {
epg: EPG_CAROUSEL_PARAMS
},
grid: {
epg: EPG_GRID
}
}
};
UIBuilderComponent = Component.extend({
/**
* Initialises the component.
*/
init: function init () {
init.base.call(this, this.pageId);
this.addClass('page');
this._buildFiltersContainer();
this._buildGenreFilters();
this._build();
this._onKeyDownBound = Utils.bind(this._onKeyDown, this);
this._onToplistFocusBound = Utils.bind(this._onToplistFocus, this);
this._activeFilters = {};
this._onSelectedItemChangeBound = Utils.bind(this._onSelectedItemChange, this);
this._onSelectBound = Utils.bind(this._onSelect, this);
this._unlockAssetsBound = Utils.bind(this._unlockAssets, this);
this._lockAssetsBound = Utils.bind(this._lockAssets, this);
},
/**
* Builds the page layout.
*
* @private
*/
_build: function () {
this._list = this.appendChildWidget(new VerticalList());
this._topList = this._list.appendChildWidget(new HorizontalList());
this._widgetList = this._list.appendChildWidget(new VerticalList());
this._widgetList.addClass('widget-list');
},
/**
* Builds the filters container.
*
* @private
*/
_buildFiltersContainer: function () {
var filtersContainer = this._filterscontainer = new HorizontalList();
filtersContainer.addClass('filters-container');
filtersContainer.addClass('filters');
},
/**
* Builds genre filters.
*
* @private
*/
_buildGenreFilters: function () {
var genreFiltersButton = this._genrefilter = new Filter({
text: l10n.get('genre.all'),
id: 'filter-genre-btn',
isDropDown: true,
classList: 'icon-button',
type: 'genre'
}),
commonFiltersButton = this._commonfilter = new Filter({
text: l10n.get('filter.filter'),
id: 'filter-filter-btn',
isDropDown: true,
classList: 'icon-button',
type: 'filter'
}),
sortingFiltersButton = this._sortfilter = new Filter({
text: l10n.get('sort.all'),
id: 'filter-sort-btn',
isDropDown: true,
classList: 'icon-button',
type: 'sort'
});
this._filterscontainer.appendChildWidget(genreFiltersButton);
this._filterscontainer.appendChildWidget(commonFiltersButton);
this._filterscontainer.appendChildWidget(sortingFiltersButton);
},
/**
* Builds the title.
*
* @param {Object} params - Title params.
* @param {Object} params.label.text - Label text.
* @param {Array} params.label.classNames - Label class names..
* @private
*/
_buildTitle: function (params) {
var titleParams = params || {},
title = this._title = new Label(titleParams.label),
branding = this._branding = new Container();
branding.addClass('page-branding');
branding.addClass('page-title');
branding.appendChildWidget(title);
this._header.appendChildWidget(branding);
},
/**
* BeforeRender event.
*/
onBeforeRender: function () {
this.addEventListener('keydown', this._onKeyDownBound);
this._topList.addEventListener('keydown', this._onKeyDownBound);
this._topList.addEventListener('focus', this._onToplistFocusBound);
this.addEventListener('selecteditemchange', this._onSelectedItemChangeBound);
this.addEventListener('select', this._onSelectBound);
application.addEventListener('$unlocked', this._unlockAssetsBound);
application.addEventListener('$locked', this._lockAssetsBound);
HaloManager.getInstance()
.getServiceMessage()
.then(function (r) {
if (r) {
application.route('service');
}
});
},
/**
* BeforeHide event.
*/
onBeforeHide: function () {
this.removeEventListener('keydown', this._onKeyDownBound);
this._topList.removeEventListener('keydown', this._onKeyDownBound);
this._topList.removeEventListener('focus', this._onToplistFocusBound);
this.removeEventListener('selecteditemchange', this._onSelectedItemChangeBound);
this.removeEventListener('select', this._onSelectBound);
application.removeEventListener('$unlocked', this._unlockAssetsBound);
application.removeEventListener('$locked', this._lockAssetsBound);
this._clearProgressInterval();
this._clearUpdateCarouselsInterval();
this._grid = null;
},
/**
* AddFilterWidget to the page layout event.
*/
_addFilterWidget: function () {
this._topList.appendChildWidget(this._filterscontainer);
},
/**
* Builds the clock.
*
* @private
*/
_buildClock: function () {
var clock = this._clock = new Clock();
clock.addClass('header-clock');
this._topList.appendChildWidget(clock);
},
/**
* Toplist focus event.
*
* @param {Object} e - The event data.
* @private
*/
_onToplistFocus: function (e) {
if (e.target === this._hero) {
if (!this._hero.isVisible()) {
this._hero.show();
e.index = 0;
e.item = e.target;
this._scroll(e, true, true);
this._widgetList.setActiveChildIndex(0);
if (this._grid) {
this._grid.alignToFirstItem();
}
}
if (this._grid && this._grid.getActiveChildIndex() > 0) {
this._grid.alignToFirstItem();
}
}
},
/**
* KeyDown event.
*
* @param {Object} e - The event data.
* @private
*/
_onKeyDown: function (e) {
var filtersContainerItem;
switch (e.keyCode) {
case KeyEvent.VK_BACK:
this._onBack();
break;
case KeyEvent.VK_LEFT:
e.preventDefault();
e.stopPropagation();
application.focusMenu('main');
break;
case KeyEvent.VK_RIGHT:
e.preventDefault();
e.stopPropagation();
break;
case KeyEvent.VK_DOWN:
if (this._filterscontainer && this._filterscontainer.isFocussed()) {
if (this.heroFound) {
this._hero.focusAction(this._hero.getButtonCount() - 1);
if (!this._hero.isVisible()) {
this._widgetList.focus();
if (this._grid) {
this._grid.alignToActiveIndex();
}
}
e.preventDefault();
e.stopPropagation();
} else if (this._grid) {
this._widgetList.focus();
this._grid.alignToActiveIndex();
}
}
break;
case KeyEvent.VK_UP:
if (this.heroFound && this._hero.isFocussed()) {
this._hero.setButtonActiveChildIndex(this._hero.getButtonCount() - 1);
filtersContainerItem = this._filterscontainer && this._filterscontainer.getChildWidgetByIndex(0);
if (filtersContainerItem) {
filtersContainerItem.focus();
}
e.preventDefault();
e.stopPropagation();
}
break;
}
},
/**
* Builds the widgets and appends them to the component.
*
* @param {Array} widgets - The widgets.
*/
buildWidgets: function (widgets) {
this.heroFound = false;
Utils.each(widgets, function (widget) {
this.buildWidget(widget);
}, this);
if (!this.heroFound) {
this.addClass('no-hero-item');
this._list.getChildWidgetByIndex(0).focus();
} else {
if (this.hasClass('no-hero-item')) {
this.removeClass('no-hero-item');
}
}
},
/**
* Builds the individual widget.
*
* @param {Object} widget - The widget. See WIDGET_TYPES for available widgets.
*/
buildWidget: function (widget) {
var list = this._widgetList,
widgetsParams = WIDGETS_PARAMS[this.pageId],
editorialBlocks,
sliderItems,
carouselsWidgetParams,
widgetLayout;
switch (widget.getWidgetType()) {
case WIDGET_TYPES.HERO:
if (widget._item && widget.hasRights() && !this.heroFound) {
if (!widget._item.isLocked()) {
this._hero = new Hero(widget, widgetsParams.hero);
this._topList.insertChildWidget(0, this._hero);
this.heroFound = true;
}
}
break;
case WIDGET_TYPES.CAROUSEL:
if (widget.getItems().length) {
widgetLayout = widget.getLayout();
carouselsWidgetParams = this._getWidgetParams(widgetLayout);
list.appendChildWidget(new Carousel(widget, carouselsWidgetParams));
}
break;
case WIDGET_TYPES.EDITORIAL_BLOCK:
editorialBlocks = widget.getEditorialBlocks({hasContent: true});
if (editorialBlocks.length) {
this._hero = list
.appendChildWidget(new HeroSlider(editorialBlocks, widgetsParams.editorialBlocks));
this.heroFound = true;
}
break;
case WIDGET_TYPES.HERO_SLIDER:
sliderItems = widget.getItems();
this._hero = list.appendChildWidget(new HeroSlider(sliderItems, widgetsParams.hero));
this.heroFound = true;
break;
// No default.
}
},
/**
* Returns the widgets params for the type of layout.
*
* @param {string} widgetLayout - The type of layout of the widget.
* @returns {Object} - The widget's params.
*/
_getWidgetParams: function (widgetLayout) {
var widgetsParams = WIDGETS_PARAMS[this.pageId];
switch (widgetLayout) {
case 'EPG_LARGE':
return widgetsParams.carousels.epg;
case 'VOD_SMALL':
return widgetsParams.carousels.vod;
case 'BOOKMARK_LARGE_HORIZONTAL':
return widgetsParams.carousels.bookmark;
default:
return widgetsParams.carousels.vod || widgetsParams.carousels.epg;
}
},
/**
* Aligns the carousels to force them to render items.
*/
alignCarousels: function () {
Utils.each(this._widgetList.getChildWidgets(), function (widget) {
if (widget && widget instanceof Carousel) {
widget.alignToIndex(0);
}
});
},
alignCarouselsToLastIndex: function () {
Utils.each(this._widgetList.getChildWidgets(), function (widget) {
if (widget && widget instanceof Carousel) {
widget.alignToLastIndex();
}
});
},
/**
* Removes all the widgets.
*/
removeAll: function () {
this._hero = null;
this._list.removeChildWidgets();
this._widgetList.removeChildWidgets();
this._topList.removeChildWidgets();
this._list.appendChildWidget(this._topList);
this._list.appendChildWidget(this._widgetList);
},
/**
* Returns true if the component has a hero element attached.
*
* @returns {boolean} - True if the component has a hero element attached.
*/
hasHeroElement: function () {
return this._hero !== null;
},
/**
* Shows the Hero widget.
*/
showHero: function () {
var self = this;
if (this.hasHeroElement()) {
if (!this._hero.isVisible() || this._isHidingHero) {
this._isShowingHero = true;
this._hero.show({
duration: 500,
onComplete: function () {
self._isShowingHero = false;
}
});
}
}
},
/**
* Hides the Hero widget.
*
* @param {boolean} skipAnimation - Whether to skip animation.
*/
hideHero: function (skipAnimation) {
var self = this;
if (this.hasHeroElement()) {
if (this._hero.isVisible() || this._isShowingHero) {
if (skipAnimation) {
this._hero.hide({skipAnim: true});
} else {
this._isHidingHero = true;
this._hero.hide({
duration: 300,
onComplete: function () {
self._isHidingHero = false;
}
});
}
}
}
},
/**
* Routes to watch all module.
*
* @param {Promise} watchAllItem - The watchall item.
*/
goToWatchAll: function (watchAllItem) {
var widgetsParams = WIDGETS_PARAMS[this.pageId],
watchAllEndpoint = watchAllItem.getWatchAll(),
gridParams;
if (watchAllItem.isEpgLayout()) {
gridParams = widgetsParams.grid.epg;
} else if (watchAllItem.isBookmarkLayout()) {
gridParams = widgetsParams.grid.bookmark;
} else {
gridParams = widgetsParams.grid.vod;
}
application.route('watchall', {
watchAll: watchAllEndpoint,
gridParams: gridParams
});
},
/**
* Unlock the assets.
*
* @private
*/
_unlockAssets: function () {
var rows = this._widgetList.getChildWidgets(),
assets;
this._assetsUnlocked = true;
Utils.each(rows, function (row) {
if (row.getCarousel) {
assets = row.getCarousel().getChildWidgets();
Utils.each(assets, function (asset) {
if (asset.hasClass('asset')) {
asset.unlock();
}
});
}
});
if (this._grid) {
Utils.each(this._grid.getList().getChildWidgets(), function (row) {
Utils.each(row.getChildWidgets(), function (asset) {
if (asset.hasClass('asset')) {
asset.unlock();
}
});
});
}
},
/**
* Lock the assets.
*
* @private
*/
_lockAssets: function () {
var rows = this._widgetList.getChildWidgets(),
assets;
this._assetsUnlocked = false;
Utils.each(rows, function (row) {
if (row.getCarousel) {
assets = row.getCarousel().getChildWidgets();
Utils.each(assets, function (asset) {
if (asset.hasClass('asset')) {
asset.lock();
}
});
}
});
if (this._grid) {
Utils.each(this._grid.getList().getChildWidgets(), function (row) {
Utils.each(row.getChildWidgets(), function (asset) {
if (asset.hasClass('asset')) {
asset.lock();
}
});
});
}
},
/**
* Routes to the details page of the content.
*
* @param {Object} routeParams - The route params.
*/
_routeToDetails: function (routeParams) {
application.route('details', {
data: routeParams.data,
callback: Utils.bind(this.focus, this)
});
},
/**
*
* @param {Object} routeParams - The player's route params.
* @param {string} playerRoute - The player to be used (liveplayer, vodplayer, catchupplayer...).
* @private
*/
_routeToPlayer: function (routeParams, playerRoute) {
application.route(playerRoute || 'player', routeParams);
GA.onEvent('player', 'started', {eventLabel: this.pageId});
},
/**
* Back event.
*
* @private
*/
_onBack: function () {
this.parentWidget.back();
application.focusMenu('main');
},
/**
* Starts the progressbars update interval.
*/
_updateProgressbars: function () {
var now = application.getDate(),
rows = this._widgetList.getChildWidgets(),
rowItems,
currentProgramFinished,
dataItem,
hasProgressbar,
updateableCarousel;
if (this._progressInterval) {
this._clearProgressInterval();
Utils.each(rows, Utils.bind(function (row) {
updateableCarousel = row.getCarousel && !row.hasClass('hero') &&
this._isUpdateableCarousel(row.getCarouselLayout());
if (updateableCarousel) {
rowItems = row.getCarousel().getChildWidgets();
Utils.each(rowItems, Utils.bind(function (rowItem) {
if (rowItem.hasClass('asset')) {
hasProgressbar = rowItem.updateProgressBar();
dataItem = rowItem.getDataItem();
currentProgramFinished = now >= dataItem.endTime;
// A program has finished in the row, add it to the update list.
if (currentProgramFinished && hasProgressbar) {
if (!this._updateRowsList) {
this._updateRowsList = [];
}
if (!Utils.contains(this._updateRowsList, row)) {
this._updateRowsList.push(row);
}
}
}
}, this));
}
}, this));
}
if (!this._updateCarouselsInterval) {
this._updateCarouselsInterval = setInterval(Utils.bind(this._updateCarousels, this), UPDATE_CAROUSEL_INTERVAL_TIME);
}
this._progressInterval = setInterval(Utils.bind(this._updateProgressbars, this), PROGRESS_INTERVAL_TIME);
},
/**
* Clears any progress interval update.
*
* @private
*/
_clearProgressInterval: function () {
clearInterval(this._progressInterval);
this._progressInterval = null;
},
/**
* Clears any progress interval update.
*
* @private
*/
_clearUpdateCarouselsInterval: function () {
clearInterval(this._updateCarouselsInterval);
this._updateCarouselsInterval = null;
},
/**
* Checks carousels with EPG layout that can be updated with new data.
* i.e. live carousels in home or sports page.
*
* @param {string} carouselLayout - The carousel layout.
* @returns {boolean} - True if the carousel can be updated.
**/
_isUpdateableCarousel: function (carouselLayout) {
switch (carouselLayout) {
case 'EPG_SPORT':
// Falls through.
case 'EPG_LARGE':
return true;
default:
return false;
}
},
/**
* Updates the carousels found on the update list.
*
* @private
*/
_updateCarousels: function () {
if (this._updateRowsList && this._updateRowsList.length) {
this._clearProgressInterval();
Utils.each(this._updateRowsList, Utils.bind(function (row) {
row.updateCarousel();
this._updateProgressbars();
}, this));
this._updateRowsList.length = 0;
}
}
});
UIBuilderComponent.WIDGET_TYPES = WIDGET_TYPES;
return UIBuilderComponent;
});