diff --git a/404.html b/404.html index 94288e04da..159f44a7ee 100644 --- a/404.html +++ b/404.html @@ -18,6 +18,42 @@ + - diff --git a/scripts/aem.js b/scripts/aem.js index ec275180fb..825d2e680e 100644 --- a/scripts/aem.js +++ b/scripts/aem.js @@ -503,20 +503,35 @@ function decorateSections(main) { */ // eslint-disable-next-line import/prefer-default-export async function fetchPlaceholders(prefix = 'default') { + const overrides = getMetadata('placeholders') || ''; + const [fallback, override] = overrides.split('\n'); window.placeholders = window.placeholders || {}; if (!window.placeholders[prefix]) { window.placeholders[prefix] = new Promise((resolve) => { - const url = getMetadata('placeholders') || `${prefix === 'default' ? '' : prefix}/placeholders.json`; - fetch(url) - .then((resp) => { + const url = fallback || `${prefix === 'default' ? '' : prefix}/placeholders.json`; + Promise.all([fetch(url), override ? fetch(override) : Promise.resolve()]) + .then(async ([resp, oResp]) => { if (resp.ok) { - return resp.json(); + if (oResp?.ok) { + return Promise.all([resp.json(), await oResp.json()]); + } + return Promise.all([resp.json(), {}]); } return {}; }) - .then((json) => { + + .then(([json, oJson]) => { const placeholders = {}; + // build placeholders object json.data.forEach(({ Key, Value }) => { + // check for overrides + if (oJson?.data) { + const overrideItem = oJson.data.find((item) => item.Key === Key); + if (overrideItem) { + // eslint-disable-next-line no-param-reassign + Value = overrideItem.Value; + } + } if (Key) { const keys = Key.split('.'); const lastKey = keys.pop(); @@ -527,10 +542,13 @@ async function fetchPlaceholders(prefix = 'default') { target[lastKey] = Value; } }); + // cache placeholders window.placeholders[prefix] = placeholders; + // return placeholders resolve(window.placeholders[prefix]); }) - .catch(() => { + .catch((error) => { + console.error('error loading placeholders', error); // error loading placeholders window.placeholders[prefix] = {}; resolve(window.placeholders[prefix]); diff --git a/scripts/configs.js b/scripts/configs.js index 93e69e2cbe..95dd232739 100644 --- a/scripts/configs.js +++ b/scripts/configs.js @@ -1,3 +1,7 @@ +/* eslint-disable import/no-cycle */ + +import { getRootPath } from './scripts.js'; + const ALLOWED_CONFIGS = ['prod', 'stage', 'dev']; /** @@ -29,21 +33,22 @@ export const calcEnvironment = () => { return environment; }; -function buildConfigURL(environment) { +function buildConfigURL(environment, root = '/') { const env = environment || calcEnvironment(); let fileName = 'configs.json'; if (env !== 'prod') { fileName = `configs-${env}.json`; } - const configURL = new URL(`${window.location.origin}/${fileName}`); + const configURL = new URL(`${window.location.origin}${root}${fileName}`); return configURL; } const getConfigForEnvironment = async (environment) => { const env = environment || calcEnvironment(); + const root = getRootPath() || '/'; try { - const configJSON = window.sessionStorage.getItem(`config:${env}`); + const configJSON = window.sessionStorage.getItem(`config:${env}:${root}`); if (!configJSON) { throw new Error('No config in session storage'); } @@ -55,13 +60,13 @@ const getConfigForEnvironment = async (environment) => { return parsedConfig; } catch (e) { - let configJSON = await fetch(buildConfigURL(env)); + let configJSON = await fetch(buildConfigURL(env, root)); if (!configJSON.ok) { throw new Error(`Failed to fetch config for ${env}`); } configJSON = await configJSON.json(); configJSON[':expiry'] = Math.round(Date.now() / 1000) + 7200; - window.sessionStorage.setItem(`config:${env}`, JSON.stringify(configJSON)); + window.sessionStorage.setItem(`config:${env}:${root}`, JSON.stringify(configJSON)); return configJSON; } }; @@ -87,13 +92,21 @@ export const getConfigValue = async (configParam, environment) => { export const getHeaders = async (scope, environment) => { const env = environment || calcEnvironment(); const config = await getConfigForEnvironment(env); - const configElements = config.data.filter((el) => el?.key.includes(`headers.${scope}`)); + const configElements = config.data.filter((el) => el?.key.includes('headers.all') || el?.key.includes(`headers.${scope}`)); return configElements.reduce((obj, item) => { let { key } = item; + + // global values + if (key.includes('commerce.headers.all.')) { + key = key.replace('commerce.headers.all.', ''); + } + + // scoped values if (key.includes(`commerce.headers.${scope}.`)) { key = key.replace(`commerce.headers.${scope}.`, ''); } + return { ...obj, [key]: item.value }; }, {}); }; diff --git a/scripts/constants.js b/scripts/constants.js index 31c9a3a26b..8bdf14426a 100644 --- a/scripts/constants.js +++ b/scripts/constants.js @@ -1,14 +1,16 @@ -export const SUPPORT_PATH = '/support'; +import { localizeLink } from './scripts.js'; + +export const SUPPORT_PATH = localizeLink('/support'); // GUEST -export const ORDER_STATUS_PATH = '/order-status'; -export const ORDER_DETAILS_PATH = '/order-details'; -export const RETURN_DETAILS_PATH = '/return-details'; -export const CREATE_RETURN_PATH = '/create-return'; -export const SALES_GUEST_VIEW_PATH = '/sales/guest/view/'; +export const ORDER_STATUS_PATH = localizeLink('/order-status'); +export const ORDER_DETAILS_PATH = localizeLink('/order-details'); +export const RETURN_DETAILS_PATH = localizeLink('/return-details'); +export const CREATE_RETURN_PATH = localizeLink('/create-return'); +export const SALES_GUEST_VIEW_PATH = localizeLink('/sales/guest/view/'); // CUSTOMER -export const CUSTOMER_PATH = '/customer'; +export const CUSTOMER_PATH = localizeLink('/customer'); export const CUSTOMER_ORDER_DETAILS_PATH = `${CUSTOMER_PATH}${ORDER_DETAILS_PATH}`; export const CUSTOMER_RETURN_DETAILS_PATH = `${CUSTOMER_PATH}${RETURN_DETAILS_PATH}`; export const CUSTOMER_CREATE_RETURN_PATH = `${CUSTOMER_PATH}${CREATE_RETURN_PATH}`; @@ -18,7 +20,7 @@ export const CUSTOMER_ADDRESS_PATH = `${CUSTOMER_PATH}/address`; export const CUSTOMER_LOGIN_PATH = `${CUSTOMER_PATH}/login`; export const CUSTOMER_ACCOUNT_PATH = `${CUSTOMER_PATH}/account`; export const CUSTOMER_FORGOTPASSWORD_PATH = `${CUSTOMER_PATH}/forgotpassword`; -export const SALES_ORDER_VIEW_PATH = '/sales/order/view/'; +export const SALES_ORDER_VIEW_PATH = localizeLink('/sales/order/view/'); // TRACKING export const UPS_TRACKING_URL = 'https://www.ups.com/track'; diff --git a/scripts/initializers/pdp.js b/scripts/initializers/pdp.js index 2b5223e26f..8871141599 100644 --- a/scripts/initializers/pdp.js +++ b/scripts/initializers/pdp.js @@ -1,5 +1,5 @@ +/* eslint-disable import/no-cycle */ /* eslint-disable import/prefer-default-export */ -/* eslint import/no-cycle: [2, { maxDepth: 1 }] */ import { initializers } from '@dropins/tools/initializer.js'; import { Image, provider as UI } from '@dropins/tools/components.js'; diff --git a/scripts/scripts.js b/scripts/scripts.js index d2edf055ec..f8d8798728 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -150,6 +150,7 @@ export function decorateMain(main) { buildAutoBlocks(main); decorateSections(main); decorateBlocks(main); + // decorateLinks(main); // author can decide when to localize links } function preloadFile(href, as) { @@ -356,6 +357,51 @@ export async function fetchIndex(indexFile, pageSize = 500) { return newIndex; } +/** + * Get root path + */ +export function getRootPath() { + window.ROOT_PATH = window.ROOT_PATH || getMetadata('root') || '/'; + return window.ROOT_PATH; +} + +/** + * Decorates links. + * @param {Element} main The main element + */ +export function decorateLinks(main) { + const root = getRootPath(); + if (root === '/') return; + const links = main.querySelectorAll('a'); + + links.forEach((link) => { + const url = new URL(link.href, window.location.origin); + const { pathname } = url; + + // If the link is already localized, do nothing + if (pathname.startsWith('//') || pathname.startsWith(root)) return; + + if ( + link.href.startsWith('/') + || link.href.startsWith(window.location.origin) + ) { + link.href = `${root}${url.pathname.substring(1)}${url.search}${url.hash}`; + } + }); +} + +/** + * Decorates links. + * @param {string} [url] url to be localized + */ +export function localizeLink(link) { + const root = getRootPath().replace(/\/$/, ''); + + // If the link is already localized, do nothing + if (link.startsWith(root)) return link; + return `${root}${link}`; +} + /** * Check if consent was given for a specific topic. * @param {*} topic Topic identifier