mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
unify guide between web and theater
This commit is contained in:
parent
e8c224ae0b
commit
af4e8c5529
23 changed files with 1314 additions and 1024 deletions
|
@ -16,12 +16,12 @@
|
|||
},
|
||||
"devDependencies": {},
|
||||
"ignore": [],
|
||||
"version": "1.2.39",
|
||||
"_release": "1.2.39",
|
||||
"version": "1.2.45",
|
||||
"_release": "1.2.45",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "1.2.39",
|
||||
"commit": "956d151bb5506eea13c56b69f21220b55d2b7d35"
|
||||
"tag": "1.2.45",
|
||||
"commit": "9c7b62bb1c95ce96c86da19d7d961eda9042acf7"
|
||||
},
|
||||
"_source": "https://github.com/MediaBrowser/emby-webcomponents.git",
|
||||
"_target": "^1.2.0",
|
||||
|
|
12
dashboard-ui/bower_components/emby-webcomponents/clearbutton.css
vendored
Normal file
12
dashboard-ui/bower_components/emby-webcomponents/clearbutton.css
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
.clearButton {
|
||||
background: transparent;
|
||||
border: 0 !important;
|
||||
padding: 0 !important;
|
||||
cursor: pointer;
|
||||
outline: none !important;
|
||||
color: inherit;
|
||||
width: 100%;
|
||||
vertical-align: middle;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
382
dashboard-ui/bower_components/emby-webcomponents/guide/guide.css
vendored
Normal file
382
dashboard-ui/bower_components/emby-webcomponents/guide/guide.css
vendored
Normal file
|
@ -0,0 +1,382 @@
|
|||
.tvguide {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: initial;
|
||||
}
|
||||
|
||||
.tvGuideHeader {
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tvGuideHeader.headroom--unpinned {
|
||||
-webkit-transform: translateY(-98px);
|
||||
transform: translateY(-98px);
|
||||
}
|
||||
|
||||
.tvProgramSectionHeader {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tvProgram {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tvProgramTimeSlotInner {
|
||||
padding: .5em;
|
||||
}
|
||||
|
||||
.tvProgramInfo {
|
||||
vertical-align: middle;
|
||||
padding: .5em .5em;
|
||||
border-bottom: .65vh solid #121212;
|
||||
}
|
||||
|
||||
.tvProgramCurrentTimeSlot {
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
.tvProgramName {
|
||||
color: #fff;
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
.tvProgramTime {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.newTvProgram {
|
||||
color: yellow;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.liveTvProgram {
|
||||
color: #64A239;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.premiereTvProgram {
|
||||
color: orange;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.programAccent {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
.sportsProgramInfo .programAccent {
|
||||
background-color: #0A7C33;
|
||||
}
|
||||
|
||||
.newsProgramInfo .programAccent {
|
||||
background-color: #523378;
|
||||
}
|
||||
|
||||
.movieProgramInfo .programAccent {
|
||||
background-color: #A43913;
|
||||
}
|
||||
|
||||
.childProgramInfo .programAccent {
|
||||
background-color: #0B487D;
|
||||
}
|
||||
|
||||
.timerCircle {
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
border-radius: 50%;
|
||||
background: #cc0000;
|
||||
border: 1px solid #cc3333;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.seriesTimerCircle {
|
||||
position: relative;
|
||||
margin-left: 0;
|
||||
left: 21px;
|
||||
opacity: .3;
|
||||
}
|
||||
|
||||
.itemMiscInfo .seriesTimerCircle:first-child {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.seriesTimerCircle + .seriesTimerCircle {
|
||||
opacity: .65;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.seriesTimerCircle + .seriesTimerCircle + .seriesTimerCircle {
|
||||
opacity: 1;
|
||||
left: -21px;
|
||||
}
|
||||
|
||||
.channelTimeslotHeader {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.timeslotHeaders {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mobileGuide .timeslotHeaders {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.programContainer {
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.programGridContainer {
|
||||
margin-left: 12.2vw;
|
||||
}
|
||||
|
||||
.channelPrograms {
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.channelPrograms, .timeslotHeadersInner {
|
||||
width: 1800vw;
|
||||
}
|
||||
|
||||
@media all and (min-width: 600px) {
|
||||
|
||||
.channelPrograms, .timeslotHeadersInner {
|
||||
width: 1400vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-width: 800px) {
|
||||
|
||||
.channelPrograms, .timeslotHeadersInner {
|
||||
width: 1200vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-width: 1280px) {
|
||||
|
||||
.channelPrograms, .timeslotHeadersInner {
|
||||
width: 660vw;
|
||||
}
|
||||
}
|
||||
|
||||
.timeslotHeader {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
text-indent: .35em;
|
||||
}
|
||||
|
||||
.channelHeaderCell, .channelTimeslotHeader {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
border-right: 1px solid #121212;
|
||||
width: 24vw;
|
||||
background: rgba(40, 40, 40, .9);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #fff !important;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@media all and (min-width: 500px) {
|
||||
|
||||
.channelHeaderCell, .channelTimeslotHeader {
|
||||
width: 16vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-width: 600px) {
|
||||
|
||||
.channelHeaderCell, .channelTimeslotHeader {
|
||||
width: 16vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-width: 800px) {
|
||||
|
||||
.channelHeaderCell, .channelTimeslotHeader {
|
||||
width: 14vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-width: 1280px) {
|
||||
|
||||
.channelHeaderCell, .channelTimeslotHeader {
|
||||
width: 12vw;
|
||||
}
|
||||
}
|
||||
|
||||
.btnSelectDate {
|
||||
color: #fff;
|
||||
padding-left: .5em;
|
||||
}
|
||||
|
||||
.channelHeaderCell {
|
||||
border-bottom: .65vh solid #121212 !important;
|
||||
background-size: auto 65.7%;
|
||||
background-position: 90% center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
@media all and (max-width: 800px) {
|
||||
|
||||
.guideCurrentDay {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 1280px) {
|
||||
|
||||
.guideChannelInfoWithImage .guideChannelName {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.channelPrograms, .channelHeaderCell {
|
||||
height: 15vh;
|
||||
}
|
||||
|
||||
@media all and (min-height: 500px) {
|
||||
|
||||
.channelPrograms, .channelHeaderCell {
|
||||
height: 10.5vh;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-height: 600px) {
|
||||
|
||||
.channelPrograms, .channelHeaderCell {
|
||||
height: 9vh;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-height: 720px) {
|
||||
|
||||
.channelPrograms, .channelHeaderCell {
|
||||
height: 6vh;
|
||||
}
|
||||
|
||||
.layout-tv .channelPrograms, .layout-tv .channelHeaderCell {
|
||||
height: 7.6vh;
|
||||
}
|
||||
}
|
||||
|
||||
.channelTimeslotHeader {
|
||||
border-right-color: transparent;
|
||||
}
|
||||
|
||||
.channelTimeslotHeader, .timeslotHeader {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.timeslotHeader, .channelTimeslotHeader {
|
||||
height: 5.75vh;
|
||||
}
|
||||
|
||||
.pointerInput .channelHeaderCell:hover {
|
||||
background-color: #444;
|
||||
}
|
||||
|
||||
.channelList {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.programGrid {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.timeslotHeader {
|
||||
width: 2.0833333333333333333333333333333%;
|
||||
}
|
||||
|
||||
.programCell {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
/* Unfortunately the borders using vh get rounded while the bottom property doesn't. So this is a little hack to try and make them even*/
|
||||
bottom: .59vh;
|
||||
border-left: .65vh solid #121212 !important;
|
||||
background-color: rgba(32, 32, 32, .95);
|
||||
display: flex;
|
||||
color: #fff !important;
|
||||
text-decoration: none;
|
||||
overflow: hidden;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.timeslotCellInner {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.guideProgramName {
|
||||
padding: 0 .5em 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.guideProgramTime {
|
||||
padding: 0 .5em .35em;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.programCell iron-icon {
|
||||
margin-left: auto;
|
||||
margin-right: .5em;
|
||||
height: 3.5vh;
|
||||
width: 3.5vh;
|
||||
color: #ddd;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.programCell iron-icon + iron-icon {
|
||||
margin-left: .25em;
|
||||
}
|
||||
|
||||
.guideChannelInfo {
|
||||
display: inline-block;
|
||||
max-width: 110px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
padding-left: .7em;
|
||||
}
|
||||
|
||||
.guideChannelName {
|
||||
margin-left: auto;
|
||||
margin-right: .3em;
|
||||
max-width: 6vw;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.channelList, .programGrid {
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.programCell:focus, .channelHeaderCell:focus, .btnSelectDate:focus {
|
||||
background-color: #52B54B;
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
.programCell:focus .programAccent {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.timerIcon, .seriesTimerIcon {
|
||||
color: #cc3333 !important;
|
||||
}
|
633
dashboard-ui/bower_components/emby-webcomponents/guide/guide.js
vendored
Normal file
633
dashboard-ui/bower_components/emby-webcomponents/guide/guide.js
vendored
Normal file
|
@ -0,0 +1,633 @@
|
|||
define(['globalize', 'connectionManager', 'loading', 'scrollHelper', 'datetime', 'focusManager', 'imageLoader', 'events', 'layoutManager', 'itemShortcuts', 'registrationservices', 'clearButtonStyle', 'css!./guide.css', 'scrollStyles'], function (globalize, connectionManager, loading, scrollHelper, datetime, focusManager, imageLoader, events, layoutManager, itemShortcuts, registrationServices) {
|
||||
|
||||
var baseUrl;
|
||||
|
||||
function Guide(options) {
|
||||
|
||||
var self = this;
|
||||
var items = {};
|
||||
|
||||
self.refresh = function () {
|
||||
reloadPage(options.element);
|
||||
};
|
||||
|
||||
self.destroy = function () {
|
||||
itemShortcuts.off(options.element);
|
||||
items = {};
|
||||
};
|
||||
|
||||
// 30 mins
|
||||
var cellCurationMinutes = 30;
|
||||
var cellDurationMs = cellCurationMinutes * 60 * 1000;
|
||||
var msPerDay = 86400000;
|
||||
|
||||
var currentDate;
|
||||
|
||||
var channelQuery = {
|
||||
|
||||
StartIndex: 0,
|
||||
EnableFavoriteSorting: true
|
||||
};
|
||||
|
||||
var channelsPromise;
|
||||
|
||||
function normalizeDateToTimeslot(date) {
|
||||
|
||||
var minutesOffset = date.getMinutes() - cellCurationMinutes;
|
||||
|
||||
if (minutesOffset >= 0) {
|
||||
|
||||
date.setHours(date.getHours(), cellCurationMinutes, 0, 0);
|
||||
|
||||
} else {
|
||||
|
||||
date.setHours(date.getHours(), 0, 0, 0);
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
function showLoading() {
|
||||
loading.show();
|
||||
}
|
||||
|
||||
function hideLoading() {
|
||||
loading.hide();
|
||||
}
|
||||
|
||||
function getChannelLimit(context) {
|
||||
|
||||
return registrationServices.validateFeature('livetv').then(function () {
|
||||
|
||||
var limit = 400;
|
||||
|
||||
context.querySelector('.guideRequiresUnlock').classList.add('hide');
|
||||
|
||||
return limit;
|
||||
|
||||
}, function () {
|
||||
|
||||
var limit = 5;
|
||||
|
||||
context.querySelector('.guideRequiresUnlock').classList.remove('hide');
|
||||
context.querySelector('.unlockText').innerHTML = globalize.translate('MessageLiveTvGuideRequiresUnlock', limit);
|
||||
|
||||
return limit;
|
||||
});
|
||||
}
|
||||
|
||||
function reloadGuide(context, newStartDate) {
|
||||
|
||||
var apiClient = connectionManager.currentApiClient();
|
||||
|
||||
channelQuery.UserId = apiClient.getCurrentUserId();
|
||||
|
||||
getChannelLimit(context).then(function (channelLimit) {
|
||||
|
||||
showLoading();
|
||||
|
||||
channelQuery.Limit = channelLimit;
|
||||
channelQuery.AddCurrentProgram = false;
|
||||
|
||||
channelsPromise = channelsPromise || apiClient.getLiveTvChannels(channelQuery);
|
||||
|
||||
var date = newStartDate;
|
||||
// Add one second to avoid getting programs that are just ending
|
||||
date = new Date(date.getTime() + 1000);
|
||||
|
||||
// Subtract to avoid getting programs that are starting when the grid ends
|
||||
var nextDay = new Date(date.getTime() + msPerDay - 2000);
|
||||
|
||||
console.log(nextDay);
|
||||
channelsPromise.then(function (channelsResult) {
|
||||
|
||||
apiClient.getLiveTvPrograms({
|
||||
UserId: apiClient.getCurrentUserId(),
|
||||
MaxStartDate: nextDay.toISOString(),
|
||||
MinEndDate: date.toISOString(),
|
||||
channelIds: channelsResult.Items.map(function (c) {
|
||||
return c.Id;
|
||||
}).join(','),
|
||||
ImageTypeLimit: 1,
|
||||
EnableImageTypes: "Primary,Backdrop",
|
||||
SortBy: "StartDate"
|
||||
|
||||
}).then(function (programsResult) {
|
||||
|
||||
renderGuide(context, date, channelsResult.Items, programsResult.Items, apiClient);
|
||||
|
||||
hideLoading();
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getDisplayTime(date) {
|
||||
|
||||
if ((typeof date).toString().toLowerCase() === 'string') {
|
||||
try {
|
||||
|
||||
date = datetime.parseISO8601Date(date, { toLocal: true });
|
||||
|
||||
} catch (err) {
|
||||
return date;
|
||||
}
|
||||
}
|
||||
|
||||
return datetime.getDisplayTime(date).toLowerCase();
|
||||
}
|
||||
|
||||
function getTimeslotHeadersHtml(startDate, endDateTime) {
|
||||
|
||||
var html = '';
|
||||
|
||||
// clone
|
||||
startDate = new Date(startDate.getTime());
|
||||
|
||||
html += '<div class="timeslotHeadersInner">';
|
||||
|
||||
while (startDate.getTime() < endDateTime) {
|
||||
|
||||
html += '<div class="timeslotHeader">';
|
||||
|
||||
html += getDisplayTime(startDate);
|
||||
html += '</div>';
|
||||
|
||||
// Add 30 mins
|
||||
startDate.setTime(startDate.getTime() + cellDurationMs);
|
||||
}
|
||||
html += '</div>';
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function parseDates(program) {
|
||||
|
||||
if (!program.StartDateLocal) {
|
||||
try {
|
||||
|
||||
program.StartDateLocal = datetime.parseISO8601Date(program.StartDate, { toLocal: true });
|
||||
|
||||
} catch (err) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!program.EndDateLocal) {
|
||||
try {
|
||||
|
||||
program.EndDateLocal = datetime.parseISO8601Date(program.EndDate, { toLocal: true });
|
||||
|
||||
} catch (err) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getChannelProgramsHtml(context, date, channel, programs) {
|
||||
|
||||
var html = '';
|
||||
|
||||
var startMs = date.getTime();
|
||||
var endMs = startMs + msPerDay - 1;
|
||||
|
||||
programs = programs.filter(function (curr) {
|
||||
return curr.ChannelId == channel.Id;
|
||||
});
|
||||
|
||||
html += '<div class="channelPrograms">';
|
||||
|
||||
for (var i = 0, length = programs.length; i < length; i++) {
|
||||
|
||||
var program = programs[i];
|
||||
|
||||
if (program.ChannelId != channel.Id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
parseDates(program);
|
||||
|
||||
if (program.EndDateLocal.getTime() < startMs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (program.StartDateLocal.getTime() > endMs) {
|
||||
break;
|
||||
}
|
||||
|
||||
items[program.Id] = program;
|
||||
|
||||
var renderStartMs = Math.max(program.StartDateLocal.getTime(), startMs);
|
||||
var startPercent = (program.StartDateLocal.getTime() - startMs) / msPerDay;
|
||||
startPercent *= 100;
|
||||
startPercent = Math.max(startPercent, 0);
|
||||
|
||||
var renderEndMs = Math.min(program.EndDateLocal.getTime(), endMs);
|
||||
var endPercent = (renderEndMs - renderStartMs) / msPerDay;
|
||||
endPercent *= 100;
|
||||
|
||||
var cssClass = "programCell clearButton itemAction";
|
||||
var addAccent = true;
|
||||
|
||||
if (program.IsKids) {
|
||||
cssClass += " childProgramInfo";
|
||||
} else if (program.IsSports) {
|
||||
cssClass += " sportsProgramInfo";
|
||||
} else if (program.IsNews) {
|
||||
cssClass += " newsProgramInfo";
|
||||
} else if (program.IsMovie) {
|
||||
cssClass += " movieProgramInfo";
|
||||
}
|
||||
else {
|
||||
cssClass += " plainProgramInfo";
|
||||
addAccent = false;
|
||||
}
|
||||
|
||||
html += '<button data-action="link" data-isfolder="' + program.IsFolder + '" data-id="' + program.Id + '" data-serverid="' + program.ServerId + '" data-type="' + program.Type + '" class="' + cssClass + '" style="left:' + startPercent + '%;width:' + endPercent + '%;">';
|
||||
|
||||
var guideProgramNameClass = "guideProgramName";
|
||||
|
||||
html += '<div class="' + guideProgramNameClass + '">';
|
||||
|
||||
if (program.IsLive) {
|
||||
html += '<span class="liveTvProgram">' + globalize.translate('core#AttributeLive') + ' </span>';
|
||||
}
|
||||
else if (program.IsPremiere) {
|
||||
html += '<span class="premiereTvProgram">' + globalize.translate('core#AttributePremiere') + ' </span>';
|
||||
}
|
||||
else if (program.IsSeries && !program.IsRepeat) {
|
||||
html += '<span class="newTvProgram">' + globalize.translate('core#AttributeNew') + ' </span>';
|
||||
}
|
||||
|
||||
html += program.Name;
|
||||
html += '</div>';
|
||||
|
||||
if (program.IsHD) {
|
||||
html += '<iron-icon icon="core:hd"></iron-icon>';
|
||||
}
|
||||
|
||||
if (program.SeriesTimerId) {
|
||||
html += '<iron-icon class="seriesTimerIcon" icon="core:fiber-smart-record"></iron-icon>';
|
||||
}
|
||||
else if (program.TimerId) {
|
||||
html += '<iron-icon class="timerIcon" icon="core:fiber-manual-record"></iron-icon>';
|
||||
}
|
||||
|
||||
if (addAccent) {
|
||||
html += '<div class="programAccent"></div>';
|
||||
}
|
||||
|
||||
html += '</button>';
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderPrograms(context, date, channels, programs) {
|
||||
|
||||
var html = [];
|
||||
|
||||
for (var i = 0, length = channels.length; i < length; i++) {
|
||||
|
||||
html.push(getChannelProgramsHtml(context, date, channels[i], programs));
|
||||
}
|
||||
|
||||
var programGrid = context.querySelector('.programGrid');
|
||||
programGrid.innerHTML = html.join('');
|
||||
|
||||
programGrid.scrollTop = 0;
|
||||
programGrid.scrollLeft = 0;
|
||||
}
|
||||
|
||||
function renderChannelHeaders(context, channels, apiClient) {
|
||||
|
||||
var html = '';
|
||||
|
||||
for (var i = 0, length = channels.length; i < length; i++) {
|
||||
|
||||
var channel = channels[i];
|
||||
var hasChannelImage = channel.ImageTags.Primary;
|
||||
var dataSrc = '';
|
||||
if (hasChannelImage) {
|
||||
|
||||
var url = apiClient.getScaledImageUrl(channel.Id, {
|
||||
maxHeight: 200,
|
||||
tag: channel.ImageTags.Primary,
|
||||
type: "Primary"
|
||||
});
|
||||
|
||||
dataSrc = ' data-src="' + url + '"';
|
||||
}
|
||||
|
||||
html += '<button type="button" class="channelHeaderCell clearButton itemAction lazy"' + dataSrc + ' data-action="link" data-isfolder="' + channel.IsFolder + '" data-id="' + channel.Id + '" data-serverid="' + channel.ServerId + '" data-type="' + channel.Type + '">';
|
||||
|
||||
var cssClass = hasChannelImage ? 'guideChannelInfo guideChannelInfoWithImage' : 'guideChannelInfo';
|
||||
|
||||
html += '<div class="' + cssClass + '"><div class="guideChannelName">' + channel.Number + '</div></div>';
|
||||
|
||||
html += '</button>';
|
||||
}
|
||||
|
||||
var channelList = context.querySelector('.channelList');
|
||||
channelList.innerHTML = html;
|
||||
imageLoader.lazyChildren(channelList);
|
||||
}
|
||||
|
||||
function renderGuide(context, date, channels, programs, apiClient) {
|
||||
|
||||
//var list = [];
|
||||
//channels.forEach(function(i) {
|
||||
// list.push(i);
|
||||
//});
|
||||
//channels.forEach(function (i) {
|
||||
// list.push(i);
|
||||
//});
|
||||
//channels.forEach(function (i) {
|
||||
// list.push(i);
|
||||
//});
|
||||
//channels.forEach(function (i) {
|
||||
// list.push(i);
|
||||
//});
|
||||
//channels.forEach(function (i) {
|
||||
// list.push(i);
|
||||
//});
|
||||
//channels.forEach(function (i) {
|
||||
// list.push(i);
|
||||
//});
|
||||
//channels.forEach(function (i) {
|
||||
// list.push(i);
|
||||
//});
|
||||
//channels.forEach(function (i) {
|
||||
// list.push(i);
|
||||
//});
|
||||
//channels.forEach(function (i) {
|
||||
// list.push(i);
|
||||
//});
|
||||
//channels.forEach(function (i) {
|
||||
// list.push(i);
|
||||
//});
|
||||
//channels.forEach(function (i) {
|
||||
// list.push(i);
|
||||
//});
|
||||
//channels.forEach(function (i) {
|
||||
// list.push(i);
|
||||
//});
|
||||
//channels = list;
|
||||
renderChannelHeaders(context, channels, apiClient);
|
||||
|
||||
var startDate = date;
|
||||
var endDate = new Date(startDate.getTime() + msPerDay);
|
||||
context.querySelector('.timeslotHeaders').innerHTML = getTimeslotHeadersHtml(startDate, endDate);
|
||||
items = {};
|
||||
renderPrograms(context, date, channels, programs);
|
||||
|
||||
if (layoutManager.tv) {
|
||||
focusManager.autoFocus(context.querySelector('.programGrid'), true);
|
||||
}
|
||||
}
|
||||
|
||||
function nativeScrollTo(container, pos, horizontal) {
|
||||
|
||||
if (container.scrollTo) {
|
||||
if (horizontal) {
|
||||
container.scrollTo(pos, 0);
|
||||
} else {
|
||||
container.scrollTo(0, pos);
|
||||
}
|
||||
} else {
|
||||
if (horizontal) {
|
||||
container.scrollLeft = Math.round(pos);
|
||||
} else {
|
||||
container.scrollTop = Math.round(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var lastGridScroll = 0;
|
||||
var lastHeaderScroll = 0;
|
||||
function onProgramGridScroll(context, elem, timeslotHeaders) {
|
||||
|
||||
if ((new Date().getTime() - lastHeaderScroll) >= 1000) {
|
||||
lastGridScroll = new Date().getTime();
|
||||
nativeScrollTo(timeslotHeaders, elem.scrollLeft, true);
|
||||
}
|
||||
}
|
||||
|
||||
function onTimeslotHeadersScroll(context, elem, programGrid) {
|
||||
|
||||
if ((new Date().getTime() - lastGridScroll) >= 1000) {
|
||||
lastHeaderScroll = new Date().getTime();
|
||||
nativeScrollTo(programGrid, elem.scrollLeft, true);
|
||||
}
|
||||
}
|
||||
|
||||
function getFutureDateText(date) {
|
||||
|
||||
var weekday = [];
|
||||
weekday[0] = globalize.translate('core#OptionSundayShort');
|
||||
weekday[1] = globalize.translate('core#OptionMondayShort');
|
||||
weekday[2] = globalize.translate('core#OptionTuesdayShort');
|
||||
weekday[3] = globalize.translate('core#OptionWednesdayShort');
|
||||
weekday[4] = globalize.translate('core#OptionThursdayShort');
|
||||
weekday[5] = globalize.translate('core#OptionFridayShort');
|
||||
weekday[6] = globalize.translate('core#OptionSaturdayShort');
|
||||
|
||||
var day = weekday[date.getDay()];
|
||||
date = date.toLocaleDateString();
|
||||
|
||||
if (date.toLowerCase().indexOf(day.toLowerCase()) == -1) {
|
||||
return day + " " + date;
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
function changeDate(page, date) {
|
||||
|
||||
var newStartDate = normalizeDateToTimeslot(date);
|
||||
currentDate = newStartDate;
|
||||
|
||||
reloadGuide(page, newStartDate);
|
||||
|
||||
var text = getFutureDateText(date);
|
||||
text = '<span class="guideCurrentDay">' + text.replace(' ', ' </span>');
|
||||
page.querySelector('.btnSelectDate').innerHTML = text;
|
||||
}
|
||||
|
||||
var dateOptions = [];
|
||||
|
||||
function setDateRange(page, guideInfo) {
|
||||
|
||||
var today = new Date();
|
||||
today.setHours(today.getHours(), 0, 0, 0);
|
||||
|
||||
var start = datetime.parseISO8601Date(guideInfo.StartDate, { toLocal: true });
|
||||
var end = datetime.parseISO8601Date(guideInfo.EndDate, { toLocal: true });
|
||||
|
||||
start.setHours(0, 0, 0, 0);
|
||||
end.setHours(0, 0, 0, 0);
|
||||
|
||||
if (start.getTime() >= end.getTime()) {
|
||||
end.setDate(start.getDate() + 1);
|
||||
}
|
||||
|
||||
start = new Date(Math.max(today, start));
|
||||
|
||||
dateOptions = [];
|
||||
|
||||
while (start <= end) {
|
||||
|
||||
dateOptions.push({
|
||||
name: getFutureDateText(start),
|
||||
id: start.getTime()
|
||||
});
|
||||
|
||||
start.setDate(start.getDate() + 1);
|
||||
start.setHours(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
var date = new Date();
|
||||
|
||||
if (currentDate) {
|
||||
date.setTime(currentDate.getTime());
|
||||
}
|
||||
|
||||
changeDate(page, date);
|
||||
}
|
||||
|
||||
function reloadPage(page) {
|
||||
|
||||
showLoading();
|
||||
|
||||
var apiClient = connectionManager.currentApiClient();
|
||||
|
||||
apiClient.getLiveTvGuideInfo().then(function (guideInfo) {
|
||||
|
||||
setDateRange(page, guideInfo);
|
||||
});
|
||||
}
|
||||
|
||||
function selectDate(page) {
|
||||
|
||||
require(['actionsheet'], function (actionsheet) {
|
||||
|
||||
actionsheet.show({
|
||||
items: dateOptions,
|
||||
title: globalize.translate('core#HeaderSelectDate'),
|
||||
callback: function (id) {
|
||||
|
||||
var date = new Date();
|
||||
date.setTime(parseInt(id));
|
||||
changeDate(page, date);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function createVerticalScroller(view, pageInstance) {
|
||||
|
||||
if (layoutManager.tv) {
|
||||
scrollHelper.centerFocus.on(view.querySelector('.smoothScrollY'), false);
|
||||
|
||||
var programGrid = view.querySelector('.programGrid');
|
||||
|
||||
scrollHelper.centerFocus.on(programGrid, true);
|
||||
}
|
||||
}
|
||||
|
||||
function parentWithClass(elem, className) {
|
||||
|
||||
while (!elem.classList || !elem.classList.contains(className)) {
|
||||
elem = elem.parentNode;
|
||||
|
||||
if (!elem) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
var selectedMediaInfoTimeout;
|
||||
var focusedElement;
|
||||
function onProgramGridFocus(e) {
|
||||
|
||||
var programCell = parentWithClass(e.target, 'programCell');
|
||||
|
||||
if (!programCell) {
|
||||
return;
|
||||
}
|
||||
|
||||
focusedElement = e.target;
|
||||
if (selectedMediaInfoTimeout) {
|
||||
clearTimeout(selectedMediaInfoTimeout);
|
||||
}
|
||||
selectedMediaInfoTimeout = setTimeout(onSelectedMediaInfoTimeout, 700);
|
||||
}
|
||||
|
||||
function onSelectedMediaInfoTimeout() {
|
||||
var focused = focusedElement
|
||||
if (focused && document.activeElement == focused) {
|
||||
var id = focused.getAttribute('data-id');
|
||||
var item = items[id];
|
||||
|
||||
if (item) {
|
||||
events.trigger(self, 'focus', [
|
||||
{
|
||||
item: item
|
||||
}]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fetch(baseUrl + '/tvguide.template.html', { mode: 'no-cors' }).then(function (response) {
|
||||
return response.text();
|
||||
}).then(function (template) {
|
||||
|
||||
var context = options.element;
|
||||
context.innerHTML = globalize.translateDocument(template, 'core');
|
||||
|
||||
var programGrid = context.querySelector('.programGrid');
|
||||
var timeslotHeaders = context.querySelector('.timeslotHeaders');
|
||||
|
||||
programGrid.addEventListener('focus', onProgramGridFocus, true);
|
||||
programGrid.addEventListener('scroll', function () {
|
||||
|
||||
onProgramGridScroll(context, this, timeslotHeaders);
|
||||
});
|
||||
|
||||
timeslotHeaders.addEventListener('scroll', function () {
|
||||
onTimeslotHeadersScroll(context, this, programGrid);
|
||||
});
|
||||
|
||||
context.querySelector('.btnSelectDate').addEventListener('click', function () {
|
||||
selectDate(context);
|
||||
});
|
||||
|
||||
context.querySelector('.btnUnlockGuide').addEventListener('click', function () {
|
||||
reloadPage(context);
|
||||
});
|
||||
|
||||
context.classList.add('tvguide');
|
||||
|
||||
createVerticalScroller(context, self);
|
||||
itemShortcuts.on(context);
|
||||
|
||||
self.refresh();
|
||||
});
|
||||
};
|
||||
|
||||
Guide.setBaseUrl = function (url) {
|
||||
baseUrl = url;
|
||||
};
|
||||
|
||||
return Guide;
|
||||
});
|
22
dashboard-ui/bower_components/emby-webcomponents/guide/tvguide.template.html
vendored
Normal file
22
dashboard-ui/bower_components/emby-webcomponents/guide/tvguide.template.html
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
<div class="tvGuideHeader">
|
||||
<div class="channelTimeslotHeader">
|
||||
<button class="btnSelectDate clearButton" style="display:block;"></button>
|
||||
</div>
|
||||
<div class="timeslotHeaders smoothScrollX" style="scroll-behavior: auto;"></div>
|
||||
</div>
|
||||
|
||||
<div class="smoothScrollY guideVerticalScroller" style="flex-grow: 1;">
|
||||
|
||||
<div class="programContainer">
|
||||
<div class="channelList"></div>
|
||||
|
||||
<div class="programGridContainer programGrid smoothScrollX" style="white-space: nowrap;">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="guideRequiresUnlock readOnlyContent hide" style="margin:1em auto;text-align:center;padding:1em;flex-shrink:0;">
|
||||
<p class="unlockText"></p>
|
||||
<paper-button raised class="secondary block btnUnlockGuide"><iron-icon icon="check"></iron-icon><span>${ButtonUnlockGuide}</span></paper-button>
|
||||
</div>
|
|
@ -271,17 +271,17 @@ define(['loading', 'viewManager', 'skinManager', 'pluginManager', 'backdrop', 'b
|
|||
var apiClient = connectionManager.currentApiClient();
|
||||
var pathname = ctx.pathname.toLowerCase();
|
||||
|
||||
console.log('Emby.Page - processing path request ' + pathname);
|
||||
console.log('embyRouter - processing path request ' + pathname);
|
||||
|
||||
if ((!apiClient || !apiClient.isLoggedIn()) && !route.anonymous) {
|
||||
console.log('Emby.Page - route does not allow anonymous access, redirecting to login');
|
||||
console.log('embyRouter - route does not allow anonymous access, redirecting to login');
|
||||
beginConnectionWizard();
|
||||
return;
|
||||
}
|
||||
|
||||
if (apiClient && apiClient.isLoggedIn()) {
|
||||
|
||||
console.log('Emby.Page - user is authenticated');
|
||||
console.log('embyRouter - user is authenticated');
|
||||
|
||||
var isCurrentRouteStartup = currentRouteInfo ? currentRouteInfo.route.startup : true;
|
||||
if (ctx.isBack && (route.isDefaultRoute || route.startup) && !isCurrentRouteStartup) {
|
||||
|
@ -289,7 +289,7 @@ define(['loading', 'viewManager', 'skinManager', 'pluginManager', 'backdrop', 'b
|
|||
return;
|
||||
}
|
||||
else if (route.isDefaultRoute) {
|
||||
console.log('Emby.Page - loading skin home page');
|
||||
console.log('embyRouter - loading skin home page');
|
||||
skinManager.loadUserSkin();
|
||||
return;
|
||||
} else if (route.roles) {
|
||||
|
@ -303,7 +303,7 @@ define(['loading', 'viewManager', 'skinManager', 'pluginManager', 'backdrop', 'b
|
|||
}
|
||||
}
|
||||
|
||||
console.log('Emby.Page - proceeding to ' + pathname);
|
||||
console.log('embyRouter - proceeding to ' + pathname);
|
||||
callback();
|
||||
}
|
||||
|
||||
|
@ -484,8 +484,10 @@ define(['loading', 'viewManager', 'skinManager', 'pluginManager', 'backdrop', 'b
|
|||
function showItem(item) {
|
||||
|
||||
if (typeof (item) === 'string') {
|
||||
Emby.Models.item(item).then(showItem);
|
||||
|
||||
require(['connectionManager'], function (connectionManager) {
|
||||
var apiClient = connectionManager.currentApiClient();
|
||||
apiClient.getItem(apiClient.getCurrentUserId(), item).then(showItem);
|
||||
});
|
||||
} else {
|
||||
skinManager.getCurrentSkin().showItem(item);
|
||||
}
|
||||
|
|
168
dashboard-ui/bower_components/emby-webcomponents/shortcuts.js
vendored
Normal file
168
dashboard-ui/bower_components/emby-webcomponents/shortcuts.js
vendored
Normal file
|
@ -0,0 +1,168 @@
|
|||
define(['playbackManager', 'inputManager', 'connectionManager', 'embyRouter'], function (playbackManager, inputManager, connectionManager, embyRouter) {
|
||||
|
||||
function playAllFromHere(card, serverId) {
|
||||
var cards = card.parentNode.querySelectorAll('.itemAction[data-id]');
|
||||
var ids = [];
|
||||
|
||||
var foundCard = false;
|
||||
for (var i = 0, length = cards.length; i < length; i++) {
|
||||
if (cards[i] == card) {
|
||||
foundCard = true;
|
||||
}
|
||||
if (foundCard) {
|
||||
ids.push(cards[i].getAttribute('data-id'));
|
||||
}
|
||||
}
|
||||
playbackManager.play({
|
||||
ids: ids,
|
||||
serverId: serverId
|
||||
});
|
||||
}
|
||||
|
||||
function showSlideshow(startItemId, serverId) {
|
||||
|
||||
var apiClient = connectionManager.getApiClient(serverId);
|
||||
var userId = apiClient.getCurrentUserId();
|
||||
|
||||
return apiClient.getItem(userId, startItemId).then(function (item) {
|
||||
|
||||
return apiClient.getItems(userId, {
|
||||
|
||||
MediaTypes: 'Photo',
|
||||
Filters: 'IsNotFolder',
|
||||
ParentId: item.ParentId
|
||||
|
||||
}).then(function (result) {
|
||||
|
||||
var items = result.Items;
|
||||
|
||||
var index = items.map(function (i) {
|
||||
return i.Id;
|
||||
|
||||
}).indexOf(startItemId);
|
||||
|
||||
if (index == -1) {
|
||||
index = 0;
|
||||
}
|
||||
|
||||
require(['slideshow'], function (slideshow) {
|
||||
|
||||
var newSlideShow = new slideshow({
|
||||
showTitle: false,
|
||||
cover: false,
|
||||
items: items,
|
||||
startIndex: index,
|
||||
interval: 8000,
|
||||
interactive: true
|
||||
});
|
||||
|
||||
newSlideShow.show();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function showItem(options) {
|
||||
|
||||
if (options.Type == 'Photo') {
|
||||
|
||||
showSlideshow(options.Id, options.ServerId);
|
||||
return;
|
||||
}
|
||||
|
||||
embyRouter.showItem(options);
|
||||
}
|
||||
|
||||
function executeAction(card, action) {
|
||||
var id = card.getAttribute('data-id');
|
||||
var serverId = card.getAttribute('data-serverid');
|
||||
var type = card.getAttribute('data-type');
|
||||
var isfolder = card.getAttribute('data-isfolder') == 'true';
|
||||
|
||||
if (action == 'link') {
|
||||
showItem({
|
||||
Id: id,
|
||||
Type: type,
|
||||
IsFolder: isfolder,
|
||||
ServerId: serverId
|
||||
});
|
||||
}
|
||||
|
||||
else if (action == 'instantmix') {
|
||||
playbackManager.instantMix(id, serverId);
|
||||
}
|
||||
|
||||
else if (action == 'play') {
|
||||
|
||||
var startPositionTicks = parseInt(card.getAttribute('data-startpositionticks') || '0');
|
||||
|
||||
playbackManager.play({
|
||||
ids: [id],
|
||||
startPositionTicks: startPositionTicks,
|
||||
serverId: serverId
|
||||
});
|
||||
}
|
||||
|
||||
else if (action == 'playallfromhere') {
|
||||
playAllFromHere(card, serverId);
|
||||
}
|
||||
|
||||
else if (action == 'setplaylistindex') {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function onClick(e) {
|
||||
var card = parentWithClass(e.target, 'itemAction');
|
||||
|
||||
if (card) {
|
||||
var action = card.getAttribute('data-action');
|
||||
|
||||
if (action) {
|
||||
executeAction(card, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parentWithClass(elem, className) {
|
||||
|
||||
while (!elem.classList || !elem.classList.contains(className)) {
|
||||
elem = elem.parentNode;
|
||||
|
||||
if (!elem) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
function onCommand(e) {
|
||||
var cmd = e.detail.command;
|
||||
|
||||
if (cmd == 'play') {
|
||||
var card = parentWithClass(e.target, 'itemAction');
|
||||
|
||||
if (card) {
|
||||
executeAction(card, cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function on(context) {
|
||||
context.addEventListener('click', onClick);
|
||||
inputManager.on(context, onCommand);
|
||||
}
|
||||
|
||||
function off(context) {
|
||||
context.removeEventListener('click', onClick);
|
||||
inputManager.off(context, onCommand);
|
||||
}
|
||||
|
||||
return {
|
||||
on: on,
|
||||
off: off
|
||||
};
|
||||
|
||||
});
|
|
@ -32,14 +32,14 @@
|
|||
"web-component-tester": "^4.0.0",
|
||||
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
|
||||
},
|
||||
"homepage": "https://github.com/PolymerElements/iron-icon",
|
||||
"homepage": "https://github.com/polymerelements/iron-icon",
|
||||
"_release": "1.0.8",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v1.0.8",
|
||||
"commit": "f36b38928849ef3853db727faa8c9ef104d611eb"
|
||||
},
|
||||
"_source": "git://github.com/PolymerElements/iron-icon.git",
|
||||
"_source": "git://github.com/polymerelements/iron-icon.git",
|
||||
"_target": "^1.0.0",
|
||||
"_originalSource": "PolymerElements/iron-icon"
|
||||
"_originalSource": "polymerelements/iron-icon"
|
||||
}
|
|
@ -36,7 +36,7 @@
|
|||
"tag": "v1.4.0",
|
||||
"commit": "554f7418fdbd97688eb21518b5f8172167d53a95"
|
||||
},
|
||||
"_source": "git://github.com/polymerelements/iron-selector.git",
|
||||
"_source": "git://github.com/PolymerElements/iron-selector.git",
|
||||
"_target": "^1.0.0",
|
||||
"_originalSource": "polymerelements/iron-selector"
|
||||
"_originalSource": "PolymerElements/iron-selector"
|
||||
}
|
|
@ -1,519 +0,0 @@
|
|||
define(['jQuery', 'livetvcss', 'scripts/livetvcomponents', 'scrollStyles'], function ($) {
|
||||
|
||||
return function (options) {
|
||||
|
||||
var self = this;
|
||||
|
||||
self.refresh = function () {
|
||||
reloadPage(options.element);
|
||||
};
|
||||
|
||||
// 30 mins
|
||||
var cellCurationMinutes = 30;
|
||||
var cellDurationMs = cellCurationMinutes * 60 * 1000;
|
||||
var msPerDay = 86400000;
|
||||
|
||||
var currentDate;
|
||||
|
||||
var defaultChannels = browserInfo.mobile ? 50 : 100;
|
||||
var channelLimit = 1000;
|
||||
|
||||
var channelQuery = {
|
||||
|
||||
StartIndex: 0,
|
||||
Limit: defaultChannels,
|
||||
EnableFavoriteSorting: true
|
||||
};
|
||||
|
||||
var channelsPromise;
|
||||
|
||||
function normalizeDateToTimeslot(date) {
|
||||
|
||||
var minutesOffset = date.getMinutes() - cellCurationMinutes;
|
||||
|
||||
if (minutesOffset >= 0) {
|
||||
|
||||
date.setHours(date.getHours(), cellCurationMinutes, 0, 0);
|
||||
|
||||
} else {
|
||||
|
||||
date.setHours(date.getHours(), 0, 0, 0);
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
function reloadChannels(page) {
|
||||
channelsPromise = null;
|
||||
reloadGuide(page);
|
||||
}
|
||||
|
||||
function reloadGuide(page) {
|
||||
|
||||
Dashboard.showLoadingMsg();
|
||||
|
||||
channelQuery.UserId = Dashboard.getCurrentUserId();
|
||||
|
||||
channelQuery.Limit = Math.min(channelQuery.Limit || defaultChannels, channelLimit);
|
||||
channelQuery.AddCurrentProgram = false;
|
||||
|
||||
channelsPromise = channelsPromise || ApiClient.getLiveTvChannels(channelQuery);
|
||||
|
||||
var date = currentDate;
|
||||
// Add one second to avoid getting programs that are just ending
|
||||
date = new Date(date.getTime() + 1000);
|
||||
|
||||
// Subtract to avoid getting programs that are starting when the grid ends
|
||||
var nextDay = new Date(date.getTime() + msPerDay - 2000);
|
||||
|
||||
console.log(nextDay);
|
||||
channelsPromise.then(function (channelsResult) {
|
||||
|
||||
ApiClient.getLiveTvPrograms({
|
||||
UserId: Dashboard.getCurrentUserId(),
|
||||
MaxStartDate: nextDay.toISOString(),
|
||||
MinEndDate: date.toISOString(),
|
||||
channelIds: channelsResult.Items.map(function (c) {
|
||||
return c.Id;
|
||||
}).join(','),
|
||||
ImageTypeLimit: 1,
|
||||
EnableImages: false,
|
||||
SortBy: "StartDate"
|
||||
|
||||
}).then(function (programsResult) {
|
||||
|
||||
renderGuide(page, date, channelsResult.Items, programsResult.Items);
|
||||
|
||||
Dashboard.hideLoadingMsg();
|
||||
|
||||
LibraryBrowser.setLastRefreshed(page);
|
||||
|
||||
});
|
||||
|
||||
if (options.enablePaging !== false) {
|
||||
var channelPagingHtml = LibraryBrowser.getQueryPagingHtml({
|
||||
startIndex: channelQuery.StartIndex,
|
||||
limit: channelQuery.Limit,
|
||||
totalRecordCount: channelsResult.TotalRecordCount,
|
||||
updatePageSizeSetting: false,
|
||||
showLimit: true
|
||||
});
|
||||
|
||||
var channelPaging = page.querySelector('.channelPaging');
|
||||
channelPaging.innerHTML = channelPagingHtml;
|
||||
$(channelPaging);
|
||||
}
|
||||
|
||||
page.querySelector('.btnNextPage').addEventListener('click', function () {
|
||||
channelQuery.StartIndex += channelQuery.Limit;
|
||||
reloadChannels(page);
|
||||
});
|
||||
|
||||
page.querySelector('.btnPreviousPage').addEventListener('click', function () {
|
||||
channelQuery.StartIndex -= channelQuery.Limit;
|
||||
reloadChannels(page);
|
||||
});
|
||||
|
||||
page.querySelector('#selectPageSize').addEventListener('change', function () {
|
||||
channelQuery.Limit = parseInt(this.value);
|
||||
channelQuery.StartIndex = 0;
|
||||
reloadChannels(page);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getTimeslotHeadersHtml(startDate, endDateTime) {
|
||||
|
||||
var html = '';
|
||||
|
||||
// clone
|
||||
startDate = new Date(startDate.getTime());
|
||||
|
||||
html += '<div class="timeslotHeadersInner">';
|
||||
|
||||
while (startDate.getTime() < endDateTime) {
|
||||
|
||||
html += '<div class="timeslotHeader">';
|
||||
html += '<div class="timeslotHeaderInner">';
|
||||
|
||||
html += LibraryBrowser.getDisplayTime(startDate);
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
|
||||
// Add 30 mins
|
||||
startDate.setTime(startDate.getTime() + cellDurationMs);
|
||||
}
|
||||
html += '</div>';
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function parseDates(program) {
|
||||
|
||||
if (!program.StartDateLocal) {
|
||||
try {
|
||||
|
||||
program.StartDateLocal = parseISO8601Date(program.StartDate, { toLocal: true });
|
||||
|
||||
} catch (err) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!program.EndDateLocal) {
|
||||
try {
|
||||
|
||||
program.EndDateLocal = parseISO8601Date(program.EndDate, { toLocal: true });
|
||||
|
||||
} catch (err) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getChannelProgramsHtml(page, date, channel, programs) {
|
||||
|
||||
var html = '';
|
||||
|
||||
var startMs = date.getTime();
|
||||
var endMs = startMs + msPerDay - 1;
|
||||
|
||||
programs = programs.filter(function (curr) {
|
||||
return curr.ChannelId == channel.Id;
|
||||
});
|
||||
|
||||
html += '<div class="channelPrograms">';
|
||||
|
||||
for (var i = 0, length = programs.length; i < length; i++) {
|
||||
|
||||
var program = programs[i];
|
||||
|
||||
if (program.ChannelId != channel.Id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
parseDates(program);
|
||||
|
||||
if (program.EndDateLocal.getTime() < startMs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (program.StartDateLocal.getTime() > endMs) {
|
||||
break;
|
||||
}
|
||||
|
||||
var renderStartMs = Math.max(program.StartDateLocal.getTime(), startMs);
|
||||
var startPercent = (program.StartDateLocal.getTime() - startMs) / msPerDay;
|
||||
startPercent *= 100;
|
||||
startPercent = Math.max(startPercent, 0);
|
||||
|
||||
var renderEndMs = Math.min(program.EndDateLocal.getTime(), endMs);
|
||||
var endPercent = (renderEndMs - renderStartMs) / msPerDay;
|
||||
endPercent *= 100;
|
||||
|
||||
var cssClass = "programCell";
|
||||
var addAccent = true;
|
||||
|
||||
if (program.IsKids) {
|
||||
cssClass += " childProgramInfo";
|
||||
} else if (program.IsSports) {
|
||||
cssClass += " sportsProgramInfo";
|
||||
} else if (program.IsNews) {
|
||||
cssClass += " newsProgramInfo";
|
||||
} else if (program.IsMovie) {
|
||||
cssClass += " movieProgramInfo";
|
||||
}
|
||||
else {
|
||||
cssClass += " plainProgramInfo";
|
||||
addAccent = false;
|
||||
}
|
||||
|
||||
html += '<a href="itemdetails.html?id=' + program.Id + '" class="' + cssClass + '" data-programid="' + program.Id + '" style="left:' + startPercent + '%;width:' + endPercent + '%;">';
|
||||
|
||||
html += '<div class="guideProgramName">';
|
||||
html += program.Name;
|
||||
html += '</div>';
|
||||
|
||||
html += '<div class="guideProgramTime">';
|
||||
if (program.IsLive) {
|
||||
html += '<span class="liveTvProgram">' + Globalize.translate('LabelLiveProgram') + ' </span>';
|
||||
}
|
||||
else if (program.IsPremiere) {
|
||||
html += '<span class="premiereTvProgram">' + Globalize.translate('LabelPremiereProgram') + ' </span>';
|
||||
}
|
||||
else if (program.IsSeries && !program.IsRepeat) {
|
||||
html += '<span class="newTvProgram">' + Globalize.translate('LabelNewProgram') + ' </span>';
|
||||
}
|
||||
|
||||
html += LibraryBrowser.getDisplayTime(program.StartDateLocal);
|
||||
html += ' - ';
|
||||
html += LibraryBrowser.getDisplayTime(program.EndDateLocal);
|
||||
|
||||
if (program.SeriesTimerId) {
|
||||
html += '<div class="timerCircle seriesTimerCircle"></div>';
|
||||
html += '<div class="timerCircle seriesTimerCircle"></div>';
|
||||
html += '<div class="timerCircle seriesTimerCircle"></div>';
|
||||
}
|
||||
else if (program.TimerId) {
|
||||
|
||||
html += '<div class="timerCircle"></div>';
|
||||
}
|
||||
html += '</div>';
|
||||
|
||||
if (addAccent) {
|
||||
html += '<div class="programAccent"></div>';
|
||||
}
|
||||
|
||||
html += '</a>';
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderPrograms(page, date, channels, programs) {
|
||||
|
||||
var html = [];
|
||||
|
||||
for (var i = 0, length = channels.length; i < length; i++) {
|
||||
|
||||
html.push(getChannelProgramsHtml(page, date, channels[i], programs));
|
||||
}
|
||||
|
||||
var programGrid = page.querySelector('.programGrid');
|
||||
programGrid.innerHTML = html.join('');
|
||||
|
||||
$(programGrid).scrollTop(0).scrollLeft(0);
|
||||
}
|
||||
|
||||
function renderChannelHeaders(page, channels) {
|
||||
|
||||
var html = '';
|
||||
|
||||
for (var i = 0, length = channels.length; i < length; i++) {
|
||||
|
||||
var channel = channels[i];
|
||||
|
||||
html += '<div class="channelHeaderCellContainer">';
|
||||
|
||||
html += '<a class="channelHeaderCell" href="itemdetails.html?id=' + channel.Id + '">';
|
||||
|
||||
var hasChannelImage = channel.ImageTags.Primary;
|
||||
var cssClass = hasChannelImage ? 'guideChannelInfo guideChannelInfoWithImage' : 'guideChannelInfo';
|
||||
|
||||
html += '<div class="' + cssClass + '">' + channel.Number + '</div>';
|
||||
|
||||
if (hasChannelImage) {
|
||||
|
||||
var url = ApiClient.getScaledImageUrl(channel.Id, {
|
||||
maxHeight: 44,
|
||||
maxWidth: 70,
|
||||
tag: channel.ImageTags.Primary,
|
||||
type: "Primary"
|
||||
});
|
||||
|
||||
html += '<div class="guideChannelImage lazy" data-src="' + url + '"></div>';
|
||||
} else {
|
||||
html += '<div class="guideChannelName">' + channel.Name + '</div>';
|
||||
}
|
||||
|
||||
html += '</a>';
|
||||
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
var channelList = page.querySelector('.channelList');
|
||||
channelList.innerHTML = html;
|
||||
ImageLoader.lazyChildren(channelList);
|
||||
}
|
||||
|
||||
function renderGuide(page, date, channels, programs) {
|
||||
|
||||
renderChannelHeaders(page, channels);
|
||||
|
||||
var startDate = date;
|
||||
var endDate = new Date(startDate.getTime() + msPerDay);
|
||||
page.querySelector('.timeslotHeaders').innerHTML = getTimeslotHeadersHtml(startDate, endDate);
|
||||
renderPrograms(page, date, channels, programs);
|
||||
}
|
||||
|
||||
var gridScrolling = false;
|
||||
var headersScrolling = false;
|
||||
function onProgramGridScroll(page, elem) {
|
||||
|
||||
if (!headersScrolling) {
|
||||
gridScrolling = true;
|
||||
|
||||
$(page.querySelector('.timeslotHeaders')).scrollLeft($(elem).scrollLeft());
|
||||
gridScrolling = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onTimeslotHeadersScroll(page, elem) {
|
||||
|
||||
if (!gridScrolling) {
|
||||
headersScrolling = true;
|
||||
$(page.querySelector('.programGrid')).scrollLeft($(elem).scrollLeft());
|
||||
headersScrolling = false;
|
||||
}
|
||||
}
|
||||
|
||||
function changeDate(page, date) {
|
||||
|
||||
currentDate = normalizeDateToTimeslot(date);
|
||||
|
||||
reloadGuide(page);
|
||||
|
||||
var text = LibraryBrowser.getFutureDateText(date);
|
||||
text = '<span class="currentDay">' + text.replace(' ', ' </span>');
|
||||
page.querySelector('.currentDate').innerHTML = text;
|
||||
}
|
||||
|
||||
var dateOptions = [];
|
||||
|
||||
function setDateRange(page, guideInfo) {
|
||||
|
||||
var today = new Date();
|
||||
today.setHours(today.getHours(), 0, 0, 0);
|
||||
|
||||
var start = parseISO8601Date(guideInfo.StartDate, { toLocal: true });
|
||||
var end = parseISO8601Date(guideInfo.EndDate, { toLocal: true });
|
||||
|
||||
start.setHours(0, 0, 0, 0);
|
||||
end.setHours(0, 0, 0, 0);
|
||||
|
||||
if (start.getTime() >= end.getTime()) {
|
||||
end.setDate(start.getDate() + 1);
|
||||
}
|
||||
|
||||
start = new Date(Math.max(today, start));
|
||||
|
||||
dateOptions = [];
|
||||
|
||||
while (start <= end) {
|
||||
|
||||
dateOptions.push({
|
||||
name: LibraryBrowser.getFutureDateText(start),
|
||||
id: start.getTime(),
|
||||
ironIcon: 'today'
|
||||
});
|
||||
|
||||
start.setDate(start.getDate() + 1);
|
||||
start.setHours(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
var date = new Date();
|
||||
|
||||
if (currentDate) {
|
||||
date.setTime(currentDate.getTime());
|
||||
}
|
||||
|
||||
changeDate(page, date);
|
||||
}
|
||||
|
||||
function reloadPageAfterValidation(page, limit) {
|
||||
|
||||
channelLimit = limit;
|
||||
|
||||
ApiClient.getLiveTvGuideInfo().then(function (guideInfo) {
|
||||
|
||||
setDateRange(page, guideInfo);
|
||||
});
|
||||
}
|
||||
|
||||
function reloadPage(page) {
|
||||
|
||||
$('.guideRequiresUnlock', page).hide();
|
||||
|
||||
RegistrationServices.validateFeature('livetv').then(function () {
|
||||
Dashboard.showLoadingMsg();
|
||||
|
||||
reloadPageAfterValidation(page, 1000);
|
||||
}, function () {
|
||||
|
||||
Dashboard.showLoadingMsg();
|
||||
|
||||
var limit = 5;
|
||||
$('.guideRequiresUnlock', page).show();
|
||||
$('.unlockText', page).html(Globalize.translate('MessageLiveTvGuideRequiresUnlock', limit));
|
||||
|
||||
reloadPageAfterValidation(page, limit);
|
||||
});
|
||||
}
|
||||
|
||||
function selectDate(page) {
|
||||
|
||||
require(['actionsheet'], function (actionsheet) {
|
||||
|
||||
actionsheet.show({
|
||||
items: dateOptions,
|
||||
showCancel: true,
|
||||
title: Globalize.translate('HeaderSelectDate'),
|
||||
callback: function (id) {
|
||||
|
||||
var date = new Date();
|
||||
date.setTime(parseInt(id));
|
||||
changeDate(page, date);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', 'components/tvguide/tvguide.template.html', true);
|
||||
|
||||
xhr.onload = function (e) {
|
||||
|
||||
var template = this.response;
|
||||
var tabContent = options.element;
|
||||
tabContent.innerHTML = Globalize.translateDocument(template);
|
||||
|
||||
tabContent.querySelector('.programGrid').addEventListener('scroll', function (e) {
|
||||
|
||||
onProgramGridScroll(tabContent, e.target);
|
||||
});
|
||||
|
||||
if (browserInfo.mobile) {
|
||||
tabContent.querySelector('.tvGuide').classList.add('mobileGuide');
|
||||
} else {
|
||||
|
||||
tabContent.querySelector('.tvGuide').classList.remove('mobileGuide');
|
||||
|
||||
tabContent.querySelector('.timeslotHeaders').addEventListener('scroll', function (e) {
|
||||
|
||||
onTimeslotHeadersScroll(tabContent, e.target);
|
||||
});
|
||||
}
|
||||
|
||||
if (AppInfo.enableHeadRoom && options.enableHeadRoom) {
|
||||
requirejs(["headroom"], function () {
|
||||
|
||||
// construct an instance of Headroom, passing the element
|
||||
var headroom = new Headroom(tabContent.querySelector('.tvGuideHeader'));
|
||||
// initialise
|
||||
headroom.init();
|
||||
});
|
||||
}
|
||||
|
||||
$('.btnUnlockGuide', tabContent).on('click', function () {
|
||||
|
||||
reloadPage(tabContent);
|
||||
});
|
||||
|
||||
$('.btnSelectDate', tabContent).on('click', function () {
|
||||
|
||||
selectDate(tabContent);
|
||||
});
|
||||
|
||||
self.refresh();
|
||||
}
|
||||
|
||||
xhr.send();
|
||||
};
|
||||
});
|
|
@ -1,19 +0,0 @@
|
|||
<div class="tvGuide">
|
||||
<div class="tvGuideHeader">
|
||||
<div class="channelTimeslotHeader">
|
||||
<div class="timeslotHeaderInner">
|
||||
<a class="btnSelectDate" href="#" style="display:block;padding-left:5px;color:#fff;text-decoration:none;font-weight:normal!important;padding:5px 10px 4px 0;font-size:13px;"><iron-icon icon="schedule" style="margin-right:3px;width:18px;height:18px;"></iron-icon><span class="currentDate" style="vertical-align: middle;"></span></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeslotHeaders smoothScrollX"></div>
|
||||
</div>
|
||||
<div class="programContainer">
|
||||
<div class="channelList"></div>
|
||||
<div class="programGrid smoothScrollX"></div>
|
||||
</div>
|
||||
<div class="channelPaging"></div>
|
||||
</div>
|
||||
<div class="guideRequiresUnlock readOnlyContent" style="margin:1em auto;text-align:center;display:none;padding:1em;">
|
||||
<p class="unlockText"></p>
|
||||
<paper-button raised class="secondary block btnUnlockGuide"><iron-icon icon="check"></iron-icon><span>${ButtonUnlockGuide}</span></paper-button>
|
||||
</div>
|
|
@ -13,7 +13,8 @@
|
|||
.absolutePageTabContent {
|
||||
margin: 0 !important;
|
||||
width: 100% !important;
|
||||
top: 98px !important;
|
||||
/* Page needs to supply padding */
|
||||
top: 92px !important;
|
||||
}
|
||||
|
||||
.sidebarDivider {
|
||||
|
|
|
@ -1,421 +1,12 @@
|
|||
.guideTabContent {
|
||||
background: #161616;
|
||||
}
|
||||
|
||||
.tvGuide {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.tvGuideHeader {
|
||||
white-space: nowrap;
|
||||
position: fixed;
|
||||
top: 95px;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.neon-animating .tvGuideHeader {
|
||||
position: static;
|
||||
}
|
||||
|
||||
.tvGuideHeader.headroom--unpinned {
|
||||
-webkit-transform: translateY(-98px);
|
||||
transform: translateY(-98px);
|
||||
}
|
||||
|
||||
.tvProgramSectionHeader {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tvProgram {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
font-weight: normal !important;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.detailSectionHeader + .tvProgram {
|
||||
border-top: 6px solid #161616;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.tvProgramTimeSlot {
|
||||
width: 80px;
|
||||
vertical-align: middle;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
border-bottom: 6px solid #161616;
|
||||
}
|
||||
|
||||
.tvProgramTimeSlotInner {
|
||||
padding: .5em;
|
||||
}
|
||||
|
||||
.tvProgramInfo {
|
||||
vertical-align: middle;
|
||||
padding: .5em .5em;
|
||||
border-bottom: 6px solid #161616;
|
||||
}
|
||||
|
||||
.tvProgramTimeSlot + .tvProgramInfo {
|
||||
margin-left: 80px;
|
||||
border-left: 6px solid #161616;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tvProgramCurrentTimeSlot {
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
.tvProgramName {
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
.tvProgramTime {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.newTvProgram {
|
||||
color: #E88606;
|
||||
}
|
||||
|
||||
.liveTvProgram {
|
||||
color: #64A239;
|
||||
}
|
||||
|
||||
.premiereTvProgram {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
.programAccent {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
.sportsProgramInfo .programAccent {
|
||||
background-color: #0A7C33;
|
||||
}
|
||||
|
||||
.newsProgramInfo .programAccent {
|
||||
background-color: #523378;
|
||||
}
|
||||
|
||||
.movieProgramInfo .programAccent {
|
||||
background-color: #A43913;
|
||||
}
|
||||
|
||||
.childProgramInfo .programAccent {
|
||||
background-color: #0B487D;
|
||||
}
|
||||
|
||||
.timerCircle {
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: #cc0000;
|
||||
border: 1px solid #cc3333;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.seriesTimerCircle {
|
||||
position: relative;
|
||||
margin-left: 0;
|
||||
left: 21px;
|
||||
opacity: .3;
|
||||
}
|
||||
|
||||
.itemMiscInfo .seriesTimerCircle:first-child {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.seriesTimerCircle + .seriesTimerCircle {
|
||||
opacity: .65;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.seriesTimerCircle + .seriesTimerCircle + .seriesTimerCircle {
|
||||
opacity: 1;
|
||||
left: -21px;
|
||||
}
|
||||
|
||||
.channelImageContainer .itemDetailImage {
|
||||
max-width: 200px;
|
||||
max-height: 110px;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.channelTimeslotHeader {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.timeslotHeaders {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mobileGuide .timeslotHeaders {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.programContainer {
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
margin-top: 44px;
|
||||
}
|
||||
|
||||
.mobileGuide .programContainer {
|
||||
margin-top: 38px;
|
||||
}
|
||||
|
||||
.channelPrograms {
|
||||
white-space: nowrap;
|
||||
height: 61px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.channelPrograms, .timeslotHeadersInner {
|
||||
width: 12000px;
|
||||
}
|
||||
|
||||
.timeslotHeader {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.timeslotHeaderInner {
|
||||
padding: .6em .35em .5em;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.channelTimeslotHeader .timeslotHeaderInner {
|
||||
padding-top: .4em;
|
||||
}
|
||||
|
||||
.channelHeaderCell, .channelTimeslotHeader {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
border-right: 1px solid #161616;
|
||||
width: 199px;
|
||||
background: #303030;
|
||||
}
|
||||
|
||||
.channelHeaderCell {
|
||||
border-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.channelHeaderCellContainer + .channelHeaderCellContainer .channelHeaderCell {
|
||||
border-top: 6px solid #161616;
|
||||
}
|
||||
|
||||
.channelTimeslotHeader {
|
||||
border-right-color: #52B54B;
|
||||
}
|
||||
|
||||
.channelTimeslotHeader, .timeslotHeader {
|
||||
background: #52B54B;
|
||||
}
|
||||
|
||||
.timeslotHeader, .channelTimeslotHeader {
|
||||
height: 42px;
|
||||
}
|
||||
|
||||
.channelHeaderCell {
|
||||
padding: 0;
|
||||
text-decoration: none;
|
||||
font-weight: 400 !important;
|
||||
color: #fff !important;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.pointerInput .channelHeaderCell:hover {
|
||||
background-color: #444;
|
||||
}
|
||||
|
||||
.channelList {
|
||||
float: left;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.programGrid {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.programGrid, .timeslotHeaders {
|
||||
margin-left: 200px;
|
||||
}
|
||||
|
||||
.timeslotHeader {
|
||||
width: 2.0833333333333333333333333333333%;
|
||||
}
|
||||
|
||||
.programCell {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 6px;
|
||||
border-left: 6px solid #161616;
|
||||
background-color: #212121;
|
||||
display: block;
|
||||
color: #fff !important;
|
||||
text-decoration: none;
|
||||
font-weight: 400 !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.timeslotCellInner {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
font-weight: normal !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.channelHeaderCell {
|
||||
height: 55px;
|
||||
}
|
||||
|
||||
.guideProgramName {
|
||||
padding: .5em .5em .35em;
|
||||
}
|
||||
|
||||
.guideProgramTime {
|
||||
padding: 0 .5em .35em;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.guideChannelInfo {
|
||||
display: inline-block;
|
||||
max-width: 110px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
margin-left: .5em;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.guideChannelImage {
|
||||
width: 70px;
|
||||
height: 44px;
|
||||
background-size: contain;
|
||||
background-position: right center;
|
||||
background-repeat: no-repeat;
|
||||
margin-right: .5em;
|
||||
}
|
||||
|
||||
.guideChannelName {
|
||||
margin-right: .5em;
|
||||
max-width: 50%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@media (max-width: 1920px) {
|
||||
|
||||
.channelPrograms, .timeslotHeadersInner {
|
||||
width: 10000px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1600px) {
|
||||
|
||||
.channelPrograms, .timeslotHeadersInner {
|
||||
width: 8000px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
|
||||
.guideChannelInfoWithImage {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.timerPageImageContainer {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.channelHeaderCell, .channelTimeslotHeader {
|
||||
width: 98px;
|
||||
}
|
||||
|
||||
.programGrid, .timeslotHeaders {
|
||||
margin-left: 99px;
|
||||
}
|
||||
|
||||
.currentDay {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.channelPrograms, .timeslotHeadersInner {
|
||||
width: 6500px;
|
||||
}
|
||||
|
||||
.timeslotHeaderInner {
|
||||
padding-top: .8em;
|
||||
}
|
||||
|
||||
.channelTimeslotHeader a {
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
.channelTimeslotHeader i {
|
||||
margin-right: 4px !important;
|
||||
}
|
||||
|
||||
.channelTimeslotHeader .timeslotHeaderInner {
|
||||
padding-top: .5em;
|
||||
}
|
||||
}
|
||||
|
||||
.channelList, .programGrid {
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.itemOverlayContent {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
@media (min-width: 800px) {
|
||||
|
||||
.itemOverlayContent {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.channelList, .timeslotHeaderInner {
|
||||
font-size: 110%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
|
||||
.itemOverlayContent {
|
||||
width: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
|
||||
.itemOverlayContent {
|
||||
width: 500px;
|
||||
}
|
||||
background: #121212;
|
||||
}
|
||||
|
||||
.chkAdvanced {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* Allow vertical space for anything that slides up at the bottom */
|
||||
.guideVerticalScroller {
|
||||
padding-bottom: 160px;
|
||||
}
|
||||
|
|
|
@ -300,6 +300,10 @@ body {
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
body.autoScrollY {
|
||||
overflow-y: auto !important;
|
||||
}
|
||||
|
||||
/* Without this, no content will be displayed in mobile safari */
|
||||
.pageContainer {
|
||||
overflow-x: visible !important;
|
||||
|
|
|
@ -106,14 +106,6 @@
|
|||
color: #52B54B !important;
|
||||
}
|
||||
|
||||
.channelTimeslotHeader {
|
||||
border-right-color: #52B54B !important;
|
||||
}
|
||||
|
||||
.channelTimeslotHeader, .timeslotHeader {
|
||||
background: #52B54B !important;
|
||||
}
|
||||
|
||||
.libraryViewNav, paper-tabs {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
@ -126,18 +118,6 @@ h1, h1 a {
|
|||
font-weight: 400;
|
||||
}
|
||||
|
||||
.channelList {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.channelHeaderCellInner {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.tvGuideHeader {
|
||||
top: 86px !important;
|
||||
}
|
||||
|
||||
.libraryMenuButtonText {
|
||||
font-weight: 500 !important;
|
||||
position: absolute;
|
||||
|
@ -188,18 +168,6 @@ div.cardBox {
|
|||
background: #1c1c1c;
|
||||
}
|
||||
|
||||
@media (min-width: 600px) {
|
||||
|
||||
/* This needs a little extra space to account for the longer date format */
|
||||
.channelHeaderCell, .channelTimeslotHeader {
|
||||
width: 209px;
|
||||
}
|
||||
|
||||
.programGrid, .timeslotHeaders {
|
||||
margin-left: 210px;
|
||||
}
|
||||
}
|
||||
|
||||
.bottomFab {
|
||||
bottom: 120px !important;
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
<br />
|
||||
</div>
|
||||
</div>
|
||||
<div class="pageTabContent guideTabContent hide" style="width: auto;" data-index="1">
|
||||
<div class="pageTabContent absolutePageTabContent guideTabContent hide" style="width: auto; display: flex;padding-bottom: 0;" data-index="1">
|
||||
</div>
|
||||
<div class="pageTabContent channelsTabContent hide" data-index="2">
|
||||
<div class="viewSettings">
|
||||
|
|
|
@ -3877,13 +3877,13 @@
|
|||
if (item.IsSeries && !item.IsRepeat) {
|
||||
|
||||
require(['livetvcss']);
|
||||
miscInfo.push('<span class="newTvProgram">' + Globalize.translate('LabelNewProgram') + '</span>');
|
||||
miscInfo.push('<span class="newTvProgram">' + Globalize.translate('AttributeNew') + '</span>');
|
||||
|
||||
}
|
||||
|
||||
if (item.IsLive) {
|
||||
|
||||
miscInfo.push('<span class="liveTvProgram">' + Globalize.translate('LabelLiveProgram') + '</span>');
|
||||
miscInfo.push('<span class="liveTvProgram">' + Globalize.translate('AttributeLive') + '</span>');
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -59,13 +59,13 @@
|
|||
html += '<div class="tvProgramTime">';
|
||||
|
||||
if (program.IsLive) {
|
||||
html += '<span class="liveTvProgram">' + Globalize.translate('LabelLiveProgram') + ' </span>';
|
||||
html += '<span class="liveTvProgram">' + Globalize.translate('AttributeLive') + ' </span>';
|
||||
}
|
||||
else if (program.IsPremiere) {
|
||||
html += '<span class="premiereTvProgram">' + Globalize.translate('LabelPremiereProgram') + ' </span>';
|
||||
html += '<span class="premiereTvProgram">' + Globalize.translate('AttributePremiere') + ' </span>';
|
||||
}
|
||||
else if (program.IsSeries && !program.IsRepeat) {
|
||||
html += '<span class="newTvProgram">' + Globalize.translate('LabelNewProgram') + ' </span>';
|
||||
html += '<span class="newTvProgram">' + Globalize.translate('AttributeNew') + ' </span>';
|
||||
}
|
||||
|
||||
var minutes = program.RunTimeTicks / 600000000;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
define([], function () {
|
||||
define(['tvguide', 'embyRouter'], function (tvguide, embyRouter) {
|
||||
|
||||
window.LiveTvPage.initGuideTab = function (page, tabContent) {
|
||||
|
||||
|
@ -11,12 +11,9 @@
|
|||
page.guideInstance.refresh();
|
||||
}
|
||||
} else {
|
||||
require(['tvguide'], function (tvguide) {
|
||||
|
||||
page.guideInstance = new tvguide({
|
||||
element: tabContent,
|
||||
enableHeadRoom: true
|
||||
});
|
||||
page.guideInstance = new tvguide({
|
||||
element: tabContent,
|
||||
enableHeadRoom: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -207,6 +207,22 @@
|
|||
|
||||
});
|
||||
|
||||
pageIdOn('viewshow', "liveTvSuggestedPage", function () {
|
||||
|
||||
var page = this;
|
||||
|
||||
// Needed on the guide tab
|
||||
// Ideally this should be moved to the guide tab on show/hide
|
||||
document.body.classList.add('autoScrollY');
|
||||
});
|
||||
|
||||
pageIdOn('viewbeforehide', "liveTvSuggestedPage", function () {
|
||||
|
||||
var page = this;
|
||||
|
||||
document.body.classList.remove('autoScrollY');
|
||||
});
|
||||
|
||||
window.LiveTvPage = {
|
||||
renderSuggestedTab: renderSuggestedTab,
|
||||
initSuggestedTab: initSuggestedTab
|
||||
|
|
|
@ -297,4 +297,5 @@
|
|||
}
|
||||
};
|
||||
|
||||
return window.RegistrationServices;
|
||||
});
|
|
@ -1481,7 +1481,7 @@ var AppInfo = {};
|
|||
console.log('loaded ApiClient singleton');
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1605,7 +1605,6 @@ var AppInfo = {};
|
|||
|
||||
var paths = {
|
||||
velocity: bowerPath + "/velocity/velocity.min",
|
||||
tvguide: 'components/tvguide/tvguide',
|
||||
ironCardList: 'components/ironcardlist/ironcardlist',
|
||||
scrollThreshold: 'components/scrollthreshold',
|
||||
directorybrowser: 'components/directorybrowser/directorybrowser',
|
||||
|
@ -1635,7 +1634,9 @@ var AppInfo = {};
|
|||
layoutManager: embyWebComponentsBowerPath + "/layoutmanager",
|
||||
pageJs: embyWebComponentsBowerPath + '/page.js/page',
|
||||
focusManager: embyWebComponentsBowerPath + "/focusmanager",
|
||||
datetime: embyWebComponentsBowerPath + "/datetime",
|
||||
globalize: embyWebComponentsBowerPath + "/globalize",
|
||||
itemShortcuts: embyWebComponentsBowerPath + "/shortcuts",
|
||||
imageLoader: embyWebComponentsBowerPath + "/images/imagehelper"
|
||||
};
|
||||
|
||||
|
@ -1667,6 +1668,11 @@ var AppInfo = {};
|
|||
define("backdrop", [embyWebComponentsBowerPath + "/backdrop/backdrop"], returnFirstDependency);
|
||||
define("fetchHelper", [embyWebComponentsBowerPath + "/fetchhelper"], returnFirstDependency);
|
||||
|
||||
define("tvguide", [embyWebComponentsBowerPath + "/guide/guide", 'embyRouter'], function (tvGuide, embyRouter) {
|
||||
tvGuide.setBaseUrl(embyRouter.baseUrl() + '/bower_components/emby-webcomponents/guide');
|
||||
return tvGuide;
|
||||
});
|
||||
|
||||
define("viewManager", [embyWebComponentsBowerPath + "/viewmanager"], function (viewManager) {
|
||||
viewManager.dispatchPageEvents(true);
|
||||
return viewManager;
|
||||
|
@ -1786,12 +1792,13 @@ var AppInfo = {};
|
|||
define('webcomponentsjs', [bowerPath + '/webcomponentsjs/webcomponents-lite.min.js']);
|
||||
define('native-promise-only', [bowerPath + '/native-promise-only/lib/npo.src']);
|
||||
define("fingerprintjs2", [bowerPath + '/fingerprintjs2/fingerprint2'], returnFirstDependency);
|
||||
define("clearButtonStyle", ['css!' + embyWebComponentsBowerPath + '/clearbutton']);
|
||||
|
||||
if (Dashboard.isRunningInCordova()) {
|
||||
define('registrationservices', ['cordova/registrationservices']);
|
||||
define('registrationservices', ['cordova/registrationservices'], returnFirstDependency);
|
||||
|
||||
} else {
|
||||
define('registrationservices', ['scripts/registrationservices']);
|
||||
define('registrationservices', ['scripts/registrationservices'], returnFirstDependency);
|
||||
}
|
||||
|
||||
if (Dashboard.isRunningInCordova()) {
|
||||
|
@ -1866,6 +1873,12 @@ var AppInfo = {};
|
|||
};
|
||||
});
|
||||
|
||||
// mock this for now. not used in this app
|
||||
define("playbackManager", [], function () {
|
||||
return {
|
||||
};
|
||||
});
|
||||
|
||||
// mock this for now. not used in this app
|
||||
define("pluginManager", [], function () {
|
||||
return {
|
||||
|
@ -1884,7 +1897,7 @@ var AppInfo = {};
|
|||
|
||||
define('dialogText', ['globalize'], getDialogText());
|
||||
|
||||
define("router", [embyWebComponentsBowerPath + '/router'], function (embyRouter) {
|
||||
define("embyRouter", [embyWebComponentsBowerPath + '/router'], function (embyRouter) {
|
||||
|
||||
embyRouter.showLocalLogin = function (apiClient, serverId, manualLogin) {
|
||||
Dashboard.navigate('login.html?serverid=' + serverId);
|
||||
|
@ -1907,6 +1920,17 @@ var AppInfo = {};
|
|||
Dashboard.navigate('mypreferencesmenu.html?userId=' + ApiClient.getCurrentUserId());
|
||||
};
|
||||
|
||||
embyRouter.showItem = function (item) {
|
||||
if (typeof (item) === 'string') {
|
||||
require(['connectionManager'], function (connectionManager) {
|
||||
var apiClient = connectionManager.currentApiClient();
|
||||
apiClient.getItem(apiClient.getCurrentUserId(), item).then(showItem);
|
||||
});
|
||||
} else {
|
||||
Dashboard.navigate(LibraryBrowser.getHref(item));
|
||||
}
|
||||
};
|
||||
|
||||
return embyRouter;
|
||||
});
|
||||
}
|
||||
|
@ -2873,7 +2897,7 @@ var AppInfo = {};
|
|||
var deps = [];
|
||||
|
||||
deps.push('imageLoader');
|
||||
deps.push('router');
|
||||
deps.push('embyRouter');
|
||||
deps.push('layoutManager');
|
||||
|
||||
if (!(AppInfo.isNativeApp && browserInfo.android)) {
|
||||
|
|
|
@ -266,6 +266,13 @@
|
|||
"OptionContinuing": "Continuing",
|
||||
"OptionEnded": "Ended",
|
||||
"HeaderAirDays": "Air Days",
|
||||
"OptionSundayShort": "Sun",
|
||||
"OptionMondayShort": "Mon",
|
||||
"OptionTuesdayShort": "Tue",
|
||||
"OptionWednesdayShort": "Wed",
|
||||
"OptionThursdayShort": "Thu",
|
||||
"OptionFridayShort": "Fri",
|
||||
"OptionSaturdayShort": "Sat",
|
||||
"OptionSunday": "Sunday",
|
||||
"OptionMonday": "Monday",
|
||||
"OptionTuesday": "Tuesday",
|
||||
|
@ -1845,9 +1852,9 @@
|
|||
"OptionProductionLocations": "Production Locations",
|
||||
"OptionBirthLocation": "Birth Location",
|
||||
"LabelAllChannels": "All channels",
|
||||
"LabelLiveProgram": "LIVE",
|
||||
"LabelNewProgram": "NEW",
|
||||
"LabelPremiereProgram": "PREMIERE",
|
||||
"AttributeNew": "New",
|
||||
"AttributePremiere": "Premiere",
|
||||
"AttributeLive": "Live",
|
||||
"LabelHDProgram": "HD",
|
||||
"HeaderChangeFolderType": "Change Content Type",
|
||||
"HeaderChangeFolderTypeHelp": "To change the type, please remove and rebuild the library with the new type.",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue