1/*! Copyright (c) Safe As Milk. All rights reserved. */import{produce}from"immer";import{createStore}from"zustand";import plugins from"cart-plugins";const ALLOWED_STORE_KEYS=["cart","giftWrapping","latestAddedProduct","lineItemsBeingUpdated","noteBeingUpdated","productVariantsBeingAdded","recommendations"];class CartStore{#additionalFields={};#rootRoute;#store;static#instantiated=!1;constructor(fields={}){if(CartStore.#instantiated)throw new Error("Cart store instance already exists");CartStore.#instantiated=!0,this.#rootRoute=window.Shopify?.routes?.root??"/",this.#store=null,this.#additionalFields=Object.entries(fields).reduce(((acc,[key,value])=>({...acc,...typeof value!="function"?{[key]:value}:{}})),{}),this.#init(),this.#store.getState().cart.total_price===0&&localStorage.removeItem("freeShippingAnimationShown")}static async getCart(){try{return await(await fetch(`${window.Shopify?.routes?.root??"/"}cart.js`)).json()}catch(e){throw new Error(`Could not get cart data: "${e}"`)}}static async clearCart(){try{return await(await fetch(`${window.Shopify?.routes?.root??"/"}cart/clear.js`,{method:"POST"})).json()}catch(e){throw new Error(`Could not clear cart: "${e}"`)}}static async addVariantsToCart(variants){if(!variants)throw new Error("Variant(s) must be specified");const items=(Array.isArray(variants)?variants:[variants]).map((item=>Object.keys(item).find((key=>key.includes("properties")))?Object.entries(item).reduce(((newItem,[key,value])=>key.includes("properties")&&value!==""?{...newItem,properties:{...newItem.properties||{},[key.replace("properties[","").replace("]","")]:value}}:{...newItem,[key]:value}),{}):item)),response=await(await fetch(`${window.Shopify?.routes?.root??"/"}cart/add.js`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({items})})).json();if(response.status)throw new Error(this.#prepareErrorMessageDescription(response.description));return response.items}static async changeCartLineItem(requestBody={},signal=null){const{id,quantity,properties}=requestBody;if(!id)throw new Error("Line item id must be present in request body");const response=await(await fetch(`${window.Shopify?.routes?.root??"/"}cart/change.js`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({id,quantity,properties}),signal})).json();if(response.status)throw new Error(response.description);return response}static async updateCart(requestBody={},signal=null){if(!requestBody.hasOwnProperty("attributes")&&!requestBody.hasOwnProperty("updates")&&!requestBody.hasOwnProperty("note"))throw new Error("Cart 'attributes' or 'updates' must be specified");const response=await(await fetch(`${window.Shopify?.routes?.root??"/"}cart/update.js`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(requestBody),signal})).json();if(response.status)throw new Error(response.description);return response}getState(){return Object.fromEntries(Object.entries(this.#store.getState()).filter((([key])=>[...ALLOWED_STORE_KEYS,...Object.keys(this.#additionalFields)].includes(key))))}setState(field,value){if(field==="cart")throw new Error("Cannot set cart field directly");this.#store.getState().setFieldState(field,value)}subscribe(callback){if(!callback)throw new Error("Callback function must be provided");const filteredCallback=(state,prevState)=>{const filteredState=Object.fromEntries(Object.entries(state).filter((([key])=>[...ALLOWED_STORE_KEYS,...Object.keys(this.#additionalFields)].includes(key)))),filteredPrevState=Object.fromEntries(Object.entries(prevState).filter((([key])=>[...ALLOWED_STORE_KEYS,...Object.keys(this.#additionalFields)].includes(key))));return callback.call(this,filteredState,filteredPrevState)};return this.#store.subscribe(filteredCallback)}async clear(){document.dispatchEvent(new CustomEvent("on:cart:loading",{detail:{type:"CLEAR"}}));try{const cart=await this.#store.getState().clear();return document.dispatchEvent(new CustomEvent("on:cart:loaded",{detail:{cart,type:"CLEAR"}})),cart}catch(error){throw document.dispatchEvent(new CustomEvent("on:cart:failed",{detail:{error,type:"CLEAR"}})),error}}async add(variants){document.dispatchEvent(new CustomEvent("on:cart:loading",{detail:{type:"ADD"}}));try{const cart=await this.#store.getState().add(variants),items=(Array.isArray(variants)?variants:[variants]).map((v=>({id:v.id,quantity:Number(v.quantity)})));return document.dispatchEvent(new CustomEvent("on:cart:loaded",{detail:{items,cart,type:"ADD"}})),cart}catch(error){throw document.dispatchEvent(new CustomEvent("on:cart:failed",{detail:{error,type:"ADD"}})),error}}async change(requestBody){const{id,quantity,properties}=requestBody;document.dispatchEvent(new CustomEvent("on:cart:loading",{detail:{type:"CHANGE"}}));try{const cart=await this.#store.getState().change(id,quantity,properties);return document.dispatchEvent(new CustomEvent("on:cart:loaded",{detail:{cart,type:"CHANGE"}})),cart}catch(error){throw document.dispatchEvent(new CustomEvent("on:cart:failed",{detail:{error,type:"CHANGE"}})),error}}async update(cartUpdates,operationId="cartUpdate"){const{attributes,updates,note}=cartUpdates;document.dispatchEvent(new CustomEvent("on:cart:loading",{detail:{type:"UPDATE"}}));try{const cart=await this.#store.getState().update({attributes,updates,note},operationId);return document.dispatchEvent(new CustomEvent("on:cart:loaded",{detail:{cart,type:"UPDATE"}})),cart}catch(error){throw document.dispatchEvent(new CustomEvent("on:cart:failed",{detail:{error,type:"UPDATE"}})),error}}async refresh(){document.dispatchEvent(new CustomEvent("on:cart:loading",{detail:{type:"REFRESH"}}));try{const cart=await this.#store.getState().refresh();return document.dispatchEvent(new CustomEvent("on:cart:loaded",{detail:{cart,type:"REFRESH"}})),cart}catch(error){throw document.dispatchEvent(new CustomEvent("on:cart:failed",{detail:{error,type:"REFRESH"}})),error}}async enableCartRecommendations(intent="related"){if(intent!=="related"&&intent!=="complementary")throw new Error('Recommendations intent must be set to either "related" or "complementary"');this.cartRecommendationsIntent=intent,await this.#store.getState().enableRecommendations(intent)}async disableCartRecommendations(){this.cartRecommendationsIntent=null,await this.#store.getState().disableRecommendations()}async resetLatestAddedProduct(){await this.#store.getState().resetLatestAddedProduct()}async resetVariantsBeingAdded(id=""){await this.#store.getState().resetVariantsBeingAdded(id)}async setGiftWrapping(enable=!0){return await this.#store.getState().setGiftWrapping(enable)}async syncGiftWrapping(){await this.#store.getState().syncGiftWrapping()}async updateGiftWrappingMessage(message=""){return this.#store.getState().updateGiftWrappingMessage(message)}async updateCartNote(note=""){return this.#store.getState().updateNote(note)}#init(){const initialCartDataJson=document.getElementById("cart-data"),initialCartData=initialCartDataJson?JSON.parse(initialCartDataJson.textContent):{},giftWrappingDataJson=document.getElementById("cart-gift-wrapping-data"),giftWrappingData={giftWrapping:{...giftWrappingDataJson?{...JSON.parse(giftWrappingDataJson.textContent)}:{productId:null,wrapIndividually:!1},statusBeingUpdated:!1,messageBeingUpdated:!1}};this.#store=createStore(((set,get)=>({...this.#additionalFields,...giftWrappingData,lineItemsBeingUpdated:[],latestAddedProduct:null,noteBeingUpdated:!1,ongoingUpdates:{items:{},note:null,giftWrappingMessage:null},cart:initialCartData,recommendations:{enabled:!1,intent:"related",items:{}},productVariantsBeingAdded:[],setFieldState:async(field,value)=>{set(produce((draft=>{draft[field]=value})))},clear:async()=>{const cart=await CartStore.clearCart();return set(produce((draft=>{draft.lineItemsBeingUpdated=[],draft.latestAddedProduct=null,draft.cart={},draft.recommendations={},draft.productVariantsBeingAdded=[]}))),cart},add:async variants=>{if(!variants)throw new Error("Variant(s) must be specified");set(produce((draft=>{draft.productVariantsBeingAdded=(Array.isArray(variants)?variants:[variants]).map((v=>v.id))})));let error=null;try{const addedItems=await CartStore.addVariantsToCart(variants);set(produce((draft=>{draft.latestAddedProduct=addedItems?addedItems[0]:null})))}catch(e){error=e}
1const prevItemsCount=get().cart.item_count,cart=await CartStore.getCart();if(set(produce((draft=>{draft.cart=cart}))),prevItemsCount!==cart.item_count&&await get().updateGiftWrapping(),await get().updateRecommendations(),error)throw error;return get().cart},change:async(key,quantity,properties)=>{if(!key)throw new Error("Line item key must be specified");get().ongoingUpdates.items[key]&&(get().ongoingUpdates.items[key].abort(),set(produce((draft=>{draft.ongoingUpdates.items[key]=null}))));const controller=new AbortController,{signal}=controller;set(produce((draft=>{get().lineItemsBeingUpdated.includes(key)||draft.lineItemsBeingUpdated.push(key),draft.ongoingUpdates.items[key]=controller})));let cart,error=null;try{await CartStore.changeCartLineItem({quantity,properties,id:key},signal)}catch(e){error=e}cart=await CartStore.getCart();const prevItemsCount=get().cart.item_count;if(cart={...cart,items:cart.items.filter((item=>item.quantity>0))},set(produce((draft=>{draft.lineItemsBeingUpdated=draft.lineItemsBeingUpdated.filter((k=>k!==key)),draft.cart=cart,draft.ongoingUpdates.items[key]=null}))),prevItemsCount!==cart.item_count&&await get().updateGiftWrapping(),await get().updateRecommendations(),error)throw error;return cart},update:async({attributes,updates,note},operationId)=>{if(!attributes&&!updates&&!note)throw new Error("At least one of 'attributes', 'updates' or 'note must be specified");if(operationId==="items")throw new Error("'items' operation id is reserved");get().ongoingUpdates[operationId]&&(get().ongoingUpdates[operationId].abort(),set(produce((draft=>{draft.ongoingUpdates[operationId]=null}))));const controller=new AbortController,{signal}=controller;set(produce((draft=>{draft.ongoingUpdates[operationId]=controller}))),await CartStore.updateCart({attributes,updates,note},signal);const cart=await CartStore.getCart();return set(produce((draft=>{draft.cart=cart,draft.ongoingUpdates[operationId]=null}))),cart},refresh:async()=>{const cart=await CartStore.getCart(),prevItemsCount=get().cart.item_count;return set(produce((draft=>{draft.cart=cart}))),prevItemsCount!==cart.item_count&&await get().updateGiftWrapping(),await get().updateRecommendations(),cart},enableRecommendations:async(intent="related")=>{get().recommendations.enabled||(set(produce((draft=>{draft.recommendations.enabled=!0,draft.recommendations.intent=intent}))),await get().updateRecommendations())},disableRecommendations:async()=>{get().recommendations.enabled&&(set(produce((draft=>{draft.recommendations.enabled=!1}))),await get().updateRecommendations())},resetLatestAddedProduct:()=>{set(produce((draft=>{draft.latestAddedProduct=null})))},resetVariantsBeingAdded:(variantId="")=>{set(produce((draft=>{draft.productVariantsBeingAdded=variantId?draft.productVariantsBeingAdded.filter((id=>id!==variantId)):[]})))},setGiftWrapping:async(enable=!0)=>{const giftWrappingProductId=get().giftWrapping.productId;if(!giftWrappingProductId)return null;let cart;const oldCart=get().cart,giftWrappingIndividual=get().giftWrapping.wrapIndividually;if(enable){set(produce((draft=>{draft.giftWrapping.statusBeingUpdated=!0})));const savedGiftWrappingProductId=get().cart.attributes["_gift-wrapping-product-id"];await CartStore.updateCart({attributes:{...savedGiftWrappingProductId!==giftWrappingProductId?{"_gift-wrapping-product-id":giftWrappingProductId}:{},"gift-wrapping":enable,"gift-wrapping-message":""},updates:{[giftWrappingProductId]:giftWrappingIndividual?oldCart.items.reduce(((nonGiftWrappingQuantity,item)=>item.variant_id!==giftWrappingProductId?nonGiftWrappingQuantity+item.quantity:nonGiftWrappingQuantity),0):1}}),cart=await CartStore.getCart(),set(produce((draft=>{draft.giftWrapping.statusBeingUpdated=!1,draft.cart=cart})))}else{let signal=null,key=null;const giftWrappingItem=get().cart.items.find((item=>item.variant_id===giftWrappingProductId));if(set(produce((draft=>{draft.giftWrapping.statusBeingUpdated=!0}))),giftWrappingItem){key=giftWrappingItem.key,get().ongoingUpdates.items[key]&&(get().ongoingUpdates.items[key].abort(),set(produce((draft=>{draft.ongoingUpdates.items[key]=null}))));const controller=new AbortController;signal=controller.signal,set(produce((draft=>{get().lineItemsBeingUpdated.includes(key)||draft.lineItemsBeingUpdated.push(key),draft.ongoingUpdates.items[key]=controller})))}await CartStore.updateCart({attributes:{"_gift-wrapping-product-id":null,"gift-wrapping":enable,"gift-wrapping-message":""},updates:{[giftWrappingProductId]:0}},signal),cart=await CartStore.getCart(),set(produce((draft=>{key&&(draft.lineItemsBeingUpdated=draft.lineItemsBeingUpdated.filter((k=>k!==key))),draft.giftWrapping.statusBeingUpdated=!1,draft.cart=cart})))}return cart},syncGiftWrapping:async()=>{const giftWrappingProductId=get().giftWrapping.productId,savedGiftWrappingProductId=get().cart.attributes["_gift-wrapping-product-id"];if(savedGiftWrappingProductId&&savedGiftWrappingProductId!==giftWrappingProductId){const savedGiftWrappingProduct=get().cart.items.find((item=>item.id===savedGiftWrappingProductId));if(savedGiftWrappingProduct){get().ongoingUpdates.items[savedGiftWrappingProduct.key]&&(get().ongoingUpdates.items[savedGiftWrappingProduct.key].abort(),set(produce((draft=>{draft.ongoingUpdates.items[savedGiftWrappingProduct.key]=null}))));const controller=new AbortController,{signal}=controller;set(produce((draft=>{get().lineItemsBeingUpdated.includes(savedGiftWrappingProduct.key)||draft.lineItemsBeingUpdated.push(savedGiftWrappingProduct.key),draft.ongoingUpdates.items[savedGiftWrappingProduct.key]=controller}))),await CartStore.updateCart({attributes:{"_gift-wrapping-product-id":null},updates:{[savedGiftWrappingProductId]:0}},signal);const cart=await CartStore.getCart();set(produce((draft=>{draft.lineItemsBeingUpdated=draft.lineItemsBeingUpdated.filter((k=>k!==savedGiftWrappingProduct.key)),draft.giftWrapping.statusBeingUpdated=!1,draft.cart=cart})))}}if(!giftWrappingProductId)return;const giftWrappingEnabled=!!(get().cart.attributes["gift-wrapping"]!=="false"&&get().cart.attributes["gift-wrapping"]),giftWrappingIndividual=get().giftWrapping.wrapIndividually,giftWrappingItem=get().cart.items.find((item=>item.variant_id===giftWrappingProductId));!giftWrappingEnabled&&giftWrappingItem&&await get().setGiftWrapping(!1),giftWrappingEnabled&&!giftWrappingItem&&await get().setGiftWrapping(!0),giftWrappingEnabled&&giftWrappingItem&&giftWrappingIndividual&&giftWrappingItem.quantity===1&&await get().updateGiftWrapping(),giftWrappingEnabled&&giftWrappingItem&&!giftWrappingIndividual&&giftWrappingItem.quantity>1&&await get().updateGiftWrapping()},updateGiftWrapping:async()=>{const giftWrappingProductId=get().giftWrapping.productId;if(!giftWrappingProductId)return;const giftWrappingIndividual=get().giftWrapping.wrapIndividually,giftWrappingItem=get().cart.items.find((item=>item.variant_id===giftWrappingProductId));if(!giftWrappingItem||get().cart.items_count>1&&!giftWrappingIndividual&&giftWrappingItem)return;const{key}=giftWrappingItem;get().ongoingUpdates.items[key]&&(get().ongoingUpdates.items[key].abort(),set(produce((draft=>{draft.ongoingUpdates.items[key]=null}))));const controller=new AbortController,{signal}=controller;set(produce((draft=>{get().lineItemsBeingUpdated.includes(key)||draft.lineItemsBeingUpdated.push(key),draft.ongoingUpdates.items[key]=controller})));let giftWrappingQuantity=giftWrappingIndividual?get().cart.items.reduce(((nonGiftWrappingQuantity,item)=>item.variant_id!==giftWrappingProductId?nonGiftWrappingQuantity+item.quantity:nonGiftWrappingQuantity),0):1;giftWrappingItem&&get().cart.items.filter((i=>i.id!==giftWrappingProductId)).length===0&&(giftWrappingQuantity=0);const giftWrappingAttributeUpdate=giftWrappingQuantity===0?{attributes:{"gift-wrapping":!1,"gift-wrapping-message":""}}:{};await CartStore.updateCart({...giftWrappingAttributeUpdate,updates:{[giftWrappingProductId]:giftWrappingQuantity}},signal);const cart=await CartStore.getCart();set(produce((draft=>{draft.lineItemsBeingUpdated=draft.lineItemsBeingUpdated.filter((k=>k!==key)),draft.cart=cart})))},updateGiftWrappingMessage:async message=>{get().giftWrapping.messageBeingUpdated&&get().ongoingUpdates.giftWrappingMessage&&(get().ongoingUpdates.giftWrappingMessage.abort(),set(produce((draft=>{draft.ongoingUpdates.giftWrappingMessage=null,draft.giftWrapping.messageBeingUpdated=!1}))));const controller=new AbortController,{signal}=controller;set(produce((draft=>{draft.ongoingUpdates.giftWrappingMessage=controller,draft.giftWrapping.messageBeingUpdated=!0}))),await CartStore.updateCart({attributes:{"gift-wrapping-message":message}},signal);const cart=await CartStore.getCart();return set(produce((draft=>{draft.ongoingUpdates.giftWrappingMessage=null,draft.giftWrapping.messageBeingUpdated=!1,draft.cart=cart}))),cart},updateNote:async note=>{get().noteBeingUpdated&&get().ongoingUpdates.note&&(get().ongoingUpdates.note.abort(),set(produce((draft=>{draft.ongoingUpdates.note=null,draft.noteBeingUpdated=!1}))));const controller=new AbortController,{signal}=controller;set(produce((draft=>{draft.ongoingUpdates.note=controller,draft.noteBeingUpdated=!0}))),await CartStore.updateCart({note},signal);const cart=await CartStore.getCart();return set(produce((draft=>{draft.ongoingUpdates.note=null,draft.noteBeingUpdated=!1,draft.cart=cart}))),cart},updateRecommendations:async()=>
1{if(!get().recommendations.enabled)return;const weightDistribution="equal",{intent,items:existingRecommendations}=get().recommendations,newRecommendations={},existingRecommendationsIds=Object.keys(existingRecommendations),productIds=get().cart.items.reduce(((ids,item)=>(!item.gift_card&&!ids.includes(item.product_id.toString())&&ids.push(item.product_id.toString()),ids)),[]),remainingProductIds=existingRecommendationsIds.filter((productId=>productIds.includes(productId))),remainingRecommendations=remainingProductIds.reduce(((recommendations,productId)=>({...recommendations,[productId]:existingRecommendations[productId]})),{}),newProductIds=productIds.filter((productId=>!existingRecommendationsIds.includes(productId)));await Promise.all([...remainingProductIds.length<existingRecommendationsIds.length?remainingProductIds:[],...newProductIds].map((async productId=>{const recommendations=await(await fetch(`${this.#rootRoute}recommendations/products.json?product_id=${productId}&intent=${intent}`)).json();remainingProductIds.includes(productId)?remainingRecommendations[productId]=recommendations:newRecommendations[productId]=recommendations})));const finalRecommendations={...remainingRecommendations,...newRecommendations},calculateWeight=productId=>get().cart.items.filter((item=>item.product_id.toString()===productId)).reduce(((total,item)=>total+item.line_price),0)/get().cart.total_price;set(produce((draft=>{draft.recommendations.items=Object.keys(finalRecommendations).reduce(((recommendations,productId)=>({...recommendations,[productId]:{weight:weightDistribution==="proportional"?calculateWeight(productId):1/get().cart.items.reduce(((items,item)=>(items.includes(item.id)||items.push(item.id),items)),[]).length,products:finalRecommendations[productId].products}})),{})})))}})))}static#prepareErrorMessageDescription(description=""){return typeof description=="object"?Object.values(description).reduce(((message,value)=>{const newMessage=Array.isArray(value)?value.join("; "):value;return`${message}${newMessage} 2`}),""):description}}const Cart=new CartStore(plugins.reduce(((allFields,plugin)=>({...allFields,...plugin.fields?plugin.fields:{}})),{}));Cart.syncGiftWrapping();export{CartStore};export default Cart; 3//# sourceMappingURL=/cdn/shop/t/269/assets/cart-store.js.map?v=174650634943793070221782413371
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.