COLORE
ANTRACITE/NERO OPACO
MISURA
S
MECHANICAL SPECIFICATIONS
Maggiori Informazioni
Promo
Sì
Prezzo Finale
524,25
×
Validate your login
Please complete your information below to login.
Password
Conferma password
Invia
Accedi
Clienti registrati
Email
Password
Login
Hai dimenticato la password?
Creare un nuovo account?
let socialPopupForm = {
"formLoginUrl":"",
"forgotFormUrl":""
};
var element = document.getElementById('social-login-popup');
if (element) {
var initData = element.getAttribute('data-mage-init');
var initObject = JSON.parse(initData);
socialPopupForm=initObject.socialPopupForm;
}
function appendLoading(blockSelector) {
var block = document.querySelector(blockSelector);
if (block) {
block.style.position = 'relative';
var loadingDiv = document.createElement('div');
loadingDiv.className = 'social-login-ajax-loading';
block.insertBefore(loadingDiv, block.firstChild);
}
}
function removeLoading(blockSelector) {
var block = document.querySelector(blockSelector);
if (block) {
block.style.position = '';
var loadingElement = block.querySelector('.' + 'social-login-ajax-loading');
if (loadingElement) {
loadingElement.remove();
}
}
}
function removeMsg(block, messageClass) {
var blockSelector = document.querySelector(block);
var messagesToRemove = blockSelector.querySelectorAll('.' + messageClass.replace(/ /g, '.'));
messagesToRemove.forEach(function(message) {
message.remove();
});
}
function _appendMessage (block, message, messageClass) {
var blockSelector = document.querySelector(block);
var currentMessage = null;
var messageSection = blockSelector.querySelectorAll("." + messageClass.replace(/ /g, '.'));
if (messageSection.length === 0) {
var divElement = document.createElement('div');
divElement.className = messageClass;
blockSelector.insertBefore(divElement, blockSelector.firstChild);
currentMessage = blockSelector.firstChild;
} else {
currentMessage = messageSection[0];
}
var messageDiv = document.createElement('div');
messageDiv.textContent = message;
currentMessage.appendChild(messageDiv);
}
function addMsg (block, response) {
var message = response.message,
messageClass = "";
if(response.hasOwnProperty("success")) {
messageClass = response.success ? "message-success success message inline-block" : "message-error error message inline-block";
}
if(response.hasOwnProperty("errors")) {
messageClass = !response.errors ? "message-success success message inline-block" : "message-error error message inline-block";
}
if (Array.isArray(message) && message.length > 0) {
message.forEach(
function (msg) {
_appendMessage(block, msg, messageClass);
}
);
} else if (typeof (message) === 'string') {
_appendMessage(block, message, messageClass);
}
}
function initCustomerLoginForm () {
return {
errors: 0,
hasCaptchaToken: 0,
showPassword: false,
displayErrorMessage: false,
errorMessages: [],
setErrorMessages (messages) {
this.errorMessages = [messages];
this.displayErrorMessage = this.errorMessages.length
},
submitForm () {
this.validate()
.then(() => {
// do not rename $form, the variable is the expected name in the recaptcha output
const $form = document.querySelector('#social-form-login');
if (this.errors === 0) {
// $form.submit();
removeMsg(".social-login.authentication .social-login-customer-authentication .block-content", "message-success success message");
removeMsg(".social-login.authentication .social-login-customer-authentication .block-content", "message-error error message");
this.dispatchLoginRequest($form);
}
})
.catch((invalid) => {
if (invalid.length > 0) {
invalid[0].focus();
}
});
},
dispatchLoginRequest: function (form) {
this.isLoading = true;
const username = this.$refs['customer-email'].value;
const password = this.$refs['customer-password'].value;
const formKey = hyva.getFormKey();
const bodyFields = {
'username': username,
'password': password,
'formKey': formKey
};
appendLoading('.social-login.authentication .social-login-customer-authentication .block-content');
fetch(socialPopupForm.formLoginUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify(bodyFields)
}
).then(response => {
return response.json();
}
).then(data => {
this.isLoading = false;
addMsg(".social-login.authentication .social-login-customer-authentication .block-content", data);
if (data.errors) {
} else {
window.location.reload();
}
}).catch(error => {
});
removeLoading('.social-login.authentication .social-login-customer-authentication .block-content');
}
}
}
function forgotBtn () {
document.querySelector('.social-login.authentication').style.display = 'none';
document.querySelector('.social-login.forgot').style.display = 'block';
document.querySelector('.social-login.create').style.display = 'none'; // Assuming you want to show the createFormContainer
document.querySelector('.social-login.fake-email').style.display = 'none';
}
function createBtn () {
document.querySelector('.social-login.authentication').style.display = 'none';
document.querySelector('.social-login.forgot').style.display = 'none';
document.querySelector('.social-login.create').style.display = 'block'; // Assuming you want to show the createFormContainer
document.querySelector('.social-login.fake-email').style.display = 'none';
}
function showLogin() {
document.querySelector('.social-login.authentication').style.display = 'block';
document.querySelector('.social-login.forgot').style.display = 'none';
document.querySelector('.social-login.create').style.display = 'none'; // Assuming you want to show the createFormContainer
document.querySelector('.social-login.fake-email').style.display = 'none';
}
document.addEventListener("DOMContentLoaded", function () {
// Your code here
function showLogin() {
document.querySelector('.social-login.authentication').style.display = 'block';
document.querySelector('.social-login.forgot').style.display = 'none';
document.querySelector('.social-login.create').style.display = 'none';
document.querySelector('.social-login.fake-email').style.display = 'none';
}
document.querySelector('#social-form-password-forget .action.back').addEventListener("click", showLogin);
document.querySelector('#social-form-create .action.back').addEventListener("click", showLogin);
});
Creare un nuovo account
Nome
Cognome
Email
Iscriviti alla Newsletter
Tax/VAT Number
Password
Conferma password
Crea un Account
Indietro
function initCustomerCreateForm () {
return {
errors: 0,
hasCaptchaToken: 0,
showPassword: false,
displayErrorMessage: false,
errorMessages: [],
setErrorMessages (messages) {
this.errorMessages = [messages];
this.displayErrorMessage = this.errorMessages.length
},
submitForm () {
this.validate()
.then(() => {
removeMsg(".social-login.create .block-content", "message-success success message");
removeMsg(".social-login.create .block-content", "message-error error message");
// do not rename $form, the variable is the expected name in the recaptcha output
if (this.errors === 0) {
this.dispatchCreateRequest();
}
})
.catch((invalid) => {
if (invalid.length > 0) {
invalid[0].focus();
}
});
},
dispatchCreateRequest: function () {
appendLoading(".social-login.create .block-content");
var createForm = document.querySelector('#social-form-create');
var formData = new FormData(createForm);
var parameters = new URLSearchParams(formData).toString();
fetch(socialPopupForm.createFormUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Requested-With': 'XMLHttpRequest'
},
body: parameters,
}
).then(response => {
return response.json();
}
).then(data => {
addMsg(".social-login.forgot .block-content", data);
if (data.redirect) {
window.location.href = data.redirect;
} else if (data.success) {
addMsg(".social-login.create .block-content", data);
if (data.url === '' || data.url == null) {
window.location.reload(true);
} else {
window.location.href = data.url;
}
} else {
addMsg(".social-login.create .block-content", data);
}
}).catch(error => {
});
removeLoading(".social-login.create .block-content")
}
}
}
Password dimenticata
Inserisci il tuo indirizzo email qui sotto per ricevere un link di reimpostazione della password.
Email
Invia
Indietro
let inputCaptcha = document.getElementById("captcha_user_forgotpassword");
inputCaptcha.setAttribute("data-validate", '{"required": true}');
document.addEventListener("DOMContentLoaded", function () {
function reloadCaptcha() {
let bodyFields = {
formId: "user_forgotpassword"
};
fetch('https://m245.net/test1/captcha/refresh/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify(bodyFields)
}
).then(response => {
return response.json();
}
).then(data => {
if (data.errors) {
} else {
document.querySelector(".captcha-img").src=data.imgSrc;
}
})
}
document.querySelector('.action.reload.captcha-reload').addEventListener("click", reloadCaptcha);
});
function initCustomerForgotForm () {
return {
errors: 0,
hasCaptchaToken: 0,
showPassword: false,
displayErrorMessage: false,
errorMessages: [],
setErrorMessages (messages) {
this.errorMessages = [messages];
this.displayErrorMessage = this.errorMessages.length
},
submitForm () {
this.validate()
.then(() => {
// do not rename $form, the variable is the expected name in the recaptcha output
removeMsg(".social-login.forgot .block-content", "message-success success message");
removeMsg(".social-login.forgot .block-content", "message-error error message");
if (this.errors === 0) {
this.dispatchForgotRequest();
}
})
.catch((invalid) => {
if (invalid.length > 0) {
invalid[0].focus();
}
});
},
dispatchForgotRequest: function () {
appendLoading('.social-login.forgot .block-content');
var createForm = document.querySelector('#social-form-password-forget');
var formData = new FormData(createForm);
var parameters = new URLSearchParams(formData).toString();
fetch(socialPopupForm.forgotFormUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Requested-With': 'XMLHttpRequest'
},
body: parameters
}
).then(response => {
return response.json();
}
).then(data => {
addMsg(".social-login.forgot .block-content", data);
if (data.errors) {
} else {
}
}).catch(error => {
});
removeLoading('.social-login.forgot .block-content')
}
}
}
Oppure accedi con
Registrati con Facebook
function getPopupParams(w, h, l, t) {
var screenX = typeof window.screenX !== 'undefined' ? window.screenX : window.screenLeft;
var screenY = typeof window.screenY !== 'undefined' ? window.screenY : window.screenTop;
var outerWidth = typeof window.outerWidth !== 'undefined' ? window.outerWidth : document.body.clientWidth;
var outerHeight = typeof window.outerHeight !== 'undefined' ? window.outerHeight : (document.body.clientHeight - 22);
var width = w ? w : 500;
var height = h ? h : 420;
var left = l ? l : parseInt(screenX + ((outerWidth - width) / 2), 10);
var top = t ? t : parseInt(screenY + ((outerHeight - height) / 2.5), 10);
return (
'width=' + width +
',height=' + height +
',left=' + left +
',top=' + top
);
}
function clickLoginSocial(login_url, label) {
var date = new Date();
var currentTime = date.getTime();
window.open(login_url + '?' + currentTime, label, getPopupParams());
}
window.socialAuthenticationPopup = {"facebook":{"label":"Facebook","login_url":"https:\/\/www.bici.it\/sociallogin\/social\/login\/type\/facebook\/","url":"https:\/\/www.bici.it\/sociallogin\/social\/login\/authen\/popup\/type\/facebook\/","key":"facebook","btn_key":"facebook"}};
Richiedi informazioni sul prodotto
I campi contrassegnati dall’asterisco * sono obbligatori
Scrivere il campo Check con questa scritta:
www.bici.it
Ho preso visione e accetto l'informativa sulla Privacy Policy *
INVIA LA DOMANDA
arrow
Questa form è protetta da reCAPTCHA - Viene applicata la Google Privacy Policy e Terms of Service.
function initContactForm() {
return {
errors: 0,
hasCaptchaToken: 0,
submitForm() {
// Do not rename $form, the variable is expected to be declared in the recaptcha output
const $form = document.querySelector('#contact');
if (!this.hasCaptchaToken) {
this.errors = 1;
const executeGreCaptcha = () => {
grecaptcha.ready(() => {
grecaptcha.execute(
'6LfWkZcpAAAAAINih9s_7PYvwsNsPMVStkJt9SCI',
{action: 'submit'}
).then((token) => {
const tokenElement = $form['g\u002Drecaptcha\u002Dresponse'];
if (tokenElement && token && token.length > 0) {
$form['g\u002Drecaptcha\u002Dresponse'].value = token;
this.errors = 0;
this.hasCaptchaToken = 1;
(this.submitForm || $form.submit).call(this);
} else {
this.errors = 1;
const error = tokenElement
? 'ReCaptcha\u0020validation\u0020failed,\u0020please\u0020try\u0020again'
: 'ReCaptcha\u0020token\u0020element\u0020is\u0020missing.\u0020Please\u0020refresh\u0020the\u0020page\u0020and\u0020trying\u0020again.';
if (this.setErrorMessages) {
this.setErrorMessages([error])
this.displayErrorMessage = true;
} else {
window.dispatchMessages && window.dispatchMessages([{type: "error", text: error}], 5000);
}
}
}).catch(exception => {
console.error(exception || `An unknown error occurred during ReCaptcha validation. Are the tokens and the domain correctly set up in the Google console?`)
});
})
}
if (window.grecaptcha) {
executeGreCaptcha();
} else {
const recaptchaUrl = "https://www.google.com/recaptcha/api.js?render=6LfWkZcpAAAAAINih9s_7PYvwsNsPMVStkJt9SCI";
const script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.setAttribute('src', recaptchaUrl);
document.head.appendChild(script);
script.addEventListener("load", executeGreCaptcha, false);
}
}
if (this.errors === 0) {
$form.submit();
}
}
}
}
Servizi Offerti
Vendita in sede e online
Servizio officina in sede
Assistenza tecnica
Bosch Service - Shimano Service - Mavic Service
Spedizioni e Consegne
Costo di spedizione in tutta Italia € 10.00
Spedizione GRATUITA per ordini superiori a € 149.00
Gestione ordini entro 48 ore lavorative
Spedizioni con corriere BRT
Orari Negozio
Vieni a trovarci:
Fratelli Sala - Via Visconti di Modrone 51 - 20846 Macherio (MB)
Lun-Ven: 8.30-12.30 / 14.00-19.00
Sab: 8.30-12.00 / 14.30-19.00
Orari Assistenza
Per qualsiasi informazione non esitare a contattarci:
Tel. +39 039.2013567
E-mail. info@bici.it
Lun-Ven: 09:00-12:00 / 14:30-18:00
Iscriviti alla Newsletter per ricevere
Novità
Sconti
Promozioni
Acconsento al trattamento dei dati per le finalità espresse nell'informativa sul trattamento dei dati personali per finalità di erogazione di servizi web e comunicazioni.
ISCRIVITI
arrow
exclamation-circle
function initNewsletterForm() {
return {
errors: 0,
hasCaptchaToken: 0,
displayErrorMessage: false,
errorMessages: [],
setErrorMessages(messages) {
this.errorMessages = [messages]
this.displayErrorMessage = this.errorMessages.length
},
submitForm() {
// Do not rename $form, the variable is expected to be declared in the recaptcha output
const $form = document.querySelector('#newsletter-validate-detail');
if (this.errors === 0) {
$form.submit();
}
}
}
}
Bici.it
arrow
Chi Siamo
arrow
Contatti
arrow
Pagamenti
arrow
Spedizioni
arrow
Resi & Garanzie
arrow
Apri una richiesta di reso
arrow
Privacy policy
arrow
Cookie policy
arrow
Aggiorna le preferenze sui cookie
arrow
Termini & Condizioni
Il tuo bici.it
arrow
Account
arrow
Tracking ordini
arrow
Tracking spedizioni
arrow
Newsletter
Categorie Negozio
arrow
Biciclette E Telai
arrow
Trasmissione
arrow
Ruote
arrow
Componenti
arrow
Copertoni e Camere
arrow
Gruppi
arrow
Accessori
arrow
Abbigliamento
arrow
Manutenzione
arrow
Indoor
Marchi
arrow
Atala
arrow
Bianchi
arrow
Shimano
arrow
Mavic
arrow
Fizik
arrow
Vittoria
arrow
Continental
arrow
Look
arrow
Smp
arrow
Bicisupport
arrow
Tutti i marchi
Rimani connesso con noi
social-facebook
social-instagram
Paga sicuro con PayPal
© 2026 - Tutti i diritti riservati. Fratelli Sala s.r.l. - Via Visconti di Modrone, 51, 20846 Macherio MB - P. IVA 00690280961
Sito realizzato da IOHO
'use strict';
function amBrandsPopup() {
let cached = false;
const displayError = (error) => {
console.error(error);
typeof window.dispatchMessages !== "undefined" && window.dispatchMessages(
[{
type: "error",
text: "Qualcosa\u0020\u00E8\u0020andato\u0020storto.\u0020Riprova\u0020di\u0020nuovo."
}], 10000
);
};
let letters = [
"A","B","C","D",
"E","F","G","H","I","J","K","L","M",
"N","O","P","Q","R","S","T","U","V","W","X","Y","Z","#"
];
return {
hoverPanelActiveId: null,
selectedLetter: null,
items: [],
letters: [],
showCount: null,
showFilter: null,
showImages: true,
showSearch: null,
loading: false,
label: 'Brands',
isPopupEnabled: false,
isTopMenuItemEnabled: false,
cache: false,
isMobile: false,
initErrorMessages(errors) {
const messages = [];
for (let error in Object.keys(errors)) {
messages.push({type: 'error', text: errors[error].message});
}
typeof window.dispatchMessages !== "undefined" && window.dispatchMessages(messages, 10000);
},
chooseLetter(letter) {
this.selectedLetter = this.selectedLetter === letter ? null : letter;
},
getQuery() {
return "query BrandsPopupQuery { ambrandlist(\n showCount: true\n displayZero: true\n ) {\n all_letters,\n items {\n alt\n brandId\n cnt\n image\n img\n label\n letter\n url\n }\n }\n storeConfig {\n amshopby_brand_general_brands_brands_popup,\n amshopby_brand_general_menu_item_label,\n amshopby_brand_general_brands_page,\n amshopby_brand_general_topmenu_enabled,\n amshopby_brand_general_brands_popup_config {\n show_images,\n display_zero,\n filter_display_all,\n image_width,\n image_height,\n show_filter,\n show_count\n }\n } }" },
/**
* Retrieve media url for given path
*
* @param path
* @returns {string}
*/
getMediaUrl(path) {
let stringToRemove = 'index.php/';
let baseUrl = BASE_URL.replace(stringToRemove, '');
return path.includes(baseUrl) ? path : baseUrl + path;
},
checkIsMobileResolution() {
this.isMobile = window.matchMedia('(max-width: 1023px)').matches;
},
getBrandUrl(url){
return BASE_URL + url;
},
init() {
this.checkIsMobileResolution();
this.$watch('hoverPanelActiveId', (value) => {
if (value === 'am-brand') {
this.sendRequest();
}
});
},
/**
* Get brands by letter
*
* @param result
* @returns {{imageWidth: *, isTopMenuItemEnabled: *, showCount: *, displayAll: *, showFilter: *, showImages: *, isPopupEnabled: *, label: *, imageHeight: *, filterDisplayAll: *, brandsPage: *}}
*/
settingsMapper(result) {
const config = result.data.storeConfig?.amshopby_brand_general_brands_popup_config;
return {
showImages: config.show_images,
showCount: config.show_count,
showFilter: config.show_filter,
imageWidth: config.image_width,
imageHeight: config.image_height,
filterDisplayAll: config.filter_display_all,
displayAll: config.display_zero,
isPopupEnabled: result.data.storeConfig?.amshopby_brand_general_brands_brands_popup,
label: result.data.storeConfig?.amshopby_brand_general_menu_item_label,
isTopMenuItemEnabled: result.data.storeConfig?.amshopby_brand_general_topmenu_enabled,
brandsPage: result.data.storeConfig?.amshopby_brand_general_brands_page,
}
},
/**
* Send request to graphql
*
* @returns {Promise}
*/
async sendRequest() {
if (this.isMobile) {
return false;
}
this.loading = true;
try {
if (cached) {
this.loading = false;
return false;
}
const response = await fetch('https://www.bici.it/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Store': 'default'
},
credentials: 'include',
body: JSON.stringify({query: this.getQuery(), variables: {}})
});
const result = await response.json();
if (result?.errors) {
this.initErrorMessages(result.errors);
return;
}
const data = result.data?.ambrandlist;
const mapper = this.settingsMapper(result);
// Update component settings
Object.keys(mapper).forEach(config => {
this[config] = mapper[config];
});
if (!this.filterDisplayAll) {
letters = data.all_letters.split(',');
}
const items = data.items.map(brand => {
brand.image = brand.img
? this.getMediaUrl(brand.img) : (brand.image ? this.getMediaUrl(brand.image) : null);
return brand;
});
this.letters = letters.map(letter => ({
letter,
brands: this.getLetterItems(items, letter),
}));
this.items = this.letters.filter(row => row.brands?.length > 0);
cached = true;
} catch (error) {
displayError(error);
} finally {
this.loading = false;
}
},
/**
* Get brands by letter
*
* @param items
* @param letter
* @returns {*}
*/
getLetterItems(items, letter) {
return items.filter(row => row.letter === letter);
},
convertHtml(text) {
let textField = document.createElement("textarea");
textField.innerHTML = text;
return textField.value;
}
}
}
Telaio
Alu formed tubes 27,5", internal cables, brake calliper on C/S
Forcella
Suntour XCM RL 27,5" 100mm with remote lockout
Serie sterzo
FSA No.57BP-1 1"1/8
Guarnitura
Shimano Deore FC-M4100-2 2 Pieces, 36/26, BSA
{
"*": {
"Magento_Ui/js/core/app": {"components":{"widget-pagodil-product-view-85":{"component":"IPlusService_XPay\/js\/view\/product\/product","displayArea":"after_price","config":{"template":"IPlusService_XPay\/product\/view"},"options":{"product_id":"85","product_type_id":"simple","product_final_price":526.77,"product_final_price_in_cent":"52677","is_widget_visible":false,"is_product_installable":true,"pagodil_min_amount":0,"pagodil_max_amount":10000000000,"pagodil_installments_number":null,"pagodil_logo_kind":"logo_1","pagodil_info_link":"https:\/\/www.pagodil.it\/e-commerce\/come-funziona\/","pagodil_language":"it"}}}} }
}
@keyframes newspaperAnimation {
0% {
opacity: 0;
transform: scale(0) rotate(360deg);
}
100% {
opacity: 1;
transform: scale(1) rotate(0deg);
}
}
.mfp-newspaper-appear {
animation: newspaperAnimation 0.5s ease-out forwards;
}
@keyframes newspaperAnimationOut {
0% {
opacity: 1;
transform: scale(1) rotate(0deg);
}
100% {
opacity: 0;
transform: scale(0) rotate(360deg);
}
}
.mfp-newspaper-disappear {
animation: newspaperAnimationOut 0.5s ease-out forwards;
}
@keyframes zoomInAnimation {
0% {
opacity: 0;
transform: scale(0);
}
100% {
opacity: 1;
transform: scale(1);
}
}
.mfp-zoom-in-appear {
animation: zoomInAnimation 0.5s ease-out forwards;
}
@keyframes zoomInAnimationOut {
0% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(0);
}
}
.mfp-zoom-in-disappear {
animation: zoomInAnimationOut 0.5s ease-out forwards;
}
@keyframes slideInLeftAnimation {
0% {
opacity: 0;
transform: translateX(-10%);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
.mfp-move-horizontal-appear {
animation: slideInLeftAnimation 0.5s ease-out forwards;
}
@keyframes slideInLeftAnimationOut {
0% {
opacity: 1;
transform: translateX(0);
}
100% {
opacity: 0;
transform: translateX(20%);
}
}
.mfp-move-horizontal-disappear {
animation: slideInLeftAnimationOut 0.5s ease-out forwards;
}
@keyframes fadeInDownAnimation {
0% {
opacity: 0;
transform: translateY(-20%);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.mfp-move-from-top-appear {
animation: fadeInDownAnimation 0.5s ease-out forwards;
}
@keyframes fadeInDownAnimationOut {
0% {
opacity: 1;
transform: translateY(0);
}
100% {
opacity: 0;
transform: translateY(20%);
}
}
.mfp-move-from-top-disappear {
animation: fadeInDownAnimationOut 0.5s ease-out forwards;
}
@keyframes flipInYAnimation {
0% {
opacity: 0;
transform: perspective(400px) rotateY(-90deg);
}
100% {
opacity: 1;
transform: perspective(400px) rotateY(0);
}
}
.mfp-3d-unfold-appear {
animation: flipInYAnimation 0.5s ease-out forwards;
}
@keyframes flipInYAnimationOut {
0% {
opacity: 1;
transform: perspective(400px) rotateY(0);
}
100% {
opacity: 0;
transform: perspective(400px) rotateY(90deg);
}
}
.mfp-3d-unfold-disappear {
animation: flipInYAnimationOut 0.5s ease-out forwards;
}
@keyframes zoomOutAnimation {
0% {
opacity: 0;
transform: scale(2);
}
100% {
opacity: 1;
transform: scale(1);
}
}
.mfp-zoom-out-appear {
animation: zoomOutAnimation 0.5s ease-out forwards;
}
@keyframes zoomOutAnimationOut {
0% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(2);
}
}
.mfp-zoom-out-disappear {
animation: zoomOutAnimationOut 0.5s ease-out forwards;
}
let popup = ``;
function handleHideModalClickOutSide() {
let popup_test = document.getElementById("popup_test");
let test = document.getElementById("test");
test.style.display = 'block';
popup_test.append(test);
popup_test.onclick;
popup_test.addEventListener('click', function (e) {
if (e.target === test) {
const modal = document.querySelector('.wrap-modal-login');
modal.classList.remove('mfp-move-from-top-appear');
modal.classList.add('mfp-move-from-top-disappear');
setTimeout(()=>{
modal.classList.remove('mfp-move-from-top-disappear');
hideMyDialog();
},300);
}
});
};
handleHideModalClickOutSide();
function closeModal () {
const modal = document.querySelector('.wrap-modal-login');
modal.classList.remove('mfp-move-from-top-appear');
modal.classList.add('mfp-move-from-top-disappear');
setTimeout(()=>{
modal.classList.remove('mfp-move-from-top-disappear');
hideMyDialog();
},300);
};
window.dataLayer = window.dataLayer || [];
(function () {
/**
* @var {[Object]}
*/
const dataToPush = JSON.parse('\u005B\u005D');
dataToPush.forEach((dataObject) => {
window.dataLayer.push(dataObject);
});
})();
(_ => {
'use strict';
const SECTION_KEY = 'amga4_hyva_event_pool';
const MAGE_CACHE_SECTION_KEY = 'mage-cache-storage';
const storage = hyva.getBrowserStorage();
window.addEventListener('private-content-loaded', ({ detail }) => {
if (!detail?.data) {
return;
}
const data = detail?.data[SECTION_KEY];
if (!data) {
return;
}
let eventsPool = data?.events || [];
if (eventsPool.length) {
eventsPool.forEach(event => {
window.dataLayer.push({ecommerce: null});
window.dataLayer.push(event);
});
setTimeout(() => {
storage.setItem(
MAGE_CACHE_SECTION_KEY,
JSON.stringify({
...detail.data,
[SECTION_KEY]: {}
})
);
}, 0);
}
});
})();
(() => {
function src_default(Alpine) {
Alpine.directive("intersect", Alpine.skipDuringClone((el, { value, expression, modifiers }, { evaluateLater, cleanup }) => {
let evaluate = evaluateLater(expression);
let options = {
rootMargin: getRootMargin(modifiers),
threshold: getThreshold(modifiers)
};
let observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting === (value === "leave"))
return;
evaluate();
modifiers.includes("once") && observer.disconnect();
});
}, options);
observer.observe(el);
cleanup(() => {
observer.disconnect();
});
}));
}
function getThreshold(modifiers) {
if (modifiers.includes("full"))
return 0.99;
if (modifiers.includes("half"))
return 0.5;
if (!modifiers.includes("threshold"))
return 0;
let threshold = modifiers[modifiers.indexOf("threshold") + 1];
if (threshold === "100")
return 1;
if (threshold === "0")
return 0;
return Number(`.${threshold}`);
}
function getLengthValue(rawValue) {
let match = rawValue.match(/^(-?[0-9]+)(px|%)?$/);
return match ? match[1] + (match[2] || "px") : void 0;
}
function getRootMargin(modifiers) {
const key = "margin";
const fallback = "0px 0px 0px 0px";
const index = modifiers.indexOf(key);
if (index === -1)
return fallback;
let values = [];
for (let i = 1; i < 5; i++) {
values.push(getLengthValue(modifiers[index + i] || ""));
}
values = values.filter((v) => v !== void 0);
return values.length ? values.join(" ").trim() : fallback;
}
document.addEventListener("alpine:init", () => {
window.Alpine.plugin(src_default);
});
})();
for (const [selector, deferUntil] of Object.entries({".product-slider section[x-data]":"intersect",".product-info [x-data]":"intersect","#filters-content [x-data]":"intersect","#review_form":"intersect","section[x-data^=initRecentlyViewedProductsComponent]":"intersect","div[x-data^=initBundleOptions]":"intersect","#product_addtocart_form [x-data]":"intersect","#notice-cookie-block":"idle"})) {
document.querySelectorAll(selector).forEach(el => el.setAttribute('x-defer', `${deferUntil}`));
}
(function () {
"use strict";
const hasAlpine = new Promise(resolve => {
window.addEventListener('alpine:initialized', resolve, {once: true, passive: true});
});
const hasInteract = new Promise(resolve => {
(events => {
const onInteract = () => {
resolve();
events.forEach(type => window.removeEventListener(type, onInteract));
}
events.forEach(type => window.addEventListener(type, onInteract, {once: true, passive: true}))
})(['touchstart', 'mouseover', 'wheel', 'scroll', 'keydown'])
});
const onIntersect = (el) => {
return new Promise(resolve => {
const observer = new IntersectionObserver(entries => {
for (const entry of entries) {
if (entry.isIntersecting) {
observer.disconnect()
resolve();
}
}
}, {});
observer.observe(el);
});
}
function runComponent(el) {
hasAlpine.then(() => {
el.removeAttribute('x-ignore');
queueMicrotask(() => Alpine.initTree(el));
});
}
function initDeferredComponents() {
document.querySelectorAll('[x-data][x-defer]').forEach(el => {
el.setAttribute('x-ignore', '');
const deferUntil = (el.getAttribute('x-defer') || '').trim();
switch (deferUntil) {
case 'interact':
hasInteract.then(() => runComponent(el));
break;
case 'intersect':
onIntersect(el).then(() => runComponent(el))
break;
case 'idle':
window.requestIdleCallback
? window.requestIdleCallback(() => runComponent(el), {timeout: 4000})
: setTimeout(() => runComponent(el), 4000);
break;
case 'eager':
runComponent(el);
break;
default:
if (deferUntil.startsWith('event:') && deferUntil.length > 6) {
window.addEventListener(deferUntil.substring(6), () => runComponent(el), {once: true, passive: true});
}
}
});
}
window.addEventListener('alpine:init', initDeferredComponents, {once: true, passive: true});
})()
// x-teleport implementation
(_ => {
document.addEventListener('DOMContentLoaded', event => {
// x-teleport available only for template
if (!Alpine.version.startsWith('2')) {
return;
}
const teleportDirective = (el, expression) => {
let target = document.querySelector(expression)
let clone = el.content.cloneNode(true).firstElementChild;
target.appendChild(clone);
setTimeout(() => {
target.querySelectorAll('[x-data]')
.forEach(el => Alpine.initializeComponent(el));
}, 0)
};
document.querySelectorAll('template').forEach(el => {
if (!el.hasAttribute('x-teleport')) {
return;
}
teleportDirective(el, el.getAttribute('x-teleport'));
});
document.querySelectorAll('[x-data]').forEach(el => {
if (el.hasAttribute('x-init')) {
return;
}
setTimeout(() => {
Alpine.initializeComponent(el);
}, 0);
});
});
})()
'use strict';
function dispatchMessages(messages, hideAfter) {
const messagesEvent = new CustomEvent("messages-loaded", {
detail: {
messages: messages,
hideAfter: hideAfter
}
});
window.dispatchEvent(messagesEvent);
}
if (typeof hyva === 'undefined' || (!hyva.getBrowserStorage || !hyva.getCookie || !hyva.setCookie)) {
console.warn("Hyvä helpers are not loaded yet. Make sure they are included before this script");
}
(function( hyva, undefined ) {
hyva.initFormKey = () => {
const inputSelector = 'input[name="form_key"]',
formKey = hyva.getFormKey();
Array.from(document.querySelectorAll(inputSelector)).map(function (input) {
input.value = formKey
});
}
hyva.initMessages = () => {
try {
const messages = hyva.getCookie('mage-messages');
window.mageMessages = messages ? JSON.parse(decodeURIComponent(messages).replace(/\+/g, ' ')) : [];
dispatchMessages(window.mageMessages);
// empty `mage-messages` cookie
const skipSetDomain = true;
hyva.setCookie('mage-messages','', -1, skipSetDomain);
} catch (error) {
console.warn('Error parsing Cookie Messages:', error);
}
}
window.addEventListener('DOMContentLoaded', hyva.initFormKey);
hyva.alpineInitialized(hyva.initMessages)
}( window.hyva = window.hyva || {} ));
'use strict';
{
const private_content_key = 'mage-cache-storage';
const private_content_expire_key = 'mage-cache-timeout';
const private_content_version_key = 'private_content_version';
const section_data_ids_key = 'section_data_ids';
const mage_cache_session_id_key = 'mage-cache-sessid';
const last_visited_store_key = 'last_visited_store';
const ttl = 3600;
if (typeof hyva === 'undefined' || (!hyva.getBrowserStorage || !hyva.getCookie || !hyva.setCookie)) {
console.warn("Hyvä helpers are not loaded yet. Make sure they are included before this script");
}
function loadSectionData () {
const browserStorage = hyva.getBrowserStorage();
if (!browserStorage) {
typeof window.dispatchMessages !== "undefined" && window.dispatchMessages(
[{
type: "warning",
text: "Please enable LocalStorage in your browser."
}]
);
return;
}
try {
let isInvalid = false;
if (hyva.getCookie(last_visited_store_key) !== CURRENT_STORE_CODE) {
isInvalid = true;
}
hyva.setCookie(last_visited_store_key, CURRENT_STORE_CODE, false, false);
if (!hyva.getCookie(mage_cache_session_id_key)) {
isInvalid = true;
browserStorage.removeItem(private_content_key);
const skipSetDomain = true;
const days = false;
hyva.setCookie(mage_cache_session_id_key, true, days, skipSetDomain)
}
const cookieVersion = hyva.getCookie(private_content_version_key);
const storageVersion = browserStorage.getItem(private_content_version_key);
if (cookieVersion && !storageVersion || cookieVersion !== storageVersion) {
isInvalid = true;
}
const privateContentExpires = browserStorage.getItem(private_content_expire_key);
if (privateContentExpires && new Date(privateContentExpires) < new Date()) {
browserStorage.removeItem(private_content_key);
}
if (isInvalid && cookieVersion) {
fetchPrivateContent([]);
} else if (cookieVersion && storageVersion && cookieVersion === storageVersion) {
const privateContent = JSON.parse(browserStorage.getItem(private_content_key));
if (
privateContent &&
privateContentExpires &&
privateContent.cart &&
privateContent.customer
) {
dispatchPrivateContent(privateContent);
} else {
fetchPrivateContent([]);
}
} else {
if (document.getElementById('default-section-data')) {
const privateContent = JSON.parse(document.getElementById('default-section-data').innerText.trim());
dispatchPrivateContent(privateContent);
} else {
dispatchPrivateContent({});
}
}
} catch (error) {
console.warn('Error retrieving Private Content:', error);
}
}
hyva.alpineInitialized(loadSectionData)
window.addEventListener('reload-customer-section-data', loadSectionData);
function dispatchPrivateContent(data) {
const privateContentEvent = new CustomEvent("private-content-loaded", {
detail: {
data: data
}
});
window.dispatchEvent(privateContentEvent);
}
function fetchPrivateContent(sections) {
fetch(`${BASE_URL}customer/section/load/?sections=${encodeURIComponent(sections.join(','))}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => response.ok && response.json())
.then(
data => {
if (data) {
try {
const browserStorage = hyva.getBrowserStorage();
// merge new data preserving non-invalidated sections
const oldSectionData = JSON.parse(browserStorage.getItem(private_content_key) || '{}') || {};
if ((! data.cart || ! data.cart.cartId) && oldSectionData['checkout-data']) {
delete oldSectionData['checkout-data'];
}
const newSectionData = Object.assign(oldSectionData, data);
dispatchPrivateContent(newSectionData);
// don't persist messages, they've been dispatched already
if (newSectionData.messages && newSectionData.messages.messages ) {
newSectionData.messages.messages = [];
}
browserStorage.setItem(private_content_key, JSON.stringify(newSectionData));
const expiresAt = new Date(Date.now() + (ttl * 1000)).toISOString();
browserStorage.setItem(private_content_expire_key, expiresAt);
const newCookieVersion = hyva.getCookie(private_content_version_key);
browserStorage.setItem(private_content_version_key, newCookieVersion);
// We don't need the section_data_ids in Hyvä, but we store them for compatibility
// with Luma Fallback. Otherwise, not all sections are loaded in Luma Checkout
hyva.setCookie(
section_data_ids_key,
JSON.stringify(
Object.keys(data).reduce((sectionDataIds, sectionKey) => {
sectionDataIds[sectionKey] = data[sectionKey]['data_id'];
return sectionDataIds;
}, {})
),
false,
true
);
} catch (error) {
console.warn("Couldn't store privateContent", error);
}
}
}
);
}
}
(() => {
document.addEventListener('submit', event => event.target.action = event.target.action.replace('%25uenc%25', hyva.getUenc()));
})()
(events => {
const dispatchUserInteractionEvent = () => {
events.forEach(type => window.removeEventListener(type, dispatchUserInteractionEvent))
window.dispatchEvent(new Event('init-external-scripts'))
};
events.forEach(type => window.addEventListener(type, dispatchUserInteractionEvent, {once: true, passive: true}))
})(['touchstart', 'mouseover', 'wheel', 'scroll', 'keydown'])
function initConfigurableOptions(productId, optionConfig) {
function findPathParam(key) {
const baseUrl = (BASE_URL.substring(0, 2) === '//' ? 'http:' : '') + BASE_URL;
const baseUrlParts = (new URL(baseUrl)).pathname.replace(/\/$/, '').split('/');
const pathParts = window.location.pathname.split('/').slice(baseUrlParts.length + 3);
for (let i = 0; i < pathParts.length; i += 2) {
if (pathParts[i] === key && pathParts.length > i) {
return pathParts[i + 1];
}
}
}
return {
optionConfig,
productId,
itemId: (new URLSearchParams(window.location.search)).get('id') || findPathParam('id'),
allowedAttributeOptions: [],
selectedValues: [],
init() {
this.findAllowedAttributeOptions();
this.$nextTick(() => {
if (typeof this.optionConfig.defaultValues === 'object') {
for (const [attributeId, value] of Object.entries(this.optionConfig.defaultValues)) {
this.changeOption(attributeId, value + '');
}
}
this.preselectQuerystringItems();
this.preselectLocationHashItems();
});
},
productIndex: 0,
productIndexForPrice: 0,
optionIsActive(attributeId, optionId) {
return !!this.getAllowedAttributeOptions(attributeId).find(
option => option.id === optionId
)
},
optionIsEnabled(attributeId, optionId) {
for (const productId in this.optionConfig.index) {
if (this.optionConfig.index[productId][attributeId] === optionId) {
return true;
}
}
return false;
},
findSimpleIndex() {
this.productIndex = this.calculateSimpleIndexForPartialSelection(this.selectedValues);
this.productIndexForPrice = this.findCheapestProductForPartialSelection(this.selectedValues);
},
calculateSimpleIndexForPartialSelection(selectedValues) {
if (selectedValues.length === 0) return 0;
return this.findProductIdsForPartialSelection(selectedValues)[0];
},
calculateSimpleIndexForFullSelection(selectedValues) {
if (! this.isFullSelection(selectedValues)) return false;
const productIndexes = this.optionConfig.index;
return Object.keys(productIndexes).find(productIndex => {
const productCandidateOptions = productIndexes[productIndex];
for (const productOption in productCandidateOptions) {
if (
!selectedValues[productOption] ||
selectedValues[productOption] !== productCandidateOptions[productOption]
) {
return false;
}
}
return productIndex;
});
},
findAllowedAttributeOptions() {
this.allowedAttributeOptions = this.calculateAllowedAttributeOptions(this.selectedValues);
},
calculateAllowedAttributeOptions(selectedValues) {
const allAttributes = this.optionConfig.attributes;
const allAttributesSorted = Object.values(allAttributes).sort((a,b) => {
return a.position - b.position
});
const newAllowedAttributeOptions = [];
allAttributesSorted.forEach(attribute => {
const selectionWithoutAttr = Object.assign({}, this.removeAttrFromSelection(selectedValues, attribute.id));
const availableIndexes = this.calculateAvailableProductIndexes(selectionWithoutAttr);
newAllowedAttributeOptions[attribute.id] = allAttributes[attribute.id].options.filter(option => {
return !!option.products.find(product => {
return availableIndexes.includes(product);
})
});
});
return newAllowedAttributeOptions;
},
calculateAvailableProductIndexes(selectedOptions) {
if (Object.keys(selectedOptions).length === 0) {
if (Object.values(this.optionConfig.salable || {}).length) {
return [].concat.apply([], [].concat.apply([], Object.values(this.optionConfig.salable).map(Object.values))).filter((x, i, a) => a.indexOf(x) === i)
}
return Object.keys(this.optionConfig.index);
}
const selectedIds = Object.keys(selectedOptions);
if (Object.values(this.optionConfig.salable || {}).length) {
const selectedOptionIndexes = selectedIds.map(attrId => {
const optionValue = selectedOptions[attrId];
return this.optionConfig.salable[attrId] && this.optionConfig.salable[attrId][optionValue] || []
})
return selectedOptionIndexes.reduce((acc, optionIndexes) => {
return acc.filter(index => optionIndexes.includes(index));
});
} else {
const productIndexes = this.optionConfig.index;
return Object.keys(productIndexes).filter(index => {
for (const attrId of selectedIds) {
if (productIndexes[index][attrId] !== `${selectedOptions[attrId]}`) return false
}
return true
});
}
},
findAttributeByOptionId(optionId) {
for (const attributeId in this.optionConfig.attributes) {
const attributeOptions = this.optionConfig.attributes[attributeId].options || [];
if (attributeOptions.find(option => option.id === optionId)) {
return attributeId;
}
}
},
getAllowedAttributeOptions(attributeId) {
return this.allowedAttributeOptions[attributeId] || []
},
getAllAttributeOptions(attributeId) {
return (
this.optionConfig.attributes[attributeId] &&
this.optionConfig.attributes[attributeId].options
) || []
},
getProductIdsForOption(option) {
const attributeId = this.findAttributeByOptionId(option.id);
const allOptions = this.optionConfig.attributes[attributeId];
const opt = (allOptions && allOptions.options || []).find(o => o.id === option.id);
return opt && opt.products
? opt.products
: [];
},
findProductIdsForPartialSelection(optionSelection) {
const candidateProducts = Object.values(optionSelection).reduce((candidates, optionId) => {
const newCandidates = this.getProductIdsForOption({id: optionId});
return candidates === null
? newCandidates
: candidates.filter(productId => newCandidates.includes(productId));
}, null);
return candidateProducts || [];
},
findCheapestProductForPartialSelection(optionSelection) {
const candidateProducts = this.findProductIdsForPartialSelection(optionSelection);
return candidateProducts.reduce((cheapest, simpleIdx) => {
// in the first iteration we start with simpleIdx as the currently cheapest product
if (! this.optionConfig.optionPrices[cheapest]) return simpleIdx;
const knownCheapestPrice = this.optionConfig.optionPrices[cheapest].finalPrice.amount;
return knownCheapestPrice > this.optionConfig.optionPrices[simpleIdx].finalPrice.amount
? simpleIdx
: cheapest;
}, 0)
},
findProductIdToUseForOptionPrice(option) {
// try to find a product for a complete selection
const attributeId = this.findAttributeByOptionId(option.id);
const optionSelection = Object.assign({}, this.selectedValues, {[attributeId]: option.id});
const matchingSimpleIndex = this.calculateSimpleIndexForFullSelection(optionSelection);
// if there is no complete selection, use the cheapest product for the option
return matchingSimpleIndex || this.findCheapestProductForPartialSelection(optionSelection);
},
getAttributeOptionLabel(option) {
const optionProduct = this.findProductIdToUseForOptionPrice(option);
if ((! optionProduct) || (optionProduct === this.productIndexForPrice)) {
return option.label;
}
const currentPrice = this.getOptionPriceAdjustmentBasePrice();
if (this.optionConfig.optionPrices[optionProduct]) {
const optionPrice = this.optionConfig.optionPrices[optionProduct].finalPrice.amount;
if (optionPrice !== currentPrice){
return option.label + ' ' + hyva.formatPrice(optionPrice - currentPrice, true);
}
}
return option.label;
},
getOptionPriceAdjustmentBasePrice() {
if (this.optionConfig.optionPrices[this.productIndexForPrice]) {
return this.optionConfig.optionPrices[this.productIndexForPrice].finalPrice.amount
}
return this.optionConfig.prices.finalPrice.amount; // default price if no option selection
},
clearOptionIfActive(optionId, value) {
if (this.selectedValues[optionId] === value) {
this.blurLabel()
this.changeOption(optionId, '')
}
},
removeAttrFromSelection(selectedValues, attributeId) {
attributeId = parseInt(attributeId);
return selectedValues.reduce((newSelection, val, attr) => {
if (attr !== attributeId) {
newSelection[attr] = val;
}
return newSelection;
}, []);
},
changeOption(attributeId, value) {
if (value === '') {
this.selectedValues = this.removeAttrFromSelection(this.selectedValues, attributeId)
} else if (value && this.getAllowedAttributeOptions(attributeId).find(option => option.id === value)) {
this.selectedValues[attributeId] = value;
}
this.findSimpleIndex();
this.findAllowedAttributeOptions();
this.updatePrices();
this.updateGallery();
const candidates = this.findProductIdsForPartialSelection(this.selectedValues);
window.dispatchEvent(
new CustomEvent(
'configurable-selection-changed',
{
detail: {
productId: this.productId,
optionId: attributeId,
value: value,
productIndex: this.productIndex,
selectedValues: this.selectedValues,
candidates: candidates,
skuCandidates: Object.values(candidates).map(id => this.optionConfig.sku[id]),
}
}
)
);
},
calculateIsMinimalPrice() {
return ! this.isFullSelection(this.selectedValues);
},
isFullSelection(selectedValues) {
return Object.values(selectedValues).length === Object.keys(this.optionConfig.attributes).length;
},
updatePrices() {
const value = this.optionConfig.optionPrices[this.productIndexForPrice] || this.optionConfig.prices;
window.dispatchEvent(
new CustomEvent(
"update-prices-" + this.productId,
{
detail: Object.assign(
value,
{ isMinimalPrice: this.calculateIsMinimalPrice() }
)
}
)
);
},
updateGallery () {
if (this.productIndex) {
const images = this.optionConfig.images[this.productIndex];
images && window.dispatchEvent(new CustomEvent(
"update-gallery",
{ detail: this.sortImagesByPosition(images) }
));
} else {
window.dispatchEvent(new Event("reset-gallery"));
}
},
sortImagesByPosition(images) {
return images.sort((x, y) => {
return x.position === y.position ? 0 : (parseInt(x.position) > parseInt(y.position) ? 1 : -1)
});
},
onGetCartData(data) {
if (this.productId === '85') {
this.preselectCartItems(data);
}
},
preselectCartItems(data) {
// pre-select options based on cart data for current (quote) itemId
const cart = data && data.cart;
if (cart && cart.items) {
const cartItem = cart.items.find((item) => {
return (
item.item_id === this.itemId
&& item.product_id === this.productId
)
});
if (cartItem && cartItem.options && cartItem.options.length) {
cartItem.options.map(option => {
this.changeOption(option.option_id, option.option_value);
})
}
}
},
preselectQuerystringItems() {
// pre-select option like ?size=167
const urlQueryParams = new URLSearchParams(window.location.search.replace('?',''));
this.preselectItemsBasedOnLocation(attribute => urlQueryParams.get(attribute.code));
},
preselectLocationHashItems() {
// pre-select option like #144=167
const urlHashParams = new URLSearchParams(window.location.hash.replace('#',''));
this.preselectItemsBasedOnLocation(attribute => urlHashParams.get(attribute.id));
},
preselectItemsBasedOnLocation(getLocationValue) {
Object.values(this.optionConfig.attributes).map(attribute => {
const v = getLocationValue(attribute);
v && this.changeOption(attribute.id, v)
});
}
}
}
function initSwatchOptions(swatchConfig) {
return {
swatchConfig,
getAttributeSwatchData(attributeId) {
const swatchConfig = Object.assign({}, this.swatchConfig[attributeId]);
swatchConfig['details'] = JSON.parse(swatchConfig['additional_data']);
return swatchConfig;
},
mapSwatchTypeNumberToTypeCode(typeNumber) {
switch ("" + typeNumber) {
case "1":
return "color"
case "2":
return "image"
case "3":
return "empty"
case "0":
default:
return "text"
}
},
getTypeOfFirstOption(attributeId) {
for (const optionId in this.swatchConfig[attributeId]) {
const option = this.swatchConfig[attributeId][optionId];
if (typeof option.type !== 'undefined') {
return this.mapSwatchTypeNumberToTypeCode(option.type);
}
}
},
getVisualSwatchType(attributeId, targetOptionId) {
// If a type configuration is present for the given option id, use it
const config = this.swatchConfig[attributeId];
if (config[targetOptionId] && typeof config[targetOptionId].type !== 'undefined') {
return this.mapSwatchTypeNumberToTypeCode(config[targetOptionId].type);
}
// Otherwise - if no config is present for the target option - use the type of the first option
// with a type property from the attribute, thus assuming its the same type as the target option.
// (This edge case condition can occur on single swatch products if some options are not salable)
return this.getTypeOfFirstOption(attributeId);
},
getSwatchType(attributeId, optionId) {
// Deserialize the attribute details the first time they are used
if (this.swatchConfig[attributeId] && ! this.swatchConfig[attributeId].details) {
this.swatchConfig[attributeId] = this.getAttributeSwatchData(attributeId);
}
const type = this.swatchConfig[attributeId] &&
this.swatchConfig[attributeId].details &&
this.swatchConfig[attributeId].details.swatch_input_type ||
"empty";
return type === 'visual' ? this.getVisualSwatchType(attributeId, optionId) : type;
},
isTextSwatch(attributeId, optionId) {
return this.getSwatchType(attributeId, optionId) === 'text';
},
isVisualSwatch(attributeId, optionId) {
const type = this.getSwatchType(attributeId, optionId);
return ['image', 'color'].includes(type);
},
getSwatchBackgroundStyle(attributeId, optionId) {
const config = this.getSwatchConfig(attributeId, optionId);
const type = this.getSwatchType(attributeId, optionId);
if (type === "color") {
return 'background-color:' + config.value;
} else if (type === "image") {
return "background: #ffffff url('" + config.value + "') no-repeat center";
} else {
return '';
}
},
getSwatchText(attributeId, optionId) {
const config = this.getSwatchConfig(attributeId, optionId);
return config.label || config.value || this.getOptionLabelFromOptionConfig(attributeId, optionId);
},
getOptionLabelFromOptionConfig(attributeId, optionId) {
// Fallback if no value is present in swatchConfig data
// Reference issue https://gitlab.hyva.io/hyva-themes/magento2-default-theme/-/issues/190
const option = this.getAllAttributeOptions(attributeId).filter(option => option.id === optionId);
return option && option[0] && option[0].label ||'';
},
getSwatchConfig(attributeId, optionId) {
return this.swatchConfig[attributeId] && this.swatchConfig[attributeId][optionId]
? this.swatchConfig[attributeId][optionId]
: false;
},
activeTooltipItem: false,
tooltipPositionElement: false,
isTooltipVisible() {
return this.activeTooltipItem &&
this.getSwatchConfig(
this.activeTooltipItem.attribute,
this.activeTooltipItem.item
);
},
isFirstItemCol() {
return this.activeTooltipItem.index === 0;
},
getTooltipImageStyle(attributeId, optionId) {
const config = this.getSwatchConfig(attributeId, optionId);
const type = this.getSwatchType(attributeId, optionId);
if (type === "color") {
return 'background-color:' + config.value + '; width: 110px; height: 90px;';
} else if (type === "image") {
return "background: #ffffff url('" + config.thumb +
"') center center no-repeat; width: 110px; height: 90px;";
} else {
return 'display:none';
}
},
getTooltipPosition() {
return this.tooltipPositionElement ?
`top: ${this.tooltipPositionElement.offsetTop}px;` +
`left: ${
this.tooltipPositionElement.offsetLeft - (
this.tooltipPositionElement.closest('.snap') &&
this.tooltipPositionElement.closest('.snap').scrollLeft ||
0
)
}px;` : ''
},
getTooltipLabel() {
return this.getSwatchConfig(this.activeTooltipItem.attribute, this.activeTooltipItem.item).label
},
focusedLabel: false,
focusLabel(optionId) {
this.focusedLabel = optionId;
},
blurLabel() {
this.focusedLabel = false;
},
showSwatches: false,
initShowSwatchesIntersect() {
if ('IntersectionObserver' in window && !window.scrollY) {
let io = new IntersectionObserver(
entries => {
entries.map(entry => {
if (entry.isIntersecting) {
this.showSwatches = true;
io.unobserve(this.$root);
}
})
}
);
io.observe(this.$root);
} else {
this.showSwatches = true
}
}
}
}
'use strict';
const grecaptchaV2LoadCallbacks = [];
function executeRecaptchaLoadCallbacks () {
while (window.grecaptcha && grecaptchaV2LoadCallbacks.length > 0) {
const callback = grecaptchaV2LoadCallbacks.pop();
typeof callback === 'string' ? window[callback]() : callback();
}
}
(function() {
const loadRecaptchaScript = (event) => {
if (window.grecaptcha || !event.target.form) {
return;
}
const url = 'https://www.google.com/recaptcha/api.js?onload=executeRecaptchaLoadCallbacks';
const siteKey = '6LfWkZcpAAAAAINih9s_7PYvwsNsPMVStkJt9SCI';
const script = document.createElement('script');
script.type = 'text/javascript';
script.src = siteKey ? `${url}&render=${siteKey}` : `${url}&render=explicit`;
script.async = true;
document.head.appendChild(script);
script.addEventListener('load', () => {
Array.from(document.querySelectorAll('button[data-recaptcha-btn]:disabled')).forEach(btn => btn.removeAttribute('disabled'));
});
}
window.forceLoadRecaptchaScript = (form) => {
loadRecaptchaScript({target: {form: form || 'dummy'}});
executeRecaptchaLoadCallbacks();
};
document.body.addEventListener('input', loadRecaptchaScript, { once: true });
document.body.addEventListener('focus', loadRecaptchaScript, { once: true });
}());
(function () {
const reviewComponent = document.querySelector('[x-data="initReviewForm()"]');
reviewComponent?.setAttribute(
'x-init',
`$watch(
'errorMessages',
errorMessages => {
errorMessages?.length > 0 && window.dispatchEvent(new CustomEvent("reload-customer-section-data"))
}
);${reviewComponent.getAttribute('x-init') || ''}`
);
})();
'use strict';
function initParentContainerProcessor() {
const selectors = {
labelContainer: '.amasty-label-container',
galleryImg: '#gallery img:not(.amasty-label-image)'
}
return {
/**
* @param {AmlabelComponent} label
* @return {void}
*/
process: function (label) {
this.moveLabelToParent(label);
this.setParentStyles(label);
},
/**
* @param {AmlabelComponent} label
* @return {void}
*/
moveLabelToParent: function (label) {
const newParent = this.getNewParent(label);
if (!newParent) {
return;
}
const wrapperElement = newParent.querySelector(label.wrapperClass);
label.parent = newParent;
// if same wrapper already exists - move label there
if (wrapperElement && !wrapperElement.isSameNode(label.element)) {
this.insertLabelByPriority(label, wrapperElement);
} else {
newParent.prepend(label.element);
}
},
/**
* @param {AmlabelComponent} label
* @return {HTMLElement|null}
*/
getNewParent: function (label) {
// move label to container from settings
let newParent = label.element.closest(label.config.path)
|| this.findPreviousNodeExcludingScriptsAndDifferentLabels(label.element)?.querySelector(label.config.path)
|| label.parent.querySelector(label.config.path);
// Try to find default gallery container on product page if none of the config selectors found
if (!newParent && label.config.move !== 1 && label.config.mode === 'prod') {
newParent = document.querySelector(selectors.galleryImg)?.parentElement;
}
return newParent;
},
/**
* @param {HTMLElement} element
* @return {HTMLElement|null}
*/
findPreviousNodeExcludingScriptsAndDifferentLabels: function (element) {
const previousElement = element?.previousElementSibling;
if (!previousElement) {
return null;
}
if (previousElement?.tagName !== 'SCRIPT'
&& !previousElement?.className?.includes?.('amlabel-position-wrapper')
) {
return previousElement;
}
return this.findPreviousNodeExcludingScriptsAndDifferentLabels(previousElement);
},
/**
* @param {AmlabelComponent} label
* @param {HTMLElement} wrapperElement
* @return {void}
*/
insertLabelByPriority: function (label, wrapperElement) {
const labelContainers = wrapperElement.querySelectorAll(selectors.labelContainer);
const currentLabelOrder = label.labelContainer.dataset.amlabelOrder;
let insertAfterContainer = null;
// find the label behind which we want to insert current one by priority
labelContainers.forEach(labelContainer => {
if (labelContainer.dataset.amlabelOrder < currentLabelOrder) {
insertAfterContainer = labelContainer;
}
});
insertAfterContainer === null
? wrapperElement.prepend(label.labelContainer)
: insertAfterContainer.after(label.labelContainer);
label.element.remove();
label.element = wrapperElement;
},
/**
* @param {AmlabelComponent} label
* @return {void}
*/
setParentStyles: function (label) {
/* required for child position absolute */
!(label.parent.style.position === 'absolute') && (label.parent.style.position = 'relative');
!label.parent.style.width && label.parent.classList.add('w-fit-content');
!label.parent.style.marginLeft && label.parent.classList.add('ml-auto');
!label.parent.style.marginRight && label.parent.classList.add('mr-auto');
},
}
}
'use strict';
function initLabelItemProcessor() {
/**
* @typedef {Object} AmlabelComponent
*
* @property {HTMLElement} element Label wrapper element
* @property {HTMLElement} parent Element where label should be placed
* @property {boolean} initialized Is label initialized
* @property {string} wrapperClass Dynamic class for label wrapper to place multiple labels properly by position
* @property {HTMLElement} labelContainer Container for label text image and tooltip
* @property {Config} config Label Configuration
* @property {Object} parentContainerProcessor
* @property {Object} labelItemProcessor
*/
/**
* @typedef {Object} AmLabelConfig
*
* @property {0|1} alignment Labels Alignment config value (0 - Horizontal| 1 - Vertical)
* @property {number} label Label Id
* @property {number} margin Margin between labels config value
* @property {('cat'|'prod')} mode Label mode
* @property {number} move
* @property {number} order Label order
* @property {string} path The css selector to which label should place
* @property {string} position Label position
* @property {string} product Product Id
* @property {string} redirect_url
* @property {string} size Label size
* @property {{
* backgroundColor: string,
* color: string,
* content: string,
* status: 1|2|3
* }} tooltip
*/
return {
/**
* @param {AmlabelComponent} label
* @return {void}
*/
process: function(label) {
this.setLabelSize(label);
this.setLinkProps(label);
},
/**
* @param {AmlabelComponent} label
* @return {void}
*/
setLabelSize: function (label) {
if (!label.config.size) {
return;
}
label.labelContainer.style.width = label.parent.offsetWidth * label.config.size / 100 + 'px';
},
/**
*
* @param {Object} $event
* @param {HTMLElement} tooltipNode
*/
placeTooltip: function($event, tooltipNode) {
const labelNode = $event.target;
const labelNodeRect = labelNode.getBoundingClientRect();
tooltipNode.style.bottom = (window.innerHeight - labelNodeRect.top) + 'px';
tooltipNode.style.left = (labelNodeRect.left + labelNode.offsetWidth / 2 ) + 'px';
},
/**
* @param {AmlabelComponent} label
* @return {void}
*/
setLinkProps: function (label) {
!!label.config.redirect_url && label.labelContainer.classList.add('-link', 'cursor-pointer');
},
/**
* @param {Object} $event Click event
* @param {string} redirectUrl
*/
openLink: function($event, redirectUrl) {
if (redirectUrl.length === 0) {
return;
}
$event.preventDefault();
window.open(redirectUrl, '_blank');
}
}
}
(function () {
'use strict';
const observedEvent = 'amlabel-swatch-product-changed';
const selectors = {
swatchOptions: 'div[x-data^=initConfigurable][x-data*=Options]',
addToCartFormOnListing: 'form.product_addtocart_form',
galleryImage: '#gallery img:not(.amasty-label-image)',
labelWrapper: '.amlabel-position-wrapper'
}
/**
* @return {void}
*/
const injectLabelObserver = function () {
const swatches = document.querySelectorAll(selectors.swatchOptions);
swatches.forEach(swatch => {
const isListing = !!swatch.closest(selectors.addToCartFormOnListing);
const injectedDispatch = `$dispatch('${observedEvent}', `
+ `{productIndex: productIndex, element: $el, optionConfig: optionConfig, isListing: ${isListing ? 1 : 0}})`;
let mutatedInitAttribute = swatch.getAttribute('x-init');
mutatedInitAttribute += `; $watch('productIndex', (productIndex) => { ${injectedDispatch} });`;
swatch.setAttribute('x-init', mutatedInitAttribute);
});
}
/**
* @param {{
* productIndex: number,
* element: HTMLElement,
* optionConfig: Object,
* isListing: 0|1
* }} payload
* @return {void}
*/
const updateLabel = function ({detail: payload}) {
const {productIndex, element, optionConfig, isListing} = payload;
if (!productIndex) {
return;
}
const reloadUrl = optionConfig.label_reload;
const headers = {
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
};
const body = new URLSearchParams({
product_ids: productIndex,
in_product_list: isListing
});
const labelContainerSelector = isListing ? optionConfig.label_category : optionConfig.label_product;
fetch(`${reloadUrl}?${body.toString()}`, {headers: headers, method: 'GET'}).then((response) => {
if (response.ok) {
return response.json();
}
}).then((result) => {
const labelHtml = result.labels && result.labels[productIndex];
let labelContainer = findClosest(element, labelContainerSelector);
// Try to find default gallery container on product page if none of the config selectors found
if (labelContainer === null && isListing === 0) {
labelContainer = document.querySelector(selectors.galleryImage)?.parentElement;
}
if (!!labelContainer) {
!!labelHtml ? replaceLabels(labelContainer, labelHtml) : clearLabelWrapper(labelContainer);
}
}).catch((error) => {
typeof window.dispatchMessages !== "undefined" && window.dispatchMessages(
[{
type: 'error',
text: error
}],
5000
);
});
}
/**
* We use approach from hyva.replaceDomElement method to replace labels and init components
*
* @param {HTMLElement} labelContainer
* @param {string} labelHtml
* @return {void}
*/
function replaceLabels(labelContainer, labelHtml) {
const parser = new DOMParser();
const doc = parser.parseFromString(labelHtml, 'text/html');
const nodes = doc.querySelectorAll(selectors.labelWrapper);
if (!nodes) {
return;
}
clearLabelWrapper(labelContainer);
// Extract all the script tags from the new content.
// Script tags won't execute when inserted into a dom-element directly,
// therefore we need to inject them to the head of the document.
const tmpScripts = doc.getElementsByTagName('script');
if (tmpScripts.length > 0) {
// Push all script tags into an array
// (to prevent dom manipulation while iterating over dom nodes)
const scripts = [...tmpScripts];
// Iterate over all script tags and duplicate+inject each into the head
scripts.forEach(script => {
let scriptNode = document.createElement('script');
scriptNode.innerHTML = script.innerHTML;
document.head.appendChild(scriptNode);
// Remove the original (non-executing) node from the page
script.parentNode.removeChild(script);
});
}
labelContainer.append(...nodes);
}
/**
* @param {HTMLElement} labelContainer
* @return {void}
*/
function clearLabelWrapper(labelContainer) {
labelContainer.querySelectorAll(selectors.labelWrapper).forEach(wrapper => wrapper.remove());
}
/**
* @param {HTMLElement} currentElement
* @param {string} selector
* @return {HTMLElement|null}
*/
function findClosest(currentElement, selector) {
// We need to limit parent searches to prevent labels from being associated with the wrong product.
const maxDepth = 3;
let counter = 0;
let closest = null;
while (currentElement.parentNode && closest === null && counter < maxDepth) {
++counter;
currentElement = currentElement.parentNode;
closest = currentElement.querySelector(selector);
}
return closest;
}
injectLabelObserver();
window.addEventListener(observedEvent, updateLabel);
})();
(function () {
'use strict';
const selectors = {
body: '.catalog-product-view',
productItem: 'product-item',
productGalleryImage: '#gallery img'
}
window.addEventListener('alpine:init', () => {
Alpine.directive('amintersect', (el, { expression }, { evaluate, cleanup }) => {
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
evaluate(expression);
observer.disconnect();
}
})
}, { threshold: 0.5 });
observer.observe(getElement());
cleanup(() => {
observer.disconnect()
});
function getElement() {
return !!document.querySelector(selectors.body) ? getImageProductBlock(el) : getCategoryProductBlock(el);
}
function getCategoryProductBlock(el) {
if (el.previousElementSibling === null) {
return el;
}
if (!el.previousElementSibling.classList.contains(selectors.productItem)) {
return getCategoryProductBlock(el.previousElementSibling);
}
return el.previousElementSibling;
}
function getImageProductBlock(el) {
return el.parentNode.querySelector(selectors.productGalleryImage)?.parentNode ?? el.parentNode;
}
});
});
})();
'use strict';
(function () {
const modals = [];
const excludedFromFocusTrapping = new Set();
function trapFocusInNextModalWithOverlay() {
for (let idx = modals.length -1; idx >= 0; idx--) {
const nextOnStack = modals[idx];
const nextDialogElement = nextOnStack.instance.$refs[nextOnStack.name];
if (! isOverlayDisabled(nextDialogElement)) {
hyva.trapFocus(nextDialogElement);
break;
}
}
}
function focusables(dialogElement) {
const selector = 'button, [href], input, textarea, select, details, [tabindex]:not([tabindex="-1"])';
return Array.from(dialogElement.querySelectorAll(selector))
.filter(el => !el.hasAttribute('disabled'));
}
function firstVisible(elements) {
const a = Array.from(elements);
for (let i = 0; i < a.length; i++) {
if (a[i].offsetWidth || a[i].offsetHeight || a[i].getClientRects().length) return a[i];
}
return null;
}
function isInViewport(element) {
const rect = element && element.getBoundingClientRect();
return rect &&
rect.top >= 0 &&
rect.left >= 0 &&
rect.right <= window.innerWidth &&
rect.bottom <= window.innerHeight;
}
function setFocusAfterTransition(dialogElement, duration) {
const nested = Array.from(dialogElement.querySelectorAll('[role="dialog"]'));
const candidates = Array.from(dialogElement.querySelectorAll('[x-focus-first]'));
next: for (let candidate of candidates) {
for (let child of nested) {
if (child.contains(candidate)) continue next;
}
setTimeout(() => candidate.focus(), 50);
break;
}
window.setTimeout(() => {
const focusElement = firstVisible(dialogElement.querySelectorAll('[x-focus-first]')) ||
focusables(dialogElement)[0] ||
null;
focusElement && isInViewport(focusElement) && focusElement.focus();
}, Math.max(1, duration));
}
function determineTrigger($refs, dialog, trigger) {
if (typeof trigger === 'undefined' && typeof dialog === 'object' && dialog.target instanceof HTMLElement) {
return dialog.target;
}
if (typeof dialog === 'string' && typeof trigger === 'object' && trigger.target instanceof HTMLElement) {
return trigger.target;
}
if (typeof trigger === 'string') {
try {
return $refs[trigger] || document.querySelector(trigger)
} catch (e) {}
}
if (trigger instanceof Element) {
return trigger;
}
return null;
}
function isOverlayDisabled(dialog) {
return dialog && dialog.hasAttribute('x-no-overlay')
}
function areRemainingModalsWithoutOverlay(modals)
{
const overflowDisabled = modals.map(modal => modal.instance.$refs[modal.name]).filter(isOverlayDisabled);
return overflowDisabled.length === modals.length;
}
window.hyva.modal = function(options) {
const config = Object.assign({
dialog: 'dialog', duration: 300, transitionEnter: 'transition ease-out duration-300',
transitionEnterStart: 'opacity-0',
transitionEnterEnd: 'opacity-100',
transitionLeave: 'transition ease-in duration-300',
transitionLeaveStart: 'opacity-100',
transitionLeaveEnd: 'opacity-0',
}, options);
let lastHide = 0;
return {
opened: {},
show(dialog, trigger) {
const focusTargetAfterHide = determineTrigger(this.$refs, dialog, trigger);
const name = typeof dialog === 'string' ? dialog : config.dialog;
const dialogElement = this.$refs[name];
if (! dialogElement) {
return;
}
const useOverlay = ! dialogElement.hasAttribute('x-no-overlay');
dialogElement.scrollTop = 0;
if (this.opened[name]) {
return;
}
if (focusTargetAfterHide) {
focusTargetAfterHide.setAttribute('aria-expanded', 'true');
}
this.opened[name] = true;
useOverlay && this.$nextTick(() => hyva.trapFocus(dialogElement));
setFocusAfterTransition(dialogElement, config.duration);
const frame = {name, instance: this, focusTarget: focusTargetAfterHide, time: Date.now()};
modals.push(frame);
if (useOverlay) {
document.body.classList.add('overflow-hidden');
}
return new Promise(resolve => frame.resolve = resolve);
},
cancel() {
this.hide(false);
},
ok() {
this.hide(true);
},
hide(value) {
if (Date.now() - lastHide < config.duration) {
return;
}
lastHide = Date.now();
const modal = modals.pop() || {};
const name = modal.name;
this.opened[name] = false;
hyva.releaseFocus(modal.instance.$refs[modal.name])
trapFocusInNextModalWithOverlay();
const nextFocusAfterHide = modal.focusTarget;
nextFocusAfterHide && setTimeout(() => {
nextFocusAfterHide.setAttribute('aria-expanded', 'false');
nextFocusAfterHide.focus()
}, config.duration);
if (modals.length === 0 || areRemainingModalsWithoutOverlay(modals)) {
document.body.classList.remove('overflow-hidden');
}
modal.resolve(value);
},
overlay(dialog) {
const name = typeof dialog === 'string' ? dialog : config.dialog;
return {
['x-show']() {
return this.opened[name]
},
['x-transition:enter']: config.transitionEnter,
['x-transition:enter-start']: config.transitionEnterStart,
['x-transition:enter-end']: config.transitionEnterEnd,
['x-transition:leave']: config.transitionLeave,
['x-transition:leave-start']: config.transitionLeaveStart,
['x-transition:leave-end']: config.transitionLeaveEnd,
['@hyva-modal-show.window'](event) {
event.detail && event.detail.dialog === name && this.show(name, event.detail.focusAfterHide)
}
};
}
};
}
window.hyva.modal.peek = () => modals.length > 0 && modals[modals.length -1]
window.hyva.modal.pop = function () {
if (modals.length > 0) {
const modal = modals[modals.length -1];
modal.instance.hide();
}
}
window.hyva.modal.excludeSelectorsFromFocusTrap = function (selectors) {
typeof selectors === 'string' || selectors instanceof String
? excludedFromFocusTrapping.add(selectors)
: selectors.map(selector => excludedFromFocusTrapping.add(selector));
}
window.hyva.modal.eventListeners = {
keydown: event => {
if (event.key === 'Escape') {
window.hyva.modal.pop();
}
},
click: event => {
if (modals.length > 0) {
const modal = modals[modals.length -1];
const dialog = modal.instance.$refs[modal.name];
if (modal.time + 50 < Date.now() && // if last click processing is more than 50ms ago
! isOverlayDisabled(dialog) && // if dialog has overlay
! dialog.contains(event.target)) { // if click is outside of dialog
modal.instance.hide();
}
}
}
};
document.addEventListener('keydown', window.hyva.modal.eventListeners.keydown);
document.addEventListener('click', window.hyva.modal.eventListeners.click);
})();
{"messages":[],"customer":[],"compare-products":[],"last-ordered-items":[],"cart":[],"directory-data":{"IT":{"name":"Italia","regions":{"811":{"code":"AG","name":"Agrigento"},"812":{"code":"AL","name":"Alessandria"},"813":{"code":"AN","name":"Ancona"},"814":{"code":"AO","name":"Aosta"},"816":{"code":"AR","name":"Arezzo"},"817":{"code":"AP","name":"Ascoli-Piceno"},"818":{"code":"AT","name":"Asti"},"819":{"code":"AV","name":"Avellino"},"820":{"code":"BA","name":"Bari"},"821":{"code":"BT","name":"Barletta-Andria-Trani"},"822":{"code":"BL","name":"Belluno"},"823":{"code":"BN","name":"Benevento"},"824":{"code":"BG","name":"Bergamo"},"825":{"code":"BI","name":"Biella"},"826":{"code":"BO","name":"Bologna"},"827":{"code":"BZ","name":"Bolzano"},"828":{"code":"BS","name":"Brescia"},"829":{"code":"BR","name":"Brindisi"},"830":{"code":"CA","name":"Cagliari"},"831":{"code":"CL","name":"Caltanissetta"},"832":{"code":"CB","name":"Campobasso"},"833":{"code":"CI","name":"Carbonia Iglesias"},"834":{"code":"CE","name":"Caserta"},"835":{"code":"CT","name":"Catania"},"836":{"code":"CZ","name":"Catanzaro"},"837":{"code":"CH","name":"Chieti"},"838":{"code":"CO","name":"Como"},"839":{"code":"CS","name":"Cosenza"},"840":{"code":"CR","name":"Cremona"},"841":{"code":"KR","name":"Crotone"},"842":{"code":"CN","name":"Cuneo"},"843":{"code":"EN","name":"Enna"},"844":{"code":"FM","name":"Fermo"},"845":{"code":"FE","name":"Ferrara"},"846":{"code":"FI","name":"Firenze"},"847":{"code":"FG","name":"Foggia"},"848":{"code":"FC","name":"Forli-Cesena"},"849":{"code":"FR","name":"Frosinone"},"850":{"code":"GE","name":"Genova"},"851":{"code":"GO","name":"Gorizia"},"852":{"code":"GR","name":"Grosseto"},"853":{"code":"IM","name":"Imperia"},"854":{"code":"IS","name":"Isernia"},"815":{"code":"AQ","name":"L'Aquila"},"855":{"code":"SP","name":"La-Spezia"},"856":{"code":"LT","name":"Latina"},"857":{"code":"LE","name":"Lecce"},"858":{"code":"LC","name":"Lecco"},"859":{"code":"LI","name":"Livorno"},"860":{"code":"LO","name":"Lodi"},"861":{"code":"LU","name":"Lucca"},"862":{"code":"MC","name":"Macerata"},"863":{"code":"MN","name":"Mantova"},"864":{"code":"MS","name":"Massa-Carrara"},"865":{"code":"MT","name":"Matera"},"866":{"code":"VS","name":"Medio Campidano"},"867":{"code":"ME","name":"Messina"},"868":{"code":"MI","name":"Milano"},"869":{"code":"MO","name":"Modena"},"870":{"code":"MB","name":"Monza-Brianza"},"871":{"code":"NA","name":"Napoli"},"872":{"code":"NO","name":"Novara"},"873":{"code":"NU","name":"Nuoro"},"874":{"code":"OG","name":"Ogliastra"},"875":{"code":"OT","name":"Olbia Tempio"},"876":{"code":"OR","name":"Oristano"},"877":{"code":"PD","name":"Padova"},"878":{"code":"PA","name":"Palermo"},"879":{"code":"PR","name":"Parma"},"880":{"code":"PV","name":"Pavia"},"881":{"code":"PG","name":"Perugia"},"882":{"code":"PU","name":"Pesaro-Urbino"},"883":{"code":"PE","name":"Pescara"},"884":{"code":"PC","name":"Piacenza"},"885":{"code":"PI","name":"Pisa"},"886":{"code":"PT","name":"Pistoia"},"887":{"code":"PN","name":"Pordenone"},"888":{"code":"PZ","name":"Potenza"},"889":{"code":"PO","name":"Prato"},"890":{"code":"RG","name":"Ragusa"},"891":{"code":"RA","name":"Ravenna"},"892":{"code":"RC","name":"Reggio-Calabria"},"893":{"code":"RE","name":"Reggio-Emilia"},"894":{"code":"RI","name":"Rieti"},"895":{"code":"RN","name":"Rimini"},"896":{"code":"RM","name":"Roma"},"897":{"code":"RO","name":"Rovigo"},"898":{"code":"SA","name":"Salerno"},"899":{"code":"SS","name":"Sassari"},"900":{"code":"SV","name":"Savona"},"901":{"code":"SI","name":"Siena"},"902":{"code":"SR","name":"Siracusa"},"903":{"code":"SO","name":"Sondrio"},"904":{"code":"TA","name":"Taranto"},"905":{"code":"TE","name":"Teramo"},"906":{"code":"TR","name":"Terni"},"907":{"code":"TO","name":"Torino"},"908":{"code":"TP","name":"Trapani"},"909":{"code":"TN","name":"Trento"},"910":{"code":"TV","name":"Treviso"},"911":{"code":"TS","name":"Trieste"},"912":{"code":"UD","name":"Udine"},"913":{"code":"VA","name":"Varese"},"914":{"code":"VE","name":"Venezia"},"915":{"code":"VB","name":"Verbania"},"916":{"code":"VC","name":"Vercelli"},"917":{"code":"VR","name":"Verona"},"918":{"code":"VV","name":"Vibo-Valentia"},"919":{"code":"VI","name":"Vicenza"},"920":{"code":"VT","name":"Viterbo"}}},"data_id":1790584246},"captcha":[],"instant-purchase":[],"loggedAsCustomer":[],"persistent":[],"review":[],"payments":[],"wishlist":{"items":[]},"amga4_event_pool":[],"amga4_hyva_event_pool":[],"recently_viewed_product":[],"recently_compared_product":[],"product_data_storage":[],"paypal-billing-agreement":[]}
function cookie() {
return {
ajaxAddCookieUrl: 'https://www.bici.it/popup/ajax/addCookie/',
sendCountCookie: function () {
fetch(
this.ajaxAddCookieUrl,
{
method: 'get',
data: JSON.stringify({
isAjax: 'true',
form_key: "GfGRrjDRUO35wWWt",
cookie_type: 'aw_popup_viewed_page',
current_url: 'https://www.bici.it/catalog/product/view/id/85/'
}),
headers: {contentType: 'application/json'}
}
)
},
sendCookie: function (popupId, cookieType, cookieLifetime) {
fetch(`${this.ajaxAddCookieUrl}'?isAjax=true&cookie_type=${cookieType}&popup_id=${popupId}&cookie_lifetime=${cookieLifetime}`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
}
})
}
}
}
(()=> {
'use strict';
const amBrandPosition = {
selectors: {
brands: '#maincontent > .container .amshopby-option-link',
productInfoTitleP: '.product-info-main p.title-font'
},
amBrandPositionInit() {
const brands = document.querySelectorAll(this.selectors.brands);
let target;
if (!brands.length) {
return;
}
target = document.querySelector(this.selectors.productInfoTitleP);
for (let brand of brands) {
brand.classList.remove('flex');
brand.classList.add('inline-flex');
this.moveElement(brand, target);
}
},
moveElement(element, target) {
const fragment = document.createDocumentFragment();
fragment.appendChild(element);
target.append(fragment);
element.classList.remove('hidden');
}
}
document.addEventListener('DOMContentLoaded', () => {
amBrandPosition.amBrandPositionInit();
})
})()
function popupManager() {
return {
cookiePregValue: 'aw_popup_viewed_popup_',
_getCookie: function (cname) {
var name = cname + "=",
decodedCookie = decodeURIComponent(document.cookie),
ca = decodedCookie.split(';'),
i, c;
for (i = 0; i < ca.length; i++) {
c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return '';
},
/**
* event for leave browser popup
* see http://bradsknutson.com/blog/javascript-detect-mouse-leaving-browser-window/
*/
addEvent: function (obj, evt, fn) {
if (obj.addEventListener) {
obj.addEventListener(evt, fn, false);
} else if (obj.attachEvent) {
obj.attachEvent("on" + evt, fn);
}
},
popupSetClass: function (popup, value) {
popup.setAttribute('class', value);
}
}
}
(() => {
const originalModal = window.hyva.modal;
window.hyva.modal = function (options) {
const instance = originalModal(options);
const originalShow = instance.show;
const originalHide = instance.hide;
instance.show = function (value, event, key, cookieLifetime, equals, effect) {
if (popupManager()._getCookie(popupManager().cookiePregValue + key) !== '') {
return;
}
let popupElement = document.querySelector('.aheadworks_popup'+key);
effect = effect.trim();
let popupClasses = 'inline-block bg-white shadow-xl rounded-lg p-10 max-h-screen overflow-auto aw-'+effect+' aw-'+effect+'_in aheadworks_popup'+key
switch (event) {
case 'immediately_page_loaded':
cookie().sendCookie(key, 'aw_popup_viewed_popup', cookieLifetime, true);
originalShow.call(this, value);
setTimeout(function () {
window.dispatchEvent(new Event('resize'));
popupManager().popupSetClass(popupElement, popupClasses);
}, 100);
break;
case 'x_sec_after_duration':
if (equals) {
setTimeout(function () {
cookie().sendCookie(key, 'aw_popup_viewed_popup', cookieLifetime, true);
originalShow.call(this, value);
setTimeout(function () {
window.dispatchEvent(new Event('resize'));
popupManager().popupSetClass(popupElement, popupClasses);
}, 100);
}.bind(this), equals * 1000);
} else {
originalShow.call(this, value)
}
break;
case 'once_page_scrolled':
window.onscroll = function (ev) {
if (popupManager()._getCookie(popupManager().cookiePregValue + key) === '') {
var offset = window.scrollY || document.documentElement.scrollTop,
windowHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight,
progress = Math.floor(offset / windowHeight * 100);
if (progress >= equals) {
cookie().sendCookie(key, 'aw_popup_viewed_popup', cookieLifetime, true);
originalShow.call(this, value);
setTimeout(function () {
window.dispatchEvent(new Event('resize'));
popupManager().popupSetClass(popupElement, popupClasses);
}, 100);
}
}
}.bind(this);
break;
case 'once_customer_pages_viewed':
cookie().sendCookie(key, 'aw_popup_viewed_popup', cookieLifetime, true);
originalShow.call(this, value);
setTimeout(function () {
window.dispatchEvent(new Event('resize'));
popupManager().popupSetClass(popupElement, popupClasses);
}, 100);
break;
case 'once_cursor_leave_browser':
popupManager().addEvent(document, "mouseout", function (e) {
e = e ? e : window.event;
var from = e.relatedTarget || e.toElement;
if (!from || from.nodeName === "HTML") {
if (popupManager()._getCookie(popupManager().cookiePregValue + key) === '') {
cookie().sendCookie(key, 'aw_popup_viewed_popup', cookieLifetime, true);
originalShow.call(this, value);
setTimeout(function () {
window.dispatchEvent(new Event('resize'));
popupManager().popupSetClass(popupElement, popupClasses);
}, 100);
}
}
}.bind(this));
break;
}
}
instance.hide = function (value, effect, key) {
originalHide.call(this, value);
let popupElement = document.querySelector('.aheadworks_popup'+key),
popupClasses = 'inline-block bg-white shadow-xl rounded-lg p-10 max-h-screen overflow-auto aw-' + effect + ' aw-' + effect + '_out aheadworks_popup' + key;
popupManager().popupSetClass(popupElement, popupClasses);
}
return instance;
}
window.hyva.modal.eventListeners = originalModal.eventListeners;
window.hyva.modal.excludeSelectorsFromFocusTrap = originalModal.excludeSelectorsFromFocusTrap;
window.hyva.modal.pop = originalModal.pop;
})(),
(function () {
const originalListener = window.hyva.modal.eventListeners.click;
document.removeEventListener('click', originalListener);
window.hyva.modal.eventListeners.keydown = event => {
var path = event.path || (event.composedPath && event.composedPath());
if (path) {
path.forEach(function (element, k) {
var key;
if (!this.event.target.classList.contains('aw-close-popup')) {
if (path[k].id) {
if (path[k].id.includes('aheadworks_popup')) {
key = path[k].id.replace(/[^0-9]/g, "");
let sendStatistic = false;
path.forEach(function (elemin, keyin) {
if(elemin.tagName) {
if (elemin.tagName === 'A' || elemin.tagName === 'BUTTON') {
sendStatistic = true;
return;
}
}
})
if(sendStatistic) {
cookie().sendCookie(key, 'aw_popup_used_popup', 0, true);
}
}
}
}
})
}
originalListener(event);
};
document.addEventListener('click', window.hyva.modal.eventListeners.keydown);
})()