diff --git a/legacy/src/_shared/js/capi-multiple.js b/legacy/src/_shared/js/capi-multiple.js index 44530f77..0d7cb490 100644 --- a/legacy/src/_shared/js/capi-multiple.js +++ b/legacy/src/_shared/js/capi-multiple.js @@ -33,7 +33,7 @@ function buildTitle (card, cardInfo, cardNumber) { let title = card.querySelector('.advert__title'); let kickerText = OVERRIDES.kickers[cardNumber]; - let kicker = kickerText ? `` : ''; + let kicker = kickerText ? `
` : ''; let icon = ''; let headline = OVERRIDES.headlines[cardNumber] || cardInfo.articleHeadline; diff --git a/legacy/src/_shared/scss/_adverts-capi.scss b/legacy/src/_shared/scss/_adverts-capi.scss index 7709947c..00606b5b 100644 --- a/legacy/src/_shared/scss/_adverts-capi.scss +++ b/legacy/src/_shared/scss/_adverts-capi.scss @@ -62,7 +62,7 @@ } .adverts--tone-paidfor { - background-color: $neutral-6; + background-color: #ededed; > .adverts__header { background: $paidfor-background; @@ -231,8 +231,7 @@ fill: $paid-article-icon; } - .advert__kicker, - .advert__kicker::after { + .advert__kicker { color: $paid-card-kicker; } } @@ -296,16 +295,3 @@ } } - -.advert__kicker { - &:after { - content: '/'; - display: inline-block; - margin-left: .2em; - color: mix($neutral-1, #ffffff, 20%); - } - - &:hover:after { - text-decoration: none; - } -} diff --git a/legacy/src/_shared/scss/_adverts-fabric.scss b/legacy/src/_shared/scss/_adverts-fabric.scss new file mode 100644 index 00000000..9bc75dd9 --- /dev/null +++ b/legacy/src/_shared/scss/_adverts-fabric.scss @@ -0,0 +1,3 @@ +body { + margin: 0; +} diff --git a/legacy/src/_shared/scss/_adverts-merchandising.scss b/legacy/src/_shared/scss/_adverts-merchandising.scss new file mode 100644 index 00000000..9bc75dd9 --- /dev/null +++ b/legacy/src/_shared/scss/_adverts-merchandising.scss @@ -0,0 +1,3 @@ +body { + margin: 0; +} diff --git a/legacy/src/_shared/scss/_adverts.scss b/legacy/src/_shared/scss/_adverts.scss index 7f23f420..ea8cfe6c 100644 --- a/legacy/src/_shared/scss/_adverts.scss +++ b/legacy/src/_shared/scss/_adverts.scss @@ -270,7 +270,7 @@ } } - + @include mq(leftCol) { padding: $gs-baseline 0; } @@ -574,7 +574,7 @@ /* Theming */ .adverts--legacy { - background: #eaeaea; + background: #ededed; > .adverts__header .button { color: #000000; diff --git a/legacy/src/_shared/scss/_hosted.scss b/legacy/src/_shared/scss/_hosted.scss index febbe7bf..d5088fc9 100644 --- a/legacy/src/_shared/scss/_hosted.scss +++ b/legacy/src/_shared/scss/_hosted.scss @@ -5,7 +5,7 @@ $phablet: 660px; .creative--hosted { font-family: $f-sans-serif-text; min-height: 100px; - background: #e9e9e9; + background: #ededed; @include mq(tablet) { background: none; @@ -17,7 +17,7 @@ $phablet: 660px; padding: 6px 0 24px; border-top: 1px solid; margin: 0 auto; - background: #e9e9e9; + background: #ededed; @include mq($until: mobileLandscape) { margin-left: 10px; diff --git a/legacy/src/capi-multiple-hosted/web/index.scss b/legacy/src/capi-multiple-hosted/web/index.scss index 4b134b24..978ebf14 100644 --- a/legacy/src/capi-multiple-hosted/web/index.scss +++ b/legacy/src/capi-multiple-hosted/web/index.scss @@ -1,6 +1,7 @@ @import '_core'; @import '_adverts-capi'; @import '_hosted'; +@import '_adverts-merchandising'; .advert__image-container { position: relative; diff --git a/legacy/src/capi-multiple-paidfor/app/app-capi-multiple.js b/legacy/src/capi-multiple-paidfor/app/app-capi-multiple.js index 6db41a64..ed5c0ea1 100644 --- a/legacy/src/capi-multiple-paidfor/app/app-capi-multiple.js +++ b/legacy/src/capi-multiple-paidfor/app/app-capi-multiple.js @@ -31,7 +31,7 @@ function buildTitle (card, cardInfo, cardNumber) { let title = card.querySelector('.advert__title'); let kickerText = OVERRIDES.kickers[cardNumber]; - let kicker = kickerText ? `` : ''; + let kicker = kickerText ? `
` : ''; let icon = ''; let headline = OVERRIDES.headlines[cardNumber] || cardInfo.articleHeadline; diff --git a/legacy/src/capi-multiple-paidfor/web/index.scss b/legacy/src/capi-multiple-paidfor/web/index.scss index b7a11721..b3153f78 100644 --- a/legacy/src/capi-multiple-paidfor/web/index.scss +++ b/legacy/src/capi-multiple-paidfor/web/index.scss @@ -5,6 +5,7 @@ @import '_paidfor-meta'; @import '_palette'; @import '_popup-paidfor'; +@import '_adverts-merchandising'; .adverts { color: $neutral-1; diff --git a/legacy/src/capi-single-paidfor/web/index.html b/legacy/src/capi-single-paidfor/web/index.html index 3e15376f..2f67eff1 100644 --- a/legacy/src/capi-single-paidfor/web/index.html +++ b/legacy/src/capi-single-paidfor/web/index.html @@ -53,8 +53,8 @@

diff --git a/legacy/src/capi-single-paidfor/web/index.scss b/legacy/src/capi-single-paidfor/web/index.scss index 7488f171..5a9f0d1a 100644 --- a/legacy/src/capi-single-paidfor/web/index.scss +++ b/legacy/src/capi-single-paidfor/web/index.scss @@ -45,7 +45,8 @@ } .badge__logo { - width: 200px; + display: block; + max-width: 200px; margin-left: 0; } } diff --git a/legacy/src/events-multiple/web/index.scss b/legacy/src/events-multiple/web/index.scss index 9ab63fc4..ce745e4e 100644 --- a/legacy/src/events-multiple/web/index.scss +++ b/legacy/src/events-multiple/web/index.scss @@ -3,4 +3,4 @@ @import '_adverts-masterclasses'; @import '_adverts-live'; @import '_advert'; - +@import '_adverts-merchandising'; diff --git a/legacy/src/fabric-custom/web/index.scss b/legacy/src/fabric-custom/web/index.scss index 3195f46f..9bd3561d 100644 --- a/legacy/src/fabric-custom/web/index.scss +++ b/legacy/src/fabric-custom/web/index.scss @@ -1 +1,2 @@ @import '_core'; +@import '_adverts-fabric'; diff --git a/legacy/src/fabric-video/web/index.scss b/legacy/src/fabric-video/web/index.scss index 344f77bf..1564845d 100644 --- a/legacy/src/fabric-video/web/index.scss +++ b/legacy/src/fabric-video/web/index.scss @@ -1,4 +1,5 @@ @import '_core'; +@import '_adverts-fabric'; .creative--fabric-video { margin: auto; diff --git a/legacy/src/fabric/web/index.scss b/legacy/src/fabric/web/index.scss index d1ea9136..36d997be 100644 --- a/legacy/src/fabric/web/index.scss +++ b/legacy/src/fabric/web/index.scss @@ -1,4 +1,5 @@ @import '_core'; +@import '_adverts-fabric'; .creative--fabric { &, diff --git a/legacy/src/manual-multiple/web/index.scss b/legacy/src/manual-multiple/web/index.scss index 7563aa06..bbfb97ea 100644 --- a/legacy/src/manual-multiple/web/index.scss +++ b/legacy/src/manual-multiple/web/index.scss @@ -21,3 +21,4 @@ @import '_adverts-climate'; @import '_adverts-support'; @import '_adverts-subs-rebrand'; +@import '_adverts-merchandising'; diff --git a/legacy/src/programmatic-dmpu/test.json b/legacy/src/programmatic-dmpu/test.json index 3f6ae6c9..1569202c 100644 --- a/legacy/src/programmatic-dmpu/test.json +++ b/legacy/src/programmatic-dmpu/test.json @@ -4,5 +4,6 @@ "TrackingPixel": "https://gu.com/tracking-pixel.gif", "ResearchPixel": "https://gu.com/research-pixel.gif", "ViewabilityTracker": "", - "BackgroundImage":"https://adops-assets.s3.amazonaws.com/dap-fabrics/prog-dmpu-test-image/dmpu-test.jpg" + "BackgroundImage":"https://adops-assets.s3.amazonaws.com/dap-fabrics/prog-dmpu-test-image/dmpu-test.jpg", + "thirdPartyJSTracking": " " } diff --git a/legacy/src/programmatic-dmpu/web/index.html b/legacy/src/programmatic-dmpu/web/index.html index 5d024b90..e299ad56 100644 --- a/legacy/src/programmatic-dmpu/web/index.html +++ b/legacy/src/programmatic-dmpu/web/index.html @@ -7,3 +7,4 @@
+[%thirdPartyJSTracking%] \ No newline at end of file diff --git a/legacy/src/programmatic-mpu/test.json b/legacy/src/programmatic-mpu/test.json index 47a8da9c..725af8dd 100644 --- a/legacy/src/programmatic-mpu/test.json +++ b/legacy/src/programmatic-mpu/test.json @@ -4,5 +4,6 @@ "TrackingPixel": "https://gu.com/tracking-pixel.gif", "ResearchPixel": "https://gu.com/research-pixel.gif", "ViewabilityTracker": "", - "BackgroundImage":"https://adops-assets.s3.amazonaws.com/SkySports_Aug18_Background.jpg" + "BackgroundImage":"https://adops-assets.s3.amazonaws.com/SkySports_Aug18_Background.jpg", + "thirdPartyJSTracking": " " } diff --git a/legacy/src/programmatic-mpu/web/index.html b/legacy/src/programmatic-mpu/web/index.html index d78a5033..59e4cfce 100644 --- a/legacy/src/programmatic-mpu/web/index.html +++ b/legacy/src/programmatic-mpu/web/index.html @@ -7,3 +7,4 @@
+[%thirdPartyJSTracking%] \ No newline at end of file diff --git a/src/lib/Previews.svelte b/src/lib/Previews.svelte index 9718bec0..6e867af0 100644 --- a/src/lib/Previews.svelte +++ b/src/lib/Previews.svelte @@ -34,7 +34,7 @@ }; onMount(() => { - window.addEventListener('message', (ev: MessageEvent) => { + window.addEventListener('message', (ev: MessageEvent) => { if (!ev.isTrusted) return; const { source, data: json } = ev; @@ -49,7 +49,16 @@ const iframe = source.frameElement as HTMLIFrameElement; iframe.height = String(data.value.height); break; - + case 'get-page-url': + source.postMessage( + JSON.stringify({ + id: data.id, + result: + 'https://www.theguardian.com/lifeandstyle/2023/jul/19/a-moment-that-changed-me-i-borrowed-a-dog-and-discovered-a-healthier-happier-way-of-life', + }), + '*', + ); + break; default: break; } diff --git a/src/lib/cmp.ts b/src/lib/cmp.ts new file mode 100644 index 00000000..637c7701 --- /dev/null +++ b/src/lib/cmp.ts @@ -0,0 +1,68 @@ +import { generateId, timeout } from './messenger'; + +interface ConsentState { + dateCreated: string; + gpcEnabled: boolean; + newUser: boolean; + uspString: string; +} + +interface CmpReturn { + __cmpReturn: { + returnValue: ConsentState; + callId: string; + success: boolean; + }; +} + +const isReplyFromCMP = (json: unknown): json is CmpReturn => { + const reply = json as CmpReturn; + return '__cmpReturn' in reply && typeof reply.__cmpReturn === 'object'; +}; + +const decodeReply = (e: MessageEvent): CmpReturn | void => { + try { + const json: unknown = JSON.parse(e.data); + + if (isReplyFromCMP(json)) { + return json; + } + + return; + } catch (_) { + return; + } +}; + +const isCcpaOptedOut = (consentState: ConsentState) => { + return consentState.uspString[2] === 'Y'; +}; + +const getUSPData = async (): Promise => + timeout( + new Promise((resolve) => { + const callId = generateId(); + const message = { + __cmpCall: { + command: 'getUSPData', + version: 1, + callId, + }, + }; + + const listener = (e: MessageEvent) => { + const decoded = decodeReply(e); + if (decoded && decoded.__cmpReturn.callId === callId) { + resolve(decoded.__cmpReturn.returnValue); + window.removeEventListener('message', listener); + } + }; + + window.addEventListener('message', listener); + + window.top?.postMessage(JSON.stringify(message), '*'); + }), + 3000, + ); + +export { getUSPData, isCcpaOptedOut }; diff --git a/src/lib/messenger.ts b/src/lib/messenger.ts index 239fe6ba..e3e07520 100644 --- a/src/lib/messenger.ts +++ b/src/lib/messenger.ts @@ -1,5 +1,6 @@ type StandardMessage = { type: Type; + id?: string; iframeId?: string; slotId?: string; /** @@ -7,6 +8,8 @@ type StandardMessage = { * * We mostly treat this as unknown and leave it up to the message * listeners to convert to a type they can handle + * + * Although some messages don't have a value property, e.g. 'get-page-url', this property is still required due to validation in messenger.ts in the commercial repo */ value: Data; }; @@ -27,12 +30,28 @@ type BackgroundMessage = StandardMessage< backgroundPosition: string; backgroundSize: string; ctaUrl: string; + videoSource: string; } >; type TypeMessage = StandardMessage<'type', string>; -type Message = ResizeMessage | StringMessage | BackgroundMessage | TypeMessage; +type GetPageURLMessage = StandardMessage<'get-page-url', string>; + +type PassbackRefreshMessage = StandardMessage<'passback-refresh', string>; + +type Message = + | ResizeMessage + | StringMessage + | BackgroundMessage + | TypeMessage + | GetPageURLMessage + | PassbackRefreshMessage; + +type MessengerResponse = { + id: string; + result: string; +}; const generateId = () => { const _4chars = () => @@ -42,6 +61,12 @@ const generateId = () => { return `${_4chars()}${_4chars()}-${_4chars()}-${_4chars()}-${_4chars()}-${_4chars()}${_4chars()}${_4chars()}`; }; +const timeout = async (promise: Promise, ms: number): Promise => + Promise.race([ + promise, + new Promise((resolve) => window.setTimeout(resolve, ms)), + ]); + /** * Post message to parent frame * @@ -52,5 +77,49 @@ const post = (arg: Message): void => { window.top?.postMessage(JSON.stringify({ id: generateId(), ...arg }), '*'); }; -export { post }; +const isReplyFromMessenger = (json: unknown): json is MessengerResponse => { + const reply = json as MessengerResponse; + return ( + 'result' in reply && + typeof reply.result === 'string' && + 'id' in reply && + typeof reply.id === 'string' + ); +}; + +const decodeReply = (e: MessageEvent): MessengerResponse | void => { + try { + const json: unknown = JSON.parse(e.data); + + if (isReplyFromMessenger(json)) { + return json; + } + + return; + } catch (_) { + return; + } +}; + +const postAndListen = (arg: Message): Promise => + timeout( + new Promise((resolve) => { + const id = generateId(); + + const listener = (e: MessageEvent) => { + const decoded = decodeReply(e); + if (decoded && decoded.id === id) { + resolve(decoded.result); + window.removeEventListener('message', listener); + } + }; + + window.addEventListener('message', listener); + + post({ ...arg, id }); + }), + 3000, + ); + +export { post, timeout, postAndListen, generateId }; export type { Message }; diff --git a/src/lib/messenger/get-page-url.ts b/src/lib/messenger/get-page-url.ts new file mode 100644 index 00000000..5cf34ce2 --- /dev/null +++ b/src/lib/messenger/get-page-url.ts @@ -0,0 +1,7 @@ +import { postAndListen } from '$lib/messenger'; + +const getPageURL = () => { + return postAndListen({ type: 'get-page-url', value: '' }); +}; + +export { getPageURL }; diff --git a/src/lib/public-good.ts b/src/lib/public-good.ts new file mode 100644 index 00000000..7688a0f8 --- /dev/null +++ b/src/lib/public-good.ts @@ -0,0 +1,34 @@ +/** + * https://assets.publicgood.com/docs/gen/pgmApi.html#create__anchor + */ +export interface PgmApiOptions { + partnerId: string; + attributes: { + targetId?: string; + targetType?: string; + url: string; + }; + onShow?: () => void; + onHide?: () => void; +} + +/** + * https://assets.publicgood.com/docs/gen/global.html#MatchInfo + */ +interface MatchInfo { + action: string; + cpm: number; + partner_id: string; + campaign_id: string; +} + +export interface PgmApi { + create: (el: HTMLElement, options: PgmApiOptions) => void; + getAdvice(options: PgmApiOptions): Promise; +} + +export const create = (el: HTMLElement, options: PgmApiOptions) => { + const win = window as typeof window & { pgmApi: PgmApi }; + + win.pgmApi.create(el, options); +}; diff --git a/src/lib/rollup.ts b/src/lib/rollup.ts index 33a06597..9411d731 100644 --- a/src/lib/rollup.ts +++ b/src/lib/rollup.ts @@ -9,10 +9,6 @@ import { terser } from 'rollup-plugin-terser'; import preprocess from 'svelte-preprocess'; import type { Props } from './svelte'; -const performancescript = `window.performance.mark('svelteEnd'); -const measure = window.performance.measure('svelte', 'svelteStart','svelteEnd'); -document.querySelector("#metrics").innerText = \`\${measure.duration.toFixed(2)}ms\`;`; - const virtual = (template: string, props: Props): Plugin => ({ name: 'virtual-template', resolveId: (source: string) => { @@ -28,11 +24,10 @@ const virtual = (template: string, props: Props): Plugin => ({ } if (id === 'dom') { return `import Template from "./src/templates/csr/${template}/index.svelte"; -window.performance.mark('svelteStart'); new Template({ target: document.querySelector('#svelte'), props: ${JSON.stringify(props)}, -});${performancescript}`; +});`; } return null; }, diff --git a/src/routes/csr/[template].json.ts b/src/routes/csr/[template].json.ts index 525d8507..b7994305 100644 --- a/src/routes/csr/[template].json.ts +++ b/src/routes/csr/[template].json.ts @@ -70,7 +70,6 @@ export const GET: RequestHandler = async ({ params }) => { const html = [ ``, `
`, - `
`, ``, ].join('\n'); diff --git a/src/templates/components/Card.svelte b/src/templates/components/CapiCard.svelte similarity index 100% rename from src/templates/components/Card.svelte rename to src/templates/components/CapiCard.svelte diff --git a/src/templates/components/EventsHeader.svelte b/src/templates/components/EventsHeader.svelte new file mode 100644 index 00000000..555c1965 --- /dev/null +++ b/src/templates/components/EventsHeader.svelte @@ -0,0 +1,114 @@ + + + + +
+ + + + + +
+ + diff --git a/src/templates/components/ManualCard.svelte b/src/templates/components/ManualCard.svelte new file mode 100644 index 00000000..ef026052 --- /dev/null +++ b/src/templates/components/ManualCard.svelte @@ -0,0 +1,162 @@ + + + + + +
+ + + +
+
+

{boldTitle}{regularTitle}

+

{EventDateTime}

+
+
+ Book tickets + + + + + diff --git a/src/templates/components/icons/ArrowRight.svelte b/src/templates/components/icons/ArrowRight.svelte index 4fc53882..bb962003 100644 --- a/src/templates/components/icons/ArrowRight.svelte +++ b/src/templates/components/icons/ArrowRight.svelte @@ -3,7 +3,13 @@ export let flip = false; - + + + + + + + + + + + + + + + + diff --git a/src/templates/components/icons/OtherLogos.svelte b/src/templates/components/icons/OtherLogos.svelte index f698f23d..bd10b7e6 100644 --- a/src/templates/components/icons/OtherLogos.svelte +++ b/src/templates/components/icons/OtherLogos.svelte @@ -148,67 +148,6 @@ > - - - - - + {:catch} diff --git a/src/templates/csr/events-multiple/index.svelte b/src/templates/csr/events-multiple/index.svelte new file mode 100644 index 00000000..4b4f19f9 --- /dev/null +++ b/src/templates/csr/events-multiple/index.svelte @@ -0,0 +1,98 @@ + + + + + + diff --git a/src/templates/csr/events-multiple/test.json b/src/templates/csr/events-multiple/test.json new file mode 100644 index 00000000..c38c524e --- /dev/null +++ b/src/templates/csr/events-multiple/test.json @@ -0,0 +1,22 @@ +{ + "BannerDescription": "Hear from leading thinkers and award-winning journalists in livestreamed and interactive events", + "HeaderButtonText": "Browse all", + "HeaderButtonUrl": "https://www.theguardian.com/guardian-masterclasses", + "NumberOfCards": 4, + "EventTitle1": "Carlo Rovelli meets Dara Ó Briain: Live in London and online", + "EventDateTime1": "Monday 30 October 2023, 7pm–8.30pm GMT", + "EventImage1": "https://media.guim.co.uk/a2a1c4db8beab56c032b81def3ba41eab148a6ea/0_0_1200_720/1200.jpg", + "EventUrl1": "https://membership.theguardian.com/live/carlo-rovelli-meets-dara-o-briain-live-in-london-and-online-687195088007", + "EventTitle2": "Guardian Newsroom: How will the US presidential race unfold?", + "EventDateTime2": "Thursday 2 November 2023, 8pm–9.15pm GMT", + "EventImage2": "https://media.guim.co.uk/1b3edde6a42aec60a8f05930bb17784b7478697d/0_260_3900_2340/3900.jpg", + "EventUrl2": "https://membership.theguardian.com/live/guardian-newsroom-how-will-the-us-presidential-race-unfold-715912903767", + "EventTitle3": "John Crace meets Ian Hislop", + "EventDateTime3": "Monday 6 November 2023, 8pm–9.15pm GMT", + "EventImage3": "https://media.guim.co.uk/5b5f3a8ff2eb9532d3c467fb940755aa2bc3e718/0_1_1200_720/1200.jpg", + "EventUrl3": "https://membership.theguardian.com/live/john-crace-meets-ian-hislop-685572585057", + "EventTitle4": "In conversation with Afua Hirsch", + "EventDateTime4": "Wednesday 8 November 2023, 8pm–9pm GMT", + "EventImage4": "https://media.guim.co.uk/3ce77c6fd861c52d0c6e5b14f3cd8f4b48b9057a/0_475_3333_2000/3333.jpg", + "EventUrl4": "https://membership.theguardian.com/live/in-conversation-with-afua-hirsch-722077582487" +} diff --git a/src/templates/csr/public-good/index.svelte b/src/templates/csr/public-good/index.svelte new file mode 100644 index 00000000..67192337 --- /dev/null +++ b/src/templates/csr/public-good/index.svelte @@ -0,0 +1,55 @@ + + + + + +
+ + diff --git a/src/templates/ssr/interscroller/index.ts b/src/templates/ssr/interscroller/index.ts index 6eb3d27c..1a50aee5 100644 --- a/src/templates/ssr/interscroller/index.ts +++ b/src/templates/ssr/interscroller/index.ts @@ -15,6 +15,7 @@ post({ backgroundPosition: 'center center', backgroundSize: 'cover', ctaUrl: `%%CLICK_URL_UNESC%%%%DEST_URL%%`, + videoSource: `[%VideoSource%]`, }, });