1import { decorateIcons } from '../../../scripts/aem.js'; 2import { getConfigValue, getLanguageAttr } from '../../../scripts/configs.js'; 3import { calculateDiscount, getCurrencySymbolFromConfig } from '../../../scripts/utils.js'; 4import { isLoggedInUser, hasValue } from '../../../scripts/scripts.js'; 5import { 6 getAuraLocalStorageData, 7 isAuraLoggedIn, 8 isEarlyAccessToSaleEnabled, 9 checkBeforeOrDuringEarlyAccess, 10} from '../../../scripts/aura/api.js'; 11// eslint-disable-next-line import/no-cycle 12import { formatPrice, compileCurrency } from '../pdp-utils.js'; 13import { buildLiveCountdown, createEarlyAccessToSalePopup } from '../../header/header-utils.js'; 14import { auraButtonClickEventHandler } from '../../header/aura-header.js'; 15import { dataLayerEarlyAccessToSaleEvent } from '../../../scripts/analytics/google-data-layer.js'; 16 17const appendPromotionsBelowPriceBlock = await getConfigValue('append-promo-below-price-block'); 18 19function priceLabel(placeholders) { 20 const priceDiv = document.querySelector('.pdp-product__prices'); 21 const priceTitle = document.createElement('span'); 22 priceTitle.classList.add('pdp-price-title'); 23 priceDiv.appendChild(priceTitle); 24 priceTitle.textContent = `${placeholders.price || 'Price:'}`; 25} 26 27export async function decorateMemberPrice( 28 ctx, 29 $block, 30 placeholders, 31 // eslint-disable-next-line no-unused-vars 32 blockConfigs, 33 memberPriceProvided = null, 34) { 35 let memberPrice; 36 37 if (memberPriceProvided) { 38 memberPrice = memberPriceProvided; 39 } else { 40 const memberPriceValue = ctx.data?.attributes.find((attr) => attr.name === 'member_price')?.value; 41 if (memberPriceValue) { 42 memberPrice = memberPriceValue; 43 } 44 } 45 46 if (memberPrice) { 47 const priceDiv = document.querySelector('.pdp-product__prices'); 48 const memberPriceContainer = document.createElement('div'); 49 memberPriceContainer.classList.add('pdp-member-price'); 50 priceDiv.classList.add('member-price'); 51 if (isLoggedInUser()) { 52 memberPriceContainer.classList.add('logged-in'); 53 priceDiv.classList.add('member-price-logged-in'); 54 } else { 55 memberPriceContainer.classList.add('guest'); 56 priceDiv.classList.add('member-price-guest'); 57 } 58 59 const memberPriceElement = document.createElement('div'); 60 memberPriceElement.classList.add('pdp-member-price-container'); 61 62 const memberPriceText = document.createElement('span'); 63 memberPriceText.classList.add('pdp-member-price--text'); 64 const currency = ctx.data.prices?.final?.currency || 'AED'; 65 memberPriceText.textContent = await formatPrice( 66 currency, 67 memberPrice, 68 blockConfigs?.flags?.countryCode || 'AE', 69 blockConfigs.flags.decimalDigits || 2, 70 ); 71 72 const memberPriceLabel = document.createElement('span'); 73 memberPriceLabel.classList.add('pdp-member-price--label'); 74 memberPriceLabel.textContent = placeholders.memberPriceLabel || 'Member Price'; 75 76 const lang = getLanguageAttr(); 77 78 if (lang === 'ar') { 79 memberPriceElement.appendChild(memberPriceLabel); 80 memberPriceElement.appendChild(memberPriceText); 81 } else { 82 memberPriceElement.appendChild(memberPriceText); 83 memberPriceElement.appendChild(memberPriceLabel); 84 } 85 86 memberPriceContainer.appendChild(memberPriceElement); 87 priceDiv.insertAdjacentElement('afterend', memberPriceContainer); 88 } 89} 90 91export async function decorateAuraPrice( 92 ctx, 93 $block, 94 placeholders, 95 blockConfigs, 96 auraPriceProvided, 97 selectedVariant = null, 98) { 99 let auraPrice; 100 // Remove any existing aura price elements from the DOM before adding new ones 101 const auraPriceDiv = document.querySelectorAll('.pdp-aura-price'); 102 if (auraPriceDiv) { 103 auraPriceDiv.forEach((element) => { 104 element.remove(); 105 }); 106 } 107 const auraOfferContainer = document.querySelector('.get-aura-offer-container'); 108 if (auraOfferContainer) { 109 auraOfferContainer.remove(); 110 } 111 112 if (auraPriceProvided) { 113 auraPrice = auraPriceProvided; 114 } else { 115 const auraPriceValue = ctx.data?.attributes.find((attr) => attr.name === 'member_price')?.value; 116 if (auraPriceValue) { 117 auraPrice = auraPriceValue; 118 } else if (ctx?.data?.productTypeName === 'ComplexProductView') { 119 auraPrice = ctx?.data?.variants[0]?.product?.attributes.find((attr) => attr.name === 'member_price')?.value 120 ? parseFloat(ctx?.data?.variants[0]?.product?.attributes.find((attr) => attr.name === 'member_price')?.value) : undefined; 121 if (blockConfigs?.flags?.updateAuraPrice === true) { 122 auraPrice = blockConfigs?.decorations?.updateAuraPrice(ctx); 123 } 124 } 125 } 126 127 const priceDiv = document.querySelector('.pdp-product__prices'); 128 const priceSpecialElement = document.querySelector('.pdp-product__price-special.pdp-product__price'); 129 const auraPriceContainer = document.createElement('div'); 130 auraPriceContainer.classList.add('pdp-aura-price'); 131 priceDiv.classList.add('aura-price'); 132 133 const auraIcon = document.createElement('span'); 134 auraIcon.classList.add('icon', 'icon-aura-icon'); 135 136 const auraPriceElement = document.createElement('div'); 137 auraPriceElement.classList.add('pdp-aura-price-container'); 138 139 const auraPriceText = document.createElement('span'); 140 auraPriceText.classList.add('pdp-aura-price--text'); 141 const currency = ctx.data.prices?.final?.currency || 'AED'; 142 auraPriceText.textContent = await formatPrice( 143 currency, 144 auraPrice, 145 blockConfigs?.flags?.countryCode || 'AE', 146 blockConfigs.flags.decimalDigits || 2, 147 ); 148 149 const auraPriceLabel = document.createElement('span'); 150 auraPriceLabel.classList.add('pdp-aura-price--label'); 151 auraPriceLabel.textContent = placeholders.auraPrice || 'Aura Price'; 152 153 auraPriceContainer.appendChild(auraIcon); 154 auraPriceElement.appendChild(auraPriceText); 155 auraPriceContainer.appendChild(auraPriceElement); 156 auraPriceElement.appendChild(auraPriceLabel); 157 158 // extract special price 159 let specialPrice; 160 const finalPrice = selectedVariant?.product?.price?.final?.amount?.value 161 ?? ctx.data?.prices?.final?.amount; 162 const regularPrice = selectedVariant?.product?.price?.regular?.amount?.value 163 ?? ctx.data?.prices?.regular?.amount; 164 165 const fallbackSpecialPrice = ctx.data?.attributes?.find((attr) => attr.name === 'special_price')?.value; 166 const specialPriceValue = finalPrice ?? fallbackSpecialPrice; 167 if (specialPriceValue) { 168 specialPrice = specialPriceValue; 169 } 170 171 /* Special price lower than the aura price hide AURA */ 172 if (typeof auraPrice === 'undefined' || parseFloat(auraPrice) === 0 || parseFloat(specialPrice) <= parseFloat(auraPrice)) { 173 // TODO: use class rather than style display none 174 auraPriceContainer.classList.add('hide'); 175 } 176 decorateIcons(auraPriceContainer); 177 priceDiv.appendChild(auraPriceContainer); 178 179 ctx.onChange((next) => { 180 if (isAuraLoggedIn()) { 181 auraPriceContainer.classList.add('logged-in'); 182 if (hasValue(specialPrice) && hasValue(regularPrice) && hasValue(auraPrice) 183 && parseFloat(specialPrice) !== parseFloat(regularPrice) 184 && parseFloat(specialPrice) > parseFloat(auraPrice)) { 185 priceSpecialElement?.classList.add('hide'); 186 } 187 188 let mauraPrice; 189 190 if (auraPriceProvided) { 191 mauraPrice = auraPriceProvided; 192 } else { 193 const auraPriceValue = next.data?.attributes.find((attr) => attr.name === 'member_price')?.value; 194 if (auraPriceValue) { 195 mauraPrice = auraPriceValue; 196 } else if (next?.data?.productTypeName === 'ComplexProductView') { 197 const val = next?.data?.variants?.[0]?.product?.attributes.find((attr) => attr.name === 'member_price')?.value; 198 mauraPrice = val ? parseFloat(val) : undefined; 199 } 200 } 201 if (mauraPrice) { 202 priceDiv.classList.add('aura-price-logged-in'); 203 } else { 204 priceDiv.classList.remove('aura-price-logged-in'); 205 } 206 const auralogin = document.querySelector('.get-aura-offer-container'); 207 if (auralogin) { 208 auralogin.remove(); 209 } 210 } 211 }); 212 213 const auraCustomerData = getAuraLocalStorageData(); 214 if (isAuraLoggedIn()) { 215 auraPriceContainer.classList.add('logged-in'); 216 if (auraPrice) { 217 priceDiv.classList.add('aura-price-logged-in'); 218 } 219 if (priceSpecialElement && finalPrice !== regularPrice) { 220 if (specialPrice && auraPrice) { 221 if (parseFloat(specialPrice) <= parseFloat(auraPrice)) { 222 priceSpecialElement.classList.remove('hide'); 223 priceSpecialElement.classList.add('no-strike'); 224 } else { 225 priceSpecialElement.classList.add('hide'); // Hide if both exist 226 } 227 } else { 228 priceSpecialElement.classList.remove('hide'); // Show if only priceSpecialElement exists 229 } 230 } else if (finalPrice === regularPrice) { 231 priceSpecialElement.classList.remove('hide'); 232 } 233 const currencySymbol = await getCurrencySymbolFromConfig(); 234 if (currencySymbol) { 235 const priceEl = priceSpecialElement.querySelector('.pdp-price-range .dropin-price'); 236 priceEl.textContent = priceEl.textContent 237 .replace(/\u00A0|\u200A|\u200F/g, '') 238 .replace(/[^\d.,-]/g, ''); 239 } 240 } else { 241 const timer = setTimeout(async () => { 242 const openAuraModal = blockConfigs?.flags?.isAuraEnabled 243 ? await import('../../../scripts/aura/common.js') 244 : {}; 245 const auraoffer = document.querySelector('.aura-price'); 246 // Remove any existing offer container 247 const auraGetOfferContainer = document.querySelector('.get-aura-offer-container'); 248 if (auraGetOfferContainer) { 249 auraGetOfferContainer.remove(); 250 } 251 252 if (auraPrice && Number(specialPrice) > Number(auraPrice)) { 253 const joinAuraBannerContainer = document.createElement('div'); 254 joinAuraBannerContainer.className = 'get-aura-offer-container'; 255 256 const paragraph = document.createElement('p'); 257 258 auraoffer.append(joinAuraBannerContainer); 259 joinAuraBannerContainer.appendChild(paragraph); 260 261 let joinAuraLabel; 262 let auraLoginButtonLabel; 263 let linkAuraLabel; 264 const getAuraOfferText = placeholders.getAuraOffer || 'Get this Aura Offer'; 265 266 if (isLoggedInUser() && ['1', 1].includes(auraCustomerData?.apc_link)) { 267 linkAuraLabel = placeholders.linkYourAura || 'Link your Aura'; 268 auraLoginButtonLabel = ''; // No login link for this case 269 const contentHtmlg = `<div class="get-aura-offer-links"> 270 <p>${getAuraOfferText}
271 <span class="aura-underline-text"> 272 ${auraLoginButtonLabel ? '' : `<a href="#join" class="linking-link">${linkAuraLabel}</a>`} 273 </span> 274 </p> 275 </div>`; 276 joinAuraBannerContainer.innerHTML = contentHtmlg; 277 } else { 278 // User type #1: Ecom guest and Aura guest 279 joinAuraLabel = placeholders.join || 'Join'; 280 auraLoginButtonLabel = placeholders.auraLoginButton || 'Login'; 281 const contentHtmlg = `<div class="get-aura-offer-links"> 282 <p>${getAuraOfferText} 283 <span class="aura-underline-text"> 284 <a href="#login" class="login-link">${auraLoginButtonLabel}</a> 285 ${auraLoginButtonLabel ? `<span>${placeholders.or || 'or'}</span> <a href="#login" class="join-link">${joinAuraLabel}</a>` : ''} 286 </span> 287 </p> 288 </div>`; 289 joinAuraBannerContainer.innerHTML = contentHtmlg; 290 } 291 292 const footerLinks = [ 293 { selector: '.join-link', action: 'join' }, 294 { selector: '.login-link', action: 'link' }, 295 { selector: '.linking-link', action: 'link' }, 296 ]; 297 footerLinks.forEach(({ selector, action }) => { 298 const link = joinAuraBannerContainer.querySelector(selector); 299 if (link) { 300 link.addEventListener('click', () => openAuraModal.default(action)); 301 } 302 }); 303 } 304 clearTimeout(timer); 305 }, 100); 306 } 307} 308 309export async function decorateAuraEarlyAccessPrice( 310 ctx, 311 $block, 312 placeholders, 313 blockConfigs, 314 earlyAccessToSale, 315 memberPrice = null, 316 isQuickView = false, 317 selectedVariant = null, 318) { 319 if (blockConfigs?.decorations?.decorateAuraEarlyAccessPrice) { 320 blockConfigs?.decorations?.decorateAuraEarlyAccessPrice( 321 ctx, 322 $block, 323 placeholders, 324 blockConfigs, 325 earlyAccessToSale, 326 memberPrice, 327 isQuickView, 328 selectedVariant, 329 ); 330 } else { 331 if (document.querySelector('.aura-early-access-price-container')) { 332 document.querySelectorAll('.aura-early-access-price-container').forEach((el) => el.remove()); 333 } 334 let auraPrice = memberPrice || ctx.data?.attributes.find((attr) => attr.name === 'member_price')?.value; 335 if (ctx?.data?.productTypeName === 'ComplexProductView' && !auraPrice && selectedVariant) { 336 auraPrice = selectedVariant?.product?.attributes.find((attr) => attr.name === 'member_price')?.value 337 ? parseFloat(selectedVariant?.product?.attributes.find((attr) => attr.name === 'member_price')?.value) : undefined; 338 } else if (ctx?.data?.productTypeName === 'ComplexProductView' && !auraPrice) { 339 auraPrice = ctx?.data?.variants[0]?.product?.attributes.find((attr) => attr.name === 'member_price')?.value 340 ? parseFloat(ctx?.data?.variants[0]?.product?.attributes.find((attr) => attr.name === 'member_price')?.value) : undefined; 341 } 342 let label = ''; 343 const earlyAccessPhase = await checkBeforeOrDuringEarlyAccess(); 344 if (earlyAccessPhase === 'before') { 345 label = placeholders.beforeEarlyAccessToSalePdp || 'Early Access to Sale on this Item in'; 346 } else if (earlyAccessPhase === 'during') { 347 label = placeholders.duringEarlyAccessToSalePdp || 'Time left for Early Access to Sale'; 348 } 349 const salePrice = selectedVariant 350 ? selectedVariant?.product?.price?.final?.amount?.value 351 : ctx.data?.prices?.final?.amount; 352 const regularPrice = selectedVariant 353 ? selectedVariant?.product?.price?.regular?.amount?.value 354 : ctx.data?.prices?.regular?.amount; 355 const currency = ctx.data?.prices?.final?.currency || 'AED'; 356 const auraPriceDivs = document.querySelectorAll('.pdp-aura-price'); 357 auraPriceDivs.forEach((element) => element.remove()); 358 const slotMicroFunctions = { 359 decorateAuraPrice: blockConfigs?.decorations?.decorateAuraPrice || decorateAuraPrice, 360 decorateMemberPrice: blockConfigs?.decorations?.decorateMemberPrice || decorateMemberPrice, 361 }; 362 if (parseFloat(auraPrice) 363 && parseFloat(auraPrice) < parseFloat(salePrice) && blockConfigs?.flags?.isAuraEnabled) { 364 document.querySelector('.pdp-product__promotion-labels-container')?.classList.add('hide');
365 if (parseFloat(salePrice) === parseFloat(regularPrice)) { 366 document.querySelector('.pdp-product__price-special')?.classList.remove('hide'); 367 } 368 if (parseFloat(salePrice) !== parseFloat(regularPrice)) { 369 document.querySelector('.pdp-product__price-special')?.classList.add('hide'); 370 if (!isAuraLoggedIn()) { 371 document.querySelector('.pdp-product__price-regular .dropin-price')?.classList.remove('dropin-price--strikethrough'); 372 } 373 } 374 slotMicroFunctions.decorateAuraPrice( 375 ctx, 376 $block, 377 placeholders, 378 blockConfigs, 379 auraPrice, 380 selectedVariant, 381 ); 382 } else if (parseFloat(auraPrice) 383 && parseFloat(auraPrice) < parseFloat(salePrice) && !blockConfigs?.flags?.isAuraEnabled) { 384 slotMicroFunctions.decorateMemberPrice(ctx, $block, placeholders, blockConfigs, auraPrice); 385 } else if (parseFloat(salePrice) === parseFloat(regularPrice)) { 386 document.querySelector('.pdp-product__price-special')?.classList.remove('hide'); 387 } else { 388 const container = document.createElement('div'); 389 container.className = 'container aura-early-access-price-container'; 390 // AED Price Line 391 const priceLine = document.createElement('div'); 392 priceLine.className = 'sale-prices'; 393 const priceText = document.createElement('span'); 394 priceText.className = 'price-text'; 395 priceText.textContent = formatPrice(currency, salePrice, blockConfigs?.flags?.countryCode || 'AE', blockConfigs.flags.decimalDigits || 2); 396 const customerAuraData = getAuraLocalStorageData(); 397 398 const labelText = document.createElement('span'); 399 labelText.className = 'label-text'; 400 labelText.textContent = placeholders?.earlyAccessPriceLabel || 'Aura Star & VIP Only'; 401 if (earlyAccessPhase === 'before' && isQuickView) { 402 labelText.textContent = ''; 403 } 404 405 if (earlyAccessPhase === 'during') { 406 priceLine.appendChild(priceText); 407 } 408 409 priceLine.appendChild(labelText); 410 container.appendChild(priceLine); 411 412 const notLinked = ['1', 1].includes(customerAuraData?.apc_link); 413 const isLoggedIn = isLoggedInUser(); 414 415 // Subtext 416 if (!isQuickView) { 417 const content = document.createElement('div'); 418 content.className = 'timer-content'; 419 const subtext = document.createElement('div'); 420 subtext.className = 'subtext'; 421 subtext.textContent = label; 422 423 content.appendChild(subtext); 424 content.appendChild(buildLiveCountdown(placeholders)); 425 container.appendChild(content); 426 427 const promoLinkContainer = document.createElement('div'); 428 promoLinkContainer.className = 'promo-link-container'; 429 const promoLink = document.createElement('a'); 430 promoLink.className = 'promo-link'; 431 promoLink.href = '#'; 432 promoLink.textContent = placeholders.learnMoreText || 'Learn More'; 433 promoLinkContainer.appendChild(promoLink); 434 if (!customerAuraData || !customerAuraData?.aura_membership || (isLoggedIn && notLinked)) { 435 const joinnow = document.createElement('a'); 436 joinnow.className = 'join-now promo-link'; 437 joinnow.href = '#'; 438 const linkText = isLoggedIn && notLinked ? placeholders.linkYourAura || 'Link Your Aura' : placeholders.joinAuraText || 'Join Aura'; 439 joinnow.textContent = linkText; 440 auraButtonClickEventHandler(joinnow, notLinked ? 'link' : 'join', 'SignIn_'); 441 joinnow.addEventListener('click', (event) => { 442 event.preventDefault(); 443 dataLayerEarlyAccessToSaleEvent(ctx.data, 'early_access banner click'); 444 }); 445 const promoLinkText = document.createElement('span'); 446 promoLinkText.className = 'promo-link-text'; 447 promoLinkText.textContent = placeholders.or || 'or'; 448 promoLinkContainer.appendChild(promoLinkText); 449 promoLinkContainer.appendChild(joinnow); 450 } 451 452 container.appendChild(promoLinkContainer); 453 promoLink.addEventListener('click', (event) => { 454 event.preventDefault(); 455 dataLayerEarlyAccessToSaleEvent(ctx.data, 'early_access banner click'); 456 createEarlyAccessToSalePopup(); 457 }); 458 } 459 460 const userEligibilityCheck = (!isLoggedIn || (isLoggedIn && parseInt(customerAuraData?.apc_link, 10) !== 1)) && earlyAccessToSale?.early_access_allow_tier?.includes(cu
460stomerAuraData?.aura_Status) && earlyAccessPhase === 'during'; 461 if (document.querySelector('.aura-early-access-price-container')) { 462 document.querySelectorAll('.aura-early-access-price-container').forEach((el) => el.remove()); 463 } 464 if (!isQuickView) { 465 document.querySelector('.pdp-product__prices').insertAdjacentElement('afterend', container); 466 document.querySelector('.pdp-product__price-regular .dropin-price')?.classList.remove('dropin-price--strikethrough'); 467 } else { 468 if (userEligibilityCheck) { 469 document.querySelector('.modal-content .pdp-product__prices').insertAdjacentElement('beforebegin', container); 470 } else { 471 document.querySelector('.modal-content .pdp-product__prices').insertAdjacentElement('afterend', container); 472 } 473 document.querySelector('.modal-content .pdp-product__price-regular .dropin-price')?.classList.remove('dropin-price--strikethrough'); 474 } 475 if (userEligibilityCheck) { 476 document.querySelector('.pdp-product__price-regular .dropin-price')?.classList.add('dropin-price--strikethrough'); 477 document.querySelector('.dropin-price--small.dropin-price--bold')?.classList.add('dropin-price--strikethrough'); 478 const currencySymbol = await getCurrencySymbolFromConfig(); 479 if (currencySymbol) { 480 const priceElement = document.querySelector('.pdp-product__price-regular .dropin-price'); 481 if (priceElement) { 482 let priceValue = priceElement.innerText; 483 priceValue = priceValue.replace(currencySymbol, '').trim(); 484 priceElement.innerText = priceValue; 485 } 486 } 487 document.querySelector('.pdp-product__price-regular .dropin-price').classList.add('dropin-price--strikethrough'); 488 } 489 } 490 } 491} 492 493export async function decorateAuraMemberPrice( 494 ctx, 495 $block, 496 placeholders, 497 blockConfigs, 498 memberPrice = null, 499) { 500 const isQuickView = $block.id === 'product-overview-popup'; 501 const earlyAccessToSale = await isEarlyAccessToSaleEnabled(); 502 const salePrice = ctx.data?.prices?.final?.amount; 503 const regularPrice = ctx.data?.prices?.regular?.amount; 504 const auraPrice = memberPrice || ctx.data?.attributes.find((attr) => attr.name === 'member_price')?.value; 505 // if hello early access price enabled only for few variants 506 const hmEarlyAccessPrice = ctx.data?.variants?.find((variant) => variant?.product?.attributes?.find((item) => item?.name === 'eats_price')?.value > 0); 507 const hmEarlyAccessPercentage = ctx.data?.variants?.find((variant) => variant?.product?.attributes?.find((item) => item?.name === 'eats_category_percent')?.value > 0); 508 const isHelloMemberBrand = blockConfigs?.flags?.isHelloMemberBrand; 509 const earlyAccessPhase = earlyAccessToSale?.early_access_enable === 1 510 ? await checkBeforeOrDuringEarlyAccess() : 0; 511 if (blockConfigs?.flags?.aurapriceDisabled && earlyAccessPhase === 0) return; 512 const slotMicroFunctions = { 513 decorateAuraPrice: blockConfigs?.decorations?.decorateAuraPrice || decorateAuraPrice, 514 decorateMemberPrice: blockConfigs?.decorations?.decorateMemberPrice || decorateMemberPrice, 515 decorateAuraEarlyAccessPrice: blockConfigs?.decorations?.decorateAuraEarlyAccessPrice 516 || decorateAuraEarlyAccessPrice, 517 decorateMemberEarlyAccessPrice: blockConfigs?.decorations?.decorateMemberEarlyAccessPrice, 518 }; 519 if (earlyAccessPhase !== 0 && (hmEarlyAccessPrice || hmEarlyAccessPercentage) 520 && blockConfigs?.decorations?.decorateMemberEarlyAccessPrice) { 521 slotMicroFunctions.decorateMemberEarlyAccessPrice( 522 ctx, 523 placeholders, 524 blockConfigs, 525 earlyAccessToSale, 526 ); 527 } else if (earlyAccessPhase !== 0 528 && salePrice && !isHelloMemberBrand 529 && ((auraPrice && salePrice <= auraPrice) || (salePrice !== regularPrice))) { 530 slotMicroFunctions.decorateAuraEarlyAccessPrice( 531 ctx, 532 $block, 533 placeholders, 534 blockConfigs, 535 earlyAccessToSale, 536 auraPrice, 537 isQuickView, 538 ); 539 } else if (blockConfigs?.flags?.isAuraEnabled) { 540 slotMicroFunctions.decorateAuraPrice(ctx, $block, placeholders, blockConfigs, memberPrice); 541 } else { 542 slotMicroFunctions.decorateMemberPrice(ctx, $block, placeholders, blockConfigs, memberPrice); 543 } 544} 545 546export async function decorateSpecialPrice( 547 ctx,
548 placeholders, 549 blockConfigs, 550 decimalDigits, 551 countryCode, 552 isVatIncluded = true, 553) { 554 const special = ctx.data.prices.special || ctx.data.prices.final; 555 const { maximumAmount, minimumAmount, amount } = special; 556 let maxPrice; 557 let minPrice; 558 let price; 559 560 const minimumFractionDigits = (decimalDigits); 561 const maximumFractionDigits = (decimalDigits); 562 563 // Use compileCurrency utility to get the best available currency 564 const priceCurrency = await compileCurrency(special.currency, placeholders.currency); 565 if (maximumAmount && minimumAmount) { 566 maxPrice = formatPrice(priceCurrency, maximumAmount, countryCode, maximumFractionDigits); 567 minPrice = formatPrice(priceCurrency, minimumAmount, countryCode, minimumFractionDigits); 568 } else { 569 price = formatPrice(priceCurrency, amount, countryCode, decimalDigits); 570 } 571 if (blockConfigs?.flags?.addPriceLabel) { 572 priceLabel(placeholders); 573 } 574 const specialPriceHtml = document.createElement('div'); 575 specialPriceHtml.classList.add('pdp-price-range'); 576 577 const specialPriceMin = document.createElement('span'); 578 specialPriceMin.classList.add('dropin-price', 'dropin-price--default', 'dropin-price--small', 'dropin-price--bold'); 579 specialPriceMin.textContent = minPrice; 580 581 const specialPriceHyphen = document.createElement('span'); 582 specialPriceHyphen.classList.add('pdp-price-range__label'); 583 specialPriceHyphen.textContent = '-'; 584 585 const specialPriceMax = document.createElement('span'); 586 specialPriceMax.classList.add('dropin-price', 'dropin-price--default', 'dropin-price--small', 'dropin-price--bold'); 587 specialPriceMax.textContent = maxPrice; 588 589 const specialPriceSpan = document.createElement('span'); 590 specialPriceSpan.classList.add('dropin-price', 'dropin-price--default', 'dropin-price--small', 'dropin-price--bold'); 591 specialPriceSpan.textContent = price; 592 593 if (maximumAmount && minimumAmount) { 594 specialPriceHtml.appendChild(specialPriceMin); 595 specialPriceHtml.appendChild(specialPriceHyphen); 596 specialPriceHtml.appendChild(specialPriceMax); 597 if (blockConfigs?.flags?.hideSpecialPrice) { 598 [specialPriceMin, specialPriceHyphen, specialPriceMax].forEach((priceEl) => priceEl.classList.add('hidden')); 599 } 600 } else { 601 specialPriceHtml.appendChild(specialPriceSpan); 602 } 603 604 ctx.replaceWith(specialPriceHtml); 605 const includingVat = document.createElement('div'); 606 if (isVatIncluded) { 607 includingVat.classList.add('pdp-product__price--including-vat'); 608 includingVat.textContent = placeholders.priceIncludingVat || 'Inc. VAT'; 609 ctx.appendChild(includingVat); 610 if (ctx.data.prices.regular && ctx.data.prices.regular.variant === 'strikethrough') { 611 includingVat?.parentElement?.classList.add('hide'); 612 } 613 // KW has no VAT 614 } 615 616 // When customSpecialPriceOnChange is set, the market's block-config owns 617 // all SpecialPrice updates via decorateAdditionalSpecialPrice â skip the 618 // shared onChange handler to avoid stale-data conflicts and double renders. 619 if (!blockConfigs?.flags?.customSpecialPriceOnChange) { 620 ctx.onChange(async (next) => { 621 const newSpecial = next.data.prices.special || next.data.prices.final; 622 const { 623 maximumAmount: newMaximumAmount, minimumAmount: newMinimumAmount, amount: newAmount, 624 } = newSpecial; 625 626 if (newSpecial) { 627 // Use compileCurrency utility to get the best available currency 628 const newPriceCurrency = await compileCurrency(newSpecial.currency, placeholders.currency); 629 price = formatPrice( 630 newPriceCurrency, 631 newAmount, 632 countryCode, 633 decimalDigits, 634 ); 635 minPrice = formatPrice( 636 newPriceCurrency, 637 newMinimumAmount, 638 countryCode, 639 minimumFractionDigits, 640 ); 641 maxPrice = formatPrice( 642 newPriceCurrency, 643 newMaximumAmount, 644 countryCode, 645 maximumFractionDigits, 646 ); 647 648 if (newMaximumAmount && newMinimumAmount) { 649 specialPriceMin.textContent = minPrice; 650 specialPriceMax.textContent = maxPrice; 651 specialPriceHtml.innerHTML = ''; 652 specialPriceHtml.appendChild(specialPriceMin); 653 specialPriceHtml.appendChild(specialPriceHyphen); 654 specialPriceHtml.appendChild(specialPriceMax); 655 } else { 656 specialPriceSpan.textContent = price; 657 specialPriceHtml.innerHTML = specialPriceSpan.outerHTML; 658 } 659 if (isVatIncluded) { 660 if (next.data.prices.regular && next.data.prices.regular.variant === 'strikethrough') { 661 includingVat?.parentElement?.classList.add('hide'); 662 } else if (!next.data.prices.regular || next.data.prices.regular.variant === 'default') { 663 includingVat?.parentElement?.classList.remove('hide'); 664 } 665 } 666 } 667 }); 668 } 669} 670 671const earlyAccessPhase = await checkBeforeOrDuringEarlyAccess(); 672export function decorateRegularPrice( 673 ctx,
674 placeholders, 675 blockConfigs, 676 decimalDigits, 677 countryCode, 678 isVatIncluded = true, 679) { 680 const { regular } = ctx.data.prices; 681 const formattedPrice = formatPrice(regular.currency, regular.amount, countryCode, decimalDigits); 682 Promise.all([ 683 getCurrencySymbolFromConfig(), 684 ]).then(([currencySymbol]) => { 685 const regularPrice = document.createElement('div'); 686 regularPrice.classList.add('pdp-price-range'); 687 const regularPriceSpan = document.createElement('span'); 688 regularPriceSpan.classList.add('dropin-price', 'dropin-price--small', 'dropin-price--bold'); 689 regularPriceSpan.textContent = `${formattedPrice}`; 690 691 regularPrice.appendChild(regularPriceSpan); 692 693 if (regular?.variant) { 694 if (currencySymbol) { 695 if (regular?.variant === 'strikethrough') { 696 regularPriceSpan.textContent = formattedPrice.replace(currencySymbol, '').trim(); 697 } 698 } 699 regularPriceSpan.classList.add(`dropin-price--${regular.variant}`); 700 } 701 ctx.replaceWith(regularPrice); 702 703 if (isVatIncluded && ctx.data.prices.regular) { 704 const { priceIncludingVat } = placeholders; 705 const includingVat = document.createElement('div'); 706 includingVat.classList.add('pdp-product__price--including-vat'); 707 includingVat.textContent = priceIncludingVat || 'Inc. VAT'; 708 ctx.appendChild(includingVat); 709 } 710 let newFormattedPrice; 711 ctx.onChange((next) => { 712 const { regular: newRegular } = next.data.prices; 713 newFormattedPrice = formatPrice( 714 newRegular.currency, 715 newRegular.amount, 716 countryCode, 717 decimalDigits, 718 ); 719 if (currencySymbol) { 720 const pricesEl = document.querySelector('.pdp-product__prices'); 721 const hasAuraPrice = pricesEl.classList.contains('aura-price'); 722 if (isAuraLoggedIn() && hasAuraPrice) { 723 // Remove currency symbol ONLY for regular price 724 newFormattedPrice = newFormattedPrice.replace(currencySymbol, '').trim(); 725 } else if ((earlyAccessPhase !== 'during' && earlyAccessPhase !== 'before' && earlyAccessPhase !== 0) && isAuraLoggedIn() && isLoggedInUser()) { 726 // Remove currency symbol ONLY for regular price 727 newFormattedPrice = newFormattedPrice.replace(currencySymbol, '').trim(); 728 } else if (!earlyAccessPhase) { 729 // Remove currency symbol ONLY for regular price 730 newFormattedPrice = newFormattedPrice.replace(currencySymbol, '').trim(); 731 } 732 } 733 regularPriceSpan.textContent = `${newFormattedPrice}`; 734 }); 735 }); 736} 737 738const earlyAccessEnabled = await isEarlyAccessToSaleEnabled(); 739const auraCustomerData = getAuraLocalStorageData(); 740// eslint-disable-next-line no-unused-vars 741export function decoratePromotions(ctx, $block, placeholders, locale, blockConfigs) { 742 ctx.replaceWith(document.createElement('div')); 743 const lang = getLanguageAttr(); 744 const productPromotions = ctx.data.promotions || []; 745 746 const { final, regular } = ctx.data.prices; 747 if (final?.amount < regular?.amount) { 748 const discount = calculateDiscount(regular.amount, final.amount); 749 productPromotions.unshift({ 750 label: placeholders.plpSave !== '0' ? `(${placeholders.plpSave} ${discount}%)` : `-${discount}%`, 751 type: 'discount', 752 }); 753 } else if (final?.minimumAmount < final?.maximumAmount) { 754 const discount = calculateDiscount(final.maximumAmount, final.minimumAmount); 755 productPromotions.unshift({ 756 label: placeholders.plpSave !== '0' ? `(${placeholders.plpSave} ${discount}%)` : `-${discount}%`, 757 type: 'discount', 758 }); 759 } else { 760 productPromotions.unshift({ 761 label: '', 762 type: 'discount', 763 hide: true, 764 }); 765 } 766 const labelsContainer = document.createElement('div'); 767 const linksContainer = document.createElement('div'); 768 if (productPromotions.length) { 769 const promoWithoutLinks = productPromotions.filter((promotion) => !promotion.url); 770 const promoWithLinks = productPromotions.filter((promotion) => !!promotion.url); 771 772 if (!blockConfigs.flags.discountValuehide) { 773 labelsContainer.classList.add('pdp-product__promotion-labels-container'); 774 promoWithoutLinks.forEach((promotion) => { 775 if (promotion?.context?.includes('web') && promotion.label?.trim()) { 776 const promotionLabel = document.createElement('div'); 777 const promotionClass = promotion?.type === 'discount' ? 'discount-label' : ''; 778 promotionLabel.classList.add('promo-label'); 779 if (promotionClass.trim() !== '') { 780 promotionLabel.classList.add(promotionClass); 781 } 782 if (promotion.hide) { 783 promotionLabel.classList.add('hide'); 784 labelsContainer.classList.add('hide'); 785 } 786 787 promotionLabel.textContent = promotion.label; 788 labelsContainer.appendChild(promotionLabel); 789 } 790 }); 791 if (appendPromotionsBelowPriceBlock !== 'true') { 792 ctx.prependSibling(labelsContainer); 793 } 794 if (earlyAccessPhase === 'before' 795 || (earlyAccessPhase !== 0 796 && !earlyAccessEnabled?.early_access_allow_tier.includes(auraCustomerData?.aura_Status)) 797 || (earlyAccessPhase === 'during' && ((isLoggedInUser() && [1, '1'].includes(auraCustomerData?.apc_link))))) { 798 labelsContainer.querySelectorAll('.promo-label').forEach((el) => { el.classList.add('hide'); }); 799 } 800 } 801 802 const linkContainerClass = 'pdp-product__promotion-links-container'; 803 804 if (appendPromotionsBelowPriceBlock) { 805 linksContainer.classList.add(linkContainerClass); 806 } 807 808 if (promoWithLinks.length) { 809 if (!linksContainer.classList.contains(linkContainerClass)) { 810 linksContainer.classList.add(linkContainerClass); 811 } 812 promoWithLinks.forEach((promotion) => { 813 if (promotion?.context?.includes('web') && promotion?.label && promotion?.label?.trim()) { 814 const promotionLabel = document.createElement('div'); 815 promotionLabel.classList.add('promo-label');
816 promotionLabel.innerHTML = `<a href="/${lang}/${promotion.url}" alt="${promotion.description}">${promotion.label}</a>`; 817 promotionLabel.addEventListener('click', (event) => { 818 event.preventDefault(); 819 localStorage.setItem('categoryListName', `pdp-pl-${promotion.label}`); // Set the pdp promo in local storage 820 window.location.href = `/${lang}/${promotion.url}`; 821 }); 822 823 linksContainer.appendChild(promotionLabel); 824 } 825 }); 826 } 827 } 828 829 if (appendPromotionsBelowPriceBlock === 'true') { 830 linksContainer.prepend(labelsContainer); 831 const priceBlock = document.querySelector('.pdp-product__prices'); 832 if (priceBlock) { 833 priceBlock.insertAdjacentElement('afterend', linksContainer); 834 } 835 } else { 836 ctx.prependSibling(linksContainer); 837 } 838 839 let discountPromotion = labelsContainer?.querySelector('.discount-label'); 840 if (!discountPromotion) { 841 discountPromotion = document.createElement('div'); 842 discountPromotion.classList.add('promo-label', 'discount-label'); 843 } 844 845 ctx.onChange(async (next) => { 846 const { final: finalChanged, regular: regularChanged } = next.data.prices; 847 const isShowPdpDiscountPrice = blockConfigs?.flags?.isShowPdpDiscountPrice; 848 const isShowQuickViewDiscountPrice = blockConfigs?.flags?.isShowQuickViewDiscountPrice; 849 850 const formatDiscountValue = await getConfigValue('plp-show-discount-text-format') || 'false'; 851 const formatDiscountPosition = await getConfigValue('plp-show-discount-text-position') || 'false'; 852 853 if (discountPromotion) { 854 if (finalChanged?.amount < regularChanged?.amount) { 855 const discountChanged = calculateDiscount(regularChanged.amount, finalChanged.amount); 856 let discountText = ''; 857 const isHtmlRtl = document.documentElement.lang; 858 if (placeholders.plpSave !== '0') { 859 if (isShowPdpDiscountPrice) { 860 discountText = `${placeholders.plpSave} ${discountChanged}%`; 861 } else if (isShowQuickViewDiscountPrice) { 862 discountText = `${placeholders.plpSave} ${discountChanged}%`; 863 } else if (blockConfigs?.flags?.isShowMinusDiscountChanged) { 864 discountText = `-${discountChanged}%`; 865 } else { 866 discountText = `(${placeholders.plpSave} ${discountChanged}%)`; 867 // Check if the discount label should be left side of discount value for Arabic 868 if (blockConfigs?.flags?.arabicDiscountLabelLeft && isHtmlRtl === 'ar') { 869 discountText = `( %${discountChanged} ${placeholders.plpSave})`; 870 } 871 } 872 873 if (formatDiscountValue === 'true' && formatDiscountPosition === 'false') { 874 discountText = `${placeholders.plpSave} ${discountChanged}%`; 875 if (isHtmlRtl === 'ar') { 876 discountText = ` ${placeholders.plpSave} %${discountChanged}`; 877 } 878 } else if (formatDiscountPosition === 'true') { 879 discountText = `${discountChanged}% ${placeholders.plpSave}`; 880 if (isHtmlRtl === 'ar') { 881 discountText = `${placeholders.plpSave} %${discountChanged}`; 882 } 883 } 884 } else { 885 discountText = `-${discountChanged}%`; 886 if (blockConfigs?.flags?.enableRtlDiscountReverse && isHtmlRtl) { 887 discountText = `%${discountChanged}-`; 888 discountPromotion.setAttribute('dir', 'ltr'); 889 } 890 } 891 discountPromotion.textContent = discountText; 892 discountPromotion.classList.remove('hide'); 893 labelsContainer?.appendChild(discountPromotion); 894 } else { 895 discountPromotion.textContent = ''; 896 discountPromotion.classList.add('hide'); 897 } 898 if (earlyAccessPhase === 'before' 899 || (earlyAccessPhase !== 0 900 && !earlyAccessEnabled?.early_access_allow_tier.includes(auraCustomerData?.aura_Status)) 901 || (earlyAccessPhase === 'during' && ((isLoggedInUser() && [1, '1'].includes(auraCustomerData?.apc_link))))) { 902 labelsContainer.querySelectorAll('.promo-label').forEach((el) => { el.classList.add('hide'); }); 903 } 904 } 905 }); 906}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.