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