unified-datalayer
Version:
A multi-framework utility package for managing XP Data Layer events
6 lines (5 loc) • 19.8 kB
JavaScript
class G{dataLayer;constructor(_){this.dataLayer=_}pushEvent(_,w={}){return this.dataLayer.pushEvent(_,w)}clearProducts(){this.dataLayer.clearProducts()}formatString(_){if(!_)return"";let w=_.toLowerCase();return w=w.trim(),w=w.replace(/\|/g," "),w=w.replace(/\s+/g,"-"),w}formatProduct(_){let w={brand:this.formatString(_.brand),category:_.category?_.category.map(($)=>this.formatString($)).join(","):"",child_sku:_.child_sku,color:this.formatString(_.color),discount:Number(((_.full_price-_.listed_price)/_.full_price*100).toFixed(2)),feature:_.feature||[],full_price:_.full_price,gender:this.formatString(_.gender),is_markdown:_.full_price!==_.listed_price,listed_price:_.listed_price,name:this.formatString(_.name),parent_category:this.formatString(_.parent_category),parent_sku:_.parent_sku,sku_available:_.sku_available||!1};if(_.available_size)w.available_size=_.available_size;if(_.barcode)w.barcode=_.barcode;if(_.rating)w.rating=_.rating;if(_.reward_points)w.reward_points=_.reward_points;if(_.model)w.model=this.formatString(_.model);if(_.specialty)w.specialty=this.formatString(_.specialty);if(_.sport)w.sport=this.formatString(_.sport);if(_.story)w.story=this.formatString(_.story);return w}formatCartItem(_){let w={brand:this.formatString(_.brand),category:_.category?_.category.map(($)=>this.formatString($)).join(","):"",child_sku:_.child_sku,color:this.formatString(_.color),discount:Number(((_.full_price-_.listed_price)/_.full_price*100).toFixed(2)),feature:_.feature||[],full_price:_.full_price,gender:this.formatString(_.gender),is_markdown:_.full_price!==_.listed_price,listed_price:_.listed_price,name:this.formatString(_.name),parent_category:this.formatString(_.parent_category),parent_sku:_.parent_sku,sku_available:_.sku_available||!1,size:_.size,sku_by_size:_.sku_by_size,qty:_.qty};if(_.available_size)w.available_size=_.available_size;if(_.barcode)w.barcode=_.barcode;if(_.rating)w.rating=_.rating;if(_.reward_points)w.reward_points=_.reward_points;if(_.model)w.model=this.formatString(_.model);if(_.specialty)w.specialty=this.formatString(_.specialty);if(_.sport)w.sport=this.formatString(_.sport);if(_.story)w.story=this.formatString(_.story);return w}validateString(_,w,$={}){let{required:C=!0,allowEmpty:q=!1,minLength:R,maxLength:P,pattern:U}=$;if(C&&(_===void 0||_===null))throw new Error(`${w} is required.`,{cause:"REQUIRED_FIELD_MISSING"});if(typeof _!=="string")throw new Error(`${w} must be a string.`,{cause:"TYPE_MISMATCH"});if(!q&&_.trim().length===0)throw new Error(`${w} cannot be empty or whitespace.`,{cause:"EMPTY_VALUE_NOT_ALLOWED"});if(R!==void 0&&_.length<R)throw new Error(`${w} must be at least ${R} characters long.`,{cause:"STRING_LENGTH_VIOLATION"});if(P!==void 0&&_.length>P)throw new Error(`${w} cannot exceed ${P} characters.`,{cause:"STRING_LENGTH_VIOLATION"});if(U&&!U.test(_))throw new Error(`${w} format is invalid.`,{cause:"PATTERN_MISMATCH"})}validateNumber(_,w,$={}){let{required:C=!0,integer:q=!1,min:R,max:P,positive:U=!1}=$;if(C&&(_===void 0||_===null))throw new Error(`${w} is required.`,{cause:"REQUIRED_FIELD_MISSING"});if(typeof _!=="number")throw new Error(`${w} must be a number.`,{cause:"TYPE_MISMATCH"});if(isNaN(_))throw new Error(`${w} must be a valid number.`,{cause:"TYPE_MISMATCH"});if(q&&!Number.isInteger(_))throw new Error(`${w} must be an integer.`,{cause:"TYPE_MISMATCH"});if(U&&_<0)throw new Error(`${w} must be positive.`,{cause:"VALUE_OUT_OF_RANGE"});if(R!==void 0&&_<R)throw new Error(`${w} must be at least ${R}.`,{cause:"VALUE_OUT_OF_RANGE"});if(P!==void 0&&_>P)throw new Error(`${w} cannot exceed ${P}.`,{cause:"VALUE_OUT_OF_RANGE"})}validateMultiple(_){for(let w of _)if(w.type==="string")this.validateString(w.value,w.paramName,w.options);else if(w.type==="number")this.validateNumber(w.value,w.paramName,w.options)}validateProductData(_,w="productData"){if(!_||typeof _!=="object")throw new Error(`${w} must be an object.`,{cause:"FORMAT_VALIDATION_FAILED"});let $=[],C=(q)=>{try{q()}catch(R){$.push(`${R.message}`)}};if(C(()=>this.validateString(_.brand,"brand")),C(()=>this.validateString(_.child_sku,"child_sku")),C(()=>this.validateString(_.color,"color")),C(()=>this.validateString(_.gender,"gender")),C(()=>this.validateString(_.name,"name")),C(()=>this.validateString(_.parent_category,"parent_category")),C(()=>this.validateString(_.parent_sku,"parent_sku")),C(()=>this.validateNumber(_.full_price,"full_price",{positive:!0})),C(()=>this.validateNumber(_.listed_price,"listed_price",{positive:!0})),!Array.isArray(_.category))$.push("category: must be an array");else _.category.forEach((q,R)=>{C(()=>this.validateString(q,`category[${R}]`))});if(_.sku_available!==void 0&&typeof _.sku_available!=="boolean")$.push("sku_available: must be a boolean");if($.length>0){let q=`${w} validation failed:
+ ${$.join(`
+ `)}`;throw new Error(q,{cause:"FORMAT_VALIDATION_FAILED"})}}validateCartProductData(_,w="cartProductData"){if(!_||typeof _!=="object")throw new Error(`${w} must be an object.`,{cause:"FORMAT_VALIDATION_FAILED"});let $=[],C=(q)=>{try{q()}catch(R){$.push(`${R.message}`)}};if(C(()=>this.validateString(_.brand,"brand")),C(()=>this.validateString(_.child_sku,"child_sku")),C(()=>this.validateString(_.color,"color")),C(()=>this.validateString(_.gender,"gender")),C(()=>this.validateString(_.name,"name")),C(()=>this.validateString(_.parent_category,"parent_category")),C(()=>this.validateString(_.parent_sku,"parent_sku")),C(()=>this.validateString(_.sku_by_size,"sku_by_size")),C(()=>this.validateString(_.size,"size")),C(()=>this.validateNumber(_.full_price,"full_price",{positive:!0})),C(()=>this.validateNumber(_.listed_price,"listed_price",{positive:!0})),C(()=>this.validateNumber(_.qty,"qty",{positive:!0})),!Array.isArray(_.category))$.push("category: must be an array");else _.category.forEach((q,R)=>{C(()=>this.validateString(q,`category[${R}]`))});if(_.sku_available!==void 0&&typeof _.sku_available!=="boolean")$.push("sku_available: must be a boolean");if($.length>0){let q=`${w} validation failed:
+ ${$.join(`
+ `)}`;throw new Error(q,{cause:"FORMAT_VALIDATION_FAILED"})}}}class J extends G{createStart(){try{this.pushEvent("account_create-start",{default:{page:{type:"account",action:"create-start"}},form_info:{name:"create-account",type:"registration"}})}catch(_){console.error(`[unified-datalayer] ${_.cause}: ${_.message}`)}}createComplete(){try{this.pushEvent("account_create-complete",{default:{page:{type:"account",action:"create-complete"}},form_info:{name:"create-account",type:"registration",nl_subscription:!0,loyalty_subscription:!0}})}catch(_){console.error(`[unified-datalayer] ${_.cause}: ${_.message}`)}}loginStart(){try{this.pushEvent("account_login-start",{default:{page:{type:"account",action:"login-start"}},form_info:{name:"account-login",type:"login"}})}catch(_){console.error(`[unified-datalayer] ${_.cause}: ${_.message}`)}}loginSuccess(){try{this.pushEvent("account_login-success",{default:{page:{type:"account",action:"login-complete"},user:{user_state:window.localStorage.getItem("uem_hashed")?"customer":"guest",login_state:window.localStorage.getItem("uem_hashed")?"logged-in":"anonymous",uem_hashed:window.localStorage.getItem("uem_hashed")??"",session_id:"",division_id:""}},form_info:{name:"account-login",type:"login"}})}catch(_){console.error(`[unified-datalayer] ${_.cause}: ${_.message}`)}}}class W extends G{cartItems=[];cartInfo={cartId:"",quoteId:"",cart_quantity:"0",cart_total:"0.00"};calculateCart(_){let w=_.reduce((C,q)=>C+(q.qty||0),0),$=_.reduce((C,q)=>C+(q.listed_price||0)*(q.qty||0),0);return{cart_quantity:w.toString(),cart_total:$.toFixed(2)}}add(_){try{this.validateCartProductData(_);let w=this.formatCartItem(_),$=this.cartItems.findIndex((q)=>q.child_sku===_.child_sku);if($!==-1)this.cartItems[$].qty=(this.cartItems[$].qty||0)+(_.qty||1);else this.cartItems.push(_);let C=this.calculateCart(this.cartItems);this.cartInfo={...this.cartInfo,...C},this.pushEvent("cart_add",{default:{page:{type:"cart",action:"add",name:"add-to-cart"}},cart_items:[w]})}catch(w){console.error(`[unified-datalayer] ${w.cause}: ${w.message}`)}}remove(_){try{this.validateString(_,"childSku");let w=this.cartItems.findIndex((P)=>P.child_sku===_);if(w===-1){console.warn(`Item with SKU ${_} not found in cart`);return}let $={...this.cartItems[w]};this.cartItems.splice(w,1);let C=this.calculateCart(this.cartItems);this.cartInfo={...this.cartInfo,...C};let q=this.formatCartItem($),R=[...this.cartItems.map((P)=>this.formatCartItem(P)),null];this.pushEvent("cart_remove",{default:{page:{type:"cart",action:"remove",name:"remove-from-cart"}},cart_item_removed:q,cart_items:R,cart:this.cartInfo})}catch(w){console.error(`[unified-datalayer] ${w.cause}: ${w.message}`)}}update(_,w){try{this.validateMultiple([{value:_,paramName:"childSku",type:"string"},{value:w,paramName:"quantity",type:"number"}]);let $=this.cartItems.findIndex((R)=>R.child_sku===_);if($===-1){console.warn(`Item with SKU ${_} not found in cart`);return}this.cartItems[$].qty=w;let C=this.calculateCart(this.cartItems);this.cartInfo={...this.cartInfo,...C};let q=this.cartItems.map((R)=>this.formatCartItem(R));this.pushEvent("cart_update",{default:{page:{type:"cart",action:"update",name:"update-cart-item-qty"}},cart_items:q,cart:this.cartInfo})}catch($){console.error(`[unified-datalayer] ${$.cause}: ${$.message}`)}}miniView(_,w){try{if(!_)throw new Error("items is required.",{cause:"REQUIRED_FIELD_MISSING"});if(w?.cartId)this.validateString(w.cartId,"cartId");else if(w?.quoteId)this.validateString(w.quoteId,"quoteId");else throw new Error("either cartId or quoteId is required.",{cause:"REQUIRED_FIELD_MISSING"});_.forEach((C)=>{this.validateCartProductData(C)}),this.syncCartState(_,w);let $=this.cartItems.map((C)=>this.formatCartItem(C));this.pushEvent("cart_view-mini",{default:{page:{type:"cart",action:"view-mini",name:"view-mini-cart"}},cart_items:$,cart:this.cartInfo})}catch($){console.error(`[unified-datalayer] ${$.cause}: ${$.message}`)}}fullView(_,w){try{if(!_)throw new Error("items is required.",{cause:"REQUIRED_FIELD_MISSING"});if(w.cartId)this.validateString(w.cartId,"cartId");else if(w.quoteId)this.validateString(w.quoteId,"quoteId");else throw new Error("either cartId or quoteId is required.",{cause:"REQUIRED_FIELD_MISSING"});_.forEach((C)=>{this.validateCartProductData(C)}),this.syncCartState(_,w);let $=this.cartItems.map((C)=>this.formatCartItem(C));this.pushEvent("cart_view-full",{default:{page:{type:"cart",action:"view-full",name:"view-full-cart"}},cart_items:$,cart:this.cartInfo})}catch($){console.error(`[unified-datalayer] ${$.cause}: ${$.message}`)}}syncCartState(_,w){this.cartItems=[..._],this.cartInfo={cartId:w.cartId||"",quoteId:w.quoteId||"",...this.calculateCart(_)}}getCartItems(){return[...this.cartItems]}getCartInfo(){return{...this.cartInfo}}}class X extends G{calculateCart(_){let w=_.reduce((C,q)=>C+(q.qty||0),0),$=_.reduce((C,q)=>C+(q.listed_price||0)*(q.qty||0),0);return{cart_quantity:w.toString(),cart_total:$.toFixed(2)}}start(_,w,$="regular"){try{if(!_)throw new Error("items is required.",{cause:"REQUIRED_FIELD_MISSING"});if(w?.cartId)this.validateString(w.cartId,"cartId");else if(w?.quoteId)this.validateString(w.quoteId,"quoteId");else throw new Error("either cartId or quoteId is required.",{cause:"REQUIRED_FIELD_MISSING"});_.forEach((R)=>{this.validateCartProductData(R)});let C=_.map((R)=>this.formatCartItem(R)),q={...this.calculateCart(_),...w};this.pushEvent("checkout_start",{default:{page:{type:"checkout",action:"start"},user:{user_state:window.localStorage.getItem("uem_hashed")?"customer":"guest",login_state:window.localStorage.getItem("uem_hashed")?"logged-in":"anonymous",uem_hashed:window.localStorage.getItem("uem_hashed")??"",session_id:"",division_id:""}},checkout_type:$,cart:q,cart_items:C})}catch(C){console.error(`[unified-datalayer] ${C.cause}: ${C.message}`)}}step2(){try{}catch(_){console.error(`[unified-datalayer] ${_.cause}: ${_.message}`)}}step3(){try{}catch(_){console.error(`[unified-datalayer] ${_.cause}: ${_.message}`)}}}class Y extends G{success(){try{this.pushEvent("order_success",{default:{page:{type:"order",action:"success"}},order:{orderId:"2000013527",currency:"AUD",checkout_type:"regular",newsletter_signup:!0,geo:{state:"nsw",city:"wolli-creek",zipcode:"2205"},payments:[{type:"creditcard",method:"braintree-vi",amount:359.9,status:"processing"},{type:"loyalty-points",method:"loyalty",points_total:300,amount:30}],delivery:{type:"delivery",method:"delivery"},shipping:{base:9,gst:1},total:189.99,revenue:170.14,tax:19.85},cart:{cartId:"3f176aac-f6a6-4b0e-8f4c-555143560c7a",cart_quantity:"1",cart_total:"189.99"},cart_items:[{brand:"asics",gender:"women",parent_category:"footwear",category:"womens-run",model:"gel-kayano",story:"gel-kayano-25",color:"blue",sport:"run",speciality:"run",parent_sku:"1012A026",child_sku:"1012A026-101",name:"asics-gel-kayano-25-womens-skylight-illusion-blue",qty:1,sale_price:170.14,sale_GST:19.85,is_markdown:!1}]})}catch(_){console.error(`[unified-datalayer] ${_.cause}: ${_.message}`)}}}class Z extends G{home(){this.pushEvent("page_default",{default:{page:{type:"home",action:"view",path:typeof window!=="undefined"?window.location.pathname:"",title:typeof document!=="undefined"?this.formatString(document.title):"",url:typeof window!=="undefined"?window.location.href:""}}})}view(_,w="view"){try{this.validateString(_,"pageType"),this.validateString(w,"action"),this.pushEvent("page_default",{default:{page:{type:_,action:w,path:typeof window!=="undefined"?window.location.pathname:"",title:typeof document!=="undefined"?this.formatString(document.title):"",url:typeof window!=="undefined"?window.location.href:""}}})}catch($){console.error(`[unified-datalayer] ${$.cause}: ${$.message}`)}}error(){}}class M extends G{view(_){try{this.validateProductData(_);let w=[this.formatProduct(_)];this.clearProducts(),this.pushEvent("product_view",{default:{page:{type:"product",action:"view",path:typeof window!=="undefined"?window.location.pathname:"",title:typeof document!=="undefined"?this.formatString(document.title):"",url:typeof window!=="undefined"?window.location.href:""}},products:w})}catch(w){console.error(`[unified-datalayer] ${w.cause}: ${w.message}`)}}colorSelect(_){try{this.validateString(_,"color"),this.pushEvent("product_color-select",{default:{page:{type:"product",action:"color-select"}},products:[{color:_}]})}catch(w){console.error(`[unified-datalayer] ${w.cause}: ${w.message}`)}}sizeSelect(_){try{this.validateString(_,"size"),this.pushEvent("product_size-select",{default:{page:{type:"product",action:"size-select"}},products:[{size:_}]})}catch(w){console.error(`[unified-datalayer] ${w.cause}: ${w.message}`)}}}class g extends G{view(_,w){try{let $=w||(typeof window!=="undefined"?window.location.pathname.split("/").filter(Boolean).pop():"");if(!_)throw new Error("productsArray is required.",{cause:"REQUIRED_FIELD_MISSING"});_.forEach((q)=>{this.validateProductData(q)});let C=_.map((q,R)=>({position:R,...this.formatProduct(q)}));this.clearProducts(),this.pushEvent("product_listing-view",{default:{page:{type:"product",action:"listing-view",list_name:$}},products:C})}catch($){console.error(`[unified-datalayer] ${$.cause}: ${$.message}`)}}filter(_){try{if(!_)throw new Error("list_filters is required.",{cause:"REQUIRED_FIELD_MISSING"});this.validateMultiple([{value:_.filter_type,paramName:"filter_type",type:"string"},{value:_.filter_value,paramName:"filter_value",type:"string"}]),this.pushEvent("product_listing-filters",{default:{page:{type:"product",action:"listing-filters"}},list_filters:_})}catch(w){console.error(`[unified-datalayer] ${w.cause}: ${w.message}`)}}sort(_){try{this.validateString(_,"option"),this.pushEvent("product_listing-sort",{default:{page:{type:"product",action:"listing-sort"}},list_sort:{option:_}})}catch(w){console.error(`[unified-datalayer] ${w.cause}: ${w.message}`)}}click(){}}class H extends G{view(_){try{if(!_)throw new Error("productsArray is required.",{cause:"REQUIRED_FIELD_MISSING"});_.forEach(($)=>{this.validateProductData($)});let w=_.map(($)=>({...this.formatProduct($)}));this.clearProducts(),this.pushEvent("wishlist_home",{default:{page:{type:"wishlist",action:"home"}},products:w})}catch(w){console.error(`[unified-datalayer] ${w.cause}: ${w.message}`)}}add(_){try{this.validateProductData(_);let w=[this.formatProduct(_)];this.clearProducts(),this.pushEvent("add_to-wishlist",{default:{page:{type:"wishlist",action:"add"}},products:w})}catch(w){console.error(`[unified-datalayer] ${w.cause}: ${w.message}`)}}}class Q{static instance=null;config=null;user=null;isFirstEventAfterRefresh=!0;dataLayer=[];previousEvent=null;propertiesToNullify={default:["error"]};page;pdp;plp;cart;checkout;account;wishlist;order;constructor(){if(typeof window!=="undefined")window.adobeDataLayer=window.adobeDataLayer||[],this.dataLayer=window.adobeDataLayer;this.page=new Z(this),this.pdp=new M(this),this.plp=new g(this),this.cart=new W(this),this.checkout=new X(this),this.account=new J(this),this.wishlist=new H(this),this.order=new Y(this)}static getInstance(){if(!Q.instance)Q.instance=new Q;return Q.instance}init(_){if(!_.siteInfo)throw new Error("DataLayer initialisation failed: siteInfo is required");if(this.config=_,typeof window!=="undefined")this.user={user_state:window.localStorage.getItem("uem_hashed")?"customer":"guest",login_state:window.localStorage.getItem("uem_hashed")?"logged-in":"anonymous",uem_hashed:window.localStorage.getItem("uem_hashed")??"",session_id:"",division_id:""}}pushEvent(_,w={}){try{if(this.validateInitialisation(),typeof window==="undefined")return console.log(`Event would be pushed: ${_}`,w),Promise.resolve();let $=this.prepareEventObject(_,w);return this.applyNullificationLogic($),new Promise((q,R)=>{try{this.dataLayer.push($),this.storeCleanPreviousEvent($),q()}catch(P){console.error("Error pushing to data layer:",P.message),R(P)}})}catch($){return console.error(`Error in pushEvent (${_}):`,$.message),Promise.reject($)}}clearProducts(){this.dataLayer.push({products:null})}validateInitialisation(){if(this.isFirstEventAfterRefresh&&(!this.config||!this.config.siteInfo))throw new Error("DataLayer not initialised: Call DataLayer.init({siteInfo: {...}}) before pushing events")}prepareEventObject(_,w){let $={...w};if(this.isFirstEventAfterRefresh&&this.config){if($.default=$.default||{},$.default.site=this.config.siteInfo,this.user)$.default.user=this.user;this.isFirstEventAfterRefresh=!1}return{event:_,...$}}applyNullificationLogic(_){if(!this.previousEvent)return;Object.keys(this.previousEvent).forEach((w)=>{if(w==="event")return;if(w==="default")this.handleDefaultObjectNullification(_,w);else if(!(w in _))_[w]=null})}handleDefaultObjectNullification(_,w){let $=this.previousEvent?.[w];if(typeof $!=="object"||$===null||Array.isArray($))return;if(!(w in _)||typeof _[w]!=="object"||_[w]===null)_[w]={};(this.propertiesToNullify[w]||[]).forEach((q)=>{if(typeof _[w]==="object"&&_[w]!==null&&q in $&&!(q in _[w]))_[w][q]=null})}storeCleanPreviousEvent(_){let w=JSON.parse(JSON.stringify(_));if(Object.keys(w).forEach(($)=>{if($!=="default"&&w[$]===null)delete w[$]}),w.default&&typeof w.default==="object")Object.keys(w.default).forEach(($)=>{if(w.default[$]===null)delete w.default[$]});this.previousEvent=w}resetFirstEventFlag(){this.isFirstEventAfterRefresh=!0}setPropertiesToNullify(_){this.propertiesToNullify=_}addPropertiesToNullify(_,w){this.propertiesToNullify[_]=[...this.propertiesToNullify[_]||[],...w]}}var h=Q;function E(){return Q.getInstance()}if(typeof window!=="undefined")window.addEventListener("unhandledrejection",function(_){console.error("Unhandled promise rejection (DataLayer):",_.reason)});export{E as getDataLayer,h as DataLayer};