define('application/components/channellist', [
'rofl/widgets/component',
'rofl/widgets/label',
'rofl/lib/l10n',
'application/widgets/channellist/grid',
'rofl/widgets/verticallist',
'rofl/lib/utils',
'rofl/events/keyevent',
'antie/runtimecontext',
'application/managers/epg',
'application/models/configuration',
'application/widgets/clock',
'rofl/widgets/horizontallist',
'application/widgets/infoblock',
'rofl/analytics/web/google',
'rofl/widgets/container',
'application/managers/channel',
'application/widgets/guide/directdial',
'application/models/epg/item',
'application/managers/halo',
'application/constants'
], function (
Component,
Label,
L10N,
Grid,
VerticalList,
Utils,
KeyEvent,
RuntimeContext,
EPGManager,
ApiConfig,
Clock,
HorizontalList,
InfoBlock,
GoogleAnalytics,
Container,
ChannelManager,
DirectDial,
EPGItem,
HaloManager,
Constants
) {
'use strict';
var l10n = L10N.getInstance(),
application = RuntimeContext.getCurrentApplication(),
configuration = application.getConfiguration(),
epgManager = EPGManager.getInstance(),
GA = GoogleAnalytics.getInstance(),
device = RuntimeContext.getDevice(),
layout = application.getLayout(),
channelManager = ChannelManager.getInstance(),
infoblockConfig = {
id: 'back-on-top-block',
text: l10n.get('infoblock'),
classname: ['icon', 'icon-back-v2'],
position: 'right'
},
ChannelList;
ChannelList = Component.extend({
/**
* Initialises the component.
*/
init: function init () {
init.base.call(this, 'channel-list');
this._build();
this._onKeyDownBound = Utils.bind(this._onKeyDown, this);
this._onSelectedItemChangeBound = Utils.bind(this._onSelectedItemChange, this);
this._onVisibilityChangeBound = Utils.bind(this._onVisibilityChange, this);
this._onSelectBound = Utils.bind(this._onSelect, this);
this._unlockAssetsBound = Utils.bind(this._unlockAssets, this);
this._lockAssetsBound = Utils.bind(this._lockAssets, this);
this._enablePrependData = false;
},
/**
* Builds the component.
*
* @private
*/
_build: function () {
this._buildBackButton();
this._buildGradient();
this._buildTitle();
this._buildSubtitle();
this._buildClock();
this._buildList();
this._buildGrid();
this._buildInfoBlock();
this._buildDirectDial();
},
/**
* Builds the top bar list.
*
* @private
*/
_buildBackButton: function () {
var topBar = this._topBar = new HorizontalList(),
branding = this._branding = new Container();
branding.addClass('page-branding');
branding.addClass('page-title');
topBar.addClass('topbar');
topBar.appendChildWidget(branding);
this.appendChildWidget(topBar);
},
/**
* Builds the top bar list.
*
* @private
*/
_buildGradient: function () {
var gradient = this._gradient = new Container();
gradient.addClass('gradient');
this._topBar.appendChildWidget(gradient);
},
/**
* Builds the direct dial.
*
* @private
*/
_buildDirectDial: function () {
var directDial = this._directDial = new DirectDial();
this.appendChildWidget(directDial);
},
/**
* Builds the title.
*
* @private
*/
_buildTitle: function () {
var title = this._title = new Label({ text: l10n.get('channellist.title'), classNames: ['top-title'] });
this._branding.appendChildWidget(title);
},
/**
* Builds the subtitle.
*
* @private
*/
_buildSubtitle: function () {
var subtitle = this._subtitle = new Label({ text: l10n.get('channellist.subtitle'), classNames: ['main-subtitle'] });
this._topBar.appendChildWidget(subtitle);
},
/**
* Builds the list.
*
* @private
*/
_buildList: function () {
var list = this._list = new VerticalList();
this.appendChildWidget(list);
},
/**
* Builds the grid.
*
* @private
*/
_buildGrid: function () {
var opts = {
loadmoreCallback: Utils.bind(this._getChannels, this)
},
grid = this._grid = new Grid(opts);
this._list.appendChildWidget(grid);
},
/**
* Builds the clock.
*
* @private
*/
_buildClock: function () {
var clock = this._clock = new Clock();
clock.addClass('header-clock');
this._topBar.appendChildWidget(clock);
},
/**
* Builds the infoblock.
*
* @private
*/
_buildInfoBlock: function () {
var infoblock = this._infoblock = new InfoBlock(infoblockConfig);
infoblock.addClass('info-block');
this.appendChildWidget(infoblock);
},
/**
* BeforeRender event.
*/
onBeforeRender: function () {
this._loadingTime = application.getDate();
},
/**
* Before show event.
*
* @param {Event} e - Event.
*/
onBeforeShow: function (e) {
var args = e.args || null,
channelNumber;
this._endIndex = configuration.loading.channelList.maxChannels;
this._startChannelsPage = application.getDate();
this._enablePrependData = false;
this._channelsLoading = false;
this._grid.resetStartEndIndex();
this._grid.resetLoadmoreIndex();
GA.onPageView('channellist');
application.hideLoader();
this._infoblock.hide({skipAnim: true});
this._topBar.removeClass('active-gradient');
this._directDial.hide({skipAnim: true});
this.resetFocus();
if (!args.lastChannelId) {
this._getChannels(0, this._endIndex);
}
this._grid.addEventListener('selecteditemchange', this._onSelectedItemChangeBound);
this.addEventListener('keydown', this._onKeyDownBound);
this.addEventListener('select', this._onSelectBound);
document.addEventListener('visibilitychange', Utils.bind(this._onVisibilityChangeBound, this));
application.addEventListener('$unlocked', this._unlockAssetsBound);
application.addEventListener('$locked', this._lockAssetsBound);
if (args.lastChannelId) {
channelNumber = channelManager.getChannelById(args.lastChannelId).getNumber();
this._onDirectDial(channelNumber);
} else {
this._grid.alignToFirstItem();
}
if (Utils.isFunction(args.callback)) {
args.callback();
}
this._updateProgressbars();
HaloManager.getInstance()
.getServiceMessage()
.then(function (r) {
if (r) {
application.route('service');
}
});
},
/**
* Starts the progressbars update interval.
*/
_updateProgressbars: function () {
var gridAssets,
currentProgramFinished,
channelId,
now = application.getDate(),
dataItem;
if (this._progressInterval) {
clearInterval(this._progressInterval);
gridAssets = this._grid.getCarouselItems();
Utils.each(gridAssets, Utils.bind(function (gridAsset) {
gridAsset.updateProgressBar();
dataItem = gridAsset.getDataItem();
currentProgramFinished = now >= dataItem.endTime;
if (currentProgramFinished) {
channelId = dataItem.item.getChannelId();
this._setNextProgram(channelId, gridAsset);
}
}, this));
}
this._progressInterval = setInterval(Utils.bind(this._updateProgressbars, this), 5000);
},
/**
* Sets the new program for the given channel id.
*
* @param {number} channelId - Channel id.
* @param {Object} currentItem - Current broadcasting program.
* @private
*/
_setNextProgram: function (channelId, currentItem) {
var resultsArray, data;
epgManager.getCurrentItem(channelId)
.then(Utils.bind(function (result) {
// Create epgData when missing.
Utils.each(result, function (val, key) {
result[key] = val || this.createInfoNotAvailable({}, key);
});
resultsArray = Utils.values(result);
data = resultsArray[0];
if (currentItem.getId() !== (data && data.getId())) {
this._grid.updateAsset(data, currentItem);
}
}, this));
},
/**
* Reset grid focus.
*/
resetFocus: function () {
this._grid.focus();
this._grid.setStyleTo('top', '0');
},
/**
* Before hide event.
*/
onBeforeHide: function () {
var duration = ((application.getDate() - this._startChannelsPage) / 60000).toFixed(2),
grid = this._grid;
grid.removeEventListener('selecteditemchange', this._onSelectedItemChangeBound);
grid.resetLoadmoreIndex();
grid.resetStartEndIndex();
this.removeEventListener('keydown', this._onKeyDownBound);
this.removeEventListener('select', this._onSelectBound);
application.removeEventListener('$unlocked', this._unlockAssetsBound);
application.removeEventListener('$locked', this._lockAssetsBound);
this._clearProgressInterval();
GA.onEvent('page', 'time', {
eventLabel: 'channellist',
eventValue: duration
});
},
/**
* Handle KeyEvent.
*
* @param {KeyEvent} e - KeyEvent instance.
* @private
*/
_onKeyDown: function (e) {
var activeRowNumber = this._grid.getActiveChildIndex() + 1;
switch (e.keyCode) {
case KeyEvent.VK_LEFT:
e.preventDefault();
e.stopPropagation();
application.focusMenu('main');
break;
case KeyEvent.VK_RIGHT:
e.preventDefault();
e.stopPropagation();
break;
case KeyEvent.VK_BACK:
if (activeRowNumber !== 1) {
this._grid.resetLoadmoreIndex();
this._grid.resetStartEndIndex();
this._getChannels(0, this._endIndex);
this._grid.alignToFirstItem();
} else {
application.focusMenu('main');
}
break;
case KeyEvent.VK_0:
case KeyEvent.VK_1:
case KeyEvent.VK_2:
case KeyEvent.VK_3:
case KeyEvent.VK_4:
case KeyEvent.VK_5:
case KeyEvent.VK_6:
case KeyEvent.VK_7:
case KeyEvent.VK_8:
case KeyEvent.VK_9:
this._onDirectDialInput(e.keyChar);
break;
}
},
/**
* OnDirectDialInput.
*
* @param {string} char - The input character.
* @private
*/
_onDirectDialInput: function (char) {
var directDial = this._directDial;
clearTimeout(this._timer);
directDial.setKeyInput(char);
if (!directDial.isVisible()) {
directDial.show({duration: 200});
}
this._clearDirectDialTimeout();
this._directDialTimeout = setTimeout(Utils.bind(this._onDirectDial, this), 3000);
},
/**
* Gets executed when the direct dial finalises.
*
* @param {Number|null} number - The channel number.
* @private
*/
_onDirectDial: function (number) {
var maxChannels = configuration.loading.channelList.maxChannels,
endIndex = maxChannels,
grid = this._grid,
rowIndex = 3,
itemsBefore = 12,
itemsAfter = 4,
reset = true,
channelIndex,
startIndex,
channel,
inRowIndex;
number = number || parseInt(this._directDial.getCurrentInput());
this._enablePrependData = false;
if (!isNaN(number)) {
channel = channelManager.getChannelByNumber(number);
if (channel) {
channelIndex = channelManager.getChannelIndex(channel.getId());
inRowIndex = channelIndex % 4;
startIndex = channelIndex - itemsBefore - inRowIndex;
endIndex = startIndex + maxChannels + itemsAfter;
if (startIndex < 0) {
startIndex = 0;
}
if (startIndex === 0) {
if (channelIndex < 4) {
rowIndex = 0;
} else if (channelIndex >= 4 && channelIndex < 8) {
rowIndex = 1;
} else if (channelIndex >= 8 && channelIndex < 12) {
rowIndex = 2;
}
}
grid.setRowIndex(rowIndex);
grid.setInRowIndex(inRowIndex);
grid.setStartIndex(startIndex);
grid.setEndIndex(endIndex);
grid.setReachedEnd(false);
grid.resetLoadmoreIndex();
this.resetFocus();
this._getChannels(startIndex, endIndex, reset);
this._directDial.hide({duration: 200});
this._directDial.reset();
}
}
},
/**
* Clears the direct dial timeout.
*
* @private
*/
_clearDirectDialTimeout: function () {
clearTimeout(this._directDialTimeout);
},
/**
* Get data from API.
*
* @param {number} start - Start index.
* @param {number} end - End index.
* @param {boolean} reset - True if grid should load data as new list with clearing the previous data.
* @param {boolean} prependData - True if items should prepending to the grid.
*
* @private
*/
_getChannels: function (start, end, reset, prependData) {
var maxIndex = channelManager.getChannelMap().length - 1,
grid = this._grid,
maxChannels = configuration.loading.guide.maxChannels,
rowIndex = grid.getRowIndex(),
channels,
resultsArray,
lastActiveRow,
lastActiveRowItem,
self = this;
this._channelsLoading = true;
reset = reset || false;
prependData = prependData || false;
application.showLoader(true);
if (start === 0) {
grid.setStartIndex(start);
grid.setEndIndex(end);
}
if (start <= 0) {
start = 0;
this._enablePrependData = false;
}
channels = channelManager.getChannelMap(start, end);
epgManager.getCurrentItem(channels)
.then(Utils.bind(function (result) {
var value = ((application.getDate() - this._loadingTime) / 1000).toFixed(2);
Utils.each(result, function (val, key) {
// Create epgData when missing.
if (!val) {
result[key] = self.createInfoNotAvailable({}, key);
}
});
resultsArray = Utils.values(result);
resultsArray.sort(function (a, b) {
return a.getChannel().getNumber() > b.getChannel().getNumber() ? 1 : -1;
});
if (!grid.isVisible()) {
grid.show();
}
if ((start === 0 && !prependData) || reset) {
grid.setDataItem({
items: resultsArray
}, true);
if (reset) {
if (start + maxChannels >= maxIndex) {
grid.setReachedEnd(true);
}
if (start !== 0) {
this._enablePrependData = true;
}
grid.getList().alignToIndex(rowIndex);
grid.getList().getActiveChildWidget().setActiveChildIndex(grid.getInRowIndex());
}
this._channelsLoading = false;
} else {
if (prependData) {
lastActiveRow = grid.getActiveRow();
lastActiveRowItem = lastActiveRow.getActiveChildWidget();
grid.prependData(resultsArray, false, false);
grid.setActiveChildWidget(lastActiveRow);
grid.getActiveChildWidget().setActiveChildWidget(lastActiveRowItem);
grid.getList().alignToIndex(grid.getList().getActiveChildIndex());
lastActiveRowItem.focus();
} else {
grid.appendData(resultsArray, false, false);
}
this._channelsLoading = false;
}
application.hideLoader();
GA.onEvent('page', 'load', {
eventLabel: 'channellist',
eventValue: value
});
}, this))
['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'
});
});
},
/**
* Creates epgData when missing.
*
* @param {Object} data - The data.
* @param {string} channelId - The channel ID.
* @returns {EpgItem} - New EPGItem.
*/
createInfoNotAvailable: function (data, channelId) {
return new EPGItem(data, channelManager.getChannelById(channelId));
},
/**
* Opens player and plays channel.
*
* @param {string} data - The data item to attempt to play.
* @private
*/
_playAsset: function (data) {
application.route('liveplayer', {
data: data,
type: Constants.LIVE_VIDEO_TYPE,
channelId: null,
callingPage: 'channelList'
});
GA.onEvent('player', 'started', {eventLabel: 'channellist'});
},
/**
* OnSelect event.
*
* @param {Object} e - Event.
* @private
*/
_onSelect: function (e) {
var dataItem = e.target.getDataItem().item;
if (e.target.id === 'back-on-top-block') {
this._grid.alignToFirstItem();
return;
}
if (e.target.isLocked()) {
application.route('parentalpin', {
successCallback:
Utils.bind(function () {
this._playAsset(dataItem);
this._unlockAssets();
}, this),
escapeCallback: Utils.bind(this.focus, this)
});
return;
}
this._playAsset(dataItem);
},
/**
* Unlock the assets.
*
* @private
*/
_unlockAssets: function () {
var rows = this._list.getChildWidgetByIndex(0).getList().getChildWidgets(),
assets;
Utils.each(rows, function (row) {
assets = row.getChildWidgets();
Utils.each(assets, function (asset) {
asset.unlock();
});
});
},
/**
* Unlock the assets.
*
* @private
*/
_lockAssets: function () {
var rows = this._list.getChildWidgetByIndex(0).getList().getChildWidgets(),
assets;
Utils.each(rows, function (row) {
assets = row.getChildWidgets();
Utils.each(assets, function (asset) {
asset.lock();
});
});
},
/**
* Change elements positon on selected item change.
*
* @param {Object} e - The selected item change event.
* @private
*/
_onSelectedItemChange: function (e) {
var activeRowNumber = this._grid.getActiveChildIndex() + 1,
shouldPrepend = false;
if (activeRowNumber === 3 && this._enablePrependData) {
shouldPrepend = true;
}
if (activeRowNumber > 1) {
this._infoblock.show();
} else {
this._infoblock.hide();
}
if (activeRowNumber > 1) {
this._gradient.setStyleTo('display', 'block');
} else {
this._gradient.setStyleTo('display', 'none');
}
if (!this._channelsLoading) {
this._grid.checkLoadMore(shouldPrepend);
}
},
/**
* Visibility change event.
*
* @param {Object} e - The event data.
* @private
*/
_onVisibilityChange: function (e) {
if (this.isFocussed() && (document.visibilityState === 'visible' || e === 'visible')) {
this._updateProgressbars();
} else {
this._clearProgressInterval();
}
},
/**
* Clears any progress interval update.
*
* @private
*/
_clearProgressInterval: function () {
clearInterval(this._progressInterval);
this._progressInterval = null;
}
});
return ChannelList;
});