<div id="site" data-namespace="">

    <div class="o-podcast-player 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="Svenskarna och internet 2020">
                <div class="o-podcast-player__player">
                    <div class="o-podcast-player__player__show-info">
                        <h4 class="o-podcast-player__title color-ruby js-title">Svenskarna och internet 2020</h4>
                        <div class="display-flex align-items-center">
                            <time class="o-podcast-player__playtime js-duration">53:04</time>
                            <span class="o-podcast-player__spacer">-</span>
                            <div class="o-podcast-player__description js-description">Årets högtidsstund för alla som älskar internetstatistik är här! Vår rapport Svenskarna och internet har utökats inför ett speciellt år med extra frågor kring Coronapandemin. Jannike Tillå, Måns Jonasson och Jenny Andersson från Internetstiftelsen diskuterar insikterna från årets rapport.</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  js-timeleft">53:04</time>
                        <audio controls id="podcastPlayer" class="u-visuallyhidden" src="https://traffic.libsyn.com/secure/internetpodden/20201215-svenskarna-och-internet-2020.mp3?dest-id&#x3D;1534313"></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": false,
  "hidden": false,
  "src": "https://traffic.libsyn.com/secure/internetpodden/20201215-svenskarna-och-internet-2020.mp3?dest-id=1534313",
  "image": true,
  "title": "Svenskarna och internet 2020",
  "description": "Årets högtidsstund för alla som älskar internetstatistik är här! Vår rapport Svenskarna och internet har utökats inför ett speciellt år med extra frågor kring Coronapandemin. Jannike Tillå, Måns Jonasson och Jenny Andersson från Internetstiftelsen diskuterar insikterna från årets rapport.",
  "duration": "53:04",
  "has_modifier": false,
  "is_single_episode": true,
  "date": "15 december, 2020",
  "episodeImage": "https://ssl-static.libsyn.com/p/assets/a/9/6/5/a9653e010511f97e/internetpodden-logo.png"
}
  • Content:
    @charset "UTF-8";
    
    @include organism(podcast-episodes) {
    
    	// Episode
    	@include e(episode) {
    		justify-content: center;
    		margin-right: 0;
    		margin-left: 0;
    		padding: rhythm(2);
    		border-radius: $border-radius;
    		background-color: $color-snow;
    
    		@include bp-up(sm) {
    			padding-right: rhythm(2);
    			padding-left: rhythm(2);
    		}
    
    		@include bp-up(md) {
    			justify-content: flex-start;
    		}
    
    		@include bp-up(md) {
    			padding: rhythm(3);
    		}
    
    		@include m(list) {
    			margin-bottom: rhythm(2);
    
    			@include e(image) {
    				display: none;
    
    				@include bp-up(md) {
    					display: block;
    					width: rem(170px);
    					height: rem(170px);
    				}
    			}
    		}
    
    		@include m(hero) {
    			padding-right: 0;
    			padding-left: 0;
    			border-radius: 0;
    			background-color: transparent;
    
    			@include bp-up(md) {
    				flex-direction: row-reverse;
    				align-items: center;
    			}
    
    			@include e(image) {
    				width: rem(310px);
    				height: rem(310px);
    				margin-right: 0;
    				margin-left: rhythm(4);
    			}
    
    			@include e(info) {
    				> p {
    					overflow: visible;
    					text-overflow: inherit;
    					white-space: normal;
    				}
    			}
    
    			@include e(link) {
    				pointer-events: none;
    			}
    		}
    
    		@include e(image) {
    			margin-bottom: rhythm(2);
    			border-radius: $border-radius;
    
    			@include bp-down(sm) {
    				display: none;
    			}
    
    			@include bp-up(sm) {
    				display: block;
    				width: rem(255px);
    				height: rem(255px);
    				margin-right: rhythm(3);
    			}
    		}
    
    		@include e(meta) {
    			display: flex;
    			margin-bottom: rhythm(1);
    		}
    
    		@include e(info) {
    			h1 span {
    				pointer-events: none;
    			}
    
    			h1 svg {
    				width: $icon-size-large * 1.5;
    				height: $icon-size-large * 1.5;
    			}
    
    			h2 svg {
    				width: $icon-size-large * 1.2;
    				height: $icon-size-large * 1.2;
    			}
    
    			> p {
    				overflow: hidden;
    				text-overflow: ellipsis;
    				white-space: nowrap;
    
    				@include bp-up(sm-xs) {
    					overflow: visible;
    					text-overflow: inherit;
    					white-space: normal;
    				}
    			}
    		}
    
    		@include e(link) {
    			color: $color-cyberspace;
    			text-decoration: none;
    
    			&:hover,
    			&:focus {
    				text-decoration: underline;
    			}
    		}
    
    		@include e(button) {
    			display: flex;
    			align-items: center;
    			margin: 0 rhythm(1);
    			padding: 0;
    			border: 0;
    			background-color: transparent;
    
    			&:hover {
    				> svg {
    					fill: $color-ruby;
    				}
    			}
    
    			* {
    				pointer-events: none;
    			}
    		}
    
    		@include e(share) {
    			display: flex;
    			flex-wrap: wrap;
    			align-items: center;
    			margin-top: rhythm(1);
    
    			@include e(spacer) {
    				display: none;
    				margin-bottom: rhythm(2);
    
    				@include bp-up(lg) {
    					display: block;
    					margin-right: rhythm(2);
    					margin-bottom: 0;
    					margin-left: rhythm(2);
    					line-height: 1;
    				}
    			}
    
    			@include e(list) {
    				display: flex;
    				flex-basis: 100%;
    
    				> dt {
    					margin-right: rhythm(2);
    				}
    
    				> dd {
    					margin-left: 0;
    				}
    
    				@include bp-up(lg) {
    					flex-basis: auto;
    				}
    			}
    		}
    
    		@include e(listen-link) {
    			margin-right: rhythm(2);
    			color: $color-cyberspace;
    		}
    
    		@include e(download-link) {
    			display: flex;
    			flex-basis: 100%;
    			flex-grow: 1;
    			margin-bottom: rhythm(2);
    			color: $color-cyberspace;
    
    			> span {
    				font-family: $font-family-base;
    				font-size: $size-base;
    				text-decoration: underline;
    			}
    
    			> svg {
    				transform: translateY(3px);
    			}
    
    			@include bp-up(lg) {
    				margin-bottom: 0;
    			}
    
    			@include bp-up(md) {
    				flex-basis: auto;
    				flex-grow: 0;
    			}
    		}
    
    		@include e(download-icon) {
    			width: $icon-size;
    			height: $icon-size;
    			margin-right: rhythm(1);
    			padding: 3px;
    			border-radius: $border-radius;
    		}
    	}
    }
    
    // Track list (Prototype)
    @include organism(podcast-track-list) {
    	display: flex;
    	flex-direction: column;
    
    	> li {
    		display: flex;
    		padding: rhythm(1) rhythm(2);
    
    		+ li {
    			border-top: 1px solid $color-concrete;
    		}
    
    		> button {
    			display: flex;
    			align-items: center;
    		}
    	}
    }
    
  • URL: /components/raw/podcast/_podcast-episodes.scss
  • Filesystem Path: src/organisms/podcast/_podcast-episodes.scss
  • Size: 3.9 KB
  • Content:
    @charset "UTF-8";
    
    @use "sass:math";
    
    @include organism(podcast-player) {
    
    	// Player
    
    	$time-left-width: rem(65px);
    
    	position: fixed;
    	z-index: z_index(middlegroundImportant);
    	right: 0;
    	bottom: 0;
    	left: 0;
    	padding: rhythm(1) 0 0 0;
    	transform: translateY(0);
    	transition: transform 0.25s ease-out;
    	background-color: $color-snow;
    	box-shadow: 0 -#{rhythm(1)} rhythm(3) lighten($color-cyberspace, 70%);
    
    	@include m(hidden) {
    		transform: translateY(100%);
    		box-shadow: none;
    	}
    
    	@include e(player) {
    		display: flex;
    		flex-grow: 1;
    		align-items: center;
    
    		@include e(show-info) {
    			display: none;
    
    			@include bp-up(sm) {
    				display: block;
    			}
    		}
    
    		@include e(image) {
    			display: none;
    
    			@include bp-up(md) {
    				display: flex;
    				flex-grow: 0;
    				width: rhythm(7);
    				height: rhythm(7);
    				margin-right: rhythm(2);
    				border-radius: $border-radius;
    			}
    		}
    	}
    
    	@include e(show-info) {
    		display: flex;
    		flex-direction: column;
    	}
    
    	@include e(title) {
    		margin-bottom: 0;
    		color: $color-ruby;
    		font-family: $font-family-headings;
    		overflow: hidden;
    		text-overflow: ellipsis;
    		white-space: nowrap;
    		max-width: rem(200px);
    
    		@include bp-up(sm) {
    			max-width: rem(250px);
    		}
    
    		@include bp-up(lg) {
    			max-width: rem(500px);
    		}
    	}
    
    	@include e(description) {
    		display: block;
    		max-width: rem(200px);
    		overflow: hidden;
    		text-overflow: ellipsis;
    		white-space: nowrap;
    
    		> p {
    			display: none;
    		}
    
    		@include bp-up(sm) {
    			max-width: rem(250px);
    		}
    
    		@include bp-up(lg) {
    			max-width: rem(500px);
    		}
    	}
    
    	@include e(playtime) {
    		@include bp-between(sm, md) {
    			display: none;
    		}
    	}
    
    	@include e(spacer) {
    		@include bp-between(sm, md) {
    			display: none;
    		}
    	}
    
    	@include e(controls) {
    		display: flex;
    		flex-grow: 1;
    		align-items: center;
    		justify-content: center;
    	}
    
    	@include e(spacer) {
    		margin-right: rhythm(0.5);
    		margin-left: rhythm(0.5);
    	}
    
    	@include e(button) {
    		display: flex;
    		align-items: center;
    		margin: 0 rhythm(1);
    		padding: 0;
    		border: 0;
    		background-color: transparent;
    
    		&:hover {
    			> svg {
    				fill: $color-ruby;
    			}
    		}
    	}
    
    	@include e(timeleft) {
    		min-width: $time-left-width;
    	}
    
    	@include e(rewind-icon) {
    		width: $icon-size-large * 1.2;
    		height: $icon-size-large * 1.2;
    	}
    
    	@include e(forward-icon) {
    		width: $icon-size-large * 1.2;
    		height: $icon-size-large * 1.2;
    	}
    
    	@include e(play-icon) {
    		display: block;
    		width: $icon-size-large * 1.5;
    		height: $icon-size-large * 1.5;
    	}
    
    	@include e(progressbar) {
    		flex-wrap: wrap;
    		width: 100%;
    		height: rhythm(0.5);
    		margin-top: rhythm(1);
    		background-color: $color-concrete;
    
    		@include e(progress) {
    			width: 0;
    			height: rhythm(1);
    		}
    	}
    
    	@include e(close) {
    		position: absolute;
    		right: rhythm(0.5);
    		top: 50%;
    		transform: translateY(-50%);
    
    		@include bp-up(xxl) {
    			right: rhythm(2);
    		}
    	}
    }
    
  • URL: /components/raw/podcast/_podcast-player.scss
  • Filesystem Path: src/organisms/podcast/_podcast-player.scss
  • Size: 2.9 KB
  • Content:
    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`);
    	});
    }
    
  • URL: /components/raw/podcast/podcast.js
  • Filesystem Path: src/organisms/podcast/podcast.js
  • Size: 7.6 KB

No notes defined.