@salla.sa/twilight 1.6.13 → 1.6.15-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/index.js CHANGED
@@ -1,2 +1,4012 @@
1
- import e from"@salla.sa/base";export{default}from"@salla.sa/base";import t from"infinite-scroll";import a from"lang.js";import r from"axios";import s from"jwt-decode";let i=function(e,t,a){return alert(e)},n=function(e,t){return i(e,l.error,t)},l={error:"error",success:"success",info:"info"};var o={fire:function(e,t,a){return i(e,t,a)},setNotifier:function(e){i=e},error:n,success:function(e,t){return i(e,l.success,t)},info:function(e,t){return i(e,l.info,t)},sallaInitiated:function(){let e=window.location.href.match(/([\?\&]danger=)[^&]+/g);e&&e.forEach((e=>{n(decodeURI(e.replace("?danger=","").replace("&danger=","")))}))},types:l};class d{constructor(e,t){return this.api=e,this.event=t,new Proxy(this,{get:function(a,r){return"event"===r?t:"api"===r?e:e&&e[r]||a[r]}})}}String.prototype.toStudlyCase=function(){return this.trim().replace(/([^a-zA-Z\d].)/g,(function(e){return e.toUpperCase().replace(/[^a-zA-Z\d]/g,"")}))},String.prototype.toDatasetName=function(){return this.startsWith("data-")?this.substr(5).toStudlyCase():this.toStudlyCase()},String.prototype.toSelector=function(){return this.trim().startsWith(".")||this.trim().startsWith("#")?this:"#"+this},String.prototype.replaceArray=function(e,t){for(var a,r=this,s=0;s<e.length;s++)a=new RegExp(e[s],"g"),r=r.replace(a,t[s]);return r},String.prototype.rtrim=function(e){return void 0===e&&(e="\\s"),this.replace(new RegExp("["+e+"]*$"),"")},String.prototype.ltrim=function(e){return void 0===e&&(e="\\s"),this.replace(new RegExp("^["+e+"]*"),"")},String.prototype.digitsOnly=function(){return Salla.helpers.digitsOnly(this)},Element.prototype.matches||(Element.prototype.matches=Element.prototype.msMatchesSelector),Element.prototype.closest||(Element.prototype.closest=function(e){for(var t=this;t;){if(t.matches(e))return t;t=t.parentElement}}),Element.prototype.getElementSallaData=function(e,...t){if(!this.getAttribute)return;if(this.hasAttribute("data-json"))try{return JSON.parse(this.getAttribute("data-json"))}catch(e){}let a=this.getAttribute("data-function");if(a&&window[a])return"function"==typeof window[a]?this.getFilteredData(window[a].call(this,...t)):this.getFilteredData(window[a]);let r=this.hasAttribute("data-form-selector")?document.querySelector(this.dataset.formSelector):void 0;if(r="FORM"===this.tagName?this:r||this.closest("form")||this.closest("[salla-form-data]")||this,r&&"FORM"===r.tagName)return this.getFilteredData(new FormData(r),null,r);let s=r.querySelectorAll("[name]");if(!s.length)return this.getFilteredData();let i=Object.assign({},this.dataset);return s.forEach((e=>{if(!["checkbox","radio"].includes(e.type)||e.checked)try{let t=Salla.helpers.inputData(e.name,e.value,i);i[t.name]=t.value}catch(t){Salla.log(e.name+" can't be send")}})),this.getFilteredData(i)},Element.prototype.canEventFireHook=function(e){return!!this.hasAttribute&&this.hasAttribute(Salla.api.hooksPrefix+e.type)},Element.prototype.hasAttributeStartsWith=function(e,t){e=e.toLowerCase();for(var a=0;a<this.attributes.length;a++){let r=this.attributes[a].name.toLowerCase();if(0===r.indexOf(e))return!t||r}return!1},HTMLFormElement.prototype.getAjaxFormData=function(e){var t=this.querySelectorAll('input[type="file"]');t.forEach((e=>{e.files.length||e.setAttribute("disabled","")}));var a=new FormData(this);return t.forEach((e=>{e.files.length||e.removeAttribute("disabled")})),{formData:this.getFilteredData(a,e),url:this.getAttribute("action"),method:this.getAttribute("method")||"post",events:{success:this.dataset.onSuccess,fail:this.dataset.onFail}}},Element.prototype.getFilteredData=function(e=null,t=null,a=null){return e=e||a?.dataset||this.dataset,a&&this.name&&void 0!==this.value&&(e=function(e,t,a){e instanceof FormData?e.append(t,a):e[t]=a;return e}(e,this.name,this.value)),["filterBeforeSubmit","filterBeforeSend"].forEach((r=>{let s=a?.dataset[r]||this.dataset[r];if(s){var i=window[s];if("function"==typeof i){if(!i(e,a||this,t)&&e)throw`Data failed to be pass verify function window.${s}(formData, element, event)!`;return i(e,a||this,t)}Salla.log("window."+s+"() not found!")}})),e},HTMLAnchorElement.prototype.getAjaxFormData=function(e){return{formData:this.getFilteredData(null,e),url:this.getAttribute("href"),method:this.dataset.type||"get",events:{success:this.dataset.onSuccess,fail:this.dataset.onFail}}};class c{constructor(){this.events={},this.namespace="";let e=/function (.{1,})\(/.exec(this.constructor.toString());this.className=(e&&e.length>1?e[1]:"").toLowerCase()}after_init(){this.createDynamicFunctions()}createDynamicFunctions(){Object.keys(this.events).forEach((e=>{this.createDynamicEventFuns(e),this.createDynamicListenerFuns(e)}))}createDynamicEventFuns(e){if(this[e])return;let t=this;this[e]=function(...a){return t.dispatch(e,...a)}}createDynamicListenerFuns(e){let t="on"+e.charAt(0).toUpperCase()+e.slice(1);if(this[t])return;let a=this;this[t]=function(t){return a.on(e,t)}}getEventName(e){return e=this.events[e]||e,!this.namespace||e.includes("::")?e:this.namespace+Salla.event.delimiter+e}dispatch(e,...t){return Salla.event.dispatch(this.getEventName(e),...t)}on(e,t){return Salla.event.addListener(this.getEventName(e),t)}once(e,t){return Salla.event.once(this.getEventName(e),t)}}function u(e){e&&e.sallaInitiated&&!e.initiated&&(e.sallaInitiated(),e.initiated=!0)}Salla.event.auth=new class extends c{constructor(){super(),this.namespace="auth",this.events={login:"login",logout:"logout",codeSent:"code.sent",codeNotSent:"code.not-sent",verified:"verified",verificationFailed:"verification.failed",loggedIn:"logged.in",registered:"registered",registrationFailed:"registration.failed",loggedOut:"logged.out",failedLogout:"failed.logout",refreshFailed:"refresh.failed",tokenFetched:"token.fetched"},this.after_init()}login(e){return e?(e.type&&this.setTypeActionOnVerified(e.type),this.next_event=e.next_event||null,this.dispatch("login",...arguments)):(this.next_event=null,this.dispatch("login",...arguments))}loggedIn(e){Salla.profile.info().then((e=>{salla.config.set("user",{id:e.data.id,type:"user",email:e.data.email,mobile:e.data.phone.code+e.data.phone.number,country_code:e.data.phone.country,language_code:e.data.language,currency_code:e.data.currency})})).finally((()=>this.dispatch("loggedIn",e)))}setTypeActionOnVerified(e){this.type_action_on_verified=e}getTypeActionOnVerified(){return this.type_action_on_verified||"redirect"}},Salla.event.cart=new class extends c{constructor(){super(),this.namespace="cart",this.events={latestFetched:"latest.fetched",latestFailed:"latest.failed",updated:"updated",itemUpdated:"item.updated",itemUpdatedFailed:"item.updated.failed",itemAdded:"item.added",itemAddedFailed:"item.added.failed",itemDeleted:"item.deleted",itemDeletedFailed:"item.deleted.failed",submitted:"submitted",submitFailed:"submit.failed",imageDeleted:"image.deleted",imageNotDeleted:"image.not.deleted",detailsFetched:"details.fetched",detailsNotFetched:"details.not.fetched",successReset:"success.reset",couponAdded:"coupon.added",couponDeleted:"coupon.deleted",couponAdditionFailed:"coupon.addition.failed",couponDeletionFailed:"coupon.deletion.failed"},this.after_init()}updated(e){return Salla.cookie.set("fresh_summary",1),e&&"object"==typeof e?(e.offer&&salla.product.event.offerExisted(e.offer),salla.storage.set("cart.summary",{total:e.cart.total,sub_total:e.cart.sub_total,discount:e.cart.discount,real_shipping_cost:e.cart.real_shipping_cost,count:e.cart.count,shipping_cost:e.cart.free_shipping_bar?.has_free_shipping?0:e.cart.real_shipping_cost}),this.dispatch("updated",e.cart)):(Salla.logger.info("Cart summary not an object!",e),this.dispatch("updated"))}latestFetched(e){return this.updated(e.data),this.dispatch("latestFetched",e)}itemAdded(e,t){return this.updated(e.data),this.dispatch("itemAdded",e,t)}itemDeleted(e,t){return this.updated(e.data),this.dispatch("itemDeleted",e,t)}itemUpdated(e,t){return this.updated(e.data),this.dispatch("itemUpdated",e,t)}couponAdded(e,t){return this.updated(e.data),this.dispatch("couponAdded",e,t)}couponDeleted(e,t){return this.updated(e.data),this.dispatch("couponDeleted",e,t)}},Salla.event.order=new class extends c{constructor(){super(),this.namespace="order",this.events={canceled:"canceled",notCanceled:"not.canceled",orderCreated:"order.created",orderCreationFailed:"order.creation.failed",invoiceSent:"invoice.sent",invoiceNotSent:"invoice.not.sent"},this.after_init()}},Salla.event.scope=new class extends c{constructor(){super(),this.namespace="scope",this.events={fetched:"fetched",notFetched:"not.fetched",productAvailabilityFetched:"product-availability.fetched",productAvailabilityNotFetched:"product-availability.not.fetched",changeSucceeded:"changed",changeFailed:"not.changed"},this.after_init()}},Salla.event.rating=new class extends c{constructor(){super(),this.namespace="rating",this.events={orderNotFetched:"order.not.fetched",orderFetched:"order.fetched",storeRated:"store.rated",storeFailed:"store.failed",productsRated:"products.rated",productsFailed:"products.failed",shippingRated:"shipping.rated",shippingFailed:"shipping.failed"},this.after_init()}},Salla.event.comment=new class extends c{constructor(){super(),this.namespace="comment",this.events={added:"added",additionFailed:"addition.failed"},this.after_init()}},Salla.event.loyalty=new class extends c{constructor(){super(),this.namespace="loyalty",this.events={exchangeSucceeded:"exchange.succeeded",exchangeFailed:"exchange.failed",programFetched:"program.fetched",programNotFetched:"program.not.fetched",resetSucceeded:"exchange-reset.succeeded",resetFailed:"exchange-reset.failed"},this.after_init()}},Salla.event.product=new class extends c{constructor(){super(),this.namespace="product",this.events={priceUpdated:"price.updated",priceUpdateFailed:"price.updated.failed",availabilitySubscribed:"availability.subscribed",availabilitySubscribeFailed:"availability.subscribe.failed",categoriesFetched:"categories.fetched",categoriesFailed:"categories.failed",searchFailed:"search.failed",searchResults:"search.results",offerExisted:"offer.existed",fetchOffersFailed:"fetch.offers.failed",offersFetched:"offers.fetched",sizeGuideFetched:"size-guide.fetched",SizeGuideFetchFailed:"size-guide.failed",giftFetched:"gift.fetched",giftFetchFailed:"gift.failed",addGiftToCartSucceeded:"gift.add-to-cart.succeeded",addGiftToCartFailed:"gift.add-to-cart.failed",giftImageUploadSucceeded:"gift.image-upload.succeeded",giftImageUploadFailed:"gift.image-upload.failed"},this.after_init()}},Salla.event.profile=new class extends c{constructor(){super(),this.namespace="profile",this.events={updated:"updated",updateFailed:"update.failed",verificationCodeSent:"verification.code.sent",updateContactsFailed:"update.contacts.failed",verified:"verified",unverified:"unverified",infoFetched:"info.fetched",infoNotFetched:"info.not.fetched"},this.after_init()}},Salla.event.currency=new class extends c{constructor(){super(),this.namespace="currency",this.events={changed:"changed",failed:"failed",fetched:"fetched",failedToFetch:"failed.to.fetch"},this.after_init()}},Salla.event.document=new class extends c{constructor(){super(),this.namespace="document",this.events={click:"click",change:"change",submit:"submit",keyup:"keyup",leaving:"leaving",request:"request",requestFailed:"request.failed"},this.after_init()}onClick(e,t){this.fireCallableFuns("click",e,t)}onChange(e,t){this.fireCallableFuns("change",e,t)}onSubmit(e,t){this.fireCallableFuns("submit",e,t)}onKeyup(e,t){this.fireCallableFuns("keyup",e,t)}fireCallableFuns(e,t,a){this.on(e,(e=>{"function"!=typeof t?"function"==typeof a&&e.target.matches(t)&&a(e):t(e)}))}fireEvent(e,t,...a){return this.fireEventForElements(e,t,!1,...a)}fireEventForAll(e,t,...a){return this.fireEventForElements(e,t,!0,...a)}fireEventForElements(e,t,a,...r){if("string"==typeof e){if(a)return document.querySelectorAll(e).forEach((e=>this.fireEventForElements(e,t,!1,...r)));e=document.querySelector(e)}if(!e)return void salla.log("Failed To get element to fire event: "+t);const s=new CustomEvent(t,...r);return e.dispatchEvent(s)}},Salla.event.wishlist=new class extends c{constructor(){super(),this.namespace="wishlist",this.events={added:"added",removed:"removed",additionFailed:"addition.failed",removingFailed:"removing.failed"},this.after_init()}},Salla.event.infiniteScroll=new class extends c{constructor(){super(),this.namespace="infiniteScroll",this.events={scrollThreshold:"scroll.threshold",request:"request",load:"load",append:"append",error:"error",last:"last",history:"history"},this.after_init()}},Salla.event.on("twilight::initiated",(()=>{Object.keys(Salla).forEach((e=>{"object"==typeof(e=Salla[e])&&(u(e),Object.keys(e).forEach((t=>{u(e[t])})))}))})),Salla.config.default_properties={debug:"undefined"!=typeof process&&"development"===process.env.NODE_ENV,token:null,fastRequests:!0,canLeave:!0,store:{api:"https://api.salla.dev/store/v1/"},currencies:{SAR:{code:"SAR",name:"ريال سعودي",symbol:"ر.س",amount:1,country_code:"sa"}}},Salla.config.merge(Salla.config.default_properties),Salla.config.triedToGetCurrencies_=!1,Salla.config.triedToGetLanguages_=!1,Salla.config.isUser=()=>"user"===Salla.config.get("user.type"),Salla.config.isGuest=()=>!Salla.config.isUser(),Salla.config.languages=async()=>{if(Salla.config.triedToGetLanguages_)return Salla.config.get("languages");Salla.config.triedToGetLanguages_=!0;let e=!0,t=[];return(await salla.document.api.request("languages",null,"get"))?.data?.map((a=>{e&&(t=[],e=!1),a.code=a.code||a.iso_code,a.url=salla.url.get(a.code),a.is_rtl=a.is_rtl||a.rtl,t.push(a)})),Salla.config.set("languages",t),t},Salla.config.currencies=async()=>{if(Salla.config.triedToGetCurrencies_)return Salla.config.get("currencies");Salla.config.triedToGetCurrencies_=!0;let e=!0,t={};return(await salla.currency.api.list())?.data?.map((a=>{e&&(t={},e=!1),a.country_code=a.code.substr(0,2).toLowerCase(),t[a.code]=a})),Salla.config.set("currencies",t),t},Salla.config.currency=e=>(e=e||Salla.config.get("user.currency_code"),Salla.config.get("currencies."+e)||Object.values(Salla.config.get("currencies"))[0]);class h{constructor(){this.endpoints={},this.webEndpoints=[],this.namespace="BaseApi",this.endpointsMethods={},this.endpointsHeaders={};let e=/function (.{1,})\(/.exec(this.constructor.toString());this.className=(e&&e.length>1?e[1]:"").toLowerCase(),this.debounce={request:void 0,time:300,enabled:!0,exclude:[]}}after_init(){}normalRequest(e,t,a=null){let r=Array.isArray(e),s=r?this.getUrl(...e):this.getUrl(e);e=r?e[0]:e,a=a||this.endpointsMethods[e]||"post";let i=this.endpointsHeaders[e];if("get"===a&&t instanceof FormData){let e={};Array.from(t.entries()).forEach((function(t){e[t[0]]=t[1]})),t={params:e}}return i&&"get"===a&&(t=t?Object.assign(t,i):i),this.webEndpoints.includes(e)?s=salla.url.get(s):"http"!==s.substring(0,4)&&(s=salla.url.api(s)),Salla.api.request(s,t,a,{headers:i})}request(e,t,a=null){return!salla.api.isFastRequestsAllowed()&&this.debounce.enabled?(this.debounce.request||(this.debounce.request=salla.helpers.debounce(this.normalRequest.bind(this),this.debounce.time)),this.debounce.request(e,t,a)):this.normalRequest(e,t,a)}getUrl(e){let t=this.endpoints[e]||e;const a=/{[^{}]+}/i;for(let e=1;e<arguments.length;e++)t=t.replace(a,arguments[e]);return t}event(){return salla.event[this.className]}}class p extends h{constructor(){super(),this.namespace="cart",this.endpoints={latest:"cart/latest",details:"cart/{cart}",quickAdd:"cart/{cart}/item/{product}/quick-add",addItem:"cart/{cart}/item/{product}/add",deleteItem:"cart/{cart}/item/{item}",updateItem:"cart/{cart}/item/{item}",deleteImage:"cart/{cart}/image/{image}",uploadImage:"cart/{cart}/image",status:"cart/{cart}/status",addCoupon:"cart/{id}/coupon",deleteCoupon:"cart/{id}/coupon"},this.endpointsMethods={latest:"get",details:"get",status:"get",updateItem:"post",deleteItem:"delete",deleteImage:"delete",deleteCoupon:"put"},this.webEndpoints=["latest"],this.after_init()}async getCurrentCartId(){return salla.storage.get("cart.id")||(await this.latest()).data.cart.id}getUploadImageEndpoint(e){return salla.url.api(this.getUrl("uploadImage",e||salla.storage.get("cart.id")))}latest(){return this.request("latest",{params:{source:""}}).then((e=>(salla.storage.set("cart",e.data.cart),salla.event.cart.latestFetched(e),e))).catch((e=>{throw salla.storage.set("cart",""),salla.event.cart.latestFailed(e),e}))}async details(){let e=await this.getCurrentCartId();return this.request(["details",e]).then((function(e){return salla.cart.event.detailsFetched(e),e})).catch((function(e){throw salla.cart.event.detailsNotFetched(e),e}))}async quickAdd(e){return this.addItem(e)}async addItem(e){let t=this.getCartPayload(e);if(!t.id){let e='There is no product "id"!';return salla.cart.event.itemAddedFailed(e),salla.api.errorPromise(e)}let a=salla.form.getPossibleValue(t.payload,["quantity","donating_amount"])?"addItem":"quickAdd",r=await this.getCurrentCartId();return this.request([a,r,t.id],t.payload).then((function(e){return salla.cart.event.itemAdded(e,t.id),e})).catch((function(e){throw salla.cart.event.itemAddedFailed(e,t.id),e}))}async deleteItem(e){let t=this.getCartPayload(e);if(!t.id){let e='There is no "id"!';return salla.cart.event.itemDeletedFailed(e),salla.api.errorPromise(e)}let a=await this.getCurrentCartId();return this.request(["deleteItem",a,t.id]).then((function(e){return salla.cart.event.itemDeleted(e,t.id),e})).catch((function(e){throw salla.cart.event.itemDeletedFailed(e,t.id),e}))}async updateItem(e){let t=this.getCartPayload(e);if(!t.id){let e='There is no "id"!';return salla.cart.event.itemUpdatedFailed(e),salla.api.errorPromise(e)}let a=await this.getCurrentCartId();return"Object"===t.payload.constructor?.name?t.payload._method="PUT":t.payload.append("_method","PUT"),this.request(["updateItem",a,t.id],t.payload).then((function(e){return salla.cart.event.itemUpdated(e,t.id),e})).catch((function(e){throw salla.cart.event.itemUpdatedFailed(e,t.id),e}))}async deleteImage(e){if(!(e=salla.form.getPossibleValue(e,["id","image_id","photo_id"]))){let e='There is no "id"!';return salla.cart.event.imageNotDeleted(e),salla.api.errorPromise(e)}let t=await this.getCurrentCartId();return this.request(["deleteImage",t,e]).then((function(t){return salla.cart.event.imageDeleted(t,e),t})).catch((function(t){throw salla.cart.event.imageNotDeleted(t,e),t}))}async status(e){return this.request(["status",e||await this.getCurrentCartId()])}async submit(){this.status(await this.getCurrentCartId()).then((e=>{let t=e.data.next_step.to;if(!["checkout","refresh","login"].includes(t)){let e="Can't find next_step );";throw salla.cart.event.submitFailed(e),e}if(salla.cart.event.submitted(e),"login"===t)return salla.auth.setCanRedirect(!1),salla.event.dispatch("login::open"),void salla.auth.event.onLoggedIn((e=>{salla.event.dispatch("login::close"),this.submit()}));"checkout"===t?window.location.href=e.data.next_step.url+(window.ApplePaySession?"?has_apple_pay=true":""):window.location.reload()})).catch((function(e){throw salla.cart.event.submitFailed(e),e}))}getCartPayload(e){let t=e?.data||("object"==typeof e?e:void 0);return e=t?salla.form.getPossibleValue(t,["prod_id","product_id","item_id","id"]):e,t="object"==typeof t?t:void 0,{id:e,payload:t}}normalRequest(e,t,a=null){return super.normalRequest(e,t,a).catch((e=>{throw 403===e?.response?.status&&(salla.storage.remove("cart"),salla.error(salla.lang.get("pages.checkout.try_again"))),e}))}reset(){salla.storage.remove("cart"),salla.cart.event.successReset()}async addCoupon(e){if(e=e.data||e,!(e=salla.form.getPossibleValue(e,["coupon"]))){let e=new Error('There is no "Coupon Code"!');return e.name="EmptyCoupon",salla.event.cart.couponAdditionFailed(e),salla.api.errorPromise(e)}let t=await salla.cart.api.getCurrentCartId();return this.request(["addCoupon",t],{coupon:e}).then((function(e){return salla.event.cart.couponAdded(e,t),e})).catch((function(e){throw salla.event.cart.couponAdditionFailed(e,t),e}))}async deleteCoupon(){let e=await salla.cart.api.getCurrentCartId();return this.request(["deleteCoupon",e],{}).then((function(t){return salla.event.cart.couponDeleted(t,e),t})).catch((function(t){throw salla.event.cart.couponDeletionFailed(t,e),t}))}}class f extends h{constructor(){super(),this.namespace="loyalty",this.endpoints={getProgram:"loyalty",exchange:"loyalty/exchange",reset:"loyalty/exchange"},this.endpointsMethods={getProgram:"get",reset:"put"},this.after_init()}getProgram(){return this.request("getProgram").then((e=>(salla.loyalty.event.programFetched(e),e))).catch((e=>{throw salla.loyalty.event.programNotFetched(e),e}))}async exchange(e,t=null){if(!(e=salla.form.getPossibleValue(e,["id","loyalty_prize_id","prize_id"]))){let e="Unable to find cart id. Please provide one.";return salla.loyalty.event.exchangeFailed(e),salla.api.errorPromise(e)}if(!(t=t||await salla.cart.getCurrentCartId())){let e="Unable to find cart id. Please provide one.";return salla.loyalty.event.exchangeFailed(e),salla.api.errorPromise(e)}return this.request("exchange",{loyalty_prize_id:e,cart_id:t}).then((function(t){return salla.loyalty.event.exchangeSucceeded(t,e),t})).catch((function(t){throw salla.loyalty.event.exchangeFailed(t,e),t}))}async reset(e=null){if(!(e=e||await salla.cart.getCurrentCartId())){let e="Unable to find cart id. Please provide one.";return salla.loyalty.event.resetFailed(e),salla.api.errorPromise(e)}return this.request("reset",{cart_id:e}).then((e=>(salla.loyalty.event.resetSucceeded(e),e))).catch((e=>{throw salla.loyalty.event.resetFailed(e),e}))}}class g extends h{constructor(){super(),this.namespace="auth",this.canRedirect_=!0,this.endpoints={login:"auth/{type}/send_verification",resend:"auth/resend_verification",verify:"auth/{type}/verify",register:"auth/register",logout:"logout",refresh:"auth/refresh"},this.webEndpoints=["logout","auth/jwt","refresh"],this.endpointsMethods={logout:"get"},this.after_init()}setCanRedirect(e){salla.api.auth.canRedirect_=e}canRedirect(){return salla.api.auth.canRedirect_}login(e){e?.data&&(e=e.data);let t=salla.form.getPossibleValue(e,["type"]);if(!["email","mobile"].includes(t)){let e="Login type should be in: [email, mobile]";return salla.auth.event.codeNotSent(e),salla.api.errorPromise(e)}return this.request(["login",t],e).then((function(e){return salla.auth.event.codeSent(e,t),e})).catch((function(e){throw salla.auth.event.codeNotSent(e,t),e}))}async verify(e,t=!0){let a=salla.form.getPossibleValue(e,["type"]);if(!a){let e="Failed to know what's login type!";return salla.auth.event.verificationFailed(e),salla.api.errorPromise(e)}t=!1!==salla.form.getPossibleValue(e,["supportWebAuth"])&&t,salla.auth.event.next_event&&(e.next_event=salla.auth.event.next_event);let r=await this.request(["verify",a],e);if(200!==r?.status)return salla.auth.event.verificationFailed(r,a),salla.api.errorPromise(r);let s="authenticated"===r.data?.case;return s&&salla.auth.event.tokenFetched(r.data.token),t&&await this.request("auth/jwt"),s&&salla.auth.event.loggedIn(r),salla.auth.event.verified(r,a),salla.api.successPromise(r)}resend(e){let t;return(e=e.data||e).type=e.type||"mobile","mobile"!==e.type||e.phone&&e.country_code?"email"!==e.type||e.email?this.request("resend",e).then((function(t){return salla.auth.event.codeSent(t,e),t})).catch((function(t){throw salla.auth.event.codeNotSent(t,e),t})):(salla.auth.event.codeNotSent(t="There is no email!",e),salla.api.errorPromise(t)):(salla.auth.event.codeNotSent(t="There is no phone or country_code!",e),salla.api.errorPromise(t))}register(e){let t;return e.data&&(t=e.element,e=e.data),salla.auth.event.next_event&&(e.next_event=salla.auth.event.next_event),this.request("register",e).then((function(e){return salla.auth.event.registered.call(t,e),"authenticated"===e.data?.case&&salla.auth.event.loggedIn(e),e})).catch((function(e){throw salla.auth.event.registrationFailed.call(t,e),e}))}logout(){return this.request("logout").then((function(e){return salla.auth.event.loggedOut(e),salla.log("Reloading after 0.1 sec..."),setTimeout((()=>location.reload()),100),e})).catch((function(e){throw salla.auth.event.failedLogout(e),e}))}refresh(){return this.request("refresh").catch((function(e){throw salla.auth.event.refreshFailed(e),e}))}}class m extends h{constructor(){super(),this.namespace="order",this.endpoints={cancel:"orders/cancel/{id}",createCartFromOrder:"reorder/{id}",sendInvoice:"orders/send/{id}"},this.endpointsMethods={createCartFromOrder:"get"},this.after_init()}show(e){let t=salla.form.getPossibleValue(e?.data||e,["id","order_id"]);salla.event.dispatch("mobile::order.placed",{order_id:t}),location.href=salla.form.getPossibleValue(e?.data||e,["url"])}cancel(e){return e=e||Salla.config.get("page.id"),this.request(["cancel",e],{params:{has_apple_pay:!!window.ApplePaySession}}).then((function(t){return salla.event.order.canceled(t,e),t})).catch((function(t){throw salla.event.order.notCanceled(t,e),t}))}createCartFromOrder(e){return e=e||salla.config.get("page.id"),this.request(["createCartFromOrder",e]).then((function(t){return salla.storage.set("cart",{id:t.data.cart_id,user_id:salla.config.get("user.id")}),salla.event.order.orderCreated(t,e),window.location.href=t.data.url,t})).catch((function(t){throw salla.event.order.orderCreationFailed(t,e),t}))}sendInvoice(e){let t=salla.form.getPossibleValue(e,["id"])||salla.config.get("page.id");if(!t||isNaN(t)){let e="There is no id!";return salla.order.event.invoiceNotSent(e),salla.api.errorPromise(e)}return this.request(["sendInvoice",t],e).then((e=>(salla.event.order.invoiceSent(e,t),e))).catch((e=>{throw salla.event.order.invoiceNotSent(e,t),e}))}}class v extends h{constructor(){super(),this.namespace="product",this.previousQuery="",this.endpoints={getPrice:"products/{id}/price",availabilitySubscribe:"products/{id}/availability-notify",search:"products/search",categories:"products/categories/{?id}",offers:"products/{product_id}/specialoffer",getSizeGuides:"products/{prod_id}/size-guides",giftDetail:"products/{product_id}/buy-as-gift",giftToCart:"products/{product_id}/buy-as-gift",giftImage:"products/buy-as-gift/image"},this.endpointsMethods={giftDetail:"get"},this.after_init()}getPrice(e){let t=e.data||e,a=salla.form.getPossibleValue(t,["id","prod_id","product_id"]);return this.request(["getPrice",a],"object"==typeof t?t:void 0).then((function(e){return salla.product.event.priceUpdated(e,a),e})).catch((function(e){throw salla.product.event.priceUpdateFailed(e,a),e}))}categories(e){return this.request(["categories",e||""],null,"get").then((function(e){return salla.product.event.categoriesFetched(e),e})).catch((function(e){throw salla.product.event.categoriesFailed(e),e}))}availabilitySubscribe(e){let t=e.data||e;return e=salla.form.getPossibleValue(t,["id","prod_id"]),this.request(["availabilitySubscribe",e],"object"==typeof t?t:void 0).then((function(t){return salla.product.event.availabilitySubscribed(t,e),t})).catch((function(t){throw salla.product.event.availabilitySubscribedFailed(t,e),t}))}search(e){let t=e.data;if(t||(t={params:"string"==typeof e?{query:e}:e}),!(e=t instanceof FormData?t.get("query"):t.query||t.params?.query)){let e='There is no "query"!';return salla.product.event.searchFailed(e),salla.api.errorPromise(e)}if(e===salla.api.product.previousQuery){let e="Query is same as previous one!";return salla.product.event.searchFailed(e),salla.api.product.previousQuery=null,salla.api.errorPromise(e)}return salla.api.product.previousQuery=e,this.request("search",t,"get").then((function(t){return salla.product.event.searchResults(t,e),t})).catch((function(t){throw salla.product.event.searchFailed(t,e),t}))}offers(e){if(!(e=salla.form.getPossibleValue(e?.data|e,["product_id","id"]))){let e='There is no "product_id"!';return salla.offer.event.fetchDetailsFailed(e),salla.api.errorPromise(e)}return this.request(["offers",e]).then((function(t){return salla.product.event.offersFetched(t,e),t})).catch((function(t){throw salla.product.event.fetchOffersFailed(t,e),t}))}getSizeGuides(e){return this.request(`products/${e}/size-guides`,null,"get").then((t=>(salla.product.event.sizeGuideFetched(t,e),t))).catch((t=>{throw salla.product.event.sizeGuideFetchFailed(t,e),t}))}getGiftDetails(e){return this.request(["giftDetail",e]).then((t=>(salla.product.event.giftFetched(t,e),t))).catch((t=>{throw salla.product.event.giftFetchFailed(t,e),t}))}addGiftToCart(e,t,a=!1){return this.request(["giftToCart",e],t).then((t=>(salla.product.event.addGiftToCartSucceeded(t,e),a&&(window.location.href=t.redirect),response))).catch((t=>{throw salla.product.event.addGiftToCartFailed(t,e),t}))}uploadGiftImage(e){return this.request("giftImage",e).then((e=>(salla.product.event.giftImageUploadSucceeded(e),e))).catch((e=>{throw salla.product.event.giftImageUploadFailed(e),e}))}}class y extends h{constructor(){super(),this.namespace="profile",this.endpoints={info:"auth/user",update:"profile/update",updateContacts:"profile/contacts/update",verify:"profile/verify"},this.after_init()}info(){return this.request("info",null,"get").then((e=>(salla.profile.event.infoFetched(e),e))).catch((e=>{throw salla.profile.event.infoNotFetched(e),e}))}update(e){return this.request("update",e).then((e=>(salla.profile.event.updated(e),e))).catch((e=>{throw salla.event.profile.updateFailed(e),e}))}updateContacts(e){return this.request("updateContacts",e).then((e=>(salla.profile.event.verificationCodeSent(e),e))).catch((e=>{throw salla.event.profile.updateContactsFailed(e),e}))}verify(e){return this.request("verify",e).then((e=>(salla.profile.event.verified(e),e))).catch((e=>{throw salla.event.profile.unVerified(e),e}))}}class w extends h{constructor(){super(),this.namespace="comment",this.endpoints={add:"{type}/{id}/comments"},this.after_init()}add(e){e?.data&&(e=e.data);let t,{id:a,type:r,comment:s}=Object.fromEntries(e);return a?r&&["products","pages","product","page"].includes(r)?s?(r+=["product","page"].includes(r)?"s":"",this.request(["add",r,a],{comment:s}).then((function(e){return salla.event.comment.added(e,a),e})).catch((function(e){throw salla.event.comment.additionFailed(e,a),e}))):(salla.event.comment.additionFailed(t="can't find comment content!"),salla.api.errorPromise(t)):(salla.event.comment.additionFailed(t="Failed to get type one of:(products, product, page, pages)!"),salla.api.errorPromise(t)):(salla.event.comment.additionFailed(t="Failed to get id!"),salla.api.errorPromise(t))}}class b extends h{constructor(){super(),this.namespace="currency",this.endpoints={change:"/",list:"currencies"},this.endpointsMethods={change:"get",list:"get"},this.webEndpoints=["change"],this.after_init()}change(e){if(!(e=salla.form.getPossibleValue(e.data||e,["currency","code"]))){let e="Can't find currency code!";return salla.currency.event.failed(e),salla.api.errorPromise(e)}return this.request("change",{params:{change_currency:"",currency:e}}).then((function(t){return salla.cookie.set("fresh_summary",1),salla.storage.set("cart",""),salla.currency.event.changed(t,e),t})).catch((function(t){throw salla.currency.event.failed(t,e),t}))}list(){return this.request("list").then((function(e){return salla.currency.event.fetched(e),e})).catch((function(e){throw salla.currency.event.failedToFetch(e),e}))}}class S extends h{constructor(){super(),this.namespace="document"}}class F extends h{constructor(){super(),this.namespace="rating",this.endpoints={store:"rating/store",products:"rating/products",shipping:"rating/shipping",order:"rating/{order_id}"},this.endpointsMethods={order:"get"},this.after_init()}order(e){let t="object"==typeof e?e:{},a=salla.form.getPossibleValue(e?.data||e,["order_id","id"]);if(!a){let e='There is no "order_id"!';return salla.event.rating.orderNotFetched(e),salla.api.errorPromise(e)}return this.request(["order",a],t).then((function(e){return salla.event.rating.orderFetched(e,a),e})).catch((function(e){throw salla.event.rating.orderNotFetched(e,a),e}))}store(e){if(!(e=e.data||e)){let e='There is no "data"!';return salla.event.rating.storeFailed(e),salla.api.errorPromise(e)}return this.request("store",e).then((function(t){return salla.event.rating.storeRated(t,e),t})).catch((function(t){throw salla.event.rating.storeFailed(t,e),t}))}products(e){if(!(e=e.data||e)){let e='There is no "data"!';return salla.event.rating.productsFailed(e),salla.api.errorPromise(e)}return this.request("products",e).then((function(t){return salla.event.rating.productsRated(t,e),t})).catch((function(t){throw salla.event.rating.productsFailed(t,e),t}))}shipping(e){if(!(e=e.data||e)){let e='There is no "data"!';return salla.event.rating.shippingFailed(e),salla.api.errorPromise(e)}return this.request("shipping",e).then((function(t){return salla.event.rating.shippingRated(t,e),t})).catch((function(t){throw salla.event.rating.shippingFailed(t,e),t}))}}class _ extends h{constructor(){super(),this.namespace="wishlist",this.endpoints={add:"products/favorites/{id}",remove:"products/favorites/{id}"},this.endpointsMethods={remove:"delete"},this.after_init()}toggle(e){return salla.storage.get("salla::wishlist",[]).includes(e)?this.remove(e):this.add(e)}add(e){let t;return salla.config.isGuest()?(salla.wishlist.event.additionFailed(t=salla.lang.get("common.messages.must_login")),salla.error(t),salla.api.errorPromise(t)):(e=salla.form.getPossibleValue(e?.data||e,["product_id","id"]))?this.request(["add",e]).then((t=>(this.updateWishlistStorage(e),salla.wishlist.event.added(t,e),t))).catch((function(t){throw salla.wishlist.event.additionFailed(t,e),t})):(salla.wishlist.event.additionFailed(t="Failed to get product id!"),salla.api.errorPromise(t))}remove(e){let t;return salla.config.isGuest()?(salla.wishlist.event.additionFailed(t=salla.lang.get("common.messages.must_login")),salla.error(t),salla.api.errorPromise(t)):(e=salla.form.getPossibleValue(e?.data||e,["product_id","id"]))?this.request(["remove",e]).then((t=>(this.updateWishlistStorage(e,!1),salla.wishlist.event.removed(t,e),t))).catch((function(t){throw salla.wishlist.event.removingFailed(t,e),t})):(salla.wishlist.event.removingFailed(t="Failed to get id!"),salla.api.errorPromise(t))}updateWishlistStorage(e,t=!0){let a=salla.storage.get("salla::wishlist",[]);t?a.push(e):a.splice(a.indexOf(e),1),salla.storage.set("salla::wishlist",a)}}class A extends h{constructor(){super(),this.namespace="scopes",this.endpoints={get:"scopes",change:"scopes",getProductAvailability:"scopes/availability?product_id={id}"},this.after_init()}get(){return this.request("scopes",null,"get").then((e=>(salla.scope.event.fetched(e),e))).catch((e=>{throw salla.scope.event.notFetched(e),e}))}change(e){return this.request("scopes",e).then((e=>(salla.scope.event.changeSucceeded(e),e))).catch((e=>{throw salla.scope.event.changeFailed(e),e}))}getProductAvailability(e=null){return this.request(`scopes/availability?product_id=${e}`,null,"get").then((t=>(salla.scope.event.productAvailabilityFetched(t,e),t))).catch((e=>{throw salla.scope.event.productAvailabilityNotFetched(e),resp}))}}class E{constructor(t){t?(e.config.merge(t),t?.events&&e.event.dispatchEvents(t?.events),t._token&&salla.api.setHeader("X-CSRF-TOKEN",t._token),this.injectMaintenanceAlert(),this.injectThemePreviewAlert(),this.injectEditAlert(),t?.user?.language_code&&salla.lang.setLocale(t?.user?.language_code),salla.lang.loadStoreTranslations(),e.event.dispatch("twilight::initiated",t)):e.event.dispatch("twilight::initiated",t)}injectMaintenanceAlert(){salla.config.get("maintenance")&&(document.querySelector(".store-notify")?salla.logger.warn(".store-notify element Existed before!"):salla.lang.onLoaded((()=>{const e=document.createElement("div");e.classList.add("store-notify"),e.style="background-color: #d63031; color: #fff; padding: 10px 15px; text-align: center; font-size: 17px;",e.innerHTML=`<p style="margin:0 !important;">${salla.lang.get("blocks.header.maintenance_alert")}</p>`,document.body.prepend(e)})))}injectThemePreviewAlert(){"preview"===salla.config.get("theme.mode")&&(document.querySelector("#s-theme_preview_bar")?salla.logger.warn("#s-theme_preview_bar element Existed before!"):salla.lang.onLoaded((()=>{let e=document.createElement("div");e.id="s-theme_preview_bar",e.setAttribute("style","display: flex; justify-content: space-between; text-align: center; background-color: #232323; color: #fff; padding: 10px; font-size: 0.875rem; line-height: 1.25rem; position: relative;"),e.innerHTML=`\n <div style="display:flex; align-items:center;">\n <img width="32" src="https://assets.salla.sa/cp/assets/images/logo-new.png">\n <span style="margin:0 10px;">${salla.lang.get("blocks.header.preview_mode")}: <span style="background:rgba(255,255,255,0.25);border-radius:15px; padding:2px 15px 4px">${salla.config.get("theme.name")}</span></span>\n </div>\n <a href="${salla.url.get("preview_theme/cancel/preview")}" style="line-height:32px; width:32px;"><i class="sicon-cancel"></i></a>\n `,document.body.prepend(e)})))}injectEditAlert(){let e=salla.config.get("edit");e&&(document.querySelector("#s-edit-alert")?salla.logger.warn("#s-edit-alert element Existed before!"):salla.lang.onLoaded((()=>{let t=document.createElement("div");t.id="s-edit-alert",t.innerHTML=`\n <a href="${e}" style="display:block; background-color:${salla.config.get("theme.color.primary","#5cd5c4")}; color:${salla.config.get("theme.color.reverse","#fff")}; padding: 10px; text-align:center; font-size: 0.875rem; line-height: 1.25rem;">\n <i class="sicon-edit"></i> \n ${salla.lang.get("pages.products.quick_edit")}\n </a>\n `,document.body.prepend(t)})))}handleElementAjaxRequest(e,t){if(!(t instanceof HTMLFormElement||t instanceof HTMLAnchorElement))return salla.logger.warn("trying to call ajax from non Element!!"),!1;e.preventDefault();let a=t.getAjaxFormData(e),r=a.method?a.method.toLowerCase():void 0;salla.api.request(a.url,a.formData,r).then((e=>(e.data&&e.request&&(e=e.data),salla.api.handleAfterResponseActions(e),this.callAjaxEvent(a.events.success,e,a.formData),e))).catch((e=>{throw salla.api.handleErrorResponse(e),this.callAjaxEvent(a.events.fail,e,a.formData),e}))}callAjaxEvent(e,t,a){if(e){if(a instanceof FormData){const e={};Array.from(a.entries()).forEach((function(t){e[t[0]]=t[1]})),a=e}window[e]?window[e](t,a):salla.event.dispatch(e,t,a)}}}e.notify=o,e.lang=new class extends a{constructor(e){(e=e||{}).messages=e.messages||window.translations,e.locale=e.locale||(window.locale||navigator.language||navigator.userLanguage||"ar").split("-")[0],e.fallback=e.fallback||e.locale,super(e),this.translationsLoaded=!1}onLoaded(e){if(this.translationsLoaded)return e();Salla.event.once("languages::translations.loaded",e)}loadStoreTranslations(){if(this.messages)return void window.addEventListener("load",(e=>{salla.event.dispatch("languages::translations.loaded"),salla.logger.info("The messages of transactions is already loaded")}));if(!salla.url.get(""))return void this.loadScript("https://cdn.salla.network/js/translations.js",!1);let e=salla.config.get("theme.translations_hash",salla.config.get("store.id","twilight"));this.loadScript(salla.url.get(`languages/assets/${e}.js`))}setMessages(e){super.setMessages(e),salla.event.dispatch("languages::translations.loaded"),this.translationsLoaded=!0}loadScript(e,t=!0){let a=document.createElement("script");a.src=e,a.onload=()=>{if(window.translations)return this.setMessages(window.translations);a.onerror()},a.onerror=()=>{if(t)return salla.logger.warn("Failed to load Translations for store, lets try load it from CDN"),this.loadScript("https://cdn.salla.network/js/translations.js",!1);salla.logger.error("Failed to load Translations, check your network logs for more details\nor: salla.lang.setMessages({....}), see https://docs.salla.dev for more information's.")},document.head.appendChild(a)}get(e,t,a){return window.translations&&(e="trans."+e),super.get(e,t,a)}set(e,t){return salla.helpers.setNested(this.messages[this.getLocale()+".trans"],e,t),this}},e.form=new class{async submit(e,t=null){let a=t;if("SubmitEvent"===t?.constructor?.name){if(t.preventDefault(),"FORM"!==t.target?.tagName)return Salla.logger.warn("Failed find the target element for submit action. make sure you submit a form element"),new Promise((()=>{throw"Failed find the target element for submit action. make sure you submit a form element"}));"SALLA-BUTTON"===t?.submitter?.parentElement?.tagName&&t.submitter.parentElement.load(),a=t.target.getElementSallaData(),salla.log("Data from element",a)}if(/^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$/gm.test(e))return salla.api.normalRequest(e,a,"post").finally((()=>{loaderSupported&&t?.submitter?.parentElement.stop()}));let r=e.split("."),s=r.splice(-1);return await salla.call(r.join("."))[s](a).finally((()=>t?.submitter?.parentElement?.stop())).catch((e=>{throw salla.logger.warn(e),e}))}onSubmit(e,t){return salla.form.submit(e,t),!1}onChange(e,t){return t?.currentTarget?"FORM"!==t?.currentTarget?.tagName||t.currentTarget.checkValidity()?(salla.form.submit(e,t.currentTarget.getElementSallaData()),!0):(salla.logger.warn(`Trying to trigger '${e}' without filling required fields!`),!1):(salla.logger.warn(`Trying to trigger '${e}' without event!`),!1)}getPossibleValue(e,t,a=!1){if(!e)return;if("object"!=typeof e)return e;let r;for(let a=0;a<t.length&&!(r=e[t[a]])&&!("undefined"!=typeof FormData&&e instanceof FormData&&(r=e.get(t[a])));a++);return r=r||e,"object"!=typeof r||a?r:void 0}},e.AppHelpers=class{toggleClassIf(e,t,a,r){return document.querySelectorAll(e).forEach((e=>this.toggleElementClassIf(e,t,a,r))),this}toggleElementClassIf(e,t,a,r){t=Array.isArray(t)?t:t.split(" "),a=Array.isArray(a)?a:a.split(" ");let s=r(e);return e?.classList.remove(...s?a:t),e?.classList.add(...s?t:a),this}isValidEmail(e){return/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(String(e).toLowerCase())}element(e){return"object"==typeof e?e:document.querySelector(e)}watchElement(e,t){return this[e]=this.element(t),this}watchElements(e){return Object.entries(e).forEach((e=>this.watchElement(e[0],e[1]))),this}on(e,t,a,r={}){return"object"==typeof t?(this.element(t).addEventListener(e,a,r),this):(document.querySelectorAll(t).forEach((t=>t.addEventListener(e,a,r))),this)}onClick(e,t){return this.on("click",e,t)}onKeyUp(e,t){return this.on("keyup",e,t)}onEnter(e,t){return this.onKeyUp(e,(e=>13===e.keyCode&&t(e))),this}all(e,t){return document.querySelectorAll(e).forEach(t),this}hideElement(e){return this.element(e).style.display="none",this}showElement(e,t="block"){return this.element(e).style.display=t,this}removeClass(e,t){return this.element(e).classList.remove(...Array.from(arguments).slice(1)),this}addClass(e,t){return this.element(e).classList.add(...Array.from(arguments).slice(1)),this}debounce(e,...t){return this.debounce_||(this.debounce_=Salla.helpers.debounce(((e,...t)=>e(...t)),500)),this.debounce_(e,...t)}},e.infiniteScroll=new class extends d{constructor(e,t){super(e,t),this.options={path:".infinite-scroll-btn",history:"push",status:".infinite-scroll-status",append:".list-block"},this.fetchOptions={headers:{"S-INFINITE-SCROLL":!0}},this.instances=[]}initiate(e,a,r){r=this.getCustomOptions(a,r);let s="string"!=typeof e?e:document.querySelector(e),i=r.path;if(!s||!i||"string"==typeof i&&!document.querySelector(i))return void Salla.logger.warn(s?"Path Option (a link that has next page link) Not Existed!":"Container For InfiniteScroll not Existed!");let n=new t(s,r);return n.on("scrollThreshold",Salla.infiniteScroll.event.scrollThreshold),n.on("request",Salla.infiniteScroll.event.request),n.on("load",Salla.infiniteScroll.event.load),n.on("append",Salla.infiniteScroll.event.append),n.on("error",Salla.infiniteScroll.event.error),n.on("last",Salla.infiniteScroll.event.last),n.on("history",Salla.infiniteScroll.event.history),this.instances.push(n),n}getCustomOptions(e,t){return(t="object"==typeof e&&e||t||this.options).fetchOptions=this.fetchOptions,t.path=t.path||this.options.path,t.button=t.button||t.path,t.status=t.status||this.options.status,t.hasOwnProperty("history")||(t.history=this.options.history),t.nextPage=t.nextPage||t.next_page,t.append="string"==typeof e&&e||t.append||this.options.append,t}}(void 0,salla.event.infiniteScroll),e.api=new class extends class{constructor(){salla.event.on("twilight::initiated",(()=>this.initiateRequest())),salla.event.on("profile::info.fetched",(e=>this.setCurrencyAndLanguage(e))),salla.event.on("auth::token.fetched",(e=>{salla.storage.set("token",e),this.setToken(e),salla.cart.api.reset()})),this.notifier_handler_disabled=!1,this.axios=r.create({headers:{common:{"X-Requested-With":"XMLHttpRequest","S-SOURCE":"twilight","S-APP-VERSION":"v2.0.0","S-APP-OS":"browser"}}})}getHeaders(){return this.axios.defaults.headers.common}setHeaders(e){return Object.entries(e).forEach((e=>this.setHeader(e[0],e[1]))),this}setHeader(e,t){return salla.infiniteScroll.fetchOptions.headers[e]=this.axios.defaults.headers.common[e]=t,this}async withoutNotifier(e){return this.notifier_handler_disabled=!0,await e().finally((()=>{this.notifier_handler_disabled=!1}))}initiateRequest(){this.axios.defaults.baseURL=Salla.config.get("store.api",Salla.config.get("store.url")),this.setHeaders({"Store-Identifier":Salla.config.get("store.id"),currency:Salla.config.get("user.currency_code","SAR"),"accept-language":salla.lang.getLocale(),"s-user-id":Salla.config.get("user.id")});let e=salla.storage.get("scope");e&&this.setHeaders({"s-scope-type":e.type,"s-scope-id":e.id}),this.injectTokenToTheRequests()}injectTokenToTheRequests(){let e=salla.storage.get("token"),t=Salla.config.isGuest(),a=salla.storage.get("cart");if(a&&(a.user_id!==Salla.config.get("user.id")||a.store_id!==Salla.config.get("store.id")))return salla.log("Auth:: The cart is not belong to current "+(a.user_id!==Salla.config.get("user.id")?"user":"store")+"!"),void salla.cart.api.reset();if(t&&!e)return;if(t&&e)return salla.log("Auth:: Token without user!"),salla.storage.remove("token"),void salla.cart.api.reset();if(!e)return salla.cart.api.reset(),void salla.auth.api.refresh();let r=s(e);return Date.now()/1e3>r.exp?(salla.log("Auth:: An expired token!"),salla.storage.remove("token"),salla.cart.api.reset(),void salla.auth.api.refresh()):r.sub!==Salla.config.get("user.id")?(salla.log("Auth:: The user id is not match the token details!"),salla.storage.remove("token"),salla.cart.api.reset(),void salla.auth.api.refresh()):void this.setToken(e)}setToken(e){return this.setHeader("Authorization","Bearer "+e)}setCurrencyAndLanguage(e){return this.setHeaders({currency:e.data.currency,"accept-language":e.data.language})}request(e,t,a="get",r={}){let s={endPoint:e,payload:t,method:a,options:r},i="undefined"!=typeof event?event.currentTarget:null,n=!1;return"SALLA-BUTTON"===i?.tagName&&(n=!0),n&&i?.load(),this.axios[s.method](s.endPoint,s.payload,s.options).then((e=>(n&&i?.stop(),e.data&&e.request&&(e=e.data),this.handleAfterResponseActions(e),e))).catch((e=>{throw n&&i?.stop(),salla.event.document.requestFailed(s,e),this.handleErrorResponse(e),e}))}handleAfterResponseActions(e){if(!e)return;let{data:t,googleTags:a=null}=e,r=t&&t.googleTags?t.googleTags:a;dataLayer&&r&&dataLayer.push(r),this.fireEventsForResponse(e),this.showAlert(e),this.renderSections(e)}fireEventsForResponse(e){let t=e?.events||e.data?.events||e.error?.events;"string"==typeof t&&(t=JSON.parse(t)),t&&Object.keys(t).forEach((e=>salla.event.dispatch(e,t[e])))}handleErrorResponse(e){if(e.response&&e.response.data)return e.response.data.error&&e.response.data.error.fields&&!this.notifier_handler_disabled?this.handleInvalidFields(e):this.handleAfterResponseActions(e.response.data)}showAlert(e){if(e&&!this.notifier_handler_disabled)return e.case&&e.msg?salla.notify.fire(e.msg,e.case,e):e.hasOwnProperty("success")&&e.data?.message?salla.notify.fire(e.data?.message,e.success?salla.notify.types.success:salla.notify.types.error,e):e.error&&e.error.message&&"FORBIDDEN"!==e.error.message?salla.error(e.error.message,e):void 0}handleInvalidFields(e){let t=e.response.data.error.fields,a=[];Object.keys(t).forEach((e=>{let r=t[e];Array.isArray(r)?r.forEach((e=>a.push(e))):a.push(r)}));let r=(a.length>1?"* ":"")+a.join("\n* ");salla.error(r,e)}isFastRequestsAllowed(){return Salla.config.get("fastRequests")}promise(e,t=!0){return new Promise(((a,r)=>t?a(e):r(e)))}errorPromise(e){return this.promise(e,!1)}successPromise(e){return this.promise(e,!0)}renderSections(e){e.sections&&Object.keys(e.sections).forEach((t=>{document.querySelectorAll(t.toSelector()).forEach((function(a){a.innerHTML=e.sections[t]}))}))}}{constructor(){super(),this.auth=new g,this.cart=new p,this.loyalty=new f,this.order=new m,this.rating=new F,this.product=new v,this.profile=new y,this.comment=new w,this.currency=new b,this.document=new S,this.wishlist=new _,this.scope=new A}},e.cart=new d(e.api.cart,e.event.cart),e.auth=new d(e.api.auth,e.event.auth),e.order=new d(e.api.order,e.event.order),e.scope=new d(e.api.scope,e.event.scope),e.rating=new d(e.api.rating,e.event.rating),e.comment=new d(e.api.comment,e.event.comment),e.loyalty=new d(e.api.loyalty,e.event.loyalty),e.product=new d(e.api.product,e.event.product),e.profile=new d(e.api.profile,e.event.profile),e.currency=new d(e.api.currency,e.event.currency),e.document=new d(e.api.document,e.event.document),e.wishlist=new d(e.api.wishlist,e.event.wishlist),e.call=t=>{let a=e,r=t.split(".");for(;r.length&&(a=a[r.shift()]););return a},e.init=e=>new E(e),salla.event.once("twilight::init",salla.init),e.success=e.notify.success,e.error=e.notify.error,e.versions.twilight="[VI]{version}[/VI]",e.event.dispatch("twilight::ready"),e.onInitiated=e=>salla.event.once("twilight::initiated",e),e.onReady=e.onInitiated;
1
+ import Salla$1 from '@salla.sa/base';
2
+ export { default } from '@salla.sa/base';
3
+ import TheInfiniteScroll from 'infinite-scroll';
4
+ import Lang from 'lang.js';
5
+ import axios from 'axios';
6
+ import jwtDecode from 'jwt-decode';
7
+
8
+ let notifier = function (message, type, data) {
9
+ return alert(message);
10
+ };
11
+
12
+ let setNotifier = function (callback) {
13
+ notifier = callback;
14
+ };
15
+
16
+ let fire = function (message, type, data) {
17
+ return notifier(message, type, data);
18
+ };
19
+
20
+ let error = function (message, data) {
21
+ return notifier(message, types.error, data);
22
+ };
23
+
24
+ let info = function (message, data) {
25
+ return notifier(message, types.info, data);
26
+ };
27
+
28
+ let success = function (message, data) {
29
+ return notifier(message, types.success, data);
30
+ };
31
+
32
+ let sallaInitiated = function () {
33
+ /**
34
+ * In salla sometimes, visitor redirected with danger url query,
35
+ * Here we get the error message then show it with salla error notifier
36
+ * 💡 we can use "(?<=[\?\&]danger=)[^&]+" to get message only, but it's not working in safari,
37
+ * so just use normal regex, then replace "danger=".
38
+ *
39
+ * TODO:: pass the error message via config
40
+ */
41
+ let results = window.location.href.match(/([\?\&]danger=)[^&]+/g);
42
+
43
+ if (!results) {
44
+ return;
45
+ }
46
+
47
+ results.forEach(message => {
48
+ error(decodeURI(message.replace('?danger=', '').replace('&danger=', '')));
49
+ });
50
+ };
51
+ /**
52
+ * in case there is need to map types, it's possible from here;
53
+ * @type {{success: string, error: string, info: string}}
54
+ */
55
+
56
+
57
+ let types = {
58
+ error: "error",
59
+ success: "success",
60
+ info: "info"
61
+ };
62
+ var Notify = {
63
+ fire,
64
+ setNotifier,
65
+ error,
66
+ success,
67
+ info,
68
+ sallaInitiated,
69
+ types
70
+ };
71
+
72
+ /**
73
+ * This Class Is helper to allow us using events & apis like:
74
+ * Salla.api.cart => Salla.cart.api
75
+ * Salla.event.cart => Salla.cart.event
76
+ *
77
+ * or even:
78
+ * Salla.cart.api.addItem() => salla.cart.addItem() 😎
79
+ *
80
+ * @property {BaseApi} api
81
+ * @property {BaseEvent} event
82
+ **/
83
+ class Module {
84
+ /**
85
+ * @param {TwilightApi|BaseApi|any} api
86
+ * @param {BaseEvent} event
87
+ * @return {Module}
88
+ */
89
+ constructor(api, event) {
90
+ this.api = api;
91
+ this.event = event;
92
+ return new Proxy(this, {
93
+ get: function get(target, name) {
94
+ // when calling `salla.cart.event` return `CartEvent` class
95
+ if (name === 'event') {
96
+ return event;
97
+ } // when calling `salla.cart.api` return `CartApi` class
98
+
99
+
100
+ if (name === 'api') {
101
+ return api;
102
+ } // useful in infiniteScrollClass, because it's without apiClass
103
+
104
+
105
+ if (!api) {
106
+ return target[name];
107
+ }
108
+
109
+ return api[name] || target[name];
110
+ }
111
+ });
112
+ }
113
+
114
+ }
115
+
116
+ // todo:: cover these helpers into types
117
+ String.prototype.toStudlyCase = function () {
118
+ return this.trim() //remove first spaces
119
+ .replace(/([^a-zA-Z\d].)/g, function (t) {
120
+ //get
121
+ return t.toUpperCase().replace(/[^a-zA-Z\d]/g, '');
122
+ });
123
+ };
124
+
125
+ String.prototype.toDatasetName = function () {
126
+ return this.startsWith('data-') ? this.substr(5).toStudlyCase() : this.toStudlyCase();
127
+ }; // helper method to get string as selector, by adding (#) to the beginning if it's not start with one.
128
+
129
+
130
+ String.prototype.toSelector = function () {
131
+ // todo:: add html tags if it's needed.
132
+ return this.trim().startsWith(".") || this.trim().startsWith("#") ? this : "#" + this;
133
+ };
134
+
135
+ String.prototype.replaceArray = function (find, replace) {
136
+ var replaceString = this;
137
+ var regex;
138
+
139
+ for (var i = 0; i < find.length; i++) {
140
+ regex = new RegExp(find[i], "g");
141
+ replaceString = replaceString.replace(regex, replace[i]);
142
+ }
143
+
144
+ return replaceString;
145
+ };
146
+
147
+ String.prototype.rtrim = function (s) {
148
+ if (s === undefined) s = '\\s';
149
+ return this.replace(new RegExp("[" + s + "]*$"), '');
150
+ };
151
+
152
+ String.prototype.ltrim = function (s) {
153
+ if (s === undefined) s = '\\s';
154
+ return this.replace(new RegExp("^[" + s + "]*"), '');
155
+ };
156
+
157
+ String.prototype.digitsOnly = function () {
158
+ return Salla.helpers.digitsOnly(this);
159
+ };
160
+
161
+ // todo:: cover these helpers into types
162
+ // support element.closest in IE
163
+ if (!Element.prototype.matches) Element.prototype.matches = Element.prototype.msMatchesSelector;
164
+ if (!Element.prototype.closest) Element.prototype.closest = function (selector) {
165
+ var el = this;
166
+
167
+ while (el) {
168
+ if (el.matches(selector)) {
169
+ return el;
170
+ }
171
+
172
+ el = el.parentElement;
173
+ }
174
+ };
175
+ /**
176
+ * In case we want to get data by attribute, lets say for example attribute "data-cart-delete"
177
+ * we will try to get data for this element, by these ways:
178
+ * 1- <a data-on-click="cart-delete-item" data-form="12324">x</a>
179
+ * 2- <a data-on-click="cart-delete-item" data-form='{"id":12324}'>x</a>
180
+ * 3- <a data-on-click="cart-delete-item" data-id="12324">x</a>
181
+ * 4- <form><input type="id" name="id"><a data-on-click="cart-delete-item">x</a></form>
182
+ * 5- <script>var item_id=12324;</script><a data-on-click="cart-delete-item" data-form="item_id">x</a>
183
+ * 6- <script>function get_item_id(){return 12324;}</script><a data-on-click="cart-delete-item" data-form="get_item_id">x</a>
184
+ * 7- <div salla-form-data><input type="hidden" name="item_id" value="12324"><a data-on-click="cart-delete-item">x</a></div>
185
+ */
186
+
187
+ Element.prototype.getElementSallaData = function (attributeName, ...restData) {
188
+ // trying to call it for non-htmlElement object
189
+ if (!this.getAttribute) {
190
+ return;
191
+ } //try to get json
192
+
193
+
194
+ if (this.hasAttribute('data-json')) {
195
+ try {
196
+ return JSON.parse(this.getAttribute('data-json'));
197
+ } catch (e) {}
198
+ }
199
+
200
+ let funName = this.getAttribute('data-function'); //if there function name existed and it's existed window Dom, or try to get data from parent form, or return dataset.
201
+
202
+ if (funName && window[funName]) {
203
+ //is data existed as function in window
204
+ if (typeof window[funName] == 'function') {
205
+ //call function via call to pass element, will be accessible in function by "this".
206
+ return this.getFilteredData(window[funName].call(this, ...restData));
207
+ } //if data existed in window Dom, return it.
208
+
209
+
210
+ return this.getFilteredData(window[funName]);
211
+ }
212
+
213
+ let formData = this.hasAttribute('data-form-selector') ? document.querySelector(this.dataset.formSelector) : undefined;
214
+
215
+ if (this.tagName === 'FORM') {
216
+ formData = this;
217
+ } else {
218
+ // try to get parent form div, or just use this element as the parent, to get it's children inputes;
219
+ formData = formData || this.closest('form') || this.closest('[salla-form-data]') || this;
220
+ }
221
+
222
+ if (formData && formData.tagName === 'FORM') {
223
+ return this.getFilteredData(new FormData(formData), null, formData);
224
+ } //try to get children inputs;
225
+
226
+
227
+ let inputs = formData.querySelectorAll("[name]");
228
+
229
+ if (!inputs.length) {
230
+ return this.getFilteredData();
231
+ } //to get normal object instead of DOMStringMap
232
+
233
+
234
+ let dataset = Object.assign({}, this.dataset);
235
+ inputs.forEach(input => {
236
+ //if it's not selected ignore it
237
+ if (['checkbox', 'radio'].includes(input.type) && !input.checked) {
238
+ return;
239
+ }
240
+
241
+ try {
242
+ //TODO:: Check multiple select how it will be.
243
+ let inputValueWithName = Salla.helpers.inputData(input.name, input.value, dataset);
244
+ dataset[inputValueWithName.name] = inputValueWithName.value;
245
+ } catch (e) {
246
+ Salla.log(input.name + ' can\'t be send');
247
+ }
248
+ });
249
+ return this.getFilteredData(dataset);
250
+ };
251
+ /**
252
+ * Salla Connects Api Actions By Hooks, it handle dynamic connect suitable events:
253
+ * @example '<button type="submit" data-on-click="auth-login" data-form="...">add</button>'
254
+ *
255
+ * @param {Event} event
256
+ * @return {boolean|string}
257
+ */
258
+
259
+
260
+ Element.prototype.canEventFireHook = function (event) {
261
+ return this.hasAttribute ? this.hasAttribute(Salla.api.hooksPrefix + event.type) : false;
262
+ };
263
+ /**
264
+ * @param {string} startsWith
265
+ * @param {boolean|undefined} returnAttribute
266
+ * @return {boolean|string}
267
+ */
268
+
269
+
270
+ Element.prototype.hasAttributeStartsWith = function (startsWith, returnAttribute) {
271
+ //to avoid any change in letters cases
272
+ startsWith = startsWith.toLowerCase();
273
+
274
+ for (var i = 0; i < this.attributes.length; i++) {
275
+ let name = this.attributes[i].name.toLowerCase();
276
+
277
+ if (name.indexOf(startsWith) === 0) {
278
+ return returnAttribute ? name : true;
279
+ }
280
+ }
281
+
282
+ return false;
283
+ };
284
+
285
+ HTMLFormElement.prototype.getAjaxFormData = function (event) {
286
+ //laravel.errors.clearValidation(event.target);
287
+ //laravel.ajax.disabledSubmitButton(event.target);
288
+ // get the input field with type="file"
289
+ var files = this.querySelectorAll('input[type="file"]'); //add the "disabled" attribute to the input
290
+
291
+ files.forEach(file => {
292
+ if (!file.files.length) {
293
+ file.setAttribute('disabled', "");
294
+ }
295
+ });
296
+ var formData = new FormData(this); // remove the "disabled" attribute
297
+
298
+ files.forEach(file => {
299
+ if (!file.files.length) {
300
+ file.removeAttribute('disabled');
301
+ }
302
+ });
303
+ return {
304
+ formData: this.getFilteredData(formData, event),
305
+ url: this.getAttribute('action'),
306
+ method: this.getAttribute('method') || 'post',
307
+ events: {
308
+ success: this.dataset.onSuccess,
309
+ fail: this.dataset.onFail
310
+ }
311
+ };
312
+ };
313
+ /**
314
+ * Fire The Function That Passed To Ajax <a> or <form>, with data attribute, Ex.
315
+ * <a class="ajax" href="..." data-filter-before-send="myFunctionName">...</a>
316
+ * it will pass (formData, object, event) to the function.
317
+ * @param {Object|null} event
318
+ * @param {Object|null} formData
319
+ * @param {HTMLElement|null} element
320
+ */
321
+
322
+
323
+ Element.prototype.getFilteredData = function (formData = null, event = null, element = null) {
324
+ formData = formData || element?.dataset || this.dataset; //when get data from selector we may need current value too, ex: <input name=quantity data-from-selector=".form">
325
+ // we need quantity value + data from selector
326
+
327
+ if (element && this.name && this.value !== undefined) {
328
+ formData = injectValueToFromData(formData, this.name, this.value);
329
+ }
330
+
331
+ ['filterBeforeSubmit', 'filterBeforeSend'].forEach(propertyName => {
332
+ let functionName = element?.dataset[propertyName] || this.dataset[propertyName];
333
+
334
+ if (functionName) {
335
+ var callback_form = window[functionName];
336
+
337
+ if (typeof callback_form === 'function') {
338
+ let filteredData = callback_form(formData, element || this, event);
339
+
340
+ if (!filteredData && formData) {
341
+ throw `Data failed to be pass verify function window.${functionName}(formData, element, event)!`;
342
+ }
343
+
344
+ return callback_form(formData, element || this, event);
345
+ }
346
+
347
+ Salla.log('window.' + functionName + '() not found!');
348
+ }
349
+ });
350
+ return formData;
351
+ };
352
+
353
+ HTMLAnchorElement.prototype.getAjaxFormData = function (event) {
354
+ return {
355
+ formData: this.getFilteredData(null, event),
356
+ url: this.getAttribute('href'),
357
+ method: this.dataset.type || 'get',
358
+ events: {
359
+ success: this.dataset.onSuccess,
360
+ fail: this.dataset.onFail
361
+ }
362
+ };
363
+ };
364
+ /**
365
+ *
366
+ * @param {object|FormData} formData
367
+ * @param key
368
+ * @param value
369
+ * @return {*}
370
+ */
371
+
372
+
373
+ function injectValueToFromData(formData, key, value) {
374
+ if (formData instanceof FormData) {
375
+ formData.append(key, value);
376
+ } else {
377
+ formData[key] = value;
378
+ }
379
+
380
+ return formData;
381
+ }
382
+
383
+ /**
384
+ * @property {Emitter} parent
385
+ * @property {array} events
386
+ * @property {string} namespace
387
+ */
388
+ class BaseEvent {
389
+ constructor() {
390
+ this.events = {};
391
+ this.namespace = '';
392
+ let results = /function (.{1,})\(/.exec(this.constructor.toString());
393
+ this.className = (results && results.length > 1 ? results[1] : "").toLowerCase();
394
+ }
395
+ /**
396
+ * should be called from child class, after preparing data, to initiate things.
397
+ */
398
+
399
+
400
+ after_init() {
401
+ this.createDynamicFunctions();
402
+ }
403
+
404
+ createDynamicFunctions() {
405
+ Object.keys(this.events).forEach(eventName => {
406
+ this.createDynamicEventFuns(eventName);
407
+ this.createDynamicListenerFuns(eventName);
408
+ });
409
+ }
410
+ /**
411
+ * instead of hardcode all events, we can do that here
412
+ * for example we have in cart class about 7 events
413
+ * instead of writing something like:
414
+ * @example
415
+ * itemAdded(response, item_id) {
416
+ * return this.dispatch('itemAdded', response, item_id);
417
+ * }
418
+ * for each event, we will do that dynamically here,
419
+ * if there is need for special handling, we can just hardcode it & it will not overriden
420
+ * @param {string} eventName
421
+ */
422
+
423
+
424
+ createDynamicEventFuns(eventName) {
425
+ // if it's already exist don't override it.
426
+ if (this[eventName]) {
427
+ return;
428
+ }
429
+
430
+ let thatEvent = this;
431
+
432
+ this[eventName] = function (...response) {
433
+ return thatEvent.dispatch(eventName, ...response);
434
+ };
435
+ }
436
+ /**
437
+ * instead of hardcode all eventsListeners, we can do that here
438
+ * for example we have in cart class about 7 events
439
+ * instead of writing some thing like:
440
+ * @example
441
+ * onItemAdded(callback) {
442
+ * return this.on('itemAdded', callback);
443
+ * }
444
+ * for each event, we will do that dynamically here,
445
+ * if there is need for special handling, we can just hardcode it & it will not overriden
446
+ * @param {string} eventName
447
+ */
448
+
449
+
450
+ createDynamicListenerFuns(eventName) {
451
+ //will be something like 'onItemAdded'
452
+ let listenerName = 'on' + eventName.charAt(0).toUpperCase() + eventName.slice(1);
453
+
454
+ if (this[listenerName]) {
455
+ return;
456
+ }
457
+
458
+ let thatEvent = this;
459
+
460
+ this[listenerName] = function (callback) {
461
+ return thatEvent.on(eventName, callback);
462
+ };
463
+ }
464
+ /**
465
+ * get event name by adding namespace, or saved events;
466
+ *
467
+ * @param {string} eventName
468
+ * @return {string}
469
+ */
470
+
471
+
472
+ getEventName(eventName) {
473
+ eventName = this.events[eventName] || eventName; //if there is no namespace or eventName has namespace, return event as is.
474
+
475
+ if (!this.namespace || eventName.includes('::')) {
476
+ return eventName;
477
+ } //will be something like "cart::"
478
+
479
+
480
+ return this.namespace + Salla.event.delimiter + eventName;
481
+ }
482
+ /**
483
+ * @param eventName
484
+ * @param {...*} passData
485
+ */
486
+
487
+
488
+ dispatch(eventName, ...passData) {
489
+ return Salla.event.dispatch(this.getEventName(eventName), ...passData);
490
+ }
491
+ /**
492
+ * @param {string} eventName
493
+ * @param {function} callback
494
+ */
495
+
496
+
497
+ on(eventName, callback) {
498
+ return Salla.event.addListener(this.getEventName(eventName), callback);
499
+ }
500
+ /**
501
+ * @param {string} eventName
502
+ * @param {function} callback
503
+ */
504
+
505
+
506
+ once(eventName, callback) {
507
+ return Salla.event.once(this.getEventName(eventName), callback);
508
+ }
509
+
510
+ }
511
+
512
+ class AuthEvents extends BaseEvent {
513
+ constructor() {
514
+ super();
515
+ this.namespace = 'auth';
516
+ /**
517
+ * 💡 Notice that you can override any event by creating method, if we have 'this.events={...,codeSent:code-sent,...}',
518
+ * it can be override by creating method like:
519
+ * codeSent(response){
520
+ * //your costume code
521
+ * ....
522
+ * //then call dispatch to emit other listeners
523
+ * return this.dispatch('codeSent', ...arguments);
524
+ * }
525
+ * 💡 namespace "auth::" will be added dynamically.
526
+ * all events will be initiated to have three functions, for ex: event "login" wil be:
527
+ * 🔸 name: auth::login
528
+ * 🔸 fire: Salla.auth.event.login(...data)
529
+ * 🔸 listen: Salla.auth.event.onLogin((...data) => Salla.log(data))
530
+ * 🔸 fireWithName: Salla.event.emit('auth::login',...data)
531
+ * 🔸 listenWithName: Salla.event.on('auth::login',(...data)=>Salla.log(data))
532
+ */
533
+
534
+ this.events = {
535
+ // prepared event use it like "Salla.auth.event.login()" or "Salla.auth.event.onLogin(func...)"
536
+ login: "login",
537
+ logout: "logout",
538
+ // events connected With auth api, also can be used separately standalone.
539
+ codeSent: "code.sent",
540
+ codeNotSent: "code.not-sent",
541
+ verified: "verified",
542
+ verificationFailed: "verification.failed",
543
+ loggedIn: "logged.in",
544
+ registered: "registered",
545
+ registrationFailed: "registration.failed",
546
+ loggedOut: "logged.out",
547
+ failedLogout: "failed.logout",
548
+ refreshFailed: "refresh.failed",
549
+ tokenFetched: "token.fetched"
550
+ };
551
+ this.after_init();
552
+ }
553
+
554
+ login(detail) {
555
+ //todo:: remove this workaround
556
+ if (!detail) {
557
+ this.next_event = null;
558
+ return this.dispatch('login', ...arguments);
559
+ }
560
+
561
+ if (detail.type) {
562
+ this.setTypeActionOnVerified(detail.type);
563
+ }
564
+
565
+ this.next_event = detail.next_event || null;
566
+ return this.dispatch('login', ...arguments);
567
+ }
568
+ /**
569
+ * @param {AuthResponse.verify} response
570
+ * @return {*}
571
+ */
572
+
573
+
574
+ loggedIn(response) {
575
+ Salla.profile.info().then(response => {
576
+ salla.config.set('user', {
577
+ id: response.data.id,
578
+ type: 'user',
579
+ email: response.data.email,
580
+ mobile: response.data.phone.code + response.data.phone.number,
581
+ country_code: response.data.phone.country,
582
+ language_code: response.data.language,
583
+ currency_code: response.data.currency
584
+ });
585
+ }).finally(() => this.dispatch('loggedIn', response));
586
+ }
587
+
588
+ setTypeActionOnVerified(type) {
589
+ this.type_action_on_verified = type;
590
+ }
591
+
592
+ getTypeActionOnVerified() {
593
+ return this.type_action_on_verified || 'redirect';
594
+ }
595
+
596
+ }
597
+
598
+ class CartEvents extends BaseEvent {
599
+ constructor() {
600
+ super();
601
+ this.namespace = 'cart';
602
+ /**
603
+ * 💡 Notice that you can override any event by creating method, if we have 'this.events={...,codeSent:code-sent,...}',
604
+ * it can be override by creating method like:
605
+ * codeSent(response){
606
+ * //your costume code
607
+ * ....
608
+ * //then call dispatch to emit other listeners
609
+ * return this.dispatch('codeSent', ...arguments);
610
+ * }
611
+ * 💡 namespace 'cart::' will be added dynamically
612
+ * all events will be initiated to have three functions, for ex: event "itemAdded" wil be:
613
+ * 🔸 name: cart::item-added
614
+ * 🔸 fire: Salla.cart.event.itemAdded(...data)
615
+ * 🔸 listen: Salla.cart.event.onItemAdded((...data) => Salla.log(data))
616
+ * 🔸 fireWithName: Salla.event.emit('cart::item-added',...data)
617
+ * 🔸 listenWithName: Salla.event.on('cart::item-added',(...data)=>Salla.log(data))
618
+ */
619
+
620
+ this.events = {
621
+ latestFetched: 'latest.fetched',
622
+ latestFailed: 'latest.failed',
623
+ updated: 'updated',
624
+ itemUpdated: 'item.updated',
625
+ itemUpdatedFailed: 'item.updated.failed',
626
+ itemAdded: 'item.added',
627
+ itemAddedFailed: 'item.added.failed',
628
+ itemDeleted: 'item.deleted',
629
+ itemDeletedFailed: 'item.deleted.failed',
630
+ submitted: 'submitted',
631
+ submitFailed: 'submit.failed',
632
+ imageDeleted: 'image.deleted',
633
+ imageNotDeleted: 'image.not.deleted',
634
+ detailsFetched: 'details.fetched',
635
+ detailsNotFetched: 'details.not.fetched',
636
+ successReset: 'success.reset',
637
+ //coupons
638
+ couponAdded: 'coupon.added',
639
+ couponDeleted: 'coupon.deleted',
640
+ couponAdditionFailed: 'coupon.addition.failed',
641
+ couponDeletionFailed: 'coupon.deletion.failed'
642
+ };
643
+ this.after_init();
644
+ }
645
+ /** ============ Events ============ **/
646
+
647
+ /**
648
+ *
649
+ * @param {CartUpdatedData|Object} data
650
+ * @return {*}
651
+ */
652
+
653
+
654
+ updated(data) {
655
+ //because we are working on api & web when updating cart via api, we need from web to get fresh summary
656
+ Salla.cookie.set('fresh_summary', 1);
657
+
658
+ if (!data || typeof data !== 'object') {
659
+ Salla.logger.info('Cart summary not an object!', data);
660
+ return this.dispatch('updated');
661
+ }
662
+
663
+ if (data.offer) {
664
+ salla.product.event.offerExisted(data.offer);
665
+ } //we can store (carSummary) as is, but it may store too much data, or different format
666
+
667
+
668
+ salla.storage.set('cart.summary', {
669
+ total: data.cart.total,
670
+ sub_total: data.cart.sub_total,
671
+ discount: data.cart.discount,
672
+ real_shipping_cost: data.cart.real_shipping_cost,
673
+ count: data.cart.count,
674
+ shipping_cost: data.cart.free_shipping_bar?.has_free_shipping ? 0 : data.cart.real_shipping_cost
675
+ });
676
+ return this.dispatch('updated', data.cart);
677
+ }
678
+ /**
679
+ * @param {CartResponse.update} response
680
+ * @return {*}
681
+ */
682
+
683
+
684
+ latestFetched(response) {
685
+ this.updated(response.data);
686
+ return this.dispatch('latestFetched', response);
687
+ }
688
+ /**
689
+ * @param {CartResponse.update} response
690
+ * @param item_id
691
+ * @return {*}
692
+ */
693
+
694
+
695
+ itemAdded(response, item_id) {
696
+ this.updated(response.data);
697
+ return this.dispatch('itemAdded', response, item_id);
698
+ }
699
+ /**
700
+ * @param {CartResponse.update} response
701
+ * @param item_id
702
+ * @return {*}
703
+ */
704
+
705
+
706
+ itemDeleted(response, item_id) {
707
+ this.updated(response.data);
708
+ return this.dispatch('itemDeleted', response, item_id);
709
+ }
710
+ /**
711
+ * @param {CartResponse.update} response
712
+ * @param {number} item_id
713
+ * @return {*}
714
+ */
715
+
716
+
717
+ itemUpdated(response, item_id) {
718
+ this.updated(response.data);
719
+ return this.dispatch('itemUpdated', response, item_id);
720
+ } //Coupons
721
+
722
+ /**
723
+ * @param {CartResponse.update} response
724
+ * @param {number} cart_id
725
+ * @return {*}
726
+ */
727
+
728
+
729
+ couponAdded(response, cart_id) {
730
+ this.updated(response.data);
731
+ return this.dispatch('couponAdded', response, cart_id);
732
+ }
733
+ /**
734
+ * @param {CartResponse.update} response
735
+ * @param {number} cart_id
736
+ * @return {*}
737
+ */
738
+
739
+
740
+ couponDeleted(response, cart_id) {
741
+ this.updated(response.data);
742
+ return this.dispatch('couponDeleted', response, cart_id);
743
+ }
744
+
745
+ }
746
+
747
+ class OrderEvents extends BaseEvent {
748
+ constructor() {
749
+ super();
750
+ this.namespace = 'order';
751
+ /**
752
+ * 💡 Notice that you can override any event by creating method, if we have 'this.events={...,codeSent:code-sent,...}',
753
+ * it can be override by creating method like:
754
+ * codeSent(response){
755
+ * //your costume code
756
+ * ....
757
+ * //then call dispatch to emit other listeners
758
+ * return this.dispatch('codeSent', ...arguments);
759
+ * }
760
+ * 💡 namespace "order::" will be added dynamically,
761
+ * all events will be initiated to have three functions, for ex: event "canceled" wil be:
762
+ * 🔸 name: order::canceled
763
+ * 🔸 fire: Salla.order.event.canceled(...data)
764
+ * 🔸 listen: Salla.order.event.onCanceled((...data) => Salla.log(data))
765
+ * 🔸 fireWithName: Salla.event.emit('order::canceled',...data)
766
+ * 🔸 listenWithName: Salla.event.on('order::canceled',(...data)=>Salla.log(data))
767
+ */
768
+
769
+ this.events = {
770
+ canceled: "canceled",
771
+ notCanceled: "not.canceled",
772
+ orderCreated: "order.created",
773
+ orderCreationFailed: "order.creation.failed",
774
+ invoiceSent: "invoice.sent",
775
+ invoiceNotSent: "invoice.not.sent"
776
+ };
777
+ this.after_init();
778
+ }
779
+
780
+ }
781
+
782
+ class ScopeEvents extends BaseEvent {
783
+ constructor() {
784
+ super();
785
+ this.namespace = 'scope';
786
+ this.events = {
787
+ fetched: 'fetched',
788
+ notFetched: 'not.fetched',
789
+ productAvailabilityFetched: 'product-availability.fetched',
790
+ productAvailabilityNotFetched: 'product-availability.not.fetched',
791
+ changeSucceeded: 'changed',
792
+ changeFailed: 'not.changed'
793
+ };
794
+ this.after_init();
795
+ }
796
+
797
+ }
798
+
799
+ class RatingEvents extends BaseEvent {
800
+ constructor() {
801
+ super();
802
+ this.namespace = 'rating';
803
+ /**
804
+ * 💡 Notice that you can override any event by creating method, if we have 'this.events={...,storeRated:store.rated,...}',
805
+ * it can be override by creating method like:
806
+ * storeRated(response){
807
+ * //your costume code
808
+ * ....
809
+ * //then call dispatch to emit other listeners
810
+ * return this.dispatch('storeRated', ...arguments);
811
+ * }
812
+ * 💡 namespace "rating::" will be added dynamically,
813
+ * all events will be initiated to have three functions, for ex: event "storeRated" wil be:
814
+ * 🔸 name: rating::store-rated
815
+ * 🔸 fire: Salla.rating.event.storeRated(...data)
816
+ * 🔸 listen: Salla.rating.event.onStoreRated((...data) => Salla.log(data))
817
+ * 🔸 fireWithName: Salla.event.emit('rating::store-rated',...data)
818
+ * 🔸 listenWithName: Salla.event.on('rating::store-rated',(...data)=>Salla.log(data))
819
+ */
820
+
821
+ this.events = {
822
+ orderNotFetched: "order.not.fetched",
823
+ orderFetched: "order.fetched",
824
+ storeRated: "store.rated",
825
+ storeFailed: "store.failed",
826
+ productsRated: "products.rated",
827
+ productsFailed: "products.failed",
828
+ shippingRated: "shipping.rated",
829
+ shippingFailed: "shipping.failed"
830
+ };
831
+ this.after_init();
832
+ }
833
+
834
+ }
835
+
836
+ class CommentEvents extends BaseEvent {
837
+ constructor() {
838
+ super();
839
+ this.namespace = 'comment';
840
+ /**
841
+ * 💡 Notice that you can override any event by creating method, if we have 'this.events={...,codeSent:code-sent,...}',
842
+ * it can be override by creating method like:
843
+ * codeSent(response){
844
+ * //your costume code
845
+ * ....
846
+ * //then call dispatch to emit other listeners
847
+ * return this.dispatch('codeSent', ...arguments);
848
+ * }
849
+ * 💡 namespace "comment::" will be added dynamically,
850
+ * all events will be initiated to have three functions, for ex: event "added" wil be:
851
+ * 🔸 name: comment::added
852
+ * 🔸 fire: Salla.comment.event.added(...data)
853
+ * 🔸 listen: Salla.comment.event.onAdded((...data) => Salla.log(data))
854
+ * 🔸 fireWithName: Salla.event.emit('comment::added',...data)
855
+ * 🔸 listenWithName: Salla.event.on('comment::added',(...data)=>Salla.log(data))
856
+ */
857
+
858
+ this.events = {
859
+ added: "added",
860
+ additionFailed: "addition.failed"
861
+ };
862
+ this.after_init();
863
+ }
864
+
865
+ }
866
+
867
+ class LoyaltyEvents extends BaseEvent {
868
+ constructor() {
869
+ super();
870
+ this.namespace = 'loyalty';
871
+ /**
872
+ * 💡 Notice that you can override any event by creating method, if we have 'this.events={...,codeSent:code-sent,...}',
873
+ * it can be override by creating method like:
874
+ * codeSent(response){
875
+ * //your costume code
876
+ * ....
877
+ * //then call dispatch to emit other listeners
878
+ * return this.dispatch('codeSent', ...arguments);
879
+ * }
880
+ * 💡 namespace 'loyalty::' will be added dynamically,
881
+ * so last result will be something like: 'loyalty::item-added'
882
+ */
883
+
884
+ this.events = {
885
+ exchangeSucceeded: 'exchange.succeeded',
886
+ exchangeFailed: 'exchange.failed',
887
+ programFetched: 'program.fetched',
888
+ programNotFetched: 'program.not.fetched',
889
+ resetSucceeded: 'exchange-reset.succeeded',
890
+ resetFailed: 'exchange-reset.failed'
891
+ };
892
+ this.after_init();
893
+ }
894
+
895
+ }
896
+
897
+ class ProfileEvents extends BaseEvent {
898
+ constructor() {
899
+ super();
900
+ this.namespace = 'profile';
901
+ /**
902
+ * 💡 Notice that you can override any event by creating method, if we have 'this.events={...,codeSent:code-sent,...}',
903
+ * it can be override by creating method like:
904
+ * codeSent(response){
905
+ * //your costume code
906
+ * ....
907
+ * //then call dispatch to emit other listeners
908
+ * return this.dispatch('codeSent', ...arguments);
909
+ * }
910
+ * 💡 namespace "profile::" will be updated dynamically,
911
+ * all events will be initiated to have three functions, for ex: event "updated" wil be:
912
+ * 🔸 name: profile::updated
913
+ * 🔸 fire: Salla.profile.event.updated(...data)
914
+ * 🔸 listen: Salla.profile.event.onAdded((...data) => Salla.log(data))
915
+ * 🔸 fireWithName: Salla.event.emit('profile::updated',...data)
916
+ */
917
+
918
+ this.events = {
919
+ updated: "updated",
920
+ updateFailed: "update.failed",
921
+ verificationCodeSent: "verification.code.sent",
922
+ updateContactsFailed: "update.contacts.failed",
923
+ verified: "verified",
924
+ unverified: "unverified",
925
+ infoFetched: 'info.fetched',
926
+ infoNotFetched: 'info.not.fetched'
927
+ };
928
+ this.after_init();
929
+ }
930
+
931
+ }
932
+
933
+ class ProductEvents extends BaseEvent {
934
+ constructor() {
935
+ super();
936
+ this.namespace = 'product';
937
+ /**
938
+ * 💡 Notice that you can override any event by creating method, if we have 'this.events={...,codeSent:code-sent,...}',
939
+ * it can be override by creating method like:
940
+ * codeSent(response){
941
+ * //your costume code
942
+ * ....
943
+ * //then call dispatch to emit other listeners
944
+ * return this.dispatch('codeSent', ...arguments);
945
+ * }
946
+ * 💡 namespace "product::" will be added dynamically,
947
+ * all events will be initiated to have three functions, for ex: event "priceUpdated" wil be:
948
+ * 🔸 name: product::price.updated
949
+ * 🔸 fire: Salla.product.event.priceUpdated(...data)
950
+ * 🔸 listen: Salla.product.event.onPriceUpdated((...data) => Salla.log(data))
951
+ * 🔸 fireWithName: Salla.event.emit('product::price.updated',...data)
952
+ * 🔸 listenWithName: salla.event.on('product::price.updated',(...data)=>salla.log(data))
953
+ */
954
+
955
+ this.events = {
956
+ priceUpdated: 'price.updated',
957
+ priceUpdateFailed: 'price.updated.failed',
958
+ availabilitySubscribed: 'availability.subscribed',
959
+ availabilitySubscribeFailed: 'availability.subscribe.failed',
960
+ categoriesFetched: 'categories.fetched',
961
+ categoriesFailed: 'categories.failed',
962
+ searchFailed: 'search.failed',
963
+ searchResults: 'search.results',
964
+ offerExisted: 'offer.existed',
965
+ fetchOffersFailed: 'fetch.offers.failed',
966
+ offersFetched: 'offers.fetched',
967
+ sizeGuideFetched: "size-guide.fetched",
968
+ SizeGuideFetchFailed: "size-guide.failed",
969
+ giftFetched: "gift.fetched",
970
+ giftFetchFailed: "gift.failed",
971
+ addGiftToCartSucceeded: "gift.add-to-cart.succeeded",
972
+ addGiftToCartFailed: "gift.add-to-cart.failed",
973
+ giftImageUploadSucceeded: "gift.image-upload.succeeded",
974
+ giftImageUploadFailed: "gift.image-upload.failed"
975
+ };
976
+ this.after_init();
977
+ }
978
+
979
+ }
980
+
981
+ class DocumentEvents extends BaseEvent {
982
+ constructor() {
983
+ super();
984
+ this.namespace = 'document';
985
+ /**
986
+ * 💡 Notice that you can override any event by creating method, if we have 'this.events={...,codeSent:code-sent,...}',
987
+ * it can be override by creating method like:
988
+ * codeSent(response){
989
+ * //your costume code
990
+ * ....
991
+ * //then call dispatch to emit other listeners
992
+ * return this.dispatch('codeSent', ...arguments);
993
+ * }
994
+ * 💡 namespace "document::" will be added dynamically,
995
+ * all events will be initiated to have three functions, for ex: event "click" wil be:
996
+ * 🔸 name: document::click
997
+ * 🔸 fire: Salla.document.event.click(...data)
998
+ * 🔸 listen: Salla.document.event.onClick((...data) => salla.log(data))
999
+ * 🔸 fireWithName: salla.event.emit('document::click',...data)
1000
+ * 🔸 listenWithName: salla.event.on('document::click',(...data)=>salla.log(data))
1001
+ */
1002
+
1003
+ this.events = {
1004
+ click: "click",
1005
+ change: "change",
1006
+ submit: "submit",
1007
+ keyup: "keyup",
1008
+ leaving: "leaving",
1009
+ request: "request",
1010
+ requestFailed: "request.failed"
1011
+ };
1012
+ this.after_init();
1013
+ }
1014
+ /**
1015
+ * @param {string|function} selector
1016
+ * @param {function|undefined} callback
1017
+ */
1018
+
1019
+
1020
+ onClick(selector, callback) {
1021
+ this.fireCallableFuns('click', selector, callback);
1022
+ }
1023
+ /**
1024
+ * @param {string|function} selector
1025
+ * @param {function|undefined} callback
1026
+ */
1027
+
1028
+
1029
+ onChange(selector, callback) {
1030
+ this.fireCallableFuns('change', selector, callback);
1031
+ }
1032
+ /**
1033
+ * @param {string|function} selector
1034
+ * @param {function|undefined} callback
1035
+ */
1036
+
1037
+
1038
+ onSubmit(selector, callback) {
1039
+ this.fireCallableFuns('submit', selector, callback);
1040
+ }
1041
+ /**
1042
+ * @param {string|function} selector
1043
+ * @param {function|undefined} callback
1044
+ */
1045
+
1046
+
1047
+ onKeyup(selector, callback) {
1048
+ this.fireCallableFuns('keyup', selector, callback);
1049
+ }
1050
+ /**
1051
+ * Alternative For
1052
+ * @example: $(document).on("click","#test-element",function(event){})
1053
+ *
1054
+ * in case selector passed as function, will work as shorthand for
1055
+ * @example: document.addEventListener("clcik", function(event){});
1056
+ *
1057
+ * @param {string} eventName = [click, keyup, submit, change]
1058
+ * @param {string|function} selector
1059
+ * @param {function|undefined} callback
1060
+ */
1061
+
1062
+
1063
+ fireCallableFuns(eventName, selector, callback) {
1064
+ this.on(eventName, event => {
1065
+ if (typeof selector == 'function') {
1066
+ selector(event);
1067
+ return;
1068
+ }
1069
+
1070
+ if (typeof callback == 'function' && event.target.matches(selector)) {
1071
+ callback(event);
1072
+ }
1073
+ });
1074
+ }
1075
+ /**
1076
+ * Helper method to fake firing event on element, for example, we have listener:
1077
+ * @example
1078
+ * element.addEventListener("countrychange",()=>{...});
1079
+ * we want to fire this event programmatically, we can do that by:
1080
+ * @example
1081
+ * salla.document.event.fireEvent(element, 'countrychange', optionalDataPassedThrowEvent)
1082
+ * @param {string|HTMLElement} element
1083
+ * @param {string} eventName
1084
+ * @param {Object|undefined} details
1085
+ * @return {boolean}
1086
+ */
1087
+
1088
+
1089
+ fireEvent(element, eventName, ...details) {
1090
+ return this.fireEventForElements(element, eventName, false, ...details);
1091
+ }
1092
+ /**
1093
+ * Helper method to fake firing event on multi elements, for example, we have listener:
1094
+ * @example
1095
+ * ...('.intl-input').forEach(element=> element.addEventListener("countrychange",()=>{...}));
1096
+ * we want to fire this event programmatically, we can do that by:
1097
+ * @example
1098
+ * salla.document.event.fireEventForAll('.intl-input', 'countrychange', optionalDataPassedThrowEvent)
1099
+ * @param {string} elementSelector
1100
+ * @param {string} eventName
1101
+ * @param {Object|undefined} details
1102
+ * @return {boolean}
1103
+ */
1104
+
1105
+
1106
+ fireEventForAll(elementSelector, eventName, ...details) {
1107
+ return this.fireEventForElements(elementSelector, eventName, true, ...details);
1108
+ }
1109
+ /**
1110
+ *
1111
+ * @param {string|HTMLElement} element
1112
+ * @param {string} eventName
1113
+ * @param {boolean} isAllSelector
1114
+ * @param {Object|undefined} details
1115
+ * @return {boolean}
1116
+ */
1117
+
1118
+
1119
+ fireEventForElements(element, eventName, isAllSelector, ...details) {
1120
+ //if it's srting, means it's selector, so try to get real element from the selector.
1121
+ if (typeof element == 'string') {
1122
+ if (isAllSelector) {
1123
+ return document.querySelectorAll(element).forEach(element_ => {
1124
+ return this.fireEventForElements(element_, eventName, false, ...details);
1125
+ });
1126
+ }
1127
+
1128
+ element = document.querySelector(element);
1129
+ }
1130
+
1131
+ if (!element) {
1132
+ salla.log('Failed To get element to fire event: ' + eventName);
1133
+ return;
1134
+ }
1135
+
1136
+ const event = new CustomEvent(eventName, ...details);
1137
+ return element.dispatchEvent(event);
1138
+ }
1139
+
1140
+ }
1141
+
1142
+ class WishlistEvents extends BaseEvent {
1143
+ constructor() {
1144
+ super();
1145
+ this.namespace = 'wishlist';
1146
+ /**
1147
+ * 💡 Notice that you can override any event by creating method, if we have 'this.events={...,codeSent:code-sent,...}',
1148
+ * it can be override by creating method like:
1149
+ * codeSent(response){
1150
+ * //your costume code
1151
+ * ....
1152
+ * //then call dispatch to emit other listeners
1153
+ * return this.dispatch('codeSent', ...arguments);
1154
+ * }
1155
+ * 💡 namespace "wishlist::" will be added dynamically,
1156
+ * all events will be initiated to have three functions, for ex: event "added" wil be:
1157
+ * 🔸 name: wishlist::added
1158
+ * 🔸 fire: Salla.wishlist.event.added(...data)
1159
+ * 🔸 listen: Salla.wishlist.event.onAdded((...data) => Salla.log(data))
1160
+ * 🔸 fireWithName: Salla.event.emit('wishlist::added',...data)
1161
+ * 🔸 listenWithName: Salla.event.on('wishlist::added',(...data)=>Salla.log(data))
1162
+ */
1163
+
1164
+ this.events = {
1165
+ added: "added",
1166
+ removed: "removed",
1167
+ additionFailed: "addition.failed",
1168
+ removingFailed: "removing.failed"
1169
+ };
1170
+ this.after_init();
1171
+ }
1172
+
1173
+ }
1174
+
1175
+ class CurrencyEvents extends BaseEvent {
1176
+ constructor() {
1177
+ super();
1178
+ this.namespace = 'currency';
1179
+ /**
1180
+ * 💡 Notice that you can override any event by creating method, if we have 'this.events={...,codeSent:code-sent,...}',
1181
+ * it can be override by creating method like:
1182
+ * codeSent(response){
1183
+ * //your costume code
1184
+ * ....
1185
+ * //then call dispatch to emit other listeners
1186
+ * return this.dispatch('codeSent', ...arguments);
1187
+ * }
1188
+ * 💡 namespace 'currency::' will be added dynamically,
1189
+ * all events will be initiated to have three functions, for ex: event "changed" wil be:
1190
+ * 🔸 name: currency::changed
1191
+ * 🔸 fire: Salla.currency.event.changed(...data)
1192
+ * 🔸 listen: Salla.currency.event.onChanged((...data) => Salla.log(data))
1193
+ * 🔸 fireWithName: Salla.event.emit('currency::changed',...data)
1194
+ * 🔸 listenWithName: Salla.event.on('currency::changed',(...data)=>Salla.log(data))
1195
+ */
1196
+
1197
+ this.events = {
1198
+ changed: 'changed',
1199
+ failed: 'failed',
1200
+ fetched: 'fetched',
1201
+ failedToFetch: 'failed.to.fetch'
1202
+ };
1203
+ this.after_init();
1204
+ }
1205
+
1206
+ }
1207
+
1208
+ class InfiniteScrollEvents extends BaseEvent {
1209
+ constructor() {
1210
+ super();
1211
+ this.namespace = 'infiniteScroll';
1212
+ /**
1213
+ * 💡 Notice that you can override any event by creating method, if we have 'this.events={...,codeSent:code-sent,...}',
1214
+ * it can be override by creating method like:
1215
+ * codeSent(response){
1216
+ * //your costume code
1217
+ * ....
1218
+ * //then call dispatch to emit other listeners
1219
+ * return this.dispatch('codeSent', ...arguments);
1220
+ * }
1221
+ * 💡 namespace "infiniteScroll::" will be added dynamically,
1222
+ * so last result will be something like: "infiniteScroll::loaded"
1223
+ * also you can check:
1224
+ * @see https://infinite-scroll.com/events.html#infinite-scroll-events
1225
+ */
1226
+
1227
+ this.events = {
1228
+ // also events can be accessed directly via:
1229
+ // Salla.infiniteScroll.initiate(...).on('load', func);
1230
+ scrollThreshold: 'scroll.threshold',
1231
+ request: 'request',
1232
+ load: 'load',
1233
+ append: 'append',
1234
+ error: 'error',
1235
+ last: 'last',
1236
+ history: 'history'
1237
+ };
1238
+ this.after_init();
1239
+ }
1240
+
1241
+ }
1242
+
1243
+ Salla.event.auth = new AuthEvents();
1244
+ Salla.event.cart = new CartEvents();
1245
+ Salla.event.order = new OrderEvents();
1246
+ Salla.event.scope = new ScopeEvents();
1247
+ Salla.event.rating = new RatingEvents();
1248
+ Salla.event.comment = new CommentEvents();
1249
+ Salla.event.loyalty = new LoyaltyEvents();
1250
+ Salla.event.product = new ProductEvents();
1251
+ Salla.event.profile = new ProfileEvents();
1252
+ Salla.event.currency = new CurrencyEvents();
1253
+ Salla.event.document = new DocumentEvents();
1254
+ Salla.event.wishlist = new WishlistEvents();
1255
+ Salla.event.infiniteScroll = new InfiniteScrollEvents();
1256
+ /**
1257
+ * handy way to be able to create function that will be called after salla initiated;
1258
+ * we can add 'sallaInitiated()' method to any child in salla, or one of them chidren
1259
+ * for example:
1260
+ * Salla.notify.sallaInitiated()
1261
+ * Salla.api.cart.sallaInitiated()
1262
+ */
1263
+
1264
+ Salla.event.on('twilight::initiated', () => {
1265
+ Object.keys(Salla).forEach(sallaSection => {
1266
+ sallaSection = Salla[sallaSection];
1267
+
1268
+ if (typeof sallaSection != 'object') {
1269
+ return;
1270
+ } //search main salla elements like (Salla.api, salla.notify, salla.event)
1271
+
1272
+
1273
+ callSallaInitiate(sallaSection); //search children of salla elements like (salla.api.cart, salla.event.auth ..etc)
1274
+
1275
+ Object.keys(sallaSection).forEach(innerSallaSection => {
1276
+ callSallaInitiate(sallaSection[innerSallaSection]);
1277
+ });
1278
+ });
1279
+ });
1280
+ /**
1281
+ * make sure not to re call sallaInitiated method more than once for same class;
1282
+ * like:
1283
+ * salla.cart.event
1284
+ * salla.event.cart
1285
+ * because they are the same.
1286
+ * @param sallaSection
1287
+ */
1288
+
1289
+ function callSallaInitiate(sallaSection) {
1290
+ if (!sallaSection || !sallaSection.sallaInitiated || sallaSection.initiated) {
1291
+ return;
1292
+ }
1293
+
1294
+ sallaSection.sallaInitiated();
1295
+ sallaSection.initiated = true;
1296
+ }
1297
+
1298
+ /**
1299
+ * @property {TwilightConfig|any} properties_
1300
+ * @property {Object|{[string]:Currency,SAR:Currency}} currencies_
1301
+ * @property {boolean} triedToGetCurrencies_
1302
+ * @property {Language[]} languages_
1303
+ * @property {boolean} triedToGetLanguages_
1304
+ */
1305
+ Salla.config.default_properties = {
1306
+ debug: typeof process !== 'undefined' && process.env.NODE_ENV === 'development',
1307
+ token: null,
1308
+ fastRequests: true,
1309
+ canLeave: true,
1310
+ store: {
1311
+ api: "https://api.salla.dev/store/v1/"
1312
+ },
1313
+ currencies: {
1314
+ SAR: {
1315
+ "code": "SAR",
1316
+ "name": "ريال سعودي",
1317
+ "symbol": "ر.س",
1318
+ "amount": 1,
1319
+ "country_code": "sa"
1320
+ }
1321
+ }
1322
+ };
1323
+ Salla.config.merge(Salla.config.default_properties);
1324
+ Salla.config.triedToGetCurrencies_ = false;
1325
+ Salla.config.triedToGetLanguages_ = false;
1326
+
1327
+ Salla.config.isUser = () => {
1328
+ return Salla.config.get('user.type') === 'user';
1329
+ };
1330
+
1331
+ Salla.config.isGuest = () => {
1332
+ return !Salla.config.isUser();
1333
+ };
1334
+ /**
1335
+ * @return {Promise<Language[]|*>}
1336
+ */
1337
+
1338
+
1339
+ Salla.config.languages = async () => {
1340
+ if (Salla.config.triedToGetLanguages_) {
1341
+ return Salla.config.get('languages');
1342
+ }
1343
+
1344
+ Salla.config.triedToGetLanguages_ = true;
1345
+ let isFirst = true;
1346
+ let languages_ = [];
1347
+ (await salla.document.api.request('languages', null, 'get'))?.data?.map(lang => {
1348
+ if (isFirst) {
1349
+ // clear old currencies
1350
+ languages_ = [];
1351
+ isFirst = false;
1352
+ }
1353
+
1354
+ lang.code = lang.code || lang.iso_code;
1355
+ lang.url = salla.url.get(lang.code);
1356
+ lang.is_rtl = lang.is_rtl || lang.rtl;
1357
+ languages_.push(lang);
1358
+ });
1359
+ Salla.config.set('languages', languages_);
1360
+ return languages_;
1361
+ };
1362
+
1363
+ Salla.config.currencies = async () => {
1364
+ if (Salla.config.triedToGetCurrencies_) {
1365
+ return Salla.config.get('currencies');
1366
+ }
1367
+
1368
+ Salla.config.triedToGetCurrencies_ = true;
1369
+ let isFirst = true;
1370
+ let currencies_ = {};
1371
+ (await salla.currency.api.list())?.data?.map(currency => {
1372
+ if (isFirst) {
1373
+ //clear old currencies
1374
+ currencies_ = {};
1375
+ isFirst = false;
1376
+ }
1377
+
1378
+ currency.country_code = currency.code.substr(0, 2).toLowerCase();
1379
+ currencies_[currency.code] = currency;
1380
+ });
1381
+ Salla.config.set('currencies', currencies_);
1382
+ return currencies_;
1383
+ };
1384
+ /**
1385
+ * @param {string|undefined} isoCode
1386
+ * @return {Currency}
1387
+ */
1388
+
1389
+
1390
+ Salla.config.currency = isoCode => {
1391
+ isoCode = isoCode || Salla.config.get('user.currency_code');
1392
+ return Salla.config.get('currencies.' + isoCode) || Object.values(Salla.config.get('currencies'))[0];
1393
+ };
1394
+
1395
+ class InfiniteScroll extends Module {
1396
+ constructor(api, event) {
1397
+ super(api, event);
1398
+ this.options = {
1399
+ path: '.infinite-scroll-btn',
1400
+ history: 'push',
1401
+ status: ".infinite-scroll-status",
1402
+ append: ".list-block"
1403
+ };
1404
+ this.fetchOptions = {
1405
+ headers: {
1406
+ 'S-INFINITE-SCROLL': true // salla is not ready yet
1407
+ // 'Accept-Language': Salla.config.get('user.language_code','ar'),
1408
+
1409
+ }
1410
+ };
1411
+ this.instances = [];
1412
+ }
1413
+ /**
1414
+ * product-block
1415
+ * @param {string|Element} selector
1416
+ * @param {Object|string} [append=".list-block"] - if it's object will consider it as options
1417
+ * @param {Object|string} [customOptions=this.options]
1418
+ * @return {undefined|TheInfiniteScroll}
1419
+ */
1420
+
1421
+
1422
+ initiate(selector, append, customOptions) {
1423
+ customOptions = this.getCustomOptions(append, customOptions);
1424
+ let container = typeof selector != 'string' ? selector : document.querySelector(selector),
1425
+ path = customOptions.path;
1426
+
1427
+ if (!container || !path || !(typeof path != 'string' || document.querySelector(path))) {
1428
+ Salla.logger.warn(container ? 'Path Option (a link that has next page link) Not Existed!' : 'Container For InfiniteScroll not Existed!');
1429
+ return undefined;
1430
+ }
1431
+
1432
+ let infScroll = new TheInfiniteScroll(container, customOptions); // also events can be accessed directly via:
1433
+ // Salla.infiniteScroll.initiate(...).on('load', func);
1434
+
1435
+ infScroll.on('scrollThreshold', Salla.infiniteScroll.event.scrollThreshold);
1436
+ infScroll.on('request', Salla.infiniteScroll.event.request);
1437
+ infScroll.on('load', Salla.infiniteScroll.event.load);
1438
+ infScroll.on('append', Salla.infiniteScroll.event.append);
1439
+ infScroll.on('error', Salla.infiniteScroll.event.error);
1440
+ infScroll.on('last', Salla.infiniteScroll.event.last);
1441
+ infScroll.on('history', Salla.infiniteScroll.event.history);
1442
+ this.instances.push(infScroll);
1443
+ return infScroll;
1444
+ }
1445
+
1446
+ getCustomOptions(append, customOptions) {
1447
+ // if append is passed as object means it's customOptions
1448
+ customOptions = (typeof append == 'object' ? append : false) || customOptions || this.options;
1449
+ customOptions.fetchOptions = this.fetchOptions; // load more button
1450
+ // if customOptions passed without path.
1451
+
1452
+ customOptions.path = customOptions.path || this.options.path;
1453
+ customOptions.button = customOptions.button || customOptions.path;
1454
+ customOptions.status = customOptions.status || this.options.status; // if customOptions passed without history.
1455
+
1456
+ if (!customOptions.hasOwnProperty('history')) {
1457
+ customOptions.history = this.options.history;
1458
+ }
1459
+
1460
+ customOptions.nextPage = customOptions.nextPage || customOptions.next_page;
1461
+ customOptions.append = (typeof append == 'string' ? append : false) || customOptions.append || this.options.append;
1462
+ return customOptions;
1463
+ }
1464
+
1465
+ }
1466
+
1467
+ class AppHelpers {
1468
+ /**
1469
+ * @param {string} selector
1470
+ * @param {array<string>} classes1
1471
+ * @param {array<string>} classes2
1472
+ * @param callback
1473
+ */
1474
+ toggleClassIf(selector, classes1, classes2, callback) {
1475
+ document.querySelectorAll(selector).forEach(element => this.toggleElementClassIf(element, classes1, classes2, callback));
1476
+ return this;
1477
+ }
1478
+
1479
+ toggleElementClassIf(element, classes1, classes2, callback) {
1480
+ classes1 = Array.isArray(classes1) ? classes1 : classes1.split(' ');
1481
+ classes2 = Array.isArray(classes2) ? classes2 : classes2.split(' ');
1482
+ let isClasses1 = callback(element);
1483
+ element?.classList.remove(...(isClasses1 ? classes2 : classes1));
1484
+ element?.classList.add(...(isClasses1 ? classes1 : classes2));
1485
+ return this;
1486
+ }
1487
+ /**
1488
+ * @param {string} email
1489
+ * @return {boolean}
1490
+ */
1491
+
1492
+
1493
+ isValidEmail(email) {
1494
+ const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
1495
+ return re.test(String(email).toLowerCase());
1496
+ }
1497
+ /**
1498
+ * @param {string|HTMLElement} selector
1499
+ * @return {null|HTMLElement}
1500
+ */
1501
+
1502
+
1503
+ element(selector) {
1504
+ if (typeof selector == 'object') {
1505
+ return selector;
1506
+ }
1507
+
1508
+ return document.querySelector(selector);
1509
+ }
1510
+ /**
1511
+ * @param {string} name
1512
+ * @param {string} selector
1513
+ * @return {Helpers}
1514
+ */
1515
+
1516
+
1517
+ watchElement(name, selector) {
1518
+ this[name] = this.element(selector);
1519
+ return this;
1520
+ }
1521
+ /**
1522
+ * @param {Object.<string, string>} elements
1523
+ */
1524
+
1525
+
1526
+ watchElements(elements) {
1527
+ Object.entries(elements).forEach(element => this.watchElement(element[0], element[1]));
1528
+ return this;
1529
+ }
1530
+ /**
1531
+ * @param {string} action
1532
+ * @param {string|HTMLElement} element
1533
+ * @param {function} callback
1534
+ * @param {object|undefined} options
1535
+ * @return {AppHelpers}
1536
+ */
1537
+
1538
+
1539
+ on(action, element, callback, options = {}) {
1540
+ if (typeof element == 'object') {
1541
+ this.element(element).addEventListener(action, callback, options);
1542
+ return this;
1543
+ } //if it's selector loop through all of the elements
1544
+
1545
+
1546
+ document.querySelectorAll(element).forEach(el => el.addEventListener(action, callback, options));
1547
+ return this;
1548
+ }
1549
+ /**
1550
+ * @param {string|HTMLElement} element
1551
+ * @param {function} callback
1552
+ * @return {AppHelpers}
1553
+ */
1554
+
1555
+
1556
+ onClick(element, callback) {
1557
+ return this.on('click', element, callback);
1558
+ }
1559
+ /**
1560
+ * @param {string|HTMLElement} element
1561
+ * @param {function} callback
1562
+ * @return {AppHelpers}
1563
+ */
1564
+
1565
+
1566
+ onKeyUp(element, callback) {
1567
+ return this.on('keyup', element, callback);
1568
+ }
1569
+ /**
1570
+ * @param {string|HTMLElement} element
1571
+ * @param {function} callback
1572
+ * @return {AppHelpers}
1573
+ */
1574
+
1575
+
1576
+ onEnter(element, callback) {
1577
+ this.onKeyUp(element, event => event.keyCode === 13 && callback(event));
1578
+ return this;
1579
+ }
1580
+ /**
1581
+ * @param {string|HTMLElement} element
1582
+ * @param {function} callback
1583
+ * @return {AppHelpers}
1584
+ */
1585
+
1586
+
1587
+ all(element, callback) {
1588
+ document.querySelectorAll(element).forEach(callback);
1589
+ return this;
1590
+ }
1591
+ /**
1592
+ * @param {string|HTMLElement} element
1593
+ * @return {AppHelpers}
1594
+ */
1595
+
1596
+
1597
+ hideElement(element) {
1598
+ this.element(element).style.display = 'none';
1599
+ return this;
1600
+ }
1601
+ /**
1602
+ * @param {string|HTMLElement} element
1603
+ * @return {AppHelpers}
1604
+ */
1605
+
1606
+
1607
+ showElement(element, display = 'block') {
1608
+ this.element(element).style.display = display;
1609
+ return this;
1610
+ }
1611
+ /**
1612
+ * 💡 you can pass multi classes: this.removeClass(element, 'class_1', 'class_2', ...)
1613
+ * @param {string|HTMLElement} element
1614
+ * @param {string} className
1615
+ * @return {AppHelpers}
1616
+ */
1617
+
1618
+
1619
+ removeClass(element, className) {
1620
+ this.element(element).classList.remove(...Array.from(arguments).slice(1));
1621
+ return this;
1622
+ }
1623
+ /**
1624
+ * 💡 you can pass multi classes: this.addClass(element, 'class_1', 'class_2', ...)
1625
+ * @param {string|HTMLElement} element
1626
+ * @param {string} className
1627
+ * @return {AppHelpers}
1628
+ */
1629
+
1630
+
1631
+ addClass(element, className) {
1632
+ this.element(element).classList.add(...Array.from(arguments).slice(1));
1633
+ return this;
1634
+ }
1635
+ /**
1636
+ * @param {function} fn
1637
+ * @param data
1638
+ * @return {(function(...[*]): Promise<unknown>)|*}
1639
+ */
1640
+
1641
+
1642
+ debounce(fn, ...data) {
1643
+ if (!this.debounce_) {
1644
+ this.debounce_ = Salla.helpers.debounce((callback, ...innerData) => callback(...innerData), 500);
1645
+ }
1646
+
1647
+ return this.debounce_(fn, ...data);
1648
+ }
1649
+
1650
+ }
1651
+
1652
+ class Form {
1653
+ /**
1654
+ * Submit form via API actions.
1655
+ *
1656
+ * @param {ApiActionName|string} actionNameOrUrl
1657
+ * @param {null|Event|Object} eventOrData
1658
+ */
1659
+ async submit(actionNameOrUrl, eventOrData = null) {
1660
+ let data = eventOrData; // its submit event, so we'll have a submit button
1661
+
1662
+ if (eventOrData?.constructor?.name === 'SubmitEvent') {
1663
+ eventOrData.preventDefault();
1664
+
1665
+ if (eventOrData.target?.tagName !== 'FORM') {
1666
+ Salla.logger.warn('Failed find the target element for submit action. make sure you submit a form element');
1667
+ return new Promise(() => {
1668
+ throw 'Failed find the target element for submit action. make sure you submit a form element';
1669
+ });
1670
+ } // todo:: use form.checkValidity() && form.reportValidity()
1671
+ // we need to find the submit button to have the loading effect
1672
+
1673
+
1674
+ if (eventOrData?.submitter?.parentElement?.tagName === 'SALLA-BUTTON') {
1675
+ eventOrData.submitter.parentElement.load();
1676
+ }
1677
+
1678
+ data = eventOrData.target.getElementSallaData();
1679
+ salla.log('Data from element', data);
1680
+ } // if the action is url, lets do a normal request
1681
+
1682
+
1683
+ if (/^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$/gm.test(actionNameOrUrl)) {
1684
+ return salla.api.normalRequest(actionNameOrUrl, data, 'post').finally(() => {
1685
+ if (loaderSupported) {
1686
+ eventOrData?.submitter?.parentElement.stop();
1687
+ }
1688
+ });
1689
+ } // todo :: check if the actionName is valid salla action
1690
+ // its salla action
1691
+
1692
+
1693
+ let className = actionNameOrUrl.split('.');
1694
+ let functionName = className.splice(-1);
1695
+ return await salla.call(className.join('.'))[functionName](data).finally(() => eventOrData?.submitter?.parentElement?.stop()).catch(error => {
1696
+ salla.logger.warn(error);
1697
+ throw error;
1698
+ });
1699
+ }
1700
+ /**
1701
+ * Submit form via API actions.
1702
+ *
1703
+ * @param {ApiActionName|string} actionNameOrUrl
1704
+ * @param {null|Event|Object} eventOrData
1705
+ * @return {boolean}
1706
+ */
1707
+
1708
+
1709
+ onSubmit(actionNameOrUrl, eventOrData) {
1710
+ salla.form.submit(actionNameOrUrl, eventOrData);
1711
+ return false;
1712
+ }
1713
+ /**
1714
+ * Submit form via API actions.
1715
+ *
1716
+ * @param {ApiActionName|string} actionName
1717
+ * @param {null|Event|Object} event
1718
+ * @return {boolean}
1719
+ */
1720
+
1721
+
1722
+ onChange(actionName, event) {
1723
+ if (!event?.currentTarget) {
1724
+ salla.logger.warn(`Trying to trigger '${actionName}' without event!`);
1725
+ return false;
1726
+ }
1727
+
1728
+ if (event?.currentTarget?.tagName === 'FORM' && !event.currentTarget.checkValidity()) {
1729
+ salla.logger.warn(`Trying to trigger '${actionName}' without filling required fields!`);
1730
+ return false;
1731
+ }
1732
+
1733
+ salla.form.submit(actionName, event.currentTarget.getElementSallaData());
1734
+ return true;
1735
+ }
1736
+ /**
1737
+ *
1738
+ * @param {object|FormData} formData
1739
+ * @param {Array} possibleNames
1740
+ * @param {boolean|undefined} objectable - can wanted value be object? default No.
1741
+ * @return {undefined|string|*}
1742
+ */
1743
+
1744
+
1745
+ getPossibleValue(formData, possibleNames, objectable = false) {
1746
+ if (!formData) {
1747
+ return undefined;
1748
+ }
1749
+
1750
+ if (typeof formData !== 'object') {
1751
+ return formData;
1752
+ }
1753
+
1754
+ let wantedValue;
1755
+
1756
+ for (let i = 0; i < possibleNames.length; i++) {
1757
+ if (wantedValue = formData[possibleNames[i]]) {
1758
+ break;
1759
+ }
1760
+
1761
+ if (typeof FormData !== 'undefined' && formData instanceof FormData && (wantedValue = formData.get(possibleNames[i]))) {
1762
+ break;
1763
+ }
1764
+ }
1765
+
1766
+ wantedValue = wantedValue || formData;
1767
+ return typeof wantedValue == 'object' && !objectable ? undefined : wantedValue;
1768
+ }
1769
+
1770
+ }
1771
+
1772
+ class SallaLang extends Lang {
1773
+ constructor(options) {
1774
+ options = options || {}; // TODO:: allow the developer to pass initiate values
1775
+ // to avoid this message even when he will add translations later.
1776
+
1777
+ options.messages = options.messages || window.translations;
1778
+ options.locale = options.locale || (window.locale || navigator.language || navigator.userLanguage || 'ar').split('-')[0];
1779
+ options.fallback = options.fallback || options.locale;
1780
+ super(options);
1781
+ this.translationsLoaded = false; // handy, when we are trying to run onLoaded(), after is already loaded.
1782
+ }
1783
+
1784
+ onLoaded(callback) {
1785
+ if (this.translationsLoaded) {
1786
+ return callback();
1787
+ } // todo:: in case there is need support Salla.event.on() for synced translations
1788
+
1789
+
1790
+ Salla.event.once('languages::translations.loaded', callback);
1791
+ }
1792
+
1793
+ loadStoreTranslations() {
1794
+ if (this.messages) {
1795
+ // because load the messages is faster than load js url
1796
+ // we want to delay the event of lang until all dom render
1797
+ // to make sure the web component will receive the event when fired
1798
+ window.addEventListener('load', event => {
1799
+ salla.event.dispatch('languages::translations.loaded');
1800
+ salla.logger.info('The messages of transactions is already loaded');
1801
+ });
1802
+ return;
1803
+ }
1804
+
1805
+ let url = salla.url.get('');
1806
+
1807
+ if (!url) {
1808
+ this.loadScript('https://cdn.salla.network/js/translations.js', false);
1809
+ return;
1810
+ }
1811
+
1812
+ let hash = salla.config.get('theme.translations_hash', salla.config.get('store.id', 'twilight')); // load default translations for salla, or using global translations file
1813
+
1814
+ this.loadScript(salla.url.get(`languages/assets/${hash}.js`));
1815
+ }
1816
+
1817
+ setMessages(translations) {
1818
+ super.setMessages(translations);
1819
+ salla.event.dispatch('languages::translations.loaded');
1820
+ this.translationsLoaded = true;
1821
+ }
1822
+
1823
+ loadScript(url, withFallback = true) {
1824
+ let script = document.createElement('script');
1825
+ script.src = url;
1826
+
1827
+ script.onload = () => {
1828
+ if (window.translations) {
1829
+ return this.setMessages(window.translations);
1830
+ }
1831
+
1832
+ script.onerror();
1833
+ };
1834
+
1835
+ script.onerror = () => {
1836
+ if (withFallback) {
1837
+ salla.logger.warn("Failed to load Translations for store, lets try load it from CDN");
1838
+ return this.loadScript('https://cdn.salla.network/js/translations.js', false);
1839
+ }
1840
+
1841
+ salla.logger.error("Failed to load Translations, check your network logs for more details" + "\nor: salla.lang.setMessages({....}), see https://docs.salla.dev for more information's.");
1842
+ };
1843
+
1844
+ document.head.appendChild(script);
1845
+ }
1846
+
1847
+ get(key, replacements, locale) {
1848
+ //if translations in window object, means it's using lang.js formatting, so lets ignore first level key.
1849
+ if (window.translations) {
1850
+ key = 'trans.' + key;
1851
+ }
1852
+
1853
+ return super.get(key, replacements, locale);
1854
+ }
1855
+
1856
+ set(key, message) {
1857
+ salla.helpers.setNested(this.messages[this.getLocale() + '.trans'], key, message);
1858
+ return this;
1859
+ }
1860
+
1861
+ }
1862
+
1863
+ /**
1864
+ * @property {string} className
1865
+ * @property {Object.<string, string>} endpoints - keep urls of endpoints
1866
+ * @property {Object.<string, string>} endpointsMethods - {deleteItem:"delete"}
1867
+ * @property {{request: Function|undefined, time: number, enabled: boolean, exclude: Array}} debounce - keep debounce function for this api.
1868
+ *
1869
+ */
1870
+
1871
+ /** @typedef {Object} DataFromHook */
1872
+ class BaseApi {
1873
+ constructor() {
1874
+ /**
1875
+ * this class is the base class for api classes like (cart, auth)
1876
+ * we will initiate it from Main Salla Api/axios,
1877
+ * So we will Pass it (Main Salla Api) as an alias to be able to call it's methods from children apis,
1878
+ *
1879
+ * 💡 All axios methods will be available via it, like "this.parent.post()"
1880
+ */
1881
+ this.endpoints = {};
1882
+ this.webEndpoints = []; //all endpoints should be api, if there is an exception, put endpoint here.
1883
+
1884
+ this.namespace = 'BaseApi';
1885
+ /**
1886
+ * @see Object.prototype.getElementSallaData
1887
+ * @see Object.prototype.canEventFireHook
1888
+ * @type {Object.<string, string>}
1889
+ */
1890
+
1891
+ this.endpointsMethods = {}; //"post" is the default, can use other method for endpoint here.
1892
+
1893
+ this.endpointsHeaders = {}; //pass specific headers for specific endpoints
1894
+
1895
+ let results = /function (.{1,})\(/.exec(this.constructor.toString());
1896
+ this.className = (results && results.length > 1 ? results[1] : "").toLowerCase(); // we can make api debouncable or not,
1897
+ // or change debounce time by overriding this object with wanted values.
1898
+
1899
+ this.debounce = {
1900
+ request: undefined,
1901
+ time: 300,
1902
+ enabled: true,
1903
+ exclude: []
1904
+ };
1905
+ }
1906
+ /**
1907
+ * should be called from child class, after preparing data, to initiate things.
1908
+ */
1909
+
1910
+
1911
+ after_init() {}
1912
+ /**
1913
+ * @param {string[]|string} endPoint - pass [url|endpoint|array(url|endpoint, urlParam)]
1914
+ * @param {Object|undefined} formData
1915
+ * @param {string|null} method
1916
+ * @return {Promise}
1917
+ */
1918
+
1919
+
1920
+ normalRequest(endPoint, formData = undefined, method = null) {
1921
+ let isArray = Array.isArray(endPoint); //if endPoint is an array pass it using (spread operator "three dots"), otherwise pass normal string
1922
+
1923
+ let endPointUrl = isArray ? this.getUrl(...endPoint) : this.getUrl(endPoint); //if there is need to use different method than post, otherwise use post;
1924
+
1925
+ endPoint = isArray ? endPoint[0] : endPoint;
1926
+ method = method || this.endpointsMethods[endPoint] || 'post';
1927
+ let headers = this.endpointsHeaders[endPoint];
1928
+
1929
+ if (method === 'get' && formData instanceof FormData) {
1930
+ let parameters = {};
1931
+ Array.from(formData.entries()).forEach(function (data) {
1932
+ parameters[data[0]] = data[1];
1933
+ });
1934
+ formData = {
1935
+ params: parameters
1936
+ };
1937
+ } // else if (method === 'get' && formData && !formData.params) {
1938
+ // formData.params = formData;
1939
+ // }
1940
+
1941
+
1942
+ if (headers && method === 'get') {
1943
+ formData = formData ? Object.assign(formData, headers) : headers;
1944
+ } // use api baseurl, when it's api
1945
+
1946
+
1947
+ if (this.webEndpoints.includes(endPoint)) {
1948
+ endPointUrl = salla.url.get(endPointUrl);
1949
+ } else if (endPointUrl.substring(0, 4) !== 'http') {
1950
+ endPointUrl = salla.url.api(endPointUrl);
1951
+ }
1952
+
1953
+ return Salla.api.request(endPointUrl, formData, method, {
1954
+ headers: headers
1955
+ });
1956
+ }
1957
+ /**
1958
+ * @param {string|array} endPoint - pass [url|endpoint|array(url|endpoint, urlParam)]
1959
+ * @param {Object|undefined} formData
1960
+ * @param {string|null} method
1961
+ * @return {Promise}
1962
+ */
1963
+
1964
+
1965
+ request(endPoint, formData = undefined, method = null) {
1966
+ if (!salla.api.isFastRequestsAllowed() && this.debounce.enabled) {
1967
+ if (!this.debounce.request) {
1968
+ this.debounce.request = salla.helpers.debounce(this.normalRequest.bind(this), this.debounce.time);
1969
+ }
1970
+
1971
+ return this.debounce.request(endPoint, formData, method);
1972
+ }
1973
+
1974
+ return this.normalRequest(endPoint, formData, method);
1975
+ }
1976
+ /**
1977
+ * pass all endpoints from here to be free changing endpoints without breakchange
1978
+ * @param {...string} endPoint
1979
+ * @return {string|undefined}
1980
+ */
1981
+
1982
+
1983
+ getUrl(endPoint) {
1984
+ let endPointUrl = this.endpoints[endPoint] || endPoint;
1985
+ /**
1986
+ * in case there is parameters need to be replaced in url, user can pass them, we will handle replacement
1987
+ * @example
1988
+ * 'test/jamal/{id1}/{another}/edit'
1989
+ * we will run throw passed arguments after argument one, then replace one by one.
1990
+ * final result will be something like
1991
+ * 'test/jamal/100/56/edit'
1992
+ */
1993
+
1994
+ const regex = /{[^{}]+}/i;
1995
+
1996
+ for (let i = 1; i < arguments.length; i++) {
1997
+ endPointUrl = endPointUrl.replace(regex,
1998
+ /*urlParReplacement*/
1999
+ arguments[i]);
2000
+ }
2001
+
2002
+ return endPointUrl;
2003
+ }
2004
+ /**
2005
+ * @return {BaseEvent|AuthEvent|CartEvent}
2006
+ */
2007
+
2008
+
2009
+ event() {
2010
+ return salla.event[this.className];
2011
+ }
2012
+
2013
+ }
2014
+
2015
+ class CartApi extends BaseApi {
2016
+ constructor() {
2017
+ super();
2018
+ this.namespace = 'cart';
2019
+ this.endpoints = {
2020
+ latest: 'cart/latest',
2021
+ details: 'cart/{cart}',
2022
+ // summary : 'cart/{cart}/summary',
2023
+ quickAdd: 'cart/{cart}/item/{product}/quick-add',
2024
+ addItem: 'cart/{cart}/item/{product}/add',
2025
+ deleteItem: 'cart/{cart}/item/{item}',
2026
+ updateItem: 'cart/{cart}/item/{item}',
2027
+ deleteImage: 'cart/{cart}/image/{image}',
2028
+ uploadImage: 'cart/{cart}/image',
2029
+ status: 'cart/{cart}/status',
2030
+ // submit : 'cart/{cart}',
2031
+ // uploadOptionImage: 'cart/{cart}/option-image',
2032
+ addCoupon: 'cart/{id}/coupon',
2033
+ deleteCoupon: 'cart/{id}/coupon'
2034
+ };
2035
+ this.endpointsMethods = {
2036
+ latest: 'get',
2037
+ details: 'get',
2038
+ // summary : 'get',
2039
+ status: 'get',
2040
+ updateItem: 'post',
2041
+ deleteItem: 'delete',
2042
+ deleteImage: 'delete',
2043
+ deleteCoupon: 'put'
2044
+ };
2045
+ this.webEndpoints = ['latest' //it's important to be web for getting cart by session when user not logged in
2046
+ ];
2047
+ this.after_init();
2048
+ }
2049
+
2050
+ async getCurrentCartId() {
2051
+ // guest
2052
+ // -- generate a cart and store the id in the local
2053
+ // -- all api call via cart id
2054
+ // -- once login -> exchange the cart id to the user
2055
+ // -- make sure in the middleware of cart api the cart id is not pending or throw error to generate a new one
2056
+ // -- in check status handling update local storage if the cart is not avillable
2057
+ // user
2058
+ // -- generte a cart via api and store the id in local storage
2059
+ // -- all api call via cart id
2060
+ // -- make sure in the middleware of cart api the cart id is not pending or throw error to generate a new one
2061
+ // -- check the status of the cart before the submit
2062
+ // -- in check status handling update local storage if the cart is not avillable
2063
+ return salla.storage.get('cart.id') || (await this.latest()).data.cart.id;
2064
+ }
2065
+
2066
+ getUploadImageEndpoint(cart_id) {
2067
+ return salla.url.api(this.getUrl('uploadImage', cart_id || salla.storage.get('cart.id')));
2068
+ }
2069
+
2070
+ latest() {
2071
+ return this.request('latest', {
2072
+ params: {
2073
+ source: ''
2074
+ }
2075
+ }).then(res => {
2076
+ salla.storage.set('cart', res.data.cart);
2077
+ salla.event.cart.latestFetched(res);
2078
+ return res;
2079
+ }).catch(error => {
2080
+ salla.storage.set('cart', '');
2081
+ salla.event.cart.latestFailed(error);
2082
+ throw error;
2083
+ });
2084
+ }
2085
+
2086
+ async details() {
2087
+ let cartId = await this.getCurrentCartId();
2088
+ return this.request(['details', cartId]).then(function (response) {
2089
+ salla.cart.event.detailsFetched(response);
2090
+ return response;
2091
+ }).catch(function (error) {
2092
+ salla.cart.event.detailsNotFetched(error);
2093
+ throw error;
2094
+ });
2095
+ }
2096
+ /**
2097
+ * An Alias For addItem, But For QuickAdd Only.
2098
+ *
2099
+ * todo:: drop it is not used
2100
+ * @param {int} prodId
2101
+ * @return {Promise}
2102
+ * @deprecated drop it is not used
2103
+ */
2104
+
2105
+
2106
+ async quickAdd(prodId) {
2107
+ return this.addItem(prodId);
2108
+ }
2109
+ /**
2110
+ * Add item to cart
2111
+ * 💡 If Quantity not passed will use 'quickAdd' endpoint.
2112
+ *
2113
+ * @param {int|DataFromHook|FormData|Object} productIdOrObject
2114
+ * @return {Promise<unknown>|boolean}
2115
+ */
2116
+
2117
+
2118
+ async addItem(productIdOrObject) {
2119
+ let data = this.getCartPayload(productIdOrObject);
2120
+
2121
+ if (!data.id) {
2122
+ let message = 'There is no product "id"!';
2123
+ salla.cart.event.itemAddedFailed(message);
2124
+ return salla.api.errorPromise(message);
2125
+ } //if quantity is existed, change endpoint & add quantity to formData.
2126
+
2127
+
2128
+ let endpoint = salla.form.getPossibleValue(data.payload, ['quantity', 'donating_amount']) ? 'addItem' : 'quickAdd';
2129
+ let cartId = await this.getCurrentCartId();
2130
+ return this.request([endpoint, cartId, data.id], data.payload).then(function (response) {
2131
+ salla.cart.event.itemAdded(response, data.id);
2132
+ return response;
2133
+ }).catch(function (error) {
2134
+ salla.cart.event.itemAddedFailed(error, data.id);
2135
+ throw error;
2136
+ });
2137
+ }
2138
+
2139
+ async deleteItem(item_id) {
2140
+ let data = this.getCartPayload(item_id);
2141
+
2142
+ if (!data.id) {
2143
+ let message = 'There is no "id"!';
2144
+ salla.cart.event.itemDeletedFailed(message);
2145
+ return salla.api.errorPromise(message);
2146
+ }
2147
+
2148
+ let cartId = await this.getCurrentCartId();
2149
+ return this.request(['deleteItem', cartId, data.id]).then(function (response) {
2150
+ salla.cart.event.itemDeleted(response, data.id);
2151
+ return response;
2152
+ }).catch(function (error) {
2153
+ salla.cart.event.itemDeletedFailed(error, data.id);
2154
+ throw error;
2155
+ });
2156
+ }
2157
+
2158
+ async updateItem(item_id) {
2159
+ let data = this.getCartPayload(item_id);
2160
+
2161
+ if (!data.id) {
2162
+ let message = 'There is no "id"!';
2163
+ salla.cart.event.itemUpdatedFailed(message);
2164
+ return salla.api.errorPromise(message);
2165
+ }
2166
+
2167
+ let cartId = await this.getCurrentCartId();
2168
+
2169
+ if (data.payload.constructor?.name === 'Object') {
2170
+ data.payload['_method'] = 'PUT';
2171
+ } else {
2172
+ data.payload.append('_method', 'PUT');
2173
+ }
2174
+ /**
2175
+ * Lets us POST to support form data
2176
+ * this isn't a symfony (or laravel, or any other framework) issue, it's a limitation of PHP.
2177
+ */
2178
+
2179
+
2180
+ return this.request(['updateItem', cartId, data.id], data.payload).then(function (response) {
2181
+ salla.cart.event.itemUpdated(response, data.id);
2182
+ return response;
2183
+ }).catch(function (error) {
2184
+ salla.cart.event.itemUpdatedFailed(error, data.id);
2185
+ throw error;
2186
+ });
2187
+ }
2188
+
2189
+ async deleteImage(fileId) {
2190
+ if (!(fileId = salla.form.getPossibleValue(fileId, ['id', 'image_id', 'photo_id']))) {
2191
+ let message = 'There is no "id"!';
2192
+ salla.cart.event.imageNotDeleted(message);
2193
+ return salla.api.errorPromise(message);
2194
+ }
2195
+
2196
+ let cartId = await this.getCurrentCartId();
2197
+ return this.request(['deleteImage', cartId, fileId]).then(function (response) {
2198
+ salla.cart.event.imageDeleted(response, fileId);
2199
+ return response;
2200
+ }).catch(function (error) {
2201
+ salla.cart.event.imageNotDeleted(error, fileId);
2202
+ throw error;
2203
+ });
2204
+ }
2205
+
2206
+ async status(cartId) {
2207
+ return this.request(['status', cartId || (await this.getCurrentCartId())]);
2208
+ }
2209
+
2210
+ async submit() {
2211
+ this.status(await this.getCurrentCartId()).then(res => {
2212
+ let next_step = res.data.next_step.to;
2213
+
2214
+ if (!['checkout', 'refresh', 'login'].includes(next_step)) {
2215
+ let message = "Can't find next_step );";
2216
+ salla.cart.event.submitFailed(message);
2217
+ throw message;
2218
+ }
2219
+
2220
+ salla.cart.event.submitted(res);
2221
+
2222
+ if (next_step === 'login') {
2223
+ salla.auth.setCanRedirect(false); // next_step is login
2224
+
2225
+ salla.event.dispatch('login::open'); //todo:: make sure this will not be fired more than once & it will not be infinite loop
2226
+
2227
+ salla.auth.event.onLoggedIn(response => {
2228
+ salla.event.dispatch('login::close');
2229
+ this.submit();
2230
+ });
2231
+ return;
2232
+ }
2233
+
2234
+ if (next_step === 'checkout') {
2235
+ window.location.href = res.data.next_step.url + (window.ApplePaySession ? '?has_apple_pay=true' : '');
2236
+ } else {
2237
+ //next_step === 'refresh'
2238
+ window.location.reload();
2239
+ }
2240
+ }).catch(function (error) {
2241
+ salla.cart.event.submitFailed(error);
2242
+ throw error;
2243
+ });
2244
+ }
2245
+ /**
2246
+ * Extract ProductId & formData from Passed Object
2247
+ * @param {number|Object} productIdOrObject
2248
+ * @return {{payload: *, id: number}}
2249
+ */
2250
+
2251
+
2252
+ getCartPayload(productIdOrObject) {
2253
+ /**
2254
+ * if productIdOrObject is Object, get data from it;
2255
+ * @see Object.prototype.getElementSallaData
2256
+ */
2257
+ let payload = productIdOrObject?.data || (typeof productIdOrObject == 'object' ? productIdOrObject : undefined); //try to get productIdOrObject from payload if it's existed
2258
+
2259
+ productIdOrObject = payload ? salla.form.getPossibleValue(payload, ['prod_id', 'product_id', 'item_id', 'id']) : productIdOrObject;
2260
+ payload = typeof payload == 'object' ? payload : undefined;
2261
+ return {
2262
+ id: productIdOrObject,
2263
+ payload: payload
2264
+ };
2265
+ }
2266
+
2267
+ normalRequest(endPoint, formData = undefined, method = null) {
2268
+ return super.normalRequest(endPoint, formData, method).catch(error => {
2269
+ // it Looks that cart id not valid🤔
2270
+ if (error?.response?.status === 403) {
2271
+ salla.storage.remove('cart');
2272
+ salla.error(salla.lang.get('pages.checkout.try_again'));
2273
+ }
2274
+
2275
+ throw error;
2276
+ });
2277
+ }
2278
+
2279
+ reset() {
2280
+ salla.storage.remove('cart');
2281
+ salla.cart.event.successReset();
2282
+ } //Coupons
2283
+
2284
+ /**
2285
+ * Add Coupon to cart*
2286
+ * @param {string|Object|FormData|Object} coupon
2287
+ */
2288
+
2289
+
2290
+ async addCoupon(coupon) {
2291
+ coupon = coupon.data || coupon;
2292
+
2293
+ if (!(coupon = salla.form.getPossibleValue(coupon, ['coupon']))) {
2294
+ let error = new Error('There is no "Coupon Code"!');
2295
+ error.name = 'EmptyCoupon';
2296
+ salla.event.cart.couponAdditionFailed(error);
2297
+ return salla.api.errorPromise(error);
2298
+ }
2299
+
2300
+ let cartId = await salla.cart.api.getCurrentCartId();
2301
+ return this.request(['addCoupon', cartId], {
2302
+ coupon: coupon
2303
+ }).then(function (response) {
2304
+ salla.event.cart.couponAdded(response, cartId);
2305
+ return response;
2306
+ }).catch(function (error) {
2307
+ salla.event.cart.couponAdditionFailed(error, cartId);
2308
+ throw error;
2309
+ });
2310
+ }
2311
+
2312
+ async deleteCoupon() {
2313
+ let cartId = await salla.cart.api.getCurrentCartId();
2314
+ return this.request(['deleteCoupon', cartId], {}).then(function (response) {
2315
+ salla.event.cart.couponDeleted(response, cartId);
2316
+ return response;
2317
+ }).catch(function (error) {
2318
+ salla.event.cart.couponDeletionFailed(error, cartId);
2319
+ throw error;
2320
+ });
2321
+ }
2322
+
2323
+ }
2324
+
2325
+ class LoyaltyApi extends BaseApi {
2326
+ constructor() {
2327
+ super();
2328
+ this.namespace = 'loyalty';
2329
+ this.endpoints = {
2330
+ getProgram: 'loyalty',
2331
+ exchange: 'loyalty/exchange',
2332
+ reset: "loyalty/exchange"
2333
+ };
2334
+ this.endpointsMethods = {
2335
+ getProgram: 'get',
2336
+ reset: 'put'
2337
+ };
2338
+ this.after_init();
2339
+ }
2340
+
2341
+ getProgram() {
2342
+ return this.request('getProgram').then(response => {
2343
+ salla.loyalty.event.programFetched(response);
2344
+ return response;
2345
+ }).catch(err => {
2346
+ salla.loyalty.event.programNotFetched(err);
2347
+ throw err;
2348
+ });
2349
+ }
2350
+ /**
2351
+ * @param {object | FormData | string} prize_id
2352
+ * @param {number|Null} cart_id
2353
+ */
2354
+
2355
+
2356
+ async exchange(prize_id, cart_id = null) {
2357
+ prize_id = salla.form.getPossibleValue(prize_id, ['id', 'loyalty_prize_id', 'prize_id']);
2358
+
2359
+ if (!prize_id) {
2360
+ let error = 'Unable to find cart id. Please provide one.';
2361
+ salla.loyalty.event.exchangeFailed(error);
2362
+ return salla.api.errorPromise(error);
2363
+ }
2364
+
2365
+ cart_id = cart_id || (await salla.cart.getCurrentCartId());
2366
+
2367
+ if (!cart_id) {
2368
+ let error = 'Unable to find cart id. Please provide one.';
2369
+ salla.loyalty.event.exchangeFailed(error);
2370
+ return salla.api.errorPromise(error);
2371
+ }
2372
+
2373
+ return this.request('exchange', {
2374
+ loyalty_prize_id: prize_id,
2375
+ cart_id: cart_id
2376
+ }).then(function (response) {
2377
+ salla.loyalty.event.exchangeSucceeded(response, prize_id);
2378
+ return response;
2379
+ }).catch(function (error) {
2380
+ salla.loyalty.event.exchangeFailed(error, prize_id);
2381
+ throw error;
2382
+ });
2383
+ }
2384
+ /**
2385
+ *
2386
+ * Reset or delete exchange
2387
+ */
2388
+
2389
+
2390
+ async reset(cart_id = null) {
2391
+ cart_id = cart_id || (await salla.cart.getCurrentCartId());
2392
+
2393
+ if (!cart_id) {
2394
+ let error = 'Unable to find cart id. Please provide one.';
2395
+ salla.loyalty.event.resetFailed(error);
2396
+ return salla.api.errorPromise(error);
2397
+ }
2398
+
2399
+ return this.request('reset', {
2400
+ cart_id: cart_id
2401
+ }).then(response => {
2402
+ salla.loyalty.event.resetSucceeded(response);
2403
+ return response;
2404
+ }).catch(err => {
2405
+ salla.loyalty.event.resetFailed(err);
2406
+ throw err;
2407
+ });
2408
+ }
2409
+
2410
+ }
2411
+
2412
+ class AuthApi extends BaseApi {
2413
+ constructor() {
2414
+ super();
2415
+ this.namespace = 'auth';
2416
+ this.canRedirect_ = true;
2417
+ this.endpoints = {
2418
+ login: "auth/{type}/send_verification",
2419
+ resend: "auth/resend_verification",
2420
+ verify: "auth/{type}/verify",
2421
+ register: "auth/register",
2422
+ logout: "logout",
2423
+ // todo :: rename it to auth/logout
2424
+ refresh: "auth/refresh" //is workaround when we are logged in by web but we don't have token
2425
+
2426
+ };
2427
+ this.webEndpoints = ['logout', 'auth/jwt', 'refresh'];
2428
+ this.endpointsMethods = {
2429
+ logout: 'get'
2430
+ };
2431
+ this.after_init();
2432
+ }
2433
+
2434
+ setCanRedirect(canRedirect) {
2435
+ salla.api.auth.canRedirect_ = canRedirect;
2436
+ }
2437
+
2438
+ canRedirect() {
2439
+ return salla.api.auth.canRedirect_;
2440
+ }
2441
+ /**
2442
+ * login
2443
+ *
2444
+ * @param {AuthRequest.loginByMobile | AuthRequest.loginByEmail | object| FormData} payload
2445
+ * @return {Promise}
2446
+ * TODO:: Remove type, just use data
2447
+ */
2448
+
2449
+
2450
+ login(payload) {
2451
+ /**
2452
+ * if type is Object, get data from it;
2453
+ * @see Object.prototype.getElementSallaData
2454
+ */
2455
+ if (payload?.data) {
2456
+ payload = payload.data;
2457
+ }
2458
+
2459
+ let type = salla.form.getPossibleValue(payload, ['type']);
2460
+
2461
+ if (!['email', 'mobile'].includes(type)) {
2462
+ let error = 'Login type should be in: [email, mobile]';
2463
+ salla.auth.event.codeNotSent(error);
2464
+ return salla.api.errorPromise(error);
2465
+ }
2466
+
2467
+ return this.request(['login', type], payload).then(function (response) {
2468
+ salla.auth.event.codeSent(response, type);
2469
+ return response;
2470
+ }).catch(function (error) {
2471
+ salla.auth.event.codeNotSent(error, type);
2472
+ throw error;
2473
+ });
2474
+ }
2475
+ /**
2476
+ * verify
2477
+ *
2478
+ * @param {AuthRequest.verifyByEmail|AuthRequest.verifyByMobile} payload
2479
+ * @param supportWebAuth
2480
+ * @return {Promise}
2481
+ */
2482
+
2483
+
2484
+ async verify(payload, supportWebAuth = true) {
2485
+ let type = salla.form.getPossibleValue(payload, ['type']);
2486
+
2487
+ if (!type) {
2488
+ let message = 'Failed to know what\'s login type!';
2489
+ salla.auth.event.verificationFailed(message);
2490
+ return salla.api.errorPromise(message);
2491
+ } // workaround to allow supportWebAuth by passing it as argument or inside payload
2492
+
2493
+
2494
+ supportWebAuth = salla.form.getPossibleValue(payload, ['supportWebAuth']) === false ? false : supportWebAuth; // some times, there is need to fire action, after login,
2495
+ // search for "element.hasAttribute('data-auth-required')" in "helpers/initiateSalla.js" to know
2496
+ // todo:: it looks it's workaround to do thing after login,
2497
+ // I think we can keep any thing we need in salla.storage
2498
+ // then apply and delete it when we need
2499
+
2500
+ if (salla.auth.event.next_event) {
2501
+ payload.next_event = salla.auth.event.next_event;
2502
+ }
2503
+ /** @type {AuthResponse.verify} */
2504
+
2505
+
2506
+ let response = await this.request(['verify', type], payload);
2507
+
2508
+ if (response?.status !== 200) {
2509
+ salla.auth.event.verificationFailed(response, type);
2510
+ return salla.api.errorPromise(response);
2511
+ }
2512
+
2513
+ let isAuthenticated = response.data?.case === 'authenticated';
2514
+ isAuthenticated && salla.auth.event.tokenFetched(response.data.token);
2515
+ /**
2516
+ * in this route we have middleware to share same user in api with current web session
2517
+ * @see \Modules\Auth\Http\Middleware\CustomJWTAuthForWebRoutes
2518
+ */
2519
+
2520
+ supportWebAuth && (await this.request('auth/jwt'));
2521
+ isAuthenticated && salla.auth.event.loggedIn(response);
2522
+ salla.auth.event.verified(response, type);
2523
+ return salla.api.successPromise(response);
2524
+ }
2525
+ /**
2526
+ * @param {AuthRequest.resend} data
2527
+ * @return {Promise<AuthResponse.resend>}
2528
+ */
2529
+
2530
+
2531
+ resend(data) {
2532
+ data = data.data || data;
2533
+ data.type = data.type || 'mobile';
2534
+ let message;
2535
+
2536
+ if (data.type === 'mobile' && (!data.phone || !data.country_code)) {
2537
+ salla.auth.event.codeNotSent(message = 'There is no phone or country_code!', data);
2538
+ return salla.api.errorPromise(message);
2539
+ }
2540
+
2541
+ if (data.type === 'email' && !data.email) {
2542
+ salla.auth.event.codeNotSent(message = 'There is no email!', data);
2543
+ return salla.api.errorPromise(message);
2544
+ }
2545
+
2546
+ return this.request('resend', data).then(function (response) {
2547
+ salla.auth.event.codeSent(response, data);
2548
+ return response;
2549
+ }).catch(function (error) {
2550
+ salla.auth.event.codeNotSent(error, data);
2551
+ throw error;
2552
+ });
2553
+ }
2554
+ /**
2555
+ * register
2556
+ * @param {object|Object} payload
2557
+ * @return {Promise}
2558
+ */
2559
+
2560
+
2561
+ register(payload) {
2562
+ let element;
2563
+ /**
2564
+ * @see Object.prototype.getElementSallaData
2565
+ */
2566
+
2567
+ if (payload.data) {
2568
+ element = payload.element;
2569
+ payload = payload.data;
2570
+ } // sometimes, there is need to fire action, after login,
2571
+ // search for "element.hasAttribute('data-auth-required')" in "helpers/initiateSalla.js" to know
2572
+
2573
+
2574
+ if (salla.auth.event.next_event) {
2575
+ payload.next_event = salla.auth.event.next_event;
2576
+ }
2577
+
2578
+ return this.request('register', payload).then(function (response) {
2579
+ salla.auth.event.registered.call(element, response);
2580
+
2581
+ if (response.data?.case === 'authenticated') {
2582
+ salla.auth.event.loggedIn(response);
2583
+ }
2584
+
2585
+ return response;
2586
+ }).catch(function (error) {
2587
+ salla.auth.event.registrationFailed.call(element, error);
2588
+ throw error;
2589
+ });
2590
+ }
2591
+
2592
+ logout() {
2593
+ return this.request('logout').then(function (response) {
2594
+ salla.auth.event.loggedOut(response);
2595
+ salla.log('Reloading after 0.1 sec...');
2596
+ setTimeout(() => location.reload(), 100);
2597
+ return response;
2598
+ }).catch(function (error) {
2599
+ salla.auth.event.failedLogout(error);
2600
+ throw error;
2601
+ });
2602
+ }
2603
+
2604
+ refresh() {
2605
+ return this.request('refresh').catch(function (error) {
2606
+ salla.auth.event.refreshFailed(error);
2607
+ throw error;
2608
+ });
2609
+ }
2610
+
2611
+ }
2612
+
2613
+ class OrderApi extends BaseApi {
2614
+ constructor() {
2615
+ super();
2616
+ this.namespace = 'order';
2617
+ this.endpoints = {
2618
+ cancel: 'orders/cancel/{id}',
2619
+ createCartFromOrder: 'reorder/{id}',
2620
+ sendInvoice: 'orders/send/{id}'
2621
+ };
2622
+ this.endpointsMethods = {
2623
+ createCartFromOrder: 'get'
2624
+ };
2625
+ this.after_init();
2626
+ }
2627
+ /**
2628
+ * @param {object|FormData} payload
2629
+ */
2630
+
2631
+
2632
+ show(payload) {
2633
+ let orderId = salla.form.getPossibleValue(payload?.data || payload, ['id', 'order_id']); //important for mobile apps
2634
+
2635
+ salla.event.dispatch('mobile::order.placed', {
2636
+ order_id: orderId
2637
+ });
2638
+ location.href = salla.form.getPossibleValue(payload?.data || payload, ['url']);
2639
+ }
2640
+ /**
2641
+ * @param {int} orderId
2642
+ * @return {Promise}
2643
+ */
2644
+
2645
+
2646
+ cancel(orderId) {
2647
+ orderId = orderId || Salla.config.get('page.id');
2648
+ return this.request(['cancel', orderId], {
2649
+ params: {
2650
+ has_apple_pay: !!window.ApplePaySession
2651
+ }
2652
+ }).then(function (response) {
2653
+ salla.event.order.canceled(response, orderId);
2654
+ return response;
2655
+ }).catch(function (error) {
2656
+ salla.event.order.notCanceled(error, orderId);
2657
+ throw error;
2658
+ });
2659
+ }
2660
+ /**
2661
+ * @param {int} orderId
2662
+ * @return {Promise}
2663
+ */
2664
+
2665
+
2666
+ createCartFromOrder(orderId) {
2667
+ orderId = orderId || salla.config.get('page.id');
2668
+ return this.request(['createCartFromOrder', orderId]).then(function (response) {
2669
+ salla.storage.set('cart', {
2670
+ id: response.data.cart_id,
2671
+ user_id: salla.config.get('user.id')
2672
+ });
2673
+ salla.event.order.orderCreated(response, orderId);
2674
+ window.location.href = response.data.url;
2675
+ return response;
2676
+ }).catch(function (error) {
2677
+ salla.event.order.orderCreationFailed(error, orderId);
2678
+ throw error;
2679
+ });
2680
+ }
2681
+
2682
+ sendInvoice(data) {
2683
+ let order_id = salla.form.getPossibleValue(data, ['id']) || salla.config.get('page.id');
2684
+
2685
+ if (!order_id || isNaN(order_id)) {
2686
+ let message = 'There is no id!';
2687
+ salla.order.event.invoiceNotSent(message);
2688
+ return salla.api.errorPromise(message);
2689
+ }
2690
+
2691
+ return this.request(['sendInvoice', order_id], data).then(res => {
2692
+ salla.event.order.invoiceSent(res, order_id);
2693
+ return res;
2694
+ }).catch(error => {
2695
+ salla.event.order.invoiceNotSent(error, order_id);
2696
+ throw error;
2697
+ });
2698
+ }
2699
+
2700
+ }
2701
+
2702
+ class ProductApi extends BaseApi {
2703
+ constructor() {
2704
+ super();
2705
+ this.namespace = 'product';
2706
+ this.previousQuery = '';
2707
+ this.endpoints = {
2708
+ getPrice: 'products/{id}/price',
2709
+ availabilitySubscribe: 'products/{id}/availability-notify',
2710
+ search: 'products/search',
2711
+ categories: 'products/categories/{?id}',
2712
+ offers: 'products/{product_id}/specialoffer',
2713
+ getSizeGuides: 'products/{prod_id}/size-guides',
2714
+ giftDetail: 'products/{product_id}/buy-as-gift',
2715
+ giftToCart: 'products/{product_id}/buy-as-gift',
2716
+ giftImage: 'products/buy-as-gift/image'
2717
+ };
2718
+ this.endpointsMethods = {
2719
+ giftDetail: 'get'
2720
+ };
2721
+ this.after_init();
2722
+ }
2723
+ /**
2724
+ * Getting Price Of Product
2725
+ * @param {int|Object|Object|FormData} productIdOrPayload
2726
+ * @return {Promise}
2727
+ */
2728
+
2729
+
2730
+ getPrice(productIdOrPayload) {
2731
+ let payload = productIdOrPayload.data || productIdOrPayload;
2732
+ let product_id = salla.form.getPossibleValue(payload, ['id', 'prod_id', 'product_id']);
2733
+ return this.request(['getPrice', product_id], typeof payload == 'object' ? payload : undefined).then(function (response) {
2734
+ salla.product.event.priceUpdated(response, product_id);
2735
+ return response;
2736
+ }).catch(function (error) {
2737
+ salla.product.event.priceUpdateFailed(error, product_id);
2738
+ throw error;
2739
+ });
2740
+ }
2741
+ /**
2742
+ * @return {Promise}
2743
+ */
2744
+
2745
+
2746
+ categories(categoryId) {
2747
+ return this.request(['categories', categoryId || ''], null, 'get').then(function (response) {
2748
+ salla.product.event.categoriesFetched(response);
2749
+ return response;
2750
+ }).catch(function (error) {
2751
+ salla.product.event.categoriesFailed(error);
2752
+ throw error;
2753
+ });
2754
+ }
2755
+
2756
+ availabilitySubscribe(prodId) {
2757
+ let formData = prodId.data || prodId;
2758
+ prodId = salla.form.getPossibleValue(formData, ['id', 'prod_id']);
2759
+ return this.request(['availabilitySubscribe', prodId], typeof formData == 'object' ? formData : undefined).then(function (response) {
2760
+ salla.product.event.availabilitySubscribed(response, prodId);
2761
+ return response;
2762
+ }).catch(function (error) {
2763
+ salla.product.event.availabilitySubscribedFailed(error, prodId);
2764
+ throw error;
2765
+ });
2766
+ }
2767
+ /**
2768
+ * @param {SearchPayload|string|any} query
2769
+ * @return {Promise<unknown>}
2770
+ */
2771
+
2772
+
2773
+ search(query) {
2774
+ /**
2775
+ * if query is Object, get data from it;
2776
+ * @see Object.prototype.getElementSallaData
2777
+ */
2778
+ let formData = query.data;
2779
+
2780
+ if (!formData) {
2781
+ formData = {
2782
+ params: typeof query === 'string' ? {
2783
+ query: query
2784
+ } : query
2785
+ };
2786
+ }
2787
+
2788
+ query = formData instanceof FormData ? formData.get('query') : formData.query || formData.params?.query;
2789
+
2790
+ if (!query) {
2791
+ let msg = 'There is no "query"!';
2792
+ salla.product.event.searchFailed(msg);
2793
+ return salla.api.errorPromise(msg);
2794
+ }
2795
+
2796
+ if (query === salla.api.product.previousQuery) {
2797
+ let msg = 'Query is same as previous one!';
2798
+ salla.product.event.searchFailed(msg);
2799
+ salla.api.product.previousQuery = null;
2800
+ return salla.api.errorPromise(msg);
2801
+ }
2802
+
2803
+ salla.api.product.previousQuery = query;
2804
+ return this.request('search', formData, 'get').then(function (response) {
2805
+ salla.product.event.searchResults(response, query);
2806
+ return response;
2807
+ }).catch(function (error) {
2808
+ salla.product.event.searchFailed(error, query);
2809
+ throw error;
2810
+ });
2811
+ }
2812
+ /**
2813
+ * Show offer popup for this product
2814
+ * @param {Object|number} product_id
2815
+ */
2816
+
2817
+
2818
+ offers(product_id) {
2819
+ product_id = salla.form.getPossibleValue(product_id?.data | product_id, ['product_id', 'id']);
2820
+
2821
+ if (!product_id) {
2822
+ let message = 'There is no "product_id"!';
2823
+ salla.offer.event.fetchDetailsFailed(message);
2824
+ return salla.api.errorPromise(message);
2825
+ }
2826
+
2827
+ return this.request(['offers', product_id]).then(function (response) {
2828
+ salla.product.event.offersFetched(response, product_id);
2829
+ return response;
2830
+ }).catch(function (error) {
2831
+ salla.product.event.fetchOffersFailed(error, product_id);
2832
+ throw error;
2833
+ });
2834
+ }
2835
+ /**
2836
+ * Get size guides
2837
+ *
2838
+ * The product id for which the size guide is required.
2839
+ * @param {string} prod_id
2840
+ */
2841
+
2842
+
2843
+ getSizeGuides(prod_id) {
2844
+ return this.request(`products/${prod_id}/size-guides`, null, 'get').then(response => {
2845
+ salla.product.event.sizeGuideFetched(response, prod_id);
2846
+ return response;
2847
+ }).catch(err => {
2848
+ salla.product.event.sizeGuideFetchFailed(err, prod_id);
2849
+ throw err;
2850
+ });
2851
+ }
2852
+ /**
2853
+ * Get gifting system
2854
+ *
2855
+ * The product id for which the gifting system is required.
2856
+ * @param {number} product_id
2857
+ */
2858
+
2859
+
2860
+ getGiftDetails(product_id) {
2861
+ return this.request(['giftDetail', product_id]).then(resp => {
2862
+ salla.product.event.giftFetched(resp, product_id);
2863
+ return resp;
2864
+ }).catch(err => {
2865
+ salla.product.event.giftFetchFailed(err, product_id);
2866
+ throw err;
2867
+ });
2868
+ }
2869
+ /**
2870
+ * Add gift to cart
2871
+ *
2872
+ * The product id for which the gifting system is required.
2873
+ * @param {number} product_id
2874
+ * @param {Object} payload
2875
+ * @param {Object} withRedirect
2876
+ * @return {Promise<unknown>|*}
2877
+ */
2878
+
2879
+
2880
+ addGiftToCart(product_id, payload, withRedirect = false) {
2881
+ return this.request(['giftToCart', product_id], payload).then(resp => {
2882
+ salla.product.event.addGiftToCartSucceeded(resp, product_id);
2883
+
2884
+ if (withRedirect) {
2885
+ window.location.href = resp.redirect;
2886
+ }
2887
+
2888
+ return response;
2889
+ }).catch(err => {
2890
+ salla.product.event.addGiftToCartFailed(err, product_id);
2891
+ throw err;
2892
+ });
2893
+ }
2894
+ /**
2895
+ * Upload Custom Image for gift
2896
+ * @param {FormData} multipartPayload
2897
+ */
2898
+
2899
+
2900
+ uploadGiftImage(multipartPayload) {
2901
+ return this.request('giftImage', multipartPayload).then(resp => {
2902
+ salla.product.event.giftImageUploadSucceeded(resp);
2903
+ return resp;
2904
+ }).catch(e => {
2905
+ salla.product.event.giftImageUploadFailed(e);
2906
+ throw e;
2907
+ });
2908
+ }
2909
+
2910
+ }
2911
+
2912
+ class ProfileApi extends BaseApi {
2913
+ constructor() {
2914
+ super();
2915
+ this.namespace = 'profile';
2916
+ this.endpoints = {
2917
+ info: 'auth/user',
2918
+ update: 'profile/update',
2919
+ updateContacts: 'profile/contacts/update',
2920
+ verify: 'profile/verify'
2921
+ };
2922
+ this.after_init();
2923
+ }
2924
+
2925
+ info() {
2926
+ return this.request('info', null, 'get').then(response => {
2927
+ salla.profile.event.infoFetched(response);
2928
+ return response;
2929
+ }).catch(error => {
2930
+ salla.profile.event.infoNotFetched(error);
2931
+ throw error;
2932
+ });
2933
+ }
2934
+ /**
2935
+ * @param {ProfileUpdatePayload} data
2936
+ * @return {Promise<ProfileResponse>}
2937
+ */
2938
+
2939
+
2940
+ update(data) {
2941
+ return this.request('update', data).then(response => {
2942
+ salla.profile.event.updated(response);
2943
+ return response;
2944
+ }).catch(error => {
2945
+ salla.event.profile.updateFailed(error);
2946
+ throw error;
2947
+ });
2948
+ }
2949
+ /**
2950
+ * @param {ProfileUpdateContactPayload} data
2951
+ * @return {Promise<ProfileResponse>}
2952
+ */
2953
+
2954
+
2955
+ updateContacts(data) {
2956
+ return this.request('updateContacts', data).then(response => {
2957
+ salla.profile.event.verificationCodeSent(response);
2958
+ return response;
2959
+ }).catch(error => {
2960
+ salla.event.profile.updateContactsFailed(error);
2961
+ throw error;
2962
+ });
2963
+ }
2964
+ /**
2965
+ * @param {ProfileUpdateContactPayload} data
2966
+ * @return {Promise<ProfileResponse>}
2967
+ */
2968
+
2969
+
2970
+ verify(data) {
2971
+ return this.request('verify', data).then(response => {
2972
+ salla.profile.event.verified(response);
2973
+ return response;
2974
+ }).catch(error => {
2975
+ salla.event.profile.unVerified(error);
2976
+ throw error;
2977
+ });
2978
+ }
2979
+
2980
+ }
2981
+
2982
+ class CommentApi extends BaseApi {
2983
+ constructor() {
2984
+ super();
2985
+ this.namespace = 'comment';
2986
+ this.endpoints = {
2987
+ add: '{type}/{id}/comments'
2988
+ };
2989
+ this.after_init();
2990
+ }
2991
+ /**
2992
+ * Add comment
2993
+ *
2994
+ * @param {AddCommentPayload} data
2995
+ * @return {Promise}
2996
+ */
2997
+
2998
+
2999
+ add(data) {
3000
+ /**
3001
+ * if prod_id is Object, get data from it;
3002
+ * @see Object.prototype.getElementSallaData
3003
+ */
3004
+
3005
+ if (data?.data) {
3006
+ data = data.data;
3007
+ }
3008
+
3009
+ let {
3010
+ id,
3011
+ type,
3012
+ comment
3013
+ } = Object.fromEntries(data);
3014
+ let error;
3015
+
3016
+ if (!id) {
3017
+ salla.event.comment.additionFailed(error = 'Failed to get id!');
3018
+ return salla.api.errorPromise(error);
3019
+ }
3020
+
3021
+ if (!type || !['products', 'pages', 'product', 'page'].includes(type)) {
3022
+ salla.event.comment.additionFailed(error = 'Failed to get type one of:(products, product, page, pages)!');
3023
+ return salla.api.errorPromise(error);
3024
+ }
3025
+
3026
+ if (!comment) {
3027
+ salla.event.comment.additionFailed(error = 'can\'t find comment content!');
3028
+ return salla.api.errorPromise(error);
3029
+ } //we need type in plural format to be injected into the url.
3030
+
3031
+
3032
+ type += ['product', 'page'].includes(type) ? 's' : '';
3033
+ return this.request(['add', type, id], {
3034
+ comment: comment
3035
+ }).then(function (response) {
3036
+ salla.event.comment.added(response, id);
3037
+ return response;
3038
+ }).catch(function (error) {
3039
+ salla.event.comment.additionFailed(error, id);
3040
+ throw error;
3041
+ });
3042
+ }
3043
+
3044
+ }
3045
+
3046
+ class CurrencyApi extends BaseApi {
3047
+ constructor() {
3048
+ super();
3049
+ this.namespace = 'currency';
3050
+ this.endpoints = {
3051
+ change: '/',
3052
+ list: 'currencies'
3053
+ };
3054
+ this.endpointsMethods = {
3055
+ change: 'get',
3056
+ list: 'get'
3057
+ };
3058
+ this.webEndpoints = ['change'];
3059
+ this.after_init();
3060
+ }
3061
+ /**
3062
+ * Change Current Currency
3063
+ * @param {string|Object|FormData|Object} currency
3064
+ */
3065
+
3066
+
3067
+ change(currency) {
3068
+ currency = salla.form.getPossibleValue(currency.data || currency, ['currency', 'code']);
3069
+
3070
+ if (!currency) {
3071
+ let error = 'Can\'t find currency code!';
3072
+ salla.currency.event.failed(error);
3073
+ return salla.api.errorPromise(error);
3074
+ }
3075
+
3076
+ return this.request('change', {
3077
+ params: {
3078
+ change_currency: '',
3079
+ currency: currency
3080
+ }
3081
+ }).then(function (response) {
3082
+ salla.cookie.set('fresh_summary', 1);
3083
+ salla.storage.set('cart', '');
3084
+ salla.currency.event.changed(response, currency);
3085
+ return response;
3086
+ }).catch(function (error) {
3087
+ salla.currency.event.failed(error, currency);
3088
+ throw error;
3089
+ });
3090
+ }
3091
+
3092
+ list() {
3093
+ return this.request('list').then(function (response) {
3094
+ salla.currency.event.fetched(response);
3095
+ return response;
3096
+ }).catch(function (error) {
3097
+ salla.currency.event.failedToFetch(error);
3098
+ throw error;
3099
+ });
3100
+ }
3101
+
3102
+ }
3103
+
3104
+ class DocumentApi extends BaseApi {
3105
+ constructor() {
3106
+ super();
3107
+ this.namespace = 'document';
3108
+ }
3109
+
3110
+ }
3111
+
3112
+ class RatingApi extends BaseApi {
3113
+ constructor() {
3114
+ super();
3115
+ this.namespace = 'rating';
3116
+ this.endpoints = {
3117
+ store: 'rating/store',
3118
+ products: 'rating/products',
3119
+ shipping: 'rating/shipping',
3120
+ order: 'rating/{order_id}'
3121
+ };
3122
+ this.endpointsMethods = {
3123
+ order: 'get'
3124
+ };
3125
+ this.after_init();
3126
+ }
3127
+ /**
3128
+ * @param {number|{id:number, order_id:number}} orderIdOrPayload - could be number or data
3129
+ * @return {Promise<unknown>}
3130
+ */
3131
+
3132
+
3133
+ order(orderIdOrPayload) {
3134
+ let payload = typeof orderIdOrPayload === 'object' ? orderIdOrPayload : {};
3135
+ let orderId = salla.form.getPossibleValue(orderIdOrPayload?.data || orderIdOrPayload, ['order_id', 'id']);
3136
+
3137
+ if (!orderId) {
3138
+ let error = 'There is no "order_id"!';
3139
+ salla.event.rating.orderNotFetched(error);
3140
+ return salla.api.errorPromise(error);
3141
+ }
3142
+
3143
+ return this.request(['order', orderId], payload).then(function (response) {
3144
+ salla.event.rating.orderFetched(response, orderId);
3145
+ return response;
3146
+ }).catch(function (error) {
3147
+ salla.event.rating.orderNotFetched(error, orderId);
3148
+ throw error;
3149
+ });
3150
+ }
3151
+ /**
3152
+ * Add rating to the Store
3153
+ * @param {Object|FormData|Object} data
3154
+ */
3155
+
3156
+
3157
+ store(data) {
3158
+ data = data.data || data;
3159
+
3160
+ if (!data) {
3161
+ let error = 'There is no "data"!';
3162
+ salla.event.rating.storeFailed(error);
3163
+ return salla.api.errorPromise(error);
3164
+ }
3165
+
3166
+ return this.request('store', data).then(function (response) {
3167
+ salla.event.rating.storeRated(response, data);
3168
+ return response;
3169
+ }).catch(function (error) {
3170
+ salla.event.rating.storeFailed(error, data);
3171
+ throw error;
3172
+ });
3173
+ }
3174
+ /**
3175
+ * An Alias Fro product()
3176
+ * @return {Promise<unknown>}
3177
+ */
3178
+
3179
+
3180
+ products(data) {
3181
+ data = data.data || data;
3182
+
3183
+ if (!data) {
3184
+ let error = 'There is no "data"!';
3185
+ salla.event.rating.productsFailed(error);
3186
+ return salla.api.errorPromise(error);
3187
+ }
3188
+
3189
+ return this.request('products', data).then(function (response) {
3190
+ salla.event.rating.productsRated(response, data);
3191
+ return response;
3192
+ }).catch(function (error) {
3193
+ salla.event.rating.productsFailed(error, data);
3194
+ throw error;
3195
+ });
3196
+ }
3197
+ /**
3198
+ * Add rating to the shipping
3199
+ * @param {Object|FormData|Object} data
3200
+ */
3201
+
3202
+
3203
+ shipping(data) {
3204
+ data = data.data || data;
3205
+
3206
+ if (!data) {
3207
+ let error = 'There is no "data"!';
3208
+ salla.event.rating.shippingFailed(error);
3209
+ return salla.api.errorPromise(error);
3210
+ }
3211
+
3212
+ return this.request('shipping', data).then(function (response) {
3213
+ salla.event.rating.shippingRated(response, data);
3214
+ return response;
3215
+ }).catch(function (error) {
3216
+ salla.event.rating.shippingFailed(error, data);
3217
+ throw error;
3218
+ });
3219
+ }
3220
+
3221
+ }
3222
+
3223
+ class WishlistApi extends BaseApi {
3224
+ constructor() {
3225
+ super();
3226
+ this.namespace = 'wishlist';
3227
+ this.endpoints = {
3228
+ add: "products/favorites/{id}",
3229
+ remove: "products/favorites/{id}"
3230
+ };
3231
+ this.endpointsMethods = {
3232
+ remove: 'delete'
3233
+ };
3234
+ this.after_init();
3235
+ }
3236
+ /**
3237
+ * Toggle the product from wishlist.
3238
+ *
3239
+ * @param {string|number|Object} product_id
3240
+ * @return {Promise}
3241
+ */
3242
+
3243
+
3244
+ toggle(product_id) {
3245
+ return salla.storage.get("salla::wishlist", []).includes(product_id) ? this.remove(product_id) : this.add(product_id);
3246
+ }
3247
+ /**
3248
+ * Add To User Wishlist
3249
+ *
3250
+ * @param {string|number|Object} product_id
3251
+ * @return {Promise}
3252
+ */
3253
+
3254
+
3255
+ add(product_id) {
3256
+ let error;
3257
+
3258
+ if (salla.config.isGuest()) {
3259
+ salla.wishlist.event.additionFailed(error = salla.lang.get('common.messages.must_login'));
3260
+ salla.error(error);
3261
+ return salla.api.errorPromise(error);
3262
+ }
3263
+ /**
3264
+ * if product_id is Object, get data from it;
3265
+ * @see Object.prototype.getElementSallaData
3266
+ */
3267
+
3268
+
3269
+ product_id = salla.form.getPossibleValue(product_id?.data || product_id, ['product_id', 'id']);
3270
+
3271
+ if (!product_id) {
3272
+ salla.wishlist.event.additionFailed(error = 'Failed to get product id!');
3273
+ return salla.api.errorPromise(error);
3274
+ }
3275
+
3276
+ return this.request(['add', product_id]).then(response => {
3277
+ this.updateWishlistStorage(product_id);
3278
+ salla.wishlist.event.added(response, product_id);
3279
+ return response;
3280
+ }).catch(function (error) {
3281
+ salla.wishlist.event.additionFailed(error, product_id);
3282
+ throw error;
3283
+ });
3284
+ }
3285
+ /**
3286
+ * Add To User Wishlist
3287
+ *
3288
+ * @param {string|number|Object} product_id
3289
+ * @return {Promise}
3290
+ */
3291
+
3292
+
3293
+ remove(product_id) {
3294
+ let error;
3295
+
3296
+ if (salla.config.isGuest()) {
3297
+ salla.wishlist.event.additionFailed(error = salla.lang.get('common.messages.must_login'));
3298
+ salla.error(error);
3299
+ return salla.api.errorPromise(error);
3300
+ }
3301
+ /**
3302
+ * if product_id is Object, get data from it;
3303
+ * @see Object.prototype.getElementSallaData
3304
+ */
3305
+
3306
+
3307
+ product_id = salla.form.getPossibleValue(product_id?.data || product_id, ['product_id', 'id']);
3308
+
3309
+ if (!product_id) {
3310
+ salla.wishlist.event.removingFailed(error = 'Failed to get id!');
3311
+ return salla.api.errorPromise(error);
3312
+ }
3313
+
3314
+ return this.request(['remove', product_id]).then(response => {
3315
+ this.updateWishlistStorage(product_id, false);
3316
+ salla.wishlist.event.removed(response, product_id);
3317
+ return response;
3318
+ }).catch(function (error) {
3319
+ salla.wishlist.event.removingFailed(error, product_id);
3320
+ throw error;
3321
+ });
3322
+ }
3323
+ /**
3324
+ * Push the product id to the local storage.
3325
+ *
3326
+ * @param id
3327
+ * @param isAdded
3328
+ */
3329
+
3330
+
3331
+ updateWishlistStorage(id, isAdded = true) {
3332
+ let wishlist = salla.storage.get("salla::wishlist", []);
3333
+ isAdded ? wishlist.push(id) : wishlist.splice(wishlist.indexOf(id), 1);
3334
+ salla.storage.set('salla::wishlist', wishlist);
3335
+ }
3336
+
3337
+ }
3338
+
3339
+ class ScopeApi extends BaseApi {
3340
+ constructor() {
3341
+ super();
3342
+ this.namespace = 'scopes';
3343
+ this.endpoints = {
3344
+ get: 'scopes',
3345
+ change: 'scopes',
3346
+ getProductAvailability: 'scopes/availability?product_id={id}'
3347
+ };
3348
+ this.after_init();
3349
+ }
3350
+ /**
3351
+ *
3352
+ * Get available scopes
3353
+ */
3354
+
3355
+
3356
+ get() {
3357
+ return this.request('scopes', null, 'get').then(response => {
3358
+ salla.scope.event.fetched(response);
3359
+ return response;
3360
+ }).catch(err => {
3361
+ salla.scope.event.notFetched(err);
3362
+ throw err;
3363
+ });
3364
+ }
3365
+ /**
3366
+ * Change exising scope
3367
+ * @param {FormData} payload
3368
+ */
3369
+
3370
+
3371
+ change(payload) {
3372
+ return this.request('scopes', payload).then(response => {
3373
+ salla.scope.event.changeSucceeded(response);
3374
+ return response;
3375
+ }).catch(err => {
3376
+ salla.scope.event.changeFailed(err);
3377
+ throw err;
3378
+ });
3379
+ }
3380
+ /**
3381
+ * @param {number} product_id
3382
+ */
3383
+
3384
+
3385
+ getProductAvailability(product_id = null) {
3386
+ return this.request(`scopes/availability?product_id=${product_id}`, null, 'get').then(resp => {
3387
+ salla.scope.event.productAvailabilityFetched(resp, product_id);
3388
+ return resp;
3389
+ }).catch(err => {
3390
+ salla.scope.event.productAvailabilityNotFetched(err);
3391
+ throw resp;
3392
+ });
3393
+ }
3394
+
3395
+ }
3396
+
3397
+ /**
3398
+ * @class
3399
+ * @augments axios
3400
+ */
3401
+
3402
+ class request {
3403
+ constructor() {
3404
+ salla.event.on('twilight::initiated', () => this.initiateRequest());
3405
+ salla.event.on('profile::info.fetched', response => this.setCurrencyAndLanguage(response));
3406
+ salla.event.on('auth::token.fetched', token => {
3407
+ salla.storage.set('token', token);
3408
+ this.setToken(token);
3409
+ salla.cart.api.reset();
3410
+ }); // salla.event.on('document::request.failed', (request, error) => {
3411
+ // salla.logger.warn(error, request);
3412
+ // })
3413
+
3414
+ this.notifier_handler_disabled = false;
3415
+ this.axios = axios.create({
3416
+ headers: {
3417
+ common: {
3418
+ 'X-Requested-With': 'XMLHttpRequest',
3419
+ 'S-SOURCE': 'twilight',
3420
+ 'S-APP-VERSION': 'v2.0.0',
3421
+ 'S-APP-OS': 'browser'
3422
+ }
3423
+ }
3424
+ });
3425
+ }
3426
+
3427
+ getHeaders() {
3428
+ return this.axios.defaults.headers.common;
3429
+ }
3430
+
3431
+ setHeaders(headers) {
3432
+ Object.entries(headers).forEach(header => this.setHeader(header[0], header[1]));
3433
+ return this;
3434
+ }
3435
+
3436
+ setHeader(header, value) {
3437
+ salla.infiniteScroll.fetchOptions.headers[header] = this.axios.defaults.headers.common[header] = value;
3438
+ return this;
3439
+ }
3440
+
3441
+ async withoutNotifier(callback) {
3442
+ this.notifier_handler_disabled = true;
3443
+ return await callback().finally(() => {
3444
+ this.notifier_handler_disabled = false;
3445
+ });
3446
+ }
3447
+
3448
+ initiateRequest() {
3449
+ this.axios.defaults.baseURL = Salla.config.get('store.api', Salla.config.get('store.url'));
3450
+ /**
3451
+ * About `s-user-id`
3452
+ * there is need for when login from api to get cart from web session_id not api session_id
3453
+ * @see AuthenticateCustomer::authenticate
3454
+ * @see MoveVisitorCartItemsToCustomerCart
3455
+ */
3456
+
3457
+ this.setHeaders({
3458
+ 'Store-Identifier': Salla.config.get('store.id'),
3459
+ 'currency': Salla.config.get('user.currency_code', 'SAR'),
3460
+ 'accept-language': salla.lang.getLocale(),
3461
+ 's-user-id': Salla.config.get('user.id')
3462
+ });
3463
+ let scope = salla.storage.get('scope');
3464
+
3465
+ if (scope) {
3466
+ this.setHeaders({
3467
+ 's-scope-type': scope.type,
3468
+ 's-scope-id': scope.id
3469
+ });
3470
+ }
3471
+
3472
+ this.injectTokenToTheRequests();
3473
+ }
3474
+
3475
+ injectTokenToTheRequests() {
3476
+ let token = salla.storage.get('token');
3477
+ let isGuest = Salla.config.isGuest();
3478
+ /** @type {CartSummary}*/
3479
+
3480
+ let cart = salla.storage.get('cart');
3481
+ /**
3482
+ * 🟠 Case 1: there are cart.id, but it's not linked for current user/session
3483
+ * 🔵 Action: reset cart.
3484
+ */
3485
+
3486
+ if (cart && (cart.user_id !== Salla.config.get('user.id') || cart.store_id !== Salla.config.get('store.id'))) {
3487
+ salla.log('Auth:: The cart is not belong to current ' + (cart.user_id !== Salla.config.get('user.id') ? 'user' : 'store') + '!');
3488
+ salla.cart.api.reset();
3489
+ return;
3490
+ }
3491
+ /**
3492
+ * 🟠 Case 2: current user is guest and there is no token.
3493
+ * 🔵 Action: Do nothing.
3494
+ */
3495
+
3496
+
3497
+ if (isGuest && !token) {
3498
+ return;
3499
+ }
3500
+ /**
3501
+ * 🟠 Case 3: there is a token and current user is guest
3502
+ * 🔵 Action: remove the token & reset cart.
3503
+ */
3504
+
3505
+
3506
+ if (isGuest && token) {
3507
+ salla.log('Auth:: Token without user!');
3508
+ salla.storage.remove('token');
3509
+ salla.cart.api.reset();
3510
+ return;
3511
+ } //======== following cases are when user is logged in ========//
3512
+
3513
+ /**
3514
+ * 🟠 Case 4: current user is logged-in but there is no token.
3515
+ * 🔵 Action: reset cart & get token.
3516
+ */
3517
+
3518
+
3519
+ if (!token) {
3520
+ salla.cart.api.reset();
3521
+ salla.auth.api.refresh();
3522
+ return;
3523
+ }
3524
+ /**
3525
+ * 🟠 Case 5: current user is logged-in and there is a token, but it's expired.
3526
+ * 🔵 Action: reset cart & get new token.
3527
+ */
3528
+
3529
+ /** @type {{sub: number, nbf: number, prv: string, iss: string, exp: number, iat: number, jti: string}} */
3530
+
3531
+
3532
+ let jwt = jwtDecode(token);
3533
+
3534
+ if (Date.now() / 1000 > jwt.exp) {
3535
+ salla.log('Auth:: An expired token!');
3536
+ salla.storage.remove('token');
3537
+ salla.cart.api.reset();
3538
+ salla.auth.api.refresh();
3539
+ return;
3540
+ }
3541
+ /**
3542
+ * 🟠 Case 6: current user is logged-in and there is a token, but it's not for current user.
3543
+ * 🔵 Action: reset cart & get new token.
3544
+ */
3545
+
3546
+
3547
+ if (jwt.sub !== Salla.config.get('user.id')) {
3548
+ salla.log('Auth:: The user id is not match the token details!');
3549
+ salla.storage.remove('token');
3550
+ salla.cart.api.reset();
3551
+ salla.auth.api.refresh();
3552
+ return;
3553
+ }
3554
+ /**
3555
+ * 🟠 Case 7: current user is logged-in and there is a token, and it's not expired.
3556
+ * 🔵 Action: set the token to all twilight requests.
3557
+ */
3558
+
3559
+
3560
+ this.setToken(token);
3561
+ }
3562
+
3563
+ setToken(token) {
3564
+ return this.setHeader('Authorization', 'Bearer ' + token);
3565
+ }
3566
+ /**
3567
+ *
3568
+ * @param {ProfileResponse} response
3569
+ */
3570
+
3571
+
3572
+ setCurrencyAndLanguage(response) {
3573
+ return this.setHeaders({
3574
+ 'currency': response.data.currency,
3575
+ 'accept-language': response.data.language
3576
+ });
3577
+ }
3578
+ /**
3579
+ * @param {string[]|string} endPoint - pass [url|endpoint|array(url|endpoint, urlParam)]
3580
+ * @param {Object|undefined} formData
3581
+ * @param {string|null} method
3582
+ * @param options
3583
+ * @return {Promise}
3584
+ */
3585
+
3586
+
3587
+ request(endPoint, formData = undefined, method = 'get', options = {}) {
3588
+ /**
3589
+ * in case you need to prevent request by its payload, just listen to this event, using:
3590
+ * salla.document.event.onRequest(data=>{
3591
+ * //your logic here..., if it should stop, just do what you want (showing message, doing action) then throw an exception like:
3592
+ * throw 'Error message';
3593
+ * });
3594
+ */
3595
+
3596
+ /**
3597
+ *
3598
+ * @type {RequestInfo}
3599
+ */
3600
+ let request = {
3601
+ endPoint: endPoint,
3602
+ payload: formData,
3603
+ method: method,
3604
+ options: options
3605
+ }; // it's important to keep the current target in local var
3606
+ // because we will lose it from the event when the request start
3607
+
3608
+ let eventTarget = typeof event !== 'undefined' ? event.currentTarget : null; // we need to find the event button to have the loading effect
3609
+
3610
+ let loaderSupported = false;
3611
+
3612
+ if (eventTarget?.tagName === 'SALLA-BUTTON') {
3613
+ loaderSupported = true;
3614
+ }
3615
+
3616
+ if (loaderSupported) {
3617
+ eventTarget?.load();
3618
+ }
3619
+
3620
+ return this.axios[request.method](request.endPoint, request.payload, request.options).then(response => {
3621
+ if (loaderSupported) {
3622
+ eventTarget?.stop();
3623
+ } //instead of passing all axios object, we will pass response data only
3624
+
3625
+
3626
+ if (response.data && response.request) {
3627
+ response = response.data;
3628
+ }
3629
+
3630
+ this.handleAfterResponseActions(response);
3631
+ return response;
3632
+ }).catch(error => {
3633
+ if (loaderSupported) {
3634
+ eventTarget?.stop();
3635
+ }
3636
+
3637
+ salla.event.document.requestFailed(request, error);
3638
+ this.handleErrorResponse(error);
3639
+ throw error;
3640
+ });
3641
+ }
3642
+ /**
3643
+ * @param {SuccessResponse|ErrorResponse} response
3644
+ */
3645
+
3646
+
3647
+ handleAfterResponseActions(response) {
3648
+ if (!response) {
3649
+ return;
3650
+ } // let's get the data from data or from the response its self ..
3651
+
3652
+
3653
+ let {
3654
+ data,
3655
+ googleTags = null
3656
+ } = response;
3657
+ let withGoogleTags = data && data.googleTags ? data.googleTags : googleTags;
3658
+
3659
+ if (dataLayer && withGoogleTags) {
3660
+ // let's get going and push it .
3661
+ dataLayer.push(withGoogleTags);
3662
+ }
3663
+
3664
+ this.fireEventsForResponse(response);
3665
+ this.showAlert(response);
3666
+ /**
3667
+ * @deprecated don't use it
3668
+ */
3669
+
3670
+ this.renderSections(response);
3671
+ }
3672
+ /**
3673
+ * @param {SuccessResponse|ErrorResponse} response
3674
+ */
3675
+
3676
+
3677
+ fireEventsForResponse(response) {
3678
+ // todo:: this is the old way (response.events), also string, so need to be dropped
3679
+ let events = response?.events || response.data?.events || response.error?.events;
3680
+
3681
+ if (typeof events === 'string') {
3682
+ events = JSON.parse(events);
3683
+ }
3684
+
3685
+ events && Object.keys(events).forEach(event => salla.event.dispatch(event, events[event]));
3686
+ }
3687
+ /**
3688
+ * @param {Error} error
3689
+ */
3690
+
3691
+
3692
+ handleErrorResponse(error) {
3693
+ //there is no response!
3694
+ if (!error.response || !error.response.data) {
3695
+ return;
3696
+ } //if there is fields means we should handle them differently
3697
+
3698
+
3699
+ if (error.response.data.error && error.response.data.error.fields && !this.notifier_handler_disabled) {
3700
+ return this.handleInvalidFields(error);
3701
+ }
3702
+
3703
+ return this.handleAfterResponseActions(error.response.data);
3704
+ }
3705
+
3706
+ showAlert(response) {
3707
+ //if there is no case or there is no msg, return early
3708
+ if (!response || this.notifier_handler_disabled) {
3709
+ return;
3710
+ } //success response type 1
3711
+ //todo::Drop this type
3712
+ //todo:: ability to send sentry
3713
+
3714
+
3715
+ if (response.case && response.msg) {
3716
+ return salla.notify.fire(response.msg, response.case, response);
3717
+ } //success response type 2
3718
+
3719
+
3720
+ if (response.hasOwnProperty('success') && response.data?.message) {
3721
+ return salla.notify.fire(response.data?.message, response.success ? salla.notify.types.success : salla.notify.types.error, response);
3722
+ } //error response type 1
3723
+
3724
+
3725
+ if (response.error && response.error.message && response.error.message !== 'FORBIDDEN') {
3726
+ return salla.error(response.error.message, response);
3727
+ }
3728
+ }
3729
+
3730
+ handleInvalidFields(error) {
3731
+ // let's collect fields errors & show them as one error list;
3732
+ let errors = error.response.data.error.fields;
3733
+ let errorsMsgs = [];
3734
+ Object.keys(errors).forEach(errorName => {
3735
+ let errorMsg = errors[errorName];
3736
+
3737
+ if (Array.isArray(errorMsg)) {
3738
+ errorMsg.forEach(errorMsg_ => errorsMsgs.push(errorMsg_));
3739
+ } else {
3740
+ errorsMsgs.push(errorMsg);
3741
+ }
3742
+ });
3743
+ let errorMsg = (errorsMsgs.length > 1 ? '* ' : '') + errorsMsgs.join("\n* ");
3744
+ salla.error(errorMsg, error);
3745
+ }
3746
+
3747
+ isFastRequestsAllowed() {
3748
+ return Salla.config.get('fastRequests');
3749
+ }
3750
+
3751
+ promise(data, isResolve = true) {
3752
+ return new Promise((resolve, reject) => isResolve ? resolve(data) : reject(data));
3753
+ }
3754
+
3755
+ errorPromise(data) {
3756
+ return this.promise(data, false);
3757
+ }
3758
+
3759
+ successPromise(data) {
3760
+ return this.promise(data, true);
3761
+ }
3762
+ /**
3763
+ * @deprecated don't use it.
3764
+ * @param response
3765
+ */
3766
+
3767
+
3768
+ renderSections(response) {
3769
+ if (!response.sections) {
3770
+ return;
3771
+ }
3772
+
3773
+ Object.keys(response.sections).forEach(section => {
3774
+ document.querySelectorAll(section.toSelector()).forEach(function (sectionElement) {
3775
+ sectionElement.innerHTML = response.sections[section];
3776
+ });
3777
+ });
3778
+ }
3779
+
3780
+ }
3781
+
3782
+ /**
3783
+ * @property {CartApi} cart
3784
+ * @property {LoyaltyApi} Loyalty
3785
+ * @property {AuthApi} auth
3786
+ */
3787
+
3788
+ class Api extends request {
3789
+ constructor() {
3790
+ super();
3791
+ this.auth = new AuthApi();
3792
+ this.cart = new CartApi();
3793
+ this.loyalty = new LoyaltyApi();
3794
+ this.order = new OrderApi();
3795
+ this.rating = new RatingApi();
3796
+ this.product = new ProductApi();
3797
+ this.profile = new ProfileApi();
3798
+ this.comment = new CommentApi();
3799
+ this.currency = new CurrencyApi();
3800
+ this.document = new DocumentApi();
3801
+ this.wishlist = new WishlistApi();
3802
+ this.scope = new ScopeApi();
3803
+ }
3804
+
3805
+ }
3806
+
3807
+ class Init {
3808
+ /**
3809
+ * @param {TwilightConfig} config
3810
+ */
3811
+ constructor(config) {
3812
+ if (!config) {
3813
+ Salla$1.event.dispatch('twilight::initiated', config);
3814
+ return;
3815
+ } // todo :: cleanup events before the merge
3816
+
3817
+
3818
+ Salla$1.config.merge(config);
3819
+ config?.events && Salla$1.event.dispatchEvents(config?.events);
3820
+
3821
+ if (config._token) {
3822
+ salla.api.setHeader("X-CSRF-TOKEN", config._token);
3823
+ }
3824
+
3825
+ this.injectMaintenanceAlert();
3826
+ this.injectThemePreviewAlert();
3827
+ this.injectEditAlert();
3828
+
3829
+ if (config?.user?.language_code) {
3830
+ salla.lang.setLocale(config?.user?.language_code);
3831
+ }
3832
+
3833
+ salla.lang.loadStoreTranslations();
3834
+ Salla$1.event.dispatch('twilight::initiated', config);
3835
+ }
3836
+
3837
+ injectMaintenanceAlert() {
3838
+ if (!salla.config.get('maintenance')) {
3839
+ return;
3840
+ }
3841
+
3842
+ if (document.querySelector('.store-notify')) {
3843
+ salla.logger.warn('.store-notify element Existed before!');
3844
+ return;
3845
+ }
3846
+
3847
+ salla.lang.onLoaded(() => {
3848
+ const alertElement = document.createElement('div');
3849
+ alertElement.classList.add('store-notify');
3850
+ alertElement.style = "background-color: #d63031; color: #fff; padding: 10px 15px; text-align: center; font-size: 17px;";
3851
+ alertElement.innerHTML = `<p style="margin:0 !important;">${salla.lang.get('blocks.header.maintenance_alert')}</p>`;
3852
+ document.body.prepend(alertElement);
3853
+ });
3854
+ }
3855
+
3856
+ injectThemePreviewAlert() {
3857
+ if (salla.config.get('theme.mode') !== 'preview') {
3858
+ return;
3859
+ }
3860
+
3861
+ if (document.querySelector('#s-theme_preview_bar')) {
3862
+ salla.logger.warn('#s-theme_preview_bar element Existed before!');
3863
+ return;
3864
+ }
3865
+
3866
+ salla.lang.onLoaded(() => {
3867
+ let alertElement = document.createElement('div');
3868
+ alertElement.id = 's-theme_preview_bar';
3869
+ alertElement.setAttribute('style', 'display: flex; justify-content: space-between; text-align: center; background-color: #232323; color: #fff; padding: 10px; font-size: 0.875rem; line-height: 1.25rem; position: relative;');
3870
+ alertElement.innerHTML = `
3871
+ <div style="display:flex; align-items:center;">
3872
+ <img width="32" src="https://assets.salla.sa/cp/assets/images/logo-new.png">
3873
+ <span style="margin:0 10px;">${salla.lang.get('blocks.header.preview_mode')}: <span style="background:rgba(255,255,255,0.25);border-radius:15px; padding:2px 15px 4px">${salla.config.get('theme.name')}</span></span>
3874
+ </div>
3875
+ <a href="${salla.url.get('preview_theme/cancel/preview')}" style="line-height:32px; width:32px;"><i class="sicon-cancel"></i></a>
3876
+ `;
3877
+ document.body.prepend(alertElement);
3878
+ });
3879
+ }
3880
+ /**
3881
+ * @deprecated
3882
+ */
3883
+
3884
+
3885
+ injectEditAlert() {
3886
+ let edit = salla.config.get('edit');
3887
+
3888
+ if (!edit) {
3889
+ return;
3890
+ }
3891
+
3892
+ if (document.querySelector('#s-edit-alert')) {
3893
+ salla.logger.warn('#s-edit-alert element Existed before!');
3894
+ return;
3895
+ }
3896
+
3897
+ salla.lang.onLoaded(() => {
3898
+ let alertElement = document.createElement('div');
3899
+ alertElement.id = 's-edit-alert';
3900
+ alertElement.innerHTML = `
3901
+ <a href="${edit}" style="display:block; background-color:${salla.config.get('theme.color.primary', '#5cd5c4')}; color:${salla.config.get('theme.color.reverse', '#fff')}; padding: 10px; text-align:center; font-size: 0.875rem; line-height: 1.25rem;">
3902
+ <i class="sicon-edit"></i>
3903
+ ${salla.lang.get('pages.products.quick_edit')}
3904
+ </a>
3905
+ `;
3906
+ document.body.prepend(alertElement);
3907
+ });
3908
+ }
3909
+ /**
3910
+ * Link Action Of forms & links that have '.ajax' class.
3911
+ *
3912
+ * @param {Event} event
3913
+ * @param {Element} element
3914
+ */
3915
+
3916
+
3917
+ handleElementAjaxRequest(event, element) {
3918
+ if (!(element instanceof HTMLFormElement || element instanceof HTMLAnchorElement)) {
3919
+ salla.logger.warn('trying to call ajax from non Element!!');
3920
+ return false;
3921
+ }
3922
+
3923
+ event.preventDefault(); // todo :: support async to allow for complex logic before the submit which take a time
3924
+
3925
+ let data = element.getAjaxFormData(event);
3926
+ let method = data.method ? data.method.toLowerCase() : undefined;
3927
+ salla.api.request(data.url, data.formData, method).then(response => {
3928
+ //instead of passing all axios object, we will pass response data only
3929
+ if (response.data && response.request) {
3930
+ response = response.data;
3931
+ }
3932
+
3933
+ salla.api.handleAfterResponseActions(response);
3934
+ this.callAjaxEvent(data.events.success, response, data.formData);
3935
+ return response;
3936
+ }).catch(error => {
3937
+ salla.api.handleErrorResponse(error);
3938
+ this.callAjaxEvent(data.events.fail, error, data.formData);
3939
+ throw error;
3940
+ });
3941
+ }
3942
+
3943
+ callAjaxEvent(event, response, formData) {
3944
+ if (!event) {
3945
+ return;
3946
+ }
3947
+
3948
+ if (formData instanceof FormData) {
3949
+ const parameters = {};
3950
+ Array.from(formData.entries()).forEach(function (data) {
3951
+ parameters[data[0]] = data[1];
3952
+ });
3953
+ formData = parameters;
3954
+ }
3955
+
3956
+ if (window[event]) {
3957
+ window[event](response, formData);
3958
+ return;
3959
+ }
3960
+
3961
+ salla.event.dispatch(event, response, formData);
3962
+ }
3963
+
3964
+ }
3965
+
3966
+ Salla$1.notify = Notify;
3967
+ Salla$1.lang = new SallaLang();
3968
+ Salla$1.form = new Form();
3969
+ Salla$1.AppHelpers = AppHelpers;
3970
+ Salla$1.infiniteScroll = new InfiniteScroll(undefined, salla.event.infiniteScroll);
3971
+ Salla$1.api = new Api(); // alies access to the module have API & Event class
3972
+
3973
+ Salla$1.cart = new Module(Salla$1.api.cart, Salla$1.event.cart);
3974
+ Salla$1.auth = new Module(Salla$1.api.auth, Salla$1.event.auth);
3975
+ Salla$1.order = new Module(Salla$1.api.order, Salla$1.event.order);
3976
+ Salla$1.scope = new Module(Salla$1.api.scope, Salla$1.event.scope);
3977
+ Salla$1.rating = new Module(Salla$1.api.rating, Salla$1.event.rating);
3978
+ Salla$1.comment = new Module(Salla$1.api.comment, Salla$1.event.comment);
3979
+ Salla$1.loyalty = new Module(Salla$1.api.loyalty, Salla$1.event.loyalty);
3980
+ Salla$1.product = new Module(Salla$1.api.product, Salla$1.event.product);
3981
+ Salla$1.profile = new Module(Salla$1.api.profile, Salla$1.event.profile);
3982
+ Salla$1.currency = new Module(Salla$1.api.currency, Salla$1.event.currency);
3983
+ Salla$1.document = new Module(Salla$1.api.document, Salla$1.event.document);
3984
+ Salla$1.wishlist = new Module(Salla$1.api.wishlist, Salla$1.event.wishlist);
3985
+
3986
+ Salla$1.call = action => {
3987
+ let object = Salla$1;
3988
+ let arr = action.split('.');
3989
+
3990
+ while (arr.length && (object = object[arr.shift()]));
3991
+
3992
+ return object;
3993
+ }; // alias for quick access
3994
+
3995
+
3996
+ Salla$1.init = config => new Init(config);
3997
+
3998
+ salla.event.once('twilight::init', salla.init);
3999
+ Salla$1.success = Salla$1.notify.success;
4000
+ Salla$1.error = Salla$1.notify.error;
4001
+ Salla$1.versions.twilight = '[VI]{version}[/VI]';
4002
+ Salla$1.event.dispatch('twilight::ready');
4003
+
4004
+ Salla$1.onInitiated = callback => salla.event.once('twilight::initiated', callback);
4005
+ /**
4006
+ * @deprecated use onInitiated
4007
+ * @type {function(*): TwilightEmitter | Emitter | Listener}
4008
+ */
4009
+
4010
+
4011
+ Salla$1.onReady = Salla$1.onInitiated;
2
4012
  //# sourceMappingURL=index.js.map