Stylish enough for the city,
rugged enough for the trail.
${product.gender ? `${product.gender} ` : ""}${product.name}
${
product.price
? `
${setCurrencySymbol(product.price.currency)}
${product.price.amount}
`
: ""
}
`;
return slide;
};
const settings = {
siteId: config.siteId,
limit: config.limit ?? 15,
baseUrl:
config.baseUrl ??
"https://prod-fed-bff.orangehill-05b6c905.eastus.azurecontainerapps.io/products",
categoryId: config.categoryId,
includedFields: config.includedFields,
logData: config.logData ?? false,
swiperSlider: config.swiperSlider ?? false,
swiperConfig: config.swiperConfig ?? {},
};
const filterDuplicateProducts = (products) => {
return products.filter((product, index) => {
if (index === 0) return true;
const currentProduct = product.representedProduct;
const previousProduct = products[index - 1].representedProduct;
const currentName = product.productName;
const previousName = products[index - 1].productName;
const currentGender = currentProduct.c_genericSizeType;
const previousGender = previousProduct.c_genericSizeType;
return (
currentGender !== previousGender || currentName !== previousName
);
});
};
const getProductUrl = (productId) => {
const pathSegments = window.location.pathname
.split("/")
.filter((segment) => segment);
pathSegments[pathSegments.length - 1] = `${productId}.html`;
return `/${pathSegments.join("/")}`;
};
const findOrderableVariantImage = (product) => {
const orderableVariant = product.variants?.find(
(variant) => variant.orderable,
);
if (!orderableVariant) {
return (
product.imageGroups?.[0]?.images?.[0] || {
link: "",
alt: product.productName,
}
);
}
const variantColor = orderableVariant.variationValues?.color;
const matchingImageGroup = product.imageGroups?.find((group) => {
const colorAttr = group.variationAttributes?.find(
(attr) => attr.id === "color",
);
return colorAttr?.values?.[0]?.value === variantColor;
});
return (
matchingImageGroup?.images?.[0] ||
product.imageGroups?.[0]?.images?.[0] || {
link: "",
alt: product.productName,
}
);
};
const formatProductData = (product) => {
const formattedData = {
productId: product.productId,
url: getProductUrl(product.productId),
orderable: product.orderable ?? false,
};
if (settings.includedFields.includes("productName")) {
formattedData.name = product.productName;
}
if (
settings.includedFields.includes("gender") &&
product.representedProduct?.c_genericSizeType
) {
const genderType = product.representedProduct.c_genericSizeType;
formattedData.gender =
genderType === "M"
? "Men's"
: genderType === "W"
? "Women's"
: "Unisex";
}
if (settings.includedFields.includes("imageGroups")) {
const productImage = findOrderableVariantImage(product);
formattedData.image = {
src: productImage.link,
alt: productImage.alt,
};
}
if (settings.includedFields.includes("price")) {
console.log("Raw product price data:", {
price: product.price,
currency: product.currency,
priceMax: product.priceMax,
priceMin: product.priceMin,
salesPrice: product.salesPrice,
product,
});
formattedData.price = {
amount: product.priceMax || product.price,
currency: product.currency || "CAD",
};
}
if (
settings.includedFields.includes("ratings") &&
product.representedProduct
) {
formattedData.ratings = {
average:
parseFloat(product.representedProduct.c_bvAverageRating) || 0,
count: parseInt(product.representedProduct.c_bvReviewCount) || 0,
};
}
return formattedData;
};
const fetchProducts = async () => {
try {
const apiUrl = `${settings.baseUrl}/${settings.categoryId}?siteId=${settings.siteId}&limit=${settings.limit}`;
const response = await fetch(apiUrl);
const data = await response.json();
const filteredProducts = filterDuplicateProducts(data.hits);
const formattedProducts = filteredProducts.map(formatProductData);
if (settings.logData) {
console.group("Category Product Fetch Results");
console.log("Raw Data:", data.hits);
console.log("Filtered Products:", filteredProducts);
console.log("Formatted Products:", formattedProducts);
console.groupEnd();
}
return formattedProducts;
} catch (error) {
console.error("Error fetching products:", error);
return [];
}
};
const initializeSlider2 = async (containerSelector, options = {}) => {
if (!settings.swiperSlider) {
console.warn(
"Swiper slider is not enabled in the configuration. Add it with `swiperSlider: true`.",
);
return;
}
const products = await fetchProducts();
const container = document.querySelector(containerSelector);
if (!container) return;
const swiperWrapper = container.querySelector(".swiper-wrapper");
if (!swiperWrapper) return;
products.forEach((product) => {
const slide = createSlide(product);
swiperWrapper.appendChild(slide);
});
const swiperConfig = {
...defaultSwiperConfig,
...settings.swiperConfig,
...options,
};
new Swiper(containerSelector, swiperConfig);
};
return {
fetchProducts,
initializeSlider: initializeSlider2,
};
};
var category_product_fetch_default = createCategoryProductFetcher;
// 2025/05/MER/FED-21985 AKW - EMEA homepage redesign/scripts/lp-home-dynamic-speedarc-matis.js
function createDynamicTrailRunSlider(config = {}) {
const sliderConfig = {
// Element selectors
parentSelector: "#lp-home-dynamic-speedarc-matis-20250522",
swiperSelector: ".dynamic-product-swiper",
wrapperSelector: ".swiper-wrapper",
skeletonSelector: ".skeleton-loader",
nextButtonSelector: ".dynamic-swiper-button-next",
prevButtonSelector: ".dynamic-swiper-button-prev",
// Product data
categoryId: "speedarc-matis",
limit: 30,
// Swiper config
slidesPerViewMobile: 1.8,
spaceBetweenMobile: 13,
slidesPerViewDesktop: 4.5,
spaceBetweenDesktop: 35,
breakpoint: 641,
// Override with any provided config
...config,
};
const siteId = getSiteId();
console.log("Using siteId:", siteId);
const swiperConfig = createSwiperConfig(sliderConfig);
return {
config: sliderConfig,
siteId,
swiperConfig,
init: () => initializeSlider(sliderConfig, siteId, swiperConfig),
};
}
function getSiteId() {
let siteId = "merrell_uk";
if (window.dataLayer && Array.isArray(window.dataLayer)) {
for (let i = 0; i < window.dataLayer.length; i++) {
const item = window.dataLayer[i];
if (item && item.cd29_storeID) {
siteId = item.cd29_storeID;
break;
}
}
}
return siteId;
}
function createSwiperConfig(config) {
const parentSelector = config.parentSelector;
return {
slidesPerView: config.slidesPerViewMobile,
spaceBetween: config.spaceBetweenMobile,
navigation: {
nextEl: `${parentSelector} ${config.nextButtonSelector}`,
prevEl: `${parentSelector} ${config.prevButtonSelector}`,
},
breakpoints: {
[config.breakpoint]: {
slidesPerView: config.slidesPerViewDesktop,
spaceBetween: config.spaceBetweenDesktop,
},
},
};
}
function createCustomSlide(product, rawProduct, colorData, config = {}) {
const slide = document.createElement("div");
slide.className = "swiper-slide";
let colorName = colorData ? colorData.colorName : "";
let imageSrc = product.image?.src || "";
console.log(
"Creating slide with color:",
colorName,
"and image:",
imageSrc,
);
slide.innerHTML = `
`;
return slide;
}
function createProductFetcher(config, siteId) {
return category_product_fetch_default({
siteId,
categoryId: config.categoryId,
limit: config.limit,
logData: true,
// Show raw, filtered, and formatted data in the console
swiperSlider: false,
// Disable automatic swiper creation - we'll handle it manually
includedFields: [
"productId",
"productName",
"variants",
"imageGroups",
"colorName",
"url",
"image",
"gender",
],
});
}
function extractColorFromImageUrl(imageUrl, imageGroups) {
if (!imageUrl || !imageGroups || !imageGroups.length) return null;
const urlParts = imageUrl.split("/");
const imageFileName = urlParts[urlParts.length - 1];
const colorCodeMatch = imageFileName.match(/MRLM-(J\d+)/);
const colorCode = colorCodeMatch ? colorCodeMatch[1] : null;
if (!colorCode) return null;
for (const group of imageGroups) {
if (!group.variationAttributes) continue;
const colorAttr = group.variationAttributes.find(
(attr) => attr.id === "color",
);
if (!colorAttr || !colorAttr.values || !colorAttr.values.length)
continue;
const groupColorCode = colorAttr.values[0].value;
if (groupColorCode === colorCode) {
let colorName = "";
if (group.images && group.images.length > 0) {
const image = group.images[0];
if (image.title) {
const titleParts = image.title.split(", ");
if (titleParts.length > 1) {
colorName = titleParts[1];
}
}
}
return {
colorCode,
colorName,
imageGroup: group,
};
}
}
return null;
}
async function initializeSlider(config, siteId, swiperConfig) {
try {
const parentSelector = config.parentSelector;
const productFetcher = createProductFetcher(config, siteId);
const products = await productFetcher.fetchProducts();
const container = document.querySelector(
`${parentSelector} ${config.swiperSelector}`,
);
if (!container) {
console.error(
`Could not find the ${config.swiperSelector} container within ${parentSelector}`,
);
return;
}
const swiperWrapper = container.querySelector(config.wrapperSelector);
if (!swiperWrapper) {
console.error(
`Could not find the ${config.wrapperSelector} inside the container`,
);
return;
}
let rawProducts;
try {
const apiUrl = `https://prod-fed-bff.orangehill-05b6c905.eastus.azurecontainerapps.io/products/${config.categoryId}?siteId=${siteId}&limit=${config.limit}`;
const response = await fetch(apiUrl);
const data = await response.json();
rawProducts = data.hits || [];
} catch (fetchError) {
console.error("Error fetching raw product data:", fetchError);
rawProducts = productFetcher.getRawData() || [];
}
console.log("Fetched products:", products);
console.log("Raw products data:", rawProducts);
const productsByModel = /* @__PURE__ */ new Map();
rawProducts.forEach((rawProduct) => {
if (!rawProduct.imageGroups || rawProduct.imageGroups.length === 0)
return;
const modelName = rawProduct.productName;
if (!productsByModel.has(modelName)) {
productsByModel.set(modelName, {
rawProduct,
colorVariants: [],
});
}
const modelData = productsByModel.get(modelName);
for (let i = 1; i < rawProduct.imageGroups.length; i++) {
const group = rawProduct.imageGroups[i];
if (!group.variationAttributes) continue;
const colorAttr = group.variationAttributes.find(
(attr) => attr.id === "color",
);
if (!colorAttr || !colorAttr.values || !colorAttr.values.length)
continue;
const colorCode = colorAttr.values[0].value;
let colorName = "";
if (group.images && group.images.length > 0) {
const image = group.images[0];
if (image.title) {
const titleParts = image.title.split(", ");
if (titleParts.length > 1) {
colorName = titleParts[1];
}
}
}
let isColorOrderable = false;
if (rawProduct.variants && rawProduct.variants.length > 0) {
isColorOrderable = rawProduct.variants.some(
(variant) =>
variant.variationValues?.color === colorCode &&
variant.orderable === true,
);
}
if (colorCode && colorName && isColorOrderable) {
const existingVariant = modelData.colorVariants.find(
(v) => v.colorCode === colorCode,
);
if (!existingVariant) {
modelData.colorVariants.push({
colorCode,
colorName,
imageGroup: group,
orderable: true,
});
}
}
}
});
const addedColorCodes = /* @__PURE__ */ new Set();
const maxSlides = 8;
for (const [modelName, modelData] of productsByModel.entries()) {
if (!modelData.colorVariants.length) continue;
let selectedVariant = null;
for (const variant of modelData.colorVariants) {
if (!addedColorCodes.has(variant.colorCode)) {
selectedVariant = variant;
break;
}
}
if (!selectedVariant && modelData.colorVariants.length > 0) {
selectedVariant = modelData.colorVariants[0];
}
if (selectedVariant) {
const matchingProduct = products.find((p) => p.name === modelName);
if (matchingProduct) {
addedColorCodes.add(selectedVariant.colorCode);
const modifiedProduct = { ...matchingProduct };
if (
selectedVariant.imageGroup.images &&
selectedVariant.imageGroup.images.length > 0
) {
const imageUrl = selectedVariant.imageGroup.images[0].link;
modifiedProduct.image = {
src: imageUrl,
alt: `${modelName}, ${selectedVariant.colorName}`,
};
}
const slide = createCustomSlide(
modifiedProduct,
modelData.rawProduct,
selectedVariant,
config,
);
swiperWrapper.appendChild(slide);
}
}
}
if (addedColorCodes.size < maxSlides && products.length > 0) {
for (const [modelName, modelData] of productsByModel.entries()) {
if (
!modelData.colorVariants.length ||
addedColorCodes.size >= maxSlides
)
continue;
for (const variant of modelData.colorVariants) {
if (
!addedColorCodes.has(variant.colorCode) &&
addedColorCodes.size < maxSlides
) {
const matchingProduct = products.find(
(p) => p.name === modelName,
);
if (matchingProduct) {
addedColorCodes.add(variant.colorCode);
const modifiedProduct = { ...matchingProduct };
if (
variant.imageGroup.images &&
variant.imageGroup.images.length > 0
) {
const imageUrl = variant.imageGroup.images[0].link;
modifiedProduct.image = {
src: imageUrl,
alt: `${modelName}, ${variant.colorName}`,
};
}
const slide = createCustomSlide(
modifiedProduct,
modelData.rawProduct,
variant,
config,
);
swiperWrapper.appendChild(slide);
}
}
}
}
}
if (swiperWrapper.children.length === 0) {
console.warn(
"Falling back to original product list as no orderable color variants were found",
);
products.forEach((product, index) => {
const rawProduct =
rawProducts.find((raw) => raw.productId === product.productId) ||
null;
const colorData = product.image?.src
? extractColorFromImageUrl(
product.image.src,
rawProduct?.imageGroups,
)
: { colorName: product.colorName || "" };
const slide = createCustomSlide(
product,
rawProduct,
colorData,
config,
);
swiperWrapper.appendChild(slide);
console.log(`Added fallback slide for product: ${product.name}`);
});
}
if (swiperWrapper.children.length === 0) {
console.error("No slides were created, cannot initialize Swiper");
return;
}
console.log(
`Initializing Swiper with ${swiperWrapper.children.length} slides`,
);
const swiper = new Swiper(
`${parentSelector} ${config.swiperSelector}`,
swiperConfig,
);
const skeleton = document.querySelector(
`${parentSelector} ${config.skeletonSelector}`,
);
const slider = document.querySelector(
`${parentSelector} ${config.swiperSelector}`,
);
if (skeleton) skeleton.style.display = "none";
if (slider) slider.style.display = "block";
return swiper;
} catch (error) {
console.error("Error initializing slider:", error);
return null;
}
}
var trailRunSlider = createDynamicTrailRunSlider();
trailRunSlider.init();
})();