-
💥💥Extra 10% Off | Code: ENF10💥💥FREE SHIPPING ORDER OVER $89💥💥RETURN & REFUND IN 30 DAYS
💥💥Extra 10% Off | Code: ENF10💥💥FREE SHIPPING ORDER OVER $89💥💥RETURN & REFUND IN 30 DAYS
Shop now
💥💥Extra 10% Off | Code: ENF10💥💥FREE SHIPPING ORDER OVER $89💥💥RETURN & REFUND IN 30 DAYS
💥💥Extra 10% Off | Code: ENF10💥💥FREE SHIPPING ORDER OVER $89💥💥RETURN & REFUND IN 30 DAYS
2/3
${data.index + 1}/${data.total}
| size chart |
|
|
|
| Size |
Length |
chest |
waist |
| S |
68CM |
74CM |
70CM |
| M |
69CM |
78CM |
74CM |
| L |
70CM |
82CM |
78CM |
| XL |
71CM |
86CM |
82CM |
| XXL |
72CM |
90CM |
86CM |
let section_id = '1774506019626';
window.reviewSettings = {};
window.reviewSettings[section_id] = {
"sub_title": null,
"star_least": "5",
"only_featured": false,
"with_photo": false,
"review_insufficient": "no_reviews",
"minimum_comment_num": 5,
"fill_strategy": "empty",
"layout": "grid",
"image_size": "natural",
"wall_mobile_num": 2,
"wall_pc_num": 4,
"limit": 8,
"show_product": false,
"hide_review_section": true,
"title": "Reviews",
"accent_color": null,
"color_title": "#000000",
"text_color": "#000000",
"card_wrap_color": null,
"background_color": "#ffffff"
};
const TAG = 'spz-custom-revue-util';
const DEFAULT_DELAY_TIME = 100;
class SpzCustomRevueUtil extends SPZ.BaseElement {
constructor(element) {
super(element);
this.templates_ = SPZServices.templatesForDoc();
}
buildCallback = () => {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
}
static deferredMount() {
return false;
}
mountCallback() {
}
debounceRender(el, thisEl, containerStr) {
return this.smoothRender_(el, thisEl, containerStr).then(() => this.attemptToFit_(thisEl));
}
smoothRender_(newEl, thisEl, containerStr) {
const that = this;
that.appendAsUnvisibleContainer_(newEl, thisEl);
const components = newEl.querySelectorAll('[layout]');
return Promise.race([
Promise.all(
Array.prototype.map.call(components, (e) =>
SPZ.whenDefined(e).then(() => e.whenBuilt())
)
),
SPZServices.timerFor(that.win).promise(DEFAULT_DELAY_TIME),
]).then(() => {
return containerStr !== 'form_' ? thisEl.mutateElement(() => that.quickReplace(thisEl, newEl)) : thisEl.mutateElement(() => that.quickReplaceForm(thisEl, newEl));
});
}
quickReplace(thisEl, newEl) {
thisEl.container_ && this.toggleVisible_(thisEl.container_);
this.toggleVisible_(newEl, true);
thisEl.container_ && SPZCore.Dom.removeElement(thisEl.container_);
thisEl.container_ = newEl;
};
quickReplaceForm(thisEl, newEl) {
thisEl.form_ && this.toggleVisible_(thisEl.form_);
this.toggleVisible_(newEl, true);
const children = thisEl.form_.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.toggleVisible_(thisEl.form_, true);
thisEl.form_.appendChild(newEl);
};
appendAsUnvisibleContainer_(el, thisEl) {
this.toggleVisible_(el);
thisEl.element.appendChild(el);
}
attemptToFit_(thisEl) {
const fitFunc = () => {
thisEl.mutateElement(this.setElementHeight_.bind(thisEl));
};
const container = thisEl.container_ || thisEl.form_;
if (container) {
const children = container.querySelectorAll('*:not(template)');
const spzChildren = Array.prototype.filter
.call(children, SPZUtils.isSpzElement)
.filter((e) => !(e.isMount && e.isMount()));
spzChildren
.map((e) => SPZ.whenDefined(e).then(() => e.whenMounted()))
.forEach((p) => p.then(() => fitFunc()));
}
return fitFunc();
}
setElementHeight_() {
const targetHeight = (this.container_ || this.form_)?./*OK*/ scrollHeight;
const height = this.element./*OK*/ offsetHeight;
if (height !== targetHeight) {
SPZCore.Dom.setStyles(this.element, {
height: `${targetHeight}px`,
});
}
}
toggleVisible_(el, visible = false) {
if (!visible) {
el.classList.add('i-spzhtml-layout-fill');
SPZCore.Dom.setStyles(el, {
'z-index': -100000,
'opacity': 0,
});
} else {
el.classList.remove('i-spzhtml-layout-fill');
SPZCore.Dom.setStyles(el, {
'z-index': 'auto',
'opacity': 1,
});
}
}
setMinWidth_() {
const targetWidth = this.container_?./*OK*/ scrollWidth;
const width = this.element./*OK*/ offsetWidth;
if (width !== targetWidth) {
SPZCore.Dom.setStyles(this.element, {
'min-width': `${targetWidth}px`,
});
}
}
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, SpzCustomRevueUtil);
const TAG = 'spz-custom-revue-render';
class SPZCustomRevueRender extends SPZ.BaseElement {
constructor(element) {
super(element);
}
static deferredMount() {
return false;
}
buildCallback = () => {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
}
mountCallback = () => {}
render = (data) => {
return this.templates_
.findAndRenderTemplate(this.element, data, null)
.then((el) => {
if (this.element.children.length > 0) {
this.element.children[0].style.display = 'none';
}
this.element.appendChild(el);
// const utilsEl = document.getElementById('spz_custom_revue_util');
// utilsEl && SPZ.whenApiDefined(utilsEl).then((api) => {
// api.debounceRender(el, this);
// });
});
}
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, SPZCustomRevueRender)
${function(){
return `
${data.starNum}/${data.starTotal}
`;
}()}
${function(){
return `
${data.showStarText === 'true' ? `
${data.starNum}/${data.starTotal}
` : ''}
`;
}()}
const TAG = 'spz-custom-revue-star';
class SPZCustomRevueStar extends SPZ.BaseElement {
constructor(element) {
super(element);
}
static deferredMount() {
return false;
}
buildCallback = () => {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.starNum = this.element.getAttribute('starNum');
this.starTotal = this.element.getAttribute('starTotal');
this.showStarText = this.element.getAttribute('showStarText');
this.starColor = this.element.getAttribute('color');
this.interact = this.element.getAttribute('interact');
this.starSize = this.element.getAttribute('starSize') || 14;
}
mountCallback = () => {
this.doRender_({
starTotal: this.starTotal,
totalArray: Array.from({ length: Number(this.starTotal) }, (v, k) => k + 1),
starNum: this.starNum,
showStarText: this.showStarText,
starColor: this.starColor,
starSize: this.starSize
}).then(() => {
if (this.interact) {
this.addEventListeners_();
}
});
}
addEventListeners_ = () => {
const stars = document.querySelectorAll('.revue-star__star');
stars.forEach(star => {
star.addEventListener('click', event => {
const starEl = star.closest('.revue-star__star');
const starIndex = Number(starEl.dataset.index);
let isHalf = event.offsetX < star.offsetWidth / 2;
// rtl
if (document.documentElement.getAttribute('dir') === 'rtl') {
isHalf = event.offsetX > star.offsetWidth / 2;
}
const starValue = isHalf ? starIndex - 0.5 : starIndex;
this.starClickHandler_({ value: starValue });
});
});
}
renderStar = () => {
const isRtl = document.documentElement.getAttribute('dir') === 'rtl';
const stars = this.element.querySelectorAll('.revue-star__star');
stars.forEach((star, i) => {
const starIndex = i + 1;
const starEl = star.querySelector('svg:nth-child(2)');
const isHalf = this.starNum % 1 > 0 && Math.ceil(this.starNum) === starIndex;
const isSolid = starIndex <= Math.ceil(this.starNum);
starEl.style.display = isSolid ? 'block' : 'none';
if (isHalf) {
if (isRtl) {
// RTL布局下,如果是半星,显示星星的右半边
starEl.style.clipPath = `polygon(50% 0, 100% 0, 100% 100%, 50% 100%)`;
} else {
// LTR布局下,如果是半星,显示星星的左半边
starEl.style.clipPath = `polygon(0 0, 50% 0, 50% 100%, 0 100%)`;
}
} else {
starEl.style.clipPath = `polygon(0 0, 100% 0, 100% 100%, 0 100%)`
}
});
const showCountEle = this.element.querySelector('#revue-star-show-count');
showCountEle && SPZ.whenApiDefined(showCountEle).then((api) => {
api.render({ starNum: this.starNum, starTotal: this.starTotal });
});
}
doRender_ = (data) => {
return this.templates_
.findAndRenderTemplate(this.element, { starSize: this.starSize, ...data }, null)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
})
.then(() => {
this.starNum = data.starNum;
this.renderStar();
});
}
starClickHandler_ = (event) => {
this.starNum = event.value;
this.renderStar();
this.triggerEvent_('change', { value: event.value });
}
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, SPZCustomRevueStar)
${function() {
return `
${data.count > 99 ? '99+' : data.count < 1 ? '' : data.count}
`;
}()}
const TAG = 'spz-custom-revue-like';
class SPZCustomRevueLike extends SPZ.BaseElement {
constructor(element) {
super(element);
}
static deferredMount() {
return false;
}
buildCallback = () => {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.grayColor = this.element.getAttribute('gray_color') || "#BDBDBD";
this.likedColor = this.element.getAttribute('like_color') || "#FFCB44";
this.color = this.grayColor;
this.count = this.element.getAttribute('count');
this.revueId = this.element.getAttribute('revue-id');
this.location = this.element.getAttribute('location');
}
mountCallback = () => {
const likes = sessionStorage.getItem('likes') ? JSON.parse(sessionStorage.getItem('likes')) : [];
const like = likes.find(item => item.id === this.revueId);
if (like) {
this.color = like.like_status === 1 ? this.likedColor : this.grayColor;
}
// 如果location是modal,则找到相同revue-id的list的元素,拿到其count,存在list count变了,但是modal的count没变的情况
if (this.location === 'modal') {
const listElement = document.querySelector(`spz-custom-revue-like[revue-id="${this.revueId}"] .revue-like-count`);
if (listElement) {
this.count = listElement.getAttribute('data-real-count');
}
}
this.doRender_({
color: this.color,
count: this.count
}).then(() => {
this.addEventListeners_();
if(this.location === 'list') { // modal数量变更,list同步变更
document.addEventListener('like-clicked', (e) => {
if (e.detail.location !== this.location && e.detail.id === this.revueId) {
this.color = e.detail.like_status === 1 ? this.likedColor : this.grayColor;
this.count = e.detail.count;
this.element.querySelector('.revue-like__icon').querySelector('svg').setAttribute('fill', this.color);
this.element.querySelector('.revue-like__icon').querySelector('svg').querySelector('path').setAttribute('fill', this.color);
this.element.querySelector('.revue-like-count').innerText = this.count > 99 ? '99+' : this.count < 1 ? '' : this.count;
this.element.querySelector('.revue-like-count').setAttribute('data-real-count', this.count);
if(this.count > 0){
this.element.querySelector('.revue-like-count').classList.remove('hidden');
}else{
this.element.querySelector('.revue-like-count').classList.add('hidden');
}
}
});
}
});
}
addEventListeners_ = () => {
const icon = this.element.querySelector('.revue-like__icon');
icon.addEventListener('click', (e) => {
e.stopPropagation();
const likeStatus = this.color === this.likedColor ? 0 : 1;
this.color = this.color === this.likedColor ? this.grayColor : this.likedColor;
this.count = likeStatus === 1 ? parseInt(this.count) + 1 : parseInt(this.count) - 1;
icon.querySelector('svg').setAttribute('fill', this.color);
icon.querySelector('svg').querySelector('path').setAttribute('fill', this.color);
this.element.querySelector('.revue-like-count').innerText = this.count > 99 ? '99+' : this.count < 1 ? '' : this.count;
this.element.querySelector('.revue-like-count').setAttribute('data-real-count', this.count);
if(this.count > 0){
this.element.querySelector('.revue-like-count').classList.remove('hidden');
}else{
this.element.querySelector('.revue-like-count').classList.add('hidden');
}
this.postLike(likeStatus);
if (this.location === 'modal') {
const clickedEvent = new CustomEvent('like-clicked', {
detail: {
id: this.revueId,
like_status: likeStatus,
count: this.count,
location: this.location
}
});
document.dispatchEvent(clickedEvent);
}
});
}
setLikeToStorage = (likeToStore) => {
if (typeof (Storage) !== 'function') return;
const likesInStore = sessionStorage.getItem('likes') ? JSON.parse(sessionStorage.getItem('likes')) : [];
const reviewIndex = likesInStore.findIndex(item => item.id === likeToStore.id);
if (reviewIndex !== -1) {
likesInStore[reviewIndex].like_status = likeToStore.like_status;
likesInStore[reviewIndex].count = likeToStore.count;
} else {
likesInStore.push(likeToStore);
}
sessionStorage.setItem('likes', JSON.stringify(likesInStore));
}
doRender_ = (data) => {
return this.templates_
.findAndRenderTemplate(this.element, data, null)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
});
}
postLike = (likeStatus) => {
fetch('/api/comment/like', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: this.revueId,
status: likeStatus
})
}).then((res) => {
if (res.status === 200) {
this.setLikeToStorage({
id: this.revueId,
like_status: likeStatus,
count: this.count
});
}
});
}
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, SPZCustomRevueLike)
${function() {
return `
${function() {
if(data.imgCover) {
if(media.videosrc) {
let src = '';
if (media.videosrc) {
src = media.videosrc + '.' + media.ext;
}
const videoDom = `
`;
if(!isPC){
return `
${videoDom}
`
}
return `
${videoDom}
`
} else if(media.mp4 || media.hls) {
const videoDom = `
`;
if(!isPC){
return `
${videoDom}
`
}
return `
${videoDom}
`
} else {
if(!isPC){
return `
`
}else{
return `
`
}
}
} else {
if (media.videosrc) {
let src = '';
if (media.videosrc) {
src = media.videosrc + '.' + media.ext;
}
return `
`
} else if(media.mp4 || media.hls) {
return `
`
} else {
return `
`
}
}
}()}
`;
}()}
const TAG = 'spz-custom-revue-media';
class SPZCustomRevueMedia extends SPZ.BaseElement {
constructor(element) {
super(element);
}
static deferredMount() {
return false;
}
buildCallback = () => {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.imgCover = this.element.getAttribute('img-cover') ?? false;
this.pc_layout = this.element.getAttribute('pc-layout') ?? '';
// data-images 格式为 xxxx.png?width=1&height=1,xxxx.png?width=1&height=1
const images = this.element.getAttribute('data-images').split(',') || [];
const parsedImages = images.map(image => {
return this.mediaParse_(image);
});
this.images = parsedImages;
this.isPC = window.innerWidth > 960;
}
mountCallback = () => {
this.doRender_({
images: this.images,
isPC: this.isPC,
imgCover: this.imgCover,
pc_layout: this.pc_layout
}).then(() => {
this.addEventListeners_();
});
}
addEventListeners_ = () => {
const images = this.element.querySelectorAll('.revue-image-item');
images.forEach((image, index) => {
image.addEventListener('click', () => {
const carousel = document.querySelector('#revue-image-carousel-render');
carousel && SPZ.whenApiDefined(carousel).then((api) => {
const width = this.isPC ? 460 : window.innerWidth * 0.9;
const height = this.isPC ? 630 : 500;
api.render({ images: this.images, index: index, width: width, height: height });
});
});
});
}
doRender_ = (data) => {
return this.templates_
.findAndRenderTemplate(this.element, data, null)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
});
}
mediaParse_ = function (url) {
var result = {};
try {
url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (str, key, value) {
try {
result[key] = decodeURIComponent(value);
} catch (e) {
result[key] = value;
}
});
result.preview_image = url.split('?')[0];
} catch (e) {};
return result;
}
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, SPZCustomRevueMedia)
${function() {
return `
`
}()}
${function() {
return `
Most liked
Highest ratings
Lowest ratings
`
}()}
${function() {
return `
Most liked
Highest ratings
Lowest ratings
`
}()}
const TAG = 'spz-custom-revue-sort';
class SPZCustomRevueSort extends SPZ.BaseElement {
constructor(element) {
super(element);
}
static deferredMount() {
return false;
}
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;
}
buildCallback = () => {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.isPC = window.innerWidth > 960;
this.width = this.isPC ? `${this.element.getAttribute('width') || 150}px` : '100%';
this.randomStr = Math.random().toString(36).substr(2);
this.sectionId = this.element.getAttribute('section-id') || '1774506019626';
this.prefix = this.element.getAttribute('prefix');
}
mountCallback = () => {
const data = {
width: this.width,
randomStr: this.randomStr
};
this.doRender_(data).then(() => {
let revueSortListRender = this.isPC ? this.element.querySelector(`#${this.prefix}-revue-sort-list-render-${this.sectionId}`) : this.element.querySelector(`#${this.prefix}-revue-sort-dropdown-render-${this.sectionId}`);
revueSortListRender && SPZ.whenApiDefined(revueSortListRender).then((api) => {
api.render(data).then(() => {
if (this.isPC) {
this.addEventListenersForPC_();
} else {
this.addEventListenersForMobile_();
}
});
});
});
}
doRender_ = (data) => {
return this.templates_
.findAndRenderTemplate(this.element, data, null)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
});
}
addEventListenersForPC_ = () => {
const revueSelectList = this.element.querySelector('.revue_select_list');
const revueSelectItem = this.element.querySelectorAll('.revue_select_item');
const revueSelectSortIcon = this.element.querySelector(`#${this.prefix}-revue_select_sort_icon-${this.sectionId}`);
revueSelectItem.forEach(item => {
item.addEventListener('click', () => {
const sort = item.getAttribute('data-sort');
const direction = item.getAttribute('data-direction');
this.triggerEvent_('sort', { sort, direction });
this.element.querySelector('.revue_select_label').innerText = item.innerText;
revueSelectList.classList.remove('revue_select_list_active');
const revueChecked = this.element.querySelector(`#${this.prefix}-revue_checked`);
revueChecked && SPZCore.Dom.removeElement(revueChecked);
const revueCheckedClone = revueChecked.cloneNode(true);
item.appendChild(revueCheckedClone);
const pcDropdownEle = document.querySelector(`#${this.prefix}-revue-sort-pc-dropdown-${this.sectionId}`);
if (!revueSelectSortIcon.classList.contains('up_icon')) {
return;
}
revueSelectSortIcon.classList.remove('up_icon');
SPZ.whenApiDefined(pcDropdownEle).then((api) => {
api.close();
});
});
});
window.addEventListener('scroll', (e) => {
if (!revueSelectSortIcon || !revueSelectSortIcon.classList.contains('up_icon')) {
return;
}
revueSelectSortIcon.classList.remove('up_icon');
SPZ.whenApiDefined(pcDropdownEle).then((api) => {
api.close();
});
});
}
addEventListenersForMobile_ = () => {
const revueSortDropdownRender = document.querySelector(`#${this.prefix}-revue-sort-dropdown-render-${this.sectionId}`);
revueSortDropdownRender && SPZ.whenApiDefined(revueSortDropdownRender).then(async (api) => {
await api.render();
const revueSortDropdownItem = document.querySelectorAll(`#${this.prefix}-revue-sort-dropdown-${this.sectionId} .revue_sort_dropdown_item`);
revueSortDropdownItem.forEach(item => {
item.addEventListener('click', () => {
const sort = item.getAttribute('data-sort');
const direction = item.getAttribute('data-direction');
revueSortDropdownItem.forEach((_item)=>{_item.classList.remove('selected')})
item.classList.add('selected');
// 抛出事件
this.triggerEvent_('sort', { sort, direction });
// 移除revue_checked元素,复制一个新的到当前选中的元素
const revueChecked = document.querySelector(`#${this.prefix}-revue-sort-dropdown-${this.sectionId} #${this.prefix}-revue_checked`);
revueChecked && SPZCore.Dom.removeElement(revueChecked);
const revueCheckedClone = revueChecked.cloneNode(true);
item.appendChild(revueCheckedClone);
const mDropdownEle = document.querySelector(`#${this.prefix}-revue-sort-dropdown-${this.sectionId}`);
SPZ.whenApiDefined(mDropdownEle).then((api) => {
api.close();
});
});
});
})
}
}
SPZ.defineElement(TAG, SPZCustomRevueSort)
const TAG = 'spz-custom-revue-flow';
class SpzCustomRevueFlow extends SPZ.BaseElement {
constructor(element) {
super(element);
this.sectionId = this.element.getAttribute('section-id');
this.show_product = '';
this.with_photo = '';
this.limit = '';
this.star_least = '';
this.layout = ''
this.wall_pc_num = ''
this.wall_mobile_num = ''
this.accent_color = ''
this.isProductPage = '1' == 1;
this.isCollectionPage = '1' == 2;
this.isCartPage = '1' == 13;
this.lastWidth = window.innerWidth;
}
static deferredMount() {
return false;
}
buildCallback = () => {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.setupAction_();
const url = new URL(window.location.href);
const preview_theme_id = url.searchParams.get('preview_theme_id');
if (preview_theme_id) {
this.preview_theme_id = preview_theme_id;
}
this.commentConfig = {};
this.sort = 'created_at';
this.direction = 'desc';
this.isPC = window.innerWidth > (window.breakpoint || 960);
this.appendList = [];
this.commentListRes = [];
this.cardConfig = window.reviewSettings[this.sectionId];
}
render_ = (data={}) => {
const {star_least, with_photo, show_product, limit, layout, wall_pc_num, wall_mobile_num, accent_color, fill_strategy, review_insufficient, minimum_comment_num, only_featured, hide_review_section} = this.cardConfig;
Object.assign(this, {star_least, with_photo, show_product, limit, layout, wall_pc_num, wall_mobile_num, accent_color, fill_strategy, review_insufficient, minimum_comment_num, only_featured, hide_review_section});
if(this.layout === 'wall'){
this.with_photo = 1;
};
this.params = {
offset: this.appendList.length || 0,
sort_by: this.sort,
sort_direction: this.direction,
show_reply: 1 || this.commentConfig.show_reply ? 1 : 0,
with_photo: this.with_photo,
...data
}
if(this.fill_strategy == 'store'){
if(this.review_insufficient == 'less_than'){
this.params.fill_min_threshold = minimum_comment_num;
}else{
this.params.fill_min_threshold = 1;
}
this.params.fill_strategy = this.fill_strategy;
}
const summaryObj = {
star_least:this.star_least,
product_ids: this.isProductPage ? '7e3c2b7a-b391-41ec-959c-1768fe2fd3d1' : this.isCartPage ? '' : '',
collection_id: this.isCollectionPage ? '' : '',
filter_type: (this.isProductPage || this.isCartPage) ? 'product' : this.isCollectionPage ? 'collection' : 'store',
fill_strategy: this.params?.fill_strategy || '',
only_media: !!this.params.with_photo,
only_featured:this.only_featured
}
if(this.params.fill_min_threshold){
summaryObj.fill_min_threshold = this.params.fill_min_threshold;
}
Promise.all([
this.fetchSummary_(summaryObj),
this.fetchCommentConfig_(),
this.fetchCommentList_(this.params)
]).then(response => {
const [starCountRes,commentConfigRes, commentListRes] = response;
this.commentConfig = commentConfigRes.data;
this.commentConfig.show_product = this.show_product;
this.commentListRes = commentListRes;
this.starCountRes = starCountRes;
/* 评论不足逻辑 */
const listLen = Number(commentListRes?.data?.count) || 0;
const isListEmpty = listLen === 0;
const isLessThanMinimum = this.review_insufficient === 'less_than' && listLen < this.minimum_comment_num;
const shouldHide = isListEmpty || isLessThanMinimum;
/* 隐藏评论区域 */
if ((this.fill_strategy === 'hide' && shouldHide) || (this.hide_review_section && isListEmpty)) {
this.renderHideSkeleton_();
this.renderHideComment_();
return;
}
/* 显示空状态 */
if (this.fill_strategy === 'empty' && shouldHide) {
this.renderHideSkeleton_();
if (this.isProductPage) {
this.renderEmptyComment_();
} else {
this.renderHideComment_();
}
return;
}
if (this.preview_theme_id) {
this.fetchThemeConfig_(this.preview_theme_id).then(themeConfig => {
if (themeConfig?.star_color) {
this.commentConfig.star_color = themeConfig.star_color;
}
if(this.accent_color && this.accent_color != 'null'){
this.commentConfig.star_color = this.accent_color;
}
});
}
/* render */
const colums = this.calculateColums_();
this.renderFlowMain_({ config: this.commentConfig, comment: commentListRes.data, column_count: colums }).then(() => {
this.renderHeader_({
starData: this.starCountRes.data,
listData: this.commentListRes.data,
star_color: this.commentConfig.star_color,
comment_avg_star: this.commentListRes.data.avg_star,
comment_count: this.commentListRes.data?.count,
isPC: this.isPC,
...this.commentConfig,
});
this.renderStarCounts_({ ...this.starCountRes.data, ...this.commentConfig });
this.addImpression(`[data-section-id="${this.sectionId}"] .revue_container`);
this.renderCommentList_({ list: commentListRes.data, config: this.commentConfig, shop_name: window.SHOPLAZZA.shop.shop_name },true);
});
window.removeEventListener('resize', this.rerenderFn);
window.addEventListener('resize', this.rerenderFn);
})
.catch(error => {
this.renderHideSkeleton_();
this.renderHideComment_();
console.error('error', error);
});
}
mountCallback = () => {
this.render_()
}
/* fetch api/comment-config */
fetchCommentConfig_ = async () => {
const response = await fetch('/api/comment-config');
return response.json();
}
fetchSummary_ = async (data) => {
const response = await fetch('/api/v1/comments/summary',{
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
return response.json();
}
fetchCommentList_ = async(data) => {
const response = await fetch(`/api/v1/comments`,{
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
...data,
offset: data.offset,
show_product:!!this.show_product,
star_least:this.star_least,
limit:this.limit,
sort_by:data.sort_by || 'created_at',
sort_direction: data.sort_direction || 'desc',
filter_type:(this.isProductPage || this.isCartPage) ? 'product' : this.isCollectionPage ? 'collection' : 'store',
show_reply: !!data.show_reply,
only_media: !!data.with_photo,
product_ids: this.isProductPage ? '7e3c2b7a-b391-41ec-959c-1768fe2fd3d1' : this.isCartPage ? '' : '',
collection_id: this.isCollectionPage ? '' : '',
only_featured: this.only_featured,
})
});
if(response.status != 200){
return Promise.reject(false);
}
return response.json();
}
fetchThemeConfig_ = async(themeId) => {
const response = await fetch(`/api/comment/theme-config?theme_id=${themeId}`);
return response.json();
}
renderHideSkeleton_ = () => {
const skeleton = document.getElementById(`revue_flow_skeleton-${this.sectionId}`);
if(skeleton){
skeleton.style.display = 'none';
};
}
renderHideComment_ = () => {
const holderEl = document.getElementById(`revue_no_data_placeholder_${this.sectionId}`);
if (window.top !== window.self) {
SPZ.whenApiDefined(holderEl).then((api) => {
api.render({}, true);
});
}else{
holderEl.style.display = 'none';
}
}
renderEmptyComment_ = () => {
const emptyEle = document.querySelector(`#revue-empty-1774506019626`);
if(emptyEle) {
emptyEle.classList.remove('hidden');
}
}
renderFlowMain_ = async (data) => {
const mainEle = document.querySelector(`#revue_flow_render-${this.sectionId}`);
if (mainEle) {
const api = await SPZ.whenApiDefined(mainEle);
return api.render({ ...data },true);
}
}
calculateColums_ = () => {
let colums = 1;
this.isPC = window.innerWidth > (window.breakpoint || 960);
if (this.layout === 'grid') {
colums = this.isPC ? 4 : 2;
} else {
colums = this.isPC ? 2 : 1;
}
if(this.layout == 'wall'){
colums = this.isPC ? (this.wall_pc_num || 4) : (this.wall_mobile_num || 2);
}
return colums
}
rerenderFn = (list) => {
try{
if(!this?.commentListRes?.data) return;
const currentWidth = window.innerWidth;
if (currentWidth == this.lastWidth ) {
return
} else {
this.lastWidth = currentWidth;
}
const throttleHandle = SPZCore.Types.throttle(window,()=>{
let colums = this.calculateColums_();
this.renderFlowMain_({ config: this.commentConfig, comment: this.commentListRes.data, column_count: colums }).then(() => {
this.renderHeader_({
starData: this.starCountRes.data,
listData: this.commentListRes.data,
star_color: this.commentConfig.star_color,
comment_avg_star: this.commentListRes.data.avg_star,
comment_count: this.commentListRes.data?.count,
isPC: this.isPC,
...this.commentConfig,
});
this.renderStarCounts_({ ...this.starCountRes.data, ...this.commentConfig });
this.renderCommentList_({ list: this.commentListRes.data, config: this.commentConfig, shop_name: window.SHOPLAZZA.shop.shop_name }, true);
});
},200)
throttleHandle()
}catch(e){
console.log(e);
}
}
renderCommentList_ = (data,redo=false) => {
if(this.accent_color && this.accent_color != 'null'){
this.commentConfig.star_color = this.accent_color;
}
const listEle = document.querySelector(`#revue_flow_list-${this.sectionId}`);
if (listEle) {
const current_list = data.list.list.map((item, index) => {
return {
...item,
config: this.commentConfig,
index: data.sorted ? index : this.appendList.length + index,
shop_name: window.SHOPLAZZA.shop.shop_name
}
});
if (data.sorted) {
this.appendList = current_list;
SPZ.whenApiDefined(listEle).then((api) => {
api.listRender({ count: data.list.count, list: current_list },true);
});
} else {
let obj = {};
this.appendList = this.appendList.concat(current_list).reduce((cur,next) => {
obj[next.id] ? "" : obj[next.id] = true && cur.push(next);
return cur;
},[]);
SPZ.whenApiDefined(listEle).then((api) => {
api.listRender({ count: data.list.count, list: current_list },redo);
});
}
};
this.renderLoadMoreBtn(data.list);
}
mediaParse_ = function (url) {
var result = {};
try {
url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (str, key, value) {
try {
result[key] = decodeURIComponent(value);
} catch (e) {
result[key] = value;
}
});
result.src = url.split('?')[0];
} catch (e) {};
return result;
}
impFunc = function (selector, cb) {
// 添加自动曝光
const el = document.querySelector(selector);
const onImpress = () => {
cb();
};
// 元素未曝光时添加曝光事件监听,已曝光则可以立刻触发处理器
if (el && !el.getAttribute('imprsd')) {
el.addEventListener('impress', onImpress);
} else if (el) {
onImpress();
}
};
addImpression = function (selector) {
this.impFunc(selector, () => {
window.sa && window.sa.track('plugin_reviews_masonry_pv', {
layout_type: this.layout,
level_type: this.star_least,
show_number: this.limit,
plugin_timestamp: new Date().valueOf().toString(),
reviews_num: this.appendList.length
});
});
};
addModalImpression = function (selector, params) {
this.impFunc(selector, () => {
window.sa && window.sa.track('plugin_reviews_modal_pv', {
...params,
plugin_timestamp: new Date().valueOf().toString(),
});
});
};
setupAction_ = () => {
this.registerAction('refresh', async(invocation) => {
this.render_({
...this.params,
offset: 0,
sort_by: 'created_at',
sort_direction: 'desc',
show_reply: true,
with_photo: false,
})
});
this.registerAction('renderTypeChangeList', async(invocation) => {
const {type,direction } = invocation.args.data;
this.with_photo = type === 'with_photo';
this.direction = direction;
this.params = {
...this.params,
offset: 0,
sort_by: this.sort,
sort_direction: this.direction,
show_reply: 1,
with_photo: this.with_photo
};
this.fetchCommentList_(this.params).then(response => {
this.renderCommentList_({ sorted: true, list: response.data, config: this.commentConfig, shop_name: window.SHOPLAZZA.shop.shop_name }, true);
});
})
this.registerAction('renderSortedList', async(invocation) => {
const {sort, direction} = invocation.args.data;
this.sort = sort;
this.direction = direction;
const panelId = this.panelId;
this.params = {
...this.params,
offset: 0,
sort_by: this.sort,
sort_direction: this.direction,
show_reply: 1 ,
with_photo: this.with_photo
}
this.fetchCommentList_(this.params).then(response => {
this.renderCommentList_({ sorted: true, list: response.data, config: this.commentConfig, shop_name: window.SHOPLAZZA.shop.shop_name }, true);
});
});
this.registerAction('renderProductCommentModal', async(invocation) => {
const id = invocation.args.id;
const current = this.appendList?.find(_data => _data.id == id);
const modalEle = document.querySelector(`#revueDetailModal-${this.sectionId}`);
const imgArr = current.img.map(image => {
const width = this.getUrlKey('width', image);
const height = this.getUrlKey('height', image);
return {
width,
height,
rate: (height/width).toFixed(2)*100,
url: image
};
});
if (modalEle) {
SPZ.whenApiDefined(modalEle).then((api) => {
api.renderModalFn({
data: {
...current,
img: imgArr
},
commentConfig: this.commentConfig,
layout: this.layout,
level_type: this.star_least,
show_number: this.limit
});
});
}
});
this.registerAction('loadMore', async(invocation) => {
this.params = {
...this.params,
offset: this.appendList.length,
sort_by: this.sort,
sort_direction: this.direction,
show_reply: 1 || this.commentConfig.show_reply ? 1 : 0,
with_photo: this.with_photo
}
this.fetchCommentList_(this.params).then(response => {
this.renderCommentList_({ list: response.data, config: this.commentConfig, shop_name: window.SHOPLAZZA.shop.shop_name });
});
});
}
getUrlKey = (name, url) => {
return (
decodeURIComponent(
(new RegExp("[?|&]" + name + "=" + "([^&;]+?)(&|#|;|$)").exec(
url
) || [, ""])[1].replace(/\+/g, "%20")
) || null
);
}
renderHeader_ = (data) => {
if(this.accent_color && this.accent_color != 'null'){
data.star_color = this.commentConfig.star_color = this.accent_color;
}
const headerEle = document.querySelector(`#review-revue-header-${this.sectionId}`);
if (headerEle) {
SPZ.whenApiDefined(headerEle).then(async (api) => {
api.render(data);
});
}
}
renderStarCounts_ = (data) => {
const summaryEle = document.querySelector(`#revue-summary-${this.sectionId}`);
if (summaryEle) {
SPZ.whenApiDefined(summaryEle).then((api) => {
api.render({
...data,
star_color: this.commentConfig.star_color
});
});
}
}
renderLoadMoreBtn = (data) => {
const loadEle = document.querySelector(`#revue_flow_load_more_render-${this.sectionId}`);
if (loadEle) {
SPZ.whenApiDefined(loadEle).then((api) => {
api.render({ comment: data }, true);
});
}
}
triggerEvent_(name, data) {
const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {});
this.action_.trigger(this.element, name, event);
}
unmountCallback(){
}
isLayoutSupported(layout) {
return layout == SPZCore.Layout.CONTAINER;
}
}
SPZ.defineElement(TAG, SpzCustomRevueFlow)
const TAG = 'spz-custom-revue-modal';
class SPZCustomRevueModal extends SPZ.BaseElement {
constructor(element) {
super(element);
this.renderedId = '';
this.closeCB = null;
this.sectionId = this.element.getAttribute('section-id');
}
static deferredMount() {
return false;
}
buildCallback = () => {
this.setupAction_();
}
mountCallback = () => {
}
setupAction_ = () => {
this.registerAction('renderModal', this.renderModalFn)
this.registerAction('closeFn',() => {
this?.closeCB?.()
})
}
mediaParse_ = function (url) {
var result = {};
try {
url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (str, key, value) {
try {
result[key] = decodeURIComponent(value);
} catch (e) {
result[key] = value;
}
});
result.src = url.split('?')[0];
} catch (e) {};
return result;
}
impFunc = function (selector, cb) {
// 添加自动曝光
const el = document.querySelector(selector);
const onImpress = () => {
cb();
};
// 元素未曝光时添加曝光事件监听,已曝光则可以立刻触发处理器
if (el && !el.getAttribute('imprsd')) {
el.addEventListener('impress', onImpress);
} else if (el) {
onImpress();
}
};
addModalImpression = function (selector, params) {
this.impFunc(selector, () => {
window.sa && window.sa.track('plugin_reviews_modal_pv', {
...params,
plugin_timestamp: new Date().valueOf().toString(),
});
});
};
renderModalFn(receivedData){
if(!receivedData) return;
const {
data:current,
commentConfig,
layout,
level_type,
show_number,
closeCB,
mimic_mobile_style,
props
} = receivedData;
try{
if(closeCB){
this.closeCB = () => {
closeCB()
};
}
}catch(e){
console.log(e);
};
const commentModalEl = document.querySelector(`#revue-product-comment-modal-${this.sectionId}`);
const modalRenderEl = document.querySelector(`#revue_flow_modal_render-${this.sectionId}`);
if (!!mimic_mobile_style) {
if (commentModalEl) {
commentModalEl.classList.add('mobile-wrap');
}
if (modalRenderEl) {
modalRenderEl.classList.add('w-h-full-h5');
}
};
const parsedImages = current?.img?.map(image => {
return this.mediaParse_(`${image.url}?width=${image.width}&height=${image.height}`);
});
const modalEle = document.querySelector(`#revue_flow_modal_render-${this.sectionId}`);
if (modalEle) {
SPZ.whenApiDefined(modalEle).then((api) => {
api.render({ ...current, img: parsedImages, config: commentConfig, shop_name: window.SHOPLAZZA.shop.shop_name, mimic_mobile_style }, true).then(() => {
this.addModalImpression('.revue_modal_container', {
id: current.id,
username: current.username,
content: current.content,
star: current.star,
is_verified: current.is_verified,
is_featured: current.is_featured,
anonymous: current.anonymous,
iso_code_3: current.iso_code_3,
like_count: current.like,
layout_type: layout,
level_type: level_type,
show_number: show_number,
});
}).then(()=>{
this.renderedId = current.id
});
});
}
}
isLayoutSupported(layout) {
return layout == SPZCore.Layout.CONTAINER;
}
}
SPZ.defineElement('spz-custom-revue-modal', SPZCustomRevueModal)
${function() {
return `
${function(){
if (media.videosrc) {
const src = media.videosrc + '.' + media.ext;
return `
`
} else if(media.mp4 || media.hls) {
return `
`
} else {
return ``
}
}()}
`;
}()}
const TAG = 'spz-custom-revue-video';
class SPZCustomRevueVideo extends SPZ.BaseElement {
constructor(element) {
super(element);
}
static deferredMount() {
return false;
}
buildCallback = () => {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
// data-images 格式为 xxxx.png?width=1&height=1,xxxx.png?width=1&height=1
const images = this.element.getAttribute('data-images').split(',') || [];
const parsedImages = images.map(image => {
return this.mediaParse_(image);
});
this.images = parsedImages;
this.isPC = window.innerWidth > 960;
}
loadVideo = () => {
this.doRender_({
images: this.images,
isPC: this.isPC
}).then(()=>{
this.triggerEvent_('connected', {});
})
}
mountCallback = () => {
this.loadVideo();
this.registerAction('loadVideo', async(invocation) => {
this.loadVideo();
})
}
doRender_ = (data) => {
return this.templates_
.findAndRenderTemplate(this.element, data, null)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
});
}
mediaParse_ = function (url) {
var result = {};
try {
url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (str, key, value) {
try {
result[key] = decodeURIComponent(value);
} catch (e) {
result[key] = value;
}
});
result.preview_image = url.split('?')[0];
} catch (e) {};
return result;
}
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, SPZCustomRevueVideo)
const TAG = 'spz-custom-revue-header';
class SPZCustomRevueHeader extends SPZ.BaseElement {
constructor(element) {
super(element);
this.showCount = this.element.getAttribute('show-count');
}
static deferredMount() {
return false;
}
isLayoutSupported(layout) {
return layout == SPZCore.Layout.CONTAINER;
}
buildCallback() {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.showCount = this.element.getAttribute('show-count');
this.showSummary = this.element.getAttribute('show-summary');
this.showWriteReview = this.element.getAttribute('show-write-review');
this.showType = this.element.getAttribute('show-type') ;
this.showSort = this.element.getAttribute('show-sort') ;
this.sectionId = this.element.getAttribute('section-id');
this.viewall = this.element.getAttribute('viewall') ?? false;
this.prefix = this.element.getAttribute('prefix');
}
mountCallback() {
}
triggerEvent_(name, data) {
const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {});
this.action_.trigger(this.element, name, event);
}
render(data) {
const ndata = {
...data,
showCount: this.showCount,
showSummary: this.showSummary,
showWriteReview: this.showWriteReview,
showType: this.showType,
showSort: this.showSort,
}
if(this.viewall == 'review'){
ndata.viewall = false
}
return this.templates_
.findAndRenderTemplate(this.element, ndata, null, true)
.then(({el}) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
}).then(() => {
if(data && Object.keys(data).length > 0) {
this.updateRender(data);
this.setupSummaryContainerEffects_(data);
}
});
}
updateRender(data) {
this.renderStarCounts_(data);
this.renderTypeSelect(data);
this.renderSortSelect(data);
}
renderStarCounts_ (data) {
const renderData = {
...data.starData,
...data,
star_color: data.star_color,
isPC: data.isPC,
}
const summaryEle = data.isPC ? this.element.querySelector(`#${this.prefix}-revue-summary-${this.sectionId}_header_pc`) : this.element.querySelector(`#${this.prefix}-revue-summary-${this.sectionId}_header`);
if(summaryEle) {
SPZ.whenApiDefined(summaryEle).then((api) => {
api.render(renderData);
});
}
}
renderTypeSelect(data) {
const typeSelect = this.element.querySelector(`#${this.prefix}-revue-header-type-${this.sectionId}`);
if(typeSelect) {
SPZ.whenApiDefined(typeSelect).then((api) => {
api.render(data);
api.registerAction('headerType_', (invocation) => {
this.triggerEvent_('headerType', invocation.args.data);
});
});
}
}
renderSortSelect(data) {
const suffix = data.suffix || this.sectionId;
const sortSelect = this.element.querySelector(`#${this.prefix}-revue-header-sort-${suffix}`);
if(sortSelect) {
SPZ.whenApiDefined(sortSelect).then((api) => {
api.registerAction('headerSort_', (invocation) => {
this.triggerEvent_('headerSort', invocation.args.data);
});
});
}
}
setupSummaryContainerEffects_(data) {
if(data.isPC) {
this.setupSummaryContainerHover_();
} else {
this.setupSummaryContainerTap_();
}
}
setupSummaryContainerHover_() {
const summaryContainer = this.element.querySelector(`#revue-header-summary-container-${this.sectionId}`);
const summaryEle = this.element.querySelector(`#${this.prefix}-revue-summary-${this.sectionId}_header_pc`);
if (!summaryContainer || !summaryEle) return;
let isHovering = false;
// 鼠标移入容器时显示summary
SPZUtils.Event.listen(summaryContainer, 'mouseenter', () => {
isHovering = true;
summaryEle.removeAttribute('hidden');
const selectIcon = summaryContainer.querySelector(`#revue-header-summary-icon-${this.sectionId}`);
if(selectIcon) {
selectIcon.classList.add('up-icon');
}
});
// 鼠标移入summary时也保持显示
SPZUtils.Event.listen(summaryEle, 'mouseenter', () => {
isHovering = true;
});
// 鼠标移出容器时,检查是否还在summary上
SPZUtils.Event.listen(summaryContainer, 'mouseleave', () => {
isHovering = false;
setTimeout(() => {
if (!isHovering) {
summaryEle.setAttribute('hidden', 'true');
const selectIcon = summaryContainer.querySelector(`#revue-header-summary-icon-${this.sectionId}`);
if(selectIcon) {
selectIcon.classList.remove('up-icon');
}
}
}, 50);
});
// 鼠标移出summary时,检查是否还在容器上
SPZUtils.Event.listen(summaryEle, 'mouseleave', () => {
isHovering = false;
setTimeout(() => {
if (!isHovering) {
summaryEle.setAttribute('hidden', 'true');
const selectIcon = summaryEle.querySelector(`#revue-header-summary-icon-${this.sectionId}`);
if(selectIcon) {
selectIcon.classList.remove('up-icon');
}
}
}, 50);
});
}
setupSummaryContainerTap_() {
const selectIcon = this.element.querySelector(`#revue-header-summary-icon-${this.sectionId}`);
const summaryEle = this.element.querySelector(`#${this.prefix}-revue-summary-${this.sectionId}_header`);
if(!summaryEle) return;
let isTapped = false; // 是否显示summary
SPZ.whenApiDefined(summaryEle).then((api) => {
api.registerAction('display', () => {
if(isTapped) {
isTapped = false;
summaryEle.removeAttribute('hidden');
selectIcon.classList.add('up-icon');
} else {
isTapped = true;
summaryEle.setAttribute('hidden', 'true');
selectIcon.classList.remove('up-icon');
}
});
});
}
}
SPZ.defineElement(TAG, SPZCustomRevueHeader);
${function(){
const starOrder = ['one_star', 'two_star', 'three_star', 'four_star', 'five_star'];
function sortStarRatings(ratings) {
const sortedRatingsArr = [];
starOrder.map((star,index) => {
sortedRatingsArr.push(index+1);
return star;
});
return sortedRatingsArr;
};
const star_levels = sortStarRatings(data.star_detail).reverse();
return `
${data.comment_avg_star}
Total reviews: ${data.comment_count > 999 ? '999+' : data.comment_count}
`;
}()}
${function() {
return `
`
}()}
${function() {
const list = data.listData;
return `
With Photos(${list.image_count})
`
}()}
${function() {
const list = data.listData;
return `
With Photos(${list.image_count})
`
}()}
const TAG = 'spz-custom-revue-type';
class SPZCustomRevueType extends SPZ.BaseElement {
constructor(element) {
super(element);
}
static deferredMount() {
return false;
}
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;
}
buildCallback = () => {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.isPC = window.innerWidth > 960;
this.width = this.isPC ? `${this.element.getAttribute('width') || 150}px` : '100%';
this.randomStr = Math.random().toString(36).substr(2);
this.sectionId = this.element.getAttribute('section-id') || '1774506019626';
this.prefix = this.element.getAttribute('prefix');
}
mountCallback = () => {
}
render = (data) => {
const renderData = {
...data,
width: this.width,
randomStr: this.randomStr
};
return this.templates_
.findAndRenderTemplate(this.element, renderData, null)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
}).then(() => {
let revueTypeListRender = this.isPC ? this.element.querySelector(`#${this.prefix}-revue-type-list-render-${this.sectionId}`) : this.element.querySelector(`#${this.prefix}-revue-type-dropdown-render-${this.sectionId}`);
revueTypeListRender && SPZ.whenApiDefined(revueTypeListRender).then((api) => {
api.render(renderData).then(() => {
if (this.isPC) {
this.addEventListenersForPC_();
} else {
this.addEventListenersForMobile_();
}
});
});
});
}
addEventListenersForPC_ = () => {
const revueSelectList = this.element.querySelector('.revue_select_list');
const revueSelectItem = this.element.querySelectorAll('.revue_select_item');
const revueSelectTypeIcon = this.element.querySelector(`#${this.prefix}-revue_select_type_icon-${this.sectionId}`);
revueSelectItem.forEach(item => {
item.addEventListener('click', () => {
const type = item.getAttribute('data-type');
const direction = item.getAttribute('data-direction');
this.triggerEvent_('type', { type, direction });
this.element.querySelector('.revue_select_label').innerText = item.innerText;
revueSelectList.classList.remove('revue_select_list_active');
const revueChecked = this.element.querySelector(`#${this.prefix}-revue_checked`);
revueChecked && SPZCore.Dom.removeElement(revueChecked);
const revueCheckedClone = revueChecked.cloneNode(true);
item.appendChild(revueCheckedClone);
if (!revueSelectTypeIcon.classList.contains('up_icon')) {
return;
}
const pcDropdownEle = this.element.querySelector(`#${this.prefix}-revue-type-pc-dropdown-${this.sectionId}`);
revueSelectTypeIcon.classList.remove('up_icon');
SPZ.whenApiDefined(pcDropdownEle).then((api) => {
api.close();
});
});
});
window.addEventListener('scroll', (e) => {
if (!revueSelectTypeIcon.classList.contains('up_icon')) {
return;
}
revueSelectTypeIcon.classList.remove('up_icon');
SPZ.whenApiDefined(pcDropdownEle).then((api) => {
api.close();
});
});
}
addEventListenersForMobile_ = () => {
const revueTypeDropdownItem = this.element.querySelectorAll(`#${this.prefix}-revue-type-dropdown-${this.sectionId} .revue_type_dropdown_item`);
revueTypeDropdownItem.forEach(item => {
item.addEventListener('click', () => {
const type = item.getAttribute('data-type');
const direction = item.getAttribute('data-direction');
revueTypeDropdownItem.forEach((_item)=>{_item.classList.remove('selected')})
item.classList.add('selected');
// 抛出事件
this.triggerEvent_('type', { type, direction });
// 移除revue_checked元素,复制一个新的到当前选中的元素
const revueChecked = this.element.querySelector(`#${this.prefix}-revue-type-dropdown-${this.sectionId} #${this.prefix}-revue_checked`);
revueChecked && SPZCore.Dom.removeElement(revueChecked);
const revueCheckedClone = revueChecked.cloneNode(true);
item.appendChild(revueCheckedClone);
const mDropdownEle = this.element.querySelector(`#${this.prefix}-revue-type-dropdown-${this.sectionId}`);
SPZ.whenApiDefined(mDropdownEle).then((api) => {
api.close();
});
});
});
}
}
SPZ.defineElement(TAG, SPZCustomRevueType)
${function() {
const isPercentage = data.show_percentage === 'true' && data.total <= data.show_percentage_num;
return `
${!isPercentage ? `${data.count}` : `${(data.count / data.total * 100).toFixed(1)}%`}
`
}()}
const TAG = 'spz-custom-revue-progress';
class SPZCustomRevueProgress extends SPZ.BaseElement {
constructor(element) {
super(element);
}
static deferredMount() {
return false;
}
buildCallback = () => {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.isPC = window.innerWidth > (window.breakpoint || 960);
this.height = '6px';
this.show_percentage = this.element.getAttribute('show_percentage') || 'false';
this.show_percentage_num = this.element.getAttribute('show_percentage_num') || 100;
this.color = this.element.getAttribute('color') || '#000000';
this.count = this.element.getAttribute('count');
this.total = this.element.getAttribute('total');
}
mountCallback = () => {
this.doRender_({
count: Number(this.count),
total: Number(this.total),
height: this.height,
color: this.color,
show_percentage: this.show_percentage,
show_percentage_num: this.show_percentage_num
}).then(() => {
});
}
doRender_ = (data) => {
return this.templates_
.findAndRenderTemplate(this.element, data, null)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
});
}
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, SPZCustomRevueProgress)
(function() {
const TAG = 'spz-custom-new-revue';
class SpzCustomNewRevue extends SPZ.BaseElement {
constructor(element) {
super(element);
this.config_ = null;
this.loading_ = false;
this.accent_color = this.element.getAttribute('accent-color');
this.sectionId = this.element.getAttribute('section-id');
this.prefix = this.element.getAttribute('prefix');
}
buildCallback() {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.form_ = SPZCore.Dom.scopedQuerySelector(
this.element,
'form'
);
this.hasShowLengthInputs_ = SPZCore.Dom.scopedQuerySelectorAll(
this.form_,
'[showlength]'
);
[...this.hasShowLengthInputs_].forEach(item => {
const countRecordDom = SPZCore.Dom.scopedQuerySelector(
this.form_,
`#${item.id} ~ div[type="count-record"]`
);
if (!countRecordDom) {
console.error(`Cannot find count record DOM element for input ${item.id}`);
return;
}
item.addEventListener('input', (e) => {
countRecordDom.innerText = `${e.target.value.length}/3000`;
});
});
this.setupAction_();
this.getRevueConfigData_();
}
setupAction_() {
this.registerAction('submitForm', async(invocation) => {
if (this.loading_) {
return;
}
this.loading_ = true;
const formData = Object.entries(invocation.args.data).reduce((acc, [key, value]) => {
if (key === 'star' || key === 'type') {
acc[key] = Number(value[0]);
} else {
acc[key] = value[0];
}
return acc;
}, {});
try {
const data = await fetch('/api/comment', {
method: "post",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(formData)
}).then(res => res.json());
if (data.state === 0) {
this.triggerEvent_('submitSuccess', {
panelId: 'with_photo',
message: ''
});
return;
}
throw new Error(data.msg);
} catch(e) {
e = await e;
this.triggerEvent_('submitError', {data: e.message});
} finally {
this.loading_ = false;
}
});
this.registerAction('renderFormStar', async(invocation) => {
this.triggerEvent_('rerenderFormStar', { star_color: this.starColor_ });
})
}
mountCallback() {
}
getRevueConfigData_ = () => {
fetch('/api/comment-config')
.then(res => res.json())
.then(data => {
this.config_ = data.data;
// anonymous_permission 是否支持匿名
if (!this.config_.anonymous_permission) {
const anonymousInput = this.form_.querySelector(`#${this.prefix}-revue-anonymous-${this.sectionId}`);
anonymousInput.value = 'false';
anonymousInput.parentNode.classList.add('hidden', 'anonymous-permission-hidden');
}
this.starColor_ = this.config_.star_color;
if(this.accent_color && this.accent_color != 'null'){
this.starColor_ = this.accent_color;
}
// render star
// star_color 星星颜色
const starEl = this.form_.querySelector(`#${this.prefix}-revue_write_modal_star-${this.sectionId}`);
if (starEl) {
SPZ.whenApiDefined(starEl).then((api) => {
api.render({ star_color: this.starColor_ });
});
}
});
}
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, SpzCustomNewRevue);
})()
(function() {
const TAG = 'spz-custom-revue-product-info-script';
class SpzCustomRevueProductInfoScript extends SPZ.BaseElement {
constructor(element) {
super(element);
/** @private {!Element} */
this.product_id = null;
}
async buildCallback() {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.product_id = this.getProductId_();
this.triggerEvent_('init', { product_id: this.product_id });
try {
const data = await this.getProductInfo_();
if (data?.data?.product) {
this.triggerEvent_('finish', data.data.product);
}
} catch (error) {
console.error('Failed to fetch product info:', error);
// Handle the error appropriately
}
}
getProductId_ = () => {
return window.SHOPLAZZA.meta.page.resource_id;
}
async getProductInfo_() {
if (!this.product_id) {
console.error('Product ID is undefined or null');
return null;
}
try {
const response = await fetch(`/api/products/${this.product_id}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error fetching product info:', error);
throw error; // Rethrow to be caught by the caller
}
}
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.LOGIC;
}
}
SPZ.defineElement(TAG, SpzCustomRevueProductInfoScript);
})()
${function(){
return `
${data.starNum}/${data.starTotal}
`;
}()}
${function(){
return `
${data.showStarText === 'true' ? `
${data.starNum}/${data.starTotal}
` : ''}
`;
}()}
const TAG = 'spz-custom-revue-star';
class SPZCustomRevueStar extends SPZ.BaseElement {
constructor(element) {
super(element);
}
static deferredMount() {
return false;
}
buildCallback = () => {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.starNum = this.element.getAttribute('starNum');
this.starTotal = this.element.getAttribute('starTotal');
this.showStarText = this.element.getAttribute('showStarText');
this.starColor = this.element.getAttribute('color');
this.interact = this.element.getAttribute('interact');
this.starSize = this.element.getAttribute('starSize') || 14;
}
mountCallback = () => {
this.doRender_({
starTotal: this.starTotal,
totalArray: Array.from({ length: Number(this.starTotal) }, (v, k) => k + 1),
starNum: this.starNum,
showStarText: this.showStarText,
starColor: this.starColor,
starSize: this.starSize
}).then(() => {
if (this.interact) {
this.addEventListeners_();
}
});
}
addEventListeners_ = () => {
const stars = document.querySelectorAll('.revue-star__star');
stars.forEach(star => {
star.addEventListener('click', event => {
const starEl = star.closest('.revue-star__star');
const starIndex = Number(starEl.dataset.index);
let isHalf = event.offsetX < star.offsetWidth / 2;
// rtl
if (document.documentElement.getAttribute('dir') === 'rtl') {
isHalf = event.offsetX > star.offsetWidth / 2;
}
const starValue = isHalf ? starIndex - 0.5 : starIndex;
this.starClickHandler_({ value: starValue });
});
});
}
renderStar = () => {
const isRtl = document.documentElement.getAttribute('dir') === 'rtl';
const stars = this.element.querySelectorAll('.revue-star__star');
stars.forEach((star, i) => {
const starIndex = i + 1;
const starEl = star.querySelector('svg:nth-child(2)');
const isHalf = this.starNum % 1 > 0 && Math.ceil(this.starNum) === starIndex;
const isSolid = starIndex <= Math.ceil(this.starNum);
starEl.style.display = isSolid ? 'block' : 'none';
if (isHalf) {
if (isRtl) {
// RTL布局下,如果是半星,显示星星的右半边
starEl.style.clipPath = `polygon(50% 0, 100% 0, 100% 100%, 50% 100%)`;
} else {
// LTR布局下,如果是半星,显示星星的左半边
starEl.style.clipPath = `polygon(0 0, 50% 0, 50% 100%, 0 100%)`;
}
} else {
starEl.style.clipPath = `polygon(0 0, 100% 0, 100% 100%, 0 100%)`
}
});
const showCountEle = this.element.querySelector('#revue-star-show-count');
showCountEle && SPZ.whenApiDefined(showCountEle).then((api) => {
api.render({ starNum: this.starNum, starTotal: this.starTotal });
});
}
doRender_ = (data) => {
return this.templates_
.findAndRenderTemplate(this.element, { starSize: this.starSize, ...data }, null)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
})
.then(() => {
this.starNum = data.starNum;
this.renderStar();
});
}
starClickHandler_ = (event) => {
this.starNum = event.value;
this.renderStar();
this.triggerEvent_('change', { value: event.value });
}
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, SPZCustomRevueStar)
(function() {
const TAG = 'spz-custom-new-revue-files-show';
class SpzCustomNewRevueFilesShow extends SPZ.BaseElement {
constructor(element) {
super(element);
/** @private {!Element} */
this.files_ = []
}
buildCallback() {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.setupAction_();
this.element.setAttribute('nums', this.files_.length);
}
mountCallback() {
}
setupAction_() {
this.registerAction('upload', async(invocation) => {
const uploadFileList = invocation.args?.data || [];
uploadFileList.forEach(file => {
if(this.files_.some(item => item.url === file.url)) return
this.files_.push(file);
})
this.doRender_();
});
this.registerAction('delete', async(invocation) => {
this.files_ = this.files_.filter((_, index) => index !== invocation.args.index);
this.doRender_();
this.triggerEvent_('delete', { count: this.files_.length, files: this.files_ });
});
this.registerAction('preview', async(invocation) => {
let previewFileData = this.files_[invocation.args.index];
if (previewFileData.type === 'video') {
previewFileData = {...this.parseVideoSrc_(previewFileData.url), ...previewFileData};
}
this.triggerEvent_('preview', previewFileData);
});
this.registerAction('clear', async(invocation) => {
this.files_ = [];
this.doRender_();
});
}
triggerEvent_(name, data) {
const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {});
this.action_.trigger(this.element, name, event);
}
parseVideoSrc_(src) {
const url = new URL(src);
const params = new URLSearchParams(url.search);
return {
videoUrl: url.origin + url.pathname,
mediaType: params.get('media_type'),
vID: params.get('vID'),
mp4: params.get('mp4'),
hls: params.get('hls')
};
}
doRender_ = () => {
this.triggerEvent_('setInputValue', {
data: this.files_
.map(file => {
const url = file.type === 'video' ? file.poster : file.url;
return `${url}?width=${file.width}&height=${file.height}`;
})
.join(',')
});
this.element.setAttribute('nums', this.files_.length);
return this.templates_
.findAndRenderTemplate(this.element, {
files: this.files_
})
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
})
}
isLayoutSupported = (layout) => {
return layout == SPZCore.Layout.CONTAINER;
}
}
SPZ.defineElement(TAG, SpzCustomNewRevueFilesShow);
})()
${function() {
if (!data) {
return '';
}
const {url, type, height, width, poster, mp4} = data;
if (type === 'image') {
return `
`
}
if (type === 'video') {
return `
`
}
return ``
}()}
${function() {
const item = data.originData;
const config = data.config;
item.imgArr = item.img.map(image => {
const width = getUrlKey('width', image);
const height = getUrlKey('height', image);
return {
width,
height,
rate: (height/width).toFixed(2)*100,
url: image
};
});
const randomStr = Math.random().toString(36).substring(7);
const formatDate = value => {
let date = new Date(value * 1000);
const day = date.toLocaleString('en-US', { day: '2-digit' });
const month = date.toLocaleString('en-US', { month: 'short' });
const year = date.toLocaleString('en-US', { year: 'numeric' });
return month + ' ' + day + ', ' + year;
};
function getUrlKey(name, url) {
return (
decodeURIComponent(
(new RegExp("[?|&]" + name + "=" + "([^&;]+?)(&|#|;|$)").exec(
url
) || [, ""])[1].replace(/\+/g, "%20")
) || null
);
}
let videoMp4 = '';
let videoHls = '';
if(item.imgArr.length > 0 && item.imgArr[0].url?.includes('media_type=video')){
videoMp4 = getUrlKey('mp4',item.imgArr[0].url);
videoHls = getUrlKey('hls',item.imgArr[0].url);
}
function mediaParse_(url) {
var result = {};
try {
url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (str, key, value) {
try {
result[key] = decodeURIComponent(value);
} catch (e) {
result[key] = value;
}
});
result.preview_image = url.split('?')[0];
} catch (e) {};
return result;
}
const parsedImages = item?.img.map(image => {
return mediaParse_(image);
});
return `
${item.content}
${data.shop_name} reply:
${item.reply && item.reply.length && item.reply[0].content}
${formatDate(item.created_at)}
`;
}()}
${function() {
const item = data.originData;
const config = data.config;
item.imgArr = item.img.map(image => {
const width = getUrlKey('width', image);
const height = getUrlKey('height', image);
return {
width,
height,
rate: (height/width).toFixed(2)*100,
url: image
};
});
const randomStr = Math.random().toString(36).substring(7);
const formatDate = value => {
let date = new Date(value * 1000);
const day = date.toLocaleString('en-US', { day: '2-digit' });
const month = date.toLocaleString('en-US', { month: 'short' });
const year = date.toLocaleString('en-US', { year: 'numeric' });
return month + ' ' + day + ', ' + year;
};
function getUrlKey(name, url) {
return (
decodeURIComponent(
(new RegExp("[?|&]" + name + "=" + "([^&;]+?)(&|#|;|$)").exec(
url
) || [, ""])[1].replace(/\+/g, "%20")
) || null
);
}
let videoMp4 = '';
let videoHls = '';
if(item.imgArr.length > 0 && item.imgArr[0].url?.includes('media_type=video')){
videoMp4 = getUrlKey('mp4',item.imgArr[0].url);
videoHls = getUrlKey('hls',item.imgArr[0].url);
}
function mediaParse_(url) {
var result = {};
try {
url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (str, key, value) {
try {
result[key] = decodeURIComponent(value);
} catch (e) {
result[key] = value;
}
});
result.preview_image = url.split('?')[0];
} catch (e) {};
return result;
}
const parsedImages = item?.img.map(image => {
return mediaParse_(image);
});
return `
${item.username}
${formatDate(item.created_at)}
`;
}()}
${function() {
const item = data.originData;
const config = data.config;
item.imgArr = item.img.map(image => {
const width = getUrlKey('width', image);
const height = getUrlKey('height', image);
return {
width,
height,
rate: (height/width).toFixed(2)*100,
url: image
};
});
const randomStr = Math.random().toString(36).substring(7);
const formatDate = value => {
let date = new Date(value * 1000);
const day = date.toLocaleString('en-US', { day: '2-digit' });
const month = date.toLocaleString('en-US', { month: 'short' });
const year = date.toLocaleString('en-US', { year: 'numeric' });
return month + ' ' + day + ', ' + year;
};
function getUrlKey(name, url) {
return (
decodeURIComponent(
(new RegExp("[?|&]" + name + "=" + "([^&;]+?)(&|#|;|$)").exec(
url
) || [, ""])[1].replace(/\+/g, "%20")
) || null
);
}
let videoMp4 = '';
let videoHls = '';
if(item.imgArr.length > 0 && item.imgArr[0].url?.includes('media_type=video')){
videoMp4 = getUrlKey('mp4',item.imgArr[0].url);
videoHls = getUrlKey('hls',item.imgArr[0].url);
}
function mediaParse_(url) {
var result = {};
try {
url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (str, key, value) {
try {
result[key] = decodeURIComponent(value);
} catch (e) {
result[key] = value;
}
});
result.preview_image = url.split('?')[0];
} catch (e) {};
return result;
}
const parsedImages = item?.img.map(image => {
return mediaParse_(image);
});
return `
${item.username}.${item.iso_code_3}
`;
}()}
View more
Wow you reached the bottom
Write a Review
${function(){
return `
No reviews available. The product reviews component has been hidden
Review Flow
`
}()}
Reviews
No reviews yet, why don't you leave the first review?
Write a Review
${function(){
const settings_product_title = "title";
const product_grid_image_size = "150%";
const settings_product_image_hover_on = true;
let settings_product_save_label = true;
const product_sold_out_label = false;
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 ?
`
` : ''}
`;
}()}
${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 `
` }()}
${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()}
`;
}()}
${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 ? `
` : '';
}
}()}
${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}
`
}()}
${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()}
`;
}()}
${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);
${function() {
const price = data.variant?.price || data.product.selectedVariant?.price;
const compare_at_price = data.variant?.compare_at_price || data.product.selectedVariant?.compare_at_price;
return `
`
}()}
${function() {
return data.product.options.length > 0 ? data.product.options.map((option, index) => {
return `
`
}).join('') : ``;
}()}
${function() {
const value = (data.originData && data.originData.value) || data.value;
const isHasValue = value ? true : false;
return `
${value ? value : "" }
`
}()}
${data.product.selectedSkuText ? data.product.selectedSkuText : ''}
${function() {
return (data.product.options || []).map((option, index) => {
return `
`;
}).join('')
}()}
${data.value ? data.value : ''}
${function() {
let dropdownCloseEvent = '';
if(!data.config.isMobile && (data.config.style == 'circle' || data.config.style == 'square')) {
data.product.options?.forEach((item, index) => {
dropdownCloseEvent += `app-atc-popover-${index}.close;`;
})
}
return `
`;
}()}
${function(){
let product_change_event = '';
const product_options = data.product.options?.filter(Boolean) || [];
for (let opt of product_options) {
const nameEscape = opt.name.replace(/\/|\\|\s|\'|\"|`|\<|\>/g, '');
product_change_event = product_change_event + `add-cart-selected-variant-${opt.id}.rerender(data=event.selectedValues.${opt.name});`;
}
return `
${function() {
const isDarkBg = data.isDarkBg;
const textColor = isDarkBg ? '#ffffff' : '#212B36';
const delPriceColor = isDarkBg ? '#E0E0E2' : '#9CA0B0';
const variantColor = isDarkBg ? '#ffffff' : '#939393';
const config = data.config;
return `
`
}()}
${function(){
if(data.is_button_click_info) {
return `
`
} else {
return `
`
}
}()}
${function() {
if(data.is_button_click_info) {
return `
${function(){
let product_change_event = '';
const product_options = data.product.options?.filter(Boolean) || [];
for (let opt of product_options) {
const nameEscape = opt.name.replace(/\/|\\|\s|\'|\"|`|\<|\>/g, '');
product_change_event = product_change_event + `add-cart-selected-variant-${opt.id}.rerender(data=event.selectedValues.${opt.name});`;
}
return `
`
}()}
`
} else {
return ``
}
}()}
`
}()}
const TAG = 'spz-custom-add-to-cart';
class SpzCustomAddToCart extends SPZ.BaseElement {
constructor(element) {
super(element);
this.plugin_timestamp = Date.now();
this.defaultColorConfig = {
module_bg: "#FFFFFF",
button_bg: "#E84926",
button_color: "#FFFFFF",
text_color: "#202020",
price_color: "#E84926",
border_color: "#E6E6E6",
border_bg: "#FFFFFF",
round_size: '4'
};
this.config = this.defaultColorConfig;
this.originStickyTop = 0;
this.qty = 1;
this.variantId = this.element.getAttribute('variant-id');
this.trackMap = {
qty: this.trackQty.bind(this),
variant: this.trackVariant.bind(this),
addToCart: this.trackAddtoCart.bind(this),
atcView: this.trackAtcView.bind(this),
}
this.isHasView = false;
this.isFirstUpdateVariant=false;
}
static deferredMount() {
return false;
}
buildCallback() {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.locale_ = SPZServices.localeFor(this.win);
this.hasGlobalAtcListener = !!document.querySelector('#add-cart-event-proxy');
this.setupAction_();
}
mountCallback() {
// 初始化
this.init();
}
async init() {
// 如果不是详情页,不需要执行后面js
if (window.SHOP_PARAMS.template_type !== '1') return;
await this.setLocale();
this.handleIsRender();
this.bingEvents();
}
bingEvents() {
// 设备切换 重新渲染
window.addEventListener('resize',
SPZCore.Types.debounce(
this.win,
(invocation) => {
// 关闭弹窗, 解决切换屏幕尺寸不能滚动的问题
this.triggerEvent_('closeShopModal');
this.config = {
...this.config,
isMobile: window.innerWidth < 768 ? true : false,
position: window.innerWidth < 768 ? 'down' : this.config.display_position
};
this.renderAddToCart();
},
200
))
}
// 获取多语言
async setLocale() {
let data;
try {
//多语言
data = await this.locale_.i18n(['product', 'products']);
} catch (error) {
console.error(error);
}
this.i18n = {
'sold_out': 'Sold out',
'add_to_cart': 'Add to cart',
'unavailable': 'Unavailable',
'product_unavailable': 'Product is unavailable.',
...data?.product?.product_detail,
...data?.products?.product,
}
}
getProductData() {
let pJson = document.querySelector('#product-json');
if (pJson) {
return JSON.parse(pJson.innerHTML)?.product;
} else if (typeof $ === 'function') {
return $(document).data('djproduct')?.product;
}
return undefined;
}
getAddCartBtn() {
return document.querySelector('[data-section-type="product_detail"] [data-click="addToCart"], [data-section-type="product_detail"] [role="addToCart"], [data-section-type="product_detail"] [data-click="buyNow"], [data-section-type="product_detail"] [role="buyNow"], [data-section-type="product"] [data-click="addToCart"], [data-section-type="product"] [role="addToCart"], [data-section-type="product"] [data-click="buyNow"], [data-section-type="product"] [role="buyNow"]');
}
handleObserver() {
if(this.config.trigger_condition == 'theme_hidden') {
const $addCartBtn = this.getAddCartBtn();
if($addCartBtn) {
// 配置了加购/购买按钮; 设置按钮为observer观察目标
$addCartBtn.setAttribute('id', 'app-atc-need-sticky-buttons')
}
}
}
findAncestor(node, selector) {
while (node) {
if (node.querySelector(selector)) {
return node;
}
node = node.parentElement;
}
return null;
}
getThemeProductInfoForm() {
let $themeForm = '';
const $themeProductInfo = document.querySelector('[data-section-type="product_detail"], [data-section-type="product"]');
$themeForm = $themeProductInfo?.querySelector('form');
return $themeForm;
}
// 获取主题商品加购数量
getThemeQuantity() {
let $themeForm = this.getThemeProductInfoForm();
if($themeForm) {
const formData = new FormData($themeForm);
const quantity = formData.get('quantity');
return quantity;
} else {
return null;
}
}
getThemeInitVariantsData() {
const $themeForm = this.getThemeProductInfoForm();
const formData = new FormData($themeForm);
}
// 获取主题初始表单数据
getThemeProductFormData = () => {
const $themeForm = this.getThemeProductInfoForm();
if($themeForm) {
const form_data = new FormData($themeForm);
const form_data_format = {}
for (const [key, value] of form_data) {
form_data_format[key] = value;
}
return form_data_format;
} else {
return null;
}
}
brightnessByColor(c) {
let color = '' + c,
isHEX = c.indexOf('#') == 0,
isRGB = c.indexOf('rgb') == 0;
let r, g, b;
if (isHEX) {
var m = color.substr(1).match(color.length == 7 ? /(\S{2})/g : /(\S{1})/g);
if (m) {
(r = parseInt(m[0], 16)), (g = parseInt(m[1], 16)), (b = parseInt(m[2], 16));
}
}
if (isRGB) {
const m = color.match(/^rgba\((\d+),\s*(\d+),\s*(\d+),(\d+)\)$/);
if (m) {
(r = m[1]), (g = m[2]), (b = m[3]);
}
}
if (typeof r != 'undefined') return (r * 299 + g * 587 + b * 114) / 1000;
};
getSelectedVariant() {
const product = this.getProductData();
return product.variants.find(item => item.id == this.variantId);
}
setSelectedOption(product) {
const selectedVariant = this.getSelectedVariant();
const productData = Object.assign({}, product);
// 处理下拉选项
productData?.options?.forEach(option => {
option.selectList = [];
option.values?.forEach(value => {
let selectStatus = '';
selectedVariant?.options?.forEach(item => {
if(item.name == option.name && item.value == value) {
selectStatus = 'checked';
}
})
option.selectList.push({name: option.name, value: value, checked: selectStatus});
})
})
return productData;
}
getSelectedSkuText() {
const selectVariant = this.getSelectedVariant();
if(!selectVariant) {
return '';
}
const selectList = selectVariant.options;
for (var i = 0; i < selectList.length; i++) {
selectList[i].value = selectVariant[`option${i + 1}`];
}
const selectedSkuText = selectList
.map( item => {
return item.value;
})
.join('/');
return selectedSkuText;
}
// 是否展示 加购弹窗
getIsButtonClickInfo() {
const product = this.getProductData();
const isMultipleProduct = product.available && !product.has_only_default_variant; // 多款式商品
const isButtonClickInfo = this.config.isMobile && (this.config.style_mobile === "mb_simple" || this.config.style_mobile === "mb_circle") && this.config.button_click_mobile === 'info' && isMultipleProduct;
return isButtonClickInfo;
}
// 是否跟随主题的加购数量 isFollowThemeQty (移动端 simple、circle模版、 PC端 simple模版加购数量从主题获取)
getIsFollowThemeQty() {
const followThemeQtyMobile = (this.config.style_mobile === "mb_simple" || this.config.style_mobile === "mb_circle") && this.config.isMobile;
const followThemeQtyPc = this.config.style === "simple" && !this.config.isMobile;
const isFollowThemeQty = (followThemeQtyPc || followThemeQtyMobile) ? true : false;
return isFollowThemeQty;
}
getBannerRenderData() {
const product = this.getProductData();
// 当前选中变体
const selectedVariant = this.getSelectedVariant();
// 变体options下拉列表数据处理
const productData = this.setSelectedOption(product);
// 选中的sku文案
const selectedSkuText = this.getSelectedSkuText();
// 是否展示 加购弹窗
const is_button_click_info = this.getIsButtonClickInfo();
// 主题是否有配置加购/购买按钮
const isHasAddCartBtn = this.getAddCartBtn() ? true : false;
// 加购数量是否跟随主题
const isFollowThemeQty = this.getIsFollowThemeQty();
// 浅色背景颜色配置
const brightness = this.brightnessByColor(this.config.module_bg);
const isDarkBg = brightness < 115;
const renderData = {
product: {
...productData,
config: {...this.config},
selectedVariant,
selectedSkuText,
},
config: this.config,
qty: this.qty,
variant: selectedVariant,
selectedVariant,
selectedSkuText,
is_button_click_info,
isDarkBg,
isHasAddCartBtn,
isFollowThemeQty,
i18n: this.i18n,
hasGlobalAtcListener: this.hasGlobalAtcListener
};
return renderData;
}
// render add_to_cart
async renderAll() {
const renderData = this.getBannerRenderData();
this.triggerEvent_('renderBanner', renderData);
}
getConfigData() {
return fetch(`/api/add-to-cart-config`).then(res => {
return res.json();
});
}
async getConfig() {
if(!this.config.hasOwnProperty('open_status')) {
const res = await this.getConfigData();
if (res.state === 0 && res.data.open_status) {
// config数据转化
this.transConfigData(res.data);
}
}
return this.config;
}
transConfigData(data) {
const resConfig = data;
this.config = {
...this.config,
...resConfig,
isMobile: window.innerWidth < 768 ? true : false,
position: window.innerWidth < 768 ? 'down' : resConfig.display_position
};
// 颜色跟随系统时
if(resConfig.color_setting === 'default' || resConfig.color_setting === 'theme') {
this.config = {
...this.config,
...this.defaultColorConfig
}
}
}
// 是否应用到商店
async handleIsRender() {
const res = await this.getConfigData();
if (res.state === 0 && res.data.open_status) {
// config数据转化
this.transConfigData(res.data);
this.handleObserver();
// 渲染加购内容
this.renderAddToCart();
}
}
renderAddToCart() {
const product = this.getProductData();
if (product.product_type === 'gift_card') return ;
if (!product.available) return;
this.renderAll();
}
// 选择变体 上报
trackVariant() {
const product = this.getProductData();
window.sa &&
sa.track('plugin_atc_variant_click', {
product_id: product.id,
style: this.config.style,
style_mobile: this.config.style_mobile
});
}
// 更改数量上报
trackQty() {
const product = this.getProductData();
window.sa &&
sa.track('plugin_atc_qty_click', {
product_id: product.id,
style: this.config.style,
style_mobile: this.config.style_mobile
});
}
// 加购上报
trackAddtoCart() {
const product = this.getProductData();
let properties = '';
let source = 'atc';
if (this.config.button_action === 'checkout') {
source = 'buy_now';
}
const obj = {
product_id: product.id,
style: this.config.style,
style_mobile: this.config.style_mobile,
button_action: this.config.button_action,
plugin_timestamp: this.plugin_timestamp
};
const options = {
product_id: product.id,
variant_id: this.variantId,
quantity: this.qty,
source,
variant: this.getSelectedVariant(),
product: product,
process: (window.SHOP_PARAMS.product_settings || {}).add_to_cart_process,
properties: properties
}
// 注册曝光参数到add_to_cart事件
window.spzutm && window.spzutm.registerParams('add_to_cart', { add_to_cart: JSON.stringify(obj) });
window.sa && sa.track('plugin_atc_button_click', obj);
this.trackHookAddTocart();
}
trackAtcView() {
const product = this.getProductData()
if(!this.isHasView) {
sa.track('plugin_atc_view', {
product_id: product.id,
button_action: this.config.button_action,
style: this.config.style,
style_mobile: this.config.style_mobile,
plugin_timestamp: this.plugin_timestamp
});
this.isHasView = true;
}
}
trackHookAddTocart(options) {
//FLASH及之后主题trigger事件上报采用HOOK
window.djInterceptors &&
window.djInterceptors.track &&
window.djInterceptors.track.use({
event: 'dj.addToCart',
params: {
id: options.product_id,
number: options.quantity,
childrenId: options.variant.id,
item_price: options.variant.price,
name: options.product.title,
type: options.variant.type ? options.variant.type : options.product.type,
properties: properties,
quantity: options.quantity,
variant_id: options.variant.id,
product_id: options.product_id,
source: 'atc',
process: options.process
},
once: true
});
}
// 加购弹窗
renderShopModal_() {
const renderData = this.getBannerRenderData();
this.triggerEvent_('showAddToCartModal', renderData);
}
showSuccessToast() {
this.triggerEvent_('showAddToCartToast');
}
updateSelectVariant = async(selectedVariant, product) => {
const configData = await this.getConfig();
const productData = this.setSelectedOption(product);
const selectedSkuText = this.getSelectedSkuText();
const renderData = {
...productData,
selectedVariant,
config: configData,
selectedSkuText
};
this.triggerEvent_('variantChange', renderData);
const renderBannerData = this.getBannerRenderData();
if(renderBannerData.is_button_click_info && this.config.style_mobile === "mb_circle") {
this.triggerEvent_('renderBannerClickInfo', renderBannerData);
}
}
getUrlVariantId() {
const urlParams = new URLSearchParams(window.location.search);
const variantId = urlParams.get('variant');
return variantId;
}
// 商详页变体切换
djVariantChange(detail) {
const product = this.getProductData();
// 是否为商详页当前商品
const isDetailProduct = product?.id === detail?.product?.id;
if(!detail || !isDetailProduct) {
return;
}
const selectVariant = detail.selected;
// 更新变体id
const variantId = selectVariant.id;
const oldVariantId = this.variantId;
if(variantId) {
this.triggerEvent_('renderSelectedVariant');
if(oldVariantId !== variantId || !this.isFirstUpdateVariant) {
this.isFirstUpdateVariant = true;
this.variantId = variantId;
this.updateSelectVariant(selectVariant, detail.product);
}
} else if(!variantId){
// 主题未选中变体
const isDisabledAtc = this.disabledAtc();
if(isDisabledAtc) {
this.triggerEvent_('renderBannerNoSelectedVariant', { product: detail.product });
}
}
}
// 不展示变体的模版
isHiddenVariantsTemplate() {
return ((this.config.style_mobile === 'mb_circle' || this.config.style_mobile === 'mb_simple') && this.config.isMobile) || (this.config.style === 'simple' && !this.config.isMobile);
}
// 主题未选中变体
isUnSelectedThemeVariants() {
// 不展示变体的模版
const formData = this.getThemeProductFormData();
return (formData && formData.hasOwnProperty('variant_id') && (!formData.variant_id || formData.variant_id === 'undefined'));
}
// 禁用加购
disabledAtc() {
// 不展示变体的模版
const isHiddenVariantsTemplate = this.isHiddenVariantsTemplate();
const isUnSelectedThemeVariant = this.isUnSelectedThemeVariants();
return isHiddenVariantsTemplate && isUnSelectedThemeVariant;
}
// 滚动至主题变体处
scrollToThemeVariant() {
const themeFormEl = this.getThemeProductInfoForm();
if(themeFormEl) {
this.triggerEvent_('showUnSelectedVariantTips');
setTimeout(() => {
themeFormEl.scrollIntoView({ behavior: 'smooth' });
}, 1000);
}
}
async setupAction_() {
// 上报处理
this.registerAction('saTrack', async(invocation) => {
const data = invocation.args;
// this.trackMap[data.type]?.();
})
// 更新variantId
this.registerAction('updateVriantId', async(invocation) => {
const data = invocation.args.data;
if(data.variant_id && data.variant_id != 'undefined') {
this.variantId = data.variant_id;
}
})
// 渲染弹窗加购&购买按钮
this.registerAction('renderShopModal', async(invocation) => {
this.renderShopModal_();
})
this.registerAction('bannerExtraRender', async(invocation) => {
const renderData = this.getBannerRenderData();
if(renderData.is_button_click_info && this.config.style_mobile === "mb_circle") {
this.triggerEvent_('renderBannerClickInfo', renderData);
}
})
// 加购数量同步
this.registerAction('handleAddToCart', async(invocation) => {
// 主题未选中变体
const isDisabledAtc = this.disabledAtc();
if(isDisabledAtc) {
this.scrollToThemeVariant();
return;
}
const quantity = this.getThemeQuantity();
if(this.config.button_action === "checkout" ) {
this.triggerEvent_('updateQty', { quantity: Number(quantity) || this.qty });
this.triggerEvent_('handleBuyNow');
} else {
this.triggerEvent_('updateQty', { quantity: Number(quantity) || this.qty });
this.triggerEvent_('handleAtc');
}
})
// 渲染弹窗加购&购买按钮
this.registerAction('handleFixedBanner', async(invocation) => {
// 固定定位时, 触发ljs-sticky 组件showSticky方法,展示banner
if(this.config.trigger_condition == 'fixed') {
this.triggerEvent_('showBanner');
// this.trackAtcView();
}
})
this.registerAction('handleVariantsRender', async(invocation) => {
const data = invocation.args.data;
// 主题未选中变体
const isDisabledAtc = this.disabledAtc();
if(isDisabledAtc) {
this.triggerEvent_('renderBannerNoSelectedVariant', { product: data });
return;
}
this.triggerEvent_('renderSelectedVariant', { product: data });
this.triggerEvent_('variantChange', data);
})
this.registerAction('handleShowErrorToast', async(invocation) => {
const data = invocation.args.data;
if(data?.message) {
this.triggerEvent_('showAtcErrorToast', 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.LOGIC;
}
}
SPZ.defineElement(TAG, SpzCustomAddToCart)
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(',')}
`
})
}()}
${function () {
const renderData = data.data;
const position_bottom = renderData.height;
const model_style = renderData.subwindow_style == "model1" ? "model-type-icon" : "model-type-icon-with-text";
const filterSubwindow = renderData.subwindow.filter(item =>
renderData.subwindow_style == "model1" && item.image || (renderData.subwindow_style == "model2" && (item.image || item.title))
);
const initFoldStyle = renderData.hover_display === 'foldable' && 'fold';
const initShowStyle = renderData.hover_mode != 'permanent' && 'app-floating-hidden';
const is_model_style_follow_theme = renderData.template_style == "follow_theme" && renderData.subwindow_style == "model2";
const is_model_style_custom = renderData.template_style == "custom" && renderData.subwindow_style == "model2"
let isShowApp = false;
let isCustomPage = false;
const current_page_url = 'https://www.elitesfashion.com/products/sexy-bandeau-slim-fit-lace-hem-mini-skirt';
const current_page_type = 'product';
if(renderData.effective_pages.type === 'specify' && renderData.effective_pages.specify_page.includes('custom') && renderData.effective_pages.custom_page.length > 0) {
renderData.effective_pages.custom_page.forEach(item => {
const custom_url = item?.split('?')[0];
if(current_page_url === decodeURI(custom_url)) {
isCustomPage = true;
return;
}
})
}
if(renderData.effective_pages.type === 'all' || (renderData.effective_pages.type === 'specify' && renderData.effective_pages.specify_page.includes(current_page_type)) || isCustomPage) {
isShowApp = true;
}
if(isShowApp && filterSubwindow.length > 0) {
return `
${function() {
if(renderData.hover_mode === "scroll") {
return `
`
} else if (renderData.hover_mode === "threshold_scroll") {
return `
`
} else {
return ``
}
}()}
${function() {
const [firstIem, ...restItems] = filterSubwindow
const item = firstIem;
return `${firstIem ? `
-
`: ''}
${restItems.length > 0 ? `
-
${restItems.map(item => `
`).join('')
}
`: ''
}`
}()}
`
} else {
return ``
}
}()}