Files
wyczarujprezent.pl/modules/zakeke/views/js/designer.js
2024-10-28 22:14:22 +01:00

440 lines
14 KiB
JavaScript

/**
* Copyright (C) 2020 Futurenext srl
*
* This file is part of Zakeke.
*
* Zakeke Interactive Product Designer can not be copied and/or distributed
* without the express permission of Futurenext srl
*
* @author Futurenext srl <help@zakeke.com>
* @copyright 2020 Futurenext srl
* @license https://www.zakeke.com/privacy/#general_conditions
*/
function zakekeDesigner(config) {
function partition(items, key) {
return Object.values(items.reduce(
(result, item) => ({
...result,
[item[key]]: [
...(result[item[key]] || []),
item,
],
}),
{},
));
}
function uniqBy(a, key) {
let seen = {};
return a.filter(item => {
const k = key(item);
return seen.hasOwnProperty(k) ? false : (seen[k] = true);
});
}
function productAttributes() {
const formData = new FormData();
formData.append('id_product', config.params.id_product);
return fetch(config.attributesEndpoint, {
method: 'post',
body: formData,
}).then(res => res.json())
.then(res => {
const attributes = partition(res.combinations.flat(), 'id_attribute_group')
.map(options => uniqBy(options, option => option.id_attribute))
.map(options => ({
id: options[0].id_attribute_group,
label: options[0].group_name,
values: options.map(option => ({
id: option.id_attribute,
label: option.attribute_name
})
)
}), {});
const variants = res.combinations.map(options =>
options.map(option => ({
Id: option.id_attribute_group,
Value: {
Id: option.id_attribute
}
})));
return {
attributes, variants
};
});
}
function emitProductAttributes(promiseId, attributes) {
iframe.contentWindow.postMessage({
data: Object.assign({
promiseId
}, attributes),
zakekeMessageType: 2
}, '*');
}
function updatedParams(color, zakekeOptions) {
if (color == null) {
throw new Error('color param is null');
}
let params = Object.assign({}, config.params);
const colorObj = JSON.parse(color);
colorObj.forEach(function (val) {
if (!params['group']) {
params['group'] = {};
}
params['group'][val.Id] = val.Value.Id;
});
if (zakekeOptions != null) {
params = Object.assign({}, params, zakekeOptions);
}
return params;
}
function productPrice(promiseId, zakekeOptions) {
function emitProductPriceEvent(promiseId, productPrice) {
iframe.contentWindow.postMessage({
data: Object.assign({
promiseId
}, productPrice),
zakekeMessageType: 3
}, '*');
}
function processResponse(data) {
if (data.errors) {
return {
isOutOfStock: true
};
} else {
return {
isOutOfStock: !data.isInStock,
finalPrice: data.finalPrice
};
}
}
let params = zakekeOptions.params;
params['zakeke-percent-price'] = zakekeOptions['zakeke-percent-price'];
params['zakeke-price'] = zakekeOptions['zakeke-price'];
params['total_qty'] = zakekeOptions['total_qty'];
delete params['controller'];
const queryString = jQuery.param(params);
const cached = productDataCache[queryString];
if (cached !== undefined) {
emitProductPriceEvent(promiseId, cached);
return;
}
if (pendingProductDataRequests.indexOf(queryString) !== -1) {
return;
}
pendingProductDataRequests.push(queryString);
jQuery.ajax({
url: config.priceEndpoint,
type: 'POST',
headers: {
Accept: 'application/json'
},
data: params
})
.done(response => {
const responseData = processResponse(response);
productDataCache[queryString] = responseData;
emitProductPriceEvent(promiseId, responseData);
})
.fail(() => {
const productData = {
isOutOfStock: true
};
productDataCache[queryString] = productData;
emitProductPriceEvent(promiseId, productData);
})
.always(() => {
const index = pendingProductDataRequests.indexOf(queryString);
if (index !== -1) {
pendingProductDataRequests.splice(index, 1);
}
});
}
function productData(color, zakekeOptions) {
function emitProductDataEvent(productData) {
iframe.contentWindow.postMessage({
data: productData,
zakekeMessageType: 1
}, iframeSrc);
}
function processResponse(data) {
if (data.errors) {
return {
color,
isOutOfStock: true
};
} else {
return {
color,
isOutOfStock: !data.isInStock,
finalPrice: window.zakekePriceHideOnlyFrontend ? 0 : data.finalPrice
};
}
}
let params = updatedParams(color, zakekeOptions);
delete params['controller'];
const queryString = jQuery.param(params);
const cached = productDataCache[queryString];
if (cached !== undefined) {
emitProductDataEvent(cached);
return;
}
if (pendingProductDataRequests.indexOf(queryString) !== -1) {
return;
}
pendingProductDataRequests.push(queryString);
jQuery.ajax({
url: config.priceEndpoint,
type: 'POST',
headers: {
Accept: 'application/json'
},
data: params
})
.done(response => {
const responseData = processResponse(response);
productDataCache[queryString] = responseData;
emitProductDataEvent(responseData);
})
.fail(() => {
const productData = {
color,
isOutOfStock: true
};
productDataCache[queryString] = productData;
emitProductDataEvent(productData);
})
.always(() => {
const index = pendingProductDataRequests.indexOf(queryString);
if (index !== -1) {
pendingProductDataRequests.splice(index, 1);
}
});
}
function addToCartAjax(zakekeOptions) {
const params = zakekeOptions.params;
const form = document.getElementById('zakeke-addtocart');
delete params['controller'];
params['action'] = 'update';
params['add'] = '1';
return Promise.resolve(jQuery.ajax({
url: config.addEndpoint,
type: 'POST',
headers: {
Accept: 'application/json'
},
data: params
})).then(response => {
if (response.errors) {
console.error("Error: " + JSON.stringify(response));
return;
}
params['id_customization'] = response.id_customization;
delete params['fc'];
delete params['module'];
const formData = new FormData(form);
Object.keys(params).forEach(key => {
if (params[key] instanceof String || typeof (params[key]) !== 'object') {
formData.set(key, params[key])
} else {
Object.keys(params[key]).forEach(subKey => {
formData.set(key + '[' + subKey + ']', params[key][subKey])
});
}
});
return fetch(form.action, {
method: 'POST',
body: formData
});
});
}
function addToCart(color, design) {
let params = updatedParams(color, {
'zakeke_design': design
});
delete params['controller'];
params['action'] = 'update';
params['add'] = '1';
const form = document.getElementById('zakeke-addtocart');
jQuery.ajax({
url: config.addEndpoint,
type: 'POST',
headers: {
Accept: 'application/json'
},
data: params
}).done(response => {
if (response.errors) {
console.error('Error: ' + JSON.stringify(response));
return;
}
params['id_customization'] = response.id_customization;
delete params['fc'];
delete params['module'];
Object.keys(params).forEach(key => {
if (params[key] instanceof String || typeof (params[key]) !== 'object') {
const input = document.createElement('INPUT');
input.type = 'hidden';
input.name = key;
input.value = params[key];
form.appendChild(input);
} else {
Object.keys(params[key]).forEach(subKey => {
const input = document.createElement('INPUT');
input.type = 'hidden';
input.name = key + '[' + subKey + ']';
input.value = params[key][subKey];
form.appendChild(input);
});
}
});
jQuery(form).submit();
})
.fail((request, status) => {
console.error('Request addToCart failed: ' + status);
});
}
var productDataCache = {},
pendingProductDataRequests = [],
container = document.getElementById('zakeke-container'),
iframe = container.firstElementChild,
iframeSrc = null;
window.addEventListener('message', event => {
if (event.origin !== config.zakekeUrl) {
return;
}
if (event.data.zakekeMessageType === 0) {
if (config.params.remove_from_cart_url) {
jQuery.ajax(config.params.remove_from_cart_url).always(() => {
addToCart(event.data.colorId, event.data.designId);
});
} else {
addToCart(event.data.colorId, event.data.designId, event.data.modelId);
}
} else if (event.data.zakekeMessageType === 1) {
let zakekeOptions = {};
if (event.data.design.price !== undefined) {
zakekeOptions['zakeke-price'] = event.data.design.price;
}
if (event.data.design.percentPrice !== undefined) {
zakekeOptions['zakeke-percent-price'] = event.data.design.percentPrice;
}
productData(event.data.design.color, zakekeOptions);
} else if (event.data.zakekeMessageType === 2) {
productAttributes().then(attributes => emitProductAttributes(event.data.data.promiseId, attributes));
} else if (event.data.zakekeMessageType === 3) {
let zakekeOptions = {
params: Object.assign({}, config.params, {
group: event.data.data.attributes.reduce((acc, val) => {
acc[val.Id] = val.Value.Id;
return acc;
}, {}),
qty: parseInt(event.data.data.quantity)
})
};
if (event.data.data.price !== undefined) {
zakekeOptions['zakeke-price'] = event.data.data.price;
}
if (event.data.data.percentPrice !== undefined) {
zakekeOptions['zakeke-percent-price'] = event.data.data.percentPrice;
}
zakekeOptions['total_qty'] = event.data.data.totalQuantity;
productPrice(event.data.data.promiseId, zakekeOptions);
} else if (event.data.zakekeMessageType === 4) {
const totalQty = event.data.data.attributes.reduce((acc, val) => acc + val.quantity, 0);
function processAddToCartAjax(selections, i) {
if (selections.length <= i) {
window.location.assign(config.checkoutUrl);
return Promise.resolve();
}
const selection = selections[i];
const zakekeOptions = {
params: Object.assign({}, config.params, {
zakeke_design: event.data.data.designID,
group: selection.attributes.reduce((acc, val) => {
acc[val.Id] = val.Value.Id;
return acc;
}, {}),
qty: selection.quantity,
total_qty: totalQty
})
};
return addToCartAjax(zakekeOptions).then(() => {
return processAddToCartAjax(selections, i + 1);
});
}
return processAddToCartAjax(event.data.data.attributes, 0);
}
}, false);
if (window.matchMedia('(min-width: 768px)').matches) {
iframeSrc = config.customizerLargeUrl;
} else {
iframeSrc = config.customizerSmallUrl;
document.body.appendChild(container);
}
iframe.src = iframeSrc;
}
if (document.readyState === 'complete'
|| document.readyState === 'loaded'
|| document.readyState === 'interactive') {
zakekeDesigner(JSON.parse(window.zakekeDesignerConfig));
} else {
document.addEventListener('DOMContentLoaded', () => {
zakekeDesigner(JSON.parse(window.zakekeDesignerConfig));
});
}