440 lines
14 KiB
JavaScript
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));
|
|
});
|
|
} |