1(function(){ 2 if (window.__bq83629WidgetLoaded) return; 3 window.__bq83629WidgetLoaded = true; 4 5 var placeholder = document.getElementById('mini-qcm-widget'); 6 if (!placeholder) return; 7 var postIdent = placeholder.getAttribute('data-post-slug') || placeholder.getAttribute('data-post-uuid'); 8 if (!postIdent) return; 9 10 var API_BASE = 'https://www.83-629.fr'; 11 12 // CSS injecté une seule fois 13 var CSS = "\n.mqcm-container{font-family:'Inter',system-ui,-apple-system,sans-serif;background:#FFFFFF;border:1px solid #D8D0BF;border-radius:18px;padding:32px 28px;margin:40px 0;color:#2C3A4E;line-height:1.55;box-shadow:0 20px 40px -24px rgba(30,58,138,.25),0 4px 8px -4px rgba(30,58,138,.08);position:relative;overflow:hidden;animation:mqcmFadeIn .5s ease-out}\n.mqcm-container::before{content:'';position:absolute;top:0;left:0;right:0;height:4px;background:linear-gradient(90deg,#1e3a8a 0%,#3b82f6 50%,#1e3a8a 100%)}\n.mqcm-intro{background:linear-gradient(135deg,#FFFFFF 0%,#F0F4FA 100%)}\n@keyframes mqcmFadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}\n.mqcm-icon-wrap{display:flex;justify-content:center;margin-bottom:14px}\n.mqcm-icon{display:inline-flex;align-items:center;justify-content:center;width:56px;height:56px;background:linear-gradient(135deg,#dbeafe,#FFFFFF);border:1px solid #dbeafe;border-radius:50%;font-size:1.8rem;box-shadow:0 8px 16px -8px rgba(30,58,138,.2)}\n.mqcm-header{margin-bottom:22px;text-align:center}\n.mqcm-eyebrow{font-family:'JetBrains Mono',monospace;font-size:.74rem;color:#1e3a8a;text-transform:uppercase;letter-spacing:.08em;font-weight:700;margin-bottom:10px}\n.mqcm-h{font-family:'Bricolage Grotesque','Inter',sans-serif;font-size:1.35rem;font-weight:700;color:#0E1A2B;margin:0 0 14px;line-height:1.25;letter-spacing:-.01em}\n.mqcm-pills{display:flex;justify-content:center;gap:8px;margin:0 0 14px;flex-wrap:wrap}\n.mqcm-pill{display:inline-flex;align-items:center;font-family:'JetBrains Mono',monospace;font-size:.72rem;color:#1e3a8a;background:#dbeafe;padding:5px 12px;border-radius:999px;font-weight:600;letter-spacing:.03em}\n.mqcm-pill-soft{background:#FAF8F2;color:#5A6A7D;border:1px solid #D8D0BF}\n.mqcm-sub{font-size:.92rem;color:#6B7A8F;margin:0 auto;text-align:center;max-width:520px}\n.mqcm-progress-bar{height:4px;background:#EFEAE0;border-radius:999px;overflow:hidden;margin-bottom:10px}\n.mqcm-progress-bar-fill{height:100%;background:linear-gradient(90deg,#1e3a8a,#3b82f6);border-radius:999px;transition:width .4s ease-out}\n.mqcm-progress{font-family:'JetBrains Mono',monospace;font-size:.78rem;color:#6B7A8F;margin-bottom:14px;letter-spacing:.06em}\n.mqcm-question{font-family:'Bricolage Grotesque','Inter',sans-serif;font-size:1.1rem;font-weight:600;color:#0E1A2B;margin-bottom:18px;line-height:1.4}\n.mqcm-options{display:flex;flex-direction:column;gap:9px;margin-bottom:18px}\n.mqcm-option{background:#FAF8F2;border:2px solid #D8D0BF;border-radius:8px;padding:12px 16px;cursor:pointer;font-size:.94rem;color:#2C3A4E;text-align:left;font-family:inherit;width:100%;transition:all .15s;line-height:1.4}\n.mqcm-option:hover:not(:disabled){border-color:#1e3a8a;background:#dbeafe}\n.mqcm-option.mqcm-selected{border-color:#1e3a8a;background:#dbeafe;color:#0f1e4f;font-weight:600;animation:mqcm-pulse .35s ease-out}\n.mqcm-option:disabled{cursor:default;opacity:.55}\n.mqcm-option.mqcm-selected:disabled{opacity:1}\n@keyframes mqcm-pulse{0%{transform:scale(1)}50%{transform:scale(1.015);box-shadow:0 0 0 4px rgba(30,58,138,.18)}100%{transform:scale(1);box-shadow:none}}\n.mqcm-nav{display:flex;justify-content:space-between;align-items:center;gap:10px;margin-top:14px}\n.mqcm-nav-hint{font-size:.78rem;color:#6B7A8F;font-style:italic;flex:1;text-align:right}\n.mqcm-btn{padding:12px 22px;border:none;border-radius:10px;font-weight:600;cursor:pointer;font-size:.92rem;font-family:inherit;transition:all .2s;text-decoration:none;display:inline-flex;align-items:center;justify-content:center;gap:8px;text-align:center}\n.mqcm-btn-primary{background:linear-gradient(135deg,#1e3a8a,#0f1e4f);color:white;box-shadow:0 6px 14px -6px rgba(30,58,138,.6),inset 0 1px 0 rgba(255,255,255,.15)}\n.mqcm-btn-primary:hover{transform:translateY(-1px);box-shadow:0 10px 20px -8px rgba(30,58,138,.7),inset 0 1px 0 rgba(255,255,255,.18)}\n.mqcm-btn-primary:active{transform:translateY(0)}\n.mqcm-btn-secondary{background:#FFFFFF;color:#1e3a8a;border:1px solid #D8D0BF}\n.mqcm-btn-secondary:hover{border-color:#1e3a8a;background:#FAF8F2}\n.mqcm-btn-large{padding:14px 28px;font-size:1rem;display:flex;width:100%;max-width:340px;margin:0 auto}\n.mqcm-btn-arrow{font-size:1.1em;transition:transform .2s}\n.mqcm-btn-primary:hover .mqcm-btn-arrow{transform:translateX(3px)}\n.mqcm-cta-row{display:flex;flex-wrap:wrap;gap:10px;margin:18px 0;justify-content:center}\n.mqcm-signup-wall{background:linear-gradient(135deg,#EEF4FF 0%,#FFFFFF 100%);border:2px solid #1e3a8a;border-radius:12px;padding:18px 20px;margin:16px 0;text-align:center}\n.mqcm-signup-wall__title{font-family:'Bricolage Grotesque','Inter',sans-serif;font-size:1.02rem;font-weight:700;color:#1e3a8a;margin-bottom:8px}\n.mqcm-signup-wall__body{font-size:.9rem;line-height:1.55;color:#334155;margin-bottom:14px}\n.mqcm-signup-wall__body strong{color:#1e3a8a}\n.mqcm-signup-wall__foot{font-size:.76rem;color:#94a3b8;margin-top:10px;letter-spacing:.02em}\n.mqcm-btn-attest{background:linear-gradient(135deg,#16a34a 0%,#0f5132 100%);color:#fff;box-shadow:0 6px 14px -6px rgba(22,163,74,.5),inset 0 1px 0 rgba(255,255,255,.15)}\n.mqcm-btn-attest:hover{transform:translateY(-1px);box-shadow:0 10px 20px -8px rgba(22,163,74,.6)}\n.mqcm-btn-attest:disabled{opacity:.6;cursor:default}\n.mqcm-btn-attest.mqcm-vig-loading{cursor:progress}\n.mqcm-attest-hint{font-size:.84rem;color:#92400e;background:#fef3c7;border-left:3px solid #d97706;padding:8px 14px;border-radius:0 6px 6px 0;margin:6px 0;font-style:italic;text-align:left;width:100%}\n.mqcm-attest-result{background:linear-gradient(135deg,#D1FAE5 0%,#FFFFFF 100%);border:2px solid #16a34a;border-radius:12px;padding:18px 20px;margin:14px 0;text-align:center;animation:mqcmFadeIn .35s ease both}\n.mqcm-attest-level{font-family:'Bricolage Grotesque','Inter',sans-serif;font-size:1.05rem;color:#0f5132;margin-bottom:12px;font-weight:600}\n.mqcm-attest-level strong{color:#16a34a;text-transform:uppercase;letter-spacing:.03em}\n.mqcm-attest-note{font-size:.82rem;color:#6B7A8F;font-style:italic;margin-top:10px;line-height:1.45}\n.mqcm-share-prompt{margin-top:14px;padding-top:14px;border-top:1px dashed #16a34a}\n.mqcm-share-title{font-family:'Bricolage Grotesque','Inter',sans-serif;font-weight:700;color:#0f5132;font-size:.95rem;margin-bottom:10px}\n.mqcm-share-row{display:flex;flex-wrap:wrap;gap:8px;justify-content:center}\n.mqcm-share-btn{display:inline-flex;align-items:center;gap:6px;padding:8px 14px;border-radius:9999px;font-family:'Inter',sans-serif;font-weight:600;font-size:.82rem;text-decoration:none;border:1.5px solid transparent;cursor:pointer;color:#fff;transition:transform .15s}\n.mqcm-share-btn:hover{transform:translateY(-1px)}\n.mqcm-share-li{background:#0A66C2}\n.mqcm-share-wa{background:#25D366;color:#063}\n.mqcm-share-dl{background:#fff;color:#1e3a8a;border-color:rgba(30,58,138,.35)}\n.mqcm-vigilance-panel{background:linear-gradient(135deg,#F0F9F4 0%,#FFFFFF 100%);border:1px solid #D8D0BF;border-radius:12px;padding:18px 20px;margin-top:18px}\n.mqcm-vigilance-loading{text-align:center;color:#6B7A8F;font-family:'JetBrains Mono',monospace;font-size:.85rem;padding:12px}\n.mqcm-vig-head{font-family:'Bricolage Grotesque','Inter',sans-serif;font-size:.95rem;color:#0E1A2B;margin-bottom:14px;padding-bottom:10px;border-bottom:1px dotted #D8D0BF}\n.mqcm-vig-head strong{color:#16a34a}\n.mqcm-vig-row{display:flex;align-items:center;gap:12px;padding:10px 0;border-bottom:1px solid #EFEAE0}\n.mqcm-vig-row:last-child{border-bottom:none}\n.mqcm-vig-ic{font-size:1.6rem;flex-shrink:0;width:32px;text-align:center}\n.mqcm-vig-body{flex:1;min-width:0}\n.mqcm-vig-label{font-family:'Bricolage Grotesque','Inter',sans-serif;font-size:.92rem;color:#0E1A2B;margin-bottom:5px;font-weight:600}\n.mqcm-vig-count{font-family:'JetBrains Mono',monospace;font-size:.78rem;color:#6B7A8F;font-weight:500;margin-left:6px}\n.mqcm-vig-bar{height:5px;background:#EFEAE0;border-radius:999px;overflow:hidden}\n.mqcm-vig-bar-fill{height:100%;background:linear-gradient(90deg,#1e3a8a,#3b82f6);border-radius:999px;transition:width .4s ease-out}\n.mqcm-vig-row--ok .mqcm-vig-bar-fill{background:linear-gradient(90deg,#16a34a,#22c55e)}\n.mqcm-vig-row--ok .mqcm-vig-label{color:#0f5132}\n.mqcm-vig-row--ok .mqcm-vig-count{color:#16a34a;font-weight:700}\n.mqcm-vig-row--todo{opacity:.55}\n.mqcm-vig-remaining{font-family:'JetBrains Mono',monospace;font-size:.74rem;color:#6B7A8F;flex-shrink:0;white-space:nowrap}\n.mqcm-vig-btn{padding:7px 14px;font-size:.82rem;flex-shrink:0;white-space:nowrap;min-height:0}\n.mqcm-vig-next{margin-top:14px;padding:12px 14px;background:#FEF9E7;border:1px solid #F6E05E;border-radius:8px;font-size:.88rem;line-height:1.5;color:#5C4408}\n.mqcm-vig-next strong{color:#3D2C04}\n.mqcm-vig-next--max{background:#D1FAE5;border-color:#16a34a;color:#0f5132}\n.mqcm-vig-next--max strong{color:#15803d}\n.mqcm-vig-foot{margin-top:12px;text-align:center;font-size:.85rem}\n.mqcm-vig-foot a{color:#1e3a8a;font-weight:600;text-decoration:none;border-bottom:1px dotted #1e3a8a;transition:color .15s}\n.mqcm-vig-foot a:hover{color:#0f1e4f;border-bottom-style:solid}\n.mqcm-cta{margin:0 4px}\n.mqcm-meta{font-size:.84rem;color:#6B7A8F;text-align:center;margin-top:14px}\n.mqcm-link{color:#1e3a8a;text-decoration:none;font-weight:600;border-bottom:1px solid #1e3a8a}\n.mqcm-link:hover{color:#0f1e4f}\n.mqcm-disclaimer{font-size:.7rem;color:#9ca3af;text-align:center;font-style:italic;margin-top:18px;padding-top:10px;border-top:1px dotted #D8D0BF}\n.mqcm-loading{padding:60px 20px;text-align:center;color:#6B7A8F;font-family:'JetBrains Mono',monospace;font-size:.85rem}\n.mqcm-error{padding:18px;background:#FEE2E2;
13color:#B91C1C;border-radius:10px;text-align:center;font-weight:600;font-size:.9rem;margin:24px 0}\n.mqcm-score-big{font-family:'Anton','Inter',sans-serif;font-size:3.5rem;color:#0f1e4f;line-height:1;text-align:center;margin-bottom:4px}\n.mqcm-score-pct{font-family:'JetBrains Mono',monospace;font-size:.85rem;color:#6B7A8F;text-align:center;text-transform:uppercase;letter-spacing:.08em;margin-bottom:18px}\n.mqcm-score-msg{font-size:.95rem;line-height:1.55;color:#2C3A4E;background:#FAF8F2;border-left:3px solid #1e3a8a;border-radius:0 8px 8px 0;padding:14px 18px;margin-bottom:14px}\n.mqcm-band-perfect{border-color:#16a34a;background:linear-gradient(135deg,#D1FAE5,#FFFFFF)}\n.mqcm-band-perfect .mqcm-score-big{color:#16a34a}\n.mqcm-band-high{border-color:#1e3a8a;background:linear-gradient(135deg,#dbeafe,#FFFFFF)}\n.mqcm-band-mid{border-color:#d97706;background:linear-gradient(135deg,#FEF3C7,#FFFFFF)}\n.mqcm-band-mid .mqcm-score-big{color:#92400E}\n.mqcm-band-low{border-color:#dc2626;background:linear-gradient(135deg,#FEE2E2,#FFFFFF)}\n.mqcm-band-low .mqcm-score-big{color:#dc2626}\n.mqcm-hint-box{background:#FEF3C7;border:2px dashed #d97706;border-radius:10px;padding:16px;margin-top:16px}\n.mqcm-hint-label{font-family:'JetBrains Mono',monospace;font-size:.74rem;color:#d97706;text-transform:uppercase;letter-spacing:.06em;font-weight:700;margin-bottom:6px}\n.mqcm-hint-q{font-size:.9rem;color:#6B7A8F;margin-bottom:8px}\n.mqcm-hint-text{color:#1A2A40;font-size:.94rem;line-height:1.5;font-weight:500}\n.mqcm-corrections{margin-top:20px;border-top:1px solid #D8D0BF;padding-top:14px}\n.mqcm-corrections-summary{cursor:pointer;font-weight:600;color:#1e3a8a;font-size:.95rem;padding:8px 0}\n.mqcm-corrections-summary:hover{color:#0f1e4f}\n.mqcm-correction-item{background:#FAF8F2;border:1px solid #D8D0BF;border-radius:8px;padding:16px;margin:12px 0}\n.mqcm-correction-q{font-weight:600;color:#0E1A2B;margin-bottom:8px;font-size:.95rem}\n.mqcm-correction-answer{font-family:'JetBrains Mono',monospace;font-size:.82rem;color:#16a34a;background:#D1FAE5;padding:6px 10px;border-radius:5px;display:inline-block;margin-bottom:8px}\n.mqcm-correction-wrong{color:#dc2626;font-size:.82rem;margin-bottom:8px}\n.mqcm-correction-intuition{background:#FEF0B4;border-left:3px solid #b45309;padding:10px 14px;border-radius:0 6px 6px 0;font-size:.88rem;margin-bottom:8px;color:#78350f}\n.mqcm-correction-intuition-label{display:block;font-family:'JetBrains Mono',monospace;font-size:.7rem;color:#92400e;text-transform:uppercase;letter-spacing:.06em;font-weight:700;margin-bottom:4px}\n.mqcm-correction-exp{font-size:.9rem;color:#2C3A4E;line-height:1.55;margin-bottom:10px}\n.mqcm-correction-mingeau{background:#dbeafe;border-left:3px solid #1e3a8a;padding:10px 14px;border-radius:0 6px 6px 0;font-size:.88rem;margin-bottom:8px;font-style:italic}\n.mqcm-correction-mingeau-label{display:block;font-family:'JetBrains Mono',monospace;font-size:.7rem;color:#1e3a8a;text-transform:uppercase;letter-spacing:.06em;font-weight:700;margin-bottom:4px;font-style:normal}\n.mqcm-correction-meta{font-family:'JetBrains Mono',monospace;font-size:.78rem;color:#6B7A8F;margin-top:6px}\n.mqcm-correction-meta a{color:#1e3a8a;font-weight:600}\n/* Correction ALLÃGÃE anon/FREE (encart neutre bleu pâle, distinct du crème EXPERT) */\n.mqcm-corrections-light{margin-top:16px;background:#F3F6FB;border:1px solid #C7D2E3;border-radius:12px;padding:16px 18px;text-align:left}\n.mqcm-cl-title{font-weight:700;color:#1e3a8a;font-size:.95rem;margin-bottom:12px}\n.mqcm-cl-item{background:#FFFFFF;border:1px solid #DDE5F0;border-radius:8px;padding:12px 14px;margin:10px 0}\n.mqcm-cl-item.mqcm-cl-wrong{border-left:3px solid #dc2626}\n.mqcm-cl-item.mqcm-cl-ok{border-left:3px solid #16a34a}\n.mqcm-cl-q{font-weight:600;color:#0E1A2B;margin-bottom:6px;font-size:.92rem}\n.mqcm-cl-your{color:#dc2626;font-size:.82rem;margin-bottom:4px}\n.mqcm-cl-good{color:#16a34a;font-size:.85rem;margin-bottom:6px}\n.mqcm-cl-exp{font-size:.88rem;color:#2C3A4E;line-height:1.55}\n.mqcm-cl-foot{font-size:.78rem;color:#6B7A8F;margin-top:10px;font-style:italic}\n@media (max-width: 600px){\n .mqcm-container{padding:18px;margin:24px 0}\n .mqcm-h{font-size:1.1rem}\n .mqcm-question{font-size:1rem}\n .mqcm-option{font-size:.9rem;padding:10px 14px}\n .mqcm-score-big{font-size:2.8rem}\n}\n.mqcm-sticky-card{display:none;position:fixed;bottom:24px;left:24px;z-index:9000;width:240px;font-family:'Inter',system-ui,-apple-system,sans-serif;background:linear-gradient(135deg,#FFFFFF 0%,#F0F4FA 100%);border:1px solid #D8D0BF;border-radius:16px;padding:18px 16px;box-shadow:0 18px 36px -16px rgba(30,58,138,.3),0 4px 8px -4px rgba(30,58,138,.1);animation:mqcmStickyIn .55s ease-out;text-align:center;color:#2C3A4E;line-height:1.4;overflow:hidden}\n.mqcm-sticky-card::before{content:'';position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,#1e3a8a 0%,#3b82f6 50%,#1e3a8a 100%)}\n.mqcm-sticky-close{position:absolute;top:8px;right:8px;width:24px;height:24px;border:none;background:rgba(30,58,138,.08);color:#1e3a8a;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .15s;padding:0;z-index:1}\n.mqcm-sticky-close:hover{background:rgba(30,58,138,.2);transform:rotate(90deg)}\n.mqcm-sticky-icon-wrap{display:flex;justify-content:center;margin-bottom:10px}\n.mqcm-sticky-icon{display:inline-flex;align-items:center;justify-content:center;width:44px;height:44px;background:linear-gradient(135deg,#dbeafe,#FFFFFF);border:1px solid #dbeafe;border-radius:50%;font-size:1.4rem;box-shadow:0 6px 12px -6px rgba(30,58,138,.25);color:#1e3a8a}\n.mqcm-sticky-eyebrow{font-family:'JetBrains Mono',monospace;font-size:.66rem;color:#1e3a8a;text-transform:uppercase;letter-spacing:.08em;font-weight:700;margin-bottom:6px}\n.mqcm-sticky-title{font-family:'Bricolage Grotesque','Inter',sans-serif;font-size:1rem;font-weight:700;color:#0E1A2B;margin-bottom:10px;line-height:1.25;letter-spacing:-.01em}\n.mqcm-sticky-pills{display:flex;justify-content:center;gap:4px;margin-bottom:14px;flex-wrap:wrap}\n.mqcm-sticky-pill{display:inline-flex;align-items:center;font-family:'JetBrains Mono',monospace;font-size:.62rem;color:#1e3a8a;background:#dbeafe;padding:3px 8px;border-radius:999px;font-weight:600;letter-spacing:.03em}\n.mqcm-sticky-pill-soft{background:#FAF8F2;color:#6B7A8F;border:1px solid #D8D0BF}\n.mqcm-sticky-btn{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;padding:11px 14px;background:linear-gradient(135deg,#1e3a8a,#0f1e4f);color:white;border:none;border-radius:10px;cursor:pointer;font-family:inherit;font-weight:600;font-size:.82rem;text-align:center;box-shadow:0 6px 14px -6px rgba(30,58,138,.5),inset 0 1px 0 rgba(255,255,255,.15);transition:all .2s}\n.mqcm-sticky-btn:hover{transform:translateY(-1px);box-shadow:0 10px 18px -6px rgba(30,58,138,.6),inset 0 1px 0 rgba(255,255,255,.18)}\n.mqcm-sticky-arrow{font-size:1.05em;transition:transform .2s}\n.mqcm-sticky-btn:hover .mqcm-sticky-arrow{transform:translateX(2px)}\n@keyframes mqcmStickyIn{from{opacity:0;transform:translateY(-12px)}to{opacity:1;transform:translateY(0)}}\n.mqcm-sticky-pill-collapsed{display:none;position:fixed;bottom:20px;right:16px;z-index:9000;width:54px;height:54px;animation:mqcmStickyIn .4s ease-out}\n.mqcm-sticky-pill-btn{width:54px;height:54px;border:none;background:linear-gradient(135deg,#1e3a8a 0%,#0f1e4f 100%);border-radius:50%;cursor:pointer;box-shadow:0 12px 24px -10px rgba(30,58,138,.55),0 2px 6px -2px rgba(30,58,138,.25);display:flex;align-items:center;justify-content:center;transition:transform .2s;padding:0;position:relative}\n.mqcm-sticky-pill-btn:hover{transform:scale(1.06)}\n.mqcm-sticky-pill-collapsed .mqcm-sticky-icon{width:auto;height:auto;margin:0;background:none;border:none;border-radius:0;box-shadow:none;color:#FFFFFF}\n.mqcm-sticky-pill-close{position:absolute;top:-4px;right:-4px;width:20px;height:20px;border:1px solid #D8D0BF;background:#FFFFFF;color:#6B7A8F;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;z-index:2;box-shadow:0 2px 6px -2px rgba(0,0,0,.2);transition:all .15s}\n.mqcm-sticky-pill-close:hover{background:#FEE2E2;
13color:#dc2626;border-color:#dc2626}\n@media (min-width: 1280px){\n .mqcm-sticky-card.mqcm-sticky--show{display:block}\n}\n@media (max-width: 1279px){\n .mqcm-sticky-pill-collapsed.mqcm-sticky--show{display:block}\n}\n"; 14 if (!document.getElementById('mqcm-styles')) { 15 var styleEl = document.createElement('style'); 16 styleEl.id = 'mqcm-styles'; 17 styleEl.textContent = CSS; 18 document.head.appendChild(styleEl); 19 } 20 21 var state = { 22 data: null, 23 currentIdx: 0, 24 answers: [], 25 score: null, 26 hintRevealed: false, 27 startedAt: Date.now(), 28 advancing: false, 29 impressionTracked: false, 30 }; 31 32 function gtag_safe(name, params) { 33 if (typeof window.gtag === 'function') { 34 try { window.gtag('event', name, params || {}); } catch(_) {} 35 } 36 } 37 38 function esc(s) { 39 return String(s == null ? '' : s) 40 .replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>') 41 .replace(/"/g, '"').replace(/'/g, '''); 42 } 43 44 function render(html) { placeholder.innerHTML = html; } 45 46 function init() { 47 render('<div class="mqcm-loading">Chargement du test de veilleâ¦</div>'); 48 fetch(API_BASE + '/reglementation/api/blog-quiz/' + encodeURIComponent(postIdent) + '/data', { credentials: 'same-origin' }) 49 .then(function(r){ 50 if (r.status === 404) throw new Error('not_found'); 51 if (!r.ok) throw new Error('http_' + r.status); 52 return r.json(); 53 }) 54 .then(function(data){ 55 state.data = data; 56 state.answers = new Array(data.questions.length).fill(null); 57 renderIntro(); 58 injectStickyCta(data); 59 trackImpressionWhenVisible(data); 60 }) 61 .catch(function(e){ 62 if (e.message === 'not_found') { 63 placeholder.style.display = 'none'; 64 return; 65 } 66 render('<div class="mqcm-error">Test de veille indisponible (' + esc(e.message) + ')</div>'); 67 }); 68 } 69 70 // Ãmet l'impression « widget réellement VU » (et non « page chargée ») via 71 // IntersectionObserver. Avant : l'event partait au chargement â surcomptage massif. 72 // Fallback : émission immédiate si l'API n'existe pas. Tunnel : started(vu) > start_clicked > completed. 73 function trackImpressionWhenVisible(data) { 74 function fire() { 75 if (state.impressionTracked) return; 76 state.impressionTracked = true; 77 gtag_safe('blog_quiz_started_' + (data.tier || 'anonymous'), { 78 post_slug: postIdent, question_count: data.questions.length, 79 }); 80 } 81 if ('IntersectionObserver' in window && placeholder) { 82 var io = new IntersectionObserver(function(entries){ 83 for (var i = 0; i < entries.length; i++) { 84 if (entries[i].isIntersecting) { fire(); io.disconnect(); break; } 85 } 86 }, { threshold: 0.25 }); 87 io.observe(placeholder); 88 } else { 89 fire(); 90 } 91 } 92 93 function renderIntro() { 94 var d = state.data; 95 // Matrice tier à visibility (3 profils explicites) : 96 // - tier user vient du backend : 'anonymous' | 'free' | 'expert' 97 // - article visibility 'public' vs autre (paywallé EXPERT) 98 // Cas spécial : anon/FREE sur article EXPERT â ton « intuition challenge » (preserve l'angle dissonance). 99 // Tous les autres cas : ton « vérification de veille » adapté au profil. 100 var articleIsExpert = (d.post_visibility !== 'public'); 101 var tier = d.tier || 'anonymous'; 102 var eyebrow, title, sub, btnLabel, icon; 103 104 if (articleIsExpert && tier !== 'expert') { 105 icon = '\ud83d\udd0d'; // ð 106 eyebrow = 'Pas (encore) abonné EXPERT ?'; 107 title = 'Vérifie si tu maîtrises ce sujet - sans avoir besoin du compte EXPERT'; 108 sub = 'En ' + d.questions.length + ' questions chrono. Si tu as tout bon, tu connais déjà l\'essentiel.'; 109 btnLabel = 'Vérifier mon intuition'; 110 } else if (tier === 'expert') { 111 icon = '\u2696\ufe0f'; // âï¸ 112 eyebrow = 'Veille continue · R.631-7 CSI'; 113 title = 'Confirmer ma veille sur ce sujet'; 114 sub = 'Test rapide (~2 min) pour valider ta compréhension. Comptabilisé pour ton <strong>attestation de veille datée</strong> (Qualiopi RNQ critère 6).'; 115 btnLabel = 'Démarrer le test'; 116 } else if (tier === 'free') { 117 icon = '\ud83c\udfaf'; // ð¯ 118 eyebrow = 'Vérification de veille'; 119 title = 'Tester ma compréhension + débloquer mon palier'; 120 sub = 'En 2 min chrono. Comptabilisé pour ton palier de <strong>veille</strong> (R.631-7 CSI, Qualiopi RNQ critère 6).'; 121 btnLabel = 'Démarrer le test'; 122 } else { 123 icon = '\ud83c\udfaf'; // ð¯ 124 eyebrow = 'Vérification de veille'; 125 title = 'Tester ma compréhension en 2 min'; 126 sub = 'Vérifie ta compréhension de l\'article. Aucun compte requis - crée-toi un compte gratuit pour suivre tes paliers de veille.'; 127 btnLabel = 'Démarrer le test'; 128 } 129 render( 130 '<div class="mqcm-container mqcm-intro">' + 131 ' <div class="mqcm-icon-wrap"><span class="mqcm-icon">' + icon + '</span></div>' + 132 ' <div class="mqcm-header">' + 133 ' <div class="mqcm-eyebrow">' + eyebrow + '</div>' + 134 ' <h3 class="mqcm-h">' + title + '</h3>' + 135 ' <div class="mqcm-pills">' +
136 ' <span class="mqcm-pill">' + d.questions.length + ' questions</span>' + 137 ' <span class="mqcm-pill mqcm-pill-soft">~2 min</span>' + 138 ' </div>' + 139 ' <p class="mqcm-sub">' + sub + '</p>' + 140 ' </div>' + 141 ' <button class="mqcm-btn mqcm-btn-primary mqcm-btn-large" id="mqcm-start">' + btnLabel + ' <span class="mqcm-btn-arrow">→</span></button>' + 142 ' <p class="mqcm-meta"><a href="' + API_BASE + '/blog-quiz/' + esc(d.post_slug) + '/" class="mqcm-link">Ouvrir en plein écran →</a></p>' + 143 renderDisclaimer(d) + 144 '</div>' 145 ); 146 var btn = document.getElementById('mqcm-start'); 147 if (btn) btn.onclick = function(){ 148 gtag_safe('blog_quiz_start_clicked', { post_slug: postIdent, tier: (state.data && state.data.tier) || 'anonymous' }); 149 state.startedAt = Date.now(); 150 renderQuestion(); 151 }; 152 } 153 154 function renderDisclaimer(d) { 155 var text = d.needs_review 156 ? 'Aperçu en relecture' 157 : 'Aperçu validé par M. Mingeau'; 158 return '<p class="mqcm-disclaimer">
158' + text + '</p>'; 159 } 160 161 function renderQuestion() { 162 var d = state.data; 163 var q = d.questions[state.currentIdx]; 164 var total = d.questions.length; 165 var optsHtml = q.options.map(function(opt, i){ 166 var sel = state.answers[state.currentIdx] === i ? ' mqcm-selected' : ''; 167 return '<button class="mqcm-option' + sel + '" data-idx="' + i + '">' + esc(opt) + '</button>'; 168 }).join(''); 169 var isLast = state.currentIdx === total - 1; 170 var answered = state.answers.filter(function(a){ return a !== null && a !== undefined; }).length; 171 var progressPct = Math.round(answered * 100 / total); 172 var navHint = isLast 173 ? '\u2728 Dernière question - clique pour voir ton score' 174 : 'Clique sur ta réponse pour passer à la suivante'; 175 render( 176 '<div class="mqcm-container">' + 177 ' <div class="mqcm-progress-bar"><div class="mqcm-progress-bar-fill" style="width:' + progressPct + '%"></div></div>' + 178 ' <div class="mqcm-progress">Question ' + (state.currentIdx + 1) + ' / ' + total + (answered > 0 ? ' \u00b7 ' + answered + ' répondue' + (answered > 1 ? 's' : '') : '') + '</div>' + 179 ' <div class="mqcm-question">' + esc(q.question) + '</div>' + 180 ' <div class="mqcm-options">' + optsHtml + '</div>' + 181 ' <div class="mqcm-nav">' + 182 ' <button class="mqcm-btn mqcm-btn-secondary" id="mqcm-prev"' + (state.currentIdx === 0 ? ' disabled' : '') + '>← Précédent</button>' + 183 ' <div class="mqcm-nav-hint">' + navHint + '</div>' + 184 ' </div>' + 185 '</div>' 186 ); 187 Array.prototype.forEach.call(placeholder.querySelectorAll('.mqcm-option'), function(el){ 188 el.onclick = function(){ 189 if (state.advancing) return; 190 state.answers[state.currentIdx] = parseInt(el.getAttribute('data-idx'), 10); 191 Array.prototype.forEach.call(placeholder.querySelectorAll('.mqcm-option'), function(opt){ opt.classList.remove('mqcm-selected'); }); 192 el.classList.add('mqcm-selected'); 193 Array.prototype.forEach.call(placeholder.querySelectorAll('.mqcm-option'), function(opt){ opt.disabled = true; }); 194 state.advancing = true; 195 setTimeout(function(){ 196 state.advancing = false; 197 if (isLast) submitQcm(); 198 else { state.currentIdx++; renderQuestion(); } 199 }, 400); 200 }; 201 }); 202 var prev = document.getElementById('mqcm-prev'); 203 if (prev) prev.onclick = function(){ 204 if (state.advancing) return; 205 if (state.currentIdx > 0) { state.currentIdx--; renderQuestion(); } 206 }; 207 } 208 209 function submitQcm() { 210 render('<div class="mqcm-loading">Calcul du scoreâ¦</div>'); 211 fetch(API_BASE + '/reglementation/api/blog-quiz/' + encodeURIComponent(state.data.post_uuid) + '/attempt', { 212 method: 'POST', credentials: 'same-origin', 213 headers: { 'Content-Type': 'application/json' }, 214 body: JSON.stringify({ 215 answers: state.answers, 216 duration_sec: Math.round((Date.now() - state.startedAt) / 1000), 217 started_at: state.startedAt, 218 finished_at: Date.now(), 219 }), 220 }) 221 .then(function(r){ return r.json(); }) 222 .then(function(result){ 223 state.score = result; 224 gtag_safe('blog_quiz_completed_' + (result.tier || 'anonymous'), { 225 post_slug: state.data.post_slug, score: result.score, total: result.total, 226 }); 227 renderScore(); 228 }) 229 .catch(function(e){ render('<div class="mqcm-error">Erreur soumission : ' + esc(e.message) + '</div>'); }); 230 } 231
232 function renderScore() { 233 var d = state.data; 234 var s = state.score; 235 var pct = s.total > 0 ? Math.round(s.score * 100 / s.total) : 0; 236 var bandClass = pct === 100 ? 'mqcm-band-perfect' : pct >= 80 ? 'mqcm-band-high' : pct >= 60 ? 'mqcm-band-mid' : 'mqcm-band-low'; 237 238 var isConnected = s.is_expert || s.is_free; 239 var passed = pct >= 60; 240 241 // ANONYME : mur signup léger (ne bloque pas le score, posé juste sous le résultat). 242 // 62 % des parties sont anonymes et ne comptent pour AUCUNE attestation tant qu'il 243 // n'y a pas de compte â on transforme le résultat en accroche d'inscription. 244 var signupWall = ''; 245 var ctaHtml = ''; 246 if (!isConnected) { 247 var sUrl = s.cta_url || '/signup/?from=blog-quiz'; 248 signupWall = 249 '<div class="mqcm-signup-wall">' + 250 ' <div class="mqcm-signup-wall__title">' + (passed ? 'â Test réussi - mais il n\'est pas encore compté' : 'ð Ton score n\'est pas encore enregistré') + '</div>' + 251 ' <div class="mqcm-signup-wall__body">Crée ton <strong>compte gratuit</strong> pour suivre ta veille : <strong>2 tests réussis cette semaine = ta 1ʳᵠattestation de veille datée</strong>, opposable employeur / OF (critère Qualiopi). Sans compte, rien n\'est comptabilisé.</div>' + 252 ' <a href="' + esc(sUrl) + '" class="mqcm-btn mqcm-btn-primary mqcm-btn-large mqcm-cta" data-track="cta" data-cta-kind="signup">Créer mon compte gratuit â</a>' + 253 ' <div class="mqcm-signup-wall__foot">Gratuit · sans carte bancaire · paliers hebdo â annuel</div>' + 254 '</div>'; 255 } else if (s.cta_url) { 256 // FREE : CTA upgrade EXPERT (EXPERT ne reçoit pas de cta_url). 257 ctaHtml = '<a href="' + esc(s.cta_url) + '" class="mqcm-btn mqcm-btn-primary mqcm-cta" data-track="cta" data-cta-kind="upgrade">' + esc(s.cta_label || 'Découvrir') + '</a>'; 258 } 259 var hintCta = ''; 260 if (s.is_free && !state.hintRevealed) { 261 hintCta = '<button class="mqcm-btn mqcm-btn-secondary mqcm-hint-btn" id="mqcm-hint">Révéler 1 indice (FREE)</button>'; 262 } 263 // Panneau attestation : chargé séparément (GET /me/vigilance) car nécessite cookie Ghost 264 // â on insère juste un placeholder ici, et on lance le fetch après render. 265 var attestPanel = isConnected ? '<div id="mqcm-vigilance-panel" class="mqcm-vigilance-panel"><div class="mqcm-vigilance-loading">ð Chargement de tes paliers de veilleâ¦</div></div>' : ''; 266 267 // Correction allégée (anon/FREE, systematique) : fondement pour tous les items, pas seulement les echecs. 268 var lightHtml = (Array.isArray(s.corrections_light) && s.corrections_light.length) 269 ? renderLightCorrections(s.corrections_light, s.answers, (state.data && state.data.questions) || []) 270 : ''; 271 272 render( 273 '<div class="mqcm-container ' + bandClass + '">' + 274 ' <div class="mqcm-score-big">' + s.score + '/' + s.total + '</div>' + 275 ' <div class="mqcm-score-pct">' + pct + '% de bonnes réponses</div>' + 276 ' <div class="mqcm-score-msg">' + esc(s.message || '') + '</div>' + 277 lightHtml + 278 ((ctaHtml || hintCta) ? ' <div class="mqcm-cta-row">' + ctaHtml + hintCta + '</div>' : '') + 279 signupWall + 280 ' <div id="mqcm-hint-zone"></div>' + 281 attestPanel + 282 ' <div id="mqcm-attest-zone"></div>' + 283 (Array.isArray(s.questions) && s.questions.length ? renderExpertCorrections(s.questions, s.answers) : '') + 284 ' <p class="mqcm-meta"><a href="' + API_BASE + '/blog-quiz/' + esc(d.post_slug) + '/" class="mqcm-link">Voir le test en plein écran →</a></p>' + 285 '</div>' 286 ); 287 if (lightHtml) gtag_safe('blog_quiz_correction_shown', { post_slug: d.post_slug, tier: s.tier, score: s.score }); 288 var hintBtn = document.getElementById('mqcm-hint'); 289 if (hintBtn) hintBtn.onclick = revealHint; 290 // Charge dynamiquement les paliers de veille pour les FREE+EXPERT 291 if (isConnected) loadVigilancePanel(); 292 Array.prototype.forEach.call(placeholder.querySelectorAll('[data-track]'), function(el){ 293 el.addEventListener('click', function(){ 294 var kind = el.getAttribute('data-cta-kind') || 'signup'; 295 gtag_safe('blog_quiz_cta_' + kind + '_clicked', { post_slug: d.post_slug, tier: s.tier, score: s.score }); 296 // Persiste le clic en base (cta_signup_clicked / cta_upgrade_clicked). 297 // keepalive : la requête survit à la navigation déclenchée par le lien. 298 try { 299 fetch(API_BASE + '/reglementation/api/blog-quiz/' + encodeURIComponent(d.post_uuid) + '/cta', { 300 method: 'POST', credentials: 'include', keepalive: true, 301 headers: { 'Content-Type': 'application/json' }, 302 body: JSON.stringify({ kind: kind }), 303 }).catch(function(){}); 304 } catch (_e) {} 305 }); 306 }); 307 } 308 309 function renderExpertCorrections(questions, answers) { 310 return '<details class="mqcm-corrections" open><summary class="mqcm-corrections-summary">Voir les corrections détaillées (' + questions.length + ')</summary>' + 311 questions.map(function(q, i){ 312 var userAns = answers[i]; 313 var isOk = userAns === q.correct; 314 var jurisp = q.jurisprudence_ref && q.jurisprudence_ref.label 315 ? '<div class="mqcm-correction-meta">\u2696\ufe0f Jurisprudence : ' + (q.jurisprudence_ref.url ? '<a href="' + esc(q.jurisprudence_ref.url) + '" target="_blank" rel="noopener">' + esc(q.jurisprudence_ref.label) + '</a>' : esc(q.jurisprudence_ref.label)) + '</div>' 316 : ''; 317 var legifrance = q.legifrance_url 318 ? '<div class="mqcm-correction-meta">\ud83d\udcdc <a href="' + esc(q.legifrance_url) + '" target="_blank" rel="noopener">Texte officiel sur Légifrance →</a></div>' 319 : ''; 320 var intuition = q.common_intuition 321 ? '<div class="mqcm-correction-intuition"><span class="mqcm-correction-intuition-label">\ud83d\udcad Ce qu\'on croit souvent</span>' + esc(q.common_intuition) + '</div>' 322 : ''; 323 var mingeauShort = q.mingeau_comment_short && !q.mingeau_comment_full 324 ? '<div class="mqcm-correction-mingeau"><span class="mqcm-correction-mingeau-label">\u270d\ufe0f M. Mingeau</span>' + esc(q.mingeau_comment_short) + '</div>' 325 : ''; 326 var mingeauFull = q.mingeau_comment_full 327 ? '<div class="mqcm-correction-mingeau"><span class="mqcm-correction-mingeau-label">\u270d\ufe0f M. Mingeau</span>' + esc(q.mingeau_comment_full) + '</div>' 328 : mingeauShort; 329 return '<div class="mqcm-correction-item">' + 330 ' <div class="mqcm-correction-q">Q' + (i + 1) + '. ' + esc(q.question) + '</div>' + 331 ' <div class="mqcm-correction-answer">Bonne réponse : ' + String.fromCharCode(65 + q.correct) + '. ' + esc(q.options[q.correct]) + '</div>' + 332 (isOk ? '' : '<div class="mqcm-correction-wrong">Votre réponse : ' + (userAns >= 0 ? String.fromCharCode(65 + userAns) + '. ' + esc(q.options[userAns] || '') : '(non répondue)') + '</div>') + 333 intuition + 334 ' <div class="mqcm-correction-exp">' + esc(q.explanation_expert || '') + '</div>' + 335 mingeauFull + jurisp + legifrance + 336 '</div>'; 337 }).join('') + 338 '</details>'; 339 } 340 341 // Correction ALLÃGÃE pour anon/FREE en échec : bonne réponse + explication grand public, 342 // SANS les champs premium (jurisprudence/legifrance/mingeau, réservés EXPERT). 343 // dq = state.data.questions (pour retrouver le libellé de la réponse de l'utilisateur). 344 // Rendu pour TOUS les items : bonne reponse + fondement, que ce soit un echec ou une reussite. 345 function renderLightCorrections(items, answers, dq) {
346 var hasAnyWrong = items.some(function(c){ return !!c.was_wrong; }); 347 var blockTitle = hasAnyWrong ? '\ud83d\udcd8 Fondements des réponses' : '\u2705 Toutes les réponses - fondements'; 348 return '<div class="mqcm-corrections-light">' + 349 '<div class="mqcm-cl-title">' + blockTitle + '</div>' + 350 items.map(function(c, i){ 351 var wrong = !!c.was_wrong; 352 var correct = !!c.was_correct; 353 var ua = answers ? answers[i] : null; 354 var uaLabel = (wrong && ua != null && ua >= 0 && dq && dq[i] && dq[i].options) ? dq[i].options[ua] : null; 355 return '<div class="mqcm-cl-item' + (wrong ? ' mqcm-cl-wrong' : ' mqcm-cl-ok') + '">' + 356 '<div class="mqcm-cl-q">' + esc(c.question) + '</div>' + 357 (uaLabel ? '<div class="mqcm-cl-your">\u2717 Ta réponse : ' + esc(uaLabel) + '</div>' : '') + 358 (correct && !uaLabel ? '<div class="mqcm-cl-correct-badge">\u2713 Bonne réponse</div>' : '') + 359 '<div class="mqcm-cl-good">' + (correct ? 'Fondement : <strong>' : '\u2713 Bonne réponse : <strong>') + esc(c.correct_label) + '</strong></div>' + 360 (c.explanation_simple ? '<div class="mqcm-cl-exp">' + esc(c.explanation_simple) + '</div>' : '') + 361 '</div>'; 362 }).join('') + 363 '<div class="mqcm-cl-foot">Corrections complètes (fondement juridique, jurisprudence, commentaire du formateur) réservées aux membres EXPERT.</div>' + 364 '</div>'; 365 } 366 367 // Charge dynamiquement les 5 paliers de veille pour l'utilisateur connecté. 368 function loadVigilancePanel() { 369 var panel = document.getElementById('mqcm-vigilance-panel'); 370 if (!panel) return; 371 fetch(API_BASE + '/reglementation/api/blog-quiz/me/vigilance', { 372 method: 'GET', credentials: 'include', 373 }) 374 .then(function(r){ return r.json(); }) 375 .then(function(j){ 376 if (j.error || !j.levels) { 377 panel.innerHTML = '<div class="mqcm-error">Impossible de charger tes paliers (' + esc(j.error || 'erreur') + ')</div>'; 378 return; 379 } 380 var rowsHtml = j.levels.map(function(L){ 381 var stateCls = L.eligible ? ' mqcm-vig-row--ok' : (L.n_played > 0 ? ' mqcm-vig-row--prog' : ' mqcm-vig-row--todo'); 382 var rightCol = L.eligible 383 ? '<button class="mqcm-btn mqcm-btn-attest mqcm-vig-btn" data-type="' + esc(L.type) + '">ð Générer</button>' 384 : '<span class="mqcm-vig-remaining">' + L.remaining + ' test' + (L.remaining > 1 ? 's' : '') + ' restant' + (L.remaining > 1 ? 's' : '') + '</span>'; 385 return '<div class="mqcm-vig-row' + stateCls + '">' + 386 '<span class="mqcm-vig-ic">' + L.icon + '</span>' + 387 '<div class="mqcm-vig-body">' + 388 '<div class="mqcm-vig-label">' + esc(L.label) + ' <span class="mqcm-vig-count">' + L.n_played + '/' + L.min + '</span></div>' + 389 '<div class="mqcm-vig-bar"><div class="mqcm-vig-bar-fill" style="width:' + L.progress + '%"></div></div>' + 390 '</div>' + 391 rightCol + 392 '</div>'; 393 }).join(''); 394 // Prochain palier atteignable : 1er non-eligible avec n_played > 0, sinon hebdo. 395 var next = null; 396 for (var i = 0; i < j.levels.length; i++) { 397 if (!j.levels[i].eligible) { next = j.levels[i]; break; } 398 } 399 var nextHtml = ''; 400 if (next) { 401 var freshWord = next.type === 'hebdo' ? 'frais cette semaine' : 'récents'; 402 var goalWord = next.type === 'hebdo' ? ' pour ta <strong>1ʳᵠattestation</strong>' : ' pour le palier <strong>' + esc(next.label) + '</strong>'; 403 nextHtml = '<div class="mqcm-vig-next">ð¯ Plus que <strong>' + next.remaining + ' QCM ' + freshWord + '</strong> (réussi ⥠60 %)' + goalWord + ' - articles publiés ces <strong>' + next.days + ' derniers jours</strong>.</div>'; 404 } else { 405 nextHtml = '<div class="mqcm-vig-next mqcm-vig-next--max">ð <strong>Tous les paliers débloqués !</strong> Continue ta veille pour maintenir tes attestations à jour.</div>'; 406 } 407 panel.innerHTML = 408 '<div class="mqcm-vig-head">ð <strong>Tes paliers de veille</strong> - un test compte si <em>score ⥠3/5 (60 %)</em> sur un article publié dans la période.</div>' + 409 rowsHtml + 410 nextHtml + 411 '<div class="mqcm-vig-foot"><a href="https://www.83-629.fr/blog-quiz/#paliers" target="_blank" rel="noopener">ð Comprendre les paliers et voir un modèle d\'attestation â</a></div>'; 412 Array.prototype.forEach.call(panel.querySelectorAll('.mqcm-vig-btn'), function(b){ 413 b.addEventListener('click', function(){ generateAttestation(b.getAttribute('data-type'), b); }); 414 }); 415 }) 416 .catch(function(e){ 417 panel.innerHTML = '<div class="mqcm-error">Erreur réseau : ' + esc(e.message) + '</div>'; 418 }); 419 } 420 421 function generateAttestation(type, btn) { 422 var zone = document.getElementById('mqcm-attest-zone'); 423 if (btn) { btn.disabled = true; btn.textContent = 'â³â¦'; btn.classList.add('mqcm-vig-loading'); } 424 fetch(API_BASE + '/reglementation/api/blog-quiz/me/attestation', { 425 method: 'POST', credentials: 'include', 426 headers: { 'Content-Type': 'application/json' }, 427 body: JSON.stringify({ type: type }), 428 }) 429 .then(function(r){ return r.json(); }) 430 .then(function(j){ 431 if (j.error) { 432 if (zone) zone.innerHTML = '<div class="mqcm-error">' + esc(j.error) + (j.cta_url ? ' <a href="' + esc(j.cta_url) + '" target="_blank">â</a>' : '') + '</div>'; 433 if (btn) { btn.disabled = false; btn.textContent = 'ð Générer'; btn.classList.remove('mqcm-vig-loading'); } 434 return; 435 } 436 gtag_safe('blog_quiz_attestation_generated', { post_slug: state.data.post_slug, t
436ype: j.type }); 437 if (zone) zone.innerHTML = 438 '<div class="mqcm-attest-result">' + 439 ' <div class="mqcm-attest-level">' + esc(j.icon || 'ð') + ' <strong>' + esc(j.label || 'Attestation') + '</strong> - ' + esc(j.period_label || '') + '</div>' + 440 ' <a href="' + API_BASE + '/attestation/' + esc(j.public_token) + '/" target="_blank" class="mqcm-btn mqcm-btn-primary mqcm-btn-large">Voir mon attestation â</a>' + 441 ' <div class="mqcm-share-prompt">' + 442 ' <div class="mqcm-share-title">ð Bravo ! Partage ta veille :</div>' + 443 ' <div class="mqcm-share-row">' + 444 ' <a class="mqcm-share-btn mqcm-share-li" target="_blank" rel="noopener" href="https://www.linkedin.com/sharing/share-offsite/?url=' + encodeURIComponent(API_BASE + '/attestation/' + j.public_token + '/') + '">LinkedIn</a>' + 445 ' <a class="mqcm-share-btn mqcm-share-wa" target="_blank" rel="noopener" href="https://wa.me/?text=' + encodeURIComponent('Veille validee en sécurité privée sur 83-629.fr. Mon attestation : ' + API_BASE + '/attestation/' + j.public_token + '/') + '">WhatsApp</a>' + 446 ' <a class="mqcm-share-btn mqcm-share-dl" target="_blank" rel="noopener" download href="' + API_BASE + '/attestation/' + esc(j.public_token) + '/card-carre.png">⬠Image</a>' + 447 ' </div>' + 448 ' </div>' + 449 ' <p class="mqcm-attest-note">Lien permanent, partageable (employeur, donneur d\'ordre, auditeur). ' + j.n_played + ' test' + (j.n_played > 1 ? 's' : '') + ' validés sur la période · moyenne ' + j.avg_score_pct + '%.</p>' + 450 '</div>'; 451 if (btn) { btn.disabled = true; btn.textContent = 'â Générée'; btn.classList.remove('mqcm-vig-loading'); } 452 // Le résultat s'affiche dans #mqcm-attest-zone, sous le panneau des paliers 453 // (souvent hors écran sur ce résumé) : on amène l'attestation à l'utilisateur. 454 if (zone && zone.scrollIntoView) { 455 try { zone.scrollIntoView({ behavior: 'smooth', block: 'center' }); } 456 catch (e) { zone.scrollIntoView(); } 457 } 458 }) 459 .catch(function(e){ 460 if (zone) zone.innerHTML = '<div class="mqcm-error">Erreur réseau : ' + esc(e.message) + '</div>'; 461 if (btn) { btn.disabled = false; btn.textContent = 'ð Générer'; btn.classList.remove('mqcm-vig-loading'); } 462 }); 463 } 464 465 function revealHint() { 466 fetch(API_BASE + '/reglementation/api/blog-quiz/' + encodeURIComponent(state.data.post_uuid) + '/hint', { 467 method: 'POST', credentials: 'same-origin', 468 }) 469 .then(function(r){ return r.json(); }) 470 .then(function(j){ 471 if (j.error) { 472 if (j.cta_url) { window.location.href = j.cta_url; return; } 473 alert('Erreur : ' + j.error); return; 474 } 475 state.hintRevealed = true; 476 gtag_safe('blog_quiz_hint_revealed', { post_slug: state.data.post_slug, teaser_question_id: j.teaser_question_id }); 477 var zone = document.getElementById('mqcm-hint-zone'); 478 if (zone) zone.innerHTML = 479 '<div class="mqcm-hint-box">' + 480 ' <div class="mqcm-hint-label">\ud83d\udca1 Indice débloqué</div>' + 481 ' <div class="mqcm-hint-q"><em>' + esc(j.teaser_question || '') + '</em></div>' + 482 ' <div class="mqcm-hint-text">' + esc(j.hint_free || '') + '</div>' + 483 '</div>'; 484 var btn = document.getElementById('mqcm-hint'); 485 if (btn) btn.style.display = 'none'; 486 }) 487 .catch(function(e){ alert('Erreur : ' + e.message); }); 488 } 489 490 // Sticky CTA card desktop (cards à droite) + pill mobile, scroll-driven 491 function injectStickyCta(d) { 492 if (document.getElementById('mqcm-sticky')) return; 493 var articleIsExpert = (d.post_visibility !== 'public'); 494 var tier = d.tier || 'anonymous'; 495 var iconSvg = '<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9.1 9a2.9 2.9 0 1 1 4 2.7c-1 .45-1.7 1.15-1.7 2.55"></path><circle cx="11.4" cy="17.6" r="1.15" fill="currentColor" stroke="none"></circle></svg>'; 496 var eyebrow, titleText, btnLabel; 497 if (articleIsExpert && tier !== 'expert') { 498 eyebrow = 'Vous croyez maîtriser ?';
499 titleText = 'Vérifiez votre intuition'; 500 btnLabel = 'Vérifier mon intuition'; 501 } else if (tier === 'expert') { 502 eyebrow = 'Veille R.631-7 CSI'; 503 titleText = 'Confirmer ma veille'; 504 btnLabel = 'Démarrer le test'; 505 } else if (tier === 'free') { 506 eyebrow = 'Vérification de veille'; 507 titleText = 'Tester + débloquer mon palier'; 508 btnLabel = 'Démarrer le test'; 509 } else { 510 eyebrow = 'Vérification de veille'; 511 titleText = 'Tester ma compréhension'; 512 btnLabel = 'Démarrer le test'; 513 } 514 var card = document.createElement('aside'); 515 card.id = 'mqcm-sticky'; 516 card.className = 'mqcm-sticky-card'; 517 card.setAttribute('aria-label', 'Test de veille disponible pour cet article'); 518 card.innerHTML = 519 '<button type="button" class="mqcm-sticky-close" aria-label="Fermer">' + 520 ' <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>' + 521 '</button>' + 522 '<div class="mqcm-sticky-icon-wrap"><span class="mqcm-sticky-icon">' + iconSvg + '</span></div>' + 523 '<div class="mqcm-sticky-eyebrow">' + eyebrow + '</div>' + 524 '<div class="mqcm-sticky-title">' + titleText + '</div>' + 525 '<div class="mqcm-sticky-pills">' + 526 ' <span class="mqcm-sticky-pill">' + d.questions.length + ' questions</span>' + 527 ' <span class="mqcm-sticky-pill mqcm-sticky-pill-soft">~2 min</span>' + 528 '</div>' + 529 '<button type="button" class="mqcm-sticky-btn" aria-label="Aller au test de veille">' + btnLabel + ' <span class="mqcm-sticky-arrow">→</span></button>'; 530 var pill = document.createElement('div'); 531 pill.id = 'mqcm-sticky-pill'; 532 pill.className = 'mqcm-sticky-pill-collapsed'; 533 pill.innerHTML = 534 '<button type="button" class="mqcm-sticky-pill-btn" aria-label="Aller au test de veille"><span class="mqcm-sticky-icon">' + iconSvg + '</span></button>' + 535 '<button type="button" class="mqcm-sticky-pill-close" aria-label="Fermer">' + 536 ' <svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>' + 537 '</button>'; 538 document.body.appendChild(card); 539 document.body.appendChild(pill); 540 var dismissed = false; 541 // sessionStorage (et non localStorage) : la fermeture ne vaut que pour la session 542 // courante â la pastille réapparaît à la prochaine visite (fermeture non permanente). 543 try { dismissed = sessionStorage.getItem('bq83629_sticky_dismissed') === '1'; } catch (_) {} 544 function goToQcm() { 545 if (placeholder && placeholder.scrollIntoView) { 546 placeholder.scrollIntoView({ behavior: 'smooth', block: 'start' }); 547 gtag_safe('blog_quiz_sticky_clicked', { post_slug: postIdent, tier: d.tier }); 548 } 549 } 550 function dismiss() { 551 dismissed = true; 552 try { sessionStorage.setItem('bq83629_sticky_dismissed', '1'); } catch (_) {} 553 applyState(); 554 gtag_safe('blog_quiz_sticky_dismissed', { post_slug: postIdent }); 555 } 556 card.querySelector('.mqcm-sticky-btn').addEventListener('click', goToQcm); 557 card.querySelector('.mqcm-sticky-close').addEventListener('click', dismiss); 558 pill.querySelector('.mqcm-sticky-pill-btn').addEventListener('click', goToQcm); 559 pill.querySelector('.mqcm-sticky-pill-close').addEventListener('click', function(e){ e.stopPropagation(); dismiss(); }); 560 var visible = false; 561 function applyState() { 562 var show = visible && !dismissed; 563 card.classList.toggle('mqcm-sticky--show', show); 564 pill.classList.toggle('mqcm-sticky--show', show); 565 } 566 var heroImg = document.querySelector('.gh-post-page__featured-img, .gh-post-page__featured-img-wrapper, .gh-post-page__header, article header, .post-full-image'); 567 var morePosts = document.querySelector('.gh-post-page__more, .post-full-footer, .read-next'); 568 function checkVisibility() { 569 var heroOut = true; 570 if (heroImg) { 571 var r = heroImg.getBoundingClientRect(); 572 heroOut = r.bottom < 80; 573 } 574 var morePostsIn = false;
575 if (morePosts) { 576 var m = morePosts.getBoundingClientRect(); 577 morePostsIn = m.top < window.innerHeight * 0.85; 578 } 579 visible = heroOut && !morePostsIn; 580 applyState(); 581 } 582 window.addEventListener('scroll', checkVisibility, { passive: true }); 583 window.addEventListener('resize', checkVisibility, { passive: true }); 584 setTimeout(checkVisibility, 100); 585 } 586 587 if (document.readyState === 'loading') { 588 document.addEventListener('DOMContentLoaded', init); 589 } else { 590 init(); 591 } 592})();
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.