1export const PROD_ENV = 'prod'; 2export const PPROD_ENV = 'pprod'; 3export const EDS_ENV = 'aem'; 4export const STAGE_ENV = 'stage'; 5export const DEV_ENV = 'dev'; 6export const PREVIEW_ENV = 'preview'; 7const ALLOWED_CONFIGS = [PROD_ENV, PPROD_ENV, EDS_ENV, DEV_ENV, PREVIEW_ENV]; 8const SEPARATE_CONFIG_FILES = [STAGE_ENV, PREVIEW_ENV, PPROD_ENV]; 9 10export const REG_DATE_KEY = 'registration_date'; 11export const LOGOUT_SOURCE = 'logout'; 12export const CARTID_STORE = 'M2_VENIA_BROWSER_PERSISTENCE__cartId'; 13const REACT_DEPENDENCIES = ['/scripts/vendor.js', '/scripts/react-bridge.js']; 14export const BLOCK_DEPENDENCIES = { 15 cart: REACT_DEPENDENCIES, 16 'cart-login': REACT_DEPENDENCIES, 17 'cart-register': REACT_DEPENDENCIES, 18 checkout: REACT_DEPENDENCIES, 19 'order-confirmation': REACT_DEPENDENCIES, 20 'payment-success': REACT_DEPENDENCIES, 21 'payment-error': REACT_DEPENDENCIES, 22 'shared-cart': REACT_DEPENDENCIES, 23}; 24export default BLOCK_DEPENDENCIES; 25 26const IMAGES = { 27 ICON: { name: 'imagesize-icon', size: '120', mobile: '120' }, 28 XSMALL: { name: 'imagesize-xsmall', size: '250', mobile: '250' }, 29 SMALL: { name: 'imagesize-small', size: '400', mobile: '400' }, 30 XMEDIUM: { name: 'imagesize-xmedium', size: '550', mobile: '550' }, 31 MEDIUM: { name: 'imagesize-medium', size: '750', mobile: '750' }, 32 LARGE: { name: 'imagesize-large', size: '1024', mobile: '750' }, 33 XLARGE: { name: 'imagesize-xlarge', size: '1600', mobile: '750' }, 34 XXLARGE: { name: 'imagesize-xxlarge', size: '2000', mobile: '750' }, 35}; 36 37export const COUNTRY_LIST = [ 38 { 39 code: 'AE', label: 'United Arab Emirates', phone: '971', phoneLength: 9, 40 }, 41 { 42 code: 'BH', label: 'Bahrain', phone: '973', phoneLength: 8, 43 }, 44 { 45 code: 'EG', label: 'Egypt', phone: '20', phoneLength: 10, 46 }, 47 { 48 code: 'JO', label: 'Jordan', phone: '962', phoneLength: 9, 49 }, 50 { 51 code: 'KW', label: 'Kuwait', phone: '965', phoneLength: 8, 52 }, 53 { 54 code: 'QA', label: 'Qatar', phone: '974', phoneLength: 8, 55 }, 56 { 57 code: 'SA', label: 'Saudi Arabia', phone: '966', phoneLength: 9, 58 }, 59]; 60 61export const CACHE_KEYS = { 62 VERSION: 'version', 63 KEYS: 'keys', 64 CONFIGS: 'configs', 65 BRAND_CONFIGS: 'brand_configs', 66 PLACEHOLDERS: 'placeholders', 67 COMMERCE_CATEGORIES: 'categories', 68 CATEGORIES_BY_URL: 'cat_byurl', 69 TRENDING_SEARCH: 'tsearch', 70 PLP_FILTERS: 'plp_filters', 71 FILTERS: 'filter', 72 CHECKOUT_ORDER_DETAILS: 'order_info', 73 STORES: 'stores', 74 AURA_INFO: 'aura', 75 AURA_EARLY_ACCESS_SALE: 'aura_early_access', 76 IS_ITEM_ON_SALE: 'is_item_on_sale', 77 FINDATON_SELECTED_SHADES: 'kSavedShades', 78 BANNER_LIST: 'banner_list', 79 BANNER_TYPOGRAPHY: 'banner_typography', 80 HEADER_SECOUNDRY_NAV: 'sec_nav', 81 EXPRESS_DELIVERY_LOCATIONS: 'express_del_loc', 82 COUPONS_DATA: 'coupons', 83}; 84 85const CACHE_KEYS_IGNORE = [ 86 CACHE_KEYS.AURA_INFO, 87]; 88 89// store configs globally to avoid multiple requests 90if (!window.axp) window.axp = {}; 91 92/* 93 * Returns the true origin of the current page in the browser. 94 * If the page is running in a iframe with srcdoc, the ancestor origin is returned. 95 * @returns {String} The true origin 96 */ 97function getOrigin() { 98 const { location } = window; 99 return location.href === 'about:srcdoc' ? window.parent.location.origin : location.origin; 100} 101 102export function getHref() { 103 if (window.location.href !== 'about:srcdoc') return window.location.href; 104 105 const { location: parentLocation } = window.parent; 106 const urlParams = new URLSearchParams(parentLocation.search); 107 return `${parentLocation.origin}${urlParams.get('path')}`; 108} 109 110/** 111 * Returns the document language attribute value 112 * 113 * @returns String language value 114 */ 115export function getLanguageAttr() { 116 const path = getHref(); 117 if (path.includes('/ar/')) { 118 return 'ar'; 119 } 120 return document.documentElement.lang || 'en'; 121} 122 123/** 124 * This function calculates the environment in which the site is running based on the URL. 125 * It defaults to 'prod'. In non 'prod' environments, the value can be overwritten using 126 * the 'environment' key in sessionStorage. 127 * 128 * @returns {string} - environment identifier (dev, stage or prod'. 129 */ 130export const calcEnvironment = () => { 131 const { href } = window.location; 132 let environment = PROD_ENV; 133 if (href.includes('.hlx.page') || href.includes('.aem.page')) { 134 environment = EDS_ENV; 135 } else if (href.includes('-stage.factory.alshayauat.com')) { 136 environment = STAGE_ENV; 137 } else if (href.includes('localhost')) { 138 environment = DEV_ENV; 139 } else if (href.includes('-eds.factory.alshayauat.com')) { 140 environment = PREVIEW_ENV; 141 } else if (href.includes('-pprod.factory.alshayauat.com')) { 142 environment = PPROD_ENV; 143 } 144 145 const environmentFromConfig = window.sessionStorage.getItem('environment'); 146 if (environmentFromConfig 147 && ALLOWED_CONFIGS.includes(environmentFromConfig) 148 && environment !== PROD_ENV) { 149 return environmentFromConfig; 150 } 151 152 return environment; 153}; 154 155/**
156 * Localstorage caching based on ttl (in seconds) 157 * 158 */ 159export const LocalStorageCache = (() => { 160 const PREFIX = '_axp_'; 161 162 function getExpiry(ttl) { 163 return ttl > 0 ? Date.now() + (ttl * 1000) : null; 164 } 165 166 // Helper to build stored object with ttl in seconds 167 function buildItem(value, ttl) { 168 return JSON.stringify({ 169 v: value, 170 exp: ttl, 171 }); 172 } 173 174 // Helper to parse/store 175 function parseItem(item) { 176 try { 177 return JSON.parse(item); 178 } catch { 179 return null; 180 } 181 } 182 183 function getCacheKey(key, lang) { 184 let cacheKey = PREFIX + key; 185 if (lang) { 186 cacheKey += `_${getLanguageAttr()}`; 187 } 188 189 return cacheKey; 190 } 191 192 return { 193 /** 194 * Store an object or string value in localstorage 195 * 196 * @param {string} key unique key 197 * @param {object} value string or object 198 * @param {Integer} ttl Time in seconds, default is 600 sec (10 min) 199 * @param {boolean} lang language option, to create cache with local, false as default 200 */ 201 set(key, value, ttl = 600, lang = false) { 202 // ttl in hours, 0 or negative means never expires 203 const cacheKey = getCacheKey(key, lang); 204 const cacheExp = getExpiry(ttl); 205 localStorage.setItem(cacheKey, buildItem(value, cacheExp)); 206 207 const cacheObjectKeys = this.get(CACHE_KEYS.KEYS, false) || { }; 208 cacheObjectKeys[cacheKey] = cacheExp; 209 localStorage.setItem(getCacheKey(CACHE_KEYS.KEYS, false), buildItem(cacheObjectKeys, 0)); 210 }, 211 212 /** 213 * Get the cached object from localstorage, using key and language option 214 * This function also check expiry of cache. 215 * 216 * @param {string} key unique key 217 * @param {boolean} lang language option, to return cached object with local, false as default 218 * @returns Cached object if valid (not expired) object is cached, otherwise null 219 */ 220 get(key, lang = false) { 221 const itemStr = localStorage.getItem(getCacheKey(key, lang)); 222 if (!itemStr) return null; 223 224 const item = parseItem(itemStr); 225 if (!item) return null; 226 227 if (item.exp && Date.now() > item.exp) { 228 this.remove(key, lang); 229 return null; 230 } 231 return item.v; 232 }, 233 234 /** 235 * Remove the object from localstorage cache 236 * 237 * @param {string} key unique key 238 * @param {boolean} lang language option, to use cached object with local, false as default 239 */ 240 remove(key, lang = false) { 241 localStorage.removeItem(getCacheKey(key, lang)); 242 }, 243 244 /** 245 * Clear all objects of axp localstorage 246 */ 247 clear(full = false) { 248 const cacheKeys = this.get(CACHE_KEYS.KEYS, false); 249 Object.keys(localStorage).forEach((key) => { 250 if (key.startsWith(PREFIX)) { 251 if (full && !CACHE_KEYS_IGNORE.includes(key)) localStorage.removeItem(key); 252 else { 253 const itemExp = cacheKeys[key]; 254 if (itemExp && Date.now() > itemExp) { 255 localStorage.removeItem(key); 256 delete cacheKeys[key]; 257 } 258 } 259 } 260 }); 261 localStorage.setItem(getCacheKey(CACHE_KEYS.KEYS, false), buildItem(cacheKeys, 0)); 262 }, 263 }; 264})(); 265 266function buildConfigURL(env, locale) { 267 const origin = getOrigin(); 268 const localePath = locale ? `${locale}/` : ''; 269 let configFileName = 'configs.json'; 270 if (SEPARATE_CONFIG_FILES.includes(env)) { 271 configFileName = `configs-${env}.json`; 272 } 273 const configURL = new URL(`${origin}/${localePath}${configFileName}`); 274 return configURL; 275} 276 277async function getConfig() { 278 const language = getLanguageAttr(); 279 const env = calcEnvironment(); 280 const configs = {}; 281 282 const fetchGlobalConfig = fetch(buildConfigURL(env)); 283 const fetchLocalConfig = fetch(buildConfigURL(env, language)); 284 try { 285 const responses = await Promise.all([fetchGlobalConfig, fetchLocalConfig]); 286 287 // Extract JSON data from responses 288 const [configJSON, configLocaleJSON] = await Promise.all(responses 289 .map((response) => response.text())); 290 291 // merge config and locale config 292 if (configJSON && configLocaleJSON) { 293 const config = JSON.parse(configJSON); 294 const configLocal = JSON.parse(configLocaleJSON); 295 configLocal.data.forEach((localeConfig) => { 296 const existing = config.data.find((c) => c.key === localeConfig.key); 297 if (existing) { 298 existing.value = localeConfig.value; 299 } else { 300 config.data.push(localeConfig); 301 } 302 }); 303 304 config.data?.forEach((c) => { 305 configs[c.key] = c.value; 306 }); 307 } 308 } catch (e) { 309 console.error('no config loaded', e); 310 } 311 312 return configs; 313} 314 315/** 316 * This function retrieves all configuration values for a given environment. 317 * 318 * @returns {Object} - All configuration values 319 */ 320export async function getAllConfigs() { 321 let configs = window.axp?.configs; 322 323 if (!configs) { 324 configs = LocalStorageCache.get(CACHE_KEYS.CONFIGS, true); 325 if (typeof (configs) !== 'object') { 326 configs = null; 327 } 328 window.axp.configs = configs; 329 } 330 331 if (!configs) { 332 configs = await getConfig(); 333 334 window.axp.configs = configs; 335 LocalStorageCache.set(CACHE_KEYS.CONFIGS, configs, 18000, true); 336 } 337 338 return window.axp.configs; 339} 340 341/** 342 * This function retrieves a configuration value for a given environment. 343 * 344 * @param {string} configParam - The configuration parameter to retrieve. 345 * @returns {string} - The value of the configuration parameter, or undefined. 346 */ 347export async function getConfigValue(configParam) { 348 const configs = await getAllConfigs(); 349 350 return configs?.[configParam]; 351} 352 353export const formatDate = (dateString) => { 354 const language = getLanguageAttr(); 355 const date = new Date(dateString); 356 const day = date.getDate().toString().padStart(2, '0');
357 const month = date.toLocaleString(language, { month: 'short' }); 358 const year = date.getFullYear(); 359 360 return `${day} ${month} ${year}`; 361}; 362 363/** 364 * Load band config file to check which all brand level overridden files needs to be loaded. 365 * 366 * @param {string} brand Brand code 367 * 368 * @returns brand config object 369 */ 370export async function getBrandConfigs() { 371 let bConfig = window?.axp?.brandConfig || LocalStorageCache.get(CACHE_KEYS.BRAND_CONFIGS, false); 372 373 if (!bConfig) { 374 const [config] = await Promise.all([ 375 import(`/scripts/configs/${window.axp.brandCode}.js`).catch((e) => { 376 console.warn('Error loading brand config:', e); 377 return { BRAND_CONFIGS: { noconfig: true } }; 378 }), 379 ]); 380 381 bConfig = config?.BRAND_CONFIGS; 382 LocalStorageCache.set(CACHE_KEYS.BRAND_CONFIGS, bConfig, 86400, false); 383 } 384 if (!window?.axp?.brandConfig) window.axp.brandConfig = bConfig; 385 386 return bConfig; 387} 388 389/** 390 * Get maximum phone length by country code 391 * 392 * @param {*} countryCode 393 * @returns phoneLength 394 */ 395export async function getMaxLengthByCountryCode(countryCode) { 396 const countryByCode = COUNTRY_LIST.find((c) => c.code === countryCode); 397 return countryByCode.phoneLength; 398} 399 400/** 401 * Get country ISO code 402 * @param {*} countryCode 403 * @returns Phone ISO code 404 */ 405export async function getCountryIso(countryCode) { 406 const countryIso = COUNTRY_LIST.find((c) => c.code === countryCode); 407 return countryIso.phone; 408} 409 410/** 411 * Return the correct image size based on block configuration, otherwise default size is returned 412 * @param {*} blockClasses - Block class list 413 * @param {*} defaultSize - default image size 414 * @returns imageSize 415 */ 416export function getImageSize(blockClasses, defaultSize = '750', isMobile = false) { 417 const filteredImg = Object.values(IMAGES).filter( 418 (img) => blockClasses?.contains(img.name), 419 ); 420 if (isMobile) { 421 return filteredImg?.length > 0 ? filteredImg[0]?.mobile : defaultSize; 422 } 423 return filteredImg?.length > 0 ? filteredImg[0]?.size : defaultSize; 424}
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.