1/********************************************************************************* 2 3 Template Name: Sacré Français 2022 4 Template URI: https://SacreFrancais.fr 5 Description: Thème graphique du site Sacré Français - Sélectionneur d'épices 6 Author: La chose verte 7 Author URI: https://lachoseverte.com 8 Version: 2.0 9 10**********************************************************************************/ 11 12(function($) 13{ 14 'use strict'; 15 16//////////////////////////////////////////////////////////////////////////////// 17/////// /////// /////// /////// /////// /////// /////// /////// //////// 18//////////////////////////////////////////////////////////////////////////////// 19 20 jQuery.expr[':'].icontains = function(a, i, m) 21 { 22 return jQuery(a).text().toUpperCase().indexOf(m[3].toUpperCase()) >= 0; 23 }; 24 25//////////////////////////////////////////////////////////////////////////////// 26/////// /////// /////// /////// /////// /////// /////// /////// //////// 27//////////////////////////////////////////////////////////////////////////////// 28 29 $.fn.hasScrollBar = function() 30 { 31 return Math.round(this.get(0).scrollHeight) > Math.round(this.outerHeight()); 32 } 33 34//////////////////////////////////////////////////////////////////////////////// 35/////// /////// /////// /////// /////// /////// /////// /////// //////// 36//////////////////////////////////////////////////////////////////////////////// 37 38async function CheckCartBeforeCheckout() { 39 let updated = false; 40 const state = Snipcart.store.getState(); 41 const cart = state?.cart; 42 43 if (!cart?.items || !cart.items.items.length) return; 44 45 const Datas = []; 46 for (const item of cart.items.items) { 47 const packField = item.customFields?.find(f => f.name === 'Conditionnement'); 48 const weightField = item.customFields?.find(f => f.name === 'Poids'); 49 const pack = packField ? (packField.displayValue || packField.value) : null; 50 51 if (!pack) continue; 52 53 Datas.push({ 54 id: item.id, 55 pack: pack, 56 price: Number.parseFloat(item.price), 57 weight: Number.parseFloat(weightField?.value || 0), 58 uniqueId: item.uniqueId, 59 Quantity: item.quantity, 60 Name: item.name 61 }); 62 } 63 64 if (Datas.length === 0) return; 65 66 const removedNames = []; 67 68 $.ajax({ 69 type: 'POST', 70 url: RootPath() + '/Ajax.php', 71 dataType: 'json', 72 data: { 73 action: 'CheckCartProducts', 74 Datas: JSON.stringify(Datas) 75 }, 76 cache: false, 77 success: async function (json) { 78 if (!json || !json.results || !Array.isArray(json.results)) return; 79 80 for (const result of json.results) { 81 const Available = result.available; 82 const Status = result.status; 83 const UniqueId = result.uniqueId; 84 const Name = result.name; 85 const NewPrice = result.newPrice; 86 const NewWeight = result.newWeight; 87 88 // Normalise (évite NaN) 89 const priceNum = (NewPrice !== undefined && NewPrice !== null) ? Number(NewPrice) : undefined; 90 const weightNum = (NewWeight !== undefined && NewWeight !== null) ? Number(NewWeight) : undefined; 91 92 // --- Retrait --- 93 if (Status === 'remove' || Available === false) { 94 try { 95 await Snipcart.api.cart.items.remove(UniqueId); 96 removedNames.push(Name); 97 updated = true; 98 } catch (error) { 99 console.error('â Erreur Snipcart remove', UniqueId, error); 100 } 101 continue; 102 } 103 104 // --- Mise à jour --- 105 if (Status === 'update') { 106 try { 107 const current = Snipcart.store.getState()?.cart?.items?.items?.find(i => i.uniqueId === UniqueId); 108 if (!current) { 109 // Item plus présent (course/refresh) â on ignore cet item et on continue 110 continue; 111 } 112 113 // Clone & met à jour le champ "Poids" si fourni 114 let updatedFields = current.customFields || []; 115 if (Number.isFinite(weightNum)) { 116 updatedFields = updatedFields.map(f => 117 f.name === 'Poids' ? { ...f, value: weightNum } : f 118 ); 119 } 120 121 const updatePayload = { uniqueId: UniqueId }; 122 if (Number.isFinite(priceNum)) updatePayload.price = priceNum; 123 if (updatedFields) updatePayload.customFields = updatedFields; 124 125 await Snipcart.api.cart.items.update(updatePayload); 126 updated = true; 127 } catch (error) { 128 console.error(`â Erreur Snipcart update ${UniqueId} :`, error); 129 } 130 continue; 131 } 132 } 133 134 // âââ Message de retrait (si nécessaire) âââ 135 if (removedNames.length > 0) { 136 if (removedNames.length === 1) { 137 const safeName = $('<div>').text(removedNames[0]).html(); 138 ShowCartMessage( 139 `Lâarticle <strong>${safeName}</strong> a été retiré de votre panier car il nâest plus disponible.`, 140 'warning' 141 ); 142 } else { 143 const safeList = removedNames.map(n =>
143 $('<div>').text(n).html()).join(', '); 144 ShowCartMessage( 145 `Les articles <strong>${safeList}</strong> ont été retirés de votre panier car ils ne sont plus disponibles.`, 146 'warning' 147 ); 148 } 149 } 150 }, 151 error: function (xhr, status, error) { 152 console.error('â Erreur AJAX :', error, xhr?.responseText); 153 } 154 }); 155} 156 157/* 158 async function CheckCartBeforeCheckout() 159 { 160 let updated = false; 161 const cart = Snipcart.store.getState().cart; 162 if(!cart.items || !cart.items.items.length) 163 { 164 return; 165 } 166 let Datas = []; 167 const nameByUniqueId = new Map(); // <- pour reconstruire les noms après remove 168 for(const item of cart.items.items) 169 { 170 const productId = item.id; 171 const packField = item.customFields?.find(f => f.name === 'Conditionnement'); 172 const pack = packField ? (packField.displayValue || packField.value) : null; 173 174 if(!pack) continue; 175 176 Datas.push({ 177 id: productId, 178 pack: pack, 179 price: parseFloat(item.price), 180 weight: parseFloat(item.customFields?.find(f => f.name === 'Poids')?.value || 0), 181 uniqueId: item.uniqueId, 182 Quantity: item.quantity, 183 Name: item.name 184 }); 185 } 186// console.log(Datas); 187 if(Datas.length === 0) return; 188 const removedNames = []; // on accumule pour le message 189 $.ajax({ 190 type: 'POST', url: RootPath()+'/Ajax.php', dataType: 'json', 191 data: { 192 action: 'CheckCartProducts', 193 Datas: JSON.stringify(Datas) 194 }, 195 cache: false, 196 success: async function(json) 197 { 198 if(!json || !json.results) return; 199 for(const result of json.results) 200 { 201 const Available = result.available; 202 const Status = result.status; 203 const UniqueId = result.uniqueId; 204 const ProductName = result.name; 205 const NewPrice = result.newPrice; 206 const NewWeight = result.newWeight; 207 208 if(Status === 'remove' || Available === false) 209 { 210 try { 211 await Snipcart.api.cart.items.remove(UniqueId); 212// const name = nameByUniqueId.get(UniqueId) || 'Article supprimé'; 213 removedNames.push(ProductName); 214 } catch (error) { 215 console.log(error) 216 } 217 updated = true; 218 continue; 219 } 220 if(Status === 'update') 221 { 222// console.log({ Status, NewPrice, NewWeight, Available, UniqueId }); 223// console.log(Snipcart.store.getState().cart); 224 try { 225 // Récupère l'item complet dans le panier actuel 226 const cart = Snipcart.store.getState().cart; 227 const item = cart.items.items.find(i => i.uniqueId === UniqueId); 228 229 if(!item) { 230// console.warn(`Item ${UniqueId} introuvable dans le panier`); 231 return; 232 } 233 234 // Clone et modifie le champ "Poids" sans détruire les autres 235 const updatedFields = item.customFields.map(f => 236 f.name === 'Poids' 237 ? { ...f, value: NewWeight } // conserve toutes les propriétés originales (type, operation, etc.) 238 : f 239 ); 240 241 await Snipcart.api.cart.items.update({ 242 uniqueId: UniqueId, 243 price: NewPrice, 244 customFields: updatedFields 245 }); 246 247// console.log(`â Item ${UniqueId} mis à jour`, { NewPrice, NewWeight }); 248 249 } catch (error) { 250 console.error(`â Erreur Snipcart sur update ${UniqueId} :`, error); 251 } 252 updated = true; 253 } 254 } 255 // âââ Afficher le message si des articles ont été retirés âââ 256 if(removedNames.length > 0) 257 { 258 const list = removedNames 259 .map(n => `<li class="mb-0">- ${$('<div>').text(n).html()}</li>`) 260 .join(''); 261 262 let html = ''; 263 264 if (removedNames.length > 0) { 265 let html = ''; 266 267 if (removedNames.length === 1) { 268 html = `Lâarticle <strong>${$('<div>').text(removedNames[0]).html()}</strong> a été retiré de votre panier car il nâest plus disponible.`; 269 } else { 270 const names = removedNames.map(n => $('<div>').text(n).html()).join(', '); 271 html = `Les articles <strong>${names}</strong> ont été retirés de votre panier car ils ne sont plus disponibles.`; 272 } 273 274 ShowCartMessage(html, 'warning'); 275 } 276 } 277 }, 278 error: function (xhr, status, error) 279 { 280 console.error("â Erreur AJAX :", error, xhr.responseText); 281 } 282 }); 283 } 284*/ 285
286//////////////////////////////////////////////////////////////////////////////// 287/////// /////// /////// /////// /////// /////// /////// /////// //////// 288//////////////////////////////////////////////////////////////////////////////// 289 290function ShowCartMessage(html, type = 'warning') { 291 // Si un conteneur dédié existe, on lâutilise, sinon on ajoute en haut du body 292 const $host = $('#SnipcartMessages'); 293 const $container = $host.length ? $host : $('body'); 294 295 // Supprime une alerte précédente du même type pour éviter lâempilement 296 $container.find('.cart-alert').remove(); 297 298 const $alert = $(` 299 <div class="col-12 col-md-3 d-flex cart-alert alert alert-${type} Bg-Orange text-white position-fixed align-items-center fade show" 300 role="alert" 301 style="z-index: 1060; ${$host.length ? '' : 'top:5vh; left:50%; transform:translateX(-50%);'}"> 302 <div class="d-flex align-items-start gap-2"> 303 <i class="bi bi-exclamation-triangle-fill me-1"></i> 304 <div class="cart-alert-body">${html}</div> 305 </div> 306 <button type="button" class="h-100 btn-close text-white p-0 fs-3 ps-4" data-bs-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button> 307 </div> 308 `); 309 $container.append($alert); 310 311 // Fallback si Bootstrap n'est pas chargé : auto-hide 312 if (typeof bootstrap === 'undefined') { 313 setTimeout(() => $alert.fadeOut(200, () => $alert.remove()), 6000); 314 } 315} 316 317//////////////////////////////////////////////////////////////////////////////// 318/////// /////// /////// /////// /////// /////// /////// /////// //////// 319//////////////////////////////////////////////////////////////////////////////// 320 321 function RotateHomeSquares() { 322 const $main = $('#MainRotatingCard'); 323 const $cards = $('#SideRotatingCards .card'); 324 // Récupère l'URL du background-image 325 const getBgUrl = $el => { 326 const bg = $el.css('background-image'); 327 return bg.replace(/^url\(["']?/, '').replace(/["']?\)$/, ''); 328 }; 329 330 const setBgUrl = ($el, url) => { 331 $el.css('background-image', `url("${url}")`); 332 }; 333 334 // Stocke les données du main 335 const mainData = { 336 img: getBgUrl($main.find('.BgImg')), 337 title: $main.find('.card-title').html(), 338 desc: $main.find('.card-desc').html(), 339 link: $main.find('a.card-link').attr('href') 340 }; 341 342 // Récupère la dernière tuile 343 const $lastCard = $cards.last(); 344 const lastCardData = { 345 img: getBgUrl($lastCard.find('.BgImg')), 346 title: $lastCard.find('.card-title').html(), 347 desc: $lastCard.find('.card-desc').html(), 348 link: $lastCard.find('a.card-link').attr('href') 349 }; 350 351 // Décale les données des cartes 352 for(let i = $cards.length - 1; i > 0; i--) { 353 const $current = $cards.eq(i); 354 const $prev = $cards.eq(i - 1); 355 356 setBgUrl($current.find('.BgImg'), getBgUrl($prev.find('.BgImg'))); 357 $current.find('.card-title').html($prev.find('.card-title').html()); 358 $current.find('.card-desc').html($prev.find('.card-desc').html()); 359 360 const $link = $current.find('a.card-link'); 361 $link.attr('href', $prev.find('a.card-link').attr('href')); 362 adjustLinkAttributes($link); 363 } 364 365 // Le main devient la première carte 366 const $firstCard = $cards.first(); 367 setBgUrl($firstCard.find('.BgImg'), mainData.img); 368 $firstCard.find('.card-title').html(mainData.title); 369 $firstCard.find('.card-desc').html(mainData.desc); 370 const $firstLink = $firstCard.find('a.card-link'); 371 $firstLink.attr('href', mainData.link); 372 adjustLinkAttributes($firstLink); 373 374 // La dernière carte devient le main 375 setBgUrl($main.find('.BgImg'), lastCardData.img); 376 $main.find('.card-title').html(lastCardData.title); 377 $main.find('.card-desc').html(lastCardData.desc); 378 const $mainLink = $main.find('a.card-link'); 379 $mainLink.attr('href', lastCardData.link); 380 adjustLinkAttributes($mainLink); 381 } 382 383//////////////////////////////////////////////////////////////////////////////// 384/////// /////// /////// /////// /////// /////// /////// /////// //////// 385//////////////////////////////////////////////////////////////////////////////// 386 387 function adjustLinkAttributes($a) { 388 const href = $a.attr('href'); 389 if(href && href.startsWith('#')) { 390 $a.attr('data-bs-toggle', 'modal'); 391 } else { 392 $a.removeAttr('data-bs-toggle'); 393 } 394 } 395 396//////////////////////////////////////////////////////////////////////////////// 397/////// /////// /////// /////// /////// /////// /////// /////// //////// 398//////////////////////////////////////////////////////////////////////////////// 399 400 function RemoveHandSwipeImg(That) 401 { 402 $(That).on('changed.owl.carousel', function(e) 403 { 404 $(That).find('.owl-nav').addClass('Slided'); 405 406 $('.row.SF_SingleTab').each(function() 407 { 408 var $LastSlide = $(this).find('.owl-stage-outer .owl-item:last').prev('.owl-item').find('.ProductThumb'), 409 $LastCatLink = $(this).find('.LastCatLink'), 410 LastHeight = parseInt($LastSlide.height(),10), 411 PaddingTop = (LastHeight - parseInt($LastCatLink.height(),10))/2; 412 $LastCatLink.css('padding-top', PaddingTop+'px');
413 $(this).find('.CatLinkInSlide .CatLink').css('height', LastHeight+'px'); 414 }); 415 }); 416 } 417 418//////////////////////////////////////////////////////////////////////////////// 419/////// /////// /////// /////// /////// /////// /////// /////// //////// 420//////////////////////////////////////////////////////////////////////////////// 421 422 function StickyPaymentStepsButtons(Origin) 423 { 424 var intervalId; 425 function applyStickyCss() 426 { 427 var $SubmitButton = $('#snipcart[data-snipcart-page="/checkout"]').find('button.snipcart-button-primary.snipcart-submit'); 428 var $CartSummary = $('#snipcart[data-snipcart-page="/checkout"] .snipcart-cart-summary'); 429 var WindowHeight = $(window).height(); 430 431 if($SubmitButton.length && $CartSummary.length) 432 { 433 var CartSummaryRect = $CartSummary[0].getBoundingClientRect(), 434 TopButton = parseInt(CartSummaryRect.top+CartSummaryRect.height+32,10); 435 if(CartSummaryRect && WindowHeight > TopButton+32 /* && !$EditingOverLay.length */) 436 { 437 $SubmitButton.css({ 438 'position':'fixed', 439 'top':TopButton+'px', 440 'left':parseInt(CartSummaryRect.left,10)+'px', 441 'width':parseInt(CartSummaryRect.width,10)+'px', 442 'z-index':10, 443 'opacity':1 444 }); 445 clearInterval(intervalId); 446 } 447 else 448 { 449 resetCss(); 450 } 451 } 452 } 453 function resetCss() 454 { 455 var $SubmitButton = $('#snipcart[data-snipcart-page="/checkout"]').find('button.snipcart-button-primary.snipcart-submit'); 456 if($SubmitButton.length) 457 { 458 $SubmitButton.css({ 459 'position':'', 460 'top':'', 461 'left':'', 462 'width':'100%', 463 'z-index':'', 464 'opacity':'1' 465 }); 466 } 467 } 468 function startInterval() 469 { 470 if(intervalId) 471 { 472 clearInterval(intervalId); 473 } 474 intervalId = setInterval(function() 475 { 476 if(window.innerWidth >= 1024) 477 { 478 applyStickyCss(); 479 } 480 else 481 { 482 resetCss(); 483 } 484 },500); 485 } 486 var observer = new MutationObserver(function(mutationsList) 487 { 488 if(window.innerWidth >= 1024) 489 { 490 startInterval(); 491 } 492 }); 493 494 var $TargetNode = document.querySelector('#snipcart[data-snipcart-page="/checkout"]'); 495 if($TargetNode) 496 { 497 observer.observe($TargetNode,{childList:true,subtree:true,attributes:true}); 498 if(window.innerWidth >= 1024) 499 { 500 startInterval(); 501 } 502 } 503 504 window.addEventListener('resize',function() 505 { 506 if(window.innerWidth >= 1024) 507 { 508 startInterval(); 509 } 510 else 511 { 512 resetCss(); 513 clearInterval(intervalId); 514 } 515 }); 516 } 517 518//////////////////////////////////////////////////////////////////////////////// 519/////// /////// /////// /////// /////// /////// /////// /////// //////// 520//////////////////////////////////////////////////////////////////////////////// 521 522 function HTML_Reviews(Review) 523 { 524 let DateReview = new Date(Review.date), 525 HTML = '<li id="Slide'+Review.id+'" class="splide__slide p-2 mt-3 d-flex">'+ 526 '<a href="https://www.societe-des-avis-garantis.fr/sacre-francais/" target="_blank">'+ 527 '<div class="review-top d-flex">'+ 528 '<div class="wrapper_review-picto">'+ 529 '<img src="'+RootPath()+'/images/svg/cocarde.svg" alt="Société des avis garantis" class="review-picto">'+ 530 '</div>'+ 531 '<span class="Rating Rate'+Review.r+'">'+ 532 '<label></label><label></label><label></label><label></label><label></label>'+ 533 '</span>'+ 534 '</div>'+ 535 '<div class="review-text">'+ 536 '<p>'+Review.txt+'</p>'+ 537 '</div>'+ 538 '<div class="author">'+ 539 '<div class="author-picto">'+ 540 '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"><circle fill="none" cx="12" cy="7" r="3"></circle><path fill="white" d="M12 2C9.243 2 7 4.243 7 7s2.243 5 5 5 5-2.243 5-5S14.757 2 12 2zM12 10c-1.654 0-3-1.346-3-3s1.346-3 3-3 3 1.346 3 3S13.654 10 12 10zM21 21v-1c0-3.859-3.141-7-7-7h-4c-3.86 0-7 3.141-7 7v1h2v-1c0-2.757 2.243-5 5-5h4c2.757 0 5 2.243 5 5v1H21z"></path></svg>'+ 541 '</div>'+ 542 '<div class="author-infos d-flex">'+ 543 '<span class="author-pseudo">'+Review.c+'</span>'+ 544 '<span class="author-time">'+ 545 '<span class="author-date">'+ 546 '<span id="datetime">
546Le '+DateReview.toLocaleString()+'</span>'+ 547 '</span>'+ 548 '</span>'+ 549 '</div>'+ 550 '<div class="author-authenticated">'+ 551 '<svg alt="Acheteur certifié" class="" data-content="Acheteur authentifié" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#5bc200" d="M11.488,21.754c0.294,0.157,0.663,0.156,0.957-0.001c8.012-4.304,8.581-12.713,8.574-15.104 c-0.001-0.394-0.235-0.744-0.596-0.903L12.373,2.18c-0.259-0.114-0.554-0.114-0.813,0.001L3.566,5.747 C3.211,5.906,2.979,6.25,2.974,6.639C2.94,9.018,3.419,17.445,11.488,21.754z M8.674,10.293l2.293,2.293l4.293-4.293l1.414,1.414 l-5.707,5.707L7.26,11.707L8.674,10.293z"></path></svg>'+ 552 '<span>Acheteur authentifié</span>'+ 553 '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" class="svg-quote">'+ 554 '<defs>'+ 555 '<linearGradient id="gradient" gradientTransform="rotate(-20)">'+ 556 '<stop offset="0%" stop-color="#0e559f"></stop>'+ 557 '<stop offset="100%" stop-color="#e53434"></stop>'+ 558 '</linearGradient>'+ 559 '</defs>'+ 560 '<path fill="url(#gradient)" d="M20.309 17.708C22.196 15.66 22.006 13.03 22 13v-3V5c0-.552-.447-1-1-1h-6c-1.103 0-2 .897-2 2v7c0 .552.447 1 1 1h1 2.078c-.021.402-.123.912-.429 1.396-.508.801-1.465 1.348-2.846 1.624L13 17.18V20h1C16.783 20 18.906 19.229 20.309 17.708zM9.302 17.708c1.888-2.048 1.697-4.678 1.691-4.708v-3V5c0-.552-.447-1-1-1h-6c-1.103 0-2 .897-2 2v7c0 .552.447 1 1 1h1 2.078c-.021.402-.123.912-.429 1.396-.508.801-1.465 1.348-2.846 1.624L1.993 17.18V20h1C5.776 20 7.899 19.229 9.302 17.708z"></path>'+ 561 '</svg>'+ 562 '</div>'+ 563 '</div>'+ 564 '</a>'+ 565 '</li>'; 566 return HTML; 567 } 568 569//////////////////////////////////////////////////////////////////////////////// 570/////// /////// /////// /////// /////// /////// /////// /////// //////// 571//////////////////////////////////////////////////////////////////////////////// 572 573 function HTML_StarNote(Note) 574 { 575 return '<svg version="1.1" class="StarMask mx-1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 493.5 100" xml:space="preserve"><rect y="1.8" class="StarRateFull" width="490.1" height="95.7"/><rect y="1.8" class="StarRate" width="'+(100/5*Note)+'%" height="95.7"/> <g> <path class="st2" d="M0,0v100h493.5V0H0z M95.1,41.5l-19,18.4c-0.6,0.6-0.9,1.5-0.8,2.3l4.4,26.1c0.4,2.2-1.9,3.8-3.8,2.8 L52.5,78.7c-0.8-0.4-1.7-0.4-2.5,0L26.6,91c-1.9,1-4.2-0.6-3.8-2.8l4.5-26.1c0.1-0.9-0.1-1.7-0.8-2.3L7.7,41.3 c-1.6-1.5-0.7-4.2,1.5-4.5L35.4,33c0.9-0.1,1.6-0.7,2-1.4L49.1,7.9c1-2,3.8-2,4.7,0l11.6,23.7c0.4,0.8,1.1,1.3,2,1.5L93.7,37 C95.8,37.3,96.7,40,95.1,41.5z M192.9,41.5l-19,18.4c-0.6,0.6-0.9,1.5-0.8,2.3l4.4,26.1c0.4,2.2-1.9,3.8-3.8,2.8l-23.4-12.4 c-0.8-0.4-1.7-0.4-2.5,0L124.4,91c-1.9,1-4.2-0.6-3.8-2.8l4.5-26.1c0.1-0.9-0.1-1.7-0.8-2.3l-18.9-18.5c-1.6-1.5-0.7-4.2,1.5-4.5 l26.2-3.7c0.9-0.1,1.6-0.7,2-1.4l11.8-23.7c1-2,3.8-2,4.7,0l11.6,23.7c0.4,0.8,1.1,1.3,2,1.5l26.2,3.9 C193.7,37.3,194.5,40,192.9,41.5z M290.8,41.5l-19,18.4c-0.6,0.6-0.9,1.5-0.8,2.3l4.4,26.1c0.4,2.2-1.9,3.8-3.8,2.8l-23.4-12.4 c-0.8-0.4-1.7-0.4-2.5,0L222.3,91c-1.9,1-4.2-0.6-3.8-2.8l4.5-26.1c0.1-0.9-0.1-1.7-0.8-2.3l-18.9-18.5c-1.6-1.5-0.7-4.2,1.5-4.5 L231,33c0.9-0.1,1.6-0.7,2-1.4l11.8-23.7c1-2,3.8-2,4.7,0l11.6,23.7c0.4,0.8,1.1,1.3,2,1.5l26.2,3.9 C291.5,37.3,292.3,40,290.8,41.5z M388.6,41.5l-19,18.4c-0.6,0.6-0.9,1.5-0.8,2.3l4.4,26.1c0.4,2.2-1.9,3.8-3.8,2.8L346,78.7 c-0.8-0.4-1.7-0.4-2.5,0L320.1,91c-1.9,1-4.2-0.6-3.8-2.8l4.5-26.1c0.1-0.9-0.1-1.7-0.8-2.3l-18.9-18.5c-1.6-1.5-0.7-4.2,1.5-4.5 l26.2-3.7c0.9-0.1,1.6-0.7,2-1.4l11.8-23.7c1-2,3.8-2,4.7,0L359,31.7c0.4,0.8,1.1,1.3,2,1.5l26.2,3.9 C389.3,37.3,390.2,40,388.6,41.5z M486.4,41.5l-19,18.4c-0.6,0.6-0.9,1.5-0.8,2.3l4.4,26.1c0.4,2.2-1.9,3.8-3.8,2.8l-23.4-12.4 c-0.8-0.4-1.7-0.4-2.5,0L417.9,91c-1.9,1-4.2-0.6-3.8-2.8l4.5-26.1c0.1-0.9-0.1-1.7-0.8-2.3L399,41.3c-1.6-1.5-0.7-4.2,1.5-4.5 l26.2-3.7c0.9-0.1,1.6-0.7,2-1.4l11.8-23.7c1-2,3.8-2,4.7,0l11.6,23.7c0.4,0.8,1.1,1.3,2,1.5L485,37C487.1,37.3,488,40,486.4,41.5z "/></g></svg>'; 576 } 577 578//////////////////////////////////////////////////////////////////////////////// 579/////// /////// /////// /////// /////// /////// /////// /////// //////// 580//////////////////////////////////////////////////////////////////////////////// 581 582 function HTML_StarNoteSimplified(Note) 583 { 584 let HTML = '<div class="StarNote Rate'+Note+'">'; 585 586 for(let i = 0; i < 5; i++) 587 { 588 HTML += '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 89 85" xml:space="preserve"><g><path class="st0" d="M88.2,35.1l-19,18.4c-0.6,0.6-0.9,1.5-0.8,2.3l4.4,26.1c0.4,2.2-1.9,3.8-3.8,2.8L45.6,72.3 c-0.8-0.4-1.7-0.4-2.5,0L19.7,84.6c-1.9,1-4.2-0.6-3.8-2.8l4.5-26.1c0.1-0.9-0.1-1.7-0.8-2.3L0.8,34.9c-1.6-1.5-0.7-4.2,1.5-4.5 l26.2-3.8c0.9-0.1,1.6-0.7,2-1.4L42.2,1.5c1-2,3.8-2,4.7,0l11.6,23.7c0.4,0.8,1.1,1.3,2,1.5l26.3,3.9 C88.9,30.9,89.8,33.6,88.2,35.1z"/></g></svg>'; 589 } 590 HTML += '</div>'; 591 return HTML; 592 } 593 594//////////////////////////////////////////////////////////////////////////////// 595/////// /////// /////// /////// /////// /////// /////// /////// //////// 596//////////////////////////////////////////////////////////////////////////////// 597 598 function GetAsideContent() 599 { 600 $(document).on('show.bs.offcanvas', '#SF_Aside', function (e) 601 {
602 const $trigger = $(e.relatedTarget); // <a> qui a déclenché l'ouverture 603 if(!$trigger.length) return; 604 605 const action = $trigger.data('aside-action') || ''; 606 const type = $trigger.data('aside-type') || ''; 607 const $aside = $('#SF_Aside .offcanvas-body'); 608 609 // Nettoie le contenu avant chargement 610 $aside.html('<div class="text-center p-5"><div class="spinner-border text-secondary" role="status"></div></div>'); 611 612 // Vérifie les données avant dâappeler Ajax 613 if(IsEmpty(action) || IsEmpty(type)) 614 { 615 $aside.html('<div class="alert alert-warning m-3">Données manquantes.</div>'); 616 return; 617 } 618 619 $.ajax({ 620 type: 'POST', 621 url: RootPath() + '/Ajax.php', 622 dataType: 'json', 623 data: { 624 action: action, 625 CookingType: type 626 }, 627 cache: false, 628 success: function (res) 629 { 630 if(res && res.html) 631 { 632 $aside.html(res.html); 633 } 634 else if(res && res.error) 635 { 636 $aside.html('<div class="alert alert-danger m-3">' + res.error + '</div>'); 637 } 638 else 639 { 640 $aside.html('<div class="alert alert-secondary m-3">Aucun contenu disponible.</div>'); 641 } 642 }, 643 error: function (xhr, status, error) 644 { 645 console.error('Erreur AJAX:', error); 646 $aside.html('<div class="alert alert-danger m-3">Erreur de chargement : ' + error + '</div>'); 647 } 648 }); 649 }); 650 } 651 652//////////////////////////////////////////////////////////////////////////////// 653/////// /////// /////// /////// /////// /////// /////// /////// //////// 654//////////////////////////////////////////////////////////////////////////////// 655 656 function GetUniqueReviews(IdProduct) 657 { 658 setTimeout(function() 659 { 660 const Id = IsEmpty(IdProduct) ? $('#add_to_cart:not(.NoRating) button').attr('data-item-id') : IdProduct, 661 $TabInfo = $('#MoreInfoTabs'), 662 $Parent = $('#short_description_content'), 663 $TabDiv = $('#CenterColumn .MoreInfos'); 664 if(!IsEmpty(Id)) 665 { 666 $.ajax({ 667 type: 'POST', url: RootPath()+'/Ajax.php', dataType: "json", 668 data: {GetUniqueReview : Id}, 669 cache: false, 670 success: function(x) 671 { 672 let Average = Math.ceil(x.ratings.average), 673 Total = x.ratings.total, 674 Note = x.ratings.average, 675 TabTitle = '<li class="mt-2"><a id="MoreInfoTab_Reviews" href="#InfoTabReviews" class="ps-3"><span>'+x.reviews.length+' avis</span></a></li>', 676 HTML = '<div class="Rating my-1 p-1">'; 677 HTML += Note > 0 ? '<span class="Note"><b>'+Number(Note)+'</b>/5</span>' : ''; 678 HTML += Note > 0 ? '<a class="ScrollToReviews" href="#">' : ''; 679 HTML += HTML_StarNote(Note); 680 HTML += Total > 0 ? 'sur '+Total+' avis</a>' : 'Aucun avis'; 681 HTML += '</div>'; 682 if(Total > 0) 683 { 684 HTML += '<div class="AllRates">'; 685 for(let i = 5; i > 0; i--) 686 { 687 let RateNote = x.ratings.distribution[i], 688 DataNote = RateNote > 0 ? 'data-note="'+Math.ceil(100/Total*RateNote)+'"' : ''; 689 HTML += '<div class="RateNoteBox"><label>'+i+'</label><div class="RateNote"><div class="ValueRateNote" '+DataNote+' style="width:'+(100/Total*RateNote)+'%"></div><div class="IntNote">'+RateNote+'</div></div></div>'; 690 } 691 HTML += '</div>'; 692 } 693 $Parent.prepend(HTML); 694 if(Total > 0 && $TabInfo.length) 695 { 696 $TabInfo.append(TabTitle); 697 $TabDiv.append('<section id="InfoTabReviews" class="page-product-box p-3 pt-0 BoxRadius HideMoreInfoSection"><h3 class="ScriptFont mt-2 TitleAg">Les avis des internautes</h3><ul></ul></section>'); 698 $.each(x.reviews, function(i, Review) 699 { 700 $('#InfoTabReviews ul:not(.ReviewSlides)').append(HTML_Reviews(Review)); 701 }); 702 } 703 }, 704 error: function(){ ErrorAjax('Une erreur est survenue.'); } 705 }); 706 } 707 }, 500); 708 } 709 710//////////////////////////////////////////////////////////////////////////////// 711/////// /////// /////// /////// /////// /////// /////// /////// //////// 712//////////////////////////////////////////////////////////////////////////////// 713 714 function ChangeDateFormat() 715 { 716 $(document).on('click', '.snipcart-modal__container:not(.Completed)', function(e) 717 { 718 $(this).addClass('Completed'); 719 $('.snipcart-paginated-list').find('.snipcart-order__header-item').each(function() 720 { 721 var $Date = $(this).find('div').last(); 722 }); 723 }); 724 } 725 726//////////////////////////////////////////////////////////////////////////////// 727/////// /////// /////// /////// /////// /////// /////// /////// //////// 728//////////////////////////////////////////////////////////////////////////////// 729 730 function SetHtmlTagToOverflowHidden(Status) 731 { 732 if(Status === true) 733 { 734 $('html').addClass('ModalIsOpen'); 735 } 736 else 737 { 738 $('html').removeClass('ModalIsOpen'); 739 } 740 } 741 742//////////////////////////////////////////////////////////////////////////////// 743/////// /////// /////// /////// /////// /////// /////// /////// //////// 744//////////////////////////////////////////////////////////////////////////////// 745 746 function ResetHtmlTagOverflow() 747 { 748 $('#ProductModal').on('hidden.bs.modal', function() 749 { 750 SetHtmlTagToOverflowHidden(false); 751 }); 752 } 753 754//////////////////////////////////////////////////////////////////////////////// 755/////// /////// /////// /////// /////// /////// /////// /////// //////// 756//////////////////////////////////////////////////////////////////////////////// 757 758 function HTML_AjaxAlert(Message) 759 { 760 return '<div class="alert alert-warning d-flex align-items-center mx-auto mt-4" role="alert">'+ 761 '<h4 class="alert-heading">Erreur</h4>'+ 762 '<img class="bi flex-shrink-0 me-2" height="24" src="'+RootPath()+'/images/svg/WarningCoral.svg">'+ 763 '<div>'+Message+'</div>'+ 764 '</div>'; 765 } 766 767//////////////////////////////////////////////////////////////////////////////// 768/////// /////// /////// /////// /////// /////// /////// /////// //////// 769//////////////////////////////////////////////////////////////////////////////// 770 771 function isTouchDevice() 772 { 773 return (('ontouchstart' in window) || 774 (navigator.maxTouchPoints > 0) || 775 (navigator.msMaxTouchPoints > 0)); 776 } 777 778//////////////////////////////////////////////////////////////////////////////// 779/////// /////// /////// /////// /////// /////// /////// /////// //////// 780//////////////////////////////////////////////////////////////////////////////// 781 782 function RootPath() 783 { 784 return window.location.origin+(window.location.hostname === 'securelocalhost' ? '/SacreFrancais.fr' : ''); 785 } 786 787//////////////////////////////////////////////////////////////////////////////// 788/////// /////// /////// /////// /////// /////// /////// /////// //////// 789//////////////////////////////////////////////////////////////////////////////// 790 791 function SlideArrows() 792 { 793 return ['<i class="zmdi zmdi-chevron-left"></i>', '<i class="zmdi zmdi-chevron-right"></i>' ]; 794 } 795 796//////////////////////////////////////////////////////////////////////////////// 797/////// /////// /////// /////// /////// /////// /////// /////// //////// 798//////////////////////////////////////////////////////////////////////////////// 799 800 function CloneBuyForm() 801 { 802 var $BuyForm = $('.SingleProductPage #buy_block'), 803 $StickyBuyForm = $('.SingleProductPage #MainMenu #Sticky_buy_Block'), 804 ProductName = $BuyForm.find('[data-item-name]').attr('data-item-name'); 805 if($BuyForm.length) 806 { 807 if(!$StickyBuyForm.length) 808 { 809 var HTML_Form = $BuyForm.clone(); 810 $('#MainMenu').append($(HTML_Form).attr('id', 'Sticky_buy_Block').hide()); 811 setTimeout(function() 812 { 813 var Prices = $('.SingleProductPage .ProductZone #ProductDetailBox')[0].outerHTML; 814 $('.SingleProductPage #MainMenu #Sticky_buy_Block .box-info-product').prepend('<h2 data-old-price="" class="ProductName d-flex">'+ProductName+Prices+'</h2>'); 815 $('.SingleProductPage #MainMenu #Sticky_buy_Block .ProductPackContainer').each(function() 816 { 817 var NewId = $(this).find('input').attr('id')+'-Sticky'; 818 $(this).find('input').attr('id', NewId); 819 $(this).find('label').attr('for', NewId); 820 }); 821 }, 10); 822 } 823 } 824 } 825 826//////////////////////////////////////////////////////////////////////////////// 827/////// /////// /////// /////// /////// /////// /////// /////// //////// 828//////////////////////////////////////////////////////////////////////////////// 829 830 function FilteringTastes(That) 831 { 832 var Taste = $(That).val(); 833 if(Taste.length > 0) 834 { 835 $('#ProductsList .TagList').hide(); 836 $('#ProductsList .TagList a:icontains("'+Taste+'")').parent().show(); 837 $('.AlphabeticalContainer').each(function() 838 { 839 if(!$(this).find('.TagList:visible').length) 840 { 841 $(this).hide(); 842 } 843 else 844 { 845 $(this).show(); 846 } 847 }); 848 } 849 else 850 { 851 $('#ProductsList .TagList, .AlphabeticalContainer').fadeIn(100); 852 } 853 } 854 855 const OwlOptions = { loop: true, 856 margin: 30, 857 nav: true, 858 autoplay: false, 859 autoplayTimeout: 5000, 860 navText: SlideArrows(), 861 dots: false, 862 lazyLoad: true, 863 responsive: SET_ItemQuantity('xs-1-sp50 sm-1-sp50 md-2-sp100 lg-2-sp100 xl-3-sp100') 864 }; 865 866//////////////////////////////////////////////////////////////////////////////// 867/////// /////// /////// /////// /////// /////// /////// /////// //////// 868//////////////////////////////////////////////////////////////////////////////// 869 870 $('.SF_CookingFamily').owlCarousel(OwlOptions); 871 872//////////////////////////////////////////////////////////////////////////////// 873/////// /////// /////// /////// /////// /////// /////// /////// //////// 874//////////////////////////////////////////////////////////////////////////////// 875 876 $('.SF_MobileProductMainSlide').owlCarousel({ 877 loop: true, 878 margin: 0, 879 nav: true, 880 autoplay: false, 881 autoplayTimeout: 5000, 882 navText: SlideArrows(), 883 dots: false, 884 lazyLoad: true, 885 responsive: SET_ItemQuantity('xs-1-sp50 sm-1-sp50 md-1-sp0 lg-1-sp0 xl-1-sp0') 886 }); 887 888//////////////////////////////////////////////////////////////////////////////// 889/////// /////// /////// /////// /////// /////// /////// /////// //////// 890//////////////////////////////////////////////////////////////////////////////// 891 892 function RecipesSlide(x) 893 { 894 $('.SF-RecipeProductSlide').owlCarousel({ 895 loop: false, 896 margin: 30, 897 nav: true, 898 autoplay: false, 899 autoplayTimeout: 5000, 900 navText: SlideArrows(), 901 dots: false, 902 lazyLoad: true, 903 responsive: SET_ItemQuantity('xs-1-sp50 sm-1-sp50 md-2-sp100 lg-2-sp100 xl-3-sp100') 904 }); 905 } 906 907//////////////////////////////////////////////////////////////////////////////// 908/////// /////// /////// /////// /////// /////// /////// /////// //////// 909//////////////////////////////////////////////////////////////////////////////// 910 911 function ProductInProductSlide(x) 912 { 913 $('.SF-ProductInProductSlide').owlCarousel({ 914 loop: false, 915 margin: 30, 916 nav: true, 917 autoplay: false, 918 navText: SlideArrows(), 919 dots: false, 920 lazyLoad: true, 921 responsive: SET_ItemQuantity('xs-2-sp50 sm-3-sp50 md-3-sp100 lg-4-sp100 xl-4-sp100') 922 }); 923 RemoveHandSwipeImg('.SF-ProductInProductSlide'); 924 } 925 926//////////////////////////////////////////////////////////////////////////////// 927/////// /////// /////// /////// /////// /////// /////// /////// //////// 928//////////////////////////////////////////////////////////////////////////////// 929 930 function SearchToggler() 931 { 932 var $SearchBtn = $('.SearchBtn'), 933 $SearchForm = $('.SearchForm'); 934 $SearchBtn.on('click', function(e) 935 { 936 e.preventDefault(); 937 $SearchForm.toggleClass('is-visible') 938 setTimeout(function() 939 { 940 if($SearchForm.hasClass('is-visible')) 941 { 942 $('form.SearchInStore input[type="text"]').focus().click(); 943 } 944 }, 200); 945 }); 946 947 $('.SearchCloseBtn').on('click', function() 948 { 949 $SearchForm.removeClass('is-visible'); 950 }); 951 } 952 953//////////////////////////////////////////////////////////////////////////////// 954/////// /////// /////// /////// /////// /////// /////// /////// //////// 955//////////////////////////////////////////////////////////////////////////////// 956 957 function ImagesLoaded() 958 { 959 $('#ProductsList, .ProductZoomImg, .item-bestseller, .image-block').imagesLoaded(function() 960 { 961 $('.ProductThumb, .Zoom, .ProductZoomImg').removeClass('LoadingImg'); 962 }); 963 } 964 965//////////////////////////////////////////////////////////////////////////////// 966/////// /////// /////// /////// /////// /////// /////// /////// //////// 967//////////////////////////////////////////////////////////////////////////////// 968 969 function CheckResponsiveTable() 970 { 971 $('.ResponsiveTable').each(function() 972 { 973 if(this.scrollWidth > this.clientWidth) 974 { 975 $(this).addClass('Slidable'); 976 } 977 }); 978 } 979 980//////////////////////////////////////////////////////////////////////////////// 981/////// /////// /////// /////// /////// /////// /////// /////// //////// 982//////////////////////////////////////////////////////////////////////////////// 983 984 function IsEmpty(x) 985 { 986 return (x === '' || x === null || typeof x === 'undefined') ? true : false;
987 } 988 989//////////////////////////////////////////////////////////////////////////////// 990/////// /////// /////// /////// /////// /////// /////// /////// //////// 991//////////////////////////////////////////////////////////////////////////////// 992 993 function IsNotEmpty(x) 994 { 995 return (x !== /\s+?/ && x !== '' && x !== null && typeof x !== 'undefined') ? true : false; 996 } 997 998//////////////////////////////////////////////////////////////////////////////// 999/////// /////// /////// /////// /////// /////// /////// /////// //////// 1000//////////////////////////////////////////////////////////////////////////////// 1001 1002 function UpdateLanguageFlag() 1003 { 1004 var GoogTrans = document.cookie.match('(^|;)\\s*googtrans\\s*=\\s*([^;]+)')?.pop() || '', 1005 LastLang = GoogTrans.split('/').pop(); 1006 $('.TranslationFlags a[data-lang="'+LastLang+'"]').parent().prependTo('.TranslationFlags'); 1007 } 1008 1009//////////////////////////////////////////////////////////////////////////////// 1010/////// /////// /////// /////// /////// /////// /////// /////// //////// 1011//////////////////////////////////////////////////////////////////////////////// 1012 1013 function FireEvent(That, e) 1014 { 1015 //Source : https://stackoverflow.com/a/9810401 1016 if(document.createEventObject) 1017 { 1018 var evt = document.createEventObject(); 1019 return That.fireEvent('on'+e, evt) 1020 } 1021 else 1022 { 1023 var evt = document.createEvent('HTMLEvents'); 1024 evt.initEvent(e, true, true ); 1025 return !That.dispatchEvent(evt); 1026 } 1027 } 1028 1029//////////////////////////////////////////////////////////////////////////////// 1030/////// /////// /////// /////// /////// /////// /////// /////// //////// 1031//////////////////////////////////////////////////////////////////////////////// 1032 1033 function isValidEmailAddress(emailAddress) 1034 { 1035 var pattern = new RegExp(/^(("[\w-+\s]+")|([\w-+]+(?:\.[\w-+]+)*)|("[\w-+\s]+")([\w-+]+(?:\.[\w-+]+)*))(@((?:[\w-+]+\.)*\w[\w-+]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$)|(@\[?((25[0-5]\.|2[0-4][\d]\.|1[\d]{2}\.|[\d]{1,2}\.))((25[0-5]|2[0-4][\d]|1[\d]{2}|[\d]{1,2})\.){2}(25[0-5]|2[0-4][\d]|1[\d]{2}|[\d]{1,2})\]?$)/i); 1036 return pattern.test(emailAddress); 1037 } 1038 1039//////////////////////////////////////////////////////////////////////////////// 1040/////// /////// /////// /////// /////// /////// /////// /////// //////// 1041//////////////////////////////////////////////////////////////////////////////// 1042 1043 function GetProductIds() 1044 { 1045 var Ids = []; 1046 $('#ProductsList li.product').each(function() 1047 { 1048 Ids.push($(this).data('id')); 1049 }); 1050 return Ids.join(','); 1051 } 1052 1053//////////////////////////////////////////////////////////////////////////////// 1054/////// /////// /////// /////// /////// /////// /////// /////// //////// 1055//////////////////////////////////////////////////////////////////////////////// 1056 1057 function GetLastLoadProductIds() 1058 { 1059 var Ids = []; 1060 $('#ProductsList li.product').each(function() 1061 { 1062 if(!$(this).find('.ProductThumb .StarNote').length) 1063 { 1064 Ids.push($(this).data('id')); 1065 } 1066 }); 1067 return Ids.join(','); 1068 } 1069 1070//////////////////////////////////////////////////////////////////////////////// 1071/////// /////// /////// /////// /////// /////// /////// /////// //////// 1072//////////////////////////////////////////////////////////////////////////////// 1073 1074 function GetProductCatId() 1075 { 1076 var $That = $('#ProductsList li.product').first(), 1077 IdCat = IsNotEmpty($That.data('cat')) ? $That.data('cat') : '', 1078 $LoadFilter = $('body[data-load-filter]'); 1079 return $LoadFilter.length ? $LoadFilter.attr('data-load-filter') : IdCat; 1080 } 1081 1082//////////////////////////////////////////////////////////////////////////////// 1083/////// /////// /////// /////// /////// /////// /////// /////// //////// 1084//////////////////////////////////////////////////////////////////////////////// 1085 1086 function GetProductFilter() 1087 { 1088 var Filter = []; 1089 $('#FilterBlock input[type="checkbox"]:checked').each(function(i,v) 1090 { 1091 Filter.push($(this).val()); 1092 }); 1093 return Filter.join(','); 1094 } 1095 1096//////////////////////////////////////////////////////////////////////////////// 1097/////// /////// /////// /////// /////// /////// /////// /////// //////// 1098//////////////////////////////////////////////////////////////////////////////// 1099 1100 function IsItBlog() 1101 { 1102 return $('#ProductsList .product.Blog').length ? true : null; 1103 } 1104 1105//////////////////////////////////////////////////////////////////////////////// 1106/////// /////// /////// /////// /////// /////// /////// /////// //////// 1107//////////////////////////////////////////////////////////////////////////////// 1108 1109 function FilterSelect() 1110 { 1111 var Mode = parseInt($('body').attr('data-load-method'),10), 1112 LoadFilter = $('body').attr('data-load-filter'), 1113 Flavor = LoadFilter === 'Flavor' ? $('body').attr('data-flavor') : 'Spicy', 1114 Search = LoadFilter === 'Search' ? $('body').attr('data-search') : '', 1115 $Product = $('#ProductsList li.product').first(), 1116 SubCat = $Product.attr('data-subcat'), 1117 Group = $Product.attr('data-group'), 1118 Filters = ''; 1119 if(Mode === 2) 1120 { 1121 if(LoadFilter === 'Spicy') 1122 { 1123 Filters = LoadFilter+'|'+Flavor; 1124 } 1125 else if(LoadFilter === 'Search') 1126 { 1127 Filters = Search; 1128 } 1129 else if(LoadFilter === 'Flavor') 1130 { 1131 Filters = 'SubCategory|'+Flavor; 1132 } 1133 else 1134 {
1135 Filters = 'SubCategory|'+SubCat; 1136 } 1137 } 1138 else if(Mode === 3) 1139 { 1140 Filters = 'Group|'+Group; 1141 } 1142 return Filters; 1143 } 1144 1145//////////////////////////////////////////////////////////////////////////////// 1146/////// /////// /////// /////// /////// /////// /////// /////// //////// 1147//////////////////////////////////////////////////////////////////////////////// 1148 1149 function LoadingEnding() 1150 { 1151 setTimeout(function() 1152 { 1153 $('#LoadMoreItems').removeClass('Loading'); 1154 }, 1500); 1155 } 1156 1157//////////////////////////////////////////////////////////////////////////////// 1158/////// /////// /////// /////// /////// /////// /////// /////// //////// 1159//////////////////////////////////////////////////////////////////////////////// 1160 1161 function FilteringItems($That) 1162 { 1163 $('#ProductsList').fadeTo('slow', 0.3); 1164 setTimeout(function() 1165 { 1166 $That.removeClass('load'); 1167 if($('#FilterBlock input[type="checkbox"]:checked').length) 1168 { 1169 let CheckedValues = []; 1170 $('#ProductsList li.product, #ProductsList .GiftCardContainer, .RandomAdvert').each(function() 1171 { 1172 $(this).addClass('Hidden'); 1173 }); 1174 1175 $('#FilterBlock input[type="checkbox"]').each(function(i,v) 1176 { 1177 let $That = $(this); 1178 if($That.is(':checked')) 1179 { 1180 let What = $That.data('filter'), 1181 Val = $That.val().split('|'); 1182 CheckedValues.push({[What]: Val[1]}); 1183 } 1184 }); 1185 const HasSubcat = CheckedValues.some(item => item.subcat), 1186 $Subcat = CheckedValues.filter(item => item.subcat).map(item => item.subcat), 1187 $Group = CheckedValues.filter(item => item.group).map(item => item.group); 1188 $('#ProductsList li.product').each(function() 1189 { 1190 const Subcat = $(this).data('subcat'), 1191 Group = $(this).data('group'); 1192 if(HasSubcat) 1193 { 1194 if(Subcat && $Subcat.includes(Subcat)) 1195 { 1196 $(this).removeClass('Hidden'); 1197 } 1198 } 1199 else 1200 { 1201 if(Group && $Group.includes(Group)) 1202 { 1203 $(this).removeClass('Hidden'); 1204 } 1205 } 1206 }); 1207 } 1208 else 1209 { 1210 $('#ProductsList').find('li.product, li.GiftCardContainer, .RandomAdvert').removeClass('Hidden'); 1211 } 1212 UpdateGroupFilter(); 1213 ImagesLoaded(); 1214 $('#ProductsList').fadeTo('slow', 1); 1215 ScrollToAnchor('#ProductsList'); 1216 }, 750); 1217 1218 1219 setTimeout(function() 1220 { 1221 FilteringGroupCategory(); 1222 }, 850); 1223 } 1224 1225//////////////////////////////////////////////////////////////////////////////// 1226/////// /////// /////// /////// /////// /////// /////// /////// //////// 1227//////////////////////////////////////////////////////////////////////////////// 1228 1229 function UpdateGroupFilter() 1230 { 1231 $('#GroupFilter li').addClass('Hidden'); 1232 $('#ProductsList li.product:visible').each(function() 1233 { 1234 let Group = $(this).data('group').replace(/[^a-zA-Z0-9]/g, ''); 1235// console.log(Group, $('#GroupFilter li[data-group-filter="'+encodeURIComponent(Group)+'"]').length); 1236 $('#GroupFilter li[data-group-filter="'+Group+'"]').removeClass('Hidden'); 1237 }); 1238 } 1239 1240//////////////////////////////////////////////////////////////////////////////// 1241/////// /////// /////// /////// /////// /////// /////// /////// //////// 1242//////////////////////////////////////////////////////////////////////////////// 1243 1244 function FilteringGroupCategory() 1245 { 1246 let $GroupCategory = $('.GroupCategory'); 1247 if($GroupCategory.length) 1248 { 1249 $GroupCategory.each(function(i,v) 1250 { 1251 if($(this).find('li.product:not([style*="display: none"])').length) 1252 { 1253 $(this).show(); 1254 } 1255 else 1256 { 1257 $(this).hide(); 1258 } 1259 }); 1260 } 1261 } 1262 1263//////////////////////////////////////////////////////////////////////////////// 1264/////// /////// /////// /////// /////// /////// /////// /////// //////// 1265//////////////////////////////////////////////////////////////////////////////// 1266 1267 function SendForm(Id) 1268 { 1269 var formulaire = new FormData($('form#'+Id)[0]); 1270 $('form#'+Id+' .alert.ajax').remove(); 1271 $.ajax({ 1272 type: 'POST', url: RootPath()+'/Ajax.php', dataType: "json", 1273 data: formulaire, 1274 cache: false, 1275 contentType: false, 1276 processData: false, 1277 success: function(x) 1278 { 1279 if(x.hasOwnProperty('Error')) 1280 { 1281 ErrorAjax(x['Error']); 1282 } 1283 else 1284 { 1285 $('form#'+Id).parent().html(x.alerte); 1286 } 1287 }, 1288 error: function(){ ErrorAjax('Une erreur est survenue.'); } 1289 }); 1290 } 1291 1292//////////////////////////////////////////////////////////////////////////////// 1293/////// /////// /////// /////// /////// /////// /////// /////// //////// 1294//////////////////////////////////////////////////////////////////////////////// 1295 1296 function ErrorAjax(Msg) 1297 { 1298/* 1299 $('#ErrorMsg').remove(); 1300 $('body').append('<div id="ErrorMsg" class="toast fade show" role="alert" aria-live="assertive" aria-atomic="true" autohide="true">'+ 1301 '<div class="toast-header modal-header">'+ 1302 '<strong class="mr-auto p-3 pt-0 pb-0">Erreur</strong>'+ 1303 '<button type="button" class="ml-2 mb-1 close QuickView-close" data-dismiss="toast" aria-label="Close">
1303'+ 1304 '<span aria-hidden="true">×</span>'+ 1305 '</button>'+ 1306 '</div>'+ 1307 '<div class="toast-body">'+Msg+'</div>'+ 1308 '</div>'); 1309*/ 1310 } 1311 1312//////////////////////////////////////////////////////////////////////////////// 1313/////// /////// /////// /////// /////// /////// /////// /////// //////// 1314//////////////////////////////////////////////////////////////////////////////// 1315 1316 function ErrorLoginAjax(Msg) 1317 { 1318 $('#ProLoginForm').parent().append('<div class="alert alert-warning d-flex align-items-center mb-0 mt-4" role="alert"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-exclamation-triangle-fill flex-shrink-0 me-2" viewBox="0 0 16 16" role="img" aria-label="Warning:"><path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/></svg><div>'+Msg+'</div></div>'); 1319 setTimeout(function() 1320 { 1321 $('#ProLoginForm').parent().find('.alert').fadeOut(250, function() 1322 { 1323 $(this).remove(); 1324 }); 1325 }, 3000); 1326 } 1327 1328//////////////////////////////////////////////////////////////////////////////// 1329/////// /////// /////// /////// /////// /////// /////// /////// //////// 1330//////////////////////////////////////////////////////////////////////////////// 1331 1332 function FormatUrl($Form, Pack) 1333 { 1334 var Url = $Form.find('.snipcart-add-item[data-item-url]').attr('data-item-url'); 1335 if(IsNotEmpty(Url)) 1336 { 1337 var UrlPart = Url.split('/'); 1338// console.log(Url, UrlPart); 1339 if(UrlPart[UrlPart.length - 2] === 'Carte-cadeau') 1340 { 1341 UrlPart[UrlPart.length - 1] = Pack+'.html'; 1342// console.log(UrlPart); 1343 } 1344 else 1345 { 1346 UrlPart[UrlPart.length - 2] = Pack; 1347 } 1348 return UrlPart.join('/'); 1349 } 1350 } 1351 1352//////////////////////////////////////////////////////////////////////////////// 1353/////// /////// /////// /////// /////// /////// /////// /////// //////// 1354//////////////////////////////////////////////////////////////////////////////// 1355 1356 function ScrollToAnchor(Id) 1357 { 1358 var MenuHeight = $('#MainMenu').height(), 1359 BuyFormHeight = $('.SingleProductPage #buy_block').length ? 80 : 0; 1360 if(IsEmpty(Id) && location.hash) 1361 { 1362 var Id = location.hash.replace('/',''); 1363 } 1364 if(IsNotEmpty(Id) && $(Id).length) 1365 { 1366 $(Id).prop('open', true); 1367 $('html,body').animate({scrollTop: $(Id).offset().top-MenuHeight-(20+BuyFormHeight)},50); 1368 } 1369 } 1370 1371//////////////////////////////////////////////////////////////////////////////// 1372/////// /////// /////// /////// /////// /////// /////// /////// //////// 1373//////////////////////////////////////////////////////////////////////////////// 1374 1375 function SET_Taxes(Taxe) 1376 { 1377 let TvaName = ''; 1378 switch(Taxe) 1379 { 1380 case '5.5' : TvaName = 'TVA ('+Taxe+'%)'; break; 1381 default : TvaName = 'TVA ('+Taxe+'%)'; break; 1382 } 1383 return TvaName; 1384 } 1385 1386//////////////////////////////////////////////////////////////////////////////// 1387/////// /////// /////// /////// /////// /////// /////// /////// //////// 1388//////////////////////////////////////////////////////////////////////////////// 1389 1390 function SET_ItemQuantity(Level) 1391 { 1392 // Exemple : 'xs-1-sp10 sm-2-sp100 md-2-sp50 lg-4-sp100' 1393 // xs = BreakPoint - 1 = Number of items - sp10 = Stage padding 10 px 1394 const BreakPoints = { xs: 0, sm: 576, md: 768, lg: 992, xl: 1920 }; 1395 let ResponsiveSettings = {}; 1396 1397 Level.split(' ').forEach(Setting => { 1398 let [BreakPointItems, StagePadding] = Setting.split('-sp'), 1399 [BreakPoint, ItemsSlideBy] = BreakPointItems.split('-'); 1400 1401 ResponsiveSettings[BreakPoints[BreakPoint]] = 1402 { 1403 items: parseInt(ItemsSlideBy,10), 1404 slideBy: parseInt(ItemsSlideBy,10), 1405 stagePadding: parseInt(StagePadding,10) 1406 }; 1407 }); 1408 return ResponsiveSettings; 1409 } 1410 1411//////////////////////////////////////////////////////////////////////////////// 1412/////// /////// /////// /////// /////// /////// /////// /////// //////// 1413//////////////////////////////////////////////////////////////////////////////// 1414 1415 function SET_DeliveryToast(Message) 1416 { 1417 const $ToastBox = document.getElementById('ToastBox'); 1418 if($ToastBox.children.length > 0) 1419 {
1420 [...$ToastBox.children].forEach(toast => toast.remove()); 1421 } 1422 const ToastHTML = `<div class="toast align-items-center text-bg-primary border-0" role="alert" aria-live="assertive" aria-atomic="true"> 1423 <div class="d-flex flex-column"> 1424 <div class="toast-header"> 1425 <img height="15px" src="${RootPath()}/images/svg/BoxYellow.svg" class="rounded me-2" alt="Info livraison"> 1426 <strong class="me-auto">Infos livraison</strong> 1427 <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close">×</button> 1428 </div> 1429 <div class="toast-body ShippingInfo FreeShipping"> 1430 ${Message} 1431 </div> 1432 </div> 1433 </div>`; 1434 $ToastBox.insertAdjacentHTML('beforeend', ToastHTML); 1435 const $ToastItem = $ToastBox.lastElementChild; 1436 const $Toast = new bootstrap.Toast($ToastItem, { delay: 5000 }); 1437 $Toast.show(); 1438 $ToastItem.addEventListener('hidden.bs.toast', function() 1439 { 1440 $ToastItem.remove(); 1441 }); 1442 } 1443 1444//////////////////////////////////////////////////////////////////////////////// 1445/////// /////// /////// /////// /////// /////// /////// /////// //////// 1446//////////////////////////////////////////////////////////////////////////////// 1447 1448 function UpdateCartFunctions() 1449 { 1450 $('.Zoom').zoom({touch: false}); 1451 var $Thumbs = $('.ThumbNailZoom'), 1452 SyncedSecondary = true; 1453 $Thumbs.owlCarousel( 1454 { 1455 margin: 20, 1456 dots: false, 1457 nav: true, 1458 navText: SlideArrows(), 1459 loop: false, 1460 smartSpeed: 200, 1461 slideSpeed: 500, 1462 slideBy: 1, 1463 responsiveRefreshRate: 100, 1464 responsive: SET_ItemQuantity('xs-3-sp50 sm-3-sp50 md-3-sp0 lg-4-sp0 xl-4-sp0') 1465 }); 1466 RemoveHandSwipeImg('.ThumbNailZoom'); 1467 RecipesSlide(3); 1468 ProductInProductSlide(4); 1469 $Thumbs.on('click', 'img', function(e) 1470 { 1471 var $That = $(this), 1472 $BigImage = $That.closest('.ProductZoomImg'); 1473 $BigImage.find('.Zoom').addClass('LoadingImg'); 1474 $BigImage.find('.Zoom').trigger('zoom.destroy'); 1475 $BigImage.find('.Zoom img').attr('src',$That.attr('src').replace('mini_','700_')); 1476 $BigImage.find('.Zoom img').attr('data-src',$That.attr('src').replace('mini_','700_')); 1477 $BigImage.find('.Zoom figcaption').text($That.attr('alt')); 1478 setTimeout(function() 1479 { 1480 $('.Zoom').zoom({touch: false}); 1481 ImagesLoaded(); 1482 }, 50); 1483 }); 1484 1485 $(document).on('change', 'input[name="PixProductPackaging[]"], input[name^="PixProductPackaging["][name$="]"]', function(e) 1486 { 1487// console.log(this); 1488 if($(this).hasClass('Page')) 1489 { 1490 let IsChecked = $(this).prop('checked'); 1491 $('input.PixProductPackaging:not(.Modal)[value="'+this.value+'"]').each(function() 1492 { 1493 $(this).prop('checked', IsChecked); 1494 }); 1495 } 1496 1497 var $That = $(this), 1498 Img = $That.val(), 1499 $Form = $That.closest('form'), 1500 FormId = $Form.attr('data-form-id'), 1501 $Forms = $('[data-form-id="'+FormId+'"]'), 1502 Price = $That.attr('data-price'), 1503 Name = $That.attr('data-name'), 1504 Gtin = $That.attr('data-gtin'), 1505 OldPrice = $That.attr('data-old-price'), 1506 BioLabel = $That.attr('data-bio-label'), 1507 PackagingWeight = $That.attr('data-packaging-weight'), 1508 Weight = IsNotEmpty($That.attr('data-weight')) ? $That.attr('data-weight') : 0, 1509 TotalWeight = IsNotEmpty($That.attr('data-weight')) ? parseInt(PackagingWeight,10) : 0, 1510 Capacity = $That.attr('data-capacity'), 1511 Taxe = SET_Taxes($That.attr('data-taxe')), 1512 Promo = $That.attr('data-promo'), 1513 Unlabelled = ['Eco'], 1514 Label = $That.attr('data-label') !== '1' || Unlabelled.includes(Img)? 'Conditionnement sans étiquette' : 'Conditionnement avec étiquette', 1515 Url = FormatUrl($Form, Img), 1516 $PriceZone = $That.closest('.ProductZone').find('.ProductPriceZone'), 1517 $PriceZoneMenu = $That.closest('#Sticky_buy_Block').find('.ProductPriceZone'), 1518 EuroSymbol = '<sup itemprop="priceCurrency" content="EUR" class="Euro">â¬</sup>', 1519 PriceLabel = Price.split('.'), 1520 HTMLPrice = IsNotEmpty(PriceLabel[1]) ? PriceLabel[0]+'<span class="Decimal">.'+PriceLabel[1] : PriceLabel[0],
1521 PriceLabel = '<span class="" itemprop="price" content="'+Price+'">'+HTMLPrice+EuroSymbol+'</span>', 1522 $ProductLongDetail = $That.closest('.ProductLongDetail'); 1523 1524 $PriceZone.attr('content',Price).find('.Price').html(PriceLabel); 1525 $PriceZoneMenu.attr('content',Price).find('.Price').html(PriceLabel); 1526 $('#image-block .price').html(PriceLabel); 1527 $ProductLongDetail.find('.Gramme').text(Weight+'g'); 1528 $ProductLongDetail.find('.Capacity').text(Capacity+'oz'); 1529 $ProductLongDetail.find('.GtinNumber').text(Gtin); 1530 $('#CenterColumn.ProductLongDetail').attr('data-bio-label', BioLabel); 1531 $('.FakeBox.LabelTemplate.Box').attr('data-packaging', Img.replace('Eco','Zip')); 1532 1533 $Forms.each(function(i) 1534 { 1535 let $Form = $(this); 1536 $Form.find('.snipcart-add-item[data-item-custom1-value]').attr('data-item-custom1-value', Img); 1537 $Form.find('.snipcart-add-item[data-item-custom2-value]').attr('data-item-custom2-value', Weight); 1538 $Form.find('.snipcart-add-item[data-item-custom3-value]').attr('data-item-custom3-value', Capacity); 1539 $Form.find('.snipcart-add-item[data-item-custom4-value]').attr('data-item-custom4-value', Label); 1540 $Form.find('.snipcart-add-item[data-item-price]').attr('data-item-price', Price.replace(',', '.')); 1541 $Form.find('.snipcart-add-item[data-item-taxes]').attr('data-item-taxes', Taxe); 1542 if(parseInt(PackagingWeight,10)=== 0) 1543 { 1544 $Form.find('.snipcart-add-item[data-item-name]').attr('data-item-name', Name); 1545 } 1546 $Form.find('.snipcart-add-item[data-item-weight]').attr('data-item-weight', TotalWeight); 1547 $Form.find('.snipcart-add-item[data-item-url]').attr('data-item-url', Url); 1548 }); 1549 if(IsNotEmpty(Promo) && Promo !== '0') 1550 { 1551 $ProductLongDetail.find('.reduction_percent_display').text('-'+Promo+'%').show(); 1552// $ProductLongDetail.find('.ProductDetailBox .old-price .OldPrice').html(OldPrice+'<sup class="Euro">â¬</sup>').closest('.old-price').show(); 1553// console.log($ProductLongDetail.find('[data-old-price].price').length); 1554 $ProductLongDetail.find('.ProductDetailBox [data-old-price]').attr('data-old-price', OldPrice+'â¬'); 1555 $ProductLongDetail.find('[data-old-price].price').attr('data-old-price', OldPrice+'â¬'); 1556 $('#Sticky_buy_Block [data-old-price]').attr('data-old-price', OldPrice+'â¬'); 1557 } 1558 else 1559 { 1560 $ProductLongDetail.find('.reduction_percent_display').hide(); 1561 $That.closest('.ProductLongDetail').find('.ProductDetailBox .old-price').hide(); 1562 $ProductLongDetail.find('.ProductDetailBox [data-old-price]').attr('data-old-price', ''); 1563 $ProductLongDetail.find('[data-old-price].price').attr('data-old-price', ''); 1564 $('#Sticky_buy_Block [data-old-price]').attr('data-old-price', ''); 1565 } 1566 }); 1567 1568 1569 $(document).on('click', '#snipcart, .snipcart__box--header button[type="button"]', function(e) 1570 { 1571 StickyPaymentStepsButtons('Clic sur modifier'); 1572 }); 1573 setTimeout(function() 1574 { 1575 $('.product_page').addClass('Hide50'); 1576 }, 100); 1577 setTimeout(function() 1578 { 1579 $('.product_page').removeClass('Hide Hide50'); 1580 }, 1000); 1581 } 1582 1583//////////////////////////////////////////////////////////////////////////////// 1584/////// /////// /////// /////// /////// /////// /////// /////// //////// 1585//////////////////////////////////////////////////////////////////////////////// 1586 1587 function IsLocalStorageAvailable() 1588 { 1589 var test = 'test'; 1590 try 1591 { 1592 localStorage.setItem(test, test); 1593 localStorage.removeItem(test); 1594 return true; 1595 } 1596 catch(e) 1597 { 1598 return false; 1599 } 1600 } 1601 1602//////////////////////////////////////////////////////////////////////////////// 1603/////// /////// /////// /////// /////// /////// /////// /////// //////// 1604//////////////////////////////////////////////////////////////////////////////// 1605 1606 function TrimOldEntries(Data, limit = 30) 1607 { 1608 const Entries = Object.entries(Data).sort(([, a], [, b]) => new Date(a.date) - new Date(b.date)), 1609 TrimmedEntries = Entries.slice(-limit); 1610 return Object.fromEntries(TrimmedEntries); 1611 } 1612 1613//////////////////////////////////////////////////////////////////////////////// 1614/////// /////// /////// /////// /////// /////// /////// /////// //////// 1615//////////////////////////////////////////////////////////////////////////////// 1616 1617 function GET_ProductsPackageTemplates(Datas) 1618 { 1619// console.log(Datas); 1620 $.ajax({ 1621 type: 'POST', url: RootPath()+'/Ajax.php', dataType: "json", 1622 data: { GetPackageTemplate: true, PackageDatas:TrimOldEntries(Datas)}, 1623 success: function(x) 1624 { 1625 $('#ViewedProducts').find('.SF-ViewedProductSlide').append(x.HTML); 1626 setTimeout(function() 1627 { 1628 let $ViewedProductSlide = $('.SF-ViewedProductSlide'); 1629 $ViewedProductSlide.owlCarousel({ 1630 loop: false, 1631 margin: 0, 1632 nav: true, 1633 autoplay: false, 1634 navText: SlideArrows(), 1635 dots: false, 1636 lazyLoad: true, 1637 responsive: SET_ItemQuantity('xs-3-sp0 sm-3-sp0 md-6-sp0 lg-6-sp0 xl-8-sp0') 1638 }); 1639 }, 100); 1640 } 1641 }); 1642 } 1643 1644//////////////////////////////////////////////////////////////////////////////// 1645/////// /////// /////// /////// /////// /////// /////// /////// //////// 1646//////////////////////////////////////////////////////////////////////////////// 1647 1648 function StoreLogsOnError(Issue, SnipCartState) 1649 { 1650 $.ajax({ 1651 type: 'POST', url: RootPath()+'/Ajax.php', dataType: "json", 1652 data: { ErrorLogs: true, Errors:JSON.stringify(Issue), State:JSON.stringify(SnipCartState)}, 1653 success: function(x) 1654 { 1655 console.log(Issue); 1656 }, 1657 error: function(){ console.log(Issue); } 1658 }); 1659 } 1660 1661//////////////////////////////////////////////////////////////////////////////// 1662/////// /////// /////// /////// /////// /////// /////// /////// //////// 1663//////////////////////////////////////////////////////////////////////////////// 1664 1665 function StoreViewedProduct() 1666 { 1667 if(IsLocalStorageAvailable()) 1668 { 1669 let $ProductBtn = $('.snipcart-add-item:not(.NoViewedProduct)'), 1670 ViewedProduct = localStorage.getItem('ViewedProduct') === null ? {} : JSON.parse(localStorage.getItem('ViewedProduct')); 1671 if($ProductBtn.length) 1672 { 1673 $ProductBtn.each(function(i) 1674 { 1675 let $That = $(this), 1676 ProductUrl = $That.attr('data-item-url'), 1677 Productid = $That.attr('data-item-id'), 1678 ProductCatId = $That.parent().attr('data-id-categorie'), 1679 ProductSubCat = $That.parent().attr('data-sub-cat'), 1680 ProductGroup = $That.parent().attr('data-group'), 1681 ProductPacks = $That.parent().attr('data-packages'), 1682 ProductPack = $That.attr('data-item-custom1-value'); 1683 1684 ViewedProduct[Productid] = {}; 1685 ViewedProduct[Productid]['IdCat'] = ProductCatId; 1686 ViewedProduct[Productid]['SubCat'] = ProductSubCat; 1687 ViewedProduct[Productid]['Group'] = ProductGroup; 1688 ViewedProduct[Productid]['Package'] = ProductPack; 1689 ViewedProduct[Productid]['Packages'] = ProductPacks; 1690// ViewedProduct[Productid]['img'] = $That.attr('data-item-image'); 1691 ViewedProduct[Productid]['url'] = ProductUrl; 1692 ViewedProduct[Productid]['name'] = $That.attr('data-item-name'); 1693 ViewedProduct[Productid]['date'] = new Date(); 1694 ViewedProduct[Productid]['status'] = 'Viewed'; 1695 localStorage.setItem('ViewedProduct', JSON.stringify(ViewedProduct)); 1696 }); 1697 } 1698 } 1699 } 1700 1701//////////////////////////////////////////////////////////////////////////////// 1702/////// /////// /////// /////// /////// /////// /////// /////// //////// 1703//////////////////////////////////////////////////////////////////////////////// 1704 1705 function UpdateViewedProduct(ProductId, Status) 1706 { 1707 let $ViewedProducts = $('#ViewedProducts'),
1708 IsViewedProductActive = $ViewedProducts.parent().find('.snipcart-add-item.NoViewedProduct').length ? false : true, 1709 ViewedProducts = JSON.parse(localStorage.getItem('ViewedProduct')); 1710 if(IsViewedProductActive && $ViewedProducts.length && localStorage.getItem('ViewedProduct') !== null) 1711 { 1712 if(IsNotEmpty(ViewedProducts[ProductId]) && IsNotEmpty(ViewedProducts[ProductId]['status'])) 1713 { 1714 ViewedProducts[ProductId]['status'] = Status === 'Add' ? 'AddToCart' : 'Viewed'; 1715 localStorage.setItem('ViewedProduct', JSON.stringify(ViewedProducts)); 1716 DisplayViewedProducts(); 1717 } 1718 } 1719 } 1720 1721//////////////////////////////////////////////////////////////////////////////// 1722/////// /////// /////// /////// /////// /////// /////// /////// //////// 1723//////////////////////////////////////////////////////////////////////////////// 1724 1725 function DisplayViewedProducts() 1726 { 1727 let $ViewedProducts = $('#ViewedProducts'), 1728 ViewedProducts = JSON.parse(localStorage.getItem('ViewedProduct')), 1729 TotalProducts = IsNotEmpty(ViewedProducts) ? Object.keys(ViewedProducts).length : 0; 1730 if($ViewedProducts.length && localStorage.getItem('ViewedProduct') !== null) 1731 { 1732 $ViewedProducts.find('.SF-ViewedProductSlide').empty(); 1733 $('.SF-ViewedProductSlide').owlCarousel('destroy'); 1734 GET_ProductsPackageTemplates(ViewedProducts); 1735 $ViewedProducts.find('.owl-stage:empty').remove(); 1736 $ViewedProducts.removeClass('d-none'); 1737 } 1738 else 1739 { 1740 $ViewedProducts.addClass('d-none'); 1741 } 1742 } 1743 1744//////////////////////////////////////////////////////////////////////////////// 1745/////// /////// /////// /////// /////// /////// /////// /////// //////// 1746//////////////////////////////////////////////////////////////////////////////// 1747 1748 function EstimateShipping(Snipcart) 1749 { 1750 const state = Snipcart.store.getState(); 1751 var totalWeight = state.cart.items.items.reduce((acc, item) => { 1752 return acc + item.quantity * item.dimensions.weight; 1753 }, 0); 1754 if(totalWeight > 0) 1755 { 1756 $.get(RootPath()+'/Shipping/ShippingSimulator.php?Simulate='+totalWeight, function(ShippingTax) 1757 { 1758 $('.ShippingInfo small, .ShippingEvaluation small').append(' Ãconomisez <strong>'+parseFloat(ShippingTax).toFixed(2).replace(/\.00/,'')+'<sup>â¬</sup></strong> de frais de livraison en France métropolitaine, Andorre et Monaco (estimation). <a href="'+RootPath()+'/cgv.php#CGV_Livraison" target="_blank"><small><u>Consultez les tarifs de livraison</u></small></a>'); 1759 }, 'text'); 1760 } 1761 } 1762 1763/////////////////////////////////////////////////////////////////////////////// 1764/////// /////// /////// /////// /////// /////// /////// /////// //////// 1765//////////////////////////////////////////////////////////////////////////////// 1766 1767 function SetShippingDates() 1768 { 1769 let today = new Date(); 1770 function addDays(date, days) 1771 { 1772 let result = new Date(date); 1773 result.setDate(result.getDate() + days); 1774 if(result.getDay() === 0) 1775 { 1776 result.setDate(result.getDate() + 1); 1777 } 1778 return result; 1779 } 1780 let handlingTime = 24; 1781 if(today.getDay() === 0) 1782 { 1783 handlingTime = 48; 1784 } 1785 else if(today.getDay() === 6) 1786 { 1787 handlingTime = 72; 1788 } 1789 let minDeliveryDate = new Date(today); 1790 minDeliveryDate.setHours(minDeliveryDate.getHours() + handlingTime); 1791 1792 if(minDeliveryDate.getDay() === 0) { 1793 minDeliveryDate.setDate(minDeliveryDate.getDate() + 1); 1794 } 1795 1796 let maxDeliveryDate = addDays(minDeliveryDate, 5); 1797 1798 function formatDate(date) 1799 { 1800 const days = ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi']; 1801 const months = ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre']; 1802 let dayName = days[date.getDay()]; 1803 let day = date.getDate(); 1804 let month = months[date.getMonth()]; 1805 1806 return { dayName, day, month }; 1807 } 1808 1809 let minFormattedDate = formatDate(minDeliveryDate); 1810 let maxFormattedDate = formatDate(maxDeliveryDate); 1811 1812 if(minFormattedDate.month === maxFormattedDate.month) { 1813 return `le ${minFormattedDate.dayName} ${minFormattedDate.day} et le ${maxFormattedDate.dayName} ${maxFormattedDate.day}
1813 ${minFormattedDate.month}`; 1814 } else { 1815 return `le ${minFormattedDate.dayName} ${minFormattedDate.day} ${minFormattedDate.month} et le ${maxFormattedDate.dayName} ${maxFormattedDate.day} ${maxFormattedDate.month}`; 1816 } 1817 } 1818 1819//////////////////////////////////////////////////////////////////////////////// 1820/////// /////// /////// /////// /////// /////// /////// /////// //////// 1821//////////////////////////////////////////////////////////////////////////////// 1822 1823 function SetFreeShipping(Snipcart) 1824 { 1825 setTimeout(function() 1826 { 1827 const TotalPrice = Snipcart.store.getState().cart.total; 1828 if(!$('.ShippingInfo').length) 1829 { 1830 $('.snipcart-cart__footer').before('<div class="ShippingInfo"></div>') 1831 } 1832 let IsDigitalGoods = ($('form[data-form-id="GiftCard"]').length || $('form[data-form-id="Atelier"]').length) ? true : false, 1833 FreeShipLimit = 65, 1834 RestFreeShipping = TotalPrice < FreeShipLimit ? FreeShipLimit - TotalPrice : 0, 1835 $ShippingInfo = IsDigitalGoods ? $('.ShippingInfo') : $('.ShippingInfo, .ShippingEvaluation'), 1836 $ShippingNotice = $('.snipcart-summary-fees__notice'), 1837 $ShippingInfoCart = $('.snipcart-loading-overlay__content .snipcart-summary-fees'), 1838 ShippingMessage = '<p class="fw-normal">Livré en point de retrait Mondial Relay entre <strong>'+SetShippingDates()+'</strong><br><small>(pour toute livraison en France Métropolitaine)</small></p><p class="fw-normal mt-2">Les colis sont <strong>personnalisés manuellement</strong> par notre équipe qui sâefforcera de les expédier en <strong>moins de 48h</strong>.</p>', 1839 ShippingStatus = ''; 1840 if(RestFreeShipping > 0) 1841 { 1842 if(RestFreeShipping < 30) 1843 { 1844 ShippingStatus = 'Plus que <span>'+RestFreeShipping.toFixed(2).replace(/\.00/,'')+'<sup>â¬</sup></span> avant la livraison offerte.'+ShippingMessage; 1845 $ShippingInfo.addClass('FreeShipping').html(ShippingStatus); 1846 } 1847 else 1848 { 1849 ShippingStatus = 'La livraison offerte dès '+FreeShipLimit+'<sup>â¬</sup> dâachat.'+ShippingMessage; 1850 $ShippingInfo.addClass('FreeShipping').html(ShippingStatus); 1851 } 1852 $ShippingNotice.show(); 1853 EstimateShipping(Snipcart); 1854 } 1855 else 1856 { 1857 ShippingStatus = '<b class="ScriptFont">Félicitation !</b> La livraison vous est offerte. '+ShippingMessage; 1858 $ShippingInfo.addClass('FreeShipping').html(ShippingStatus); 1859 $ShippingNotice.hide(); 1860 } 1861 SET_DeliveryToast(ShippingStatus); 1862 1863 const state = Snipcart.store.getState(); 1864 var totalWeight = state.cart.items.items.reduce((acc, item) => { 1865 return acc + item.quantity * item.dimensions.weight; 1866 }, 0); 1867 }, 100); 1868 } 1869 1870//////////////////////////////////////////////////////////////////////////////// 1871/////// /////// /////// /////// /////// /////// /////// /////// //////// 1872//////////////////////////////////////////////////////////////////////////////// 1873 1874 function SET_OrderErrorMessage(Msg) 1875 { 1876 let Li = ''; 1877 if(Msg === 'domain_crawling') 1878 { 1879 Li = '<li class="Check"><b><u>Vous nâavez pas été débité.</u></b></li>'; 1880 } 1881 return '<span class="text-black fw-bold text-uppercase mt-2 fs-6">Pas de panique ! 😌</span><ul>'+Li+'<li class="Check">Notre service web hyper réactif (si, si !) vient dâêtre averti de cette erreur et vous contactera si nécessaire.</li><li class="Check">Vous pouvez contacter lâéquipe de Sacré français au <a class="LinkTel d-inline-flex" href="tel:+33652202613"><img class="me-1" height="15" src="'+RootPath()+'/images/svg/PhoneWhite.svg" alt="Téléphone">06 52 20 26 13</a> pour plus de détails ou pour passer commande si câest une urgence (la gourmandise nâattend pas !).</li></ul>'; 1882 } 1883 1884//////////////////////////////////////////////////////////////////////////////// 1885/////// /////// /////// /////// /////// /////// /////// /////// //////// 1886//////////////////////////////////////////////////////////////////////////////// 1887 1888 function SetAnimatedSlide(e) 1889 { 1890 $(e.target).find('.owl-item .slide').removeClass('opacity-0'); 1891 var $ActiveSlide = $(e.target).find('.owl-item.active .SlideHomeAnimate'), 1892 $InactiveSlide = $(e.target).find('.owl-item:not(.active) .SlideHomeAnimate'); 1893 $ActiveSlide.removeClass('d-none').addClass('d-flex animated fadeInRight'); 1894 setTimeout(function() 1895 { 1896 $ActiveSlide.find('div').first().addClass('animated fadeInLeft'); 1897 $InactiveSlide.removeClass('d-flex animated fadeInRight').addClass('d-none'); 1898 $InactiveSlide.find('.animated').removeClass('animated fadeInLeft'); 1899 }, 100); 1900 setTimeout(function() 1901 { 1902 $(e.target).find('.owl-item .slide').css({'height':$(e.target).find('.owl-item.active').outerHeight()+'px'}); 1903 }, 110); 1904 } 1905 1906//////////////////////////////////////////////////////////////////////////////// 1907/////// /////// /////// /////// /////// /////// /////// /////// //////// 1908//////////////////////////////////////////////////////////////////////////////// 1909 1910 $('#CopyLink').on('click', function(e) 1911 { 1912 e.preventDefault(); 1913 const linkToCopy = $(this).data('link'); 1914 navigator.clipboard.writeText(linkToCopy).then(function() 1915 { 1916 $('.SvgShareBtn').toggleClass('d-none').parent().find('.CopyLinkText').text('Copié !'); 1917 setTimeout(function() 1918 { 1919 $('.SvgShareBtn').toggleClass('d-none').parent().find('.CopyLinkText').text('Copier le lien'); 1920 }, 2000); 1921 }).catch(function(err) 1922 {
1923 console.error('Erreur lors de la copie :', err); 1924 }); 1925 }); 1926 1927//////////////////////////////////////////////////////////////////////////////// 1928/////// /////// /////// /////// /////// /////// /////// /////// //////// 1929//////////////////////////////////////////////////////////////////////////////// 1930 1931 if($('nav.mobilemenu__nav').length) 1932 { 1933 $('nav.mobilemenu__nav').meanmenu({ 1934 meanMenuClose: '×', 1935 meanMenuCloseSize: '18px', 1936 meanScreenWidth: '575', 1937 meanExpandableChildren: true, 1938 meanMenuContainer: '.mobile-menu', 1939 onePage: true 1940 }); 1941 } 1942 1943//////////////////////////////////////////////////////////////////////////////// 1944/////// /////// /////// /////// /////// /////// /////// /////// //////// 1945//////////////////////////////////////////////////////////////////////////////// 1946 1947 $('.SlideInBlog').owlCarousel({ 1948 loop:true, 1949 margin:50, 1950 nav:true, 1951 autoplay: true, 1952 autoplayTimeout: 10000, 1953 items:1, 1954 navText: SlideArrows(), 1955 dots: false, 1956 lazyLoad: true, 1957 }); 1958 RemoveHandSwipeImg('.SlideInBlog'); 1959 1960//////////////////////////////////////////////////////////////////////////////// 1961/////// /////// /////// /////// /////// /////// /////// /////// //////// 1962//////////////////////////////////////////////////////////////////////////////// 1963 1964 $('#ReviewSlides').owlCarousel({ 1965 loop: true, 1966 margin: 50, 1967 nav: true, 1968 autoplay: true, 1969 autoplayTimeout: 10000, 1970 autoHeight: true, 1971 items: 3, 1972 navText: SlideArrows(), 1973 dots: false, 1974 lazyLoad: true, 1975 onInitialized: function(e) 1976 { 1977 $('#ReviewSlides').removeClass('opacity-0'); 1978 }, 1979 responsive: { 1980 0:{ 1981 items:1, 1982 slideBy: 1 1983 }, 1984 576:{ 1985 items:2, 1986 slideBy: 2 1987 }, 1988 768:{ 1989 items:2, 1990 slideBy: 2 1991 }, 1992 992:{ 1993 items:3, 1994 slideBy: 3 1995 }, 1996 1920:{ 1997 items:4, 1998 slideBy: 4 1999 } 2000 } 2001 }); 2002 RemoveHandSwipeImg('#ReviewSlides'); 2003 2004//////////////////////////////////////////////////////////////////////////////// 2005/////// /////// /////// /////// /////// /////// /////// /////// //////// 2006//////////////////////////////////////////////////////////////////////////////// 2007 2008 $('#BlogHomeList').owlCarousel({ 2009 loop: true, 2010 margin: 30, 2011 nav: true, 2012 autoplay: false, 2013 navText: SlideArrows(), 2014 dots: false, 2015 lazyLoad: true, 2016 onInitialized: function(e) 2017 { 2018 $('.SF_BlogArea').removeClass('opacity-0'); 2019 }, 2020 responsive: SET_ItemQuantity('xs-1-sp50 sm-1-sp50 md-2-sp100 lg-3-sp100 xl-4-sp100') 2021 }); 2022 2023//////////////////////////////////////////////////////////////////////////////// 2024/////// /////// /////// /////// /////// /////// /////// /////// //////// 2025//////////////////////////////////////////////////////////////////////////////// 2026 2027 $('.SlideInProduct').owlCarousel({ 2028 loop: true, 2029 lazyLoad: true, 2030 margin: 15, 2031 navigation: true, 2032 slideSpeed: 1000, 2033 nav: true, 2034 pagination: false, 2035 navText: SlideArrows(), 2036 dots: false, 2037 responsive: SET_ItemQuantity('xs-1-sp50 sm-3-sp50 md-3-sp0 lg-4-sp0 xl-4-sp0') 2038 }); 2039 2040//////////////////////////////////////////////////////////////////////////////// 2041/////// /////// /////// /////// /////// /////// /////// /////// //////// 2042//////////////////////////////////////////////////////////////////////////////// 2043 2044 $('.pos-topsellers').owlCarousel({ 2045 loop: true, 2046 lazyLoad: true, 2047 items: 1, 2048 navigation: true, 2049 slideSpeed: 1000, 2050 nav: true, 2051 pagination: false, 2052 navText: SlideArrows(), 2053 dots: false 2054 }); 2055 RemoveHandSwipeImg('.pos-topsellers'); 2056 2057//////////////////////////////////////////////////////////////////////////////// 2058/////// /////// /////// /////// /////// /////// /////// /////// //////// 2059//////////////////////////////////////////////////////////////////////////////// 2060 2061 $('.BookPages').owlCarousel({ 2062 loop: true, 2063 lazyLoad: true, 2064 items: 1, 2065 autoplay: true, 2066 autoplayTimeout: 10000, 2067 navigation: true, 2068 slideSpeed: 1000, 2069 nav: true, 2070 pagination: false, 2071 navText: SlideArrows(), 2072 dots: false 2073 }); 2074 RemoveHandSwipeImg('.BookPages'); 2075 2076//////////////////////////////////////////////////////////////////////////////// 2077/////// /////// /////// /////// /////// /////// /////// /////// //////// 2078//////////////////////////////////////////////////////////////////////////////// 2079 2080 $('.SF_SliderFeature').owlCarousel({ 2081 loop: true, 2082 margin: 0, 2083 nav: false, 2084 autoplay: true, 2085 autoplayTimeout: 10000, 2086 items: 6, 2087 navText: SlideArrows(), 2088 dots: false, 2089 lazyLoad: true, 2090 responsive: SET_ItemQuantity('xs-3-sp50 sm-3-sp50 md-5-sp0 lg-5-sp0 xl-5-sp0') 2091 }); 2092 RemoveHandSwipeImg('.SF_SliderFeature'); 2093 2094//////////////////////////////////////////////////////////////////////////////// 2095/////// /////// /////// /////// /////// /////// /////// /////// //////// 2096//////////////////////////////////////////////////////////////////////////////// 2097 2098 $('#ProductBoxesByLine').owlCarousel({ 2099 loop: true, 2100 margin: 0, 2101 nav: true, 2102 autoplay: false, 2103 autoplayTimeout: 10000, 2104 navText: SlideArrows(), 2105 dots: false, 2106 lazyLoad: true, 2107 responsive: SET_ItemQuantity('xs-3-sp0 sm-3-sp0 md-6-sp0 lg-6-sp0 xl-8-sp0') 2108 }); 2109 2110//////////////////////////////////////////////////////////////////////////////// 2111/////// /////// /////// /////// /////// /////// /////// /////// //////// 2112//////////////////////////////////////////////////////////////////////////////// 2113 2114 $('.BestProductSlider').owlCarousel({ 2115 loop: false, 2116 margin: 30, 2117 nav: true, 2118 autoplay: false, 2119 autoplayTimeout: 10000, 2120 items: 3, 2121 navText: SlideArrows(), 2122 dots: false, 2123 lazyLoad: true, 2124 responsive: SET_ItemQuantity('xs-2-sp50 sm-3-sp50 md-3-sp100 lg-4-sp100 xl-4-sp100') 2125 }); 2126// RemoveHandSwipeImg('.BestProductSlider'); 2127 2128//////////////////////////////////////////////////////////////////////////////// 2129/////// /////// /////// /////// /////// /////// /////// /////// //////// 2130//////////////////////////////////////////////////////////////////////////////// 2131 2132 $('.SF-NewProductSlide').owlCarousel({ 2133 loop: true, 2134 margin: 30, 2135 nav: true, 2136 autoplay: false, 2137 autoplayTimeout: 10000, 2138 items: 4, 2139 navText: SlideArrows(), 2140 dots: false, 2141 lazyLoad: true, 2142 onInitialized: function(e) 2143 { 2144 $('#NewProducts').removeClass('opacity-0'); 2145 }, 2146 responsive: SET_ItemQuantity('xs-1-sp50 sm-3-sp50 md-3-sp0 lg-4-sp0 xl-4-sp0') 2147 }); 2148// RemoveHandSwipeImg('.SF-NewProductSlide'); 2149 2150//////////////////////////////////////////////////////////////////////////////// 2151/////// /////// /////// /////// /////// /////// /////// /////// //////// 2152//////////////////////////////////////////////////////////////////////////////// 2153 2154 RecipesSlide(2); 2155 ProductInProductSlide(3); 2156 2157//////////////////////////////////////////////////////////////////////////////// 2158/////// /////// /////// /////// /////// /////// /////// /////// //////// 2159//////////////////////////////////////////////////////////////////////////////// 2160 2161 $('.SF-MarketSlide').owlCarousel({ 2162 loop: true, 2163 margin: 30, 2164 nav: true, 2165 autoplay: false, 2166 autoplayTimeout: 10000, 2167 navText: SlideArrows(), 2168 dots: false, 2169 lazyLoad: true, 2170 responsive: SET_ItemQuantity('xs-1-sp50 sm-1-sp50 md-2-sp100 lg-3-sp100 xl-4-sp100') 2171 }); 2172 RemoveHandSwipeImg('.SF-MarketSlide'); 2173 2174//////////////////////////////////////////////////////////////////////////////// 2175/////// /////// /////// /////// /////// /////// /////// /////// //////// 2176//////////////////////////////////////////////////////////////////////////////// 2177 2178 $('.SF-ShopCategorySlide').owlCarousel({ 2179 loop: true, 2180 margin: 0, 2181 nav: false, 2182 autoplay: false, 2183 dots: false, 2184 lazyLoad: true, 2185 responsive: SET_ItemQuantity('xs-1-sp50 sm-1-sp50 md-2-sp100 lg-2-sp100 xl-3-sp100') 2186 }); 2187 RemoveHandSwipeImg('.SF-ShopCategorySlide'); 2188 2189//////////////////////////////////////////////////////////////////////////////// 2190/////// /////// /////// /////// /////// /////// /////// /////// //////// 2191//////////////////////////////////////////////////////////////////////////////// 2192 2193 SearchToggler(); 2194 2195//////////////////////////////////////////////////////////////////////////////// 2196/////// /////// /////// /////// /////// /////// /////// /////// //////// 2197//////////////////////////////////////////////////////////////////////////////// 2198 2199 $('.SlideHome:not(.WeCanDoo)').owlCarousel({ 2200 loop: true, 2201 margin: 0, 2202 nav: true, 2203 autoplay: true, 2204 autoplayHoverPause: true, 2205 autoplayTimeout: 5000, 2206 smartSpeed: 500, 2207 items: 1, 2208 navText: SlideArrows(), 2209 dots: false, 2210 lazyLoad: true, 2211 onInitialized: function(e) 2212 { 2213 SetAnimatedSlide(e); 2214 }, 2215 onTranslated: function(e) 2216 { 2217 SetAnimatedSlide(e); 2218 }, 2219 responsive: SET_ItemQuantity('xs-1-sp50 sm-1-sp50 md-1-sp0 lg-1-sp0 xl-1-sp0') 2220 }); 2221 2222//////////////////////////////////////////////////////////////////////////////// 2223/////// /////// /////// /////// /////// /////// /////// /////// //////// 2224//////////////////////////////////////////////////////////////////////////////// 2225
2226 let $LoadMoreBtn = $('#LoadMoreItems'), 2227 $Filter = $('#FilterBlock'), 2228 HasScrollBar = $Filter.length && $Filter.hasScrollBar() ? $Filter.addClass('HasScrollBar') : '', 2229 LastScrollTop = 0, 2230 Processing, 2231 $ImageBlock = $('.ProductZoomImg'), 2232 $ProductZone = $ImageBlock.parent().find('.ProductZone'), 2233 MainMenuHeight = 110, 2234 productZoneBottom = $ProductZone.length ? $ProductZone.offset().top + $ProductZone.outerHeight() : 0, 2235 imageBlockTop = $ImageBlock.length ? $ImageBlock.offset().top : 0, 2236 imageBlockHeight = $ImageBlock.outerHeight(), 2237 windowWidth = $(window).width(); 2238 2239 $(window).on('scroll touchmove resize',function() 2240 { 2241 var scroll = $(window).scrollTop(), 2242 FilterWidth = $Filter.outerWidth(), 2243 IsMobile = $('.mobile-menu').is(':visible') ? true : false, 2244 FixedFilterTop = IsMobile ? 60 : 100, 2245 HeaderHeight = $('#sf__header').height(), 2246 HeadBannerHeight = $('#page .header-container').height(), 2247 FooterTop = parseInt($('#ProductsList').outerHeight() - $('#Partners').outerHeight(),10), 2248 H_StickyMenu = $('#MainMenu.StickyMenu').outerHeight(), 2249 $BuyForm = $('.SingleProductPage #buy_block'), 2250 $ImgBlock = $('#image-block'), 2251 ImgBlock = $ImgBlock.offset(), 2252 BuyForm = $BuyForm.offset(), 2253 H_BuyForm = $BuyForm.outerHeight(), 2254 $StickyBuyForm = $('.SingleProductPage #MainMenu #Sticky_buy_Block'); 2255 if(scroll !== LastScrollTop) 2256 { 2257 $('.SF_HeaderTheme .mean-container.mobile-menu').addClass('Scrolling'); 2258 } 2259 if($(window).width() <= 575) 2260 { 2261 $.doTimeout('scroll', 250, function() 2262 { 2263 $('.SF_HeaderTheme .mean-container.mobile-menu').removeClass('Scrolling'); 2264 }); 2265 LastScrollTop = scroll; 2266 } 2267 if($BuyForm.length && scroll > BuyForm.top + H_BuyForm - H_StickyMenu) 2268 { 2269 $StickyBuyForm.slideDown(250); 2270 } 2271 else 2272 { 2273 $StickyBuyForm.slideUp(250); 2274 } 2275 if(scroll < HeaderHeight) 2276 { 2277 $('#MainMenu, #CustomMenu').removeClass('StickyMenu'); 2278 $('#sf__header').css('margin-bottom',''); 2279 } 2280 else 2281 { 2282 $('#MainMenu, #CustomMenu').addClass('StickyMenu'); 2283 $('#sf__header').css('margin-bottom','120px'); 2284 } 2285 if(Processing) 2286 { 2287 return false; 2288 } 2289 if($(window).scrollTop() >= ($(document).height() - $(window).height())*0.3 && $LoadMoreBtn.length && !$('#FilterBlock input[type="checkbox"]:checked').length) 2290 { 2291 Processing = true; 2292 $('#LoadMoreItems').addClass('Loading'); 2293 $.ajax({ 2294 type: 'POST', url: RootPath()+'/Ajax.php', dataType: "json", 2295 data: { LoadMore: true, IdCat: GetProductCatId(), NotIn: GetProductIds(), Blog: IsItBlog(), Filter: FilterSelect() }, cache: false, 2296 success: function(x) 2297 { 2298 setTimeout(function() 2299 { 2300 if(x.Total > 0) 2301 { 2302 Processing = false; 2303 $('#ProductsList').append(x.HTML); 2304 ImagesLoaded(); 2305 LoadingEnding(); 2306 } 2307 else 2308 { 2309 $LoadMoreBtn.remove(); 2310 ImagesLoaded(); 2311 } 2312 }, 250); 2313 }, 2314 error: function() 2315 { 2316 Processing = false; 2317 ErrorAjax('Une erreur est survenue.'); 2318 LoadingEnding(); 2319 } 2320 }); 2321 } 2322 }); 2323 2324//////////////////////////////////////////////////////////////////////////////// 2325/////// /////// /////// /////// /////// /////// /////// /////// //////// 2326//////////////////////////////////////////////////////////////////////////////// 2327 2328 $(document).on('change', '#FilterBlock:not(.PrintLabel) input[type="checkbox"]', function(e) 2329 { 2330 $('#FilterBlock input[type="checkbox"]').not(this).prop('checked', false); 2331 var $That = $(this).parent(), 2332 $Input = $That; 2333 if($Input.is(':checked')) 2334 { 2335 $That.removeClass('load'); 2336 } 2337 else 2338 { 2339 $That.addClass('load'); 2340 if($LoadMoreBtn.length && $('#FilterBlock:not(.PrintLabel) input[type="checkbox"]:checked').length) 2341 { 2342 $.ajax({ 2343 type: 'POST', url: RootPath()+'/Ajax.php', dataType: "json", 2344 data: { LoadMoreFilter: true, IdCat: GetProductCatId(), NotIn: GetProductIds(), Filter: GetProductFilter(), Blog: IsItBlog(), SearchFilter: FilterSelect() }, cache: false, 2345 success: function(x) 2346 { 2347 Processing = false; 2348 $('#ProductsList').append($(x.HTML)); 2349 FilteringItems($That); 2350 }, 2351 error: function() 2352 { 2353 Processing = false;
2354 ErrorAjax('Une erreur est survenue.'); 2355 } 2356 }); 2357 } 2358 else 2359 { 2360 FilteringItems($That); 2361 } 2362 } 2363 }).on('change', '#ContactModal input.UserStatus.radio', function(e) 2364 { 2365 var $ProForm = $('#ContactModal fieldset.ProContact'), 2366 $AutoFillInputs = $('.HideInput'), 2367 $ProInputs = $ProForm.find('input'), 2368 $BtnSendContact = $('button.SendContact'); 2369 if(this.value == 'Professionnel') 2370 { 2371 $AutoFillInputs.hide(); 2372 $BtnSendContact.prop('disabled', true); 2373 $ProForm.fadeIn(); 2374 $ProInputs.prop('required', true); 2375 } 2376 else 2377 { 2378 $AutoFillInputs.show(); 2379 $BtnSendContact.prop('disabled', false); 2380 $ProForm.fadeOut(); 2381 $ProInputs.val('').prop('required', false); 2382 } 2383 }).on('change', '#ConsentPersonnalData', function(e) 2384 { 2385 if($(this).is(':checked')) 2386 { 2387 $(this).closest('.form-item').removeClass('Alert'); 2388 } 2389 }); 2390 2391//////////////////////////////////////////////////////////////////////////////// 2392/////// /////// /////// /////// /////// /////// /////// /////// //////// 2393//////////////////////////////////////////////////////////////////////////////// 2394 2395 $(document).on('keyup', 'form.SearchInStore input', function(e) 2396 { 2397 $(this).parent().find('button[type="submit"]').prop('disabled', (this.value.length > 2 ? false : true)); 2398 }).on('keyup', '#FlavorFilter', function(e) 2399 { 2400 FilteringTastes(this); 2401 }); 2402 2403//////////////////////////////////////////////////////////////////////////////// 2404/////// /////// /////// /////// /////// /////// /////// /////// //////// 2405//////////////////////////////////////////////////////////////////////////////// 2406 2407 $(document).on('submit', 'form.SearchInStore', function(e) 2408 { 2409 e.preventDefault(); 2410 2411 let Searching = $(this) 2412 .find('input[name="SearchStore"]') 2413 .val() 2414 .trim(); 2415 2416 if(IsNotEmpty(Searching)) 2417 { 2418 // Encodage URL-safe, mais on garde accents et espaces dans la valeur logique 2419 let Encoded = encodeURIComponent(Searching); 2420 2421 window.location.href = RootPath() + '/SearchInStore/' + Encoded + '.html'; 2422 } 2423 }); 2424 2425//////////////////////////////////////////////////////////////////////////////// 2426/////// /////// /////// /////// /////// /////// /////// /////// //////// 2427//////////////////////////////////////////////////////////////////////////////// 2428 2429 $(document).on('submit', 'form#FormulaireContact', function(e) 2430 { 2431 var IsValid = true, 2432 Id = this.id, 2433 $Consent = $('#ConsentPersonnalData'); 2434 $('input#prenomContact, input#nomContact, input#emailContact, textarea[name="MessageContact"]').each(function(i) 2435 { 2436 var Value = $(this).val(); 2437 if(IsEmpty(Value) || (this.id === 'emailContact' && !isValidEmailAddress(Value))) 2438 { 2439 $(this).focus(); 2440 IsValid = false; 2441 return false; 2442 } 2443 }); 2444 let $Message = $('textarea[name="MessageContact"]'), 2445 Message = $('textarea[name="MessageContact"]').val(), 2446 Words = IsNotEmpty(Message) ? Message.split(' ') : []; 2447 if(Words.length < 5) 2448 { 2449 $Message.closest('.form-item').addClass('Alert').attr('data-message', 'Votre message est trop court.'); 2450 $Message.focus(); 2451 IsValid = false; 2452 return false; 2453 } 2454 if($Consent.prop('checked') !== true) 2455 { 2456 $Consent.closest('.form-item').addClass('Alert').attr('data-message', 'Veuillez accepter le traitement de vos données'); 2457 IsValid = false; 2458 return false; 2459 } 2460 e.preventDefault(); 2461 if(IsValid) 2462 { 2463 $('.form-item').removeClass('Alert'); 2464 SendForm(Id); 2465 } 2466 }); 2467 2468//////////////////////////////////////////////////////////////////////////////// 2469/////// /////// /////// /////// /////// /////// /////// /////// //////// 2470//////////////////////////////////////////////////////////////////////////////// 2471 2472 $(document).on('click', 'button.CheckSiret', function(e) 2473 { 2474 let $Siret = $('#BusinessSiretCodeContact'), 2475 $AutoFillInputs = $('.HideInput'), 2476 $BtnSendContact = $('button.SendContact'); 2477 $(this).addClass('Loading'); 2478 if($Siret.is(':visible')) 2479 { 2480 let SiretNumber = $('#BusinessSiretCodeContact').val().replace(/\D/g,''), 2481 CheckSiret = new Siret(SiretNumber); 2482 CheckSiret.exists().then(response => { 2483 if(!response) 2484 { 2485 $AutoFillInputs.hide(); 2486 $Siret.closest('.form-item').addClass('Alert').attr('data-message', 'Veuillez renseigner un SIRET valide (14 chiffre)'); 2487 $BtnSendContact.prop('disabled', true); 2488 $(this).removeClass('Loading'); 2489 return false;
2490 } 2491 else 2492 { 2493 let Etablissement = response.etablissement.unite_legale, 2494 Siege = Etablissement.etablissement_siege; 2495 $(this).removeClass('Loading'); 2496 $AutoFillInputs.show(); 2497 $('#BusinessNameContact').val(IsNotEmpty(Etablissement.denomination) ? Etablissement.denomination : response.etablissement.denomination_usuelle); 2498 $('#BusinessAddressContact').val(Siege.geo_l4); 2499 $('#BusinessZipCodeContact').val(Siege.code_postal); 2500 $('#BusinessTownContact').val(Siege.libelle_commune); 2501 $('#BusinessDateCreatedContact').val(Etablissement.date_creation); 2502 $BtnSendContact.prop('disabled', false); 2503 $Siret.closest('.form-item').removeClass('Alert'); 2504 } 2505 }); 2506 } 2507 }).on('click', 'a[href*="#"].Anchor', function(e) 2508 { 2509 e.preventDefault(); 2510 var Id = $(this).attr('href'); 2511 ScrollToAnchor(Id); 2512 }); 2513 2514//////////////////////////////////////////////////////////////////////////////// 2515/////// /////// /////// /////// /////// /////// /////// /////// //////// 2516//////////////////////////////////////////////////////////////////////////////// 2517 2518 class Siret 2519 { 2520 constructor(siret) 2521 { 2522 this.siret = siret; 2523 } 2524 valid() 2525 { 2526 if(this.siret.length > 13 && Siret.estSiretValide(this.siret)) 2527 { 2528 return true; 2529 } 2530 return false; 2531 } 2532 static estSiretValide(siret) 2533 { 2534 if((siret.length != 14) || (isNaN(siret))) 2535 return false; 2536 else 2537 { 2538 let somme = 0; 2539 let tmp; 2540 for(let cpt = 0; cpt < siret.length; cpt++) 2541 { 2542 if((cpt % 2) == 0) 2543 { 2544 tmp = siret.charAt(cpt) * 2; 2545 if(tmp > 9) 2546 tmp -= 9; 2547 } else 2548 tmp = siret.charAt(cpt); 2549 somme += parseInt(tmp); 2550 } 2551 if((somme % 10) !== 0) return false; 2552 } 2553 return true; 2554 } 2555 convert() { 2556 let siren = this.siret.substring(0, 9); 2557 let tva = (12 + 3 * (siren % 97)) % 97; 2558 return "FR" + tva + siren; 2559 } 2560 async exists(){ 2561 var myHeaders = new Headers(); 2562 myHeaders.append('Accept', '*/*'); 2563 myHeaders.append('Content-Type', 'application/json'); 2564 myHeaders.append('Access-Control-Allow-Origin', '*'); 2565 2566 let response = await fetch(`https://entreprise.data.gouv.fr/api/sirene/v3/etablissements/${this.siret}`, 2567 { 2568// mode: 'no-cors', 2569 method: 'GET', 2570 headers: myHeaders 2571 }); 2572 let data = await response.json(); 2573 if(data.etablissement === undefined){ 2574 return false; 2575 } 2576 return data; 2577 } 2578 } 2579 2580//////////////////////////////////////////////////////////////////////////////// 2581/////// /////// /////// /////// /////// /////// /////// /////// //////// 2582//////////////////////////////////////////////////////////////////////////////// 2583 2584 $(document).on('click', '.snipcart-item-quantity__quantity button, .snipcart__item__line__header__actions', function(e) 2585 { 2586 $(this).closest('.snipcart__item__line__product').addClass('Loading'); 2587 }); 2588 2589//////////////////////////////////////////////////////////////////////////////// 2590/////// /////// /////// /////// /////// /////// /////// /////// //////// 2591//////////////////////////////////////////////////////////////////////////////// 2592 2593 document.addEventListener('snipcart.ready', () => 2594 { 2595 const CATEGORIES = ['Carte cadeau', 'Atelier', 'Atelier privé']; 2596 const DEFERRED_PAYMENT = 'deferred_payment'; 2597 window.filterPaymentMethods = function(paymentMethod) 2598 { 2599 const cartHasItem = Snipcart.store.getState().cart.items.items.some((e) => CATEGORIES.includes(e.categories[0])); 2600 if(paymentMethod.id === DEFERRED_PAYMENT && cartHasItem) 2601 { 2602 return false; 2603 } 2604 return true; 2605 }; 2606 2607 //////////////////////////////////////////////////////////////////////////////// 2608 /////// /////// /////// /////// /////// /////// /////// /////// //////// 2609 //////////////////////////////////////////////////////////////////////////////// 2610 2611 Snipcart.api.session.setLanguage('fr', 2612 { 2613 'actions': 2614 {
2615 'dismiss': 'Non merci', 2616 'apply_changes': 'Appliquer les modifications', 2617 'yes_use_it': 'Oui, utilisez-le', 2618 'save_changes': 'Enregistrer les modifications', 2619 'back_to_orders': 'Retour aux commandes', 2620 'change_password': 'Changer le mot de passe', 2621 'clear_cart': 'Vider le panier', 2622 'add': 'Ajouter', 2623 'checkout': 'Commander', 2624 'continue_shopping': 'Continuer vos achats', 2625 }, 2626 'header': 2627 { 2628 'title_cart_summary': 'Votre panier', 2629 'loading': 'Chargementâ¦' 2630 }, 2631 'cart_summary': 2632 { 2633 'taxes': 'TVA', 2634 'taxes_included_in_price': 'Certains produits sont soumis à la TVA.', 2635 }, 2636 'register_form': 2637 { 2638 'requires_action': 2639 { 2640 'title': 'Vérifiez vos emails', 2641 'description': 'Ce courriel a déjà été utilisé lors dâune commande. Veuillez compléter le processus dâinscription en utilisant le lien qui vous a été envoyé par courriel.' 2642 } 2643 }, 2644 'discount_box': { 2645 'promo_code': 'Vous avez un code promo ou\u00A0carte\u00A0cadeau\u00A0?', 2646 'promo_code_placeholder': 'Saisissez votre code', 2647 'promocode_applied': 'Promotion appliquée' 2648 }, 2649 'reset_password_form': 2650 { 2651 'title': 'Réinitialisez votre mot de passe', 2652 'password': 'Nouveau mot de passe', 2653 'confirmation_password': 'Entrez à nouveau votre nouveau mot de passe', 2654 'action': 'Réinitialiser le mot de passe' 2655 }, 2656 'forgot_password_form': 2657 { 2658 'title': 'Récupérer votre mot de passe', 2659 'instructions': 'Entrez lâadresse courriel de votre compte et nous vous enverrons un lien de réinitialisation de mot de passe.', 2660 'email': 'Courriel', 2661 'email_instructions': 'Un courriel contenant un lien vous a été envoyé. Cliquez dessus pour réinitialiser votre mot de passe.', 2662 'action': 'Envoyer un courriel de réinitialisation du mot de passe' 2663 }, 2664 'change_password_form': 2665 { 2666 'title': 'Changez votre mot de passe', 2667 'current_password': 'Mot de passe actuel', 2668 'password': 'Nouveau mot de passe', 2669 'confirmation_password': 'Entrez à nouveau votre nouveau mot de passe', 2670 'action': 'Changer le mot de passe' 2671 }, 2672 'customer_details': 2673 { 2674 'title': 'Détails du compte' 2675 }, 2676 'customer_dashboard': 2677 { 2678 'orders': 'Commandes', 2679 'loading': 'Chargement...', 2680 }, 2681 'customer_orders': 2682 { 2683 'loading': 'Chargement...' 2684 }, 2685 'address_form': { 2686 'firstName': 'Prénom', 2687 'lastName': 'Nom', 2688 }, 2689 'default': 2690 { 2691 'loading': 'Chargement...', 2692 'error': 'Une erreur sâest produite.', 2693 'success': 'Succès !' 2694 }, 2695 'signin_form': 2696 { 2697 'signin': 'Connexion', 2698 'dont_have_an_account': 'Pas de compte ?', 2699 'email': 'Courriel', 2700 'password': 'Mot de passe', 2701 'forgot_your_password': 'Mot de passe oublié ?', 2702 'close_form': 'Retour' 2703 }, 2704 'cart': 2705 { 2706 'view_detailed_cart': 'Voir le panier détaillé', 2707 'summary': 'Résumé de votre panier', 2708 }, 2709 'shipping_rates': 2710 { 2711 'guaranteed_delivery_days': 'Délai de livraison estimé : %{guaranteedDaysToDelivery} jour(s).' 2712 }, 2713 'shippingRates': 2714 { 2715 'loading': 'Chargement des frais de port' 2716 }, 2717 'payment': 2718 { 2719 'form': 2720 { 2721 'invalid_number': 'Votre numéro de carte est invalide',
2722 'invalid_expiration': 'Date dâexpiration invalide', 2723 'invalid_cvv': 'CVV invalide', 2724 'deferred_payment_title': 'Payer par virement bancaire ou chèque', 2725 'deferred_payment_instructions': '<style>h3 {color: #536d30;margin: 5px 0 2px 0;font-weight: bold;font-family: sans-serif;text-transform: uppercase;font-size: 12px;border-bottom: 1px solid #536d30;padding: 0 0 2px 0; } p { font-size: 12px; padding: 0 10px; margin-bottom: 10px } p span { display: block; font-size: 12px; margin-bottom: 10px } b {font-family: sans-serif;font-size: inherit; } </style><span>En passant cette commande, vous acceptez de payer par chèque ou virement.</span><h3>Chèque</h3><span>à lâordre de <b>Sacré français</b> dans un délai de 15 jours. Adressez votre paiement par courrier postal à <b>Sacré français - MIN De Brienne - 110, Quai Paludate, Ent. 4, Bat. 2 - 33800 Bordeaux - France </b></span><h3>Virement bancaire</h3><span><b>Banque : </b> 15589 <b>Guichet : </b> 33558 <b>N° de compte : </b> 07259597843 <b>Clé : </b>66<br><b>IBAN : </b>FR76 1558 9335 5807 7551 6804 036 <b>BIC : </b>CMBRFR2BXXX</span><span class="mb-4">Votre commande sera envoyée <u>après encaissement</u> de votre paiement.</span>' 2726 }, 2727 'methods': 2728 { 2729 'deferred_payment': 'Payer par virement bancaire ou chèque' 2730 }, 2731 'checkout_with': 'Payer avec' 2732 }, 2733 'errors': 2734 { 2735 'promo_code_is_invalid': 'Ce code promo/carte cadeau nâest pas valide', 2736 'order_validation': 2737 { 2738 'domain_crawling': { 2739 'title':'Oups ! Votre commande nâa pas pu être complétée.', 2740 'description':'La disponibilité des produits de votre panier nâa pas pu être confirmée.'+SET_OrderErrorMessage('domain_crawling') 2741 }, 2742 'product_crawling': { 2743 'title':'Aïe ! Le prix de produits dans votre panier semble avoir changé.', 2744 'description': 'Essayez de les enlever et de les remettre dans votre panier.'+SET_OrderErrorMessage() 2745 }, 2746 'stock_validation': { 2747 'title':'Oooh ! Produit en rupture de stock.', 2748 'description': 'Quelques produits semblent être en rupture de stock. Veuillez réviser les quantités.'+SET_OrderErrorMessage() 2749 }, 2750 'custom_fields_validation': { 2751 'title': 'Information requise manquante.', 2752 'description': 'Il manque des informations obligatoires pour certains produits du panier. Veuillez corriger le panier pour continuer.' 2753 }, 2754 'shipping_validation': { 2755 'title': 'La méthode de livraison nâest pas valide.', 2756 'description': 'La méthode de livraison sélectionnée semble avoir été modifiée ou nâest plus disponible.'+SET_OrderErrorMessage() 2757 } 2758 } 2759 } 2760 }); 2761 2762 //////////////////////////////////////////////////////////////////////////////// 2763 /////// /////// /////// /////// /////// /////// /////// /////// //////// 2764 //////////////////////////////////////////////////////////////////////////////// 2765 2766 Snipcart.api.theme.customization.registerPaymentFormCustomization({ 2767 input: { 2768 backgroundColor: 'rgb(83 109 48 / 5%)', 2769 color: '#303030', 2770 border: '1px solid rgb(83 109 48 / 15%)', 2771 fontSize: '16px', 2772 placeholder: { 2773 color: '#536d30', 2774 }, 2775 }, 2776 label: 2777 { 2778 color: '#536d30', 2779 fontSize: '20px' 2780 } 2781 }); 2782 2783 //////////////////////////////////////////////////////////////////////////////// 2784 /////// /////// /////// /////// /////// /////// /////// /////// //////// 2785 //////////////////////////////////////////////////////////////////////////////// 2786 2787 Snipcart.events.on('snipcart.initialized', (snipcartState) => { 2788 $(document).on('click', 'button.snipcart-payment-methods-list-item__button', function(e) 2789 { 2790 $(this).addClass('active'); 2791 }); 2792 }); 2793 2794 //////////////////////////////////////////////////////////////////////////////// 2795 /////// /////// /////// /////// /////// /////// /////// /////// //////// 2796 //////////////////////////////////////////////////////////////////////////////// 2797 2798 Snipcart.events.on('item.adding', (parsedCartItem) => { 2799 $('#ProductModal').modal('hide'); 2800 SetHtmlTagToOverflowHidden(false); 2801 SetFreeShipping(Snipcart); 2802 UpdateViewedProduct(parsedCartItem.id, 'Add'); 2803 }); 2804 2805 //////////////////////////////////////////////////////////////////////////////// 2806 /////// /////// /////// /////// /////// /////// /////// /////// //////// 2807 //////////////////////////////////////////////////////////////////////////////// 2808
2809 Snipcart.events.on('item.updated', (cartItem) => { 2810 $('.snipcart__item__line__product').removeClass('Loading'); 2811 SetFreeShipping(Snipcart); 2812 }); 2813 2814 //////////////////////////////////////////////////////////////////////////////// 2815 /////// /////// /////// /////// /////// /////// /////// /////// //////// 2816 //////////////////////////////////////////////////////////////////////////////// 2817 2818 Snipcart.events.on('item.removed', (cartItem) => { 2819 SetFreeShipping(Snipcart); 2820 UpdateViewedProduct(cartItem.id, 'Remove'); 2821 }); 2822 2823 //////////////////////////////////////////////////////////////////////////////// 2824 /////// /////// /////// /////// /////// /////// /////// /////// //////// 2825 //////////////////////////////////////////////////////////////////////////////// 2826 2827// Snipcart.events.on('summary.checkout_clicked', () => { 2828// SetFreeShipping(Snipcart); 2829// }); 2830 2831 //////////////////////////////////////////////////////////////////////////////// 2832 /////// /////// /////// /////// /////// /////// /////// /////// //////// 2833 //////////////////////////////////////////////////////////////////////////////// 2834 2835 Snipcart.events.on('customer.signedin', (customer) => { 2836 setTimeout(function() 2837 { 2838 window.location.replace('#/signin','#/dashboard'); 2839 }, 250); 2840 }); 2841 2842 //////////////////////////////////////////////////////////////////////////////// 2843 /////// /////// /////// /////// /////// /////// /////// /////// //////// 2844 //////////////////////////////////////////////////////////////////////////////// 2845 2846 Snipcart.events.on('cart.confirm.error', (confirmError) => { 2847 StoreLogsOnError(confirmError, Snipcart.store.getState()); 2848 }); 2849 2850 //////////////////////////////////////////////////////////////////////////////// 2851 /////// /////// /////// /////// /////// /////// /////// /////// //////// 2852 //////////////////////////////////////////////////////////////////////////////// 2853 2854 Snipcart.events.on('payment.failed', (paymentError) => { 2855 StoreLogsOnError(paymentError, Snipcart.store.getState()); 2856 }); 2857 2858 //////////////////////////////////////////////////////////////////////////////// 2859 /////// /////// /////// /////// /////// /////// /////// /////// //////// 2860 //////////////////////////////////////////////////////////////////////////////// 2861 2862 Snipcart.events.on('theme.routechanged', (routesChange) => { 2863 $('#snipcart').attr('data-snipcart-page', routesChange.to); 2864 if(routesChange.to === '/checkout') 2865 { 2866 StickyPaymentStepsButtons('routechanged'); 2867 CheckCartBeforeCheckout(); 2868 2869 $(document).on('click', '.RemoveCartItem', function(e) 2870 { 2871 e.preventDefault(); 2872 var $That = $(this), 2873 itemId = $That.attr('data-id'); 2874 $That.prop('disabled', true); 2875 $That.closest('.snipcart-cart-summary-item').addClass('Loading'); 2876 setTimeout(function() 2877 { 2878 Snipcart.api.cart.items.remove(itemId); 2879 }, 1000); 2880 }); 2881 $(document).on('click', '.ChangeQuantity', function(e) 2882 { 2883 e.preventDefault(); 2884 var $That = $(this), 2885 itemId = $That.attr('data-id'), 2886 action = $That.attr('data-action'); 2887 $That.prop('disabled', true); 2888 $That.closest('.snipcart-cart-summary-item').addClass('Loading'); 2889 setTimeout(function() 2890 { 2891 var items = Snipcart.store.getState().cart.items.items, 2892 currentQuantity = 0; 2893 for(var i = 0; i < items.length; i++) 2894 { 2895 if(items[i].uniqueId === itemId) 2896 { 2897 currentQuantity = items[i].quantity; 2898 break; 2899 } 2900 } 2901 var quantityChange = action === 'increase' ? currentQuantity+1 : currentQuantity-1 2902 if(quantityChange > 0) 2903 { 2904 Snipcart.api.cart.items.update({uniqueId:itemId, quantity:quantityChange}); 2905 } 2906 }, 500); 2907 }); 2908 } 2909 }); 2910 2911 //////////////////////////////////////////////////////////////////////////////// 2912 /////// /////// /////// /////// /////// /////// /////// /////// //////// 2913 //////////////////////////////////////////////////////////////////////////////// 2914 2915 Snipcart.events.on('item.updated', (cartItem) => { 2916 $('.ChangeQuantity').closest('.snipcart-cart-summary-item').removeClass('Loading'); 2917 $('.ChangeQuantity').prop('disabled', false); 2918 }); 2919 2920 //////////////////////////////////////////////////////////////////////////////// 2921 /////// /////// /////// /////// /////// /////// /////// /////// //////// 2922 //////////////////////////////////////////////////////////////////////////////// 2923
2924 Snipcart.events.on('shipping.selected', (shippingMethod) => { 2925 StickyPaymentStepsButtons('shipping.selected'); 2926 }); 2927 2928 //////////////////////////////////////////////////////////////////////////////// 2929 /////// /////// /////// /////// /////// /////// /////// /////// //////// 2930 //////////////////////////////////////////////////////////////////////////////// 2931 2932 StickyPaymentStepsButtons('snipcart ready'); 2933 }); 2934 2935//////////////////////////////////////////////////////////////////////////////// 2936/////// /////// /////// /////// /////// /////// /////// /////// //////// 2937//////////////////////////////////////////////////////////////////////////////// 2938 2939 $(document).on('click', '.ProductAttribute .BtnQty', function(e) 2940 { 2941 e.preventDefault(); 2942 var IsModal = $('#ProductModal').hasClass('show') ? true : false, 2943 $Qty = IsModal ? $('.Modal.ProductAttribute .AddMoreBtn .ProductQty') : $(this).parent().find('.ProductQty'), //$('.Page.ProductAttribute .AddMoreBtn .ProductQty'), 2944 Max = $Qty.attr('max'), 2945 Count = $Qty.val(), 2946 Data = $(this).attr('data-qty') === 'Plus' ? Count++ : (Count > 1 ? Count-- : 0); 2947 if(IsNotEmpty(Max) && Count >= Max) 2948 { 2949 Count = Max; 2950 } 2951 $Qty.val(Count); 2952 $Qty.closest('form').find('.snipcart-add-item:not(.LimitedQty)[data-item-quantity]').attr('data-item-quantity', Count); 2953 $Qty.closest('form').find('.snipcart-add-item .TotalWorkShop').text(Count+' place'+(Count > 1 ? 's' : '')); 2954 }); 2955 2956//////////////////////////////////////////////////////////////////////////////// 2957/////// /////// /////// /////// /////// /////// /////// /////// //////// 2958//////////////////////////////////////////////////////////////////////////////// 2959 2960 $(document).on('click','ul#MoreInfoTabs li a',function(e) 2961 { 2962 e.preventDefault(); 2963 var Id = $(this).attr('href'); 2964 $('*[id^="InfoTab"]').addClass('HideMoreInfoSection'); 2965 $('section'+Id).removeClass('HideMoreInfoSection'); 2966 $('ul#MoreInfoTabs a[href^="#InfoTab"]').removeClass('Active'); 2967 $(this).addClass('Active'); 2968 }); 2969 2970//////////////////////////////////////////////////////////////////////////////// 2971/////// /////// /////// /////// /////// /////// /////// /////// //////// 2972//////////////////////////////////////////////////////////////////////////////// 2973 2974 $(document).on('click','.mean-container .mean-nav ul:not(.Short) li a',function(e) 2975 { 2976 $('.mean-container .mean-nav ul:not(.Short) li a').parent().removeClass('NearActive'); 2977 $(this).parent().addClass('NearActive'); 2978 }); 2979 2980//////////////////////////////////////////////////////////////////////////////// 2981/////// /////// /////// /////// /////// /////// /////// /////// //////// 2982//////////////////////////////////////////////////////////////////////////////// 2983 2984 $('.dropdown').parent('.drop').css('position' , 'relative'); 2985 2986//////////////////////////////////////////////////////////////////////////////// 2987/////// /////// /////// /////// /////// /////// /////// /////// //////// 2988//////////////////////////////////////////////////////////////////////////////// 2989 2990 $(document).on('click', '[data-idproduct]', function(e) 2991 { 2992 var IdProduct = $(this).data('idproduct'), 2993 $Modal = $('#ProductModal'), 2994 Link = $(this).closest('.ProductThumb').find('.FirstImg').attr('href'), 2995 LinkText = IsNotEmpty(Link) ? '<br>Vous pouvez néanmoins <a class="stretched-link text-danger" href="'+Link+'">accéder à la page de ce produit en suivant ce lien</a>.' : ''; 2996 $.ajax( 2997 { 2998 type: 'POST', url: RootPath()+'/Ajax.php', dataType: 'json', 2999 data: { GetProduct: IdProduct }, 3000 cache: false, 3001 success: function(Retour) 3002 { 3003 if(IsNotEmpty(Retour.HTML_ProductDetail)) 3004 { 3005 $Modal.find('.modal-product').html(Retour.HTML_ProductDetail); 3006 UpdateCartFunctions(); 3007// SetFreeShipping(Snipcart); 3008 ImagesLoaded(); 3009 SetHtmlTagToOverflowHidden(true); 3010 StoreViewedProduct(); 3011 } 3012 else 3013 { 3014 $Modal.find('.modal-product').html(HTML_AjaxAlert('Une erreur est survenue. Nous mettons tout en Åuvre pour la corriger.'+LinkText)); 3015 } 3016 }, 3017 error: function(xhr, status, error) 3018 {
3019 $Modal.find('.modal-product').html(HTML_AjaxAlert('Une erreur est survenue. Nous mettons tout en Åuvre pour la corriger.'+LinkText)); 3020 } 3021 }); 3022 }); 3023 3024//////////////////////////////////////////////////////////////////////////////// 3025/////// /////// /////// /////// /////// /////// /////// /////// //////// 3026//////////////////////////////////////////////////////////////////////////////// 3027 3028 $(document).on('click', '[data-idblog]', function(e) 3029 { 3030 var IdBlog = $(this).data('idblog'), 3031 $Modal = $('#ProductModal'), 3032 Link = $(this).closest('.ProductThumb').find('.FirstImg').attr('href'), 3033 LinkText = IsNotEmpty(Link) ? '<br>Vous pouvez néanmoins <a class="stretched-link text-danger" href="'+Link+'">accéder à cette page en suivant ce lien</a>.' : ''; 3034 $.ajax( 3035 { 3036 type: 'POST', url: RootPath()+'/Ajax.php', dataType: 'json', 3037 data: { GetBlog: IdBlog }, 3038 cache: false, 3039 success: function(Retour) 3040 { 3041 if(IsNotEmpty(Retour.HTML_BlogDetail)) 3042 { 3043 $Modal.find('.modal-product').html(Retour.HTML_BlogDetail); 3044 UpdateCartFunctions(); 3045 ImagesLoaded(); 3046 SetHtmlTagToOverflowHidden(true); 3047 } 3048 else 3049 { 3050 $Modal.find('.modal-product').html(HTML_AjaxAlert('Une erreur est survenue. Nous mettons tout en Åuvre pour la corriger.'+LinkText)); 3051 } 3052 }, 3053 error: function(xhr, status, error) 3054 { 3055 $Modal.find('.modal-product').html(HTML_AjaxAlert('Une erreur est survenue. Nous mettons tout en Åuvre pour la corriger.'+LinkText)); 3056 } 3057 }); 3058 }); 3059 3060//////////////////////////////////////////////////////////////////////////////// 3061/////// /////// /////// /////// /////// /////// /////// /////// //////// 3062//////////////////////////////////////////////////////////////////////////////// 3063 3064 $(document).on('click', '.TranslationFlags a', function() 3065 { 3066 var Lang = $(this).data('lang'); 3067 var $LangSelector = $('.goog-te-combo'); 3068 var db = $LangSelector.get(0); 3069 $LangSelector.val(Lang); 3070 FireEvent(db, 'change'); 3071 document.cookie = 'googtrans=/fr/'+Lang+''; 3072 setTimeout(function() 3073 { 3074 UpdateLanguageFlag(); 3075 }, 250); 3076 }); 3077 3078//////////////////////////////////////////////////////////////////////////////// 3079/////// /////// /////// /////// /////// /////// /////// /////// //////// 3080//////////////////////////////////////////////////////////////////////////////// 3081 3082 $(document).on('click', '[data-idmarket]', function(e) 3083 { 3084 var IdMarket = $(this).data('idmarket'), 3085 $Modal = $('#MarketModal'); 3086 $.ajax( 3087 { 3088 type: 'POST', url: RootPath()+'/Ajax.php', dataType: 'json', 3089 data: { GetMarket: IdMarket }, 3090 cache: false, 3091 success: function(Retour) 3092 { 3093 if(IsNotEmpty(Retour.HTML_MarketDetail)) 3094 { 3095 $Modal.find('.modal-product').html(Retour.HTML_MarketDetail); 3096 UpdateCartFunctions(); 3097 ImagesLoaded(); 3098 } 3099 else 3100 { 3101 $Modal.find('.modal-product').html(HTML_AjaxAlert('Une erreur est survenue. Nous mettons tout en Åuvre pour la corriger.')); 3102 } 3103 }, 3104 error: function(xhr, status, error) 3105 { 3106 $Modal.find('.modal-product').html(HTML_AjaxAlert('Une erreur est survenue. Nous mettons tout en Åuvre pour la corriger.')); 3107 } 3108 }); 3109 }); 3110 3111//////////////////////////////////////////////////////////////////////////////// 3112/////// /////// /////// /////// /////// /////// /////// /////// //////// 3113//////////////////////////////////////////////////////////////////////////////// 3114 3115 $(document).on('submit', 'form#ProLoginForm', function(e) 3116 { 3117 e.preventDefault(); 3118 let LoginForm = new FormData(this); 3119 $.ajax({ 3120 type: 'POST', url: RootPath()+'/Ajax.php', dataType: "json", 3121 data: LoginForm, 3122 cache: false, 3123 contentType: false, 3124 processData: false, 3125 success: function(x) 3126 { 3127 if(x.hasOwnProperty('Error')) 3128 { 3129 ErrorLoginAjax(x['Error']); 3130 } 3131 else 3132 { 3133 location.reload(); 3134 } 3135 }, 3136 error: function() 3137 { 3138 ErrorLoginAjax('Une erreur est survenue.'); 3139 } 3140 }); 3141 }); 3142 3143//////////////////////////////////////////////////////////////////////////////// 3144/////// /////// /////// /////// /////// /////// /////// /////// //////// 3145//////////////////////////////////////////////////////////////////////////////// 3146 3147 $(document).on('click', '.RevealPassWord', function(e) 3148 { 3149 let $Input = $(this).parent().find('input'); 3150 console.log($Input.type); 3151 if($Input.attr('type') === 'password') 3152 { 3153 $Input.attr('type', 'text'); 3154 $(this).toggleClass('active'); 3155 } 3156 else 3157 { 3158 $Input.attr('type', 'password'); 3159 } 3160 }); 3161 3162//////////////////////////////////////////////////////////////////////////////// 3163/////// /////// /////// /////// /////// /////// /////// /////// //////// 3164//////////////////////////////////////////////////////////////////////////////// 3165 3166 $(document).on('click', '.ScrollToReviews', function(e) 3167 { 3168 e.preventDefault(); 3169 ScrollToAnchor('#MoreInfoTabs'); 3170 $('#MoreInfoTabs').find('.Active').removeClass('Active'); 3171 $('#MoreInfoTab_Reviews').addClass('Active'); 3172 $('#MoreInfoTabs').parent().find('section').addClass('HideMoreInfoSection'); 3173 $('#InfoTabReviews').removeClass('HideMoreInfoSection'); 3174 }); 3175 3176//////////////////////////////////////////////////////////////////////////////// 3177/////// /////// /////// /////// /////// /////// /////// /////// //////// 3178//////////////////////////////////////////////////////////////////////////////// 3179 3180 $(document).ready(function() 3181 {
3182 UpdateCartFunctions(); 3183 ImagesLoaded(); 3184 ScrollToAnchor(); 3185 CloneBuyForm(); 3186 UpdateLanguageFlag(); 3187 CheckResponsiveTable(); 3188 ResetHtmlTagOverflow(); 3189 DisplayViewedProducts(); 3190 StoreViewedProduct(); 3191 GetUniqueReviews(''); 3192 GetAsideContent(); 3193 3194 }); 3195})(jQuery); 3196 3197//////////////////////////////////////////////////////////////////////////////// 3198/////// /////// /////// /////// /////// /////// /////// /////// //////// 3199//////////////////////////////////////////////////////////////////////////////// 3200 3201 function ManageCookies(Status) 3202 { 3203 if(Status === 'deny') 3204 { 3205 var theCookies = document.cookie.split(';'); 3206 const AuthorizedCookies = ['snipcart-cart', '_ga', 'googtrans', 'SacreFrancais']; 3207 for(var i = 0 ; i < theCookies.length; i++) 3208 { 3209 var CookieName = theCookies[i].split('=')[0].replace(' ',''); 3210 if(!AuthorizedCookies.includes(CookieName)) 3211 { 3212 document.cookie = theCookies[i].split('=')[0] + '=; path=/; expires=Thu, 01 Jan 1970 00:00:01 GMT;'; 3213 } 3214 } 3215 } 3216 } 3217 3218//////////////////////////////////////////////////////////////////////////////// 3219/////// /////// /////// /////// /////// /////// /////// /////// //////// 3220//////////////////////////////////////////////////////////////////////////////// 3221 3222 if($('#CookieConsentBox').length) 3223 { 3224 new window.cookieconsent.initialise( 3225 { 3226 //https://github.com/osano/cookieconsent#version-31 3227 cookie: { name: 'SacreFrancais', 3228 domain: 'www.SacreFrancais.fr' 3229 }, 3230 theme: 'edgeless', 3231 content: { header: 'Cookies used on the website!', 3232 message: 'Sacré français est une boutique en ligne respectueuse de votre vie privée. Néanmoins, son fonctionnement nécessite le <b><u>dépôt de cookies essentiels</u></b> :<br><span class="FakeBtn"></span> <b>Snipcart</b> permet la gestion du panier. <a class="MoreInfo" href="https://snipcart.com/fr" target="_blank">+ dâinfos</a><br><span class="FakeBtn"></span> <b>Google Analytics</b> permet dâanalyser le trafic et dâoptimiser votre expérience de navigation<br><span class="FakeBtn"></span> <b>Google Translate</b> permet la traduction du site.<br><span class="FakeBtn"></span> <b>Société des avis garantisés</b> permet d\'afficher les notes des produits.<hr class="my-1" >Dâautres <b>cookies dâagrément, non-essentiels,</b> peuvent être déposés par : <br><span class="FakeBtn active"></span> Les <b>réseaux sociaux</b> (Facebook, â¦), les <b>hébergeurs de contenu</b> (Youtube, â¦), les partenairesâ¦', 3233 dismiss: 'Fermer', 3234 allow: 'Jâaccepte le dépôt de cookies', 3235 deny: 'Je refuse les cookies non-essentiels', 3236 close: '❌', 3237 link: 'En savoir plus', 3238 policy: 'Cookies', 3239 href: 'https://www.SacreFrancais.fr/cgv.php'}, 3240 revokable:true, 3241 law: { regionalLaw: false }, 3242 location: true, 3243 type: 'opt-out', 3244 onInitialise: function(status) 3245 { 3246 var type = this.options.type, 3247 didConsent = this.hasConsented(); 3248 if(type == 'opt-out' && !didConsent) 3249 { 3250 ManageCookies(status); 3251 } 3252 }, 3253 onStatusChange: function(status, chosenBefore) 3254 { 3255 var type = this.options.type, 3256 didConsent = this.hasConsented(); 3257 if(type == 'opt-out' && !didConsent) 3258 { 3259 ManageCookies(status); 3260 } 3261 }, 3262 onRevokeChoice: function() 3263 { 3264 var type = this.options.type; 3265 if(type == 'opt-out') 3266 { 3267 ManageCookies(status); 3268 } 3269 } 3270 }); 3271 }
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.