New Products Daily

Worldwide Shipping from Europe

Premium Brands Across All Categories

New Products Daily

Worldwide Shipping from Europe

Premium Brands Across All Categories

const templateName = SHOPLAZZA?.meta?.page?.template_name || ''; const SEARCH_URL = '/search'; const TAG = 'spz-custom-smart-search-location'; const SEARCH_CONTAINER_CLASS = 'app-smart-product-search-container'; const THEME_NAME = window.SHOPLAZZA.theme.merchant_theme_name.replace(/ /g, ''); const BREAKPOINT = 960; const DELAY = 300; // --- 工具函数 --- function matchTheme(target) { return THEME_NAME.toLocaleLowerCase().includes(target.toLocaleLowerCase()); } function resolveThemeValue(themeMap, defaultValue) { let result = defaultValue; for (const key of Object.keys(themeMap)) { if (matchTheme(key)) result = themeMap[key]; } return result; } function joinSelectors(selectorList) { return [...new Set(selectorList)].join(','); } function isDesktop() { return window.matchMedia(`(min-width: ${BREAKPOINT}px)`).matches; } // --- 主题配置 --- const HEADER_SELECTOR = resolveThemeValue({ eva: 'header .header_grid_layout', geek: '.header-mobile-inner-container', onePage: 'header .header', wind: 'header #header-nav', nova: 'header .header', hero: 'header .header__nav', flash: '#shoplaza-section-header>div>div', lifestyle: '#shoplaza-section-header .header__wrapper', reformia: 'header#header', }, 'header'); const SEARCH_ICON_CLASS = resolveThemeValue({ flash: 'app-smart-icon-search-large-flash', hero: 'app-smart-icon-search-large-hero', geek: 'app-smart-icon-search-large-geek', nova: 'app-smart-icon-search-large-nova', }, 'app-smart-icon-search-large-default'); // 插件位置纠正配置:当商家将插件插入到不可见的区域时,自动迁移到正确的 DOM 位置 // pc / mobile 分别指定 PC 端和移动端的目标父容器选择器,未配置则不做迁移 const PLUGIN_RELOCATION_CONFIG = resolveThemeValue({ reformia: { pc: '.header-layout .header__actions', mobile: '.header-layout .header__actions', }, }, null); // --- 组件 --- class SpzCustomSmartSearchLocation extends SPZ.BaseElement { constructor(element) { super(element); this.outsideCarouselIndex = 0; this.insideCarouselIndex = 0; this.searchItemType = 'icon'; this._originalSearchWrapParent = null; this._skipMobileInit = false; } static deferredMount() { return false; } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } buildCallback() { this.bindResizeListener(); this.registerActions(); } mountCallback(){ this.init(); } unmountCallback(){ this.unbindResizeListener(); this.unregisterActions(); } // --- 元素查找(支持 DocumentFragment 上下文)--- getBlockWrap() { return this.element.closest('.app-smart-product-search-wrap') || document.querySelector('.app-smart-product-search-wrap'); } getBlockContainer() { return this.element.closest('.' + SEARCH_CONTAINER_CLASS) || document.querySelector('.' + SEARCH_CONTAINER_CLASS); } resolveBlockElement(selector, fallbackId) { const wrap = this.getBlockWrap(); const el = wrap?.querySelector(selector) || document.getElementById(fallbackId); return el ? SPZ.whenApiDefined(el) : Promise.resolve(null); } getSmartSearchEl() { return this.resolveBlockElement('ljs-search', 'app-smart-search-68'); } getOutsideItemEl() { return this.resolveBlockElement('.app-smart-search-outside-item', 'app-smart-search-outside-item-68'); } // --- 插件位置纠正 --- relocatePlugin() { if (!PLUGIN_RELOCATION_CONFIG) return; const targetSelector = isDesktop() ? PLUGIN_RELOCATION_CONFIG.pc : PLUGIN_RELOCATION_CONFIG.mobile; if (!targetSelector) return; if (this._relocateTimer) { clearInterval(this._relocateTimer); } const attemptRelocate = () => { const container = this.element.closest('.' + SEARCH_CONTAINER_CLASS) || document.querySelector('#app-smart-product-search-container-68'); if (!container || !document.body.contains(container)) return false; const target = document.querySelector(targetSelector); if (!target) return false; if (target.contains(container)) return true; target.insertBefore(container, target.firstChild); return true; }; if (attemptRelocate()) return; let attempts = 0; this._relocateTimer = setInterval(() => { attempts++; if (attemptRelocate() || attempts >= 20) { clearInterval(this._relocateTimer); this._relocateTimer = null; } }, 500); } // --- 初始化 --- init() { this.relocatePlugin(); this.applySearchIconClass(); this.adjustLifestyleIcon(); if (this.searchItemType === 'input') { this.initInputMode(); return; } // icon 模式 this.initIconMode(); if (isDesktop()) return; if (this._skipMobileInit) return; // icon 模式下的移动端额外处理(处理主题特定的 header 布局) if (!window.__isLoadAppSmartSearch__) { this.initMobileSmartSearch(); if (window.self === window.top) { window.__isLoadAppSmartSearch__ = true; } } } applySearchIconClass() { document.querySelectorAll('.app-smart-icon-search-large').forEach(el => { el.classList.add(SEARCH_ICON_CLASS); }); } adjustLifestyleIcon() { if (!matchTheme('lifestyle') || this.searchItemType === 'input' || isDesktop()) return; const container = this.getBlockContainer(); if (!container) return; const alreadyMoved = !!document.querySelector( '.header__wrapper .container .row.header>div>#app-smart-product-search-container-68' ); if (alreadyMoved) return; const headerDivs = document.querySelectorAll('.header__wrapper .container .row.header>div'); const lastDiv = headerDivs[headerDivs.length - 1]; lastDiv.appendChild(container); } initInputMode() { document.querySelectorAll('.app-smart-icon-search-large').forEach(el => { el.style.display = 'none'; }); const searchWrap = this.getBlockWrap(); const pcContainer = this.getBlockContainer(); const mobileContainer = document.querySelector('.smart-search-mobile-container'); // 记录原始父节点(仅首次) if (!this._originalSearchWrapParent && searchWrap && searchWrap.parentElement) { this._originalSearchWrapParent = searchWrap.parentElement; } if (isDesktop()) { // PC 端:确保 searchWrap 在原始位置并显示 if (mobileContainer) mobileContainer.style.display = 'none'; if (searchWrap && this._originalSearchWrapParent) { // 如果 searchWrap 被移到了移动端容器,移回原始位置 if (mobileContainer && mobileContainer.contains(searchWrap)) { this._originalSearchWrapParent.appendChild(searchWrap); } } if (pcContainer) pcContainer.style.display = 'block'; return; } if (templateName === 'search') { this._skipMobileInit = true; return; } // 移动端:隐藏当前实例的 PC 容器 if (pcContainer) pcContainer.style.display = 'none'; this.ensureMobileSearchContainer(); const mobileContainerAfterEnsure = document.querySelector('.smart-search-mobile-container'); if (!mobileContainerAfterEnsure) return; // 检查移动端容器是否已经有其他实例的内容 const existingWrap = mobileContainerAfterEnsure.querySelector('.app-smart-product-search-wrap'); if (existingWrap && existingWrap !== searchWrap) { // 已有其他实例,当前实例不需要移动,保持隐藏即可 return; } // 将当前实例的 searchWrap 移到移动端容器 if (searchWrap && !mobileContainerAfterEnsure.contains(searchWrap)) { mobileContainerAfterEnsure.appendChild(searchWrap); } mobileContainerAfterEnsure.style.display = ''; } ensureMobileSearchContainer() { if (document.querySelector('.smart-search-mobile-container')) return; const container = document.createElement('div'); container.classList.add('smart-search-mobile-container'); container.classList.add('smart-search-mobile-container-' + THEME_NAME.toLocaleLowerCase()); document.querySelector(HEADER_SELECTOR).appendChild(container); } initIconMode() { document.querySelectorAll('.app-smart-icon-search-large').forEach(el => { el.style.display = 'flex'; }); } initMobileSmartSearch() { if (this.hasMobilePluginParent()) { this.showMobileSmartSearch(); } else { this.addMobileSmartSearch(); } } // --- Action 注册 --- registerActions() { this.registerAction('onSearchInputChange', (invocation) => { this.onSearchInputChange(invocation.args.keyword); }); this.registerAction('onSearchFormSubmit', (invocation) => { this.onSearchFormSubmit(invocation.args.event); }); this.registerAction('onOutsideCarouselIndexChange', (invocation) => { this.outsideCarouselIndex = invocation.args.index || 0; }); this.registerAction('onInsideCarouselIndexChange', (invocation) => { this.insideCarouselIndex = invocation.args.index || 0; }); this.registerAction('getSearchItemType', () => { this.fetchAndApplySearchItemType(); }); this.registerAction('generateHotKeywordList', (invocation) => { this.generateHotKeywordList(invocation.args?.data?.data); }); this.registerAction('onTapHotWord', (invocation) => { this.onTapHotWord(invocation.args.type); }); } // --- 搜索输入 & 提交 --- onSearchInputChange(keyword) { const display = (!keyword || !keyword.length) ? 'block' : 'none'; document.querySelectorAll('.hot-words-carousel-inner-container').forEach(el => { el.style.display = display; }); } onSearchFormSubmit(event) { const keywordArray = event.q || []; const keyword = keywordArray[0]; if (keyword !== null && keyword.length) { this.executeSearch(keywordArray, 1); } else { this.onTapHotWord('inside'); } } executeSearch(value, retryCount) { this.getSmartSearchEl().then((ljsSearch) => { if (!ljsSearch) return; try { ljsSearch.handleSearchSubmit_({ value }); } catch (e) { if (retryCount < 3) { this.executeSearch(value, retryCount + 1); return; } const searchStr = value[0] || ''; const searchResult = ljsSearch.setThinkSearchData_(searchStr); ljsSearch.afterSearching({ query: searchResult.query, url: `${SEARCH_URL}?q=${searchStr}`, queryType: searchResult.queryType, }); } }); } // --- 搜索项类型 --- fetchAndApplySearchItemType() { this.getOutsideItemEl().then((outsideItem) => { if (!outsideItem) return; const type = outsideItem.getData()?.search_item_type; this.searchItemType = type || this.searchItemType; this.init(); }); } // --- 热词 --- generateHotKeywordList(data) { const searchKeywords = data?.hotKeywordList || []; const isShowHotKeyword = data?.isShowHotKeyword || false; this.getOutsideItemEl().then((outsideItem) => { if (!outsideItem) return; const hotwords = outsideItem.getData()?.search_keywords || []; const enrichedKeywords = this.enrichKeywords(searchKeywords, hotwords); this.renderHotKeywords(enrichedKeywords, isShowHotKeyword); }); } enrichKeywords(keywords, hotwords) { return keywords.map((item) => { item.url_obj = item.url_obj || {}; const hotwordItem = hotwords.find(h => h.word === item.word); if (hotwordItem) { item.icon = hotwordItem.icon || ''; } if (!item.urlObj || !item.urlObj.url) { item.urlObj = { ...item.url_obj, url: item.url_obj.type === 'search' ? `${SEARCH_URL}?q=${item.word}` : item.url_obj.url, }; } return item; }); } renderHotKeywords(keywords, isShowHotKeyword) { document.querySelectorAll('.app-hot-keyword-render-child').forEach((el) => { SPZ.whenApiDefined(el).then((hotWordsChild) => { hotWordsChild.render({ list: keywords, isShowHotKeyword }); }); }); } // --- 底纹词工具方法 --- // 将 find_keywords(字符串数组)转换为统一的关键词对象格式 // 优先使用 find_keywords,兜底使用 search_keywords normalizeOutsideKeywords(findKeywords, searchKeywords) { if (findKeywords && findKeywords.length > 0) { return findKeywords.map(keyword => ({ word: keyword, icon: '', pic: '', type: 'find_keyword', url_obj: { type: 'search', url: `${SEARCH_URL}?q=${keyword}`, }, })); } return searchKeywords || []; } // 规范化关键词项的 URL normalizeKeywordUrl(item) { if (!item) return null; if (item.url_obj) { item.url_obj.url = item.url_obj.type === 'search' ? `${SEARCH_URL}?q=${item.word}` : item.url_obj.url; } return item; } onTapHotWord(type) { const index = type === 'inside' ? this.insideCarouselIndex : this.outsideCarouselIndex; this.getOutsideItemEl().then((outsideItem) => { if (!outsideItem) return; const apiData = outsideItem.getData(); const findKeywords = apiData?.find_keywords || []; const searchKeywords = apiData?.search_keywords || []; // 外部和内部 carousel 都使用相同逻辑:优先 find_keywords,兜底 search_keywords const keywords = this.normalizeOutsideKeywords(findKeywords, searchKeywords); const currentItem = this.normalizeKeywordUrl(keywords[index] || null); this.getSmartSearchEl().then((ljsSearch) => { if (!ljsSearch) return; if (currentItem) { ljsSearch.handleHotKeyword_({ word: currentItem.word, query_type: currentItem.type, url: currentItem.url_obj?.url, }); } else { this.executeSearch([''], 1); } }); }); } // --- 底纹词配置 --- getOutsideCarouselConfig() { return this.getOutsideItemEl().then((outsideItem) => { if (!outsideItem) return { outsideCarouselIndex: this.outsideCarouselIndex }; const apiData = outsideItem.getData(); const findKeywords = apiData?.find_keywords || []; const searchKeywords = apiData?.search_keywords || []; const carouselKeywords = this.normalizeOutsideKeywords(findKeywords, searchKeywords); return { ...apiData, search_keywords: carouselKeywords, outsideCarouselIndex: this.outsideCarouselIndex, }; }); } // --- 窗口监听 --- bindResizeListener() { window.removeEventListener('resize', window.smartSearchResizeCallback); window.smartSearchResizeCallback = SPZCore.Types.debounce( this.win, () => { this.fetchAndApplySearchItemType(); }, DELAY ); window.addEventListener('resize', window.smartSearchResizeCallback); } unbindResizeListener() { if (window.smartSearchResizeCallback) { window.removeEventListener('resize', window.smartSearchResizeCallback); window.smartSearchResizeCallback = null; } if (this._relocateTimer) { clearInterval(this._relocateTimer); this._relocateTimer = null; } } unregisterActions() { const actionNames = [ 'onSearchInputChange', 'onSearchFormSubmit', 'onOutsideCarouselIndexChange', 'onInsideCarouselIndexChange', 'getSearchItemType', 'generateHotKeywordList', 'onTapHotWord', ]; actionNames.forEach((name) => { this.registerAction(name, () => {}); }); } // --- 移动端布局:插件父容器模式 --- hasMobilePluginParent() { // reformia 使用 relocatePlugin 统一处理,不走 showMobileSmartSearch return !['geek', 'flash', 'boost', 'reformia'].includes(THEME_NAME.toLocaleLowerCase()); } showMobileSmartSearch() { const PLUGIN_PARENT_SELECTORS = { nova: '.header__mobile #header__plugin-container', hero: '.header__icons .tw-flex.tw-justify-end.tw-items-center.tw-space-x-7', onePage: '.header__mobile #header__plugin-container', wind: '#header-icons .flex.justify-end.items-center', eva: '#header__icons .plugin_content', }; const parentEl = document.querySelector( joinSelectors(Object.values(PLUGIN_PARENT_SELECTORS)) ); if (!parentEl) return; const hasHiddenClass = parentEl.classList.contains('md:hidden') || parentEl.classList.contains('md:tw-hidden'); if (hasHiddenClass) { Array.from(parentEl.children).forEach((child) => { if (!this.isSmartSearchElement(child)) { child.style.display = 'none'; } }); parentEl.classList.remove('md:hidden', 'md:tw-hidden'); } else { const smartSearchEl = Array.from(parentEl.children).find( (child) => this.isSmartSearchElement(child) ); if (smartSearchEl) { smartSearchEl.style.display = 'block'; } } } isSmartSearchElement(el) { return ( el.classList.contains(SEARCH_CONTAINER_CLASS) || el.querySelectorAll(`.${SEARCH_CONTAINER_CLASS}`).length > 0 ); } // --- 移动端布局:图标插入模式 --- addMobileSmartSearch() { const HEADER_ICONS_SELECTORS = { geek: '#header-mobile-container .flex.items-center.justify-end.flex-shrink-0', flash: '#header-layout .header__icons', boost: '.header__mobile-bottom .tw-flex.tw-items-center.tw-justify-end.tw-flex-1', reformia: '.header-layout .header__actions', }; const SMART_SEARCH_ANCESTORS = [ '#header-menu-mobile #menu-drawer', '#menu-drawer .plugin__header-content', '.header__drawer', '.header-content .logo-wrap', '.header_hamburger_sidebar-container', ]; const iconsEl = document.querySelector( joinSelectors(Object.values(HEADER_ICONS_SELECTORS)) ); const searchWrapSelector = joinSelectors( SMART_SEARCH_ANCESTORS.map(a => `${a} .${SEARCH_CONTAINER_CLASS}`) ); const searchWrapEl = document.querySelector(searchWrapSelector); if (!iconsEl || !searchWrapEl) return; iconsEl.insertAdjacentElement('afterbegin', searchWrapEl); } } SPZ.defineElement(TAG, SpzCustomSmartSearchLocation); class SpzCustomSmartSearchToast extends SPZ.BaseElement { constructor(element) { super(element); this.toastDom = null; this.toastTimeout = null; } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } buildCallback(){ this.init(); } init(){ const toast = document.createElement('div'); toast.id = 'spz-custom-smart-search-toast-68'; toast.className = 'spz-custom-smart-search-toast'; document.body.appendChild(toast); this.toastDom = toast; this.registerAction('showToast',(invocation)=>{ this.showToast(invocation.args); }); this.registerAction('hideToast',(invocation)=>{ this.hideToast(invocation.args); }); } showToast({ message, duration = 2000 }){ if( !this.toastDom ) return; this.toastDom.innerHTML = message; this.toastDom.classList.add('smart-search-toast-show'); clearTimeout(this.toastTimeout); this.toastTimeout = setTimeout(() => { this.hideToast(); }, duration); } hideToast(){ if( !this.toastDom ) return; this.toastDom.classList.remove('smart-search-toast-show'); } } SPZ.defineElement('spz-custom-smart-search-toast', SpzCustomSmartSearchToast); class SpzCustomSmartSearchCookie extends SPZ.BaseElement { constructor(element) { super(element); } buildCallback() { this.registerAction('getCookie',(invocation)=>{ this.getCookie(invocation.args); }); } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } getCookie(key) { let cookieMap = {} document.cookie.split(';').map(item=>{ let [key, value] = item.trim().split('=') cookieMap[key] = value }) return cookieMap[key] || ''; } } SPZ.defineElement('spz-custom-smart-search-cookie', SpzCustomSmartSearchCookie); const default_function_name = 'smart_search'; const default_plugin_name = 'smart_search'; const default_module_type = 'smart_search'; const default_module = 'apps'; const default_business_type = 'product_plugin'; const default_event_developer = 'ray'; class SpzCustomSmartSearchTrack extends SPZ.BaseElement { constructor(element) { super(element); } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } buildCallback() { this.registerAction('track', (invocation) => { const { trackType, trackData } = invocation.args; this.track({trackType, trackData}); }); } track({trackType, trackData}) { const { function_name, plugin_name, module_type, module, business_type, event_developer, event_type, event_desc, trackEventInfo, ...otherTrackData } = trackData; window.sa.track(trackType, { function_name: function_name || default_function_name, plugin_name: plugin_name || default_plugin_name, module_type: module_type || default_module_type, module: module || default_module, business_type: business_type || default_business_type, event_developer: event_developer || default_event_developer, event_type: event_type, event_desc: event_desc, ...otherTrackData, event_info: JSON.stringify({ ...(trackEventInfo || {}), }), }); } } SPZ.defineElement('spz-custom-smart-search-track', SpzCustomSmartSearchTrack);
const marketSlot = document.querySelector(".plugin_c_market"); if(marketSlot) { marketSlot.remove(); } const SITE = (window.C_SETTINGS || window[atob('U0hPUExBWlpB')])?.routes?.root || ''; const MARKET_THEME_SETTINGS = `${SITE}/api/markets/theme_setting`; const MARKET_COUNTRIES = `${SITE}/api/markets/countries`; const IP_MATCHING_MARKET = `${SITE}/api/front/station`; const MARKET_LANGUAGES = `${SITE}/api/markets/:id/active_languages`; const URL_REDIRECT = '_market_redirected'; const COOKIE_TIPS_NAME = 'market_no_tips'; const COOKIE_TIPS_EXPIRES = 1000 * 60 * 60 * 24 * 14; const MARKET_COUNTRY_COOKIE = '_shoplazza_market_country'; const MARKET_MODAL_LOADED = '__market_modal_loaded__'; const PROPERTY_SELECTED = 'selected'; const MARKET_PC_HEADER_TRIGGER = 'market-pc-header-trigger'; const MARKET_PC_HEADER_COUNTRY_SELECT_TITLE = 'market-pc-header-country-select-title'; const MARKET_PC_HEADER_LANGUAGE_SELECT_TITLE = 'market-pc-header-language-select-title'; const MARKET_PC_HEADER_LANGUAGE_SELECT_SUB_TITLE = 'market-pc-header-language-select-sub-title'; const MARKET_PC_HEADER_COUNTRY_SELECT_SUB_TITLE = 'market-pc-header-country-select-sub-title'; const MARKET_FLAG = 'market-flag'; const MARKET_SELECT_VALUE = 'market-select-value'; const MARKET_CURRENCY = 'market-currency'; const MARKET_CURRENCY_SYMBOL = 'market-currency-symbol'; const MARKET_SELECT_ITEM = 'market-select-item'; const MARKET_SELECT_ITEM_ID = 'market-select-item-id'; const MARKET_SELECT_LIST = 'market-select-list'; const MARKET_LANGUAGE_SELECT = 'market-language-select'; const MARKET_COUNTRY_SELECT = 'market-country-select'; const MARKET_MODAL = 'market-modal'; const MARKET_GATEWAY_REDIRECT_MODAL = 'market-gateway-redirect-modal'; const MARKET_RESELECT_MODAL = 'market-reselect-modal'; const MARKET_MODAL_FLAG = 'market-modal-flag'; const MARKET_MODAL_COUNTRY_CODE = 'market-modal-country-code'; const MARKET_MODAL_CURRENCY_SYMBOL = 'market-modal-currency-symbol'; const MARKET_MODAL_CLOSE = 'market-modal-close'; const MARKET_MODAL_CONFIRM = 'market-modal-confirm'; const MARKET_MODAL_RESELECT = 'market-modal-reselect'; const MARKET_MODAL_RESELECT_COUNTRY_SELECT = 'market-modal-reselect-country-select'; const DATA_SECTION_TYPE = 'data-section-type'; const SECTION_TYPE_HEADER = 'header'; const SECTION_TYPE_FOOTER = 'footer'; class SpzCustomMarket extends SPZ.BaseElement { constructor(element) { super(element); this.global = null; this.useCountrySelect_ = false; this.useLanguageSelect_ = false; this.useModal_ = false; this.isSingleSelectMode_ = false; this.blockSectionPosition_ = ''; this.isEditMode = false; this.ipMatchingMarket = null; this.marketCountryList = []; this.marketLanguages = []; this.toast = null; } static deferredMount() { return false; } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } buildCallback() { console.debug('Geolocation'); this.xhr_ = SPZServices.xhrFor(this.win); const platform = SPZServices.platformFor(this.win); this.isIOS_ = platform.isIos(); this.action_ = SPZServices.actionServiceForDoc(this.element); this.global = this.win.C_SETTINGS || this.win[atob('U0hPUExBWlpB')]; this.isEditMode = new URLSearchParams(this.win.location.search).get('preview_theme_id') && new URLSearchParams(this.win.location.search).get('oseid'); this.init_(); } showToast_(content) { if (!this.toast) { this.toast = self.document.createElement('ljs-toast'); this.toast.setAttribute('layout', 'nodisplay'); this.toast.setAttribute('hidden', ''); this.toast.setAttribute('duration', '2000'); this.element.appendChild(this.toast); } SPZ.whenApiDefined(this.toast).then((apis) => { apis.showToast(content); }); } triggerEvent_(name, data) { const event = SPZUtils.Event.create(this.win, `${TAG}.${name}`, data); this.action_.trigger(this.element, name, event); } findBlockSectionPosition() { let current = this.element; while (current) { const dataSectionType = current.getAttribute(DATA_SECTION_TYPE); if ( dataSectionType === SECTION_TYPE_HEADER || dataSectionType === SECTION_TYPE_FOOTER ) { return dataSectionType; } current = current.parentElement; } return ''; } async initSettings_() { await this.getMarketThemeSettings_(); if (this.useCountrySelect_) { this.element.setAttribute('use-country-select', true); } if (this.useLanguageSelect_) { this.element.setAttribute('use-language-select', true); } if (this.useModal_) { this.element.setAttribute('use-modal', true); } if ( (this.useCountrySelect_ && !this.useLanguageSelect_) || (!this.useCountrySelect_ && this.useLanguageSelect_) ) { this.isSingleSelectMode_ = true; } this.blockSectionPosition_ = this.findBlockSectionPosition(); this.isPCLayout_ = this.win.matchMedia('(min-width: 960px)').matches; } renderMarketSelectTemplateNode_(clonedTemplate, data) { this.renderTemplateImg_(clonedTemplate, MARKET_FLAG, { src: data?.flag, width: '24', height: '24', }); this.renderTemplateElement_( clonedTemplate, MARKET_SELECT_VALUE, data.value ); this.renderTemplateElement_( clonedTemplate, MARKET_CURRENCY, data?.currency ); this.renderTemplateElement_( clonedTemplate, MARKET_CURRENCY_SYMBOL, data?.currencySymbol ); } bindDropdownOpenClickEvent_(triggerElement, dropdown) { if (!triggerElement) return; const triggerOpen = () => { SPZ.whenApiDefined(dropdown).then((apis) => { if (!dropdown.hasAttribute('open')) { apis.open(triggerElement); } }); }; if (this.isIOS_) { triggerElement.addEventListener('touchstart', triggerOpen); } else { triggerElement.addEventListener('click', triggerOpen); } } createDropdown_(auto=false) { const dropdown = self.document.createElement('ljs-dropdown'); dropdown.setAttribute('layout', 'nodisplay'); if (auto) { dropdown.setAttribute('auto-orientation', 'true'); } if (this.blockSectionPosition_ === 'header') { dropdown.setAttribute('overlay-style', 'top: 4px;'); dropdown.setAttribute('placement', auto ? 'bottom' : 'bottomRight'); } else if (this.blockSectionPosition_ === 'footer') { dropdown.setAttribute('overlay-style', 'top: -4px;'); dropdown.setAttribute('placement', 'top'); } return dropdown; } createDropdownIcon_() { const svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg"); svgElement.setAttribute("class", "market-rotate-180"); svgElement.setAttribute("width", "16"); svgElement.setAttribute("height", "16"); const path1 = document.createElementNS("http://www.w3.org/2000/svg", "path"); path1.setAttribute("d", "M7.47 5.172a.75.75 0 0 1 1.06 0l4.596 4.596a.75.75 0 1 1-1.06 1.06L7.468 6.232a.75.75 0 0 1 0-1.06Z"); path1.setAttribute("fill", "currentColor"); const path2 = document.createElementNS("http://www.w3.org/2000/svg", "path"); path2.setAttribute("d", "M8.533 5.172a.75.75 0 0 1 0 1.06l-4.596 4.596a.75.75 0 1 1-1.061-1.06l4.596-4.596a.75.75 0 0 1 1.06 0Z"); path2.setAttribute("fill", "currentColor"); svgElement.appendChild(path1); svgElement.appendChild(path2); return svgElement; } renderDropdownListWithClickEvent_({ triggerElement, dataList, subTitleRole, listElementWithTemplate, defaultValue, itemHandler, shouldUpdateSelectedItem = false, autoOrientation = false, }) { const dropdown = this.createDropdown_(autoOrientation); const itemTemplate = SPZCore.Dom.scopedQuerySelector( listElementWithTemplate, 'template' ); const clonedItemTemplate = itemTemplate.cloneNode(true); const fragment = self.document.createDocumentFragment(); dataList.forEach((data) => { const tempParentNode = self.document.createElement('div'); const clonedTemplate = self.document.importNode( clonedItemTemplate.content, true ); tempParentNode.appendChild(clonedTemplate); tempParentNode.firstElementChild.classList.add( 'market-select-item' ); this.renderMarketSelectTemplateNode_(tempParentNode, data); const selectItemElement = SPZCore.Dom.scopedQuerySelector( tempParentNode, `[role="${MARKET_SELECT_ITEM}"]` ); if (!selectItemElement) { return; } if (defaultValue === data.value) { if (shouldUpdateSelectedItem) { const clonedSelectItemElement = selectItemElement.cloneNode(true); triggerElement.replaceChild( clonedSelectItemElement, triggerElement.firstElementChild ); } selectItemElement.setAttribute(PROPERTY_SELECTED, ''); } selectItemElement.setAttribute(MARKET_SELECT_ITEM_ID, data.value); fragment.appendChild(tempParentNode.firstElementChild); }); let value = defaultValue; listElementWithTemplate.addEventListener( 'click', (e) => { if (this.isEditMode) { this.showToast_('编辑器下不支持切换国家'); SPZ.whenApiDefined(dropdown).then((apis) => { apis.close(); }); return; } let targetElement = e.target; while (targetElement) { if ( !targetElement || targetElement.getAttribute(MARKET_SELECT_ITEM_ID) ) { break; } targetElement = targetElement.parentNode; } if ( !targetElement || !targetElement.hasAttribute(MARKET_SELECT_ITEM_ID) ) { return; } const selectValue = targetElement.getAttribute(MARKET_SELECT_ITEM_ID); if (value === selectValue) { SPZ.whenApiDefined(dropdown).then((apis) => { apis.close(); }); return; } const selectedData = dataList.find((c) => c.value === selectValue); if (shouldUpdateSelectedItem) { const tempParentNode = self.document.createElement('div'); const clonedTemplate = self.document.importNode( clonedItemTemplate.content, true ); tempParentNode.appendChild(clonedTemplate); this.renderMarketSelectTemplateNode_(tempParentNode, selectedData); tempParentNode.firstElementChild.classList.add( 'market-select-item' ); triggerElement.replaceChild( tempParentNode.firstElementChild, triggerElement.firstElementChild ); } value = selectValue; itemHandler?.(selectedData); SPZ.whenApiDefined(dropdown).then((apis) => { apis.close(); }); }, false ); listElementWithTemplate.removeChild(itemTemplate); listElementWithTemplate.appendChild(fragment); listElementWithTemplate.classList.add('market-select-list'); dropdown.appendChild(listElementWithTemplate); if (subTitleRole) { this.renderSelectTitle_(dropdown, subTitleRole); } return dropdown; } createSelect_({ parent, defaultValue, dataList, listElementWithTemplate, itemHandler, titleRole, }) { const triggerElement = self.document.createElement('div'); triggerElement.classList.add('market-select'); const displayElement = self.document.createElement('div'); triggerElement.appendChild(displayElement); triggerElement.appendChild(this.createDropdownIcon_()); const dropdown = this.renderDropdownListWithClickEvent_({ triggerElement, dataList, listElementWithTemplate, defaultValue, itemHandler, shouldUpdateSelectedItem: true, }); this.bindDropdownOpenClickEvent_(triggerElement, dropdown); const dropdownContainer = self.document.createElement('div'); dropdownContainer.appendChild(triggerElement); dropdownContainer.appendChild(dropdown); dropdownContainer.classList.add('market-select-container'); this.renderSelectTitle_(dropdownContainer, titleRole); parent.appendChild(dropdownContainer); } getListElementWithTemplate_(role) { const template = SPZCore.Dom.scopedQuerySelector( this.element, `[role="${role}"]` ); if (template) { const clonedTemplate = template.cloneNode(true); const tempParent = self.document.createElement("div"); tempParent.appendChild(clonedTemplate.content); const listElementWithTemplate = SPZCore.Dom.scopedQuerySelector( tempParent, `[role="${MARKET_SELECT_LIST}"]` ); return listElementWithTemplate; } return null; } renderSelectTitle_(parent, role) { if (!parent || !role) { return; } const template = SPZCore.Dom.scopedQuerySelector( this.element, `[role="${role}"]` ); const content = self.document.importNode(template.content, true); parent.insertBefore(content, parent.firstElementChild); parent.firstElementChild.classList.add( role.includes('sub') ? 'market-pc-header-select-sub-title' : 'market-pc-header-select-title' ); } initSelect_(rootNode, options) { const parent = rootNode || this.element; if (this.useCountrySelect_) { this.createSelect_({ parent, titleRole: options?.shouldRenderSelectTitle ? MARKET_PC_HEADER_COUNTRY_SELECT_TITLE : undefined, defaultValue: this.getDefaultCountryValue_(), dataList: this.marketCountryList.map(this.mapCountryRenderData_), listElementWithTemplate: this.getListElementWithTemplate_( MARKET_COUNTRY_SELECT ), itemHandler: this.handleCountrySelectChange_.bind(this), }); } if (this.useLanguageSelect_) { this.createSelect_({ parent, titleRole: options?.shouldRenderSelectTitle ? MARKET_PC_HEADER_LANGUAGE_SELECT_TITLE : undefined, defaultValue: this.getDefaultLanguageValue_(), dataList: this.marketLanguages.map(this.mapLanguageRenderData_), listElementWithTemplate: this.getListElementWithTemplate_( MARKET_LANGUAGE_SELECT ), itemHandler: this.handleLanguageSelectChange_.bind(this), }); } } initPcHeaderTriggerElement() { const headerTriggerElementTemplate = SPZCore.Dom.scopedQuerySelector( this.element, `[role="${MARKET_PC_HEADER_TRIGGER}"]` ); const headerTriggerElement = self.document.importNode( headerTriggerElementTemplate.content, true ); const parentDivElement = self.document.createElement('div'); parentDivElement.setAttribute('role', MARKET_PC_HEADER_TRIGGER); parentDivElement.appendChild(headerTriggerElement); this.element.removeChild(headerTriggerElementTemplate); return parentDivElement; } initPanelWithSelect_() { const triggerElement = this.initPcHeaderTriggerElement(); const dropdown = this.createDropdown_(true); const panelElement = self.document.createElement('div'); panelElement.classList.add('market-panel'); this.initSelect_(panelElement, {shouldRenderSelectTitle: true}); dropdown.appendChild(panelElement); this.bindDropdownOpenClickEvent_(triggerElement, dropdown); this.element.appendChild(triggerElement); this.element.insertBefore(dropdown, triggerElement); } initSingleSelectDropdown_() { const triggerElement = this.initPcHeaderTriggerElement(); let dropdown; if (this.useCountrySelect_) { dropdown = this.renderDropdownListWithClickEvent_({ triggerElement, subTitleRole: MARKET_PC_HEADER_COUNTRY_SELECT_SUB_TITLE, dataList: this.marketCountryList.map(this.mapCountryRenderData_), listElementWithTemplate: this.getListElementWithTemplate_( MARKET_COUNTRY_SELECT ), defaultValue: this.getDefaultCountryValue_(), itemHandler: this.handleCountrySelectChange_.bind(this), autoOrientation: true, }); } else if (this.useLanguageSelect_) { dropdown = this.renderDropdownListWithClickEvent_({ triggerElement, subTitleRole: MARKET_PC_HEADER_LANGUAGE_SELECT_SUB_TITLE, dataList: this.marketLanguages.map(this.mapLanguageRenderData_), listElementWithTemplate: this.getListElementWithTemplate_( MARKET_LANGUAGE_SELECT ), defaultValue: this.getDefaultLanguageValue_(), itemHandler: this.handleLanguageSelectChange_.bind(this), autoOrientation: true, }); } if (dropdown) { this.bindDropdownOpenClickEvent_(triggerElement, dropdown); this.element.appendChild(triggerElement); this.element.appendChild(dropdown); } } initEntry_() { if (this.blockSectionPosition_ === SECTION_TYPE_HEADER) { if (this.isSingleSelectMode_) { this.initSingleSelectDropdown_(); } else if (this.useCountrySelect_ && this.useLanguageSelect_) { this.initPanelWithSelect_(); } } this.initSelect_(this.element); } async initData_() { const promises = []; if (this.useCountrySelect_ || this.useModal_) { promises.push(this.getCountryList_(), this.getIpMatchingMarket_()); } if (this.useLanguageSelect_) { promises.push(this.getMarketLanguages_(this.global.market.market_id)); } return Promise.all(promises).then((results) => { const ipMatchingMarket = results.find((result) => result.hasOwnProperty('matching_station') ); const marketCountryList = results.find((result) => result.hasOwnProperty('data') ); if (ipMatchingMarket && marketCountryList) { this.ipMatchingMarket = ipMatchingMarket?.matching_station?.find?.( (item) => item.type === 'multiple_market' ) || { img: marketCountryList.length ? marketCountryList?.[0]?.detail && marketCountryList[0].detail?.flag : '', }; } }); } initModal_() { if ( this.isEditMode || !this.ipMatchingMarket?.market_id || !this.useModal_ || this.hasCookie_(COOKIE_TIPS_NAME) || this.win[MARKET_MODAL_LOADED] ) { return; } this.win[MARKET_MODAL_LOADED] = true; const redirectParam = new URLSearchParams(this.win.location.search).get( URL_REDIRECT ); if (redirectParam) { this.initModalEntry_(MARKET_GATEWAY_REDIRECT_MODAL); } else if ( this.ipMatchingMarket.market_id !== this.global?.market?.market_id ) { this.initModalEntry_(MARKET_MODAL); } } async init_() { await this.initSettings_(); await this.initData_(); this.initEntry_(); this.initModal_(); } getDefaultCountryValue_() { return this.getCookie_(MARKET_COUNTRY_COOKIE) || this.marketCountryList[0]?.country_code; } mapCountryRenderData_(country) { return { flag: country?.detail?.flag || '', value: country?.country_code, currency: country?.currency, currencySymbol: country?.symbol?.val || '', url: country?.url, }; } handleCountrySelectChange_(selectedData) { this.setCookie_(MARKET_COUNTRY_COOKIE, selectedData.value); this.handleChangeURL_(selectedData.url); } getDefaultLanguageValue_() { return this.marketLanguages?.find?.( (lang) => lang.code === this.global?.market?.market_lang )?.name; } mapLanguageRenderData_(language) { return { value: language.name, url: language.url, }; } handleLanguageSelectChange_(selectedData) { this.handleChangeURL_(selectedData.url); } renderTemplateElement_(parent, role, value) { const elements = SPZCore.Dom.scopedQuerySelectorAll( parent, `[role="${role}"]` ); elements.forEach((element) => { if (element && value) { element.innerHTML = value; } }); } renderTemplateImg_(parent, role, options) { const flagElement = SPZCore.Dom.scopedQuerySelector( parent, `[role="${role}"]` ); if (flagElement && options?.src && options?.height && options?.width) { const spzImg = self.document.createElement('ljs-img'); spzImg.setAttribute('src', options.src); spzImg.setAttribute('layout', 'responsive'); spzImg.setAttribute('width', options.width); spzImg.setAttribute('height', options.height); flagElement.appendChild(spzImg); } } handleChangeURL_(url) { if (!!url) { let {origin, pathname, search} = this.win.location; pathname = pathname.replace(SITE, url != '/' ? url : ''); this.win.location.href = `${origin}${pathname}${search}`; } else { this.win.location.reload(true); } } initModalEntry_(role) { const marketModalTemplate = SPZCore.Dom.scopedQuerySelector( this.element, `[role="${role}"]` ); if (marketModalTemplate) { const tempParentNode = self.document.createElement('div'); const clonedTemplate = self.document.importNode( marketModalTemplate.content, true ); tempParentNode.appendChild(clonedTemplate); this.renderTemplateImg_(tempParentNode, MARKET_MODAL_FLAG, { src: this.ipMatchingMarket?.img, width: '80', height: '80', }); this.renderTemplateElement_( tempParentNode, MARKET_MODAL_COUNTRY_CODE, this.ipMatchingMarket?.name ); this.renderTemplateElement_( tempParentNode, MARKET_MODAL_CURRENCY_SYMBOL, this.ipMatchingMarket?.currency_with_symbol ); const lightbox = self.document.createElement('ljs-lightbox'); lightbox.setAttribute('layout', 'nodisplay'); lightbox.appendChild(tempParentNode.firstElementChild); self.document.body.appendChild(lightbox); SPZ.whenApiDefined(lightbox).then((apis) => { SPZCore.Dom.scopedQuerySelectorAll( lightbox, `[role="${MARKET_MODAL_CLOSE}"]` ).forEach((ele) => { ele.addEventListener('click', () => { this.setCookie_(COOKIE_TIPS_NAME, 1, COOKIE_TIPS_EXPIRES); apis.close(); }); }); const marketModalConfirmElement = SPZCore.Dom.scopedQuerySelector( lightbox, `[role="${MARKET_MODAL_CONFIRM}"]` ); if (marketModalConfirmElement && this.ipMatchingMarket?.url) { marketModalConfirmElement.addEventListener('click', () => { this.setCookie_(COOKIE_TIPS_NAME, 1, COOKIE_TIPS_EXPIRES); this.setCookie_(MARKET_COUNTRY_COOKIE, this.ipMatchingMarket?.name); this.handleChangeURL_(this.ipMatchingMarket?.url); }); } const marketModalReselectElement = SPZCore.Dom.scopedQuerySelector( lightbox, `[role="${MARKET_MODAL_RESELECT}"]` ); if (marketModalReselectElement) { marketModalReselectElement.addEventListener('click', () => { this.setCookie_(COOKIE_TIPS_NAME, 1, COOKIE_TIPS_EXPIRES); const marketReselectTemplate = SPZCore.Dom.scopedQuerySelector( this.element, `[role="${MARKET_RESELECT_MODAL}"]` ); if (marketReselectTemplate && lightbox.firstElementChild) { const clonedTemplate = self.document.importNode( marketReselectTemplate.content, true ); const selectParent = SPZCore.Dom.scopedQuerySelector( clonedTemplate, `[role="${MARKET_MODAL_RESELECT_COUNTRY_SELECT}"]` ); if (selectParent) { this.createSelect_({ parent: selectParent, defaultValue: this.getDefaultCountryValue_(), dataList: this.marketCountryList.map( this.mapCountryRenderData_ ), listElementWithTemplate: this.getListElementWithTemplate_( MARKET_COUNTRY_SELECT ), itemHandler: this.handleCountrySelectChange_.bind(this), }); } lightbox.replaceChild(clonedTemplate, lightbox.firstElementChild); SPZCore.Dom.scopedQuerySelectorAll( lightbox, `[role="${MARKET_MODAL_CLOSE}"]` ).forEach((ele) => { ele.addEventListener('click', () => { this.setCookie_(COOKIE_TIPS_NAME, 1, COOKIE_TIPS_EXPIRES); apis.close(); }); }); } }); } apis.open(); }); } } async getMarketThemeSettings_() { return this.xhr_.fetchJson(MARKET_THEME_SETTINGS).then((res) => { this.useCountrySelect_ = !!res.country_selector_enabled; this.useLanguageSelect_ = !!res.language_selector_enabled && this.global?.market?.market_id; this.useModal_ = !!res.redirect_pop_enabled; return res; }); } async getCountryList_() { return this.xhr_.fetchJson(MARKET_COUNTRIES).then((res) => { this.marketCountryList = res.data.length ? res.data : []; return res; }); } async getIpMatchingMarket_() { return this.xhr_.fetchJson(IP_MATCHING_MARKET, { method: 'POST', body: { stations: [], }, }); } async getMarketLanguages_(marketId) { return this.xhr_ .fetchJson(MARKET_LANGUAGES.replace(/:id/gim, marketId)) .then((res) => { this.marketLanguages = res?.languages?.length ? res.languages : []; return res; }); } hasCookie_(name) { const m = self.document.cookie .split(';') .filter((pair) => pair.trim().startsWith(name + '=')); return !!(m && m[0]); } getCookie_(name) { const m = self.document.cookie .split(';') .filter((pair) => pair.trim().startsWith(name + '=')); return m && m[0] ? m[0].split('=')[1] : null; } setCookie_(name, value, expires) { const expiresDate = new Date(Date.now() + expires).toGMTString(); self.document.cookie = `${name}=${encodeURIComponent( value )};expires=${expiresDate};path=/`; } } SPZ.defineElement('spz-custom-market', SpzCustomMarket);
  • Home
  • About Us
  • Categories
  • More links
const templateName = SHOPLAZZA?.meta?.page?.template_name || ''; const SEARCH_URL = '/search'; const TAG = 'spz-custom-smart-search-location'; const SEARCH_CONTAINER_CLASS = 'app-smart-product-search-container'; const THEME_NAME = window.SHOPLAZZA.theme.merchant_theme_name.replace(/ /g, ''); const BREAKPOINT = 960; const DELAY = 300; // --- 工具函数 --- function matchTheme(target) { return THEME_NAME.toLocaleLowerCase().includes(target.toLocaleLowerCase()); } function resolveThemeValue(themeMap, defaultValue) { let result = defaultValue; for (const key of Object.keys(themeMap)) { if (matchTheme(key)) result = themeMap[key]; } return result; } function joinSelectors(selectorList) { return [...new Set(selectorList)].join(','); } function isDesktop() { return window.matchMedia(`(min-width: ${BREAKPOINT}px)`).matches; } // --- 主题配置 --- const HEADER_SELECTOR = resolveThemeValue({ eva: 'header .header_grid_layout', geek: '.header-mobile-inner-container', onePage: 'header .header', wind: 'header #header-nav', nova: 'header .header', hero: 'header .header__nav', flash: '#shoplaza-section-header>div>div', lifestyle: '#shoplaza-section-header .header__wrapper', reformia: 'header#header', }, 'header'); const SEARCH_ICON_CLASS = resolveThemeValue({ flash: 'app-smart-icon-search-large-flash', hero: 'app-smart-icon-search-large-hero', geek: 'app-smart-icon-search-large-geek', nova: 'app-smart-icon-search-large-nova', }, 'app-smart-icon-search-large-default'); // 插件位置纠正配置:当商家将插件插入到不可见的区域时,自动迁移到正确的 DOM 位置 // pc / mobile 分别指定 PC 端和移动端的目标父容器选择器,未配置则不做迁移 const PLUGIN_RELOCATION_CONFIG = resolveThemeValue({ reformia: { pc: '.header-layout .header__actions', mobile: '.header-layout .header__actions', }, }, null); // --- 组件 --- class SpzCustomSmartSearchLocation extends SPZ.BaseElement { constructor(element) { super(element); this.outsideCarouselIndex = 0; this.insideCarouselIndex = 0; this.searchItemType = 'icon'; this._originalSearchWrapParent = null; this._skipMobileInit = false; } static deferredMount() { return false; } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } buildCallback() { this.bindResizeListener(); this.registerActions(); } mountCallback(){ this.init(); } unmountCallback(){ this.unbindResizeListener(); this.unregisterActions(); } // --- 元素查找(支持 DocumentFragment 上下文)--- getBlockWrap() { return this.element.closest('.app-smart-product-search-wrap') || document.querySelector('.app-smart-product-search-wrap'); } getBlockContainer() { return this.element.closest('.' + SEARCH_CONTAINER_CLASS) || document.querySelector('.' + SEARCH_CONTAINER_CLASS); } resolveBlockElement(selector, fallbackId) { const wrap = this.getBlockWrap(); const el = wrap?.querySelector(selector) || document.getElementById(fallbackId); return el ? SPZ.whenApiDefined(el) : Promise.resolve(null); } getSmartSearchEl() { return this.resolveBlockElement('ljs-search', 'app-smart-search-64'); } getOutsideItemEl() { return this.resolveBlockElement('.app-smart-search-outside-item', 'app-smart-search-outside-item-64'); } // --- 插件位置纠正 --- relocatePlugin() { if (!PLUGIN_RELOCATION_CONFIG) return; const targetSelector = isDesktop() ? PLUGIN_RELOCATION_CONFIG.pc : PLUGIN_RELOCATION_CONFIG.mobile; if (!targetSelector) return; if (this._relocateTimer) { clearInterval(this._relocateTimer); } const attemptRelocate = () => { const container = this.element.closest('.' + SEARCH_CONTAINER_CLASS) || document.querySelector('#app-smart-product-search-container-64'); if (!container || !document.body.contains(container)) return false; const target = document.querySelector(targetSelector); if (!target) return false; if (target.contains(container)) return true; target.insertBefore(container, target.firstChild); return true; }; if (attemptRelocate()) return; let attempts = 0; this._relocateTimer = setInterval(() => { attempts++; if (attemptRelocate() || attempts >= 20) { clearInterval(this._relocateTimer); this._relocateTimer = null; } }, 500); } // --- 初始化 --- init() { this.relocatePlugin(); this.applySearchIconClass(); this.adjustLifestyleIcon(); if (this.searchItemType === 'input') { this.initInputMode(); return; } // icon 模式 this.initIconMode(); if (isDesktop()) return; if (this._skipMobileInit) return; // icon 模式下的移动端额外处理(处理主题特定的 header 布局) if (!window.__isLoadAppSmartSearch__) { this.initMobileSmartSearch(); if (window.self === window.top) { window.__isLoadAppSmartSearch__ = true; } } } applySearchIconClass() { document.querySelectorAll('.app-smart-icon-search-large').forEach(el => { el.classList.add(SEARCH_ICON_CLASS); }); } adjustLifestyleIcon() { if (!matchTheme('lifestyle') || this.searchItemType === 'input' || isDesktop()) return; const container = this.getBlockContainer(); if (!container) return; const alreadyMoved = !!document.querySelector( '.header__wrapper .container .row.header>div>#app-smart-product-search-container-64' ); if (alreadyMoved) return; const headerDivs = document.querySelectorAll('.header__wrapper .container .row.header>div'); const lastDiv = headerDivs[headerDivs.length - 1]; lastDiv.appendChild(container); } initInputMode() { document.querySelectorAll('.app-smart-icon-search-large').forEach(el => { el.style.display = 'none'; }); const searchWrap = this.getBlockWrap(); const pcContainer = this.getBlockContainer(); const mobileContainer = document.querySelector('.smart-search-mobile-container'); // 记录原始父节点(仅首次) if (!this._originalSearchWrapParent && searchWrap && searchWrap.parentElement) { this._originalSearchWrapParent = searchWrap.parentElement; } if (isDesktop()) { // PC 端:确保 searchWrap 在原始位置并显示 if (mobileContainer) mobileContainer.style.display = 'none'; if (searchWrap && this._originalSearchWrapParent) { // 如果 searchWrap 被移到了移动端容器,移回原始位置 if (mobileContainer && mobileContainer.contains(searchWrap)) { this._originalSearchWrapParent.appendChild(searchWrap); } } if (pcContainer) pcContainer.style.display = 'block'; return; } if (templateName === 'search') { this._skipMobileInit = true; return; } // 移动端:隐藏当前实例的 PC 容器 if (pcContainer) pcContainer.style.display = 'none'; this.ensureMobileSearchContainer(); const mobileContainerAfterEnsure = document.querySelector('.smart-search-mobile-container'); if (!mobileContainerAfterEnsure) return; // 检查移动端容器是否已经有其他实例的内容 const existingWrap = mobileContainerAfterEnsure.querySelector('.app-smart-product-search-wrap'); if (existingWrap && existingWrap !== searchWrap) { // 已有其他实例,当前实例不需要移动,保持隐藏即可 return; } // 将当前实例的 searchWrap 移到移动端容器 if (searchWrap && !mobileContainerAfterEnsure.contains(searchWrap)) { mobileContainerAfterEnsure.appendChild(searchWrap); } mobileContainerAfterEnsure.style.display = ''; } ensureMobileSearchContainer() { if (document.querySelector('.smart-search-mobile-container')) return; const container = document.createElement('div'); container.classList.add('smart-search-mobile-container'); container.classList.add('smart-search-mobile-container-' + THEME_NAME.toLocaleLowerCase()); document.querySelector(HEADER_SELECTOR).appendChild(container); } initIconMode() { document.querySelectorAll('.app-smart-icon-search-large').forEach(el => { el.style.display = 'flex'; }); } initMobileSmartSearch() { if (this.hasMobilePluginParent()) { this.showMobileSmartSearch(); } else { this.addMobileSmartSearch(); } } // --- Action 注册 --- registerActions() { this.registerAction('onSearchInputChange', (invocation) => { this.onSearchInputChange(invocation.args.keyword); }); this.registerAction('onSearchFormSubmit', (invocation) => { this.onSearchFormSubmit(invocation.args.event); }); this.registerAction('onOutsideCarouselIndexChange', (invocation) => { this.outsideCarouselIndex = invocation.args.index || 0; }); this.registerAction('onInsideCarouselIndexChange', (invocation) => { this.insideCarouselIndex = invocation.args.index || 0; }); this.registerAction('getSearchItemType', () => { this.fetchAndApplySearchItemType(); }); this.registerAction('generateHotKeywordList', (invocation) => { this.generateHotKeywordList(invocation.args?.data?.data); }); this.registerAction('onTapHotWord', (invocation) => { this.onTapHotWord(invocation.args.type); }); } // --- 搜索输入 & 提交 --- onSearchInputChange(keyword) { const display = (!keyword || !keyword.length) ? 'block' : 'none'; document.querySelectorAll('.hot-words-carousel-inner-container').forEach(el => { el.style.display = display; }); } onSearchFormSubmit(event) { const keywordArray = event.q || []; const keyword = keywordArray[0]; if (keyword !== null && keyword.length) { this.executeSearch(keywordArray, 1); } else { this.onTapHotWord('inside'); } } executeSearch(value, retryCount) { this.getSmartSearchEl().then((ljsSearch) => { if (!ljsSearch) return; try { ljsSearch.handleSearchSubmit_({ value }); } catch (e) { if (retryCount < 3) { this.executeSearch(value, retryCount + 1); return; } const searchStr = value[0] || ''; const searchResult = ljsSearch.setThinkSearchData_(searchStr); ljsSearch.afterSearching({ query: searchResult.query, url: `${SEARCH_URL}?q=${searchStr}`, queryType: searchResult.queryType, }); } }); } // --- 搜索项类型 --- fetchAndApplySearchItemType() { this.getOutsideItemEl().then((outsideItem) => { if (!outsideItem) return; const type = outsideItem.getData()?.search_item_type; this.searchItemType = type || this.searchItemType; this.init(); }); } // --- 热词 --- generateHotKeywordList(data) { const searchKeywords = data?.hotKeywordList || []; const isShowHotKeyword = data?.isShowHotKeyword || false; this.getOutsideItemEl().then((outsideItem) => { if (!outsideItem) return; const hotwords = outsideItem.getData()?.search_keywords || []; const enrichedKeywords = this.enrichKeywords(searchKeywords, hotwords); this.renderHotKeywords(enrichedKeywords, isShowHotKeyword); }); } enrichKeywords(keywords, hotwords) { return keywords.map((item) => { item.url_obj = item.url_obj || {}; const hotwordItem = hotwords.find(h => h.word === item.word); if (hotwordItem) { item.icon = hotwordItem.icon || ''; } if (!item.urlObj || !item.urlObj.url) { item.urlObj = { ...item.url_obj, url: item.url_obj.type === 'search' ? `${SEARCH_URL}?q=${item.word}` : item.url_obj.url, }; } return item; }); } renderHotKeywords(keywords, isShowHotKeyword) { document.querySelectorAll('.app-hot-keyword-render-child').forEach((el) => { SPZ.whenApiDefined(el).then((hotWordsChild) => { hotWordsChild.render({ list: keywords, isShowHotKeyword }); }); }); } // --- 底纹词工具方法 --- // 将 find_keywords(字符串数组)转换为统一的关键词对象格式 // 优先使用 find_keywords,兜底使用 search_keywords normalizeOutsideKeywords(findKeywords, searchKeywords) { if (findKeywords && findKeywords.length > 0) { return findKeywords.map(keyword => ({ word: keyword, icon: '', pic: '', type: 'find_keyword', url_obj: { type: 'search', url: `${SEARCH_URL}?q=${keyword}`, }, })); } return searchKeywords || []; } // 规范化关键词项的 URL normalizeKeywordUrl(item) { if (!item) return null; if (item.url_obj) { item.url_obj.url = item.url_obj.type === 'search' ? `${SEARCH_URL}?q=${item.word}` : item.url_obj.url; } return item; } onTapHotWord(type) { const index = type === 'inside' ? this.insideCarouselIndex : this.outsideCarouselIndex; this.getOutsideItemEl().then((outsideItem) => { if (!outsideItem) return; const apiData = outsideItem.getData(); const findKeywords = apiData?.find_keywords || []; const searchKeywords = apiData?.search_keywords || []; // 外部和内部 carousel 都使用相同逻辑:优先 find_keywords,兜底 search_keywords const keywords = this.normalizeOutsideKeywords(findKeywords, searchKeywords); const currentItem = this.normalizeKeywordUrl(keywords[index] || null); this.getSmartSearchEl().then((ljsSearch) => { if (!ljsSearch) return; if (currentItem) { ljsSearch.handleHotKeyword_({ word: currentItem.word, query_type: currentItem.type, url: currentItem.url_obj?.url, }); } else { this.executeSearch([''], 1); } }); }); } // --- 底纹词配置 --- getOutsideCarouselConfig() { return this.getOutsideItemEl().then((outsideItem) => { if (!outsideItem) return { outsideCarouselIndex: this.outsideCarouselIndex }; const apiData = outsideItem.getData(); const findKeywords = apiData?.find_keywords || []; const searchKeywords = apiData?.search_keywords || []; const carouselKeywords = this.normalizeOutsideKeywords(findKeywords, searchKeywords); return { ...apiData, search_keywords: carouselKeywords, outsideCarouselIndex: this.outsideCarouselIndex, }; }); } // --- 窗口监听 --- bindResizeListener() { window.removeEventListener('resize', window.smartSearchResizeCallback); window.smartSearchResizeCallback = SPZCore.Types.debounce( this.win, () => { this.fetchAndApplySearchItemType(); }, DELAY ); window.addEventListener('resize', window.smartSearchResizeCallback); } unbindResizeListener() { if (window.smartSearchResizeCallback) { window.removeEventListener('resize', window.smartSearchResizeCallback); window.smartSearchResizeCallback = null; } if (this._relocateTimer) { clearInterval(this._relocateTimer); this._relocateTimer = null; } } unregisterActions() { const actionNames = [ 'onSearchInputChange', 'onSearchFormSubmit', 'onOutsideCarouselIndexChange', 'onInsideCarouselIndexChange', 'getSearchItemType', 'generateHotKeywordList', 'onTapHotWord', ]; actionNames.forEach((name) => { this.registerAction(name, () => {}); }); } // --- 移动端布局:插件父容器模式 --- hasMobilePluginParent() { // reformia 使用 relocatePlugin 统一处理,不走 showMobileSmartSearch return !['geek', 'flash', 'boost', 'reformia'].includes(THEME_NAME.toLocaleLowerCase()); } showMobileSmartSearch() { const PLUGIN_PARENT_SELECTORS = { nova: '.header__mobile #header__plugin-container', hero: '.header__icons .tw-flex.tw-justify-end.tw-items-center.tw-space-x-7', onePage: '.header__mobile #header__plugin-container', wind: '#header-icons .flex.justify-end.items-center', eva: '#header__icons .plugin_content', }; const parentEl = document.querySelector( joinSelectors(Object.values(PLUGIN_PARENT_SELECTORS)) ); if (!parentEl) return; const hasHiddenClass = parentEl.classList.contains('md:hidden') || parentEl.classList.contains('md:tw-hidden'); if (hasHiddenClass) { Array.from(parentEl.children).forEach((child) => { if (!this.isSmartSearchElement(child)) { child.style.display = 'none'; } }); parentEl.classList.remove('md:hidden', 'md:tw-hidden'); } else { const smartSearchEl = Array.from(parentEl.children).find( (child) => this.isSmartSearchElement(child) ); if (smartSearchEl) { smartSearchEl.style.display = 'block'; } } } isSmartSearchElement(el) { return ( el.classList.contains(SEARCH_CONTAINER_CLASS) || el.querySelectorAll(`.${SEARCH_CONTAINER_CLASS}`).length > 0 ); } // --- 移动端布局:图标插入模式 --- addMobileSmartSearch() { const HEADER_ICONS_SELECTORS = { geek: '#header-mobile-container .flex.items-center.justify-end.flex-shrink-0', flash: '#header-layout .header__icons', boost: '.header__mobile-bottom .tw-flex.tw-items-center.tw-justify-end.tw-flex-1', reformia: '.header-layout .header__actions', }; const SMART_SEARCH_ANCESTORS = [ '#header-menu-mobile #menu-drawer', '#menu-drawer .plugin__header-content', '.header__drawer', '.header-content .logo-wrap', '.header_hamburger_sidebar-container', ]; const iconsEl = document.querySelector( joinSelectors(Object.values(HEADER_ICONS_SELECTORS)) ); const searchWrapSelector = joinSelectors( SMART_SEARCH_ANCESTORS.map(a => `${a} .${SEARCH_CONTAINER_CLASS}`) ); const searchWrapEl = document.querySelector(searchWrapSelector); if (!iconsEl || !searchWrapEl) return; iconsEl.insertAdjacentElement('afterbegin', searchWrapEl); } } SPZ.defineElement(TAG, SpzCustomSmartSearchLocation); class SpzCustomSmartSearchToast extends SPZ.BaseElement { constructor(element) { super(element); this.toastDom = null; this.toastTimeout = null; } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } buildCallback(){ this.init(); } init(){ const toast = document.createElement('div'); toast.id = 'spz-custom-smart-search-toast-64'; toast.className = 'spz-custom-smart-search-toast'; document.body.appendChild(toast); this.toastDom = toast; this.registerAction('showToast',(invocation)=>{ this.showToast(invocation.args); }); this.registerAction('hideToast',(invocation)=>{ this.hideToast(invocation.args); }); } showToast({ message, duration = 2000 }){ if( !this.toastDom ) return; this.toastDom.innerHTML = message; this.toastDom.classList.add('smart-search-toast-show'); clearTimeout(this.toastTimeout); this.toastTimeout = setTimeout(() => { this.hideToast(); }, duration); } hideToast(){ if( !this.toastDom ) return; this.toastDom.classList.remove('smart-search-toast-show'); } } SPZ.defineElement('spz-custom-smart-search-toast', SpzCustomSmartSearchToast); class SpzCustomSmartSearchCookie extends SPZ.BaseElement { constructor(element) { super(element); } buildCallback() { this.registerAction('getCookie',(invocation)=>{ this.getCookie(invocation.args); }); } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } getCookie(key) { let cookieMap = {} document.cookie.split(';').map(item=>{ let [key, value] = item.trim().split('=') cookieMap[key] = value }) return cookieMap[key] || ''; } } SPZ.defineElement('spz-custom-smart-search-cookie', SpzCustomSmartSearchCookie); const default_function_name = 'smart_search'; const default_plugin_name = 'smart_search'; const default_module_type = 'smart_search'; const default_module = 'apps'; const default_business_type = 'product_plugin'; const default_event_developer = 'ray'; class SpzCustomSmartSearchTrack extends SPZ.BaseElement { constructor(element) { super(element); } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } buildCallback() { this.registerAction('track', (invocation) => { const { trackType, trackData } = invocation.args; this.track({trackType, trackData}); }); } track({trackType, trackData}) { const { function_name, plugin_name, module_type, module, business_type, event_developer, event_type, event_desc, trackEventInfo, ...otherTrackData } = trackData; window.sa.track(trackType, { function_name: function_name || default_function_name, plugin_name: plugin_name || default_plugin_name, module_type: module_type || default_module_type, module: module || default_module, business_type: business_type || default_business_type, event_developer: event_developer || default_event_developer, event_type: event_type, event_desc: event_desc, ...otherTrackData, event_info: JSON.stringify({ ...(trackEventInfo || {}), }), }); } } SPZ.defineElement('spz-custom-smart-search-track', SpzCustomSmartSearchTrack);
const marketSlot = document.querySelector(".plugin_c_market"); if(marketSlot) { marketSlot.remove(); } const SITE = (window.C_SETTINGS || window[atob('U0hPUExBWlpB')])?.routes?.root || ''; const MARKET_THEME_SETTINGS = `${SITE}/api/markets/theme_setting`; const MARKET_COUNTRIES = `${SITE}/api/markets/countries`; const IP_MATCHING_MARKET = `${SITE}/api/front/station`; const MARKET_LANGUAGES = `${SITE}/api/markets/:id/active_languages`; const URL_REDIRECT = '_market_redirected'; const COOKIE_TIPS_NAME = 'market_no_tips'; const COOKIE_TIPS_EXPIRES = 1000 * 60 * 60 * 24 * 14; const MARKET_COUNTRY_COOKIE = '_shoplazza_market_country'; const MARKET_MODAL_LOADED = '__market_modal_loaded__'; const PROPERTY_SELECTED = 'selected'; const MARKET_PC_HEADER_TRIGGER = 'market-pc-header-trigger'; const MARKET_PC_HEADER_COUNTRY_SELECT_TITLE = 'market-pc-header-country-select-title'; const MARKET_PC_HEADER_LANGUAGE_SELECT_TITLE = 'market-pc-header-language-select-title'; const MARKET_PC_HEADER_LANGUAGE_SELECT_SUB_TITLE = 'market-pc-header-language-select-sub-title'; const MARKET_PC_HEADER_COUNTRY_SELECT_SUB_TITLE = 'market-pc-header-country-select-sub-title'; const MARKET_FLAG = 'market-flag'; const MARKET_SELECT_VALUE = 'market-select-value'; const MARKET_CURRENCY = 'market-currency'; const MARKET_CURRENCY_SYMBOL = 'market-currency-symbol'; const MARKET_SELECT_ITEM = 'market-select-item'; const MARKET_SELECT_ITEM_ID = 'market-select-item-id'; const MARKET_SELECT_LIST = 'market-select-list'; const MARKET_LANGUAGE_SELECT = 'market-language-select'; const MARKET_COUNTRY_SELECT = 'market-country-select'; const MARKET_MODAL = 'market-modal'; const MARKET_GATEWAY_REDIRECT_MODAL = 'market-gateway-redirect-modal'; const MARKET_RESELECT_MODAL = 'market-reselect-modal'; const MARKET_MODAL_FLAG = 'market-modal-flag'; const MARKET_MODAL_COUNTRY_CODE = 'market-modal-country-code'; const MARKET_MODAL_CURRENCY_SYMBOL = 'market-modal-currency-symbol'; const MARKET_MODAL_CLOSE = 'market-modal-close'; const MARKET_MODAL_CONFIRM = 'market-modal-confirm'; const MARKET_MODAL_RESELECT = 'market-modal-reselect'; const MARKET_MODAL_RESELECT_COUNTRY_SELECT = 'market-modal-reselect-country-select'; const DATA_SECTION_TYPE = 'data-section-type'; const SECTION_TYPE_HEADER = 'header'; const SECTION_TYPE_FOOTER = 'footer'; class SpzCustomMarket extends SPZ.BaseElement { constructor(element) { super(element); this.global = null; this.useCountrySelect_ = false; this.useLanguageSelect_ = false; this.useModal_ = false; this.isSingleSelectMode_ = false; this.blockSectionPosition_ = ''; this.isEditMode = false; this.ipMatchingMarket = null; this.marketCountryList = []; this.marketLanguages = []; this.toast = null; } static deferredMount() { return false; } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } buildCallback() { console.debug('Geolocation'); this.xhr_ = SPZServices.xhrFor(this.win); const platform = SPZServices.platformFor(this.win); this.isIOS_ = platform.isIos(); this.action_ = SPZServices.actionServiceForDoc(this.element); this.global = this.win.C_SETTINGS || this.win[atob('U0hPUExBWlpB')]; this.isEditMode = new URLSearchParams(this.win.location.search).get('preview_theme_id') && new URLSearchParams(this.win.location.search).get('oseid'); this.init_(); } showToast_(content) { if (!this.toast) { this.toast = self.document.createElement('ljs-toast'); this.toast.setAttribute('layout', 'nodisplay'); this.toast.setAttribute('hidden', ''); this.toast.setAttribute('duration', '2000'); this.element.appendChild(this.toast); } SPZ.whenApiDefined(this.toast).then((apis) => { apis.showToast(content); }); } triggerEvent_(name, data) { const event = SPZUtils.Event.create(this.win, `${TAG}.${name}`, data); this.action_.trigger(this.element, name, event); } findBlockSectionPosition() { let current = this.element; while (current) { const dataSectionType = current.getAttribute(DATA_SECTION_TYPE); if ( dataSectionType === SECTION_TYPE_HEADER || dataSectionType === SECTION_TYPE_FOOTER ) { return dataSectionType; } current = current.parentElement; } return ''; } async initSettings_() { await this.getMarketThemeSettings_(); if (this.useCountrySelect_) { this.element.setAttribute('use-country-select', true); } if (this.useLanguageSelect_) { this.element.setAttribute('use-language-select', true); } if (this.useModal_) { this.element.setAttribute('use-modal', true); } if ( (this.useCountrySelect_ && !this.useLanguageSelect_) || (!this.useCountrySelect_ && this.useLanguageSelect_) ) { this.isSingleSelectMode_ = true; } this.blockSectionPosition_ = this.findBlockSectionPosition(); this.isPCLayout_ = this.win.matchMedia('(min-width: 960px)').matches; } renderMarketSelectTemplateNode_(clonedTemplate, data) { this.renderTemplateImg_(clonedTemplate, MARKET_FLAG, { src: data?.flag, width: '24', height: '24', }); this.renderTemplateElement_( clonedTemplate, MARKET_SELECT_VALUE, data.value ); this.renderTemplateElement_( clonedTemplate, MARKET_CURRENCY, data?.currency ); this.renderTemplateElement_( clonedTemplate, MARKET_CURRENCY_SYMBOL, data?.currencySymbol ); } bindDropdownOpenClickEvent_(triggerElement, dropdown) { if (!triggerElement) return; const triggerOpen = () => { SPZ.whenApiDefined(dropdown).then((apis) => { if (!dropdown.hasAttribute('open')) { apis.open(triggerElement); } }); }; if (this.isIOS_) { triggerElement.addEventListener('touchstart', triggerOpen); } else { triggerElement.addEventListener('click', triggerOpen); } } createDropdown_(auto=false) { const dropdown = self.document.createElement('ljs-dropdown'); dropdown.setAttribute('layout', 'nodisplay'); if (auto) { dropdown.setAttribute('auto-orientation', 'true'); } if (this.blockSectionPosition_ === 'header') { dropdown.setAttribute('overlay-style', 'top: 4px;'); dropdown.setAttribute('placement', auto ? 'bottom' : 'bottomRight'); } else if (this.blockSectionPosition_ === 'footer') { dropdown.setAttribute('overlay-style', 'top: -4px;'); dropdown.setAttribute('placement', 'top'); } return dropdown; } createDropdownIcon_() { const svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg"); svgElement.setAttribute("class", "market-rotate-180"); svgElement.setAttribute("width", "16"); svgElement.setAttribute("height", "16"); const path1 = document.createElementNS("http://www.w3.org/2000/svg", "path"); path1.setAttribute("d", "M7.47 5.172a.75.75 0 0 1 1.06 0l4.596 4.596a.75.75 0 1 1-1.06 1.06L7.468 6.232a.75.75 0 0 1 0-1.06Z"); path1.setAttribute("fill", "currentColor"); const path2 = document.createElementNS("http://www.w3.org/2000/svg", "path"); path2.setAttribute("d", "M8.533 5.172a.75.75 0 0 1 0 1.06l-4.596 4.596a.75.75 0 1 1-1.061-1.06l4.596-4.596a.75.75 0 0 1 1.06 0Z"); path2.setAttribute("fill", "currentColor"); svgElement.appendChild(path1); svgElement.appendChild(path2); return svgElement; } renderDropdownListWithClickEvent_({ triggerElement, dataList, subTitleRole, listElementWithTemplate, defaultValue, itemHandler, shouldUpdateSelectedItem = false, autoOrientation = false, }) { const dropdown = this.createDropdown_(autoOrientation); const itemTemplate = SPZCore.Dom.scopedQuerySelector( listElementWithTemplate, 'template' ); const clonedItemTemplate = itemTemplate.cloneNode(true); const fragment = self.document.createDocumentFragment(); dataList.forEach((data) => { const tempParentNode = self.document.createElement('div'); const clonedTemplate = self.document.importNode( clonedItemTemplate.content, true ); tempParentNode.appendChild(clonedTemplate); tempParentNode.firstElementChild.classList.add( 'market-select-item' ); this.renderMarketSelectTemplateNode_(tempParentNode, data); const selectItemElement = SPZCore.Dom.scopedQuerySelector( tempParentNode, `[role="${MARKET_SELECT_ITEM}"]` ); if (!selectItemElement) { return; } if (defaultValue === data.value) { if (shouldUpdateSelectedItem) { const clonedSelectItemElement = selectItemElement.cloneNode(true); triggerElement.replaceChild( clonedSelectItemElement, triggerElement.firstElementChild ); } selectItemElement.setAttribute(PROPERTY_SELECTED, ''); } selectItemElement.setAttribute(MARKET_SELECT_ITEM_ID, data.value); fragment.appendChild(tempParentNode.firstElementChild); }); let value = defaultValue; listElementWithTemplate.addEventListener( 'click', (e) => { if (this.isEditMode) { this.showToast_('编辑器下不支持切换国家'); SPZ.whenApiDefined(dropdown).then((apis) => { apis.close(); }); return; } let targetElement = e.target; while (targetElement) { if ( !targetElement || targetElement.getAttribute(MARKET_SELECT_ITEM_ID) ) { break; } targetElement = targetElement.parentNode; } if ( !targetElement || !targetElement.hasAttribute(MARKET_SELECT_ITEM_ID) ) { return; } const selectValue = targetElement.getAttribute(MARKET_SELECT_ITEM_ID); if (value === selectValue) { SPZ.whenApiDefined(dropdown).then((apis) => { apis.close(); }); return; } const selectedData = dataList.find((c) => c.value === selectValue); if (shouldUpdateSelectedItem) { const tempParentNode = self.document.createElement('div'); const clonedTemplate = self.document.importNode( clonedItemTemplate.content, true ); tempParentNode.appendChild(clonedTemplate); this.renderMarketSelectTemplateNode_(tempParentNode, selectedData); tempParentNode.firstElementChild.classList.add( 'market-select-item' ); triggerElement.replaceChild( tempParentNode.firstElementChild, triggerElement.firstElementChild ); } value = selectValue; itemHandler?.(selectedData); SPZ.whenApiDefined(dropdown).then((apis) => { apis.close(); }); }, false ); listElementWithTemplate.removeChild(itemTemplate); listElementWithTemplate.appendChild(fragment); listElementWithTemplate.classList.add('market-select-list'); dropdown.appendChild(listElementWithTemplate); if (subTitleRole) { this.renderSelectTitle_(dropdown, subTitleRole); } return dropdown; } createSelect_({ parent, defaultValue, dataList, listElementWithTemplate, itemHandler, titleRole, }) { const triggerElement = self.document.createElement('div'); triggerElement.classList.add('market-select'); const displayElement = self.document.createElement('div'); triggerElement.appendChild(displayElement); triggerElement.appendChild(this.createDropdownIcon_()); const dropdown = this.renderDropdownListWithClickEvent_({ triggerElement, dataList, listElementWithTemplate, defaultValue, itemHandler, shouldUpdateSelectedItem: true, }); this.bindDropdownOpenClickEvent_(triggerElement, dropdown); const dropdownContainer = self.document.createElement('div'); dropdownContainer.appendChild(triggerElement); dropdownContainer.appendChild(dropdown); dropdownContainer.classList.add('market-select-container'); this.renderSelectTitle_(dropdownContainer, titleRole); parent.appendChild(dropdownContainer); } getListElementWithTemplate_(role) { const template = SPZCore.Dom.scopedQuerySelector( this.element, `[role="${role}"]` ); if (template) { const clonedTemplate = template.cloneNode(true); const tempParent = self.document.createElement("div"); tempParent.appendChild(clonedTemplate.content); const listElementWithTemplate = SPZCore.Dom.scopedQuerySelector( tempParent, `[role="${MARKET_SELECT_LIST}"]` ); return listElementWithTemplate; } return null; } renderSelectTitle_(parent, role) { if (!parent || !role) { return; } const template = SPZCore.Dom.scopedQuerySelector( this.element, `[role="${role}"]` ); const content = self.document.importNode(template.content, true); parent.insertBefore(content, parent.firstElementChild); parent.firstElementChild.classList.add( role.includes('sub') ? 'market-pc-header-select-sub-title' : 'market-pc-header-select-title' ); } initSelect_(rootNode, options) { const parent = rootNode || this.element; if (this.useCountrySelect_) { this.createSelect_({ parent, titleRole: options?.shouldRenderSelectTitle ? MARKET_PC_HEADER_COUNTRY_SELECT_TITLE : undefined, defaultValue: this.getDefaultCountryValue_(), dataList: this.marketCountryList.map(this.mapCountryRenderData_), listElementWithTemplate: this.getListElementWithTemplate_( MARKET_COUNTRY_SELECT ), itemHandler: this.handleCountrySelectChange_.bind(this), }); } if (this.useLanguageSelect_) { this.createSelect_({ parent, titleRole: options?.shouldRenderSelectTitle ? MARKET_PC_HEADER_LANGUAGE_SELECT_TITLE : undefined, defaultValue: this.getDefaultLanguageValue_(), dataList: this.marketLanguages.map(this.mapLanguageRenderData_), listElementWithTemplate: this.getListElementWithTemplate_( MARKET_LANGUAGE_SELECT ), itemHandler: this.handleLanguageSelectChange_.bind(this), }); } } initPcHeaderTriggerElement() { const headerTriggerElementTemplate = SPZCore.Dom.scopedQuerySelector( this.element, `[role="${MARKET_PC_HEADER_TRIGGER}"]` ); const headerTriggerElement = self.document.importNode( headerTriggerElementTemplate.content, true ); const parentDivElement = self.document.createElement('div'); parentDivElement.setAttribute('role', MARKET_PC_HEADER_TRIGGER); parentDivElement.appendChild(headerTriggerElement); this.element.removeChild(headerTriggerElementTemplate); return parentDivElement; } initPanelWithSelect_() { const triggerElement = this.initPcHeaderTriggerElement(); const dropdown = this.createDropdown_(true); const panelElement = self.document.createElement('div'); panelElement.classList.add('market-panel'); this.initSelect_(panelElement, {shouldRenderSelectTitle: true}); dropdown.appendChild(panelElement); this.bindDropdownOpenClickEvent_(triggerElement, dropdown); this.element.appendChild(triggerElement); this.element.insertBefore(dropdown, triggerElement); } initSingleSelectDropdown_() { const triggerElement = this.initPcHeaderTriggerElement(); let dropdown; if (this.useCountrySelect_) { dropdown = this.renderDropdownListWithClickEvent_({ triggerElement, subTitleRole: MARKET_PC_HEADER_COUNTRY_SELECT_SUB_TITLE, dataList: this.marketCountryList.map(this.mapCountryRenderData_), listElementWithTemplate: this.getListElementWithTemplate_( MARKET_COUNTRY_SELECT ), defaultValue: this.getDefaultCountryValue_(), itemHandler: this.handleCountrySelectChange_.bind(this), autoOrientation: true, }); } else if (this.useLanguageSelect_) { dropdown = this.renderDropdownListWithClickEvent_({ triggerElement, subTitleRole: MARKET_PC_HEADER_LANGUAGE_SELECT_SUB_TITLE, dataList: this.marketLanguages.map(this.mapLanguageRenderData_), listElementWithTemplate: this.getListElementWithTemplate_( MARKET_LANGUAGE_SELECT ), defaultValue: this.getDefaultLanguageValue_(), itemHandler: this.handleLanguageSelectChange_.bind(this), autoOrientation: true, }); } if (dropdown) { this.bindDropdownOpenClickEvent_(triggerElement, dropdown); this.element.appendChild(triggerElement); this.element.appendChild(dropdown); } } initEntry_() { if (this.blockSectionPosition_ === SECTION_TYPE_HEADER) { if (this.isSingleSelectMode_) { this.initSingleSelectDropdown_(); } else if (this.useCountrySelect_ && this.useLanguageSelect_) { this.initPanelWithSelect_(); } } this.initSelect_(this.element); } async initData_() { const promises = []; if (this.useCountrySelect_ || this.useModal_) { promises.push(this.getCountryList_(), this.getIpMatchingMarket_()); } if (this.useLanguageSelect_) { promises.push(this.getMarketLanguages_(this.global.market.market_id)); } return Promise.all(promises).then((results) => { const ipMatchingMarket = results.find((result) => result.hasOwnProperty('matching_station') ); const marketCountryList = results.find((result) => result.hasOwnProperty('data') ); if (ipMatchingMarket && marketCountryList) { this.ipMatchingMarket = ipMatchingMarket?.matching_station?.find?.( (item) => item.type === 'multiple_market' ) || { img: marketCountryList.length ? marketCountryList?.[0]?.detail && marketCountryList[0].detail?.flag : '', }; } }); } initModal_() { if ( this.isEditMode || !this.ipMatchingMarket?.market_id || !this.useModal_ || this.hasCookie_(COOKIE_TIPS_NAME) || this.win[MARKET_MODAL_LOADED] ) { return; } this.win[MARKET_MODAL_LOADED] = true; const redirectParam = new URLSearchParams(this.win.location.search).get( URL_REDIRECT ); if (redirectParam) { this.initModalEntry_(MARKET_GATEWAY_REDIRECT_MODAL); } else if ( this.ipMatchingMarket.market_id !== this.global?.market?.market_id ) { this.initModalEntry_(MARKET_MODAL); } } async init_() { await this.initSettings_(); await this.initData_(); this.initEntry_(); this.initModal_(); } getDefaultCountryValue_() { return this.getCookie_(MARKET_COUNTRY_COOKIE) || this.marketCountryList[0]?.country_code; } mapCountryRenderData_(country) { return { flag: country?.detail?.flag || '', value: country?.country_code, currency: country?.currency, currencySymbol: country?.symbol?.val || '', url: country?.url, }; } handleCountrySelectChange_(selectedData) { this.setCookie_(MARKET_COUNTRY_COOKIE, selectedData.value); this.handleChangeURL_(selectedData.url); } getDefaultLanguageValue_() { return this.marketLanguages?.find?.( (lang) => lang.code === this.global?.market?.market_lang )?.name; } mapLanguageRenderData_(language) { return { value: language.name, url: language.url, }; } handleLanguageSelectChange_(selectedData) { this.handleChangeURL_(selectedData.url); } renderTemplateElement_(parent, role, value) { const elements = SPZCore.Dom.scopedQuerySelectorAll( parent, `[role="${role}"]` ); elements.forEach((element) => { if (element && value) { element.innerHTML = value; } }); } renderTemplateImg_(parent, role, options) { const flagElement = SPZCore.Dom.scopedQuerySelector( parent, `[role="${role}"]` ); if (flagElement && options?.src && options?.height && options?.width) { const spzImg = self.document.createElement('ljs-img'); spzImg.setAttribute('src', options.src); spzImg.setAttribute('layout', 'responsive'); spzImg.setAttribute('width', options.width); spzImg.setAttribute('height', options.height); flagElement.appendChild(spzImg); } } handleChangeURL_(url) { if (!!url) { let {origin, pathname, search} = this.win.location; pathname = pathname.replace(SITE, url != '/' ? url : ''); this.win.location.href = `${origin}${pathname}${search}`; } else { this.win.location.reload(true); } } initModalEntry_(role) { const marketModalTemplate = SPZCore.Dom.scopedQuerySelector( this.element, `[role="${role}"]` ); if (marketModalTemplate) { const tempParentNode = self.document.createElement('div'); const clonedTemplate = self.document.importNode( marketModalTemplate.content, true ); tempParentNode.appendChild(clonedTemplate); this.renderTemplateImg_(tempParentNode, MARKET_MODAL_FLAG, { src: this.ipMatchingMarket?.img, width: '80', height: '80', }); this.renderTemplateElement_( tempParentNode, MARKET_MODAL_COUNTRY_CODE, this.ipMatchingMarket?.name ); this.renderTemplateElement_( tempParentNode, MARKET_MODAL_CURRENCY_SYMBOL, this.ipMatchingMarket?.currency_with_symbol ); const lightbox = self.document.createElement('ljs-lightbox'); lightbox.setAttribute('layout', 'nodisplay'); lightbox.appendChild(tempParentNode.firstElementChild); self.document.body.appendChild(lightbox); SPZ.whenApiDefined(lightbox).then((apis) => { SPZCore.Dom.scopedQuerySelectorAll( lightbox, `[role="${MARKET_MODAL_CLOSE}"]` ).forEach((ele) => { ele.addEventListener('click', () => { this.setCookie_(COOKIE_TIPS_NAME, 1, COOKIE_TIPS_EXPIRES); apis.close(); }); }); const marketModalConfirmElement = SPZCore.Dom.scopedQuerySelector( lightbox, `[role="${MARKET_MODAL_CONFIRM}"]` ); if (marketModalConfirmElement && this.ipMatchingMarket?.url) { marketModalConfirmElement.addEventListener('click', () => { this.setCookie_(COOKIE_TIPS_NAME, 1, COOKIE_TIPS_EXPIRES); this.setCookie_(MARKET_COUNTRY_COOKIE, this.ipMatchingMarket?.name); this.handleChangeURL_(this.ipMatchingMarket?.url); }); } const marketModalReselectElement = SPZCore.Dom.scopedQuerySelector( lightbox, `[role="${MARKET_MODAL_RESELECT}"]` ); if (marketModalReselectElement) { marketModalReselectElement.addEventListener('click', () => { this.setCookie_(COOKIE_TIPS_NAME, 1, COOKIE_TIPS_EXPIRES); const marketReselectTemplate = SPZCore.Dom.scopedQuerySelector( this.element, `[role="${MARKET_RESELECT_MODAL}"]` ); if (marketReselectTemplate && lightbox.firstElementChild) { const clonedTemplate = self.document.importNode( marketReselectTemplate.content, true ); const selectParent = SPZCore.Dom.scopedQuerySelector( clonedTemplate, `[role="${MARKET_MODAL_RESELECT_COUNTRY_SELECT}"]` ); if (selectParent) { this.createSelect_({ parent: selectParent, defaultValue: this.getDefaultCountryValue_(), dataList: this.marketCountryList.map( this.mapCountryRenderData_ ), listElementWithTemplate: this.getListElementWithTemplate_( MARKET_COUNTRY_SELECT ), itemHandler: this.handleCountrySelectChange_.bind(this), }); } lightbox.replaceChild(clonedTemplate, lightbox.firstElementChild); SPZCore.Dom.scopedQuerySelectorAll( lightbox, `[role="${MARKET_MODAL_CLOSE}"]` ).forEach((ele) => { ele.addEventListener('click', () => { this.setCookie_(COOKIE_TIPS_NAME, 1, COOKIE_TIPS_EXPIRES); apis.close(); }); }); } }); } apis.open(); }); } } async getMarketThemeSettings_() { return this.xhr_.fetchJson(MARKET_THEME_SETTINGS).then((res) => { this.useCountrySelect_ = !!res.country_selector_enabled; this.useLanguageSelect_ = !!res.language_selector_enabled && this.global?.market?.market_id; this.useModal_ = !!res.redirect_pop_enabled; return res; }); } async getCountryList_() { return this.xhr_.fetchJson(MARKET_COUNTRIES).then((res) => { this.marketCountryList = res.data.length ? res.data : []; return res; }); } async getIpMatchingMarket_() { return this.xhr_.fetchJson(IP_MATCHING_MARKET, { method: 'POST', body: { stations: [], }, }); } async getMarketLanguages_(marketId) { return this.xhr_ .fetchJson(MARKET_LANGUAGES.replace(/:id/gim, marketId)) .then((res) => { this.marketLanguages = res?.languages?.length ? res.languages : []; return res; }); } hasCookie_(name) { const m = self.document.cookie .split(';') .filter((pair) => pair.trim().startsWith(name + '=')); return !!(m && m[0]); } getCookie_(name) { const m = self.document.cookie .split(';') .filter((pair) => pair.trim().startsWith(name + '=')); return m && m[0] ? m[0].split('=')[1] : null; } setCookie_(name, value, expires) { const expiresDate = new Date(Date.now() + expires).toGMTString(); self.document.cookie = `${name}=${encodeURIComponent( value )};expires=${expiresDate};path=/`; } } SPZ.defineElement('spz-custom-market', SpzCustomMarket);
  • const templateName = SHOPLAZZA?.meta?.page?.template_name || ''; const SEARCH_URL = '/search'; const TAG = 'spz-custom-smart-search-location'; const SEARCH_CONTAINER_CLASS = 'app-smart-product-search-container'; const THEME_NAME = window.SHOPLAZZA.theme.merchant_theme_name.replace(/ /g, ''); const BREAKPOINT = 960; const DELAY = 300; // --- 工具函数 --- function matchTheme(target) { return THEME_NAME.toLocaleLowerCase().includes(target.toLocaleLowerCase()); } function resolveThemeValue(themeMap, defaultValue) { let result = defaultValue; for (const key of Object.keys(themeMap)) { if (matchTheme(key)) result = themeMap[key]; } return result; } function joinSelectors(selectorList) { return [...new Set(selectorList)].join(','); } function isDesktop() { return window.matchMedia(`(min-width: ${BREAKPOINT}px)`).matches; } // --- 主题配置 --- const HEADER_SELECTOR = resolveThemeValue({ eva: 'header .header_grid_layout', geek: '.header-mobile-inner-container', onePage: 'header .header', wind: 'header #header-nav', nova: 'header .header', hero: 'header .header__nav', flash: '#shoplaza-section-header>div>div', lifestyle: '#shoplaza-section-header .header__wrapper', reformia: 'header#header', }, 'header'); const SEARCH_ICON_CLASS = resolveThemeValue({ flash: 'app-smart-icon-search-large-flash', hero: 'app-smart-icon-search-large-hero', geek: 'app-smart-icon-search-large-geek', nova: 'app-smart-icon-search-large-nova', }, 'app-smart-icon-search-large-default'); // 插件位置纠正配置:当商家将插件插入到不可见的区域时,自动迁移到正确的 DOM 位置 // pc / mobile 分别指定 PC 端和移动端的目标父容器选择器,未配置则不做迁移 const PLUGIN_RELOCATION_CONFIG = resolveThemeValue({ reformia: { pc: '.header-layout .header__actions', mobile: '.header-layout .header__actions', }, }, null); // --- 组件 --- class SpzCustomSmartSearchLocation extends SPZ.BaseElement { constructor(element) { super(element); this.outsideCarouselIndex = 0; this.insideCarouselIndex = 0; this.searchItemType = 'icon'; this._originalSearchWrapParent = null; this._skipMobileInit = false; } static deferredMount() { return false; } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } buildCallback() { this.bindResizeListener(); this.registerActions(); } mountCallback(){ this.init(); } unmountCallback(){ this.unbindResizeListener(); this.unregisterActions(); } // --- 元素查找(支持 DocumentFragment 上下文)--- getBlockWrap() { return this.element.closest('.app-smart-product-search-wrap') || document.querySelector('.app-smart-product-search-wrap'); } getBlockContainer() { return this.element.closest('.' + SEARCH_CONTAINER_CLASS) || document.querySelector('.' + SEARCH_CONTAINER_CLASS); } resolveBlockElement(selector, fallbackId) { const wrap = this.getBlockWrap(); const el = wrap?.querySelector(selector) || document.getElementById(fallbackId); return el ? SPZ.whenApiDefined(el) : Promise.resolve(null); } getSmartSearchEl() { return this.resolveBlockElement('ljs-search', 'app-smart-search-38'); } getOutsideItemEl() { return this.resolveBlockElement('.app-smart-search-outside-item', 'app-smart-search-outside-item-38'); } // --- 插件位置纠正 --- relocatePlugin() { if (!PLUGIN_RELOCATION_CONFIG) return; const targetSelector = isDesktop() ? PLUGIN_RELOCATION_CONFIG.pc : PLUGIN_RELOCATION_CONFIG.mobile; if (!targetSelector) return; if (this._relocateTimer) { clearInterval(this._relocateTimer); } const attemptRelocate = () => { const container = this.element.closest('.' + SEARCH_CONTAINER_CLASS) || document.querySelector('#app-smart-product-search-container-38'); if (!container || !document.body.contains(container)) return false; const target = document.querySelector(targetSelector); if (!target) return false; if (target.contains(container)) return true; target.insertBefore(container, target.firstChild); return true; }; if (attemptRelocate()) return; let attempts = 0; this._relocateTimer = setInterval(() => { attempts++; if (attemptRelocate() || attempts >= 20) { clearInterval(this._relocateTimer); this._relocateTimer = null; } }, 500); } // --- 初始化 --- init() { this.relocatePlugin(); this.applySearchIconClass(); this.adjustLifestyleIcon(); if (this.searchItemType === 'input') { this.initInputMode(); return; } // icon 模式 this.initIconMode(); if (isDesktop()) return; if (this._skipMobileInit) return; // icon 模式下的移动端额外处理(处理主题特定的 header 布局) if (!window.__isLoadAppSmartSearch__) { this.initMobileSmartSearch(); if (window.self === window.top) { window.__isLoadAppSmartSearch__ = true; } } } applySearchIconClass() { document.querySelectorAll('.app-smart-icon-search-large').forEach(el => { el.classList.add(SEARCH_ICON_CLASS); }); } adjustLifestyleIcon() { if (!matchTheme('lifestyle') || this.searchItemType === 'input' || isDesktop()) return; const container = this.getBlockContainer(); if (!container) return; const alreadyMoved = !!document.querySelector( '.header__wrapper .container .row.header>div>#app-smart-product-search-container-38' ); if (alreadyMoved) return; const headerDivs = document.querySelectorAll('.header__wrapper .container .row.header>div'); const lastDiv = headerDivs[headerDivs.length - 1]; lastDiv.appendChild(container); } initInputMode() { document.querySelectorAll('.app-smart-icon-search-large').forEach(el => { el.style.display = 'none'; }); const searchWrap = this.getBlockWrap(); const pcContainer = this.getBlockContainer(); const mobileContainer = document.querySelector('.smart-search-mobile-container'); // 记录原始父节点(仅首次) if (!this._originalSearchWrapParent && searchWrap && searchWrap.parentElement) { this._originalSearchWrapParent = searchWrap.parentElement; } if (isDesktop()) { // PC 端:确保 searchWrap 在原始位置并显示 if (mobileContainer) mobileContainer.style.display = 'none'; if (searchWrap && this._originalSearchWrapParent) { // 如果 searchWrap 被移到了移动端容器,移回原始位置 if (mobileContainer && mobileContainer.contains(searchWrap)) { this._originalSearchWrapParent.appendChild(searchWrap); } } if (pcContainer) pcContainer.style.display = 'block'; return; } if (templateName === 'search') { this._skipMobileInit = true; return; } // 移动端:隐藏当前实例的 PC 容器 if (pcContainer) pcContainer.style.display = 'none'; this.ensureMobileSearchContainer(); const mobileContainerAfterEnsure = document.querySelector('.smart-search-mobile-container'); if (!mobileContainerAfterEnsure) return; // 检查移动端容器是否已经有其他实例的内容 const existingWrap = mobileContainerAfterEnsure.querySelector('.app-smart-product-search-wrap'); if (existingWrap && existingWrap !== searchWrap) { // 已有其他实例,当前实例不需要移动,保持隐藏即可 return; } // 将当前实例的 searchWrap 移到移动端容器 if (searchWrap && !mobileContainerAfterEnsure.contains(searchWrap)) { mobileContainerAfterEnsure.appendChild(searchWrap); } mobileContainerAfterEnsure.style.display = ''; } ensureMobileSearchContainer() { if (document.querySelector('.smart-search-mobile-container')) return; const container = document.createElement('div'); container.classList.add('smart-search-mobile-container'); container.classList.add('smart-search-mobile-container-' + THEME_NAME.toLocaleLowerCase()); document.querySelector(HEADER_SELECTOR).appendChild(container); } initIconMode() { document.querySelectorAll('.app-smart-icon-search-large').forEach(el => { el.style.display = 'flex'; }); } initMobileSmartSearch() { if (this.hasMobilePluginParent()) { this.showMobileSmartSearch(); } else { this.addMobileSmartSearch(); } } // --- Action 注册 --- registerActions() { this.registerAction('onSearchInputChange', (invocation) => { this.onSearchInputChange(invocation.args.keyword); }); this.registerAction('onSearchFormSubmit', (invocation) => { this.onSearchFormSubmit(invocation.args.event); }); this.registerAction('onOutsideCarouselIndexChange', (invocation) => { this.outsideCarouselIndex = invocation.args.index || 0; }); this.registerAction('onInsideCarouselIndexChange', (invocation) => { this.insideCarouselIndex = invocation.args.index || 0; }); this.registerAction('getSearchItemType', () => { this.fetchAndApplySearchItemType(); }); this.registerAction('generateHotKeywordList', (invocation) => { this.generateHotKeywordList(invocation.args?.data?.data); }); this.registerAction('onTapHotWord', (invocation) => { this.onTapHotWord(invocation.args.type); }); } // --- 搜索输入 & 提交 --- onSearchInputChange(keyword) { const display = (!keyword || !keyword.length) ? 'block' : 'none'; document.querySelectorAll('.hot-words-carousel-inner-container').forEach(el => { el.style.display = display; }); } onSearchFormSubmit(event) { const keywordArray = event.q || []; const keyword = keywordArray[0]; if (keyword !== null && keyword.length) { this.executeSearch(keywordArray, 1); } else { this.onTapHotWord('inside'); } } executeSearch(value, retryCount) { this.getSmartSearchEl().then((ljsSearch) => { if (!ljsSearch) return; try { ljsSearch.handleSearchSubmit_({ value }); } catch (e) { if (retryCount < 3) { this.executeSearch(value, retryCount + 1); return; } const searchStr = value[0] || ''; const searchResult = ljsSearch.setThinkSearchData_(searchStr); ljsSearch.afterSearching({ query: searchResult.query, url: `${SEARCH_URL}?q=${searchStr}`, queryType: searchResult.queryType, }); } }); } // --- 搜索项类型 --- fetchAndApplySearchItemType() { this.getOutsideItemEl().then((outsideItem) => { if (!outsideItem) return; const type = outsideItem.getData()?.search_item_type; this.searchItemType = type || this.searchItemType; this.init(); }); } // --- 热词 --- generateHotKeywordList(data) { const searchKeywords = data?.hotKeywordList || []; const isShowHotKeyword = data?.isShowHotKeyword || false; this.getOutsideItemEl().then((outsideItem) => { if (!outsideItem) return; const hotwords = outsideItem.getData()?.search_keywords || []; const enrichedKeywords = this.enrichKeywords(searchKeywords, hotwords); this.renderHotKeywords(enrichedKeywords, isShowHotKeyword); }); } enrichKeywords(keywords, hotwords) { return keywords.map((item) => { item.url_obj = item.url_obj || {}; const hotwordItem = hotwords.find(h => h.word === item.word); if (hotwordItem) { item.icon = hotwordItem.icon || ''; } if (!item.urlObj || !item.urlObj.url) { item.urlObj = { ...item.url_obj, url: item.url_obj.type === 'search' ? `${SEARCH_URL}?q=${item.word}` : item.url_obj.url, }; } return item; }); } renderHotKeywords(keywords, isShowHotKeyword) { document.querySelectorAll('.app-hot-keyword-render-child').forEach((el) => { SPZ.whenApiDefined(el).then((hotWordsChild) => { hotWordsChild.render({ list: keywords, isShowHotKeyword }); }); }); } // --- 底纹词工具方法 --- // 将 find_keywords(字符串数组)转换为统一的关键词对象格式 // 优先使用 find_keywords,兜底使用 search_keywords normalizeOutsideKeywords(findKeywords, searchKeywords) { if (findKeywords && findKeywords.length > 0) { return findKeywords.map(keyword => ({ word: keyword, icon: '', pic: '', type: 'find_keyword', url_obj: { type: 'search', url: `${SEARCH_URL}?q=${keyword}`, }, })); } return searchKeywords || []; } // 规范化关键词项的 URL normalizeKeywordUrl(item) { if (!item) return null; if (item.url_obj) { item.url_obj.url = item.url_obj.type === 'search' ? `${SEARCH_URL}?q=${item.word}` : item.url_obj.url; } return item; } onTapHotWord(type) { const index = type === 'inside' ? this.insideCarouselIndex : this.outsideCarouselIndex; this.getOutsideItemEl().then((outsideItem) => { if (!outsideItem) return; const apiData = outsideItem.getData(); const findKeywords = apiData?.find_keywords || []; const searchKeywords = apiData?.search_keywords || []; // 外部和内部 carousel 都使用相同逻辑:优先 find_keywords,兜底 search_keywords const keywords = this.normalizeOutsideKeywords(findKeywords, searchKeywords); const currentItem = this.normalizeKeywordUrl(keywords[index] || null); this.getSmartSearchEl().then((ljsSearch) => { if (!ljsSearch) return; if (currentItem) { ljsSearch.handleHotKeyword_({ word: currentItem.word, query_type: currentItem.type, url: currentItem.url_obj?.url, }); } else { this.executeSearch([''], 1); } }); }); } // --- 底纹词配置 --- getOutsideCarouselConfig() { return this.getOutsideItemEl().then((outsideItem) => { if (!outsideItem) return { outsideCarouselIndex: this.outsideCarouselIndex }; const apiData = outsideItem.getData(); const findKeywords = apiData?.find_keywords || []; const searchKeywords = apiData?.search_keywords || []; const carouselKeywords = this.normalizeOutsideKeywords(findKeywords, searchKeywords); return { ...apiData, search_keywords: carouselKeywords, outsideCarouselIndex: this.outsideCarouselIndex, }; }); } // --- 窗口监听 --- bindResizeListener() { window.removeEventListener('resize', window.smartSearchResizeCallback); window.smartSearchResizeCallback = SPZCore.Types.debounce( this.win, () => { this.fetchAndApplySearchItemType(); }, DELAY ); window.addEventListener('resize', window.smartSearchResizeCallback); } unbindResizeListener() { if (window.smartSearchResizeCallback) { window.removeEventListener('resize', window.smartSearchResizeCallback); window.smartSearchResizeCallback = null; } if (this._relocateTimer) { clearInterval(this._relocateTimer); this._relocateTimer = null; } } unregisterActions() { const actionNames = [ 'onSearchInputChange', 'onSearchFormSubmit', 'onOutsideCarouselIndexChange', 'onInsideCarouselIndexChange', 'getSearchItemType', 'generateHotKeywordList', 'onTapHotWord', ]; actionNames.forEach((name) => { this.registerAction(name, () => {}); }); } // --- 移动端布局:插件父容器模式 --- hasMobilePluginParent() { // reformia 使用 relocatePlugin 统一处理,不走 showMobileSmartSearch return !['geek', 'flash', 'boost', 'reformia'].includes(THEME_NAME.toLocaleLowerCase()); } showMobileSmartSearch() { const PLUGIN_PARENT_SELECTORS = { nova: '.header__mobile #header__plugin-container', hero: '.header__icons .tw-flex.tw-justify-end.tw-items-center.tw-space-x-7', onePage: '.header__mobile #header__plugin-container', wind: '#header-icons .flex.justify-end.items-center', eva: '#header__icons .plugin_content', }; const parentEl = document.querySelector( joinSelectors(Object.values(PLUGIN_PARENT_SELECTORS)) ); if (!parentEl) return; const hasHiddenClass = parentEl.classList.contains('md:hidden') || parentEl.classList.contains('md:tw-hidden'); if (hasHiddenClass) { Array.from(parentEl.children).forEach((child) => { if (!this.isSmartSearchElement(child)) { child.style.display = 'none'; } }); parentEl.classList.remove('md:hidden', 'md:tw-hidden'); } else { const smartSearchEl = Array.from(parentEl.children).find( (child) => this.isSmartSearchElement(child) ); if (smartSearchEl) { smartSearchEl.style.display = 'block'; } } } isSmartSearchElement(el) { return ( el.classList.contains(SEARCH_CONTAINER_CLASS) || el.querySelectorAll(`.${SEARCH_CONTAINER_CLASS}`).length > 0 ); } // --- 移动端布局:图标插入模式 --- addMobileSmartSearch() { const HEADER_ICONS_SELECTORS = { geek: '#header-mobile-container .flex.items-center.justify-end.flex-shrink-0', flash: '#header-layout .header__icons', boost: '.header__mobile-bottom .tw-flex.tw-items-center.tw-justify-end.tw-flex-1', reformia: '.header-layout .header__actions', }; const SMART_SEARCH_ANCESTORS = [ '#header-menu-mobile #menu-drawer', '#menu-drawer .plugin__header-content', '.header__drawer', '.header-content .logo-wrap', '.header_hamburger_sidebar-container', ]; const iconsEl = document.querySelector( joinSelectors(Object.values(HEADER_ICONS_SELECTORS)) ); const searchWrapSelector = joinSelectors( SMART_SEARCH_ANCESTORS.map(a => `${a} .${SEARCH_CONTAINER_CLASS}`) ); const searchWrapEl = document.querySelector(searchWrapSelector); if (!iconsEl || !searchWrapEl) return; iconsEl.insertAdjacentElement('afterbegin', searchWrapEl); } } SPZ.defineElement(TAG, SpzCustomSmartSearchLocation); class SpzCustomSmartSearchToast extends SPZ.BaseElement { constructor(element) { super(element); this.toastDom = null; this.toastTimeout = null; } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } buildCallback(){ this.init(); } init(){ const toast = document.createElement('div'); toast.id = 'spz-custom-smart-search-toast-38'; toast.className = 'spz-custom-smart-search-toast'; document.body.appendChild(toast); this.toastDom = toast; this.registerAction('showToast',(invocation)=>{ this.showToast(invocation.args); }); this.registerAction('hideToast',(invocation)=>{ this.hideToast(invocation.args); }); } showToast({ message, duration = 2000 }){ if( !this.toastDom ) return; this.toastDom.innerHTML = message; this.toastDom.classList.add('smart-search-toast-show'); clearTimeout(this.toastTimeout); this.toastTimeout = setTimeout(() => { this.hideToast(); }, duration); } hideToast(){ if( !this.toastDom ) return; this.toastDom.classList.remove('smart-search-toast-show'); } } SPZ.defineElement('spz-custom-smart-search-toast', SpzCustomSmartSearchToast); class SpzCustomSmartSearchCookie extends SPZ.BaseElement { constructor(element) { super(element); } buildCallback() { this.registerAction('getCookie',(invocation)=>{ this.getCookie(invocation.args); }); } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } getCookie(key) { let cookieMap = {} document.cookie.split(';').map(item=>{ let [key, value] = item.trim().split('=') cookieMap[key] = value }) return cookieMap[key] || ''; } } SPZ.defineElement('spz-custom-smart-search-cookie', SpzCustomSmartSearchCookie); const default_function_name = 'smart_search'; const default_plugin_name = 'smart_search'; const default_module_type = 'smart_search'; const default_module = 'apps'; const default_business_type = 'product_plugin'; const default_event_developer = 'ray'; class SpzCustomSmartSearchTrack extends SPZ.BaseElement { constructor(element) { super(element); } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } buildCallback() { this.registerAction('track', (invocation) => { const { trackType, trackData } = invocation.args; this.track({trackType, trackData}); }); } track({trackType, trackData}) { const { function_name, plugin_name, module_type, module, business_type, event_developer, event_type, event_desc, trackEventInfo, ...otherTrackData } = trackData; window.sa.track(trackType, { function_name: function_name || default_function_name, plugin_name: plugin_name || default_plugin_name, module_type: module_type || default_module_type, module: module || default_module, business_type: business_type || default_business_type, event_developer: event_developer || default_event_developer, event_type: event_type, event_desc: event_desc, ...otherTrackData, event_info: JSON.stringify({ ...(trackEventInfo || {}), }), }); } } SPZ.defineElement('spz-custom-smart-search-track', SpzCustomSmartSearchTrack);
    const marketSlot = document.querySelector(".plugin_c_market"); if(marketSlot) { marketSlot.remove(); } const SITE = (window.C_SETTINGS || window[atob('U0hPUExBWlpB')])?.routes?.root || ''; const MARKET_THEME_SETTINGS = `${SITE}/api/markets/theme_setting`; const MARKET_COUNTRIES = `${SITE}/api/markets/countries`; const IP_MATCHING_MARKET = `${SITE}/api/front/station`; const MARKET_LANGUAGES = `${SITE}/api/markets/:id/active_languages`; const URL_REDIRECT = '_market_redirected'; const COOKIE_TIPS_NAME = 'market_no_tips'; const COOKIE_TIPS_EXPIRES = 1000 * 60 * 60 * 24 * 14; const MARKET_COUNTRY_COOKIE = '_shoplazza_market_country'; const MARKET_MODAL_LOADED = '__market_modal_loaded__'; const PROPERTY_SELECTED = 'selected'; const MARKET_PC_HEADER_TRIGGER = 'market-pc-header-trigger'; const MARKET_PC_HEADER_COUNTRY_SELECT_TITLE = 'market-pc-header-country-select-title'; const MARKET_PC_HEADER_LANGUAGE_SELECT_TITLE = 'market-pc-header-language-select-title'; const MARKET_PC_HEADER_LANGUAGE_SELECT_SUB_TITLE = 'market-pc-header-language-select-sub-title'; const MARKET_PC_HEADER_COUNTRY_SELECT_SUB_TITLE = 'market-pc-header-country-select-sub-title'; const MARKET_FLAG = 'market-flag'; const MARKET_SELECT_VALUE = 'market-select-value'; const MARKET_CURRENCY = 'market-currency'; const MARKET_CURRENCY_SYMBOL = 'market-currency-symbol'; const MARKET_SELECT_ITEM = 'market-select-item'; const MARKET_SELECT_ITEM_ID = 'market-select-item-id'; const MARKET_SELECT_LIST = 'market-select-list'; const MARKET_LANGUAGE_SELECT = 'market-language-select'; const MARKET_COUNTRY_SELECT = 'market-country-select'; const MARKET_MODAL = 'market-modal'; const MARKET_GATEWAY_REDIRECT_MODAL = 'market-gateway-redirect-modal'; const MARKET_RESELECT_MODAL = 'market-reselect-modal'; const MARKET_MODAL_FLAG = 'market-modal-flag'; const MARKET_MODAL_COUNTRY_CODE = 'market-modal-country-code'; const MARKET_MODAL_CURRENCY_SYMBOL = 'market-modal-currency-symbol'; const MARKET_MODAL_CLOSE = 'market-modal-close'; const MARKET_MODAL_CONFIRM = 'market-modal-confirm'; const MARKET_MODAL_RESELECT = 'market-modal-reselect'; const MARKET_MODAL_RESELECT_COUNTRY_SELECT = 'market-modal-reselect-country-select'; const DATA_SECTION_TYPE = 'data-section-type'; const SECTION_TYPE_HEADER = 'header'; const SECTION_TYPE_FOOTER = 'footer'; class SpzCustomMarket extends SPZ.BaseElement { constructor(element) { super(element); this.global = null; this.useCountrySelect_ = false; this.useLanguageSelect_ = false; this.useModal_ = false; this.isSingleSelectMode_ = false; this.blockSectionPosition_ = ''; this.isEditMode = false; this.ipMatchingMarket = null; this.marketCountryList = []; this.marketLanguages = []; this.toast = null; } static deferredMount() { return false; } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } buildCallback() { console.debug('Geolocation'); this.xhr_ = SPZServices.xhrFor(this.win); const platform = SPZServices.platformFor(this.win); this.isIOS_ = platform.isIos(); this.action_ = SPZServices.actionServiceForDoc(this.element); this.global = this.win.C_SETTINGS || this.win[atob('U0hPUExBWlpB')]; this.isEditMode = new URLSearchParams(this.win.location.search).get('preview_theme_id') && new URLSearchParams(this.win.location.search).get('oseid'); this.init_(); } showToast_(content) { if (!this.toast) { this.toast = self.document.createElement('ljs-toast'); this.toast.setAttribute('layout', 'nodisplay'); this.toast.setAttribute('hidden', ''); this.toast.setAttribute('duration', '2000'); this.element.appendChild(this.toast); } SPZ.whenApiDefined(this.toast).then((apis) => { apis.showToast(content); }); } triggerEvent_(name, data) { const event = SPZUtils.Event.create(this.win, `${TAG}.${name}`, data); this.action_.trigger(this.element, name, event); } findBlockSectionPosition() { let current = this.element; while (current) { const dataSectionType = current.getAttribute(DATA_SECTION_TYPE); if ( dataSectionType === SECTION_TYPE_HEADER || dataSectionType === SECTION_TYPE_FOOTER ) { return dataSectionType; } current = current.parentElement; } return ''; } async initSettings_() { await this.getMarketThemeSettings_(); if (this.useCountrySelect_) { this.element.setAttribute('use-country-select', true); } if (this.useLanguageSelect_) { this.element.setAttribute('use-language-select', true); } if (this.useModal_) { this.element.setAttribute('use-modal', true); } if ( (this.useCountrySelect_ && !this.useLanguageSelect_) || (!this.useCountrySelect_ && this.useLanguageSelect_) ) { this.isSingleSelectMode_ = true; } this.blockSectionPosition_ = this.findBlockSectionPosition(); this.isPCLayout_ = this.win.matchMedia('(min-width: 960px)').matches; } renderMarketSelectTemplateNode_(clonedTemplate, data) { this.renderTemplateImg_(clonedTemplate, MARKET_FLAG, { src: data?.flag, width: '24', height: '24', }); this.renderTemplateElement_( clonedTemplate, MARKET_SELECT_VALUE, data.value ); this.renderTemplateElement_( clonedTemplate, MARKET_CURRENCY, data?.currency ); this.renderTemplateElement_( clonedTemplate, MARKET_CURRENCY_SYMBOL, data?.currencySymbol ); } bindDropdownOpenClickEvent_(triggerElement, dropdown) { if (!triggerElement) return; const triggerOpen = () => { SPZ.whenApiDefined(dropdown).then((apis) => { if (!dropdown.hasAttribute('open')) { apis.open(triggerElement); } }); }; if (this.isIOS_) { triggerElement.addEventListener('touchstart', triggerOpen); } else { triggerElement.addEventListener('click', triggerOpen); } } createDropdown_(auto=false) { const dropdown = self.document.createElement('ljs-dropdown'); dropdown.setAttribute('layout', 'nodisplay'); if (auto) { dropdown.setAttribute('auto-orientation', 'true'); } if (this.blockSectionPosition_ === 'header') { dropdown.setAttribute('overlay-style', 'top: 4px;'); dropdown.setAttribute('placement', auto ? 'bottom' : 'bottomRight'); } else if (this.blockSectionPosition_ === 'footer') { dropdown.setAttribute('overlay-style', 'top: -4px;'); dropdown.setAttribute('placement', 'top'); } return dropdown; } createDropdownIcon_() { const svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg"); svgElement.setAttribute("class", "market-rotate-180"); svgElement.setAttribute("width", "16"); svgElement.setAttribute("height", "16"); const path1 = document.createElementNS("http://www.w3.org/2000/svg", "path"); path1.setAttribute("d", "M7.47 5.172a.75.75 0 0 1 1.06 0l4.596 4.596a.75.75 0 1 1-1.06 1.06L7.468 6.232a.75.75 0 0 1 0-1.06Z"); path1.setAttribute("fill", "currentColor"); const path2 = document.createElementNS("http://www.w3.org/2000/svg", "path"); path2.setAttribute("d", "M8.533 5.172a.75.75 0 0 1 0 1.06l-4.596 4.596a.75.75 0 1 1-1.061-1.06l4.596-4.596a.75.75 0 0 1 1.06 0Z"); path2.setAttribute("fill", "currentColor"); svgElement.appendChild(path1); svgElement.appendChild(path2); return svgElement; } renderDropdownListWithClickEvent_({ triggerElement, dataList, subTitleRole, listElementWithTemplate, defaultValue, itemHandler, shouldUpdateSelectedItem = false, autoOrientation = false, }) { const dropdown = this.createDropdown_(autoOrientation); const itemTemplate = SPZCore.Dom.scopedQuerySelector( listElementWithTemplate, 'template' ); const clonedItemTemplate = itemTemplate.cloneNode(true); const fragment = self.document.createDocumentFragment(); dataList.forEach((data) => { const tempParentNode = self.document.createElement('div'); const clonedTemplate = self.document.importNode( clonedItemTemplate.content, true ); tempParentNode.appendChild(clonedTemplate); tempParentNode.firstElementChild.classList.add( 'market-select-item' ); this.renderMarketSelectTemplateNode_(tempParentNode, data); const selectItemElement = SPZCore.Dom.scopedQuerySelector( tempParentNode, `[role="${MARKET_SELECT_ITEM}"]` ); if (!selectItemElement) { return; } if (defaultValue === data.value) { if (shouldUpdateSelectedItem) { const clonedSelectItemElement = selectItemElement.cloneNode(true); triggerElement.replaceChild( clonedSelectItemElement, triggerElement.firstElementChild ); } selectItemElement.setAttribute(PROPERTY_SELECTED, ''); } selectItemElement.setAttribute(MARKET_SELECT_ITEM_ID, data.value); fragment.appendChild(tempParentNode.firstElementChild); }); let value = defaultValue; listElementWithTemplate.addEventListener( 'click', (e) => { if (this.isEditMode) { this.showToast_('编辑器下不支持切换国家'); SPZ.whenApiDefined(dropdown).then((apis) => { apis.close(); }); return; } let targetElement = e.target; while (targetElement) { if ( !targetElement || targetElement.getAttribute(MARKET_SELECT_ITEM_ID) ) { break; } targetElement = targetElement.parentNode; } if ( !targetElement || !targetElement.hasAttribute(MARKET_SELECT_ITEM_ID) ) { return; } const selectValue = targetElement.getAttribute(MARKET_SELECT_ITEM_ID); if (value === selectValue) { SPZ.whenApiDefined(dropdown).then((apis) => { apis.close(); }); return; } const selectedData = dataList.find((c) => c.value === selectValue); if (shouldUpdateSelectedItem) { const tempParentNode = self.document.createElement('div'); const clonedTemplate = self.document.importNode( clonedItemTemplate.content, true ); tempParentNode.appendChild(clonedTemplate); this.renderMarketSelectTemplateNode_(tempParentNode, selectedData); tempParentNode.firstElementChild.classList.add( 'market-select-item' ); triggerElement.replaceChild( tempParentNode.firstElementChild, triggerElement.firstElementChild ); } value = selectValue; itemHandler?.(selectedData); SPZ.whenApiDefined(dropdown).then((apis) => { apis.close(); }); }, false ); listElementWithTemplate.removeChild(itemTemplate); listElementWithTemplate.appendChild(fragment); listElementWithTemplate.classList.add('market-select-list'); dropdown.appendChild(listElementWithTemplate); if (subTitleRole) { this.renderSelectTitle_(dropdown, subTitleRole); } return dropdown; } createSelect_({ parent, defaultValue, dataList, listElementWithTemplate, itemHandler, titleRole, }) { const triggerElement = self.document.createElement('div'); triggerElement.classList.add('market-select'); const displayElement = self.document.createElement('div'); triggerElement.appendChild(displayElement); triggerElement.appendChild(this.createDropdownIcon_()); const dropdown = this.renderDropdownListWithClickEvent_({ triggerElement, dataList, listElementWithTemplate, defaultValue, itemHandler, shouldUpdateSelectedItem: true, }); this.bindDropdownOpenClickEvent_(triggerElement, dropdown); const dropdownContainer = self.document.createElement('div'); dropdownContainer.appendChild(triggerElement); dropdownContainer.appendChild(dropdown); dropdownContainer.classList.add('market-select-container'); this.renderSelectTitle_(dropdownContainer, titleRole); parent.appendChild(dropdownContainer); } getListElementWithTemplate_(role) { const template = SPZCore.Dom.scopedQuerySelector( this.element, `[role="${role}"]` ); if (template) { const clonedTemplate = template.cloneNode(true); const tempParent = self.document.createElement("div"); tempParent.appendChild(clonedTemplate.content); const listElementWithTemplate = SPZCore.Dom.scopedQuerySelector( tempParent, `[role="${MARKET_SELECT_LIST}"]` ); return listElementWithTemplate; } return null; } renderSelectTitle_(parent, role) { if (!parent || !role) { return; } const template = SPZCore.Dom.scopedQuerySelector( this.element, `[role="${role}"]` ); const content = self.document.importNode(template.content, true); parent.insertBefore(content, parent.firstElementChild); parent.firstElementChild.classList.add( role.includes('sub') ? 'market-pc-header-select-sub-title' : 'market-pc-header-select-title' ); } initSelect_(rootNode, options) { const parent = rootNode || this.element; if (this.useCountrySelect_) { this.createSelect_({ parent, titleRole: options?.shouldRenderSelectTitle ? MARKET_PC_HEADER_COUNTRY_SELECT_TITLE : undefined, defaultValue: this.getDefaultCountryValue_(), dataList: this.marketCountryList.map(this.mapCountryRenderData_), listElementWithTemplate: this.getListElementWithTemplate_( MARKET_COUNTRY_SELECT ), itemHandler: this.handleCountrySelectChange_.bind(this), }); } if (this.useLanguageSelect_) { this.createSelect_({ parent, titleRole: options?.shouldRenderSelectTitle ? MARKET_PC_HEADER_LANGUAGE_SELECT_TITLE : undefined, defaultValue: this.getDefaultLanguageValue_(), dataList: this.marketLanguages.map(this.mapLanguageRenderData_), listElementWithTemplate: this.getListElementWithTemplate_( MARKET_LANGUAGE_SELECT ), itemHandler: this.handleLanguageSelectChange_.bind(this), }); } } initPcHeaderTriggerElement() { const headerTriggerElementTemplate = SPZCore.Dom.scopedQuerySelector( this.element, `[role="${MARKET_PC_HEADER_TRIGGER}"]` ); const headerTriggerElement = self.document.importNode( headerTriggerElementTemplate.content, true ); const parentDivElement = self.document.createElement('div'); parentDivElement.setAttribute('role', MARKET_PC_HEADER_TRIGGER); parentDivElement.appendChild(headerTriggerElement); this.element.removeChild(headerTriggerElementTemplate); return parentDivElement; } initPanelWithSelect_() { const triggerElement = this.initPcHeaderTriggerElement(); const dropdown = this.createDropdown_(true); const panelElement = self.document.createElement('div'); panelElement.classList.add('market-panel'); this.initSelect_(panelElement, {shouldRenderSelectTitle: true}); dropdown.appendChild(panelElement); this.bindDropdownOpenClickEvent_(triggerElement, dropdown); this.element.appendChild(triggerElement); this.element.insertBefore(dropdown, triggerElement); } initSingleSelectDropdown_() { const triggerElement = this.initPcHeaderTriggerElement(); let dropdown; if (this.useCountrySelect_) { dropdown = this.renderDropdownListWithClickEvent_({ triggerElement, subTitleRole: MARKET_PC_HEADER_COUNTRY_SELECT_SUB_TITLE, dataList: this.marketCountryList.map(this.mapCountryRenderData_), listElementWithTemplate: this.getListElementWithTemplate_( MARKET_COUNTRY_SELECT ), defaultValue: this.getDefaultCountryValue_(), itemHandler: this.handleCountrySelectChange_.bind(this), autoOrientation: true, }); } else if (this.useLanguageSelect_) { dropdown = this.renderDropdownListWithClickEvent_({ triggerElement, subTitleRole: MARKET_PC_HEADER_LANGUAGE_SELECT_SUB_TITLE, dataList: this.marketLanguages.map(this.mapLanguageRenderData_), listElementWithTemplate: this.getListElementWithTemplate_( MARKET_LANGUAGE_SELECT ), defaultValue: this.getDefaultLanguageValue_(), itemHandler: this.handleLanguageSelectChange_.bind(this), autoOrientation: true, }); } if (dropdown) { this.bindDropdownOpenClickEvent_(triggerElement, dropdown); this.element.appendChild(triggerElement); this.element.appendChild(dropdown); } } initEntry_() { if (this.blockSectionPosition_ === SECTION_TYPE_HEADER) { if (this.isSingleSelectMode_) { this.initSingleSelectDropdown_(); } else if (this.useCountrySelect_ && this.useLanguageSelect_) { this.initPanelWithSelect_(); } } this.initSelect_(this.element); } async initData_() { const promises = []; if (this.useCountrySelect_ || this.useModal_) { promises.push(this.getCountryList_(), this.getIpMatchingMarket_()); } if (this.useLanguageSelect_) { promises.push(this.getMarketLanguages_(this.global.market.market_id)); } return Promise.all(promises).then((results) => { const ipMatchingMarket = results.find((result) => result.hasOwnProperty('matching_station') ); const marketCountryList = results.find((result) => result.hasOwnProperty('data') ); if (ipMatchingMarket && marketCountryList) { this.ipMatchingMarket = ipMatchingMarket?.matching_station?.find?.( (item) => item.type === 'multiple_market' ) || { img: marketCountryList.length ? marketCountryList?.[0]?.detail && marketCountryList[0].detail?.flag : '', }; } }); } initModal_() { if ( this.isEditMode || !this.ipMatchingMarket?.market_id || !this.useModal_ || this.hasCookie_(COOKIE_TIPS_NAME) || this.win[MARKET_MODAL_LOADED] ) { return; } this.win[MARKET_MODAL_LOADED] = true; const redirectParam = new URLSearchParams(this.win.location.search).get( URL_REDIRECT ); if (redirectParam) { this.initModalEntry_(MARKET_GATEWAY_REDIRECT_MODAL); } else if ( this.ipMatchingMarket.market_id !== this.global?.market?.market_id ) { this.initModalEntry_(MARKET_MODAL); } } async init_() { await this.initSettings_(); await this.initData_(); this.initEntry_(); this.initModal_(); } getDefaultCountryValue_() { return this.getCookie_(MARKET_COUNTRY_COOKIE) || this.marketCountryList[0]?.country_code; } mapCountryRenderData_(country) { return { flag: country?.detail?.flag || '', value: country?.country_code, currency: country?.currency, currencySymbol: country?.symbol?.val || '', url: country?.url, }; } handleCountrySelectChange_(selectedData) { this.setCookie_(MARKET_COUNTRY_COOKIE, selectedData.value); this.handleChangeURL_(selectedData.url); } getDefaultLanguageValue_() { return this.marketLanguages?.find?.( (lang) => lang.code === this.global?.market?.market_lang )?.name; } mapLanguageRenderData_(language) { return { value: language.name, url: language.url, }; } handleLanguageSelectChange_(selectedData) { this.handleChangeURL_(selectedData.url); } renderTemplateElement_(parent, role, value) { const elements = SPZCore.Dom.scopedQuerySelectorAll( parent, `[role="${role}"]` ); elements.forEach((element) => { if (element && value) { element.innerHTML = value; } }); } renderTemplateImg_(parent, role, options) { const flagElement = SPZCore.Dom.scopedQuerySelector( parent, `[role="${role}"]` ); if (flagElement && options?.src && options?.height && options?.width) { const spzImg = self.document.createElement('ljs-img'); spzImg.setAttribute('src', options.src); spzImg.setAttribute('layout', 'responsive'); spzImg.setAttribute('width', options.width); spzImg.setAttribute('height', options.height); flagElement.appendChild(spzImg); } } handleChangeURL_(url) { if (!!url) { let {origin, pathname, search} = this.win.location; pathname = pathname.replace(SITE, url != '/' ? url : ''); this.win.location.href = `${origin}${pathname}${search}`; } else { this.win.location.reload(true); } } initModalEntry_(role) { const marketModalTemplate = SPZCore.Dom.scopedQuerySelector( this.element, `[role="${role}"]` ); if (marketModalTemplate) { const tempParentNode = self.document.createElement('div'); const clonedTemplate = self.document.importNode( marketModalTemplate.content, true ); tempParentNode.appendChild(clonedTemplate); this.renderTemplateImg_(tempParentNode, MARKET_MODAL_FLAG, { src: this.ipMatchingMarket?.img, width: '80', height: '80', }); this.renderTemplateElement_( tempParentNode, MARKET_MODAL_COUNTRY_CODE, this.ipMatchingMarket?.name ); this.renderTemplateElement_( tempParentNode, MARKET_MODAL_CURRENCY_SYMBOL, this.ipMatchingMarket?.currency_with_symbol ); const lightbox = self.document.createElement('ljs-lightbox'); lightbox.setAttribute('layout', 'nodisplay'); lightbox.appendChild(tempParentNode.firstElementChild); self.document.body.appendChild(lightbox); SPZ.whenApiDefined(lightbox).then((apis) => { SPZCore.Dom.scopedQuerySelectorAll( lightbox, `[role="${MARKET_MODAL_CLOSE}"]` ).forEach((ele) => { ele.addEventListener('click', () => { this.setCookie_(COOKIE_TIPS_NAME, 1, COOKIE_TIPS_EXPIRES); apis.close(); }); }); const marketModalConfirmElement = SPZCore.Dom.scopedQuerySelector( lightbox, `[role="${MARKET_MODAL_CONFIRM}"]` ); if (marketModalConfirmElement && this.ipMatchingMarket?.url) { marketModalConfirmElement.addEventListener('click', () => { this.setCookie_(COOKIE_TIPS_NAME, 1, COOKIE_TIPS_EXPIRES); this.setCookie_(MARKET_COUNTRY_COOKIE, this.ipMatchingMarket?.name); this.handleChangeURL_(this.ipMatchingMarket?.url); }); } const marketModalReselectElement = SPZCore.Dom.scopedQuerySelector( lightbox, `[role="${MARKET_MODAL_RESELECT}"]` ); if (marketModalReselectElement) { marketModalReselectElement.addEventListener('click', () => { this.setCookie_(COOKIE_TIPS_NAME, 1, COOKIE_TIPS_EXPIRES); const marketReselectTemplate = SPZCore.Dom.scopedQuerySelector( this.element, `[role="${MARKET_RESELECT_MODAL}"]` ); if (marketReselectTemplate && lightbox.firstElementChild) { const clonedTemplate = self.document.importNode( marketReselectTemplate.content, true ); const selectParent = SPZCore.Dom.scopedQuerySelector( clonedTemplate, `[role="${MARKET_MODAL_RESELECT_COUNTRY_SELECT}"]` ); if (selectParent) { this.createSelect_({ parent: selectParent, defaultValue: this.getDefaultCountryValue_(), dataList: this.marketCountryList.map( this.mapCountryRenderData_ ), listElementWithTemplate: this.getListElementWithTemplate_( MARKET_COUNTRY_SELECT ), itemHandler: this.handleCountrySelectChange_.bind(this), }); } lightbox.replaceChild(clonedTemplate, lightbox.firstElementChild); SPZCore.Dom.scopedQuerySelectorAll( lightbox, `[role="${MARKET_MODAL_CLOSE}"]` ).forEach((ele) => { ele.addEventListener('click', () => { this.setCookie_(COOKIE_TIPS_NAME, 1, COOKIE_TIPS_EXPIRES); apis.close(); }); }); } }); } apis.open(); }); } } async getMarketThemeSettings_() { return this.xhr_.fetchJson(MARKET_THEME_SETTINGS).then((res) => { this.useCountrySelect_ = !!res.country_selector_enabled; this.useLanguageSelect_ = !!res.language_selector_enabled && this.global?.market?.market_id; this.useModal_ = !!res.redirect_pop_enabled; return res; }); } async getCountryList_() { return this.xhr_.fetchJson(MARKET_COUNTRIES).then((res) => { this.marketCountryList = res.data.length ? res.data : []; return res; }); } async getIpMatchingMarket_() { return this.xhr_.fetchJson(IP_MATCHING_MARKET, { method: 'POST', body: { stations: [], }, }); } async getMarketLanguages_(marketId) { return this.xhr_ .fetchJson(MARKET_LANGUAGES.replace(/:id/gim, marketId)) .then((res) => { this.marketLanguages = res?.languages?.length ? res.languages : []; return res; }); } hasCookie_(name) { const m = self.document.cookie .split(';') .filter((pair) => pair.trim().startsWith(name + '=')); return !!(m && m[0]); } getCookie_(name) { const m = self.document.cookie .split(';') .filter((pair) => pair.trim().startsWith(name + '=')); return m && m[0] ? m[0].split('=')[1] : null; } setCookie_(name, value, expires) { const expiresDate = new Date(Date.now() + expires).toGMTString(); self.document.cookie = `${name}=${encodeURIComponent( value )};expires=${expiresDate};path=/`; } } SPZ.defineElement('spz-custom-market', SpzCustomMarket);
  • Log in
  • Create an account
  • (function(){ let w = window.innerWidth; function setHeaderCssVar() { const headerEle = document.getElementById('shoplaza-section-header'); if(!headerEle){ return }; document.body.style.setProperty('--window-height', `${window.innerHeight}px`); document.body.style.setProperty('--header-height', `${headerEle.clientHeight}px`); const mdScorllHideEle = headerEle.querySelector('.header__mobile .header__scroll_hide'); if (mdScorllHideEle) { document.body.style.setProperty('--header-scroll-hide-height-md', `${mdScorllHideEle.clientHeight}px`); } const pcScorllHideEle = headerEle.querySelector('.header__desktop .header__scroll_hide'); if (pcScorllHideEle) { document.body.style.setProperty('--header-scroll-hide-height-pc', `${pcScorllHideEle.clientHeight}px`); } } function handlResize() { if(w == window.innerWidth){return}; w = window.innerWidth; setHeaderCssVar(); }; function init(){ setHeaderCssVar(); window.removeEventListener('resize', window._theme_header_listener) window._theme_header_listener = handlResize; window.addEventListener('resize', window._theme_header_listener); } init(); })();

    Get in Touch

    Phone

    +46 72 444 03 30

    Call us Mon-Fri, 9:00-17:00 CET

    Chat on WhatsApp

    Email

    info@sale.kim

    For general inquiries and business opportunities

    Send Email

    Live Chat

    chat

    Instant support from our team

    Start Live Chat

    Support Ticket

    request support ticket

    Technical support and issue tracking

    Open Ticket

    Company Information

    Registered Company

    ©Contradom

    VAT Number

    SE720526929801

    PO Box Address

    XYC 880-6 BILLO

    10643 Stockholm, Sweden

    Physical Address

    Fiskensväg 24

    82151 Bollnäs, Sweden

    Registered in Sweden: ©Contradom is a registered company in Sweden, compliant with Swedish business regulations and consumer protection laws.

    We typically respond within 1-2 hours during business hours

    CET Timezone: Monday-Friday, 9:00-17:00

    sale.kim | Luxury One-Liners
    Every luxury brand, one destination
    Premium Selection
    Where excellence meets everything
    Curated Luxury
    Premium
    400,000 luxury products, one standard
    www.sale.kim
    The world's finest selection
    Global Curation
    Luxury without limits
    Endless Excellence
    Premium
    All the best brands, none of the compromise
    www.sale.kim
    Premium brands, limitless choices
    Exclusive Selection
    Curated excellence at scale
    Quality & Quantity
    Premium
    The luxury of choice, perfected
    www.sale.kim
    Where high-end meets high-volume
    Premium Scale
    Every luxury category, mastered
    Complete Curation
    Premium
    The art of luxury, the science of selection
    www.sale.kim