diff --git a/.kodiak/config.yaml b/.kodiak/config.yaml
index 89cf03ff..645bbf45 100644
--- a/.kodiak/config.yaml
+++ b/.kodiak/config.yaml
@@ -9,7 +9,7 @@ notifications:
project: MWPW # Mandatory
filters:
include:
- risk_rating: R5
+ risk_rating: R3
exclude:
files:
- acrobat/blocks/acom-widget/acom-widget.js
@@ -17,13 +17,11 @@ notifications:
fields:
assignee:
name: joaquinrivero
- customfield_11800: MWPW-140779 #epic link
+ customfield_11800: MWPW-164516 #epic link
customfield_12900:
value: Slytherin
watchers:
- casalino
- - jmichnow
- - mauchley
- tsay
labels:
- "OriginatingProcess=Kodiak"
diff --git a/acrobat/blocks/rnr/rnr.js b/acrobat/blocks/rnr/rnr.js
index 36964089..d0a4427e 100644
--- a/acrobat/blocks/rnr/rnr.js
+++ b/acrobat/blocks/rnr/rnr.js
@@ -498,7 +498,7 @@ export default async function init(element) {
window.lana?.log('Verb not configured for the rnr widget');
}
preloadIcons();
- await loadPlaceholders();
+ await loadPlaceholders('rnr');
await loadRnrData();
initControls(element);
}
diff --git a/acrobat/blocks/verb-widget/verb-widget.css b/acrobat/blocks/verb-widget/verb-widget.css
index 5f86c16f..6b7721bc 100644
--- a/acrobat/blocks/verb-widget/verb-widget.css
+++ b/acrobat/blocks/verb-widget/verb-widget.css
@@ -62,6 +62,7 @@
display: flex;
margin: unset;
margin-right: 15px;
+ margin-left: 18px;
}
.verb-footer {
@@ -118,7 +119,7 @@
flex-direction: column;
justify-content: center;
border-radius: 20px;
- border: 3px solid #d5d5d5;
+ border: 3px dashed #d5d5d5;
position: relative;
background-color: #fff;
min-height: 560px;
@@ -242,7 +243,7 @@
.verb-errorIcon::after {
content: '';
- background-image: url(/acrobat/img/icons/ui/alert.svg);
+ background-image: url('/acrobat/img/icons/ui/alert.svg');
background-repeat: no-repeat;
display: flex;
align-items: center;
@@ -362,8 +363,6 @@
flex: 1 1 55%;
flex-direction: row;
padding: 48px;
-
- /* cursor: pointer; */
}
}
@@ -398,24 +397,117 @@
border-radius: 10px;
}
-@media screen and (max-width: 768px) {
+@media screen and (min-width: 768px) {
+ :root {
+ --verb-widget-padding: 24px;
+ --verb-wrapper-padding-y: 57px;
+ --verb-wrapper-padding-x: 39px;
+ --verb-wrapper-border-width: 3px;
+ }
+
+ .verb-widget {
+ padding: var(--verb-widget-padding);
+ min-height: auto;
+ }
+
+ .verb-wrapper.tablet {
+ width: unset;
+ max-width: unset;
+ padding: var(--verb-wrapper-padding-y) var(--verb-wrapper-padding-x);
+ border: var(--verb-wrapper-border-width) dashed #d5d5d5;
+ border-radius: 8px;
+ justify-content: flex-start;
+ min-height: auto;
+ }
+
+ .tablet .verb-container {
+ padding: unset;
+ }
+
+ .tablet .verb-title {
+ font-size: 27px;
+ }
+
+ .tablet .verb-row {
+ gap: 38px;
+ }
+
+ .tablet .verb-col {
+ margin: 10px;
+ margin-bottom: 0;
+ }
+
+ .tablet .verb-col.right {
+ margin: 0;
+ }
+
+ .tablet .verb-heading {
+ font-size: 36px;
+ line-height: 45px;
+ }
+
+ .tablet .verb-copy {
+ font-size: 18px;
+ line-height: 27px;
+ margin-bottom: 32px;
+ }
+
+ .tablet .verb-cta {
+ cursor: pointer;
+ font-size: 23px;
+ line-height: 29px;
+ padding: 11px 25px;
+ }
+}
+@media screen and (min-width: 1000px) and (max-width: 1024px) {
+ :root {
+ --verb-widget-padding: 24px;
+ --verb-wrapper-padding-y: 57px;
+ --verb-wrapper-padding-x: 23px;
+ --verb-wrapper-border-width: 3px;
+ }
+
+ .verb-widget {
+ padding: var(--verb-widget-padding);
+ min-height: auto;
+ }
+
+ .verb-wrapper.tablet {
+ width: unset;
+ max-width: unset;
+ padding: var(--verb-wrapper-padding-y) var(--verb-wrapper-padding-x);
+ border: var(--verb-wrapper-border-width) dashed #d5d5d5;
+ border-radius: 8px;
+ justify-content: flex-start;
+ min-height: auto;
+ }
+}
+
+@media screen and (max-width: 767px) {
+ :root {
+ --verb-widget-padding: 16px;
+ --verb-wrapper-padding-y: 60px;
+ --verb-wrapper-padding-x: 29px;
+ --verb-wrapper-border-width: 3px;
+ }
+
.verb-widget {
background-image: linear-gradient(180deg, #EB1000 0%, #F79B94 50%, #FFF 70%);
- padding: 16px;
+ padding: var(--verb-widget-padding);
}
.verb-wrapper {
width: unset;
max-width: unset;
- padding: 24px 29px;
- border: 3px dashed #d5d5d5;
+ padding: var(--verb-wrapper-padding-y) var(--verb-wrapper-padding-x);
+ border: var(--verb-wrapper-border-width) dashed #d5d5d5;
border-radius: 8px;
- min-height: calc(100vh - (104px + 32px + 115px + 47px + 7px));
+ min-height: calc(100vh - (104px + 115px + ((var(--verb-widget-padding) + var(--verb-wrapper-padding-y) + var(--verb-wrapper-border-width)) * 2)) );
justify-content: flex-start;
}
.verb-wrapper.mobile-app {
- min-height: calc(100vh - (104px + 32px + 0px + 47px + 7px))
+ min-height: calc(100vh - (104px + ((16px + 3px) * 2) + 36px + 84px));
}
.verb-row {
diff --git a/acrobat/blocks/verb-widget/verb-widget.js b/acrobat/blocks/verb-widget/verb-widget.js
index 23d766d1..a9c5875a 100644
--- a/acrobat/blocks/verb-widget/verb-widget.js
+++ b/acrobat/blocks/verb-widget/verb-widget.js
@@ -1,6 +1,6 @@
import LIMITS from './limits.js';
import { setLibs, getEnv, isOldBrowser } from '../../scripts/utils.js';
-import verbAnalytics from '../../scripts/alloy/verb-widget.js';
+import verbAnalytics, { reviewAnalytics } from '../../scripts/alloy/verb-widget.js';
import createSvgElement from './icons.js';
const miloLibs = setLibs('/libs');
@@ -82,6 +82,38 @@ function handleExit(event) {
event.returnValue = true;
}
+function isMobileDevice() {
+ const ua = navigator.userAgent.toLowerCase();
+ const isMobileUA = /android|iphone|ipod|blackberry|windows phone/i.test(ua);
+ return isMobileUA;
+}
+
+function isTabletDevice() {
+ const ua = navigator.userAgent.toLowerCase();
+ const isIPadOS = navigator.userAgent.includes('Mac') && 'ontouchend' in document && !/iphone|ipod/i.test(ua);
+ const isTabletUA = /ipad|android(?!.*mobile)/i.test(ua);
+ const largeTouchDevice = (navigator.maxTouchPoints || navigator.msMaxTouchPoints) > 1
+ && window.innerWidth >= 768;
+ return isIPadOS || isTabletUA || largeTouchDevice;
+}
+
+function getStoreType() {
+ const { ua } = window.browser;
+ if (/android/i.test(ua)) {
+ return 'google';
+ }
+ if (/iphone|ipod/i.test(ua)) {
+ return 'apple';
+ }
+ if (navigator.userAgent.includes('Mac') && 'ontouchend' in document && !/iphone|ipod/i.test(navigator.userAgent)) {
+ return 'apple';
+ }
+ if (/ipad/i.test(ua)) {
+ return 'apple';
+ }
+ return 'desktop';
+}
+
async function showUpSell(verb, element) {
const headline = window.mph[`verb-widget-upsell-headline-${verb}`] || window.mph['verb-widget-upsell-headline'];
const headlineNopayment = window.mph['verb-widget-upsell-headline-nopayment'];
@@ -133,11 +165,10 @@ export default async function init(element) {
const children = element.querySelectorAll(':scope > div');
const VERB = element.classList[1];
const widgetHeading = createTag('h1', { class: 'verb-heading' }, children[0].textContent);
+ const storeType = getStoreType();
let mobileLink = null;
- if (/iPad|iPhone|iPod/.test(window.browser?.ua) && !window.MSStream) {
- mobileLink = window.mph[`verb-widget-${VERB}-apple`];
- } else if (/android/i.test(window.browser?.ua)) {
- mobileLink = window.mph[`verb-widget-${VERB}-google`];
+ if (storeType !== 'desktop') {
+ mobileLink = window.mph[`verb-widget-${VERB}-${storeType}`];
}
children.forEach((child) => {
@@ -215,29 +246,45 @@ export default async function init(element) {
widgetRow.append(widgetLeft, widgetRight);
widgetHeader.append(widgetIcon, widgetTitle);
errorState.append(errorIcon, errorStateText, errorCloseBtn);
+ const isMobile = isMobileDevice();
+ const isTablet = isTabletDevice();
+
+ if (isMobile) {
+ widget.classList.add('mobile');
+ } else if (isTablet) {
+ widget.classList.add('tablet');
+ }
+
if (mobileLink && LIMITS[VERB].mobileApp) {
widget.classList.add('mobile-app');
widgetLeft.append(widgetHeader, widgetHeading, widgetMobCopy, errorState, widgetMobileButton);
element.append(widget);
} else {
- widgetLeft.append(widgetHeader, widgetHeading, widgetCopy, errorState, widgetButton, button);
+ if (isMobile || isTablet) {
+ widgetLeft.append(
+ widgetHeader,
+ widgetHeading,
+ widgetMobCopy,
+ errorState,
+ widgetButton,
+ button,
+ );
+ } else {
+ widgetLeft.append(
+ widgetHeader,
+ widgetHeading,
+ widgetCopy,
+ errorState,
+ widgetButton,
+ button,
+ );
+ }
legalTwo.innerHTML = legalTwo.outerHTML.replace(window.mph['verb-widget-terms-of-use'], ` ${window.mph['verb-widget-terms-of-use']}`);
legalTwo.innerHTML = legalTwo.outerHTML.replace(window.mph['verb-widget-privacy-policy'], ` ${window.mph['verb-widget-privacy-policy']}`);
legalWrapper.append(legal, legalTwo);
footer.append(iconSecurity, legalWrapper, infoIcon);
element.append(widget, footer);
- if (window.browser?.isMobile) {
- widgetCopy.after(widgetMobCopy);
- widgetCopy.remove();
- const infoMobIconSvg = createSvgElement('INFO_ICON_MOBILE');
- infoIconSvg.remove();
- infoIcon.append(infoMobIconSvg);
- const verbMobImageSvg = createSvgElement(`${VERB}-mobile`);
- if (verbMobImageSvg) {
- verbImageSvg.remove();
- verbMobImageSvg.classList.add('icon-verb-image');
- widgetImage.appendChild(verbMobImageSvg);
- }
+ if (isMobile && !isTablet) {
widgetImage.after(widgetImage);
iconSecurity.remove(iconSecurity);
footer.prepend(infoIcon);
@@ -271,6 +318,7 @@ export default async function init(element) {
// Analytics
verbAnalytics('landing:shown', VERB);
+ reviewAnalytics(VERB);
window.prefetchInitiated = false;
diff --git a/acrobat/scripts/alloy/verb-widget.js b/acrobat/scripts/alloy/verb-widget.js
index d8342fa6..19bac3d9 100644
--- a/acrobat/scripts/alloy/verb-widget.js
+++ b/acrobat/scripts/alloy/verb-widget.js
@@ -1,3 +1,5 @@
+import frictionless from '../frictionless.js';
+
const params = new Proxy(
// eslint-disable-next-line compat/compat
new URLSearchParams(window.location.search),
@@ -23,15 +25,16 @@ if (params.dropzone2) {
appTags.push('dropzone2');
}
-export default function init(eventName, verb, metaData, documentUnloading = true) {
- function ensureSatelliteReady(callback) {
- // eslint-disable-next-line no-underscore-dangle
- if (window._satellite && typeof window._satellite.track === 'function') {
- callback();
- } else {
- setTimeout(() => ensureSatelliteReady(callback), 200);
- }
+function ensureSatelliteReady(callback) {
+ // eslint-disable-next-line no-underscore-dangle
+ if (window._satellite?.track instanceof Function) {
+ callback();
+ } else {
+ setTimeout(() => ensureSatelliteReady(callback), 200);
}
+}
+
+export default function init(eventName, verb, metaData, documentUnloading = true) {
function getSessionID() {
const aToken = window.adobeIMS.getAccessToken();
const arrayToken = aToken?.token.split('.');
@@ -135,3 +138,9 @@ export default function init(eventName, verb, metaData, documentUnloading = true
window._satellite.track('event', event);
});
}
+
+export function reviewAnalytics(verb) {
+ ensureSatelliteReady(() => {
+ frictionless(verb);
+ });
+}
diff --git a/acrobat/scripts/contentSecurityPolicy/csp.js b/acrobat/scripts/contentSecurityPolicy/csp.js
index 8d45df6a..30712a67 100644
--- a/acrobat/scripts/contentSecurityPolicy/csp.js
+++ b/acrobat/scripts/contentSecurityPolicy/csp.js
@@ -50,6 +50,6 @@ export default async function ContentSecurityPolicy() {
// Content Security Policy Logging
window.cspErrors = [];
document.addEventListener('securitypolicyviolation', (e) => {
- window.cspErrors.push(`${e.violatedDirective} violation ¶ Refused to load content from ${e.blockedURI}`);
+ window.cspErrors.push(`${e.violatedDirective} violation ¶ Refused to load content from ${e.blockedURI}, Script location: ${e.sourceFile} Line: ${e.lineNumber} Column: ${e.columnNumber}`);
});
}
diff --git a/acrobat/scripts/contentSecurityPolicy/dev.js b/acrobat/scripts/contentSecurityPolicy/dev.js
index c21838bc..452f5db7 100644
--- a/acrobat/scripts/contentSecurityPolicy/dev.js
+++ b/acrobat/scripts/contentSecurityPolicy/dev.js
@@ -121,6 +121,7 @@ const imgSrc = [
'stage.adobeccstatic.com',
'*.clarity.ms',
'*.enterprise.adobe.com',
+ 'www.stage.adobe.com',
'*.services.adobe.com',
'alb.reddit.com/rp.gif',
'bat.bing.com/action/',
@@ -168,6 +169,7 @@ const scriptSrc = [
'\'self\'',
'\'unsafe-inline\'',
'\'unsafe-eval\'',
+ 'CCb6zi09JRQ6b1z1',
'*.adobe.com',
'*.clarity.ms',
'accounts.google.com/gsi/client',
diff --git a/acrobat/scripts/contentSecurityPolicy/prod.js b/acrobat/scripts/contentSecurityPolicy/prod.js
index 5e47978f..dc0a74ac 100644
--- a/acrobat/scripts/contentSecurityPolicy/prod.js
+++ b/acrobat/scripts/contentSecurityPolicy/prod.js
@@ -8,6 +8,7 @@ const connectSrc = [
'\'self\'',
'blob:',
'14257-chimera.adobeioruntime.net',
+ 'www.adobe.com',
'*.adobe.com',
'prod.adobeccstatic.com',
'*.clicktale.net/',
@@ -114,6 +115,7 @@ const frameSrc = [
'ui.messaging.adobe.com/',
'acrobatservices.adobe.com',
'auth-light.identity.adobe.com',
+ 'pixel.everesttech.net',
';',
];
@@ -181,6 +183,7 @@ const scriptSrc = [
'\'self\'',
'\'unsafe-inline\'',
'\'unsafe-eval\'',
+ 'www.adobe.com',
'*.adobe.com',
'*.clarity.ms',
'accounts.google.com/gsi/client',
diff --git a/acrobat/scripts/contentSecurityPolicy/stage.js b/acrobat/scripts/contentSecurityPolicy/stage.js
index 981227bd..dda83a75 100644
--- a/acrobat/scripts/contentSecurityPolicy/stage.js
+++ b/acrobat/scripts/contentSecurityPolicy/stage.js
@@ -27,6 +27,7 @@ const connectSrc = [
'adobeioruntime.net',
'adobesearch-stage.adobe.io',
'analytics.tiktok.com',
+ 'api.company-target.com',
'api.company-target.com/api/v2/',
'api.iperceptions.com',
'bat.bing.com/',
@@ -55,6 +56,7 @@ const connectSrc = [
'*.aem.live',
'cdn.linkedin.oribi.io',
'*.akstat.io/',
+ 'www.facebook.com',
'facebook.com',
'px.ads.linkedin.com',
'tr6.snapchat.com',
@@ -180,6 +182,7 @@ const scriptSrc = [
'\'self\'',
'\'unsafe-inline\'',
'\'unsafe-eval\'',
+ 'www.stage.adobe.com',
'*.adobe.com',
'*.clarity.ms',
'accounts.google.com/gsi/client',
@@ -234,7 +237,7 @@ const scriptSrc = [
'tr.snapchat.com/',
'universal.iperceptions.com',
'use.typekit.net',
- 'www.everestjs.net/static/le/',
+ 'www.everestjs.net',
'www.facebook.com',
'www.google.com',
'www.googletagmanager.com',
diff --git a/acrobat/scripts/utils.js b/acrobat/scripts/utils.js
index 6676304e..deb5eb0f 100644
--- a/acrobat/scripts/utils.js
+++ b/acrobat/scripts/utils.js
@@ -34,42 +34,57 @@ export const [setLibs, getLibs] = (() => {
return `https://${branch}${branch.includes('--') ? '' : '--milo--adobecom'}.${env}.live/libs`;
})();
return libs;
- }, () => libs,
+ },
+ () => libs,
];
})();
export function getEnv() {
const { hostname } = window.location;
if (['www.adobe.com', 'sign.ing', 'edit.ing'].includes(hostname)) return 'prod';
- if ([
- 'stage--dc--adobecom.hlx.page', 'main--dc--adobecom.hlx.page',
- 'stage--dc--adobecom.hlx.live', 'main--dc--adobecom.hlx.live',
- 'stage--dc--adobecom.aem.page', 'main--dc--adobecom.aem.page',
- 'stage--dc--adobecom.aem.live', 'main--dc--adobecom.aem.live',
- 'www.stage.adobe.com',
- ].includes(hostname)) return 'stage';
+ if (
+ [
+ 'stage--dc--adobecom.hlx.page',
+ 'main--dc--adobecom.hlx.page',
+ 'stage--dc--adobecom.hlx.live',
+ 'main--dc--adobecom.hlx.live',
+ 'stage--dc--adobecom.aem.page',
+ 'main--dc--adobecom.aem.page',
+ 'stage--dc--adobecom.aem.live',
+ 'main--dc--adobecom.aem.live',
+ 'www.stage.adobe.com',
+ ].includes(hostname)
+ ) return 'stage';
return 'dev';
}
export function isOldBrowser() {
const { name, version } = window?.browser || {};
return (
- name === 'Internet Explorer' || (name === 'Microsoft Edge' && (!version || version.split('.')[0] < 86)) || (name === 'Safari' && version.split('.')[0] < 14)
+ name === 'Internet Explorer'
+ || (name === 'Microsoft Edge' && (!version || version.split('.')[0] < 86))
+ || (name === 'Safari' && version.split('.')[0] < 14)
);
}
-export async function loadPlaceholders() {
+/**
+ * Loads placeholders, if SOME were not already loaded
+ * @param {string | undefined} prefix Optional prefix for loading specific placeholders
+ */
+export async function loadPlaceholders(prefix) {
const miloLibs = setLibs('/libs');
const { getConfig } = await import(`${miloLibs}/utils/utils.js`);
const config = getConfig();
- if (!Object.keys(window.mph || {}).length) {
+ const mphKeys = Object.keys(window.mph || {}).filter((key) => !prefix || key.startsWith(prefix));
+ if (mphKeys.length === 0) {
const placeholdersPath = `${config.locale.contentRoot}/placeholders.json`;
try {
const response = await fetch(placeholdersPath);
if (response.ok) {
const placeholderData = await response.json();
placeholderData.data.forEach(({ key, value }) => {
+ if (prefix && !key.startsWith(prefix)) return;
window.mph[key] = value.replace(/\u00A0/g, ' ');
});
}
diff --git a/test/scripts/contentSecurityPolicy/csp.test.js b/test/scripts/contentSecurityPolicy/csp.test.js
index 4271c364..3ea8d79f 100644
--- a/test/scripts/contentSecurityPolicy/csp.test.js
+++ b/test/scripts/contentSecurityPolicy/csp.test.js
@@ -1,9 +1,6 @@
import { expect } from '@esm-bundle/chai';
-import * as sinon from 'sinon';
-const { default: ContentSecurityPolicy } = await import(
- '../../../acrobat/scripts/contentSecurityPolicy/csp'
-);
+const { default: ContentSecurityPolicy } = await import('../../../acrobat/scripts/contentSecurityPolicy/csp.js');
describe('contentSecurityPolicy csp', () => {
it('handles securitypolicyviolation event', async () => {
@@ -13,7 +10,7 @@ describe('contentSecurityPolicy csp', () => {
event.violatedDirective = 'test';
document.dispatchEvent(event);
expect(window.cspErrors[0]).to.eql(
- `${event.violatedDirective} violation ¶ Refused to load content from ${event.blockedURI}`
+ `${event.violatedDirective} violation ¶ Refused to load content from ${event.blockedURI}, Script location: ${event.sourceFile} Line: ${event.lineNumber} Column: ${event.columnNumber}`,
);
});
});