function getTotalHeightIncludingMargin(element) {
if (!element) return 0;
const style = window.getComputedStyle(element);
const marginTop = parseFloat(style.marginTop);
const marginBottom = parseFloat(style.marginBottom);
return element.offsetHeight + marginTop + marginBottom;
}
function updateAnnouncementContainerHeight() {
const socialLinksHeight = getTotalHeightIncludingMargin(socialLinks);
const announcementHeight = getTotalHeightIncludingMargin(announcement);
const height = Math.max(socialLinksHeight, announcementHeight);
announcementContainer.style.height = height + 'px';
}
function resetAnnouncementContainerHeight() {
announcementContainer.style.height = 'auto';
}
const socialLinks = document.getElementById('socialLinks');
const announcement = document.querySelector('.pc-announcement');
const announcementContainer = document.querySelector('.announcement-container');
const resizeObserver = new ResizeObserver(() => {
if (window.matchMedia("(min-width: 960px)").matches) {
updateAnnouncementContainerHeight();
}else {
resetAnnouncementContainerHeight();
}
});
if (socialLinks) {
resizeObserver.observe(socialLinks);
}
if (announcement) {
resizeObserver.observe(announcement);
}
4/6
${data.index + 1}/${data.total}
Material sleeve lining: 100% polyester and faux fur: 90% polyacrylic, 10% polyester Collar: Lined, high fastening Pockets: zip pockets, inside pocket Cuffs: Ribbed Hood details: Removable faux fur collar Pattern: plain colors Function: wind-repellent, water-repellent
SIZE GUIDE
How can enjoy free shipping policy? Free shipping on orders over USD$39. Both shipping costs and shipping method should be subject to your destinations and your order value.
Shipping Details
At our company, we value each and every single customer. Our top priority is making sure the items get delivered to our customer as soon as possible. We strive to provide you with services of the highest level.
The time frame of an order delivery is divided into two parts:
Total Delivery Time = Processing Time + Shipping Time
Processing time: 1~5 business days Shipping time: 7~21 business day Shipping Methods Area Shipping Time Note Standard Shipping United States, Canada,United Kingdom,Australia
7-15 business days Affected by logistics during peak season, it might be extened to 20 business days Standard Shipping The Rest Of the World 7-21 business days
All estimated/typical delivery time are derived from real-world data collected from past orders. They are approximate times for reference only. Shipping times will be affected by public holidays; manufacturers and couriers will limit their operations at these times. This is out of our control. In order to improve timeliness, please ensure the accuracy of the address and contact information. Note For Taxes: For most of the countries, our customers do not need to pay import fees, duties or VAT(Valued Additional Tax). However, for some limited countries (especially for some European countries such as Germany, Italy, UK, and Canada, etc.) may need to pay duties or VAT according to your countries' levying rules. We are excited to offer international shipping to most destinations in the world. The super savings option will take 10 to 20 business days. For faster delivery, we offer Express Shipping Service that can ship to most of the international location in 7 to 12 business days (*Affected by logistics during peak season, it might be extened to 20 business days).
Attention: Affected by Covid-19, there will be some delay on the delivery.
If you have any further questions, please contact our customer services at service@ .com
${function(){
const settings_product_title = "title";
const product_grid_image_size = "150%";
const settings_product_image_hover_on = true;
let settings_product_save_label = false;
const product_sold_out_label = true;
const settings_product_swatches_name = ["color"];
const settings_collection_color_swatches = true;
const product_price_currency = ` `
const from_on = data.price_max != data.price_min ? ''.replace(/\{\{\s*price\s*\}\}/, product_price_currency) : product_price_currency;
const variantShowLimit = 2;
const private_id = 'product-tmpl-' + Math.random().toFixed(6).slice(-6)
const product_variants = data.variants || [];
const product_id = data.id;
const images = data.images || [];
const image = data.image || {};
const imageWidth = image.width;
let imageHeight = image.height;
if (product_grid_image_size !== 'natural') {
imageHeight = (imageWidth * parseFloat(product_grid_image_size)) / 100;
}
const price = Number(data.price_min);
let compareAtPrice = Number(data.compare_at_price);
let offRatio = data.off_ratio;
const type = data.type;
const isMock = data.isMock;
let product_image_hover_on = false;
for (let i = 0; i < product_variants.length; i++) {
const item = product_variants[i];
const vcap = Number(item.compare_at_price);
if (item.price == price && vcap > compareAtPrice) {
compareAtPrice = vcap;
offRatio = item.off_ratio;
}
}
let second_image = null;
if (settings_product_image_hover_on) {
for (let i = 1; i < images.length; i++) {
const img = images[i];
if (img.src && img.src.indexOf('video=') === -1) {
second_image = img;
product_image_hover_on = true;
break;
}
}
}
let sold_label_on = false;
let sale_label_on = false;
if (settings_product_save_label == null) {
settings_product_save_label = true;
}
if (!data.available && product_sold_out_label) {
sold_label_on = true;
}
if (settings_product_save_label && compareAtPrice > price && data.available) {
sale_label_on = true;
}
const diffPrice = compareAtPrice - price;
const variantValues = [];
const showVariants = [];
if (data.need_variant_image && settings_collection_color_swatches && settings_product_swatches_name.length > 0) {
for (let i = 0; i < (data.options || []).length; i++) {
const option = data.originData.options[i];
const optionName = option.name && option.name.toLowerCase();
if (settings_product_swatches_name.includes(optionName)) {
for (let j = 0; j < product_variants.length; j++) {
const variant = product_variants[j];
const value = variant.options[i].value;
if (!variantValues.includes(value)) {
variantValues.push(value);
showVariants.push(variant);
}
}
break;
}
}
}
return `
${data.available ?
`
` : ''}
`;
}()}
Your cart is reserved for
${data.mm} m
${data.ss} s
!
${data.line_items.map(item => {
const renderDiscountApp = () => {
const isEmpty = item.discount_applications && item.discount_applications.length < 1;
const isNotExist = !item.discount_applications;
if (isEmpty || isNotExist) {
return ""
}
return `
${
(item.discount_applications || []).map(discount_item => {
const discount_item_amount = discount_item.discount_amount || discount_item.amount || '';
return `
${discount_item.title}
(- )
`
}).join('')
}
`
}
return `
${item.options.map(o => `
${o.name}: ${o.value}
`).join('')}
${(item.parsedProperties || []).map((propertie)=>{
if (propertie.isImage){
return `
${propertie.name}: View image `
}else{
return `
${propertie.name}: ${propertie.value}
`
}
}).join('')}
${renderDiscountApp()}
`;
}).join('')}
${function() {
return `
Add order note
Add order note
`;
}()}
${data.total_discount > 0 ? (
`
Save
${data.discount_applications.length > 0 && data.discount_applications.map(item => {
return `
${item.title}:
`
}).join('')}
Save
`
) : ''}
Check out
Taxes and shipping calculated at checkout
${data.total_discount > 0 ? (
`
Save
${data.discount_applications.length > 0 && data.discount_applications.map(item => {
return `
${item.title}:
`
}).join('')}
Save
`
) : ''}
Check out
Taxes and shipping calculated at checkout
${function(){
const wholesale_enabled = false;
const qty = data.quantity || 1;
const currentSelectVariant = data.variant;
const defaultVariant = (data.product && data.product.variants && data.product.variants[0]) || Object.keys(data).length > 1 ? data : null;
const productVariant = null;
const variantData = currentSelectVariant || defaultVariant || productVariant;
const wholesale_price = variantData.wholesale_price || [];
if(wholesale_enabled && wholesale_price.length > 0) {
let wholesaleIndex = wholesale_price.findIndex(item => {
return item.min_quantity > qty;
});
if(wholesaleIndex < 0){
wholesaleIndex = wholesale_price.length - 1;
}else if(wholesaleIndex > 0){
wholesaleIndex = wholesaleIndex - 1;
}
const wholesalePrice = wholesale_price[wholesaleIndex] || '';
return `
`
}else {
const price = variantData && variantData.price;
return price != undefined ? `
` : ' ';
}
}()}
${function(){
const productData = data.product;
const selectedVariant = productData.variants.find(v => v.available) || productData.variants[0];
const product_options = productData.options.filter(Boolean) || [];
return `
Price
${function() {
const origin = "shop"
const product = (origin === 'shop' ? data.product : data) || {};
const selectedVariant = product.variants.find(v => v.available) || product.variants[0];
return !!selectedVariant ? `
-
` : `
-
`;
}()}
${selectedVariant.available ? "Add to cart" : "SOLD OUT"}
Buy it now
Product was out of stock.
Product is unavailable.
${function() {
const MAX_INVENTORY = 999999;
const product0 = Object.prototype.toString.call(data) == '[object Array]' ? data[0] : (data.product || data);
const inventoryQty = product0.inventory_quantity;
const inventoryPolicy = product0.inventory_policy;
const inventoryTracking = product0.inventory_tracking;
const exactInventoryStatusId = "quick-shop-exact-inventory-render";
const lowStock = 5;
const defaultVariant = product0 && product0.variants && product0.variants[0];
const selectedVariant = product0.variants.find(v => v.available) || defaultVariant;
const selectedVariantAvailableQuantity = selectedVariant && selectedVariant.available_quantity;
let actualInventory = inventoryQty;
if ((inventoryTracking && inventoryPolicy == 'continue') || !inventoryTracking) {
actualInventory = MAX_INVENTORY;
}
return `
Avaliability:
Out of stock
in stock, ready to be shipped
Low stock
`;
}()}
` }()}
${function(){
const optionName = option.name || '';
const optionId = option.id || '';
let isThumbImage = !!option.showThumbImage;
const thumbStyle = "image";
const variantType = "button";
const isSelected = (value) => {
const selected = (data.selectedOptions || []).find(v => v.name === optionName);
return selected && selected.value.length && selected.value[0] == value;
};
const getThumbImage = (value) => {
const options = data.product.options || [];
const option = options.find(o => o.name === optionName);
if (option.thumbImages) {
const thumbImage = option.thumbImages.find(t => t.value === value);
if (thumbImage && thumbImage.image) {
return {
src: thumbImage.image.src,
alt: thumbImage.image.alt
};
}
}
return {src: '', alt: ''};
};
return `
${optionName.toLowerCase()}
${optionName}:
${data.selectedOptions && data.selectedOptions.length && data.selectedOptions.find(v => v.name === optionName).value[0]}
`;
}()}
${function(){
return `${data.value} `
}()}
${function(){
const wholesale_enabled = false;
const qty = data.quantity || 1;
const currentSelectVariant = data.variant;
const defaultVariant = (data.product && data.product.variants && data.product.variants[0]) || Object.keys(data).length > 1 ? data : null;
const productVariant = null;
const variantData = currentSelectVariant || defaultVariant || productVariant;
const wholesale_price = variantData.wholesale_price || [];
if(wholesale_enabled && wholesale_price.length > 0) {
let wholesaleIndex = wholesale_price.findIndex(item => {
return item.min_quantity > qty;
});
if(wholesaleIndex < 0){
wholesaleIndex = wholesale_price.length - 1;
}else if(wholesaleIndex > 0){
wholesaleIndex = wholesaleIndex - 1;
}
const wholesalePrice = wholesale_price[wholesaleIndex] || '';
return `
`
}else {
const price = variantData && variantData.price;
return price != undefined ? `
` : ' ';
}
}()}
const carousel = document.getElementById('quick-view-images');
const selecotr = document.getElementById('quick-view-thumb-images');
const resizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
const { height } = entry.contentRect;
selecotr.style.height = height + 'px';
}
});
resizeObserver.observe(carousel);
${function(){
const remove_variant_images_on = false;
let product_swatches_name = ["color"];
product_swatches_name = product_swatches_name.map((name) => name.toLowerCase());
const variantsNamesSet = new Set(data.options.map((opt) => opt.name.toLowerCase()) || []);
const containsSwatches = product_swatches_name.some((name) => variantsNamesSet.has(name));
const variantsList = (data && data.variants) || []
const variants = variantsList.map((item) => item.image.path) || [];
const productData = data;
let images = data.images;
if(remove_variant_images_on && containsSwatches) {
images = data.images.filter((img) => !variants.includes(img.path));
}
const selectedVariant = data.variants.find(v => v.available) || data.variants[0];
const selectedIndex = !!selectedVariant ? images.findIndex(img => img.src === (selectedVariant.image && selectedVariant.image.src)) : 0;
const initialSlide = selectedIndex === -1 ? 0 : selectedIndex;
return `
${images.map((image, index) => `
`).join('')}
${images.map(image => `
`).join('')}
${images.length > 1 ? `
` : ''}
${data.title}
Price
${function() {
const origin = "view"
const product = (origin === 'shop' ? data.product : data) || {};
const selectedVariant = product.variants.find(v => v.available) || product.variants[0];
return !!selectedVariant ? `
-
` : `
-
`;
}()}
${selectedVariant.available ? "Add to cart" : "SOLD OUT"}
Buy it now
Product was out of stock.
Product is unavailable.
${function() {
const MAX_INVENTORY = 999999;
const product0 = Object.prototype.toString.call(data) == '[object Array]' ? data[0] : (data.product || data);
const inventoryQty = product0.inventory_quantity;
const inventoryPolicy = product0.inventory_policy;
const inventoryTracking = product0.inventory_tracking;
const exactInventoryStatusId = "quick-view-exact-inventory-render";
const lowStock = 5;
const defaultVariant = product0 && product0.variants && product0.variants[0];
const selectedVariant = product0.variants.find(v => v.available) || defaultVariant;
const selectedVariantAvailableQuantity = selectedVariant && selectedVariant.available_quantity;
let actualInventory = inventoryQty;
if ((inventoryTracking && inventoryPolicy == 'continue') || !inventoryTracking) {
actualInventory = MAX_INVENTORY;
}
return `
Avaliability:
Out of stock
in stock, ready to be shipped
Low stock
`;
}()}
`
}()}
${function(){
const optionName = option.name || '';
const optionId = option.id || '';
let isThumbImage = !!option.showThumbImage;
const thumbStyle = "image";
const variantType = "button";
const isSelected = (value) => {
const selected = (data.selectedOptions || []).find(v => v.name === optionName);
return selected && selected.value.length && selected.value[0] == value;
};
const getThumbImage = (value) => {
const options = data.product.options || [];
const option = options.find(o => o.name === optionName);
if (option.thumbImages) {
const thumbImage = option.thumbImages.find(t => t.value === value);
if (thumbImage && thumbImage.image) {
return {
src: thumbImage.image.src,
alt: thumbImage.image.alt
};
}
}
return {src: '', alt: ''};
};
return `
${optionName.toLowerCase()}
${optionName}:
${data.selectedOptions && data.selectedOptions.length && data.selectedOptions.find(v => v.name === optionName).value[0]}
`;
}()}
${function(){
return `${data.value} `
}()}
const TAG = 'spz-custom-painter-button-animation';
const MAX_ITERATION_COUNT = 99999999;
const SITE = (window.C_SETTINGS && window.C_SETTINGS.routes && window.C_SETTINGS.routes.root) || '';
const ADD_TO_CART_ANIMATION_SETTING =
`${SITE}/api/marketing_atmosphere_app/add_to_cart_btn_animation/setting`;
class SpzCustomPainterButtonAnimation extends SPZ.BaseElement {
/**@override */
static deferredMount() {
return false;
}
/** @param {!SpzElement} element */
constructor(element) {
super(element);
/** @private {!../../src/service/xhr-impl.Xhr} */
this.xhr_ = SPZServices.xhrFor(this.win);
/** @private {Object} */
this.data_ = null;
/** @private {Element} */
this.addToCartButton_ = null;
/** @private {boolean} */
this.productAvailable_ = true;
/** @private {number} */
this.timerId_ = null;
/** @private {number} */
this.animationExecutionCount_ = 0;
/** @private {boolean} */
this.selectedVariantAvailable_ = true;
/** @private {number} */
this.delay_ = 5000;
/** @private {number} */
this.iterationCount_ = 5;
/** @private {string} */
this.animationClass_ = '';
}
/** @override */
isLayoutSupported(layout) {
return layout == SPZCore.Layout.LOGIC;
}
/** @override */
buildCallback() {
this.productAvailable_ = this.element.hasAttribute('product-available');
this.selectedVariantAvailable_ = this.element.hasAttribute('selected-variant-available');
}
/** @override */
mountCallback() {
this.render_();
}
/** @private */
render_() {
if (!this.productAvailable_) {
return;
}
this.fetch_().then((data) => {
if (!data) {
return;
}
this.data_ = data;
this.animationClass_ = `painter-${data.animation_name}-animation`;
this.iterationCount_ =
data.animation_iteration_count === 'infinite'
? MAX_ITERATION_COUNT
: data.animation_iteration_count;
const animationDuration = 1;
const animationDelay = data.animation_delay || 5;
this.delay_ = (animationDuration + animationDelay) * 1000;
this.handleButtonEffect_();
});
}
/**
* @param {JsonObject} data
* @return {(null|Object)}
* @private
*/
parseJson_(data) {
try {
return JSON.parse(data);
} catch (e) {
return null;
}
}
/**
* @return {Promise}
* @private
*/
fetch_() {
return this.xhr_.fetchJson(ADD_TO_CART_ANIMATION_SETTING).then((data) => {
if (!data || !data.enabled) {
return null;
}
return this.parseJson_(data.detail);
});
}
/** @private */
getAddToCartButton_() {
this.addToCartButton_ = SPZCore.Dom.scopedQuerySelector(
document.body,
'[data-section-type="product"] [role="addToCart"], [data-section-type="product_detail"] [role="addToCart"], [data-section-type="product_detail"] [data-click="addToCart"], [data-section-type="product"] [data-click="addToCart"]'
);
}
/** @private */
restartAnimation_() {
this.addToCartButton_.classList.remove(this.animationClass_);
this.addToCartButton_./* OK */ offsetWidth;
this.addToCartButton_.classList.add(this.animationClass_);
this.animationExecutionCount_++;
}
/** @private */
clearTimer_() {
this.win.clearInterval(this.timerId_);
this.timerId_ = null;
}
/** @private */
setupTimer_() {
this.timerId_ = this.win.setInterval(() => {
this.restartAnimation_();
if (this.animationExecutionCount_ >= this.iterationCount_) {
this.removeAnimationClass_();
this.clearTimer_();
}
}, this.delay_);
}
/** @private */
restartTimer_() {
if (this.animationExecutionCount_ >= this.iterationCount_) {
this.removeAnimationClass_();
return;
}
this.setupTimer_();
}
/** @private */
listenVariantChange_() {
SPZUtils.Event.listen(self.document, 'dj.variantChange', (e) => {
const selectedVariant = e.detail && e.detail.selected;
if (!selectedVariant) {
return;
}
const {available} = selectedVariant;
if (this.selectedVariantAvailable_ !== available) {
this.selectedVariantAvailable_ = available;
this.clearTimer_();
if (available) {
this.restartTimer_();
}
}
});
}
/** @private */
removeAnimationClass_() {
this.win.setTimeout(() => {
this.addToCartButton_.classList.remove(this.animationClass_);
}, 1000);
}
/** @private */
handleButtonEffect_() {
this.getAddToCartButton_();
if (!this.addToCartButton_) {
return;
}
if (this.selectedVariantAvailable_) {
++this.animationExecutionCount_;
this.addToCartButton_.classList.add(this.animationClass_);
if (this.iterationCount_ === 1) {
this.removeAnimationClass_();
return;
}
this.setupTimer_();
}
this.listenVariantChange_();
}
}
SPZ.defineElement(TAG, SpzCustomPainterButtonAnimation);
const TAG = "spz-custom-popup";
const DISPLAY_TYPE = {
POPUP: "PTT_POPUP" // 弹窗
};
const API = {
LIST: `/api/storefront/promotion/placement/list`, // 获取弹窗列表
REPORT: `/api/storefront/promotion/placement/data/report` // 上报数据
};
const DISPLAY_DEVICE = {
PC_AND_MOBILE: "PD_PC_MOBILE", // PC和移动端
PC: "PD_PC", // PC
MOBILE: "PD_MOBILE" // 移动端
};
const REPORT_EVENT = {
CLICK: "PE_CLICK", // 点击事件
IMPRESSION: "PE_IMPRESSION" // 曝光事件
};
class SpzCustomPopup extends SPZ.BaseElement {
constructor(element) {
super(element);
this.popupList_ = []; // 弹窗数据
this.popupZIndex = 1050; // 弹窗层级
// 节流处理 每5s内多次点击 算一次点击上报
this.throttleReport = this.win.SPZCore.Types.throttle(
this.win,
(data) => {
this.reportData(data)
},
5000
)
}
static deferredMount() {
return false;
}
buildCallback() {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.setupAction_();
this.viewport_ = this.getViewport();
}
mountCallback() {
this.fetchData_();
}
// 接口请求,获取数据
fetchData_() {
const id = window.SHOPLAZZA.meta.page.template_type === 51 ? window.SHOPLAZZA.meta.page.resource_id : 0;
return this.xhr_.fetchJson(API.LIST, {
method: 'POST',
body: {
page_id: window.SHOPLAZZA.meta.page.template_type,
placement_type: DISPLAY_TYPE.POPUP,
discount_id: id
}
}).then((res) => {
// 请求成功 执行render
this.doRender_(res.list);
}).catch((err) => {
console.error(err);
});
}
// 渲染dom
doRender_(data) {
this.popupList_ = data || [];
if (this.popupList_.length > 0) {
this.popupList_.forEach((item) => {
item.config = JSON.parse(item.config);
})
}
return this.templates_
.findAndRenderTemplate(this.element, { list: this.popupList_ })
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
})
.then(() => {
// 遍历显示弹窗
this.popupList_.forEach((item) => {
this.showPopup_(item);
});
})
}
showPopup_(popup) {
// 展示弹窗 符合展示条件的弹窗
const $popup = document.querySelector(`#popup-${popup.id}`);
$popup && SPZ.whenApiDefined($popup).then((api)=> {
const isPC = this.viewport_.getWidth() >= 960;
const isMobile = this.viewport_.getWidth() < 960;
const isMatchPCDevice = popup.device === DISPLAY_DEVICE.PC_AND_MOBILE || popup.device === DISPLAY_DEVICE.PC;
const isMatchMobileDevice = popup.device == DISPLAY_DEVICE.PC_AND_MOBILE || popup.device === DISPLAY_DEVICE.MOBILE;
if((isPC && isMatchPCDevice) || (isMobile && isMatchMobileDevice)) {
// 根据推送时间 延迟展示弹窗
setTimeout(() => {
api.open();
}, popup.delay_seconds * 1000);
}
})
}
// 上报数据
async reportData(data) {
this.xhr_.fetchJson(API.REPORT, {
method: "POST",
body: {
placement_id: data.placement_id,
event: data.event
}
});
}
setupAction_() {
this.registerAction('handleTrack', async(invocation) => {
// 如果是主题编辑器则不用处理
if(window.top !== window.self) {
return;
}
const data = invocation.args;
const event = data.event;
// 点击上报 节流处理
if(event === REPORT_EVENT.CLICK) {
await this.throttleReport(data);
} else {
this.reportData(data);
}
});
}
triggerEvent_(name, data) {
const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {});
this.action_.trigger(this.element, name, event);
}
isLayoutSupported(layout) {
return layout == SPZCore.Layout.CONTAINER;
}
}
SPZ.defineElement(TAG, SpzCustomPopup);
const TAG = "spz-custom-announcement";
const DISPLAY_TYPE = {
ANNOUNCEMENT: "PTT_BANNER" // 公告栏
};
const API = {
LIST: `/api/storefront/promotion/placement/list`, // 获取公告栏列表
REPORT: `/api/storefront/promotion/placement/data/report` // 上报数据
};
const DISPLAY_DEVICE = {
PC_AND_MOBILE: "PD_PC_MOBILE", // PC和移动端
PC: "PD_PC", // PC
MOBILE: "PD_MOBILE" // 移动端
};
const REPORT_EVENT = {
CLICK: "PE_CLICK", // 点击事件
IMPRESSION: "PE_IMPRESSION" // 曝光事件
};
const POSITION = {
TOP: "PP_TOP", // 顶部
BOTTOM: "PP_BOTTOM" // 底部
}
const MODE = {
FIXED: "PM_FIXED", // 固定
NORMAL: "PM_SCROLLING" // 滚动
}
const THEME_NAME = window.SHOPLAZZA.theme.merchant_theme_name;
class SpzCustomAnnouncement extends SPZ.BaseElement {
constructor(element) {
super(element);
this.announcementList_ = []; // 公告栏数据
}
static deferredMount() {
return false;
}
buildCallback() {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.setupAction_();
this.viewport_ = this.getViewport();
}
mountCallback() {
this.fetchData_();
this.createAnnouncementDom_();
this.listenCartChange_();
}
fetchData_(type = '') {
const id = window.SHOPLAZZA.meta.page.template_type === 51 ? window.SHOPLAZZA.meta.page.resource_id : 0;
return this.xhr_.fetchJson(API.LIST, {
method: 'POST',
body: {
page_id: window.SHOPLAZZA.meta.page.template_type,
placement_type: DISPLAY_TYPE.ANNOUNCEMENT,
discount_id: id
}
}).then((res) => {
this.announcementList_ = res.list || [];
if (this.announcementList_.length > 0) {
this.announcementList_.forEach((item) => {
item.config = JSON.parse(item.config);
});
}
if(type === 'cartChange') {
this.announcementList_.forEach((item) => {
this.updateText_(item);
});
} else {
this.doRender_(this.announcementList_);
}
}).catch((error) => {
console.error(error);
})
}
doRender_(data) {
return this.templates_
.findAndRenderTemplate(this.element, { list: this.announcementList_ })
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
})
.then(() => {
this.announcementList_.forEach((item) => {
this.showAnnouncement_(item);
});
}).then(() => {
this.handleThemeCompatibility_();
});
}
// 更新文案
updateText_(item) {
const announcement = document.querySelector(`#announcement-${item.id}`);
const announcementText = announcement.querySelectorAll('.announcement_text');
const textArr = item.config.text_discount.replace_texts;
const textDom = textArr.map((text) => {
return `${text} `;
}).join(',');
announcementText.forEach((text) => {
text.innerHTML = textDom;
});
}
// 创建公告栏dom
createAnnouncementDom_() {
const isHero = /Hero/.test(THEME_NAME);
const isEva = /Eva/.test(THEME_NAME);
const headerEl = document.querySelector('[data-section-type="header"]');
const headerSticky = headerEl && SPZCore.Dom.computedStyle(this.win, headerEl).position === 'sticky';
// 创建滚动的底部公告栏
const announcementBottomContainer = document.createElement('div');
announcementBottomContainer.className = 'announcement__container_bottom bootstrap';
document.body.appendChild(announcementBottomContainer);
// 创建固定的底部公告栏
const announcementBottomSticky = document.createElement('ljs-sticky');
announcementBottomSticky.className = 'announcement__container_bottom-sticky';
announcementBottomSticky.setAttribute('layout', 'container');
announcementBottomSticky.setAttribute('position', 'bottom');
announcementBottomSticky.style.position = 'fixed';
announcementBottomSticky.style.bottom = '0';
announcementBottomSticky.style.left = '0';
announcementBottomSticky.style.right = '0';
announcementBottomSticky.style.zIndex = '1030';
document.body.appendChild(announcementBottomSticky);
const announcementTopContainer = document.createElement('div');
announcementTopContainer.classList.add('announcement__container_top');
if (isHero) {
announcementTopContainer.classList.add('announcement__container_top_zIndex_1030');
}
announcementTopContainer.classList.add('bootstrap');
document.body.insertBefore(announcementTopContainer, document.body.children[0]);
const announcementTopFixedContainer = document.createElement('div');
announcementTopFixedContainer.classList.add('announcement__container_top-fixed');
if (isHero) {
announcementTopFixedContainer.classList.add('announcement__container_top_zIndex_1030');
}
announcementTopFixedContainer.classList.add('bootstrap');
const insertBeforeElement = headerSticky ? headerEl : document.body;
insertBeforeElement.insertBefore(announcementTopFixedContainer, insertBeforeElement.children[0]);
if (isEva) {
const evaHeader = document.querySelector('header.header');
const isEvaMaskHeader = evaHeader && SPZCore.Dom.computedStyle(this.win, evaHeader).position === 'absolute';
let fixedBannerTopContainer = document.querySelector('.announcement__container_top-fixed');
if (isEvaMaskHeader) {
if (fixedBannerTopContainer) {
fixedBannerTopContainer.remove();
}
const newBanner = document.createElement('div');
newBanner.className = 'announcement__container_top-fixed bootstrap';
document.body.insertBefore(newBanner, document.body.firstChild);
fixedBannerTopContainer = newBanner;
} else {
if (!headerEl) return;
const observer = new MutationObserver(() => {
const isSticky = SPZCore.Dom.computedStyle(this.win, headerEl).position === 'sticky';
if (!isSticky) return;
const isTopFixedAnnouncementInHeader = headerEl.querySelector('.announcement__container_top-fixed');
if (isTopFixedAnnouncementInHeader) return;
const announcementTopFixedContainer = document.querySelector('.announcement__container_top-fixed');
if (announcementTopFixedContainer) {
announcementTopFixedContainer.remove();
headerEl.insertBefore(announcementTopFixedContainer, headerEl.children[0]);
observer.disconnect();
}
});
observer.observe(headerEl, { attributes: true, attributeFilter: ['style', 'class'] });
}
if (headerSticky && !isEvaMaskHeader && fixedBannerTopContainer) {
fixedBannerTopContainer.style.position = 'relative';
fixedBannerTopContainer.style.zIndex = '29';
}
}
}
// 展示公告栏
showAnnouncement_(item) {
const announcement = document.querySelector(`#announcement-${item.id}`);
const announcementBottomContainer = document.querySelector('.announcement__container_bottom');
const announcementBottomSticky = document.querySelector('.announcement__container_bottom-sticky');
const announcementTopContainer = document.querySelector('.announcement__container_top');
const announcementTopFixedContainer = document.querySelector('.announcement__container_top-fixed');
const isPC = this.viewport_.getWidth() >= 960;
const isMobile = this.viewport_.getWidth() < 960;
const isMatchPCDevice = item.device === DISPLAY_DEVICE.PC_AND_MOBILE || item.device === DISPLAY_DEVICE.PC;
const isMatchMobileDevice = item.device == DISPLAY_DEVICE.PC_AND_MOBILE || item.device === DISPLAY_DEVICE.MOBILE;
if((isPC && isMatchPCDevice) || (isMobile && isMatchMobileDevice)) {
if (item.position === POSITION.BOTTOM) {
if(item.mode === MODE.FIXED) {
announcementBottomSticky && announcementBottomSticky.appendChild(announcement);
} else {
announcementBottomContainer && announcementBottomContainer.appendChild(announcement);
}
} else {
if (item.mode === MODE.FIXED) {
announcementTopFixedContainer && announcementTopFixedContainer.appendChild(announcement);
} else {
announcementTopContainer && announcementTopContainer.appendChild(announcement);
}
}
this.reportData({
placement_id: item.id,
event: REPORT_EVENT.IMPRESSION
});
}
}
// 处理主题兼容
handleThemeCompatibility_() {
try {
const isBoost = /Boost/.test(THEME_NAME);
const isHyde = /Hyde/.test(THEME_NAME);
const isEva = /Eva/.test(THEME_NAME);
const boostHeader = document.querySelector('.boost-header');
const fixedBannerTopContainer = document.querySelector('.announcement__container_top-fixed');
const notFixedBannerTopContainer = document.querySelector('.announcement__container_top');
const headerEl = document.querySelector('[data-section-type="header"]');
const headerSticky = headerEl && SPZCore.Dom.computedStyle(this.win, headerEl).position === 'sticky';
const header = document.querySelector('.header__fixed') || document.querySelector('.header__wrapper');
const headerFixed = header && SPZCore.Dom.computedStyle(this.win, header).position === 'fixed';
const handleScroll = SPZCore.Types.throttle(this.win, () => {
if (isHyde) {
if (header && headerSticky) {
header.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
} else {
notFixedBannerTopContainer.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
}
}
if (isEva) {
const evaHeader = document.querySelector('header.header');
const isEvaMaskHeader = evaHeader && SPZCore.Dom.computedStyle(this.win, evaHeader).position === 'absolute';
if (!isEvaMaskHeader) return;
if (evaHeader.classList.contains('header__fixed')) {
evaHeader.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
} else {
notFixedBannerTopContainer.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
}
if(document.documentElement.scrollTop === 0) {
evaHeader.style.marginTop = '0';
}
}
if (headerSticky) return;
if (headerFixed) {
header.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
} else {
const observer = new MutationObserver((mutationsList, observer) => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList' && fixedBannerTopContainer.childElementCount > 0) {
notFixedBannerTopContainer.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
observer.disconnect(); // 停止观察
break;
}
}
});
// 开始观察 fixedBannerTopContainer 的子节点变化
observer.observe(fixedBannerTopContainer, { childList: true, subtree: true });
// 初始检查
if (fixedBannerTopContainer.childElementCount > 0) {
notFixedBannerTopContainer.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
}
if(header) {
header.style.marginTop = '0';
}
}
if (isBoost) {
fixedBannerTopContainer.style.zIndex = '1031';
if (boostHeader && boostHeader.classList.contains('header__fixed')) {
boostHeader.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
} else {
notFixedBannerTopContainer.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
}
}
}, 16);
window.addEventListener('scroll', handleScroll);
window.dispatchEvent(new Event('scroll'));
} catch (error) {
console.error('error', error);
}
}
// 上报数据
async reportData(data) {
// 如果是主题编辑器则不用处理
if(window.top !== window.self) {
return;
}
this.xhr_.fetchJson(API.REPORT, {
method: "POST",
body: {
placement_id: data.placement_id,
event: data.event
}
});
}
// 监听购物车变化事件dj.cartChange
listenCartChange_() {
SPZUtils.Event.listen(document, 'dj.cartChange', (event) => {
this.fetchData_('cartChange');
});
}
setupAction_() {
this.registerAction('handleClose', (invocation) => {
const data = invocation.args;
const id = data.id;
const announcement = document.querySelector(`#announcement-${id}`);
announcement && SPZCore.Dom.removeElement(announcement);
window.dispatchEvent(new Event('scroll'));
});
this.registerAction('handleJumpLink', (invocation) => {
const data = invocation.args;
if(!data.show_url) return;
data.url && window.open(data.url, data.open_new_window ? '_blank' : '_self');
this.reportData({
placement_id: data.id,
event: REPORT_EVENT.CLICK
});
});
}
triggerEvent_(name, data) {
const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {});
this.action_.trigger(this.element, name, event);
}
isLayoutSupported(layout) {
return layout == SPZCore.Layout.CONTAINER;
}
}
SPZ.defineElement(TAG, SpzCustomAnnouncement);
${function() {
return data.originData.list.map((item) => {
const background = item.config.background;
const interactive = item.config.interactive;
const textArr = item.config.text_discount.replace_texts;
const textColor = item.config.text_discount.color;
const backgroundSize = background.presentation_rule === 'fill' ? 'cover' : 'contain';
const pcImage = (background.url && background.upload) ? background.url : '';
const mobileImage = (background.mobile_url && background.upload) ? background.mobile_url : '';
const color1 = background.color;
const color2 = background.color2 || background.color;
const backgroundStyle = `background: url(//img.fantaskycdn.com/${pcImage}) center / ${backgroundSize} no-repeat, linear-gradient(to right, ${color1}, ${color2});`;
const backgroundMobileStyle = `background: url(//img.fantaskycdn.com/${mobileImage}) center / ${backgroundSize} no-repeat, linear-gradient(to right, ${color1}, ${color2});`;
return `
${textArr.map((text) => {
return `
${text}
`
}).join(',')}
${textArr.map((text) => {
return `
${text}
`
}).join(',')}
`
})
}()}
ao