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