diff --git a/www/css/channelGuide.css b/www/css/channelGuide.css index 531e4b5..f30075c 100644 --- a/www/css/channelGuide.css +++ b/www/css/channelGuide.css @@ -47,7 +47,7 @@ along with this program. If not, see .*/ } .channel-guide-schedule-row{ - height: 2.2em; + height: 3.4em; } .channel-guide-channel-list-entry { @@ -67,15 +67,20 @@ along with this program. If not, see .*/ .channel-guide-schedule-track { display: flex; + position: relative; } .channel-guide-schedule-entry { display: flex; justify-content: center; align-items: center; + overflow: hidden; + position: absolute; } .channel-guide-schedule-entry h3 { margin: 0; + text-overflow: ellipsis; + overflow: hidden; text-wrap: nowrap; } \ No newline at end of file diff --git a/www/css/theme/the-network-classic.css b/www/css/theme/the-network-classic.css index 5d760f6..2ab84d6 100644 --- a/www/css/theme/the-network-classic.css +++ b/www/css/theme/the-network-classic.css @@ -393,6 +393,20 @@ input.control-prompt, input.control-prompt:focus{ border-bottom: 1px solid var(--accent0); } +.channel-guide-schedule-entry{ + padding: 0 0.7em; + background-color: var(--bg1); + border-left: 1px solid var(--accent0); +} + +.channel-guide-schedule-entry.starts-before{ + border-left: none; +} + +.channel-guide-schedule-entry.last-ends-before{ + border-right: 1px solid var(--accent0); +} + /* channel settings */ .channel-info-label{ color: var(--accent1-alt0); diff --git a/www/js/channelGuide.js b/www/js/channelGuide.js index b2fe286..cc9039e 100644 --- a/www/js/channelGuide.js +++ b/www/js/channelGuide.js @@ -19,6 +19,9 @@ class ChannelGuide{ //define property to hold refresh timer this.refreshTimer; + //define property to hold schedule cache + this.cache; + //Grab DOM Nodes this.scheduleIndex = document.querySelector('#channel-guide-schedule-index'); this.channelList = document.querySelector('#channel-guide-channel-list'); @@ -28,14 +31,17 @@ class ChannelGuide{ this.refreshList(); } - async refreshList(){ - //Fetch latest channel list and render it to the schedule page - this.renderList(await utils.ajax.getChannels()); + async refreshList(now = new Date()){ + //Freshen up the local schedule cache + this.cache = await utils.ajax.getChannels(); + + //render the freshly pulled schedule data to the page + this.renderList(this.cache, now); } renderList(schedule, now = new Date()){ this.renderIndex(now); - this.renderChannelList(schedule, now); + this.renderChannelList(schedule); this.renderSchedule(schedule, now); //if we've got a job to refresh the list @@ -70,7 +76,7 @@ class ChannelGuide{ } } - renderChannelList(schedule, now = new Date()){ + renderChannelList(schedule){ //Clean out channel list this.channelList.innerHTML = ''; @@ -96,27 +102,24 @@ class ChannelGuide{ //Clear out the schedule this.scheduledMediaDiv.innerHTML = ''; + schedule = this.fillDeadAir(schedule, now); + //Iterate through all of the received channels for(const channel of schedule){ + //Create a div to hold all of the current channels entries const channelTrack = document.createElement('div'); - channelTrack.classList.add('channel-guide-schedule-track'); + channelTrack.classList.add('channel-guide-schedule-track', 'channel-guide-schedule-row'); //For every scheduled video - for(const entry of channel.schedule){ - //Create div for current schedule entry - const entryDiv = document.createElement('div'); - entryDiv.classList.add('channel-guide-schedule-entry', 'channel-guide-schedule-row'); - - //Throw down an entry label - const entryLabel = document.createElement('h3'); - entryLabel.textContent = utils.unescapeEntities(entry.title); - - //Assemble the entry - entryDiv.appendChild(entryLabel); + for(const entryIndex in channel.schedule){ + //Get entry by index + const entry = channel.schedule[entryIndex]; + //Check if this is the last item + const last = entryIndex == channel.schedule.length - 1; //slot entry into schedule track - channelTrack.appendChild(entryDiv); + channelTrack.appendChild(this.renderScheduleItem(entry, now, last)); } //Inject channel track into schedule div @@ -124,7 +127,74 @@ class ChannelGuide{ } } - renderScheduleItem(item){ + + fillDeadAir(schedule){ + //For each video in the schedule + for(const video of schedule){ + + } + + //Return processed schedule + return schedule; + } + + renderScheduleItem(item, now = new Date(), last = false){ + //Create div for current schedule entry + const entryDiv = document.createElement('div'); + entryDiv.classList.add('channel-guide-schedule-entry', 'channel-guide-schedule-row'); + + //Throw down an entry label + const entryLabel = document.createElement('h3'); + entryLabel.textContent = utils.unescapeEntities(item.title); + + //calculate end epoch + const end = item.startTime + (item.duration * 1000); + + //Place entryDiv by scheduled time + entryDiv.style.left = `${this.offsetByEpoch(item.startTime, now)}px`; + entryDiv.style.right = `${this.offsetByEpoch(end, now, true)}px`; + + //if this started before the schedule time range + if(item.startTime < now.getTime()){ + //mark it for css + entryDiv.classList.add('starts-before'); + } + + //if this is the last item and it ends before the schedule time range + if(last && end < now.getTime() + 5400000){ + //mark it for css + entryDiv.classList.add('last-ends-before'); + } + + //Assemble the entry + entryDiv.appendChild(entryLabel); + + //Return assembled entry div + return entryDiv; + } + + offsetByEpoch(scheduleEpoch, now = new Date(), rightOffset = false){ + //Calculate how many milliseconds after the current time our scheduled item is + const ms = scheduleEpoch - now.getTime(); + + //Calculate and return offset by milliseconds + return this.offsetByMilliseconds(ms, rightOffset, rightOffset); + } + + offsetByMilliseconds(ms, rightOffset = false){ + //Calculate place in schedule as float between 0 and 1 by deviding input by schedules range (90 minutes) + //Making sure to reverse value if rightOffset is thrown + const timeFloat = rightOffset ? 1 - ms / 5400000 : ms / 5400000; + + //Get maximum position relative to the range itself + const offsetMax = this.scheduledMediaDiv.getBoundingClientRect().width; + + //Calculate offset by timefloat and offset max + const offset = (offsetMax * timeFloat) < 0 ? 0 : (offsetMax * timeFloat); + + //return position relative to parent, adding channel list width to left side to handle offset + return offset; + } }