From daff053349fa3551e342622288236039eae9240e Mon Sep 17 00:00:00 2001 From: Syb Wartna Date: Mon, 18 Dec 2023 14:18:47 +0100 Subject: [PATCH 1/7] refactor low resolution banner --- scripts/video-helper.js | 29 +++++++++++++++++++++++------ styles/styles.css | 26 ++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/scripts/video-helper.js b/scripts/video-helper.js index ec4cb763..ec24c3d4 100644 --- a/scripts/video-helper.js +++ b/scripts/video-helper.js @@ -28,13 +28,30 @@ export function selectVideoLink(links, preferredType) { export function createLowResolutionBanner() { const lowResolutionMessage = getTextLabel('Low resolution video message'); const changeCookieSettings = getTextLabel('Change cookie settings'); + let banner; - const banner = document.createElement('div'); - banner.classList.add('low-resolution-banner'); - banner.innerHTML = `${lowResolutionMessage} `; - banner.querySelector('button').addEventListener('click', () => { - window.OneTrust.ToggleInfoDisplay(); - }); + if (document.documentElement.classList.contains('redesign-v2')) { + banner = createElement('div', { classes: 'low-resolution-banner' }); + const bannerText = createElement('p'); + const bannerButton = createElement('button', { classes: ['button', 'secondary', 'dark'] }); + + bannerText.textContent = lowResolutionMessage; + bannerButton.textContent = changeCookieSettings; + + banner.appendChild(bannerText); + banner.appendChild(bannerButton); + + bannerButton.addEventListener('click', () => { + window.OneTrust.ToggleInfoDisplay(); + }); + } else { + banner = document.createElement('div'); + banner.classList.add('low-resolution-banner'); + banner.innerHTML = `${lowResolutionMessage} `; + banner.querySelector('button').addEventListener('click', () => { + window.OneTrust.ToggleInfoDisplay(); + }); + } return banner; } diff --git a/styles/styles.css b/styles/styles.css index ff2aaebf..7d413d17 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -874,6 +874,32 @@ a.button.text-link-with-video::after { font-size: 18px; } +.redesign-v2 .low-resolution-banner { + margin-bottom: 16px; + padding: 16px; + background: var(--c-grey-950); + text-align: left; + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + gap: 16px; +} + +.redesign-v2 .low-resolution-banner p { + margin: 0; +} + +.redesign-v2 .button.secondary { + margin-left: auto; +} + +@media screen and (min-width: 1200px) { + .redesign-v2 .low-resolution-banner { + padding: 16px 32px; + } +} + /* pagination */ .pager .pagination ol li .last::before { font-family: var(--ff-fontawesome); From e11a972c15bea8c7a0f2ee750b5281f8c40a8c60 Mon Sep 17 00:00:00 2001 From: Syb Wartna Date: Tue, 19 Dec 2023 17:22:02 +0100 Subject: [PATCH 2/7] refactor check of cookie setting --- scripts/common.js | 8 ++++++-- scripts/video-helper.js | 6 ++---- test/scripts/common.test.js | 10 +++++----- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/scripts/common.js b/scripts/common.js index 77e30b9a..adf6a36b 100644 --- a/scripts/common.js +++ b/scripts/common.js @@ -316,13 +316,17 @@ export const slugify = (text) => ( * Check if one trust group is checked. * @param {String} groupName the one trust croup like: C0002 */ -export function checkOneTruckGroup(groupName) { +export function checkOneTrustGroup(groupName) { const oneTrustCookie = decodeURIComponent(document.cookie.split(';').find((cookie) => cookie.trim().startsWith('OptanonConsent='))); return oneTrustCookie.includes(`${groupName}:1`); } export function isEloquaFormAllowed() { - return checkOneTruckGroup('C0004'); + return checkOneTrustGroup('C0004'); +} + +export function isExternalVideoAllowed() { + return checkOneTrustGroup('C0005'); } /* diff --git a/scripts/video-helper.js b/scripts/video-helper.js index ec24c3d4..dd8154b1 100644 --- a/scripts/video-helper.js +++ b/scripts/video-helper.js @@ -1,5 +1,5 @@ // eslint-disable-next-line import/no-cycle -import { createElement, getTextLabel } from './common.js'; +import { createElement, getTextLabel, isExternalVideoAllowed } from './common.js'; /* video helpers */ export function isLowResolutionVideoUrl(url) { @@ -13,9 +13,7 @@ export function isVideoLink(link) { export function selectVideoLink(links, preferredType) { const linksList = [...links]; - const optanonConsentCookieValue = decodeURIComponent(document.cookie.split(';').find((cookie) => cookie.trim().startsWith('OptanonConsent='))); - const cookieConsentForExternalVideos = optanonConsentCookieValue.includes('C0005:1'); - const shouldUseYouTubeLinks = cookieConsentForExternalVideos && preferredType !== 'local'; + const shouldUseYouTubeLinks = isExternalVideoAllowed() && preferredType !== 'local'; const youTubeLink = linksList.find((link) => link.getAttribute('href').includes('youtube.com/embed/')); const localMediaLink = linksList.find((link) => link.getAttribute('href').split('?')[0].endsWith('.mp4')); diff --git a/test/scripts/common.test.js b/test/scripts/common.test.js index 4b4ebe1f..8e5eee33 100644 --- a/test/scripts/common.test.js +++ b/test/scripts/common.test.js @@ -206,12 +206,12 @@ describe('slugify', () => { }); }); -describe('checkOneTruckGroup', () => { +describe('checkOneTrustGroup', () => { it('should return true when the group is present with value 1', () => { // Simulate a cookie with the group 'group1' set to 1 document.cookie = 'OptanonConsent=group1:1;'; - const result = commonScript.checkOneTruckGroup('group1'); + const result = commonScript.checkOneTrustGroup('group1'); expect(result).to.be.true; }); @@ -219,7 +219,7 @@ describe('checkOneTruckGroup', () => { // Simulate a cookie with the group 'group2' set to 0 (or any value other than 1) document.cookie = 'OptanonConsent=group2:0;'; - const result = commonScript.checkOneTruckGroup('group2'); + const result = commonScript.checkOneTrustGroup('group2'); expect(result).to.be.false; }); @@ -227,7 +227,7 @@ describe('checkOneTruckGroup', () => { // Simulate an empty cookie document.cookie = ''; - const result = commonScript.checkOneTruckGroup('group3'); + const result = commonScript.checkOneTrustGroup('group3'); expect(result).to.be.false; }); @@ -235,7 +235,7 @@ describe('checkOneTruckGroup', () => { // Simulate a cookie with a URL-encoded group name document.cookie = 'OptanonConsent=group%204:1;'; - const result = commonScript.checkOneTruckGroup('group 4'); + const result = commonScript.checkOneTrustGroup('group 4'); expect(result).to.be.true; }); }); From 975129ba69c4bf97f9a19a55cf8fba6105da27bc Mon Sep 17 00:00:00 2001 From: Syb Wartna Date: Tue, 19 Dec 2023 18:35:58 +0100 Subject: [PATCH 3/7] add banner when no fallback video is present --- blocks/embed/embed.css | 53 ++++++++++++++++++++++++++++++++++++++++- blocks/embed/embed.js | 27 ++++++++++++++++++++- placeholder.json | 16 +++++++++++-- scripts/video-helper.js | 3 +++ 4 files changed, 95 insertions(+), 4 deletions(-) diff --git a/blocks/embed/embed.css b/blocks/embed/embed.css index 346beb49..f416cd89 100644 --- a/blocks/embed/embed.css +++ b/blocks/embed/embed.css @@ -11,12 +11,51 @@ .embed-video-element { aspect-ratio: 16/9; width: 100%; + display: block; } .embed .video-icon-wrapper { pointer-events: none; } +.embed .cookie-message { + aspect-ratio: 16/9; + background: rgb(0 0 0 / 80%); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 16px; +} + +.embed .cookie-message__title, +.embed .cookie-message__text { + max-width: var(--text-block-max-width); + margin: 0 auto; + text-align: center; +} + +.embed .cookie-message__title { + font: var(--f-heading-5-font-size)/var(--f-heading-5-line-height) var(--ff-volvo-novum-medium); + letter-spacing: var(--f-heading-5-letter-spacing); +} + +.embed .cookie-message__text { + font: var(--f-body-font-size)/var(--f-body-line-height) var(--font-family-body); +} + +.embed .cookie-message__text a { + color: inherit; + text-decoration: underline; +} + +.embed .cookie-message__button-container { + margin-top: 16px; + display: flex; + justify-content: center; + gap: 16px; +} + @media screen and (min-width: 768px) { .embed-video { width: 726px; @@ -27,6 +66,18 @@ } } +@media screen and (min-width: 1200px) { + .embed .cookie-message__title { + font-size: var(--f-heading-4-font-size); + line-height: var(--f-heading-4-line-height); + } + + .embed .cookie-message__text { + font-size: var(--f-body-2-font-size); + line-height: var(--f-body-2-line-height); + } +} + @media screen and (min-width: 1300px) { .embed-video { width: 1170px; @@ -35,4 +86,4 @@ .embed.embed-full-width .embed-video { width: 100%; } -} \ No newline at end of file +} diff --git a/blocks/embed/embed.js b/blocks/embed/embed.js index 3daca21d..950509e0 100644 --- a/blocks/embed/embed.js +++ b/blocks/embed/embed.js @@ -1,3 +1,4 @@ +import { getTextLabel, isExternalVideoAllowed } from '../../scripts/common.js'; import { selectVideoLink, addPlayIcon, showVideoModal, isLowResolutionVideoUrl, createLowResolutionBanner, createIframe, @@ -5,6 +6,7 @@ import { export default function decorate(block) { const isAutoplay = block.classList.contains('autoplay'); + const isSingleVideo = block.classList.contains('single-video'); const isLoopedVideo = block.classList.contains('loop'); const isFullWidth = block.classList.contains('full-width'); const hideLowResolutionBanner = block.classList.contains('no-banner'); @@ -13,11 +15,34 @@ export default function decorate(block) { block.classList.remove('loop', 'autoplay', 'full-width'); videoWrapper.classList.add('embed-video'); + const preferredType = (() => { + if (isSingleVideo) return 'singleVideo'; + if (isFullWidth) return 'local'; + return 'auto'; + })(); + const links = block.querySelectorAll('a'); - const selectedLink = selectVideoLink(links, isFullWidth ? 'local' : 'auto'); + const selectedLink = selectVideoLink(links, preferredType); const video = document.createElement('video'); const source = document.createElement('source'); + if (isSingleVideo && !isExternalVideoAllowed()) { + block.innerHTML = ''; + + const cookieMessage = document.createRange().createContextualFragment(` + `); + block.append(cookieMessage); + + return; + } + if (!selectedLink) { block.innerHTML = ''; /* eslint-disable-next-line no-console */ diff --git a/placeholder.json b/placeholder.json index 180252bb..408136cb 100644 --- a/placeholder.json +++ b/placeholder.json @@ -228,8 +228,20 @@ "Text": "Model year" }, { - "Key": "go to top", - "Text": "Go to the top of the page" + "Key": "single video message title", + "Text": "Enable cookies to view the reveal in high resolution?" + }, + { + "Key": "single video message text", + "Text": "Our media viewer uses social media cookies and/or similar technologies set by third-party services, as outlined in our cookie policy. Use the “Okay” button or close this notice to consent and view hi-res media. Select “Deny” to view in low resolution." + }, + { + "Key": "single video message button", + "Text": "Okay" + }, + { + "Key": "single video message button deny", + "Text": "Deny" } ], ":type": "sheet" diff --git a/scripts/video-helper.js b/scripts/video-helper.js index dd8154b1..4f3116b6 100644 --- a/scripts/video-helper.js +++ b/scripts/video-helper.js @@ -17,6 +17,9 @@ export function selectVideoLink(links, preferredType) { const youTubeLink = linksList.find((link) => link.getAttribute('href').includes('youtube.com/embed/')); const localMediaLink = linksList.find((link) => link.getAttribute('href').split('?')[0].endsWith('.mp4')); + if (preferredType === 'singleVideo' && youTubeLink) { + return youTubeLink; + } if (shouldUseYouTubeLinks && youTubeLink) { return youTubeLink; } From a37287df4822e685ba2d87cffd87c26d155e22d6 Mon Sep 17 00:00:00 2001 From: Lakshmishri Date: Thu, 21 Dec 2023 16:15:00 +0100 Subject: [PATCH 4/7] add background image, change color for banner, set document cookie on okay button --- blocks/embed/embed.css | 1 - blocks/embed/embed.js | 19 +++++++++++++++---- blocks/search-results/search-results.js | 8 +------- scripts/common.js | 7 +++++++ scripts/delayed.js | 5 ++++- styles/styles.css | 3 ++- 6 files changed, 29 insertions(+), 14 deletions(-) diff --git a/blocks/embed/embed.css b/blocks/embed/embed.css index f416cd89..2c2b14d2 100644 --- a/blocks/embed/embed.css +++ b/blocks/embed/embed.css @@ -20,7 +20,6 @@ .embed .cookie-message { aspect-ratio: 16/9; - background: rgb(0 0 0 / 80%); display: flex; flex-direction: column; align-items: center; diff --git a/blocks/embed/embed.js b/blocks/embed/embed.js index 950509e0..09117ed9 100644 --- a/blocks/embed/embed.js +++ b/blocks/embed/embed.js @@ -1,4 +1,4 @@ -import { getTextLabel, isExternalVideoAllowed } from '../../scripts/common.js'; +import { createElement, getTextLabel, isExternalVideoAllowed } from '../../scripts/common.js'; import { selectVideoLink, addPlayIcon, showVideoModal, isLowResolutionVideoUrl, createLowResolutionBanner, createIframe, @@ -27,18 +27,29 @@ export default function decorate(block) { const source = document.createElement('source'); if (isSingleVideo && !isExternalVideoAllowed()) { + const img = block.querySelector('picture img'); block.innerHTML = ''; + const cookieMsgConatiner = createElement('div', { + classes: 'cookie-message', + }); + cookieMsgConatiner.style.background = `linear-gradient(180deg, rgba(0, 0, 0, 0.00) 0%, rgba(0, 0, 0, 0.80) 100%), url(${img.src}) 100% center / cover no-repeat`; + const cookieMessage = document.createRange().createContextualFragment(` - `); - block.append(cookieMessage); + `); + + cookieMsgConatiner.append(cookieMessage); + block.append(cookieMsgConatiner); + + block.querySelector('.cookie-message__button-container .primary')?.addEventListener('click', () => { + document.cookie = 'isSingleVideo=true'; + }); return; } diff --git a/blocks/search-results/search-results.js b/blocks/search-results/search-results.js index 3de8c2f6..2fdd46f8 100644 --- a/blocks/search-results/search-results.js +++ b/blocks/search-results/search-results.js @@ -1,5 +1,6 @@ import templates from './templates.js'; import { loadScript } from '../../scripts/lib-franklin.js'; +import { getCookie } from '../../scripts/common.js'; // Implementation based on searchtax documentation https://www.searchstax.com/docs/searchstudio/searchstax-studio-searchjs-module/ export default async function decorate(block) { @@ -31,13 +32,6 @@ export default async function decorate(block) { document.cookie = `${name}=${encodeURIComponent(value)}; expires=${expires}; path=${path}`; } - function getCookie(name) { - return document.cookie.split('; ').reduce((r, v) => { - const parts = v.split('='); - return parts[0] === name ? decodeURIComponent(parts[1]) : r; - }, ''); - } - // generating random value for cookie when value is missing function makeid(length) { let result = ''; diff --git a/scripts/common.js b/scripts/common.js index adf6a36b..4056870f 100644 --- a/scripts/common.js +++ b/scripts/common.js @@ -377,3 +377,10 @@ export const getLanguagePath = () => { const langCodeMatch = pathname.match('^(/[a-z]{2}(-[a-z]{2})?/).*'); return langCodeMatch ? langCodeMatch[1] : '/'; }; + +export function getCookie(name) { + return document.cookie.split('; ').reduce((r, v) => { + const parts = v.split('='); + return parts[0] === name ? decodeURIComponent(parts[1]) : r; + }, ''); +} \ No newline at end of file diff --git a/scripts/delayed.js b/scripts/delayed.js index ad4168a3..cb1f6d0c 100644 --- a/scripts/delayed.js +++ b/scripts/delayed.js @@ -1,5 +1,6 @@ // eslint-disable-next-line import/no-cycle import { loadScript, sampleRUM } from './lib-franklin.js'; +import { getCookie } from './common.js'; const COOKIES = { performance: 'C0002:1', @@ -57,9 +58,11 @@ if (!window.location.pathname.includes('srcdoc') return s1 === s2; } + const isSingleVideo = getCookie('isSingleVideo'); + window.OneTrust.OnConsentChanged(() => { // reloading the page only when the active group has changed - if (!isSameGroups(currentOnetrustActiveGroups, window.OnetrustActiveGroups)) { + if (!isSameGroups(currentOnetrustActiveGroups, window.OnetrustActiveGroups) && !isSingleVideo) { window.location.reload(); } }); diff --git a/styles/styles.css b/styles/styles.css index 7d413d17..c72aa365 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -877,13 +877,14 @@ a.button.text-link-with-video::after { .redesign-v2 .low-resolution-banner { margin-bottom: 16px; padding: 16px; - background: var(--c-grey-950); + background: var(--c-grey-4); text-align: left; display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 16px; + color: var(--c-white); } .redesign-v2 .low-resolution-banner p { From 2ba8914cb3aada9971e563a03b287edcb402bae4 Mon Sep 17 00:00:00 2001 From: Lakshmishri Date: Thu, 21 Dec 2023 16:58:10 +0100 Subject: [PATCH 5/7] fix lint and failing test --- blocks/search-results/search-results.js | 8 +++++++- scripts/common.js | 7 ------- scripts/delayed.js | 8 +++++--- test/scripts/video-helper.test.js | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/blocks/search-results/search-results.js b/blocks/search-results/search-results.js index 2fdd46f8..3de8c2f6 100644 --- a/blocks/search-results/search-results.js +++ b/blocks/search-results/search-results.js @@ -1,6 +1,5 @@ import templates from './templates.js'; import { loadScript } from '../../scripts/lib-franklin.js'; -import { getCookie } from '../../scripts/common.js'; // Implementation based on searchtax documentation https://www.searchstax.com/docs/searchstudio/searchstax-studio-searchjs-module/ export default async function decorate(block) { @@ -32,6 +31,13 @@ export default async function decorate(block) { document.cookie = `${name}=${encodeURIComponent(value)}; expires=${expires}; path=${path}`; } + function getCookie(name) { + return document.cookie.split('; ').reduce((r, v) => { + const parts = v.split('='); + return parts[0] === name ? decodeURIComponent(parts[1]) : r; + }, ''); + } + // generating random value for cookie when value is missing function makeid(length) { let result = ''; diff --git a/scripts/common.js b/scripts/common.js index 4056870f..adf6a36b 100644 --- a/scripts/common.js +++ b/scripts/common.js @@ -377,10 +377,3 @@ export const getLanguagePath = () => { const langCodeMatch = pathname.match('^(/[a-z]{2}(-[a-z]{2})?/).*'); return langCodeMatch ? langCodeMatch[1] : '/'; }; - -export function getCookie(name) { - return document.cookie.split('; ').reduce((r, v) => { - const parts = v.split('='); - return parts[0] === name ? decodeURIComponent(parts[1]) : r; - }, ''); -} \ No newline at end of file diff --git a/scripts/delayed.js b/scripts/delayed.js index cb1f6d0c..40b3fd4f 100644 --- a/scripts/delayed.js +++ b/scripts/delayed.js @@ -1,6 +1,5 @@ // eslint-disable-next-line import/no-cycle import { loadScript, sampleRUM } from './lib-franklin.js'; -import { getCookie } from './common.js'; const COOKIES = { performance: 'C0002:1', @@ -58,11 +57,14 @@ if (!window.location.pathname.includes('srcdoc') return s1 === s2; } - const isSingleVideo = getCookie('isSingleVideo'); + const isSingleVideo = document.cookie.split('; ').reduce((r, v) => { + const parts = v.split('='); + return parts[0] === 'isSingleVideo' ? decodeURIComponent(parts[1]) : r; + }, ''); window.OneTrust.OnConsentChanged(() => { // reloading the page only when the active group has changed - if (!isSameGroups(currentOnetrustActiveGroups, window.OnetrustActiveGroups) && !isSingleVideo) { + if (!isSameGroups(currentOnetrustActiveGroups, window.OnetrustActiveGroups) && isSingleVideo !== 'true') { window.location.reload(); } }); diff --git a/test/scripts/video-helper.test.js b/test/scripts/video-helper.test.js index 2e8957ad..5866b751 100644 --- a/test/scripts/video-helper.test.js +++ b/test/scripts/video-helper.test.js @@ -88,10 +88,10 @@ describe('selectVideoLink', () => { youtubeLink = commonScript.createElement('a', { props: { href: 'https://www.youtube.com/embed/example-video' } }); mp4Link = commonScript.createElement('a', { props: { href: 'https://example.com/example-video.mp4' } }); otherLink = commonScript.createElement('a', { props: { href: 'https://example.com/other-link' } }); - document.cookie = 'OptanonConsent=C0005:1'; }); it('should return YouTube link when preferredType is not "local" and YouTube link is available', () => { + document.cookie = 'OptanonConsent=C0005:1'; const links = [youtubeLink, mp4Link]; const result = videoHelper.selectVideoLink(links, 'external'); expect(result).to.equal(youtubeLink); From 107991d64a1fe450ce3748a46381f65f9a61aa14 Mon Sep 17 00:00:00 2001 From: Syb Wartna Date: Tue, 9 Jan 2024 17:07:33 +0100 Subject: [PATCH 6/7] add rounded corners to video --- blocks/embed/embed.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/blocks/embed/embed.css b/blocks/embed/embed.css index 2c2b14d2..1b2f7e3e 100644 --- a/blocks/embed/embed.css +++ b/blocks/embed/embed.css @@ -55,6 +55,10 @@ gap: 16px; } +.embed.single-video .embed-video-element { + border-radius: var(--border-radius); +} + @media screen and (min-width: 768px) { .embed-video { width: 726px; From e9fb7ad855154c7354dea356c80513e34c0c7911 Mon Sep 17 00:00:00 2001 From: Syb Wartna Date: Thu, 11 Jan 2024 14:57:41 +0100 Subject: [PATCH 7/7] split livestream code from embed --- blocks/embed/embed.css | 23 ---- blocks/embed/embed.js | 34 +----- .../v2-livestream-embed.css | 63 ++++++++++ .../v2-livestream-embed.js | 113 ++++++++++++++++++ common/modal/modal.css | 17 +++ common/modal/modal.js | 6 +- placeholder.json | 6 +- scripts/delayed.js | 35 ++++++ scripts/video-helper.js | 7 ++ styles/styles.css | 2 +- 10 files changed, 245 insertions(+), 61 deletions(-) create mode 100644 blocks/v2-livestream-embed/v2-livestream-embed.css create mode 100644 blocks/v2-livestream-embed/v2-livestream-embed.js diff --git a/blocks/embed/embed.css b/blocks/embed/embed.css index 1b2f7e3e..15271bfa 100644 --- a/blocks/embed/embed.css +++ b/blocks/embed/embed.css @@ -48,17 +48,6 @@ text-decoration: underline; } -.embed .cookie-message__button-container { - margin-top: 16px; - display: flex; - justify-content: center; - gap: 16px; -} - -.embed.single-video .embed-video-element { - border-radius: var(--border-radius); -} - @media screen and (min-width: 768px) { .embed-video { width: 726px; @@ -69,18 +58,6 @@ } } -@media screen and (min-width: 1200px) { - .embed .cookie-message__title { - font-size: var(--f-heading-4-font-size); - line-height: var(--f-heading-4-line-height); - } - - .embed .cookie-message__text { - font-size: var(--f-body-2-font-size); - line-height: var(--f-body-2-line-height); - } -} - @media screen and (min-width: 1300px) { .embed-video { width: 1170px; diff --git a/blocks/embed/embed.js b/blocks/embed/embed.js index 09117ed9..c5c86a54 100644 --- a/blocks/embed/embed.js +++ b/blocks/embed/embed.js @@ -1,12 +1,11 @@ -import { createElement, getTextLabel, isExternalVideoAllowed } from '../../scripts/common.js'; import { - selectVideoLink, addPlayIcon, showVideoModal, isLowResolutionVideoUrl, + selectVideoLink, addPlayIcon, + showVideoModal, isLowResolutionVideoUrl, createLowResolutionBanner, createIframe, } from '../../scripts/video-helper.js'; export default function decorate(block) { const isAutoplay = block.classList.contains('autoplay'); - const isSingleVideo = block.classList.contains('single-video'); const isLoopedVideo = block.classList.contains('loop'); const isFullWidth = block.classList.contains('full-width'); const hideLowResolutionBanner = block.classList.contains('no-banner'); @@ -16,7 +15,6 @@ export default function decorate(block) { videoWrapper.classList.add('embed-video'); const preferredType = (() => { - if (isSingleVideo) return 'singleVideo'; if (isFullWidth) return 'local'; return 'auto'; })(); @@ -26,34 +24,6 @@ export default function decorate(block) { const video = document.createElement('video'); const source = document.createElement('source'); - if (isSingleVideo && !isExternalVideoAllowed()) { - const img = block.querySelector('picture img'); - block.innerHTML = ''; - - const cookieMsgConatiner = createElement('div', { - classes: 'cookie-message', - }); - cookieMsgConatiner.style.background = `linear-gradient(180deg, rgba(0, 0, 0, 0.00) 0%, rgba(0, 0, 0, 0.80) 100%), url(${img.src}) 100% center / cover no-repeat`; - - const cookieMessage = document.createRange().createContextualFragment(` - - - - `); - - cookieMsgConatiner.append(cookieMessage); - block.append(cookieMsgConatiner); - - block.querySelector('.cookie-message__button-container .primary')?.addEventListener('click', () => { - document.cookie = 'isSingleVideo=true'; - }); - - return; - } - if (!selectedLink) { block.innerHTML = ''; /* eslint-disable-next-line no-console */ diff --git a/blocks/v2-livestream-embed/v2-livestream-embed.css b/blocks/v2-livestream-embed/v2-livestream-embed.css new file mode 100644 index 00000000..e31a880b --- /dev/null +++ b/blocks/v2-livestream-embed/v2-livestream-embed.css @@ -0,0 +1,63 @@ +.v2-livestream-embed { + border-radius: var(--border-radius); + aspect-ratio: 16/9; + width: 100%; +} + +.v2-livestream-embed iframe { + display: block; +} + +.v2-livestream-embed .cookie-message { + aspect-ratio: 16/9; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 16px; +} + +.v2-livestream-embed .cookie-message__title, +.v2-livestream-embed .cookie-message__text { + max-width: var(--text-block-max-width); + margin: 0 auto; + text-align: center; +} + +.v2-livestream-embed .cookie-message__title { + font: var(--f-heading-5-font-size)/var(--f-heading-5-line-height) var(--ff-volvo-novum-medium); + letter-spacing: var(--f-heading-5-letter-spacing); +} + +.v2-livestream-embed .cookie-message__text { + font: var(--f-body-font-size)/var(--f-body-line-height) var(--font-family-body); +} + +.v2-livestream-embed .cookie-message__text a { + color: inherit; + text-decoration: underline; +} + +.v2-livestream-embed .cookie-message__text a:hover { + color: var(--c-grey-300); +} + +.v2-livestream-embed .cookie-message__button-container { + margin-top: 16px; + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 16px; +} + +@media screen and (min-width: 1200px) { + .v2-livestream-embed .cookie-message__title { + font-size: var(--f-heading-4-font-size); + line-height: var(--f-heading-4-line-height); + } + + .v2-livestream-embed .cookie-message__text { + font-size: var(--f-body-2-font-size); + line-height: var(--f-body-2-line-height); + } +} diff --git a/blocks/v2-livestream-embed/v2-livestream-embed.js b/blocks/v2-livestream-embed/v2-livestream-embed.js new file mode 100644 index 00000000..13835558 --- /dev/null +++ b/blocks/v2-livestream-embed/v2-livestream-embed.js @@ -0,0 +1,113 @@ +/* eslint-disable no-console */ +/* eslint-disable no-unused-vars */ +/* eslint-disable max-len */ +import { loadScript } from '../../scripts/lib-franklin.js'; +import { createElement, getTextLabel, isExternalVideoAllowed } from '../../scripts/common.js'; +import { updateCookieValue } from '../../scripts/delayed.js'; +import { hideModal } from '../../common/modal/modal.js'; + +let player; + +function onPlayerReady(event) { + console.info('Event: onPlayerReady'); + event.target.playVideo(); +} + +function onPlayerError(event) { + console.warn('Event: onPlayerError'); + console.warn(event.data); +} + +function onPlayerAutoplayBlocked(event) { + console.warn('Event: onPlayerAutoplayBlocked'); + console.warn(event.data); +} + +export function playVideo() { + if (player && player.playVideo) { + player.playVideo(); + } +} + +export default function decorate(block) { + loadScript('https://www.youtube.com/iframe_api'); + + let [videoLink] = block.querySelectorAll('a'); + const [, videoId] = videoLink.getAttribute('href').split('/embed/'); + const [videoCode] = videoId.split('?'); + videoLink = videoCode; + + console.info(`video id: ${videoLink}`); + + if (!videoLink) { + block.innerHTML = ''; + /* eslint-disable-next-line no-console */ + console.warn('V2 Livestream Embed block: There is no video link. Please check the provided URL.'); + return; + } + + // eslint-disable-next-line func-names + window.onYouTubeIframeAPIReady = function () { + setTimeout(() => { + // eslint-disable-next-line no-undef + player = new YT.Player('livestream', { + events: { + onReady: onPlayerReady, + onError: onPlayerError, + onAutoplayBlocked: onPlayerAutoplayBlocked, + }, + }); + }, 3000); + }; + + if (!isExternalVideoAllowed()) { + const img = block.querySelector('picture img'); + block.innerHTML = ''; + + const cookieMsgContainer = createElement('div', { + classes: 'cookie-message', + }); + cookieMsgContainer.style.background = `linear-gradient(180deg, rgba(0, 0, 0, 0.00) 0%, rgba(0, 0, 0, 0.80) 100%), url(${img.src}) center / cover no-repeat`; + + const cookieMessage = document.createRange().createContextualFragment(` + + ${getTextLabel('single video message text')} + + `); + + cookieMsgContainer.append(cookieMessage); + block.append(cookieMsgContainer); + + block.querySelector('.cookie-message__button-container .primary')?.addEventListener('click', () => { + const domain = 'localhost'; + const path = '/'; // assuming root path + const expirationDate = new Date(); + expirationDate.setFullYear(expirationDate.getFullYear() + 1); // 1 year from now + const sameSite = 'Lax'; + + console.log('updatecookie'); + + updateCookieValue('OptanonConsent=', 'C0005:0', 'C0005:1', domain, path, expirationDate, sameSite); + }); + + block.querySelector('.cookie-message__button-container .secondary')?.addEventListener('click', () => { + hideModal(); + }); + + return; + } + + const iframeYT = document.createRange().createContextualFragment(` + + `); + + block.innerHTML = ''; + + block.append(...iframeYT.childNodes); +} diff --git a/common/modal/modal.css b/common/modal/modal.css index b3783b4a..07f40484 100644 --- a/common/modal/modal.css +++ b/common/modal/modal.css @@ -236,3 +236,20 @@ width: 100%; } } + +.modal-reveal { + display: flex; + flex-direction: column; + justify-content: center; +} + +/* stylelint-disable-next-line no-descending-specificity */ +.modal-reveal .modal-content { + height: auto; + aspect-ratio: auto; +} + +.modal-reveal .section > div { + padding-top: 0; + padding-bottom: 0; +} diff --git a/common/modal/modal.js b/common/modal/modal.js index a4d0a4cb..23343571 100644 --- a/common/modal/modal.js +++ b/common/modal/modal.js @@ -35,8 +35,10 @@ const createModal = () => { modalBackground.setAttribute('role', 'dialog'); modalBackground.addEventListener('click', () => { - // eslint-disable-next-line no-use-before-define - hideModal(); + if (!modalBackground.classList.contains('modal-reveal')) { + // eslint-disable-next-line no-use-before-define + hideModal(); + } }); const keyDownAction = (event) => { diff --git a/placeholder.json b/placeholder.json index 408136cb..5000b7f2 100644 --- a/placeholder.json +++ b/placeholder.json @@ -233,15 +233,15 @@ }, { "Key": "single video message text", - "Text": "Our media viewer uses social media cookies and/or similar technologies set by third-party services, as outlined in our cookie policy. Use the “Okay” button or close this notice to consent and view hi-res media. Select “Deny” to view in low resolution." + "Text": "

Our media viewer uses social media cookies and/or similar technologies set by third-party services, as outlined in our Cookie Notice. We and our digital partners use cookies to improve your browsing experience, save your preferences and provide us with information on how you use our website.

You can click “Accept All Cookies” to view the reveal and to agree to the storing of cookies on your device and to our use of cookies. You can also configure or reject cookies by clicking on “Cookie Settings” or “Reject All.” Please note that rejecting cookies means that you will not be able to view the reveal. Please also note that selecting “Reject All” still implies that necessary cookies will remain.

" }, { "Key": "single video message button", - "Text": "Okay" + "Text": "Accept All Cookies" }, { "Key": "single video message button deny", - "Text": "Deny" + "Text": "Reject All" } ], ":type": "sheet" diff --git a/scripts/delayed.js b/scripts/delayed.js index 40b3fd4f..992f946f 100644 --- a/scripts/delayed.js +++ b/scripts/delayed.js @@ -1,3 +1,4 @@ +// eslint-disable no-console // eslint-disable-next-line import/no-cycle import { loadScript, sampleRUM } from './lib-franklin.js'; @@ -14,6 +15,40 @@ const cookieSetting = decodeURIComponent(document.cookie.split(';') const isPerformanceAllowed = cookieSetting.includes(COOKIES.performance); const isSocialAllowed = cookieSetting.includes(COOKIES.social); +export function updateCookieValue(cookieName, oldValue, newValue, domain, path, expires, sameSite) { + let cookies = decodeURIComponent(document.cookie).split(';'); + console.info(cookies); + + for (let i = 0; i < cookies.length; i++) { + let cookie = cookies[i].trim(); + if (cookie.startsWith(cookieName)) { + let cookieValue = cookie.substring(cookieName.length); + console.info(cookieValue); + if (cookieValue.includes(oldValue)) { + console.info(oldValue); + let updatedValue = encodeURIComponent(cookieValue.replace(oldValue, newValue)); + console.info(updatedValue); + let updatedCookie = cookieName + updatedValue; + console.info(updatedCookie); + if (domain) { + updatedCookie += `; domain=${domain}`; + } + if (path) { + updatedCookie += `; path=${path}`; + } + if (expires) { + updatedCookie += `; expires=${expires.toUTCString()}`; + } + if (sameSite) { + updatedCookie += `; SameSite=${sameSite}`; + } + document.cookie = updatedCookie; + } + break; + } + } +} + if (isPerformanceAllowed) { loadGoogleTagManager(); loadHotjar(); diff --git a/scripts/video-helper.js b/scripts/video-helper.js index 4f3116b6..ceb55272 100644 --- a/scripts/video-helper.js +++ b/scripts/video-helper.js @@ -365,3 +365,10 @@ export const addMuteControls = (section) => { controls.addEventListener('click', () => toggleMute(video)); }; + +export function loadYouTubeIframeAPI() { + const tag = document.createElement('script'); + tag.src = 'https://www.youtube.com/iframe_api'; + const firstScriptTag = document.getElementsByTagName('script')[0]; + firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); +} diff --git a/styles/styles.css b/styles/styles.css index c72aa365..a3095c08 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -891,7 +891,7 @@ a.button.text-link-with-video::after { margin: 0; } -.redesign-v2 .button.secondary { +.redesign-v2 .low-resolution-banner .button.secondary { margin-left: auto; }