{"title":"Maillot Back to Back PSG 2026\/2027","description":"\u003ch2 data-section-id=\"1jq6ot3\" data-start=\"0\" data-end=\"28\"\u003eMaillot Back to Back PSG 2026\/2027\u003c\/h2\u003e\n\u003cp data-start=\"30\" data-end=\"442\"\u003eDécouvrez notre collection de maillots Back to Back PSG, pensée pour les supporters du Paris Saint-Germain qui recherchent un style unique et moderne. Chaque maillot Back to Back PSG combine confort, qualité et design exclusif, idéal pour le sport comme pour un usage quotidien. Inspirés de l'univers du football et de la culture parisienne, ces modèles permettent d'afficher votre passion avec élégance.\u003c\/p\u003e","products":[{"product_id":"maillot-argentine-noir-2026-2027-copie","title":"Back to Back Bleu PSG 2027\/2027","description":"\u003c!-- BLOC FLOCAGE PERSONNALISÉ --\u003e\n\u003cdiv class=\"custom-flocage-wrapper\" style=\"display:none;\"\u003e\n\n  \u003cdiv class=\"maillot-info\"\u003e\n    \u003cp class=\"flocage-title\"\u003e\n      🔥 \u003cstrong\u003eFlocage personnalisé disponible pour seulement 5€\u003c\/strong\u003e 🔥\n    \u003c\/p\u003e\n    \u003cp\u003e⚽ \u003cstrong\u003eQualité Joueur – Maillots Premium\u003c\/strong\u003e\u003c\/p\u003e\n    \u003cp\u003eNos maillots sont conçus en qualité joueur, identique à celle portée par les professionnels sur le terrain.\u003c\/p\u003e\n  \u003c\/div\u003e\n\n  \u003cdiv id=\"flocage-box\" style=\"display:none;\"\u003e\n    \u003clabel\u003eNom du joueur\u003c\/label\u003e\n    \u003cinput type=\"text\" id=\"flocage-nom\" placeholder=\"Ex : Fiére\"\u003e\n    \u003clabel\u003eNuméro\u003c\/label\u003e\n    \u003cinput type=\"text\" id=\"flocage-numero\" placeholder=\"Ex : 2\"\u003e\n    \u003clabel\u003eInscription sous le numéro\u003c\/label\u003e\n    \u003cinput type=\"text\" id=\"flocage-inscription\" placeholder=\"Ex :Malade\"\u003e\n  \u003c\/div\u003e\n\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n.custom-flocage-wrapper { margin-top: 15px; margin-bottom: 15px; }\n.maillot-info { margin-bottom: 18px; font-size: 14px; line-height: 1.7; color: #000; }\n.flocage-title { color: #ff1493; font-size: 16px; font-weight: 700; margin-bottom: 12px; }\n#flocage-box { margin-top: 15px; }\n#flocage-box label { display: block; margin-top: 12px; margin-bottom: 5px; font-weight: 600; color: #000; }\n#flocage-box input {\n  width: 100%; padding: 12px; border: 1px solid #dcdcdc;\n  border-radius: 8px; font-size: 14px; outline: none;\n  transition: 0.2s; box-sizing: border-box;\n}\n#flocage-box input:focus { border-color: #ff1493; }\n#flocage-box input:disabled {\n  background-color: #f5f5f5;\n  cursor: not-allowed;\n  color: #aaa;\n}\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n\n  var MAX_WAIT = 8000;\n  var POLL_INTERVAL = 200;\n  var elapsed = 0;\n\n  function isFlocageSelected() {\n    var checked = document.querySelectorAll('select option:checked, input[type=\"radio\"]:checked');\n    for (var i = 0; i \u003c checked.length; i++) {\n      var v = checked[i].value.toLowerCase();\n      if (\n        (v.includes(\"flocage\") || v.includes(\"personnalis\")) \u0026\u0026\n        !v.includes(\"sans\")\n      ) return true;\n    }\n    return false;\n  }\n\n  function getFlocageProperties() {\n    var nom = (document.getElementById(\"flocage-nom\") || {}).value || \"\";\n    var num = (document.getElementById(\"flocage-numero\") || {}).value || \"\";\n    var ins = (document.getElementById(\"flocage-inscription\") || {}).value || \"\";\n    var props = {};\n    if (nom.trim()) props[\"Nom du joueur\"] = nom.trim();\n    if (num.trim()) props[\"Numéro\"] = num.trim();\n    if (ins.trim()) props[\"Inscription sous le numéro\"] = ins.trim();\n    return props;\n  }\n\n  function getVariantId() {\n    var input = document.querySelector('input[name=\"id\"]');\n    if (input \u0026\u0026 input.value) return input.value;\n    var urlParams = new URLSearchParams(window.location.search);\n    if (urlParams.get(\"variant\")) return urlParams.get(\"variant\");\n    if (window.ShopifyAnalytics \u0026\u0026 window.ShopifyAnalytics.meta \u0026\u0026 window.ShopifyAnalytics.meta.selectedVariantId) {\n      return window.ShopifyAnalytics.meta.selectedVariantId;\n    }\n    return null;\n  }\n\n  function getQuantity() {\n    var q = document.querySelector('input[name=\"quantity\"]');\n    return q ? (parseInt(q.value) || 1) : 1;\n  }\n\n  function positionFlocageBlock() {\n    var wrapper = document.querySelector(\".custom-flocage-wrapper\");\n    if (!wrapper) return false;\n\n    var addBtn =\n      document.querySelector('button[name=\"add\"]') ||\n      document.querySelector(\".product-form__submit\") ||\n      document.querySelector('product-form button[type=\"submit\"]') ||\n      document.querySelector('form[action=\"\/cart\/add\"] button[type=\"submit\"]');\n\n    if (!addBtn) return false;\n\n    var btnContainer = addBtn.closest(\".product-form__buttons\") ||\n                       addBtn.closest(\".shopify-payment-button\") ||\n                       addBtn.parentElement;\n\n    if (btnContainer \u0026\u0026 !wrapper._positioned) {\n      btnContainer.insertAdjacentElement(\"beforebegin\", wrapper);\n      wrapper.style.display = \"block\";\n      wrapper._positioned = true;\n    }\n\n    return true;\n  }\n\n  function addToCartWithProperties(variantId, quantity, properties, originalBtn) {\n    if (originalBtn) {\n      originalBtn.disabled = true;\n      originalBtn._originalText = originalBtn.innerText;\n      originalBtn.innerText = \"Ajout en cours...\";\n    }\n\n    fetch(\"\/cart\/add.js\", {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application\/json\" },\n      body: JSON.stringify({ id: variantId, quantity: quantity, properties: properties })\n    })\n    .then(function (res) { return res.json(); })\n    .then(function (item) {\n      if (item \u0026\u0026 item.key) {\n        return fetch(\"\/cart\/change.js\", {\n          method: \"POST\",\n          headers: { \"Content-Type\": \"application\/json\" },\n          body: JSON.stringify({ id: item.key, quantity: quantity, properties: properties })\n        });\n      }\n    })\n    .then(function () {\n      if (originalBtn) {\n        originalBtn.disabled = false;\n        originalBtn.innerText = originalBtn._originalText || \"Ajouter au panier\";\n      }\n      [\"cart:refresh\", \"cart:updated\", \"theme:cart:open\", \"kaching:refresh\"].forEach(function(ev) {\n        document.dispatchEvent(new CustomEvent(ev));\n        window.dispatchEvent(new CustomEvent(ev));\n      });\n      setTimeout(function () {\n        var kachingOpen =\n          document.querySelector(\"[data-kaching-cart-open]\") ||\n          document.querySelector(\".kaching-cart__toggle\") ||\n          document.querySelector(\"[data-cart-open]\");\n        if (kachingOpen) kachingOpen.click();\n        if (!document.querySelector(\".kaching-cart\")) {\n          window.location.href = \"\/cart\";\n        }\n      }, 400);\n    })\n    .catch(function (err) {\n      console.error(\"[Flocage] Erreur:\", err);\n      if (originalBtn) {\n        originalBtn.disabled = false;\n        originalBtn.innerText = originalBtn._originalText || \"Ajouter au panier\";\n      }\n      alert(\"Une erreur est survenue, veuillez réessayer.\");\n    });\n  }\n\n  function hookButton(btn) {\n    if (btn._flocageHooked) return;\n    btn._flocageHooked = true;\n\n    btn.addEventListener(\"click\", function (e) {\n      if (!isFlocageSelected()) return;\n\n      e.preventDefault();\n      e.stopImmediatePropagation();\n      e.stopPropagation();\n\n      var variantId = getVariantId();\n      if (!variantId) { console.warn(\"[Flocage] Variant ID introuvable\"); return; }\n\n      addToCartWithProperties(variantId, getQuantity(), getFlocageProperties(), btn);\n    }, true);\n  }\n\n  function findAndHookButton() {\n    var btn =\n      document.querySelector('button[name=\"add\"]') ||\n      document.querySelector(\".product-form__submit\") ||\n      document.querySelector('product-form button[type=\"submit\"]') ||\n      document.querySelector('form[action=\"\/cart\/add\"] button[type=\"submit\"]');\n    if (btn) { hookButton(btn); return true; }\n    return false;\n  }\n\n  function checkFlocageVisibility() {\n    var box = document.getElementById(\"flocage-box\");\n    if (!box) return;\n    var selected = isFlocageSelected();\n    box.style.display = selected ? \"block\" : \"none\";\n    box.querySelectorAll(\"input\").forEach(function(i) {\n      i.disabled = !selected;\n      if (!selected) i.value = \"\";\n    });\n  }\n\n  var pollTimer = setInterval(function () {\n    elapsed += POLL_INTERVAL;\n    var positioned = positionFlocageBlock();\n    var hooked = findAndHookButton();\n    if ((positioned \u0026\u0026 hooked) || elapsed \u003e= MAX_WAIT) clearInterval(pollTimer);\n  }, POLL_INTERVAL);\n\n  var observer = new MutationObserver(function () {\n    positionFlocageBlock();\n    findAndHookButton();\n    checkFlocageVisibility();\n  });\n  observer.observe(document.body, { childList: true, subtree: true });\n\n  document.addEventListener(\"change\", checkFlocageVisibility);\n  checkFlocageVisibility();\n\n})();\n\u003c\/script\u003e","brand":"Lafabriquedumaillot","offers":[{"title":"M","offer_id":57369498354041,"sku":null,"price":24.99,"currency_code":"EUR","in_stock":true},{"title":"L","offer_id":57369498419577,"sku":null,"price":24.99,"currency_code":"EUR","in_stock":true},{"title":"XL","offer_id":57369498485113,"sku":null,"price":24.99,"currency_code":"EUR","in_stock":true},{"title":"2XL","offer_id":57369498550649,"sku":null,"price":24.99,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/1002\/3354\/4057\/files\/ChatGPTImage3juin2026a00_57_57.png?v=1780761647"},{"product_id":"maillot-back-to-back-blanc-2026-2027","title":"Maillot Back to Back Blanc PSG 2026\/2027","description":"","brand":"My Store 8","offers":[{"title":"M","offer_id":57369498878329,"sku":null,"price":24.99,"currency_code":"EUR","in_stock":true},{"title":"L","offer_id":57369498943865,"sku":null,"price":24.99,"currency_code":"EUR","in_stock":true},{"title":"XL","offer_id":57369499009401,"sku":null,"price":24.99,"currency_code":"EUR","in_stock":true},{"title":"2XL","offer_id":57369499074937,"sku":null,"price":24.99,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/1002\/3354\/4057\/files\/ChatGPTImage3juin2026a00_58_13_2ec1f2ff-7e2a-4153-83d4-42b999af8f11.png?v=1780761658"},{"product_id":"maillot-back-to-back-noir-2026-2027","title":"Maillot Back to Back Noir 2026\/2027 (PSG)","description":"","brand":"My Store 8","offers":[{"title":"M","offer_id":57369500057977,"sku":null,"price":24.99,"currency_code":"EUR","in_stock":true},{"title":"L","offer_id":57369500123513,"sku":null,"price":24.99,"currency_code":"EUR","in_stock":true},{"title":"XL","offer_id":57369500189049,"sku":null,"price":24.99,"currency_code":"EUR","in_stock":true},{"title":"2XL","offer_id":57369500254585,"sku":null,"price":24.99,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/1002\/3354\/4057\/files\/ChatGPTImage3juin2026a00_58_22_73b76474-6221-4698-8537-bec608d4b5f2.png?v=1780761663"}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/1002\/3354\/4057\/collections\/ChatGPT_Image_3_juin_2026_a_00_43_13_757f5cd5-7c8d-4cf9-9dc4-5ed7f9bd6413.png?v=1780761223","url":"https:\/\/lafabriquedumaillot.com\/collections\/maillot-back-to-back-psg-2026-2027.oembed","provider":"Lafabriquedumaillot","version":"1.0","type":"link"}