EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD
EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD
EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD
EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD
EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD
EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD
EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD
EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD
EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD
EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD
EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD
EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD
EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD
EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD
EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD
EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD
EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD
EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD
EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD
EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD
EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD
EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD

EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD

Price

$289.00 $289.00
Save 0%
const TAG = "spz-custom-product-automatic"; class SpzCustomProductAutomatic extends SPZ.BaseElement { constructor(element) { super(element); this.variant_id = '192842b7-6ae1-499a-a174-fc6657bb4c57'; this.isRTL = SPZ.win.document.dir === 'rtl'; this.isAddingToCart_ = false; // 加购中状态 } static deferredMount() { return false; } buildCallback() { this.action_ = SPZServices.actionServiceForDoc(this.element); this.templates_ = SPZServices.templatesForDoc(this.element); this.xhr_ = SPZServices.xhrFor(this.win); this.setupAction_(); this.viewport_ = this.getViewport(); } mountCallback() { this.init(); // 监听事件 this.bindEvent_(); } async init() { this.handleFitTheme(); const data = await this.getDiscountList(); this.renderApiData_(data); } async getDiscountList() { const productId = '348f2b2c-261b-4a05-895e-5e86f03ac7e3'; const variantId = this.variant_id; const productType = 'default'; const reqBody = { product_id: productId, variant_id: variantId, discount_method: "DM_AUTOMATIC", customer: { customer_id: window.C_SETTINGS.customer.customer_id, email: window.C_SETTINGS.customer.customer_email }, product_type: productType } const url = `/api/storefront/promotion/display_setting/text/list`; const data = await this.xhr_.fetchJson(url, { method: "post", body: reqBody }).then(res => { return res; }).catch(err => { this.setContainerDisabled(false); }) return data; } async renderDiscountList() { this.setContainerDisabled(true); const data = await this.getDiscountList(); this.setContainerDisabled(false); // 重新渲染 抖动问题处理 this.renderApiData_(data); } clearDom() { const children = this.element.querySelector('*:not(template)'); children && SPZCore.Dom.removeElement(children); } async renderApiData_(data) { const parentDiv = document.querySelector('.automatic_discount_container'); const newTplDom = await this.getRenderTemplate(data); if (parentDiv) { parentDiv.innerHTML = ''; parentDiv.appendChild(newTplDom); } else { console.log('automatic_discount_container is null'); } } doRender_(data) { const renderData = data || {}; return this.templates_ .findAndRenderTemplate(this.element, renderData) .then((el) => { this.clearDom(); this.element.appendChild(el); }); } async getRenderTemplate(data) { const renderData = data || {}; return this.templates_ .findAndRenderTemplate(this.element, { ...renderData, isRTL: this.isRTL }) .then((el) => { this.clearDom(); return el; }); } setContainerDisabled(isDisable) { const automaticDiscountEl = document.querySelector('.automatic_discount_container_outer'); if(isDisable) { automaticDiscountEl.setAttribute('disabled', ''); } else { automaticDiscountEl.removeAttribute('disabled'); } } // 绑定事件 bindEvent_() { window.addEventListener('click', (e) => { let containerNodes = document.querySelectorAll(".automatic-container .panel"); let bool; Array.from(containerNodes).forEach((node) => { if(node.contains(e.target)){ bool = true; } }) // 是否popover面板点击范围 if (bool) { return; } if(e.target.classList.contains('drowdown-icon') || e.target.parentNode.classList.contains('drowdown-icon')){ return; } const nodes = document.querySelectorAll('.automatic-container'); Array.from(nodes).forEach((node) => { node.classList.remove('open-dropdown'); }) // 兼容主题 this.toggleProductSticky(true); }) // 监听变体变化 document.addEventListener('dj.variantChange', async(event) => { // 重新渲染 const variant = event.detail.selected; if (variant.product_id == '348f2b2c-261b-4a05-895e-5e86f03ac7e3' && variant.id != this.variant_id) { this.variant_id = variant.id; this.renderDiscountList(); } }); } // 兼容主题 handleFitTheme() { // top 属性影响抖动 let productInfoEl = null; if (window.SHOPLAZZA.theme.merchant_theme_name === 'Wind' || window.SHOPLAZZA.theme.merchant_theme_name === 'Flash') { productInfoEl = document.querySelector('.product-info-body .product-sticky-container'); } else if (window.SHOPLAZZA.theme.merchant_theme_name === 'Hero') { productInfoEl = document.querySelector('.product__info-wrapper .properties-content'); } if(productInfoEl){ productInfoEl.classList.add('force-top-auto'); } } // 兼容 wind/flash /hero 主题 (sticky属性影响 popover 层级展示, 会被其他元素覆盖) toggleProductSticky(isSticky) { let productInfoEl = null; if (window.SHOPLAZZA.theme.merchant_theme_name === 'Wind' || window.SHOPLAZZA.theme.merchant_theme_name === 'Flash') { productInfoEl = document.querySelector('.product-info-body .product-sticky-container'); } else if (window.SHOPLAZZA.theme.merchant_theme_name === 'Hero') { productInfoEl = document.querySelector('.product__info-wrapper .properties-content'); } if(productInfoEl){ if(isSticky) { // 还原该主题原有的sticky属性值 productInfoEl.classList.remove('force-position-static'); return; } productInfoEl.classList.toggle('force-position-static'); } } setupAction_() { this.registerAction('handleDropdown', (invocation) => { const discount_id = invocation.args.discount_id; const nodes = document.querySelectorAll('.automatic-container'); Array.from(nodes).forEach((node) => { if(node.getAttribute('id') != `automatic-${discount_id}`) { node.classList.remove('open-dropdown'); } }) const $discount_item = document.querySelector(`#automatic-${discount_id}`); $discount_item && $discount_item.classList.toggle('open-dropdown'); // 兼容主题 this.toggleProductSticky(); }); // 加购事件 this.registerAction('handleAddToCart', (invocation) => { // 阻止事件冒泡 const event = invocation.event; if (event) { event.stopPropagation(); event.preventDefault(); } // 如果正在加购中,直接返回 if (this.isAddingToCart_) { return; } const quantity = invocation.args.quantity || 1; this.addToCart(quantity); }); } // 加购方法 async addToCart(quantity) { // 设置加购中状态 this.isAddingToCart_ = true; const productId = '348f2b2c-261b-4a05-895e-5e86f03ac7e3'; const variantId = this.variant_id; const url = '/api/cart'; const reqBody = { product_id: productId, variant_id: variantId, quantity: quantity }; try { const data = await this.xhr_.fetchJson(url, { method: 'POST', body: reqBody }); // 触发加购成功提示 this.triggerAddToCartToast_(); return data; } catch (error) { error.then(err=>{ this.showToast_(err?.message || err?.errors?.[0] || 'Unknown error'); }) } finally { // 无论成功失败,都重置加购状态 this.isAddingToCart_ = false; } } showToast_(message) { const toastEl = document.querySelector("#apps-match-drawer-add_to_cart_toast"); if (toastEl) { SPZ.whenApiDefined(toastEl).then((apis) => { apis.showToast(message); }); } } // 触发加购成功提示 triggerAddToCartToast_() { // 如果主题有自己的加购提示,则不显示 const themeAddToCartToastEl = document.querySelector('#add-cart-event-proxy'); if (themeAddToCartToastEl) return; // 显示应用的加购成功提示 this.showToast_("Added successfully"); } triggerEvent_(name, data) { const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {}); this.action_.trigger(this.element, name, event); } isLayoutSupported(layout) { return layout == SPZCore.Layout.CONTAINER; } } SPZ.defineElement(TAG, SpzCustomProductAutomatic);
class SpzCustomDiscountBundle extends SPZ.BaseElement { constructor(element) { super(element); } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } mountCallback() {} unmountCallback() {} setupAction_() { this.registerAction('showAddToCartToast', () => { const themeAddToCartToastEl = document.querySelector('#add-cart-event-proxy') if(themeAddToCartToastEl) return const toastEl = document.querySelector('#apps-match-drawer-add_to_cart_toast') SPZ.whenApiDefined(toastEl).then((apis) => { apis.showToast("Added successfully"); }); }); } buildCallback() { this.setupAction_(); }; } SPZ.defineElement('spz-custom-discount-toast', SpzCustomDiscountBundle);
/** * 优惠码组件模型类 * 处理优惠码的显示和交互逻辑 */ class SpzCustomDiscountCodeModel extends SPZ.BaseElement { constructor(element) { super(element); // 复制按钮和内容的类名 this.copyBtnClass = "discount_code_btn" this.copyClass = "discount_code_value" } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } buildCallback() { // 初始化服务 this.action_ = SPZServices.actionServiceForDoc(this.element); this.templates_ = SPZServices.templatesForDoc(this.element); this.xhr_ = SPZServices.xhrFor(this.win); } /** * 渲染优惠码组件 * @param {Object} data - 渲染数据 */ doRender_(data) { return this.templates_ .findAndRenderTemplate(this.element, Object.assign(this.getDefaultData(), data) ) .then((el) => { this.clearDom(); this.element.appendChild(el); // 绑定复制代码功能 this.copyCode(el, data); }); } /** * 获取渲染模板 * @param {Object} data - 渲染数据 */ getRenderTemplate(data) { const renderData = Object.assign(this.getDefaultData(), data); return this.templates_ .findAndRenderTemplate(this.element, renderData) .then((el) => { this.clearDom(); return el; }); } /** * 清除DOM内容 */ clearDom() { const children = this.element.querySelector('*:not(template)'); children && SPZCore.Dom.removeElement(children); } /** * 获取默认数据 * @returns {Object} 默认数据对象 */ getDefaultData() { return { isMobile: appDiscountUtils.judgeMobile(), isRTL: appDiscountUtils.judgeRTL(), image_domain: this.win.SHOPLAZZA.image_domain, copyBtnClass: this.copyBtnClass, copyClass: this.copyClass } } /** * 复制优惠码功能 * @param {Element} el - 当前元素 */ copyCode(el) { const copyBtnList = el.querySelectorAll(`.${this.copyBtnClass}`); if (copyBtnList.length > 0) { copyBtnList.forEach(item => { item.onclick = async () => { // 确保获取正确的元素和内容 const codeElement = item.querySelector(`.${this.copyClass}`); if (!codeElement) return; // 获取纯文本内容 const textToCopy = codeElement.innerText.trim(); // 尝试使用现代API,如果失败则使用备用方案 try { if (navigator.clipboard && navigator.clipboard.writeText) { await navigator.clipboard.writeText(textToCopy); } else { throw new Error('Clipboard API not available'); } // 显示复制成功提示 this.showCopySuccessToast(textToCopy, el); } catch (err) { console.error('Modern clipboard API failed, trying fallback...', err); // 使用备用复制方案 this.fallbackCopy(textToCopy, el); } const discountId = item.dataset["discountId"]; // 是否跳转落地页配置 const redirection = item.dataset["redirection"] === "true"; // 跳转到落地页 if (redirection && appDiscountUtils.inProductBody(this.element)) { this.win.open(`/promotions/discount-default/${discountId}`); } } }) } } /** * 使用 execCommand 的复制方案 * @param {string} codeText - 要复制的文本 * @param {Element} el - 当前元素 */ fallbackCopy(codeText, el) { const textarea = this.win.document.createElement('textarea'); textarea.value = codeText; // 设置样式使文本框不可见 textarea.style.position = 'fixed'; textarea.style.left = '-9999px'; textarea.style.top = '0'; // 添加 readonly 属性防止移动端虚拟键盘弹出 textarea.setAttribute('readonly', 'readonly'); this.win.document.body.appendChild(textarea); textarea.focus(); textarea.select(); try { this.win.document.execCommand('copy'); // 显示复制成功提示 this.showCopySuccessToast(codeText, el); } catch (err) { console.error('Copy failed:', err); } this.win.document.body.removeChild(textarea); } /** * 创建 Toast 元素 * @returns {Element} 创建的 Toast 元素 */ createToastEl_() { const toast = document.createElement('ljs-toast'); toast.setAttribute('layout', 'nodisplay'); toast.setAttribute('hidden', ''); toast.setAttribute('id', 'discount-code-toast'); toast.style.zIndex = '1051'; return toast; } /** * 挂载 Toast 元素到 body * @returns {Element} 挂载的 Toast 元素 */ mountToastToBody_() { const existingToast = this.win.document.getElementById('discount-code-toast'); if (existingToast) { return existingToast; } const toast = this.createToastEl_(); this.win.document.body.appendChild(toast); return toast; } /** * 复制成功的提醒 * @param {string} codeText - 要复制的文本 * @param {Element} el - 当前元素 */ showCopySuccessToast(codeText, el) { const $toast = this.mountToastToBody_(); SPZ.whenApiDefined($toast).then(toast => { toast.showToast("Discount code copied !"); this.codeCopyInSessionStorage(codeText); }); } /** * 复制优惠码成功后要存一份到本地存储中,购物车使用 * @param {string} codeText - 要复制的文本 */ codeCopyInSessionStorage(codeText) { try { sessionStorage.setItem('other-copied-coupon', codeText); } catch (error) { console.error(error) } } } // 注册自定义元素 SPZ.defineElement('spz-custom-discount-code-model', SpzCustomDiscountCodeModel);
/** * Custom discount code component that handles displaying and managing discount codes * @extends {SPZ.BaseElement} */ class SpzCustomDiscountCode extends SPZ.BaseElement { constructor(element) { super(element); // API endpoint for fetching discount codes this.getDiscountCodeApi = "\/api\/storefront\/promotion\/code\/list"; // Debounce timer for resize events this.timer = null; // Current variant ID this.variantId = "192842b7-6ae1-499a-a174-fc6657bb4c57"; // Store discount code data this.discountCodeData = {} } /** * Check if layout is supported * @param {string} layout - Layout type * @return {boolean} */ isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } /** * Initialize component after build */ buildCallback() { this.templates_ = SPZServices.templatesForDoc(); this.viewport_ = this.getViewport(); // Bind methods to maintain context this.render = this.render.bind(this); this.resize = this.resize.bind(this); this.switchVariant = this.switchVariant.bind(this); } /** * Setup component when mounted */ mountCallback() { this.getData(); // Add event listeners this.viewport_.onResize(this.resize); this.win.document.addEventListener('dj.variantChange', this.switchVariant); } /** * Cleanup when component is unmounted */ unmountCallback() { this.viewport_.removeResize(this.resize); this.win.document.removeEventListener('dj.variantChange', this.switchVariant); // 清除定时器 if (this.timer) { clearTimeout(this.timer); this.timer = null; } } /** * Handle resize events with debouncing */ resize() { if (this.timer) { clearTimeout(this.timer) this.timer = null; } this.timer = setTimeout(() => { if (appDiscountUtils.inProductBody(this.element)) { this.render(); } else { this.renderSkeleton(); } }, 200); } /** * Handle variant changes * @param {Event} event - Variant change event */ switchVariant(event) { const variant = event.detail.selected; if (variant.product_id == '348f2b2c-261b-4a05-895e-5e86f03ac7e3' && variant.id != this.variantId) { this.variantId = variant.id; this.getData(); } } /** * Fetch discount code data from API */ getData() { if (appDiscountUtils.inProductBody(this.element)) { const reqBody = { product_id: "348f2b2c-261b-4a05-895e-5e86f03ac7e3", variant_id: this.variantId, product_type: "default", } if (!reqBody.product_id || !reqBody.variant_id) return; this.discountCodeData = {}; this.win.fetch(this.getDiscountCodeApi, { method: "POST", body: JSON.stringify(reqBody), headers: { "Content-Type": "application/json" } }).then(async (response) => { if (response.ok) { let data = await response.json(); if (data.list && data.list.length > 0) { data.list[0].product_setting.template_config = JSON.parse(data.list[0].product_setting.template_config); // Format timestamps to local timezone const zone = this.win.SHOPLAZZA.shop.time_zone; data.list = data.list.map(item => { if(+item.ends_at !== -1) { item.ends_at = appDiscountUtils.convertTimestampToFormat(+item.ends_at, zone); } item.starts_at = appDiscountUtils.convertTimestampToFormat(+item.starts_at, zone); return item; }); } this.discountCodeData = data; this.render(); } else { this.clearDom(); } }).catch(err => { console.error("discount_code", err) this.clearDom(); }); } else { this.renderSkeleton(); } } /** * Clear component DOM except template */ clearDom() { const children = this.element.querySelector('*:not(template)'); children && SPZCore.Dom.removeElement(children); } /** * Render discount codes with formatted dates */ render() { // Render using discount code model SPZ.whenApiDefined(document.querySelector('#spz_custom_discount_code_model')).then(renderApi => { renderApi.doRender_({ discountCodeData: this.discountCodeData }) }).catch(err => { this.clearDom(); }) } renderSkeleton() { // Render template for non-product pages this.templates_ .findAndRenderTemplate(this.element, { isMobile: appDiscountUtils.judgeMobile() }) .then((el) => { this.clearDom(); this.element.appendChild(el); }) .catch(err => { this.clearDom(); }); } } // Register custom element SPZ.defineElement('spz-custom-discount-code', SpzCustomDiscountCode);

Quantity

const getPluginI18nMessages = (message, replaceObj = {}) => { const lang = document.documentElement.lang || "en-US"; const [form, key] = message.split('.') let text = window.payment_plugin_message['en-US'][form][key]; if (window.payment_plugin_message[lang][form].hasOwnProperty(key)) { text = window.payment_plugin_message[lang][form][key]; } Object.keys(replaceObj).forEach(key => { text = text.replace(new RegExp(`\{${key}\}`, 'gi'), replaceObj[key]); }) return text; } const zhCN = { ec: { not_active_channel: "请到收款设置中{channelName}或在「快捷支付按钮」设置中选择其他的服务提供方,否则按钮将无法展示", not_support_theme: "当前主题不支持添加「快捷支付按钮」", more_button: "更多支付方式", skeleton_layer_tips_title: "快捷支付按钮", skeleton_layer_tips_content: "请点击左侧列表中的「快捷支付按钮」,在设置页面开启想要的展示的支付按钮", mock_tips: "快捷支付按钮是否展示还取决于买家使用的浏览器以及商品的货币、金额", not_find_form_tips: "快捷支付按钮组件仅支持配置到商品详情卡片内", } }; const zhTW = { ec: { not_active_channel: "请到收款设置中{channelName}或在「快捷支付按钮」设置中选择其他的服务提供方,否则按钮将无法展示", not_support_theme: "当前主题不支持添加「快捷支付按钮」", more_button: "更多付款方式", } }; const arSA = { ec: { not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.", not_support_theme: "This Theme doesn't support adding「Express checkout button」", more_button: "المزيد من خيارات الدفع", } }; const deDE = { ec: { not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.", not_support_theme: "This Theme doesn't support adding「Express checkout button」", more_button: "Weitere Bezahlmöglichkeiten", } }; const esES = { ec: { not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.", not_support_theme: "This Theme doesn't support adding「Express checkout button」", more_button: "Más opciones de pago", } }; const frFR = { ec: { not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.", not_support_theme: "This Theme doesn't support adding「Express checkout button」", more_button: "Plus d'options de paiement", } }; const idID = { ec: { not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.", not_support_theme: "This Theme doesn't support adding「Express checkout button」", more_button: "Opsi pembayaran lainnya", } }; const itIT = { ec: { not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.", not_support_theme: "This Theme doesn't support adding「Express checkout button」", more_button: "Altre opzioni di pagamento", } }; const jaJP = { ec: { not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.", not_support_theme: "This Theme doesn't support adding「Express checkout button」", more_button: "その他の支払いオプション", } }; const koKR = { ec: { not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.", not_support_theme: "This Theme doesn't support adding「Express checkout button」", more_button: "더 많은 결제 옵션", } }; const enUS = { ec: { not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.", not_support_theme: "This Theme doesn't support adding「Express checkout button」", more_button: "More payment options", skeleton_layer_tips_title: "Express Checkout Button", skeleton_layer_tips_content: "Please click the「Express checkout button」on the block list,then you could enable the payment option you want to display in settings.", mock_tips: "Whether the Express checkout button is displayed also depends on the browser used by the buyer and the currency and amount of the product.", not_find_form_tips: "Express Checkout Button could only be added to Product details block.", } }; const nlNL = { ec: { not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.", not_support_theme: "This Theme doesn't support adding「Express checkout button」", more_button: "Meer betalingsmogelijkheden", } }; const plPL = { ec: { not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.", not_support_theme: "This Theme doesn't support adding「Express checkout button」", more_button: "Więcej Opcji Płatności", } }; const ptPT = { ec: { not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.", not_support_theme: "This Theme doesn't support adding「Express checkout button」", more_button: "Mais opções de pagamento", } }; const ruRU = { ec: { not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.", not_support_theme: "This Theme doesn't support adding「Express checkout button」", more_button: "Другие варианты оплаты", } }; const thTH = { ec: { not_active_channel: "Please activate {channelName} on B Admin or select another provider in the「Express checkout button」 settings, otherwise it will not display.", not_support_theme: "This Theme doesn't support adding「Express checkout button」", more_button: "ตัวเลือกการชำระเงินเพิ่มเติม", } }; window.payment_plugin_message = { getPluginI18nMessages, "zh-CN": zhCN, "zh-TW": zhTW, "ar-SA": arSA, "de-DE": deDE, "es-ES": esES, "fr-FR": frFR, "id-ID": idID, "it-IT": itIT, "ja-JP": jaJP, "ko-KR": koKR, "en-US": enUS, "nl-NL": nlNL, "pl-PL": plPL, "pt-PT": ptPT, "ru-RU": ruRU, "th-TH": thTH, } document.dispatchEvent(new CustomEvent('payment_plugin_message_reader'));
try { const dom = document.getElementById('pm-payment-express-button-1539149753700-11'); dom.i18n = window?.payment_plugin_message?.getPluginI18nMessages; if (dom.i18n) { document.dispatchEvent(new CustomEvent('payment_ec_core_ready', { detail: { i18n: true } })) } else { document.addEventListener('payment_plugin_message_reader', () => { dom.i18n = window?.payment_plugin_message?.getPluginI18nMessages; document.dispatchEvent(new CustomEvent('payment_ec_core_ready', { detail: { i18n: true } })) }, {once: true}); } } catch (e) { } // 通用工具方法 try { const dom = document.getElementById('pm-payment-express-button-1539149753700-11') const ROOT_URL = (C_SETTINGS && C_SETTINGS.routes && C_SETTINGS.routes.root) || ''; const eventListeners = {}; const commonUtils = function () { return { getProduct() { const productJson = document.querySelector('#product-json'); if (productJson?.textContent) { return JSON.parse(productJson.textContent); } if (window.jQuery) { const $product = window.jQuery?.(document)?.data('djproduct'); const productData = JSON.parse(JSON.stringify($product || {})); return productData || {}; } return {}; }, isChrome() { return navigator?.userAgent?.indexOf('Chrome') > -1 || navigator?.userAgent?.indexOf('CriOS') > -1; }, isSafari() { let userAgentString = navigator.userAgent; let chromeAgent = userAgentString.indexOf('Chrome') > -1 || navigator?.userAgent?.indexOf('CriOS') > -1; let safariAgent = userAgentString.indexOf('Safari') > -1; if (chromeAgent && safariAgent) { safariAgent = false; } return safariAgent; }, isPreview() { return !!window?.C_EDITING_SETTINGS?.oseid; }, multiply(a, b) { const precision = 2; // 保留两位小数 return Number((a * b).toFixed(precision)); }, loadScript(fnReady, id, src, datasets, onError, attributeConfig = {}) { const sdkDomId = id + '-sdk'; const loadedEventName = `${id}-loaded`; if (fnReady()) { return Promise.resolve({id: true}); } const existingScript = document.getElementById(sdkDomId); if (existingScript) { return new Promise((resolve) => { const handleLoaded = () => { if (fnReady()) { resolve({id: true}); } else { resolve({id: false}); onError && onError(); } }; if (existingScript.dataset.loaded === 'true') { handleLoaded(); } else { window.addEventListener(loadedEventName, handleLoaded, {once: true}); } }); } return new Promise((resolve) => { const s = document.createElement('script'); s.id = sdkDomId; s.src = src; s.defer = true; if (datasets) { Object.keys(datasets).map((item) => { s.dataset[item] = datasets[item]; }); } s.onload = function () { s.dataset.loaded = 'true'; window.dispatchEvent(new CustomEvent(loadedEventName)); resolve({id: true}); }; s.onerror = function () { resolve({id: false}); onError && onError(); }; Object.keys(attributeConfig).forEach((key) => { s.setAttribute(key, attributeConfig[key]); }); document.head.appendChild(s); }); }, track(eventName, data) { window.sa && window?.sa?.track('pm_' + eventName, JSON.parse(JSON.stringify(data))); }, getExtUrl(name) { const url = document.cookie.match(new RegExp('\\b' + name.replace(/_/g, '-') + '-(v[s0-9]+)')); if (url && url[1]) { return `${name}.${url[1]}.js`; } else { return window?.exts?.[name]; } }, req: { post: async (url, data = {}) => { try { const response = await fetch(req.ROOT_URL + url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, ...data, body: JSON.stringify(data.body), }); return await response.json() } catch (error) { throw new Error('post request error' + error); } }, get: async (url, data = {}) => { try { const response = await fetch(ROOT_URL + url); return await response.json() } catch (error) { throw new Error('get request error' + error); } } }, debounce(fn, wait) { let timeout = null; return function () { if (timeout !== null) { clearTimeout(timeout); } timeout = setTimeout(function () { fn.apply(this, arguments); }, wait); } }, delayCallback(cb) { window.requestIdleCallback ? requestIdleCallback(cb, {timeout: 50}) : setTimeout(cb, 50); }, loadFilly(tag, cb, trackEcPerfFn) { if (!tag) { return } const script = document.createElement('script'); script.type = 'text/javaScript'; script.src = `//static.staticdj.com/${tag}`; script.onload = function() { if (trackEcPerfFn && !performance.getEntriesByName('ec_filly_load_end').length) { performance.mark('ec_filly_load_end'); const fillyStart = performance.getEntriesByName('ec_filly_load_start')[0]; const channels = window?.C_SETTINGS?.payment_settings?.dynamic_config?.express_checkout_config?.express_channels || []; trackEcPerfFn({ eventName: 'ec_perf_filly_load_end', extraData: { filly_load_time: fillyStart ? performance.now() - fillyStart.startTime : null, payment_channel: channels.join(',') }, eventType: 'card_expose' }); } cb && cb(); }; document.getElementsByTagName('head')[0].appendChild(script); }, ecEvent: { on: (eventName, listener, useCapture) => { eventListeners[eventName] = listener; window.addEventListener(eventName, listener, useCapture); }, emit: (eventName, data) => window.dispatchEvent(new CustomEvent(eventName, {detail: data})), }, trackEcPerf({ eventName, extraData, eventType }) { const skeletonStart = performance.getEntriesByName('ec_skeleton_start')[0]; const sinceSkeletonStart = skeletonStart ? performance.now() - skeletonStart.startTime : null; window.sa && window.sa.track('function_expose', { function_name: 'payment_platform', plugin_name: 'payment_platform', business_type: 'product_payment', module: 'payment', module_type: 'payment_platform', event_type: eventType || 'expose', event_developer: 'cartoon', event_info: JSON.stringify( Object.assign( { process_name: eventName, process_type: 'perf', since_skeleton: sinceSkeletonStart }, extraData || {} ) ) }); } } } dom.commonUtilsFn = commonUtils; document.dispatchEvent(new CustomEvent('payment_ec_core_ready', { detail: { commonUtils: true } })) } catch (e) { } // 核心数据 try { const dom = document.getElementById('pm-payment-express-button-1539149753700-11'); const coreData = function () { const {getProduct} = dom.commonUtils; let productDetail = getProduct(); let productPrice = productDetail?.selected?.price || 289; const shopCurrencyCode = "USD"; const expressCheckoutList = { sdkErrorList: [], paymentChannelList: [], disabledChannelList: [], showChannelList: [], blockChannelList: [], extraChannelList: [], }; const channelType = { googlepay: ['shoplazzagoogle'], applepay: ['shoplazzaapple'], credit: ['paypal'] }; const ecGlobalVarEnums = { paypal: 'pluginPaypalEC' }; const providerEnums = { SHOPLAZZA: 'shoplazza', STRIPE: 'stripe', PAYPAL: 'paypal' }; const channelEnums = { SHOPLAZZA_GOOGLE: 'shoplazzagoogle', SHOPLAZZA_APPLE: 'shoplazzaapple', STRIPE_GOOGLE: 'stripegoogle', STRIPE_APPLE: 'stripeapple', PAYPAL: 'paypal' }; const channelThemeConfig = { [channelEnums.PAYPAL]: { default: { url: 'oss/operation/f557c83808e1cd456411170286a1ea95.svg', classList: ['paypal-card'], }, }, [channelEnums.SHOPLAZZA_GOOGLE]: { light: { url: 'oss/operation/778afb93da43adf75bdc80b078e5d4fd.svg', classList: ['googlepay-light'], }, dark: { url: 'oss/operation/e53180c224f0b0af44b44663775aa930.svg', classList: ['googlepay-dark'], }, }, [channelEnums.SHOPLAZZA_APPLE]: { light: { url: 'oss/operation/dadceb884044e0a9bbfe26c15192f542.svg', classList: ['applepay-light'], }, dark: { url: 'oss/operation/6597f66eac8b0681ebfb75941e8f6f52.svg', classList: ['applepay-dark'], }, }, }; function getContainerDomId() { const domIDSuffix = '-express-button-container'; const prefix = 'pm-'; const formatId = (channel) => `${prefix}${channel}${domIDSuffix}-1539149753700-11`; return { [providerEnums.PAYPAL]: formatId(providerEnums.PAYPAL), [channelEnums.SHOPLAZZA_GOOGLE]: formatId(channelEnums.SHOPLAZZA_GOOGLE), [channelEnums.SHOPLAZZA_APPLE]: formatId(channelEnums.SHOPLAZZA_APPLE), }; } return { ecGlobalVarEnums, providerEnums, channelEnums, productPrice, shopCurrencyCode, getChannelThemeConfig(ecName) { const themeType = window.PaymentEC?.settings?.express_theme_configs?.[ecName]?.theme_type?.toLowerCase() || 'default'; return channelThemeConfig[ecName][themeType] || channelThemeConfig[ecName]['dark']; }, getProductPrice() { return productDetail?.selected?.price; }, getProductDetail() { return productDetail; }, setProductDetail(data) { productDetail = data; }, isRequiresShipping() { return productDetail?.product?.requires_shipping }, getOpenChannelType() { const {paymentChannelList, blockChannelList} = expressCheckoutList const openList = paymentChannelList.filter(item => blockChannelList.includes(item)) || []; return { hasApplepay: openList.filter(item => channelType.applepay.includes(item))?.length > 0, hasGooglepay: openList.filter(item => channelType.googlepay.includes(item))?.length > 0, hasCredit: openList.filter(item => channelType.credit.includes(item))?.length > 0 } }, containerDomId: getContainerDomId(), channel2ProviderEnums: { [channelEnums.PAYPAL]: channelEnums.PAYPAL, [channelEnums.SHOPLAZZA_GOOGLE]: channelEnums.SHOPLAZZA_GOOGLE, [channelEnums.SHOPLAZZA_APPLE]: channelEnums.SHOPLAZZA_APPLE, [channelEnums.STRIPE_GOOGLE]: providerEnums.STRIPE, [channelEnums.STRIPE_APPLE]: providerEnums.STRIPE, }, getExpressCheckoutList() { return expressCheckoutList; }, setShowChannel(showChannelList = []) { expressCheckoutList.showChannelList = showChannelList; return expressCheckoutList; }, setBlockChannel(blockChannelList = []) { expressCheckoutList.blockChannelList = blockChannelList; return expressCheckoutList; }, setPaymentChannelList(paymentChannelList = []) { expressCheckoutList.paymentChannelList = paymentChannelList; return expressCheckoutList; }, setSdkErrorList(paymentChannelList = []) { expressCheckoutList.sdkErrorList = paymentChannelList; return expressCheckoutList; }, setExtraChannelList(extraChannelList = []) { expressCheckoutList.extraChannelList = extraChannelList; return expressCheckoutList; }, setDisabledChannelList(disabledChannelList = []) { expressCheckoutList.disabledChannelList = disabledChannelList; return expressCheckoutList; } } } dom.coreDataFn = coreData; document.dispatchEvent(new CustomEvent('payment_ec_core_ready', { detail: { coreData: true } })) } catch (e) { console.log(e); } // 通用业务数据处理方法 try { const dom = document.getElementById('pm-payment-express-button-1539149753700-11') const businessUtils = function () { const {track, isChrome, isSafari, req, isPreview, multiply, loadScript, trackEcPerf} = dom.commonUtils; const {getProductPrice, containerDomId, ecGlobalVarEnums, channelType, getOpenChannelType} = dom.coreData; const { channelEnums, shopCurrencyCode, isRequiresShipping, getProductDetail, setShowChannel, setBlockChannel, setSdkErrorList, setExtraChannelList, setDisabledChannelList, setPaymentChannelList, getExpressCheckoutList } = dom.coreData; const _businessUtils = { getECConfig: async () => { if (window.PaymentEC?.settings) { return window.PaymentEC?.settings; } // 优先从 C_SETTINGS 获取 express_checkout_config let ecConfig = window?.C_SETTINGS?.payment_settings?.dynamic_config?.express_checkout_config; // 如果 C_SETTINGS 中没有,使用接口请求作为兜底 if (!ecConfig) { const result = await req.get('/api/payment/settings'); ecConfig = result?.settings?.express_checkout_config || {}; } const {blockChannelList} = getExpressCheckoutList(); const filteredExpressChannels = blockChannelList.filter(ecName => ecConfig?.express_channels?.includes(ecName)) || []; setPaymentChannelList(filteredExpressChannels); window.PaymentEC.settings = { ...ecConfig, express_channels: filteredExpressChannels, currencyCode: shopCurrencyCode }; return window.PaymentEC.settings; }, getAttributeConfig(channelInfo) { const {ecGlobalVar, ecName} = channelInfo; const config = { paypal: { 'data-namespace': ecGlobalVar } }; return config[ecName] || {}; }, getThemeFormData() { let themeFormData = {}; const formDOM = dom.closest("form"); if (formDOM) { themeFormData = { note: '', product_id: '', variant_id: '', quantity: 1, properties: {}, }; const formData = new FormData(formDOM); const formDataKey = formData.keys(); for (const key of formDataKey) { const value = formData.get(key); const propertiesKey = key.match(/^properties(?:\.(\w+)$|\[(\w+)\]$)/); if (!propertiesKey) { themeFormData[key] = value; continue; } const objKey = propertiesKey[1] || propertiesKey[2]; themeFormData['properties'] = {...themeFormData['properties'], [objKey]: value}; } } return themeFormData; }, getProductFormData() { const themeFormData = _businessUtils.getThemeFormData() return [{ ...themeFormData, note: themeFormData?.note || "", product_id: themeFormData?.product_id || "", variant_id: themeFormData?.variant_id || "", quantity: themeFormData?.quantity || 1, // 与主题确认,只以一个为准,防止form不存在的数据仍被传递 properties: themeFormData?.properties || {}, }] }, getOrderFetchParams(data) { if (!data) { return {}; } return { line_items: data.map((item) => ({ ...item, note: item?.note || "", quantity: item?.quantity || 1, product_id: item?.product_id, variant_id: item?.variant_id, properties: item?.properties, })), refer_info: { source: 'buy_now', }, customer_note: '', }; }, isAllowTheme() { const allowThemeList = ['Nova 2023', 'Dropshiping', 'Geek', 'Hero', 'Eva']; const currentTheme = window?.C_SETTINGS?.theme?.merchant_theme_name; return allowThemeList.includes(currentTheme); }, getSubscriptionIdInit() { let defaultID; const selectSubscriptionEnum = { CLOSE: 1, ACTIVE: 2, } const productDetail = getProductDetail(); const sellingPlan = ""; if (!sellingPlan || typeof sellingPlan !== "object") { return null; } let sellingItems; if (sellingPlan?.spu?.[productDetail?.product?.id]) { sellingItems = sellingPlan.spu[productDetail?.product?.id] } if (sellingPlan?.sku?.[productDetail?.selected?.id]) { sellingItems = sellingPlan.sku[productDetail?.product?.id] } if (sellingItems?.cycles === selectSubscriptionEnum.ACTIVE && sellingItems?.selected_selling_plan_option_id) { defaultID = sellingItems?.selected_selling_plan_option_id } return defaultID ?? null }, getSubscriptionId() { const formData = _businessUtils.getThemeFormData(); const defaultID = _businessUtils.getSubscriptionIdInit(); console.log(`[paymentEC]订阅信息:form-${formData?.properties?._selling_plan_option_id},默认-${defaultID}`); if (formData?.properties) { return formData?.properties?._selling_plan_option_id } return defaultID ?? null; }, isSubscription() { return !!_businessUtils.getSubscriptionId(); }, isAllowSubscriptionPay(channel) { if (!_businessUtils.isSubscription()) { return true; } return [channelEnums.PAYPAL].includes(channel); }, checkApplePayAvailability(channel) { let available = false; let reason = 'no_session'; const isApplePay = channelType?.applepay?.includes?.(channel); if(!isApplePay){ return true; } if (window.ApplePaySession) { if (window.ApplePaySession.canMakePayments()) { available = true; reason = 'available'; } else { reason = 'cannot_make_payments'; } } trackEcPerf({ eventName: 'ec_applepay_availability_check', extraData: { available: available, reason: reason, layer: 'pike_init', payment_channel: channel }, eventType: 'expose' }); return available; }, blockChannelHandler() { const block_googlePay = true && "shoplazzagoogle"; const block_applePay = false && "shoplazzaapple"; const block_credit = false && "paypal"; const blockChannel = { googlepay: block_googlePay, applepay: block_applePay, credit: block_credit }; if (!isPreview()) { const expressChannels = window?.C_SETTINGS?.payment_settings?.dynamic_config?.express_checkout_config?.express_channels || []; const hasStripe = expressChannels.includes('shoplazzagoogle') || expressChannels.includes('shoplazzaapple'); const {hasApplepay} = getOpenChannelType(); if (hasStripe) { loadScript(() => window.Stripe, 'Stripe', 'https://js.stripe.com/v3/'); } if (hasApplepay) { loadScript(() => { const hasAppURL = document.getElementById('applepay-sdk'); return !!hasAppURL; }, 'applepay', 'https://applepay.cdn-apple.com/jsapi/v1/apple-pay-sdk.js'); } } const sortList = ['credit', 'googlepay', 'applepay']; const methodSort = Object.keys(blockChannel).filter(key => blockChannel[key] && key).sort((a, b) => { const indexA = sortList.indexOf(a); const indexB = sortList.indexOf(b); return indexA - indexB; }).map(key => blockChannel[key]); const result = setBlockChannel(methodSort); track('setBlockChannel', result); return result; }, showECButtonHandler() { const { paymentChannelList, sdkErrorList, disabledChannelList, extraChannelList, } = getExpressCheckoutList(); const showChannelList = paymentChannelList.filter((ecName) => !sdkErrorList.includes(ecName) && !disabledChannelList.includes(ecName) && !extraChannelList.includes(ecName)) || []; const result = setShowChannel(showChannelList); track('showECButton', result); return result; }, filterECButtonHandler({type}, cb) { const { paymentChannelList, sdkErrorList, disabledChannelList, extraChannelList, } = getExpressCheckoutList(); const showChannelList = paymentChannelList.filter((ecName) => !sdkErrorList.includes(ecName) && !disabledChannelList.includes(ecName) && !extraChannelList.includes(ecName)) || []; const result = setShowChannel(showChannelList.filter((ecName) => ecName !== type) || []); cb && cb(); track('filterECButton', result); return result; }, loadSDKErrorHandler(type) { const {sdkErrorList} = getExpressCheckoutList(); const result = setSdkErrorList([...sdkErrorList, type]); track('loadSDKError', result); return result; }, extraFilterShowHandler(channel) { const {extraChannelList} = getExpressCheckoutList(); const result = setExtraChannelList(extraChannelList.filter(ecName => ecName !== channel)); track('extraFilterEvent_show', result); return result; }, extraFilterHideHandler(channel) { const {extraChannelList} = getExpressCheckoutList(); const result = setExtraChannelList([...extraChannelList, channel]); track('extraFilterEvent_hide', result); return result; }, disabledChannelListHandler(checkoutData = {}, cb) { const {paymentChannelList} = getExpressCheckoutList(); const productDetail = getProductDetail(); const disabledChannelList = paymentChannelList.filter(ecName => { let mustDisable = false; if(!_businessUtils.checkApplePayAvailability(ecName)){ mustDisable = true; } if (!isRequiresShipping() && ecName !== channelEnums.PAYPAL) { mustDisable = true; } if (!_businessUtils.isAllowSubscriptionPay(ecName)) { mustDisable = true; } if (!productDetail?.selected?.available) { mustDisable = true; } const {payment_due} = checkoutData?.prices; const paymentDueNum = Number(payment_due || 0) * 100; const showFlag = paymentDueNum > 0; return mustDisable || !showFlag; }) const result = setDisabledChannelList(disabledChannelList) result?.disabledChannelList?.forEach(ecName => { cb && cb(ecName); }) track('disabledChannelListEvent', result); }, async getCheckoutData() { const formData = _businessUtils.getProductFormData(); const totalPrice = multiply(getProductPrice(), formData?.[0]?.quantity || 0); return { prices: {payment_due: totalPrice, subtotal_price: totalPrice}, orderParams: _businessUtils.getOrderFetchParams(_businessUtils.getProductFormData()), containerDOMIdEnums: containerDomId, ecGlobalVarEnums } }, } return _businessUtils } dom.businessUtilsFn = businessUtils; document.dispatchEvent(new CustomEvent('payment_ec_core_ready', { detail: { businessUtils: true } })) } catch (e) { } // 通用渲染方法 try { const dom = document.getElementById('pm-payment-express-button-1539149753700-11'); const containerDOM = 'pm-payment-express-button-container-1539149753700-11'; const commonRenderUtils = function () { return { addChildrenDOM(id, allowShow, options = {}) { if (!id) { return; } const paymentEl = document.getElementById(containerDOM); const childrenEL = document.getElementById(id); if (paymentEl && childrenEL) { childrenEL.style.display = allowShow ? 'block' : 'none'; return; } if (paymentEl && !childrenEL) { const dom = document.createElement('div'); dom.id = id; dom.style.display = allowShow ? 'block' : 'none'; if (options?.style) { Object.keys(options?.style).forEach(key => { dom.style[key] = options.style[key]; }) } if (Array.isArray(options?.classList)) { dom.classList.add(...options.classList) } paymentEl.appendChild(dom); } }, removeChildrenDOM(id) { if (!id) { return; } const paymentEl = document.getElementById(containerDOM); const childrenEL = document.getElementById(id); if (paymentEl && childrenEL) { // childrenEL.remove(); childrenEL.style.display = 'none'; } }, mockAddChildrenDOM(id, allowShow, options = {}) { if (!id) { return; } const paymentEl = document.getElementById(containerDOM); const childrenEL = document.getElementById(id); if (paymentEl && childrenEL) { childrenEL.style.display = allowShow ? 'flex' : 'none'; return; } if (paymentEl && !childrenEL) { const dom = document.createElement('div'); dom.id = id; dom.style.display = allowShow ? 'flex' : 'none'; if (options?.style) { Object.keys(options?.style).forEach(key => { dom.style[key] = options.style[key]; }) } if (Array.isArray(options?.classList)) { dom.classList.add(...options.classList) } dom.classList.add('mock-img'); const img = document.createElement('img'); img.src = `//static.staticdj.com/${options?.url}`; dom.appendChild(img); paymentEl.appendChild(dom); } }, resetRenderDOM() { const resetStyleList = [ "pm-payment-express-error-tips-1539149753700-11", "pm-payment-express-more-button-1539149753700-11", "pm-payment-express-mock-tips-1539149753700-11", "pm-payment-express-skeletonLayer-1539149753700-11", ] const resetHtmlList = [ "pm-payment-express-skeletonLayer-title-content-1539149753700-11", "pm-payment-express-skeletonLayer-content-1539149753700-11", "pm-payment-express-mock-tips-1539149753700-11", "pm-payment-express-error-tips-1539149753700-11", "pm-payment-express-button-container-1539149753700-11", "pm-payment-express-more-button-1539149753700-11", ] resetStyleList.forEach(domID => { const content = document.getElementById(domID); if (content) { content.style.display = 'none'; } }) resetHtmlList.forEach(domID => { const content = document.getElementById(domID); if (content) { content.innerHTML = ''; } }) } } } dom.commonRenderUtilsFn = commonRenderUtils; document.dispatchEvent(new CustomEvent('payment_ec_core_ready', { detail: { commonRenderUtils: true } })) } catch (e) { } // 错误提示渲染 try { const dom = document.getElementById('pm-payment-express-button-1539149753700-11'); const renderTipsUtils = function () { const {i18n} = dom; const {isPreview} = dom.commonUtils; const {channelEnums} = dom.coreData; return { showChannelNotOpenTips(channelList) { const tipsDom = document.getElementById('pm-payment-express-error-tips-1539149753700-11'); if (!isPreview()) { return; } if (!tipsDom) { return; } tipsDom.style.display = channelList.length > 0 ? 'block' : 'none'; const channelName = { [channelEnums.SHOPLAZZA_GOOGLE]: "ShoplazzaPayments - GooglePay", [channelEnums.SHOPLAZZA_APPLE]: "ShoplazzaPayments - ApplePay", [channelEnums.PAYPAL]: "PayPal", } channelList.forEach(ecName => { const id = `pm-payment-express-error-tips-1539149753700-11-${ecName}`; const hasDom = document.getElementById(id) if (!hasDom) { const dom = document.createElement('div'); dom.id = id; dom.innerHTML = i18n('ec.not_active_channel', {channelName: channelName[ecName]}); tipsDom.appendChild(dom); } }) }, disabledThemTips() { const tipsDom = document.getElementById('pm-payment-express-error-tips-1539149753700-11'); if (!isPreview()) { return; } if (!tipsDom) { return; } tipsDom.style.display = 'block'; const id = 'pm-payment-express-error-tips-1539149753700-11-theme'; const hasDom = document.getElementById(id); if (!hasDom) { const dom = document.createElement('div'); dom.id = id; dom.innerHTML = i18n('ec.not_support_theme'); tipsDom.appendChild(dom); } }, notFindFormTips() { const tipsDom = document.getElementById('pm-payment-express-error-tips-1539149753700-11'); if (!isPreview()) { return; } if (!tipsDom) { return; } tipsDom.style.display = 'block'; const id = 'pm-payment-express-error-tips-1539149753700-11-theme'; const hasDom = document.getElementById(id); if (!hasDom) { const dom = document.createElement('div'); dom.id = id; dom.innerHTML = i18n('ec.not_find_form_tips'); tipsDom.appendChild(dom); } }, showSkeletonLayerTips() { const skeletonLayerDOMId = 'pm-payment-express-skeletonLayer-1539149753700-11'; const skeletonLayerDOM = document.getElementById(skeletonLayerDOMId); const titleDOM = document.getElementById('pm-payment-express-skeletonLayer-title-content-1539149753700-11'); const contentDOM = document.getElementById('pm-payment-express-skeletonLayer-content-1539149753700-11'); if (!skeletonLayerDOM || !titleDOM || !contentDOM) { return; } skeletonLayerDOM.style.display = 'block'; titleDOM.innerHTML = i18n('ec.skeleton_layer_tips_title'); contentDOM.innerHTML = i18n('ec.skeleton_layer_tips_content'); }, showMockTips() { const tipsDOM = document.getElementById('pm-payment-express-mock-tips-1539149753700-11'); if (!tipsDOM) { return; } tipsDOM.style.display = 'block'; tipsDOM.innerHTML = i18n('ec.mock_tips'); } } } dom.renderTipsUtilsFn = renderTipsUtils; document.dispatchEvent(new CustomEvent('payment_ec_core_ready', { detail: { renderTipsUtils: true } })) } catch (e) { } // 更多信息渲染 try { const dom = document.getElementById('pm-payment-express-button-1539149753700-11'); const moreDOM = document.getElementById('pm-payment-express-more-button-1539149753700-11'); const moreButtonConfig = { firstClick: true, maxSize: isNaN(2) ? 1 : 2 }; const renderMoreUtils = function () { const {i18n} = dom; const {getExpressCheckoutList, channelEnums} = dom.coreData; function moreButtonEvent(cb) { if (!moreDOM) { return; } moreDOM.style.display = 'none'; moreButtonConfig.firstClick = false; cb && cb(); } return { getMoreButtonConfig() { return moreButtonConfig }, showMoreButton(cb) { if (!moreDOM) { return; } let {showChannelList} = getExpressCheckoutList(); const showLength = showChannelList.length; const {firstClick, maxSize} = moreButtonConfig; moreDOM.style.display = (firstClick && showLength > 0 && showLength > maxSize) ? 'block' : 'none'; moreDOM.innerHTML = i18n('ec.more_button'); moreDOM.onclick = () => moreButtonEvent(cb); }, } } dom.renderMoreUtilsFn = renderMoreUtils; document.dispatchEvent(new CustomEvent('payment_ec_core_ready', { detail: { renderMoreUtils: true } })) } catch (e) { } try { const dom = document.getElementById('pm-payment-express-button-1539149753700-11'); function start() { const { getExtUrl, loadFilly, delayCallback, ecEvent, track, loadScript, debounce, trackEcPerf } = dom.commonUtils; const { blockChannelHandler, getAttributeConfig, showECButtonHandler, filterECButtonHandler, loadSDKErrorHandler, extraFilterShowHandler, extraFilterHideHandler, disabledChannelListHandler, getECConfig, isAllowTheme, getCheckoutData, getThemeFormData } = dom.businessUtils; const {addChildrenDOM, removeChildrenDOM} = dom.commonRenderUtils; const {getMoreButtonConfig, showMoreButton} = dom.renderMoreUtils; const { ecGlobalVarEnums, providerEnums, channelEnums, getExpressCheckoutList, getProductPrice, getProductDetail, setProductDetail, containerDomId, channel2ProviderEnums, getChannelThemeConfig } = dom.coreData; const expressChannels = window?.C_SETTINGS?.payment_settings?.dynamic_config?.express_checkout_config?.express_channels || []; if (!performance.getEntriesByName('ec_skeleton_start').length) { performance.mark('ec_skeleton_start'); trackEcPerf({ eventName: 'ec_perf_skeleton_start', extraData: { page_start: performance.now(), payment_channel: expressChannels.join(',') }, eventType: 'card_expose' }); } function getFilly() { if (!performance.getEntriesByName('ec_filly_load_start').length) { performance.mark('ec_filly_load_start'); trackEcPerf({ eventName: 'ec_perf_filly_load_start', extraData: { payment_channel: expressChannels.join(',') }, eventType: 'card_expose' }); } const fillyTag = getExtUrl('filly'); if (fillyTag) { loadFilly(fillyTag, init, trackEcPerf); } } function extraFilterEvent(e) { const {channel, domId, allowShow} = e?.detail || {}; if (channel && domId) { if (allowShow) { extraFilterShowHandler(channel); const container = document.getElementById(domId); if (container) { container.style.border = 'none'; container.style.backgroundColor = 'transparent'; } } else { extraFilterHideHandler(channel); filterECButtonHandler({type: channel}, () => removeChildrenDOM(domId) ); } renderEC(); } } const renderEC = () => { showECButtonHandler(); const expressCheckoutList = getExpressCheckoutList(); var {showChannelList} = expressCheckoutList; const {firstClick, maxSize} = getMoreButtonConfig(); if (showChannelList.length === 0) { showMoreButton(renderEC); } showChannelList.forEach((ecName, index) => { const disableShow = firstClick && index >= maxSize; const containerId = containerDomId[channel2ProviderEnums[ecName]]; addChildrenDOM(containerId, !disableShow, getChannelThemeConfig(ecName)); showMoreButton(renderEC); }); } const loadErrorEvent = (type) => { const domID = containerDomId[type]; if (!domID) { return; } loadSDKErrorHandler(type); filterECButtonHandler({type}, () => removeChildrenDOM(domID) ); showMoreButton(renderEC); }; async function loadEC() { const themeFormData = getThemeFormData?.() || {}; if (!themeFormData?.product_id || !themeFormData?.variant_id) { return; } const ecConfig = await getECConfig(); const expressCheckoutList = getExpressCheckoutList(); track('loadEC', expressCheckoutList); if (ecConfig) { const checkoutData = await getCheckoutData(); disabledChannelListHandler(checkoutData, (ecName) => { filterECButtonHandler({type: ecName}, () => removeChildrenDOM(containerDomId[channel2ProviderEnums[ecName]]) ); }); renderEC(); window.PaymentEC.handleEcPluginsLoad = ({ channelInfos = [], loadedCbFn = () => { } }) => { const expressCheckoutLoadList = []; const { showChannelList } = getExpressCheckoutList(); const hasStripeChannels = channelInfos.some(info => info.ecGlobalVar === 'Stripe'); const stripeExpressChannels = (window?.C_SETTINGS?.payment_settings?.dynamic_config?.express_checkout_config?.express_channels || []) .filter(ch => ['shoplazzagoogle'].includes(ch)); if (hasStripeChannels && !performance.getEntriesByName('ec_stripe_load_start').length) { performance.mark('ec_stripe_load_start'); trackEcPerf({ eventName: 'ec_perf_stripe_load_start', extraData: { payment_channel: stripeExpressChannels.join(',') }, eventType: 'card_expose' }); } channelInfos.map((channelInfo) => { const {ecGlobalVar, ecName = '', sdkPath = '', datasets, fnReady, sdkReadyCb, sdkName} = channelInfo; let hasContainer = document.getElementById(containerDomId[ecName]); if (!hasContainer) { return; } const scriptName = sdkName || ecName === 'paypal' ? ecGlobalVar : ecName; performance.mark('ec_third_party_sdk_load_start'); trackEcPerf({ eventName: 'ec_third_party_sdk_load_start', extraData: { payment_channel: ecName, sdk_path: sdkPath }, eventType: 'expose' }); const attributeConfig = getAttributeConfig(channelInfo) || {}; const loadPromise = loadScript(fnReady ? fnReady : () => window[ecGlobalVar], scriptName, sdkPath, datasets, () => { loadErrorEvent(ecName); }, attributeConfig).then(() => { if (ecGlobalVar === 'Stripe' && !performance.getEntriesByName('ec_stripe_load_end').length) { performance.mark('ec_stripe_load_end'); const stripeStart = performance.getEntriesByName('ec_stripe_load_start')[0]; trackEcPerf({ eventName: 'ec_perf_stripe_load_end', extraData: { stripe_load_time: stripeStart ? performance.now() - stripeStart.startTime : null, payment_channel: stripeExpressChannels.join(',') }, eventType: 'card_expose' }); } performance.mark('ec_third_party_sdk_load_end'); const sdkStartLoadTime = performance.getEntriesByName('ec_third_party_sdk_load_start')[0]; trackEcPerf({ eventName: 'ec_third_party_sdk_load_end', extraData: { sdk_load_time: sdkStartLoadTime ? performance.now() - sdkStartLoadTime.startTime : null, payment_channel: ecName, sdk_path: sdkPath }, eventType: 'expose' }); sdkReadyCb?.({ ...checkoutData, loadSpent: 0, paymentExpressChannels: showChannelList }); }); expressCheckoutLoadList.push(loadPromise); }); const startLoadSDKTime = Date.now(); Promise.all(expressCheckoutLoadList).then(() => { const finishLoadSDKTime = Date.now(); const loadSpent = finishLoadSDKTime - startLoadSDKTime; loadedCbFn({...checkoutData, loadSpent, paymentExpressChannels: showChannelList}); }); }; // 通知外部数据变更 ecEvent.emit('tc_payment_ec_data_change', { ecGlobalVarEnums, containerDOMIdEnums: containerDomId }); } } const loadECDebounce = debounce(loadEC, 300) async function refreshEC(data = {}, sources) { if (!sources) { console.warn('[paymentEC]hide: sources is null'); return; } if (data?.detail?.selected?.price) { setProductDetail(data?.detail) } loadECDebounce(); } function init() { ecEvent.on('shoplazza_express_channels_change', extraFilterEvent, false); ecEvent.on('shoplazza_express_channels_change_ready', extraFilterEvent, false); if (typeof window.PaymentEC === 'object') { window.PaymentEC.getCheckoutData = getCheckoutData; } document.addEventListener('dj.variantChange', (data) => refreshEC(data, 'variantChange')); document.addEventListener('payment_ec_refresh', (data) => refreshEC(data, data?.detail?.sources)); refreshEC({}, 'init'); } if (isAllowTheme()) { blockChannelHandler(); // 提前加载 filly:如果 DOM 已经准备好,立即加载;否则在 DOMContentLoaded 时加载 // 使用 DOMContentLoaded 替代 load 事件,提前加载时机(比 window.load 更早) if (document.readyState === 'complete' || document.readyState === 'interactive') { getFilly(); } else if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', getFilly, {once: true}); } else { // 兜底:如果 readyState 未知,立即加载 getFilly(); } } } dom.startFn = start; document.dispatchEvent(new CustomEvent('payment_ec_core_ready', { detail: { start: true } })) } catch (e) { console.log(e); } // 预览模式 try { const dom = document.getElementById('pm-payment-express-button-1539149753700-11'); function start() { const {track} = dom.commonUtils; const {showMoreButton, getMoreButtonConfig} = dom.renderMoreUtils; const { showECButtonHandler, getECConfig, blockChannelHandler, isAllowTheme, getThemeFormData } = dom.businessUtils; const { disabledThemTips, showChannelNotOpenTips, showSkeletonLayerTips, showMockTips, notFindFormTips } = dom.renderTipsUtils; const {mockAddChildrenDOM, resetRenderDOM} = dom.commonRenderUtils; const { channelEnums, getChannelThemeConfig, getExpressCheckoutList, getOpenChannelType } = dom.coreData; const mockDomId = { [channelEnums.PAYPAL]: channelEnums.PAYPAL, [channelEnums.SHOPLAZZA_GOOGLE]: channelEnums.SHOPLAZZA_GOOGLE, [channelEnums.SHOPLAZZA_APPLE]: channelEnums.SHOPLAZZA_APPLE, [channelEnums.STRIPE_GOOGLE]: channelEnums.STRIPE_GOOGLE, [channelEnums.STRIPE_APPLE]: channelEnums.STRIPE_APPLE, } const renderNotOpenTips = () => { const {blockChannelList, paymentChannelList} = getExpressCheckoutList(); const notOpenChannel = blockChannelList.filter(ecName => !paymentChannelList.includes(ecName)); showChannelNotOpenTips(notOpenChannel); } const renderMockTips = () => { const {hasApplepay, hasGooglepay} = getOpenChannelType(); if (hasApplepay || hasGooglepay) { showMockTips(); } } const renderEC = () => { showECButtonHandler(); const {showChannelList} = getExpressCheckoutList(); const {firstClick, maxSize} = getMoreButtonConfig(); if (showChannelList.length === 0) { showMoreButton(renderEC); } showChannelList.forEach((ecName, index) => { const disableShow = firstClick && index >= maxSize; mockAddChildrenDOM(mockDomId[ecName], !disableShow, getChannelThemeConfig(ecName)); showMoreButton(renderEC); }); } async function loadEC() { const date = new Date().getTime(); dom.loadEC_timestamp = date const ecConfig = await getECConfig(); if (date !== dom.loadEC_timestamp) { return; } const expressCheckoutList = getExpressCheckoutList(); track('preview-loadEC', expressCheckoutList); resetRenderDOM(); // 初始化时没有事件推送 if (ecConfig) { renderNotOpenTips(); renderEC(); renderMockTips(); } } const init = () => { blockChannelHandler(); const {blockChannelList} = getExpressCheckoutList(); if (!isAllowTheme()) { disabledThemTips() return; } const themeFormData = getThemeFormData?.() || {}; if (!themeFormData?.product_id || !themeFormData?.variant_id) { notFindFormTips(); return; } if (blockChannelList.length > 0) { loadEC(); } else { showSkeletonLayerTips() } } init(); } dom.mockStartFn = start; document.dispatchEvent(new CustomEvent('payment_ec_core_ready', { detail: { start: true } })) } catch (e) { } try { const dom = document.getElementById('pm-payment-express-button-1539149753700-11'); window.PaymentEC = {} const delayCallback = (cb) => { window.requestIdleCallback ? requestIdleCallback(cb, {timeout: 50}) : setTimeout(cb, 50); } const checkReady = function (data) { const { i18n, commonUtilsFn, coreDataFn, businessUtilsFn, commonRenderUtilsFn, renderTipsUtilsFn, renderMoreUtilsFn, startFn, mockStartFn } = dom let readyData = { commonUtils: !!(commonUtilsFn) || false, coreData: !!(coreDataFn) || false, businessUtils: !!(businessUtilsFn) || false, commonRenderUtils: !!(commonRenderUtilsFn) || false, renderTipsUtils: !!(renderTipsUtilsFn) || false, renderMoreUtils: !!(renderMoreUtilsFn) || false, start: !!(startFn) || false, mockStart: !!(mockStartFn) || false, i18n: !!(i18n) || false } if (data?.detail) { Object.keys(data.detail).forEach(key => { readyData[key] = data.detail[key] }) } let isReady = true; Object.keys(readyData).forEach(key => { if (!readyData[key]) { isReady = false } }) return isReady } const readyFn = () => { if (!checkReady()) { return; } document.removeEventListener('payment_ec_core_ready', readyFn); dom.commonUtils = dom.commonUtilsFn(); dom.coreData = dom.coreDataFn(); dom.businessUtils = dom.businessUtilsFn(); dom.commonRenderUtils = dom.commonRenderUtilsFn(); dom.renderTipsUtils = dom.renderTipsUtilsFn(); dom.renderMoreUtils = dom.renderMoreUtilsFn(); const productData = dom?.commonUtils?.getProduct?.() || {}; if (JSON.stringify(productData) === '{}') { return; } if (dom?.commonUtils?.isPreview()) { dom.mockStartFn() } else { dom.startFn(); } } const init = () => { if (checkReady()) { readyFn(); } else { document.addEventListener('payment_ec_core_ready', readyFn) } } // 使用 DOMContentLoaded 替代 window.load,提前初始化时机(与 filly 加载逻辑一致) if (document.readyState === 'complete' || document.readyState === 'interactive') { delayCallback(init); } else if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => delayCallback(init), {once: true}); } else { // 兜底:如果 readyState 未知,立即执行 delayCallback(init); } } catch (e) { }
Visible only when the product is out of stock. Currently in preview mode.
Notify Me
(function(){ function $(selector){ return document.querySelector(selector); } const EMAIL_REG = /^(([^<>()[\]\\.,;:\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,}))$/; // 获取dom数据 const doms = { button: $('#app-arrival-reminder-button'), previewBox: $('#app-arrival-reminder-preview-box'), toast: $('#app-arrival-reminder-toast'), lightbox: $('#arrival-lightbox'), datalist: $('#app-arrival-reminder-email-list'), imgRender: $('#app-arrival-reminder-product-image-render'), productVariant: $('#app-arrival-reminder-product-variant'), emailInput: $('#app-arrival-reminder-form .reminder-email-input'), emailWarn: $('#app-arrival-reminder-form .reminder-email-warn-wrap'), emailWarnText: $('#app-arrival-reminder-form .reminder-email-warn-text'), submitButton: $('#app-arrival-reminder-submit-button') }; // 选中的sku数据 let selectedVariant = {"id":"192842b7-6ae1-499a-a174-fc6657bb4c57","product_id":"348f2b2c-261b-4a05-895e-5e86f03ac7e3","title":"","weight_unit":"kg","inventory_quantity":28,"sku":"SFS-1298K-GD","barcode":"","position":1,"option1":"","option2":"","option3":"","note":"","image":null,"wholesale_price":[{"price":289,"min_quantity":1}],"weight":"0","compare_at_price":"289","price":"289","retail_price":"289","available":true,"url":"\/products\/everstein-high-arc-pull-down-kitchen-faucet-with-2-function-sprayer-brushed-gold-sfs-1298k-gd?variant=192842b7-6ae1-499a-a174-fc6657bb4c57","available_quantity":28,"options":[],"off_ratio":"0","flashsale_info":{"variant_id":"192842b7-6ae1-499a-a174-fc6657bb4c57","product_id":"","quantity":0,"discount_id":"","limit_time":0,"limit_buy":0,"user_limit_buy":0,"discount_sales":0,"discount_sales_rate":"","discount_stock":0,"ends_at":0,"starts_at":0,"allow_oversold":"","allocation_method":"","price":"289","compare_at_price":"","discount_price":"289","customary_saved_price":"","customary_off_ratio":"","discount_saved_price":"","discount_off_ratio":"0","use_before_price":false,"before_price":"","title":"","properties":"","color_setting_promotional_copy":"","discount_quantity":0,"is_need_split":false},"sales":0}; // 上报 function reportData(params){ // 编辑/预览不上报 if(false){ return; } window.sa.track(params.event_name, { business_type: 'product_plugin', function_name: 'Back_in_Stock', plugin_name: "Back_in_Stock", template_name: "product", template_type: 1, module: "apps", module_type: "Back_in_Stock", event_info: JSON.stringify({ action_type: 'Back_in_Stock' }), ...params }); } // 按钮曝光上报 function buttonPVReport(){ reportData({ event_name: 'function_expose', event_type: 'expose', event_desc: '无货商品时触发' }); } // 弹窗曝光上报 function popupPVReport(){ reportData({ event_name: 'function_expose', event_type: 'popup_expose', event_desc: '弹窗曝光时触发' }); } // submit按钮点击上报 function submitClickReport(){ reportData({ event_name: 'function_click', event_type: 'popup_click', event_desc: '填入邮箱之后,点击notify 触发', event_info: JSON.stringify({ action_type: 'email_click' }), }); } // -----------------------初始化----------------- init(); // 初始化操作 function init(){ // 为确保window有后面添加的值例如window.sa 需要在DOMContentLoaded回调事件中执行逻辑 document.addEventListener("DOMContentLoaded", function() { // 设置邮件联想 setDataList(); if(!true){ // 初始无货 按钮pv事件埋点上报 buttonPVReport(); } }); } // 设置邮件联想 function setDataList(){ const customer_email = window.C_SETTINGS.customer.customer_email; if(customer_email){ const options = document.createElement('option'); options.setAttribute('value', customer_email); doms.datalist.appendChild(options); } } // -----------------------事件操作---------------- // 监听sku变化事件 document.addEventListener('dj.variantChange', variantChange); // 监听提醒按钮点击事件 doms.button.addEventListener('click', reminderButtonClick); // 监听提交按钮点击事件 doms.submitButton.addEventListener('click', submit); // 监听input元素的keydown事件 doms.emailInput.addEventListener('keydown', function(e) { // 判断是否按下了Enter键 if (e.keyCode === 13 || e.key === 'Enter') { submit(); } }); // 显示正常按钮 function showReminderButton(){ doms.button.classList.remove('app-arrival-hidden'); doms.previewBox.classList.add('app-arrival-preview-hidden'); buttonPVReport(); } // 展示预览按钮 function showPreviewButton(){ doms.button.classList.remove('app-arrival-hidden'); doms.previewBox.classList.remove('app-arrival-preview-hidden'); } // 隐藏按钮 function hiddenReminderButton(){ doms.button.classList.add('app-arrival-hidden'); doms.previewBox.classList.add('app-arrival-preview-hidden'); } // 提交数据, 请求接口 function submit(){ // 获取邮箱数据 const email = doms.emailInput.value.trim(); if(!email){ doms.emailWarnText.innerText = "Please enter your email address"; doms.emailWarn.classList.remove('app-arrival-hidden'); return; } if(!EMAIL_REG.test(email)){ doms.emailWarnText.innerText = "Please enter a valid email address"; doms.emailWarn.classList.remove('app-arrival-hidden'); return; } // 邮箱校验成功 doms.emailWarn.classList.add('app-arrival-hidden'); // 预览时不调接口 if(false){ SPZ.whenApiDefined(doms.toast).then(apis => { apis.showToast("Currently in preview mode, subscription is for testing only", 3000); }); return; } // 加入loading样式 doms.submitButton.classList.add('reminder-submit-button-loading'); // 提交数据请求接口 let quantity = 1; const quantityDom = $('#app-arrival-reminder-quantity-render ljs-quantity'); if(quantityDom){ quantity = Number(quantityDom.getAttribute('value')) || 1; } const params = { customer_email: email, product_num: quantity, product_id: selectedVariant.product_id, product_variant_id: selectedVariant.id }; fetch(`${window.C_SETTINGS.routes.root}/api/front/gobbler/v1/subscribe`, { method: 'POST', body: JSON.stringify(params), headers: { 'Content-Type': 'application/json' } }).then((res)=>res.json()).then((res)=>{ /* 后端错误提示 */ if(res.errors && res.errors.length){ SPZ.whenApiDefined(doms.toast).then(apis=>{ apis.showToast(res.errors[0], 3000); }); return; } /* code * 0 SUCCESS 订阅成功 * 1 PRODUCT_OFFLINE 商品下架 * 2 INVENTORY_NOT_EMPTY 商品库存不为空 * 3 REPEAT_SUBSCRIBE 重复订阅 * 4 EMAIL_EMPTY 邮件为空 * 5 EMAIL_WRONG_FORMAT 邮件格式错误 * 99999 INVALID_PARAMS 其他参数错误 */ const codeMap = { SUCCESS: ()=>{ SPZ.whenApiDefined(doms.lightbox).then(apis=>{ apis.close(); }); SPZ.whenApiDefined(doms.toast).then(apis=>{ apis.showToast("Thank you for your subscription! We will notify you as soon as the product is back in stock!", 3000); }); }, INVENTORY_NOT_EMPTY: ()=>{ SPZ.whenApiDefined(doms.lightbox).then(apis=>{ apis.close(); }); SPZ.whenApiDefined(doms.toast).then(apis=>{ apis.showToast("Subscription failed. Product back in stock, please proceed to checkout.", 3000); }); setTimeout(()=>{ window.location.reload(); }, 3000); }, REPEAT_SUBSCRIBE: ()=>{ SPZ.whenApiDefined(doms.toast).then(apis=>{ apis.showToast("You've already subscribed to this product. We'll let you know as soon as it's back in stock. Feel free to explore our other products.", 3000); }); }, EMAIL_EMPTY: ()=>{ SPZ.whenApiDefined(doms.toast).then(apis=>{ apis.showToast("Please enter your email address", 3000); }); }, EMAIL_WRONG_FORMAT: ()=>{ SPZ.whenApiDefined(doms.toast).then(apis=>{ apis.showToast("Please enter a valid email address", 3000); }); } }; codeMap.PRODUCT_OFFLINE = codeMap.SUCCESS; codeMap[res.code] && codeMap[res.code](); }).finally(()=>{ doms.submitButton.classList.remove('reminder-submit-button-loading'); }); // submit点击上报 submitClickReport(); } // 重新渲染dialog function refreshRenderDialog(){ console.log('selectedVariant', selectedVariant); const images = selectedVariant.image || {"src":"\/\/imgpro.staticdj.com\/dd866b12bd09f00b7ed647a8525391cc.jpg","path":"dd866b12bd09f00b7ed647a8525391cc.jpg","width":2000,"height":2000,"alt":"EVERSTEIN 2-Function Sprayer Pull-Down Kitchen Faucet Brushed Gold SFS-1298K-GD","aspect_ratio":1}; const imagePosition = images.aspect_ratio > 1 ? 'left top' : 'center center'; // 重新渲染图片 SPZ.whenApiDefined(doms.imgRender).then((apis)=>{ apis.render({ images, imagePosition }); }); // 邮件错误提示置空 doms.emailWarn.classList.add('app-arrival-hidden'); // 重新渲染款式说明 if(selectedVariant.title){ doms.productVariant.innerText = selectedVariant.title.split('-').join('/'); } // 表单数据置空 doms.emailInput.value = ''; } // sku变化 单一款式不会触发 function variantChange(e){ const selected = e.detail.selected; // 子款式没变不执行操作 例如数量变化 if(selected.id === selectedVariant.id){ return; } selectedVariant = selected; console.log('selectedVariant', selectedVariant); // 数量变化 if(selected.available || selected.product_id === undefined){ // 有库存 编辑时展示预览按钮 否则隐藏按钮 if(false){ showPreviewButton(); }else { hiddenReminderButton(); } }else{ // 无库存 显示正常按钮 showReminderButton(); } } // 提醒按钮点击 function reminderButtonClick(){ refreshRenderDialog(); // 弹窗曝光上报 popupPVReport(); } document.body.appendChild(doms.toast); document.body.appendChild(doms.lightbox.parentElement); })();
Free U.S shipping
90-Day Returns
Sustainably made
Secure payments
  • Clean every corner of your sink with ease using our 360-degree swivel spout and extendable hose, ensuring no spot is left untouched.

  • Effortlessly switch between a splash-free aerated stream for daily filling and a powerful spray for heavy-duty rinsing with a simple toggle button.

  • The 90° rotating handle fits tight spaces and won’t hit your backsplash, while the eco-friendly aerator saves water without sacrificing pressure.

DRIP-FREE PERFORMANCE FOR A LIFETIME

Experience ultimate reliability with our high-quality ceramic cartridge, rigorously tested for over 500,000 uses to ensure your kitchen stays dry and leak-free.

PREMIUM FINISHES FOR ANY KITCHEN STYLE

Elevate your home decor with our matte black, brushed gold, or stainless steel options, all featuring multi-layer protection against rust, corrosion, and fingerprints.

HASSLE-FREE DIY INSTALLATION

Save time and plumbing costs with our comprehensive 5-step guide and included professional tools, designed to get your new faucet running in minutes.

Product Details

Description
Product Overview

Elevate your culinary space with the EVERSTEIN High Arc Pull Down Kitchen Faucet. Designed for both style and heavy-duty performance, this commercial-style faucet features a flexible pull-down sprayer with dual-function modes to tackle any kitchen task. With a 360° swivel spout and a sleek gooseneck design, it offers maximum reach and clearance for large cookware. Available in a variety of premium finishes, this faucet seamlessly integrates into any modern kitchen decor while ensuring long-lasting, leak-free reliability.

Key Features

Dual-Function Spray Head: Easily toggle between a splash-free aerated stream for daily filling and a powerful spray for heavy-duty rinsing via the convenient rocker switch.

Maximum Flexibility & Reach: Equipped with a 9.8-inch extendable hose and a 360-degree swivel spout, providing full access to every corner of your sink for efficient cleaning.

Flexible Handle Design: The single-handle rotates 90° (45° forward and 45° backward), ensuring versatile control and flexible installation even with minimal backsplash clearance.

Durable Ceramic Cartridge: Engineered with a premium ceramic valve tested to last over 500,000 uses, ensuring a lifetime of drip-free and smooth operation.

Eco-Friendly & Easy-Clean Nozzles: Features soft silicone nozzles that allow you to wipe away mineral build-up with a touch, maintaining a steady 1.8 GPM water flow.

Premium Finish Options: Choose from multiple elegant, corrosion-resistant finishes designed to withstand fingerprints and water spots, keeping your kitchen looking pristine.

All-in-One Installation Kit: Comes complete with hot/cold inlet pipes, a G9/16" to G1/2" adapter, and a specialized socket wrench for a quick, hassle-free DIY setup.

Specifications
Size / Material / Notes
General
Brand EVERSTEIN
Model Number SFS-1298K
Product Type Pull-Down Kitchen Faucet
Material Stainless Steel (Body/Spout/Handle)
Finish Matte Black / Brushed Gold / Stainless Steel
Installation Type Single Hole / Deck Mount
Dimensions
Overall Height 18 5/16" (465mm)
Spout Height 9 1/8" (232mm)
Spout Reach 8 3/4" (222mm)
Installation Hole Size 1 19/64" - 1 3/8" (33mm - 35mm)
Recommended Countertop Thickness 19/32"-1 3/8" (15-35mm)
Features
Spray Head Functions 2-Way (Aerated Stream / Powerful Spray)
Spout Swivel 360-Degree Rotation
Handle Rotation 90°
Cartridge Type Durable Ceramic Cartridge
Flow Rate 1.8 GPM at 60 Psi
Extendable Hose Length 9 7/8" (250mm)
Included Accessories
Faucet Body 1 pc (Includes pull-down hose)
Water Supply Lines Hot and Cold inlet pipes
Installation Tools Socket wrench
Adapters G9/16" to G1/2" adapters
Counterweight 1 pc (Gravity Ball)
Installation Kit Washer, Installation manual, Gloves
Q&A
Frequently Asked Questions

Q1: Will the handle hit my backsplash if I have a tight space?

A: The handle is designed for flexible rotation with a 90° total range, allowing it to move 45° forward and 45° backward from its central position. This ensures it won't hit the backsplash, making it suitable for installations even in tight spaces.

Q2: What is the exact height of this faucet? I need to make sure it fits under my cabinets.

A: The overall height of the SFS-1298K is 18 5/16 inches (465mm). We recommend measuring the clearance between your countertop and upper cabinets to ensure a proper fit. The spout height is 9 1/8 inches (232mm), providing ample space for large pots.

Q3: Does the pull-down sprayer reach the corners of a large double sink?

A: Yes. The faucet features a 360-degree swivel spout and an extendable pull-down hose with a reach of 9 7/8 inches (250mm). Combined with the gravity ball retraction system, you can easily reach every corner of your sink and expect it to snap back into place securely.

Q4: Is the sprayer made of plastic or metal?

A: To ensure long-lasting durability, the main body, spout, and handle of the SFS-1298K are all constructed from high-grade stainless steel. This provides superior resistance to rust and corrosion compared to plastic alternatives.

Q5: What is the flow rate, and does it meet U.S. water-saving standards?

A: Yes, it features a flow rate of 1.8 GPM (6.8 L/min) at 60psi. It is equipped with an eco-friendly aerator that provides a powerful, splash-free stream while helping you conserve water without sacrificing performance.

Q6: Can I install this faucet myself, and what size mounting hole do I need?

A: Absolutely. It is designed for easy DIY installation on a single-hole deck. The required mounting hole size is between 1 19/64" and 1 3/8" (33mm - 35mm). We provide all necessary components in the box, including hot/cold inlet pipes, a socket wrench, and even installation gloves.

Manual (PDF)
Open / Download

Subscribe for Updates

Get early access to new arrivals, restocks, and special offers.