PageSourceSearch

https://www.americaneagle.ae/scripts/minicart/cart.js

js americaneagle.ae collected 2026-10-02 13:25:44 UTC 16,806 bytes, 516 lines download raw bytes

1/* eslint-disable import/no-cycle */
2import { store } from './api.js';
3import {
4  getSignInToken,
5  performCommerceRestMutation,
6  performCommerceRestQuery,
7  getProductData,
8} from '../commerce.js';
9
10import { getConfigValue, CARTID_STORE } from '../configs.js';
11import { getCustomer } from '../customer/api.js';
12import { datalayerCartError } from '../analytics/google-data-layer.js';
13import { setLoyaltyCardAPI } from '../giftcart/api.js';
14import { getAuraLocalStorageData } from '../aura/api.js';
15import { isLoggedInUser } from '../scripts.js';
16import { getCustomerTier } from '../plp-utils/preaccess.js';
17
18function fetcheGiftURL(sku) {
19  return `/V1/products?searchCriteria%5BpageSize%5D=5&searchCriteria%5BcurrentPage%5D=1&searchCriteria%5BfilterGroups%5D%5B0%5D%5Bfilters%5D%5B0%5D%5Bfield%5D=visibility&searchCriteria%5BfilterGroups%5D%5B0%5D%5Bfilters%5D%5B0%5D%5Bvalue%5D=4&searchCriteria%5BfilterGroups%5D%5B0%5D%5Bfilters%5D%5B0%5D%5BconditionType%5D=eq&searchCriteria%5BfilterGroups%5D%5B1%5D%5Bfilters%5D%5B0%5D%5Bfield%5D=status&searchCriteria%5BfilterGroups%5D%5B1%5D%5Bfilters%5D%5B0%5D%5Bvalue%5D=1&searchCriteria%5BfilterGroups%5D%5B1%5D%5Bfilters%5D%5B0%5D%5BconditionType%5D=eq&searchCriteria%5BfilterGroups%5D%5B2%5D%5Bfilters%5D%5B0%5D%5Bfield%5D=sku&searchCriteria%5BfilterGroups%5D%5B2%5D%5Bfilters%5D%5B0%5D%5Bvalue%5D=${sku}&searchCriteria%5BfilterGroups%5D%5B2%5D%5Bfilters%5D%5B0%5D%5BconditionType%5D=eq&searchCriteria%5BfilterGroups%5D%5B3%5D%5Bfilters%5D%5B0%5D%5Bfield%5D=type_id&searchCriteria%5BfilterGroups%5D%5B3%5D%5Bfilters%5D%5B0%5D%5Bvalue%5D=virtual&searchCriteria%5BfilterGroups%5D%5B3%5D%5Bfilters%5D%5B0%5D%5BconditionType%5D=eq`;
20}
21
22async function createUrl(endpoint) {
23  const baseUrl = await getConfigValue('commerce-rest-endpoint');
24
25  const storeViewCode = await getConfigValue('commerce-store-view-code');
26
27  const url = `${baseUrl}/${storeViewCode}/V1/${endpoint}`;
28
29  return url;
30}
31
32// TODO: Refactor
33const handleCartErrors = (data) => {
34  if (!data) {
35    return;
36  }
37  // Throw for everything
38  throw new Error(data.message || 'An error occurred');
39};
40
41export function waitForCart() {
42  const buttons = document.querySelectorAll('button.nav-cart-button, .minicart-header > .close');
43  buttons.forEach((button) => { button.disabled = true; });
44  return () => {
45    buttons.forEach((button) => { button.disabled = false; });
46  };
47}
48
49export async function getCart(resetCartStore = false) {
50  const token = getSignInToken();
51
52  if (!token && !store.getCartId()) {
53    return;
54  }
55
56  const done = waitForCart();
57  let data;
58  let success;
59  try {
60    let url;
61
62    if (token) {
63      url = await createUrl('carts/mine/getCart');
64    } else {
65      url = await createUrl(`guest-carts/${store.getCartId()}/getCart`);
66    }
67
68    const response = await performCommerceRestQuery(url, !!token);
69
70    ({ data, success } = response);
71
72    if (!success) {
73      handleCartErrors(data);
74    }
75
76    if (!data) {
77      store.resetCart();
78    }
79
80    if (data) {
81      window.dispatchEvent(new CustomEvent('updateMiniCartResult', { detail: data }));
82    }
83
84    data.cart.items = data.cart.items.filter((item) => item);
85    const cartObj = {
86      id: data.cart.id,
87      cart: data.cart,
88      totals: data.totals,
89    };
90
91    if (resetCartStore) {
92      const newCartId = token ? cartObj.id : store.getCartId();
93      store.resetCart();
94      await store.setCartId(newCartId);
95    }
96
97    store.setCart(cartObj);
98  } catch (err) {
99    console.error('Could not fetch cart');
100  } finally {
101    done();
102  }
103}
104
105export async function createCart() {
106  try {
107    const token = getSignInToken();
108
109    let url;
110    if (token) {
111      url = await createUrl('carts/mine');
112    } else {
113      url = await createUrl('guest-carts');
114    }
115
116    const response = await performCommerceRestMutation(url, {}, !!token);
117
118    console.error(response);
119
120    if (!response.success) {
121      handleCartErrors(response);
122    }
123    const cartId = response.data;
124    await store.setCartId(cartId);
125    const auraData = getAuraLocalStorageData();
126    const currentTier = await getCustomerTier();
127    const apcIdentifierNumber = auraData?.aura_membership;
128    const auraLink = auraData?.apc_link ? parseInt(auraData.apc_link, 10) : null;
129    if (apcIdentifierNumber && auraLink
130      && (auraLink === 2 || auraLink === 3 || (!isLoggedInUser() && auraLink === 1))) {
131      // setLoyaltyCard only if aura account is linked
132      await setLoyaltyCardAPI(apcIdentifierNumber);
133    } else if (currentTier === 'member' || currentTier === 'plus') {
134      const custResponse = await getCustomer();
135      if (custResponse?.custom_attributes) {
136        const apcIdentifierNum = custResponse.custom_attributes.find((attribute) =>
136 attribute.attribute_code === 'apc_identifier_number')?.value;
137        await setLoyaltyCardAPI(apcIdentifierNum);
138      }
139    }
140    console.debug('created empty cart', cartId);
141  } catch (err) {
142    console.error('Could not create empty cart', err);
143  }
144}
145
146function mapOption(option) {
147  const decoded = atob(option);
148  // eslint-disable-next-line no-unused-vars
149  const [optionType, optionId, optionValue] = decoded.split('/');
150  if (optionType === 'bundle') {
151    return {
152      option_id: optionId,
153      option_selections: [optionValue],
154    };
155  }
156  return {
157    option_id: optionId,
158    option_value: optionValue,
159  };
160}
161
162async function getCartInFailure(token, response) {
163  const url = token ? await createUrl('carts/mine/getCart') : await createUrl(`guest-carts/${store.getCartId()}/getCart`);
164  const updatedCartFromApi = await performCommerceRestQuery(url, !!token);
165  const isCartLocked = updatedCartFromApi?.data?.cart?.extension_attributes?.is_locked;
166  if (isCartLocked) {
167    response.errorCode = 'CART_LOCKED_ERROR';
168  }
169  return response?.errorCode;
170}
171
172export async function addToCart(sku, options, quantity, source = 'product-detail') {
173  let isBundle = false;
174  if (options?.length > 0) {
175    isBundle = options.some((option) => {
176      const decodeData = atob(option);
177      const [optionType] = decodeData.split('/');
178      return optionType === 'bundle';
179    });
180  }
181
182  const done = waitForCart();
183  let response;
184  let isEGift = false;
185  try {
186    let variables;
187    if (source === 'egift-purchase') {
188      isEGift = true;
189      variables = {
190        cartItem: {
191          quote_id: store.getCartId(),
192          product_type: options.product_type,
193          sku,
194          qty: quantity,
195          product_option: {
196            extension_attributes: {
197              hps_giftcard_item_option: {
198                hps_custom_giftcard_amount: options.custom_giftcard_amount,
199                hps_giftcard_amount: options.giftcard_amount,
200                hps_giftcard_sender_name: options.giftcard_sender_name,
201                hps_giftcard_sender_email: options.giftcard_sender_email,
202                hps_giftcard_recipient_name: options.giftcard_recipient_name,
203                hps_giftcard_recipient_email: options.giftcard_recipient_email,
204                hps_giftcard_message: options.giftcard_message,
205                extension_attributes: {},
206              },
207            },
208          },
209        },
210      };
211    } else if (source === 'product-landing') {
212      variables = {
213        cartItem: {
214          sku,
215          qty: quantity,
216          quote_id: store.getCartId(),
217          product_option: {
218            extension_attributes: {},
219          },
220        },
221      };
222
223      if (isBundle) {
224        const getOptions = options.map((option) => mapOption(option));
225        variables.cartItem.product_option.extension_attributes.bundle_options = getOptions;
226      }
227    } else {
228      variables = {
229        cartItem: {
230          sku,
231          qty: quantity,
232          product_option: {
233            extension_attributes: {
234              configurable_item_options: options.map((option) => mapOption(option)),
235              custom_options: [],
236            },
237          },
238          quote_id: store.getCartId(),
239        },
240      };
241
242      if (isBundle) {
243        delete variables.cartItem.product_option.extension_attributes.configurable_item_options;
244        // Add bundle_options
245        const getOptions = options.map((option) => mapOption(option));
246        variables.cartItem.product_option.extension_attributes.bundle_options = getOptions;
247      }
248    }
249
250    const token = getSignInToken();
251    // Check for numeric guest cart ID and refresh if needed
252    if (!token) {
253      const currentCartId = store.getCartId();
254      if (currentCartId && /^\d+$/.test(currentCartId)) {
255        store.resetCart();
256        await createCart();
257      }
258    }
259    let url;
260
261    if (token) {
262      url = await createUrl('carts/mine/items');
263    } else {
264      url = await createUrl(`guest-carts/${store.getCartId()}/items`);
265    }
266
267    response = await performCommerceRestMutation(url, variables, !!token);
268
269    if (!response.success) {
270      const fieldName = response.data?.parameters?.fieldName;
271      const { message } = response;
272
273      const errorConditions = {
274        customerCartId: 'No such entity with %fieldName = %fieldValue',
275        guestCartId: '"%fieldName" is required. Enter and try again.',
276      };
277
278      // If carId is not found clean the cart
279      if ((message === errorConditions.customerCartId && fieldName === 'cartId')
280        || (message === errorConditions.guestCartId && fieldName === 'quoteId')) {
281        store.resetCart();
282        // Handle translation
283        response.message = 'Something went wrong. Please try again.';
284        response.errorCode = 'GENERIC_ERROR';
285        window.localStorage.removeItem(CARTID_STORE);
286      } else {
287        response.errorCode = await getCartInFailure(token, response);
288      }
289
290      handleCartErrors(response);
291    }
292
293    const { data: updatedCart } = response;
294    if (updatedCart) {
295      const product = await getProductData(sku);
296
297      await getCart();
298
299      if (isEGift) {
300        product.iseGift = true;
301        if (window.egiftItems && window.egiftItems.some((item) => item.sku === sku)) {
302          const eGiftItem = window.egiftItems.find((item) => item.sku === sku);
303          product.eGiftImage = eGiftItem.image;
304        } else {
305          const storeCode = await getConfigValue('commerce-store-view-code');
306          const commerceRestEndpoint = await getConfigValue('commerce-rest-endpoint');
307          const prefix = storeCode ? `/${storeCode}` : '';
308          const egiftUrl = fetcheGiftURL(sku);
309          const egiftRestEndpoint = `${commerceRestEndpoint}${prefix}${egiftUrl}`;
310          const data = await performCommerceRestQuery(egiftRestEndpoint, false);
311          product.eGiftImage = data?.data?.items[0]?.custom_attributes?.find((attr) => attr.attribute_code === 'image')?.value;
312        }
313      }
314      document.querySelector('main').dispatchEvent(new CustomEvent('addtobag-updated', {
315        detail: {
316          product,
317          cart: store.getCart(),
318          updatedCart,
319        },
320      }));
321    }
322    return response;
323  } catch (err) {
324    console.error('Could not add item to cart', err);
325    const errorText = response?.message;
326    const cartId = store.getCartId();
327    datalayerCartError(errorText, cartId);
328    return response;
329  } finally {
330    done();
331  }
332}
333
334export async function updateItemInCart(itemId, sku, quantity) {
335  const done = waitForCart();
336  const variables = {
337    cartItem: {
338      quote_id: store.getCartId(),
339      sku,
340      qty: quantity,
341      item_id: itemId,
342      product_option: {},
343    },
344  };
345  let response;
346  try {
347    const token = getSignInToken();
348
349    let url;
350    if (token) {
351      url = await createUrl(`carts/mine/items/${itemId}`);
352    } else {
353      url = await createUrl(`guest-carts/${store.getCartId()}/items/${itemId}`);
354    }
355    response = await performCommerceRestMutation(url, variables, !!token, 'PUT');
356    console.error(response);
357
358    if (!response.success) {
359      response.errorCode = await getCartInFailure(token, response);
360      handleCartErrors(response);
361    }
362
363    await getCart();
364
365    console.debug('updated items in cart', itemId);
366
367    return response;
368  } catch (err) {
369    console.error('Could not update item in cart', err);
370    return response;
371  } finally {
372    done();
373  }
374}
375
376export async function removeItemFromCart(itemId) {
377  const done = waitForCart();
378  let response;
379  try {
380    const token = getSignInToken();
381
382    let url;
383    if (token) {
384      url = await createUrl(`carts/mine/items/${itemId}`);
385    } else {
386      url = await createUrl(`guest-carts/${store.getCartId()}/items/${itemId}`);
387    }
388
389    response = await performCommerceRestMutation(url, null, !!token, 'DELETE');
390
391    console.error(response);
392
393    if (!response.success) {
394      response.errorCode = await getCartInFailure(token, response);
395      handleCartErrors(response);
396    }
397
398    const cart = await getCart();
399    console.debug('removed item from cart', itemId, cart?.id);
400    return response;
401  } catch (err) {
402    console.error('Could not remove item from cart', err);
403    return response;
404  } finally {
405    done();
406  }
407}
408
409export async function mergeCart() {
410  try {
411    const token = getSignInToken();
412
413    if (!token) {
414      return;
415    }
416
417    // check if guest exists and has items
418    const guestCart = store.getCart();
419
420    if (guestCart?.id && guestCart?.cart?.items?.length > 0) {
421      const custResponse = await getCustomer();
422
423      console.log('mergeCart custResponse', custResponse);
424
425      const { id: customerId, store_id: storeId } = custResponse;
426
427      console.debug('Merging cart for customer', customerId, storeId);
428
429      const url = await createUrl(`cart-merge/customer-id/${customerId}/active-quote/${guestCart.id}/store-id/${storeId}`);
430
431      const response = await performCommerceRestMutation(url, [], true, 'PUT');
432
433      if (!response.success) {
434        console.error('Error merging cart', response.status, response.message);
435        return;
436      }
437    }
438
439    // load cart anyways
440    const cart = await getCart(true);
441
442    console.debug('Merged cart', cart);
443  } catch (err) {
444    console.error('Could not merge cart', err);
445  }
446}
447
448// Helper function for loading external scripts dynamically.
449function loadScript(src) {
450  const script = document.createElement('script');
451  script.type = 'text/javascript';
452  script.src = src;
453  (document.head || document.body).appendChild(script);
454}
455
456// Helper function to get item options.
457function getItemSizeById(items, itemId) {
458  const item = items.find((currentItem) => currentItem.item_id === itemId);
459  if (!item) return null;
460  try {
461    const options = JSON.parse(item.options);
462    return options[0]?.value || null;
463  } catch (error) {
464    console.error('Error parsing item options:', error);
465    return null;
466  }
467}
468
469// Helper function for initialising ShoeSizeShoppingCart.
470async function initialiseShoeSizeShoppingCart(event) {
471  const { cart: cartData, totals } = event.detail;
472
473  if (!cartData?.items?.length || !totals?.items) return;
474
475  try {
476    // Retrieve configuration values for the shopping cart initialization.
477    const [domainProd, domainStage, shopId, shoeAiMode] = await Promise.all([
478      getConfigValue('domain_production'),
479      getConfigValue('domain_staging'),
480      getConfigValue('shop_id'),
481      getConfigValue('shoe_ai_mode'),
482    ]);
483    // Determine the domain based on the AI mode.
484    const domain = shoeAiMode === 'prod' ? domainProd : domainStage;
485    // Filter cart items to include only shoe products and map them to the desired format.
486    const shoePurchases = cartData.items
487      .filter((item) => item?.name?.toLowerCase().includes('shoes'))
488      .map((item) => ({
489        shoeID: item.extension_attributes?.parent_product_sku || item.sku,
490        quantity: item.qty,
491        price: item.price,
492        currency: cartData.currency.base_currency_code,
493        size: getItemSizeById(totals.items, item.item_id),
494        scale: 'eu',
495      }));
496    // Exit the function if there are no shoe purchases in the cart.
497    if (shoePurchases.length === 0) return;
498    // Create the ShoeSizeShoppingCart object with shop ID and shoe purchases.
499    const ShoeSizeShoppingCart = {
500      shopID: shopId,
501      purchases: shoePurchases,
502    };
503    const timestamp = Math.round(Date.now() / 1000);
504    const scriptSrc = `${domain}/plugin/cart.js?shopid=${encodeURIComponent(ShoeSizeShoppingCart.shopID)}&sid=${timestamp}`;
505    // Load the script asynchronously.
506    loadScript(scriptSrc);
507  } catch (error) {
508    console.error('Error initializing ShoeSizeShoppingCart:', error);
509  }
510}
511// Check if the Shoe AI feature is enabled.
512const enableShoeai = (await getConfigValue('enable_shoeai')) === 'true';
513// Add event listener to initialize ShoeSizeShoppingCart on cart update if feature is enabled.
514if (enableShoeai) {
515  window.addEventListener('updateMiniCartResult', initialiseShoeSizeShoppingCart);
516}

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.