<div id="site" data-namespace="">
<ul class="o-podcast-track-list u-list-clean js-track-list"></ul>
<div class="o-podcast-player o-podcast-player--hidden js-podcast" data-rss="https://internetpodden.libsyn.com/rss">
<div class="wrapper">
<div class="row justify-content-between align-items-center">
<img src="https://ssl-static.libsyn.com/p/assets/a/9/6/5/a9653e010511f97e/internetpodden-logo.png" class="o-podcast-player__player__image js-image" alt="false">
<div class="o-podcast-player__player">
<div class="o-podcast-player__player__show-info">
<h4 class="o-podcast-player__title color-ruby js-title"></h4>
<div class="display-flex align-items-center">
<time class="o-podcast-player__playtime js-duration"></time>
<span class="o-podcast-player__spacer">-</span>
<div class="o-podcast-player__description js-description"></div>
</div>
</div>
<div class="o-podcast-player__controls">
<button type="button" class="o-podcast-player__button js-step-backward" title="Back 15 seconds">
<span class="u-visuallyhidden">Back 15 seconds</span>
<svg class="icon o-podcast-player__rewind-icon">
<use xlink:href="#icon-backward-15"></use>
</svg>
</button>
<button type="button" class="o-podcast-player__button o-podcast-player__button--play js-play-button" title="Play/Pause">
<span class="u-visuallyhidden">Play/Pause</span>
<svg class="icon o-podcast-player__play-icon js-play-icon">
<use xlink:href="#icon-play"></use>
</svg>
<svg class="icon o-podcast-player__play-icon js-pause-icon is-hidden">
<use xlink:href="#icon-pause"></use>
</svg>
</button>
<button type="button" class="o-podcast-player__button js-step-forward" title="Forward 60 seconds">
<span class="u-visuallyhidden">Forward 60 seconds</span>
<svg class="icon o-podcast-player__rewind-icon">
<use xlink:href="#icon-forward-60"></use>
</svg>
</button>
<time class="o-podcast-player__timeleft u-visibility-hidden js-timeleft"></time>
<audio controls id="podcastPlayer" class="u-visuallyhidden"></audio>
</div>
</div>
</div>
</div>
<button class="a-button a-button--standalone-icon a-button--icon o-podcast-player__close js-close-player">
<span class="a-button__text">Sträng</span>
<svg class=" icon a-button__icon">
<use xlink:href="#icon-close"></use>
</svg>
</button>
<div class="o-podcast-player__progressbar">
<div class="o-podcast-player__progressbar__progress background-ruby js-progress"></div>
</div>
</div>
</div>
<div id="site" data-namespace="">
{{#if playlist}}
<ul class="o-podcast-track-list u-list-clean js-track-list"></ul>
{{/if}}
<div class="o-podcast-player{{#if hidden}} o-podcast-player--hidden{{/if}} js-podcast" data-rss="https://internetpodden.libsyn.com/rss">
<div class="wrapper">
<div class="row justify-content-between align-items-center">
<img src="{{episodeImage}}" class="o-podcast-player__player__image js-image" alt="{{title}}">
<div class="o-podcast-player__player">
<div class="o-podcast-player__player__show-info">
<h4 class="o-podcast-player__title color-ruby js-title">{{#if title}}{{title}}{{/if}}</h4>
<div class="display-flex align-items-center">
<time class="o-podcast-player__playtime js-duration">{{#if duration}}{{duration}}{{/if}}</time>
<span class="o-podcast-player__spacer">-</span>
<div class="o-podcast-player__description js-description">{{#if description}}{{description}}{{/if}}</div>
</div>
</div>
<div class="o-podcast-player__controls">
<button type="button" class="o-podcast-player__button js-step-backward" title="Back 15 seconds">
<span class="u-visuallyhidden" >Back 15 seconds</span>
{{>@ icon id="backward-15" additional_classes="o-podcast-player__rewind-icon"}}
</button>
<button type="button" class="o-podcast-player__button o-podcast-player__button--play js-play-button" title="Play/Pause">
<span class="u-visuallyhidden">Play/Pause</span>
{{>@ icon id="play" additional_classes="o-podcast-player__play-icon js-play-icon"}}
{{>@ icon id="pause" additional_classes="o-podcast-player__play-icon js-pause-icon is-hidden"}}
</button>
<button type="button" class="o-podcast-player__button js-step-forward" title="Forward 60 seconds">
<span class="u-visuallyhidden" >Forward 60 seconds</span>
{{>@ icon id="forward-60" additional_classes="o-podcast-player__rewind-icon"}}
</button>
<time class="o-podcast-player__timeleft {{#unless duration}}u-visibility-hidden{{/unless}} js-timeleft">{{#if duration}}{{duration}}{{/if}}</time>
<audio controls id="podcastPlayer" class="u-visuallyhidden" {{#if src}}src="{{src}}"{{/if}}></audio>
</div>
</div>
</div>
</div>
<button class="a-button a-button--standalone-icon a-button--icon o-podcast-player__close js-close-player">
<span class="a-button__text">Sträng</span>
<svg class=" icon a-button__icon">
<use xlink:href="#icon-close"></use>
</svg>
</button>
<div class="o-podcast-player__progressbar">
<div class="o-podcast-player__progressbar__progress background-ruby js-progress"></div>
</div>
</div>
</div>
{
"playlist": true,
"hidden": true,
"src": false,
"image": false,
"title": false,
"description": false,
"duration": false,
"has_modifier": false,
"is_single_episode": false,
"date": "15 december, 2020",
"episodeImage": "https://ssl-static.libsyn.com/p/assets/a/9/6/5/a9653e010511f97e/internetpodden-logo.png"
}
@charset "UTF-8";
@use '../../configurations/mixins' as mixin;
@use '../../configurations/bem' as bem;
@use '../../configurations/config' as config;
@use '../../configurations/variables' as var;
@use '../../configurations/functions' as func;
@use '../../configurations/colors/colors' as colors;
@use '../../configurations/colors/colors-functions' as colorFunc;
@use '../../vendor/grid/breakpoints' as breakpoint;
@use '../../vendor/grid/grid' as grid;
@use '../../vendor/baseline/plumber' as plumber;
@include mixin.organism(podcast-episodes) {
// Episode
@include bem.e(episode) {
justify-content: center;
margin-right: 0;
margin-left: 0;
padding: func.rhythm(2);
border-radius: var.$border-radius;
background-color: colors.$color-snow;
@include breakpoint.bp-up(sm) {
padding-right: func.rhythm(2);
padding-left: func.rhythm(2);
}
@include breakpoint.bp-up(md) {
justify-content: flex-start;
}
@include breakpoint.bp-up(md) {
padding: func.rhythm(3);
}
@include bem.m(list) {
margin-bottom: func.rhythm(2);
@include bem.e(image) {
display: none;
@include breakpoint.bp-up(md) {
display: block;
width: func.to_rem(170px);
height: func.to_rem(170px);
}
}
}
@include bem.m(hero) {
padding-right: 0;
padding-left: 0;
border-radius: 0;
background-color: transparent;
@include breakpoint.bp-up(md) {
flex-direction: row-reverse;
align-items: center;
}
@include bem.e(image) {
width: func.to_rem(310px);
height: func.to_rem(310px);
margin-right: 0;
margin-left: func.rhythm(4);
}
@include bem.e(info) {
> p {
overflow: visible;
text-overflow: inherit;
white-space: normal;
}
}
@include bem.e(link) {
pointer-events: none;
}
}
@include bem.e(image) {
margin-bottom: func.rhythm(2);
border-radius: var.$border-radius;
@include breakpoint.bp-down(sm) {
display: none;
}
@include breakpoint.bp-up(sm) {
display: block;
width: func.to_rem(255px);
height: func.to_rem(255px);
margin-right: func.rhythm(3);
}
}
@include bem.e(meta) {
display: flex;
margin-bottom: func.rhythm(1);
}
@include bem.e(info) {
h1 span {
pointer-events: none;
}
h1 svg {
width: var.$icon-size-large * 1.5;
height: var.$icon-size-large * 1.5;
}
h2 svg {
width: var.$icon-size-large * 1.2;
height: var.$icon-size-large * 1.2;
}
> p {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@include breakpoint.bp-up(sm-xs) {
overflow: visible;
text-overflow: inherit;
white-space: normal;
}
}
}
@include bem.e(link) {
color: colors.$color-cyberspace;
text-decoration: none;
&:hover,
&:focus {
text-decoration: underline;
}
}
@include bem.e(button) {
display: flex;
align-items: center;
margin: 0 func.rhythm(1);
padding: 0;
border: 0;
background-color: transparent;
&:hover {
> svg {
fill: colors.$color-ruby;
}
}
* {
pointer-events: none;
}
}
@include bem.e(share) {
display: flex;
flex-wrap: wrap;
align-items: center;
margin-top: func.rhythm(1);
@include bem.e(spacer) {
display: none;
margin-bottom: func.rhythm(2);
@include breakpoint.bp-up(lg) {
display: block;
margin-right: func.rhythm(2);
margin-bottom: 0;
margin-left: func.rhythm(2);
line-height: 1;
}
}
@include bem.e(list) {
display: flex;
flex-basis: 100%;
> dt {
margin-right: func.rhythm(2);
}
> dd {
margin-left: 0;
}
@include breakpoint.bp-up(lg) {
flex-basis: auto;
}
}
}
@include bem.e(listen-link) {
margin-right: func.rhythm(2);
color: colors.$color-cyberspace;
}
@include bem.e(download-link) {
display: flex;
flex-basis: 100%;
flex-grow: 1;
margin-bottom: func.rhythm(2);
color: colors.$color-cyberspace;
> span {
font-family: var.$font-family-base;
font-size: var.$size-base;
text-decoration: underline;
}
> svg {
transform: translateY(3px);
}
@include breakpoint.bp-up(lg) {
margin-bottom: 0;
}
@include breakpoint.bp-up(md) {
flex-basis: auto;
flex-grow: 0;
}
}
@include bem.e(download-icon) {
width: var.$icon-size;
height: var.$icon-size;
margin-right: func.rhythm(1);
padding: 3px;
border-radius: var.$border-radius;
}
}
}
// Track list (Prototype)
@include mixin.organism(podcast-track-list) {
display: flex;
flex-direction: column;
> li {
display: flex;
padding: func.rhythm(1) func.rhythm(2);
+ li {
border-top: 1px solid colors.$color-concrete;
}
> button {
display: flex;
align-items: center;
}
}
}
@charset "UTF-8";
@use "sass:color";
@use "sass:math";
@use '../../configurations/mixins' as mixin;
@use '../../configurations/bem' as bem;
@use '../../configurations/config' as config;
@use '../../configurations/variables' as var;
@use '../../configurations/functions' as func;
@use '../../configurations/colors/colors' as colors;
@use '../../configurations/colors/colors-functions' as colorFunc;
@use '../../vendor/grid/breakpoints' as breakpoint;
@use '../../vendor/grid/grid' as grid;
@use '../../vendor/baseline/plumber' as plumber;
@include mixin.organism(podcast-player) {
/* Player */
$time-left-width: func.to_rem(65px);
position: fixed;
z-index: func.z_index(middlegroundImportant);
right: 0;
bottom: 0;
left: 0;
padding: func.rhythm(1) 0 0 0;
transform: translateY(0);
transition: transform 0.25s ease-out;
background-color: colors.$color-snow;
box-shadow: 0 -#{func.rhythm(1)} func.rhythm(3) color.adjust(colors.$color-cyberspace, $lightness: 70%);
@include bem.m(hidden) {
transform: translateY(100%);
box-shadow: none;
}
@include bem.e(player) {
display: flex;
flex-grow: 1;
align-items: center;
@include bem.e(show-info) {
display: none;
@include breakpoint.bp-up(sm) {
display: block;
}
}
@include bem.e(image) {
display: none;
@include breakpoint.bp-up(md) {
display: flex;
flex-grow: 0;
width: func.rhythm(7);
height: func.rhythm(7);
margin-right: func.rhythm(2);
border-radius: var.$border-radius;
}
}
}
@include bem.e(show-info) {
display: flex;
flex-direction: column;
}
@include bem.e(title) {
margin-bottom: 0;
color: colors.$color-ruby;
font-family: var.$font-family-headings;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: func.to_rem(200px);
@include breakpoint.bp-up(sm) {
max-width: func.to_rem(250px);
}
@include breakpoint.bp-up(lg) {
max-width: func.to_rem(500px);
}
}
@include bem.e(description) {
display: block;
max-width: func.to_rem(200px);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
> p {
display: none;
}
@include breakpoint.bp-up(sm) {
max-width: func.to_rem(250px);
}
@include breakpoint.bp-up(lg) {
max-width: func.to_rem(500px);
}
}
@include bem.e(playtime) {
@include breakpoint.bp-between(sm, md) {
display: none;
}
}
@include bem.e(spacer) {
@include breakpoint.bp-between(sm, md) {
display: none;
}
}
@include bem.e(controls) {
display: flex;
flex-grow: 1;
align-items: center;
justify-content: center;
}
@include bem.e(spacer) {
margin-right: func.rhythm(0.5);
margin-left: func.rhythm(0.5);
}
@include bem.e(button) {
display: flex;
align-items: center;
margin: 0 func.rhythm(1);
padding: 0;
border: 0;
background-color: transparent;
&:hover {
> svg {
fill: colors.$color-ruby;
}
}
}
@include bem.e(timeleft) {
min-width: $time-left-width;
}
@include bem.e(rewind-icon) {
width: var.$icon-size-large * 1.2;
height: var.$icon-size-large * 1.2;
}
@include bem.e(forward-icon) {
width: var.$icon-size-large * 1.2;
height: var.$icon-size-large * 1.2;
}
@include bem.e(play-icon) {
display: block;
width: var.$icon-size-large * 1.5;
height: var.$icon-size-large * 1.5;
}
@include bem.e(progressbar) {
flex-wrap: wrap;
width: 100%;
height: func.rhythm(0.5);
margin-top: func.rhythm(1);
background-color: colors.$color-concrete;
@include bem.e(progress) {
width: 0;
height: func.rhythm(1);
}
}
@include bem.e(close) {
position: absolute;
right: func.rhythm(0.5);
top: 50%;
transform: translateY(-50%);
@include breakpoint.bp-up(xxl) {
right: func.rhythm(2);
}
}
}
let namespace;
const namespaceElement = document.querySelector('#site');
const podCast = document.querySelector('.js-podcast');
const audio = document.getElementById('podcastPlayer');
const jsTrackList = document.querySelector('.js-track-list');
const title = document.querySelector('.js-title');
const description = document.querySelector('.js-description');
const image = document.querySelector('.js-image');
const progress = document.querySelector('.js-progress');
const durationElement = document.querySelector('.js-duration');
const timeleftElement = document.querySelector('.js-timeleft');
const stepForward = document.querySelector('.js-step-forward');
const stepBackward = document.querySelector('.js-step-backward');
const playButton = document.querySelector('.js-play-button');
const playIcon = document.querySelector('.js-play-icon');
const pauseIcon = document.querySelector('.js-pause-icon');
const closeButton = document.querySelector('.js-close-player');
let rssURL = '';
if (podCast) {
rssURL = podCast.dataset.rss;
}
if (!namespaceElement) {
namespace = '';
} else {
namespace = namespaceElement.dataset.namespace;
}
function timeupdate() {
audio.addEventListener('timeupdate', () => {
const duration = parseInt(audio.duration, 10);
const currentTime = parseInt(audio.currentTime, 10);
const timeLeft = duration - currentTime;
let s; let
m;
s = timeLeft % 60;
m = Math.floor(timeLeft / 60) % 60;
s = s < 10 ? `0${s}` : s;
m = m < 10 ? `0${m}` : m;
if (timeLeft > 0) {
timeleftElement.classList.remove('u-visibility-hidden');
timeleftElement.innerHTML = `${m}:${s}`;
}
}, false);
}
let html = '';
function getItems(el) {
html += `
<li>
<button
class="${namespace}o-podcast-player__button display-flex js-play-episode"
data-src="${el.querySelector('enclosure').getAttribute('url')}"
data-title="${el.querySelector('title').innerHTML}"
data-description="${el.querySelector('description').innerHTML.replace(/(<([^>]+)>)/gi, '').replace('<![CDATA[', '').replace(']]>', '')}"
data-image="${el.querySelector('image').getAttribute('href')}"
data-duration="${el.querySelector('duration').innerHTML}"
type="button"><svg class="icon ${namespace}o-podcast-player__play-icon u-m-r-2"><use xlink:href="#icon-play"></use></svg></div><div class="u-visuallyhidden">Spela avsnittet ${el.querySelector('title').innerHTML}</div></button>
<div class="${namespace}o-podcast-player__show-info">
<div class="${namespace}o-podcast-player__title">${el.querySelector('title').innerHTML}</div>
<div class="${namespace}o-podcast-player__description js-description">${el.querySelector('description').innerHTML}</div>
</div>
</li>
`;
if (jsTrackList) {
jsTrackList.innerHTML = html;
}
}
if (rssURL) {
fetch(rssURL)
.then((response) => response.text())
.then((str) => new window.DOMParser().parseFromString(str, 'text/xml'))
.then((data) => {
const items = data.querySelectorAll('item');
items.forEach(getItems);
});
}
function playEpisode(playBtn) {
audio.src = playBtn.dataset.src;
durationElement.innerHTML = playBtn.dataset.duration;
title.innerHTML = playBtn.dataset.title;
description.innerHTML = playBtn.dataset.description;
image.src = playBtn.dataset.image;
podCast.classList.remove(`${namespace}o-podcast-player--hidden`);
timeleftElement.classList.add('u-visibility-hidden');
if (audio.play) {
audio.pause();
pauseIcon.classList.remove('is-hidden');
playIcon.classList.add('is-hidden');
audio.currentTime = 0;
}
audio.play();
timeupdate();
setTimeout(() => {
timeleftElement.classList.remove('u-visibility-hidden');
}, 1000);
}
document.body.addEventListener('click', (e) => {
const playBtn = e.target.closest('.js-play-episode');
if (playBtn) {
e.preventDefault();
// Clear old episodedata
audio.currentTime = 0;
timeupdate();
audio.pause();
sessionStorage.removeItem('episodeData');
// Play new episode
playEpisode(playBtn);
}
});
if (playButton) {
playButton.addEventListener('click', () => {
if (audio.paused) {
audio.muted = false;
audio.play();
pauseIcon.classList.remove('is-hidden');
playIcon.classList.add('is-hidden');
timeleftElement.classList.add('u-visibility-hidden');
timeupdate();
timeleftElement.classList.remove('u-visibility-hidden');
} else {
audio.pause();
pauseIcon.classList.add('is-hidden');
playIcon.classList.remove('is-hidden');
}
});
}
if (audio) {
audio.onended = () => {
pauseIcon.classList.add('is-hidden');
playIcon.classList.remove('is-hidden');
timeleftElement.classList.add('u-visibility-hidden');
};
audio.ontimeupdate = () => {
const timer = `${(audio.currentTime / audio.duration) * 100}%`;
progress.style.width = timer;
};
}
if (stepForward) {
stepForward.addEventListener('click', () => {
audio.currentTime += 60;
timeupdate();
});
}
if (stepBackward) {
stepBackward.addEventListener('click', () => {
audio.currentTime -= 15;
timeupdate();
});
}
function saveState() {
const podcastData = {
podCastTitle: title.innerHTML,
episodeDescription: description.innerHTML,
episodeSrc: audio.src,
episodeCurrentTime: audio.currentTime,
episodeDuration: durationElement.innerHTML,
episodeImage: image.src,
};
sessionStorage.setItem('episodeData', JSON.stringify(podcastData));
if (!audio.paused) {
let existing = sessionStorage.getItem('episodeData');
existing = existing ? JSON.parse(existing) : {};
existing.podcastWasPlaying = true;
sessionStorage.setItem('episodeData', JSON.stringify(existing));
} else {
let existing = sessionStorage.getItem('episodeData');
existing = existing ? JSON.parse(existing) : {};
existing.podcastWasPlaying = false;
sessionStorage.setItem('episodeData', JSON.stringify(existing));
}
}
// Handle continous play when user leaves the page
if (podCast) {
window.addEventListener('visibilitychange', saveState);
window.addEventListener('beforeunload', saveState);
}
if (sessionStorage.getItem('episodeData') && podCast) {
const arr = JSON.parse(sessionStorage.getItem('episodeData'));
if (arr.episodeCurrentTime) {
podCast.classList.remove(`${namespace}o-podcast-player--hidden`);
audio.src = arr.episodeSrc;
image.src = arr.episodeImage;
title.innerHTML = arr.podCastTitle;
description.innerHTML = arr.episodeDescription;
durationElement.innerHTML = arr.episodeDuration;
if (arr.podcastWasPlaying === true) {
const playPromise = audio.play();
if (playPromise !== undefined) {
playPromise.then(() => {
// Continue playing audio on reload
audio.currentTime = arr.episodeCurrentTime;
timeupdate();
audio.play();
pauseIcon.classList.remove('is-hidden');
playIcon.classList.add('is-hidden');
}).catch(() => {
// User reloaded page manually. Cannot play audio
audio.addEventListener('loadedmetadata', () => {
audio.currentTime = arr.episodeCurrentTime;
timeupdate();
});
pauseIcon.classList.add('is-hidden');
playIcon.classList.remove('is-hidden');
audio.pause();
});
}
} else {
audio.addEventListener('loadedmetadata', () => {
audio.currentTime = arr.episodeCurrentTime;
timeupdate();
});
pauseIcon.classList.add('is-hidden');
playIcon.classList.remove('is-hidden');
audio.pause();
}
}
}
if (closeButton) {
closeButton.addEventListener('click', () => {
audio.currentTime = 0;
timeupdate();
audio.pause();
sessionStorage.removeItem('episodeData');
podCast.classList.add(`${namespace}o-podcast-player--hidden`);
});
}
No notes defined.