1if (typeof IRaiserFrame === 'undefined') { 2 var IRaiserFrame = { 3 4 currentScript: document.currentScript, 5 6 // Iframe title attribute translations, keyed by the same ISO codes as allowedLangs. 7 // Used for screen reader accessibility, the iframe content itself is localized by DF. 8 TITLES: { 9 donation: { 10 ar_AR: 'ÙÙ ÙØ°Ø¬ Ø§ÙØªØ¨Ø±Ø¹', 11 bg_BG: 'ФоÑмÑлÑÑ Ð·Ð° даÑение', 12 cs_CZ: 'Dárcovský formuláÅ', 13 da_DK: 'Donationsformular', 14 de_DE: 'Spendenformular', 15 el_GR: 'ΦÏÏμα δÏÏεάÏ', 16 en_EN: 'Donation form', 17 en_HK: 'Donation form', 18 en_ME: 'Donation form', 19 es_ES: 'Formulario de donación', 20 et_EE: 'Annetusvorm', 21 fi_FI: 'Lahjoituslomake', 22 fr_FR: 'Formulaire de don', 23 hr_HR: 'Obrazac za donaciju', 24 hu_HU: 'Adományozási űrlap', 25 id_ID: 'Formulir donasi', 26 it_IT: 'Modulo di donazione', 27 ja_JP: 'å¯ä»ãã©ã¼ã ', 28 ka_GE: 'á¨áááá¬áá á£ááááá¡ á¤áá áá', 29 kk_KZ: 'ÒайÑÑÑмдÑлÑÒ Ð¿ÑÑÑнÑ', 30 ko_KR: 'ê¸°ë¶ ìì', 31 nl_NL: 'Donatieformulier', 32 no_NO: 'Donasjonsskjema', 33 pl_PL: 'Formularz darowizny', 34 pt_PT: 'Formulário de doação', 35 ro_RO: 'Formular de donaÈie', 36 ru_RU: 'ФоÑма пожеÑÑвованиÑ', 37 se_SE: 'Donationsformulär', 38 sk_SK: 'Darovacà formulár', 39 sl_SI: 'Obrazec za donacijo', 40 sr_RS: 'ÐбÑÐ°Ð·Ð°Ñ Ð·Ð° донаÑиÑÑ', 41 tr_TR: 'BaÄıŠformu', 42 uk_UA: 'ФоÑма пожеÑÑви', 43 vi_VN: 'Mẫu quyên góp', 44 zh_CN: 'ææ¬¾è¡¨å', 45 zh_TW: 'ææ¬¾è¡¨å®', 46 }, 47 presentation: { 48 ar_AR: 'عرض Ø§ÙØÙ ÙØ©', 49 bg_BG: 'ÐÑедÑÑавÑне на кампаниÑÑа', 50 cs_CZ: 'PÅedstavenà kampanÄ', 51 da_DK: 'Kampagnepræsentation', 52 de_DE: 'Kampagnenpräsentation', 53 el_GR: 'ΠαÏÎ¿Ï ÏίαÏη εκÏÏÏαÏείαÏ', 54 en_EN: 'Campaign presentation', 55 en_HK: 'Campaign presentation', 56 en_ME: 'Campaign presentation', 57 es_ES: 'Presentación de la campaña', 58 et_EE: 'Kampaania tutvustus', 59 fi_FI: 'Kampanjan esittely', 60 fr_FR: 'Présentation de la campagne', 61 hr_HR: 'Predstavljanje kampanje', 62 hu_HU: 'A kampány bemutatása', 63 id_ID: 'Presentasi kampanye', 64 it_IT: 'Presentazione della campagna', 65 ja_JP: 'ãã£ã³ãã¼ã³ç´¹ä»', 66 ka_GE: 'ááááááááá¡ áá ááááá¢ááªáá', 67 kk_KZ: 'ÐаÑÒан ÑанÑÑÑÑÑÑлÑмÑ', 68 ko_KR: 'ìº íì¸ ìê°', 69 nl_NL: 'Campagnepresentatie', 70 no_NO: 'Presentasjon av kampanjen', 71 pl_PL: 'Prezentacja kampanii', 72 pt_PT: 'Apresentação da campanha', 73 ro_RO: 'Prezentarea campaniei', 74 ru_RU: 'ÐÑезенÑаÑÐ¸Ñ ÐºÐ°Ð¼Ð¿Ð°Ð½Ð¸Ð¸', 75 se_SE: 'Kampanjpresentation', 76 sk_SK: 'Predstavenie kampane', 77 sl_SI: 'Predstavitev kampanje', 78 sr_RS: 'ÐÑедÑÑавÑаÑе кампаÑе', 79 tr_TR: 'Kampanya sunumu', 80 uk_UA: 'ÐÑезенÑаÑÑÑ ÐºÐ°Ð¼Ð¿Ð°Ð½ÑÑ', 81 vi_VN: 'Giá»i thiá»u chiến dá»ch', 82 zh_CN: 'æ´»å¨ä»ç»', 83 zh_TW: 'æ´»åä»ç´¹', 84 }, 85 }, 86 87 getIframeTitle(kind, src) { 88 let lang = 'en_EN'; 89 try { 90 lang = new URL(src).searchParams.get('lang') || 'en_EN'; 91 } catch (e) { /* fall back to en_EN */ } 92 return IRaiserFrame.TITLES[kind][lang] || IRaiserFrame.TITLES[kind].en_EN; 93 }, 94 95 // Ajout d'une fonction pour rechercher les ancres dans les href 96 processAnchors() { 97 document.querySelectorAll('a[href*="#iraiser_popin"], a[href*="#iraiser_native"]').forEach(element => { 98 let href = element.getAttribute('href'); 99 100 // Vérification de l'ancre et ajout de la classe appropriée 101 const isPopin = href.includes('#iraiser_popin'); 102 if (isPopin) { 103 element.classList.add('iraiser_popin'); 104 href = href.replace('#iraiser_popin', ''); 105 } else if (href.includes('#iraiser_native')) { 106 element.classList.add('iraiser_native'); 107 href = href.replace('#iraiser_native', ''); 108 } 109 110 if(href.includes('#scroll')){ 111 element.setAttribute('data-scroll', 'true'); 112 href = href.replace('#scroll', ''); 113 } 114 115 // #noleftside is popin-only (native has no left iframe) 116 if(isPopin && href.includes('#noleftside')){ 117 element.setAttribute('data-noleftside', 'true'); 118 href = href.replace('#noleftside', ''); 119 } 120 121 // #step2: open the donation form directly on step 2 (requires amount & frequency query params) 122 if(href.includes('#step2')){ 123 element.setAttribute('data-step', '2'); 124 href = href.replace('#step2', ''); 125 } 126 127 // Mise à jour du href sans l'ancre 128 element.setAttribute('href', href); 129 }); 130 }, 131 132 // Adds a query parameter to the URL 133 addQueryParam(url, param, value) { 134 const urlObj = new URL(url); 135 urlObj.searchParams.set(param, value); 136 return urlObj.toString(); 137 }, 138 139 // Corrects iframe src URL by modifying path and adding necessary parameters 140 correctIframeSrc(url, type, busId, element) { 141 const urlObj = new URL(url); 142 const originUrlObj = new URL(url); 143 urlObj.search = ''; 144 145 urlObj.searchParams.set('private_window', type); 146 urlObj.searchParams.set('reserved_donation_embed_form', type);
147 urlObj.searchParams.set('_cv', 1); 148 149 // Retrieve the language from the current page and add it as a query parameter 150 const pageLang = document.documentElement.lang.substring(0, 2).toLowerCase() || 'en'; 151 let isoLang = originUrlObj.searchParams.get('lang'); 152 if (!isoLang) { 153 // Construct the 4-character ISO code with underscore, handling special cases 154 switch (pageLang) { 155 case 'en': 156 isoLang = 'en_EN'; 157 break; 158 case 'zh': 159 isoLang = (document.documentElement.dataset.region === 'TW') ? 'zh_TW' : 'zh_CN'; 160 break; 161 case 'et': 162 isoLang = 'et_EE'; 163 break; 164 case 'sl': 165 isoLang = 'sl_SI'; 166 break; 167 case 'sr': 168 isoLang = 'sr_RS'; 169 break; 170 case 'uk': 171 isoLang = 'uk_UA'; 172 break; 173 case 'el': 174 isoLang = 'el_GR'; 175 break; 176 default: 177 isoLang = `${pageLang}_${pageLang.toUpperCase()}`; 178 break; 179 } 180 } 181 182 const allowedLangs = [ 183 'ar_AR', 'bg_BG', 'cs_CZ', 'da_DK', 'de_DE', 'el_GR', 'en_EN', 'en_HK', 'en_ME', 'es_ES', 'et_EE', 184 'fi_FI', 'fr_FR', 'hr_HR', 'hu_HU', 'id_ID', 'it_IT', 'ja_JP', 'ka_GE', 'kk_KZ', 'ko_KR', 'nl_NL', 185 'no_NO', 'pl_PL', 'pt_PT', 'ro_RO', 'ru_RU', 'se_SE', 'sk_SK', 'sl_SI', 'sr_RS', 'tr_TR', 'uk_UA', 186 'vi_VN', 'zh_CN', 'zh_TW' 187 ]; 188 189 // If isoLang is not in the allowed list, default to 'en_EN' 190 if (!allowedLangs.includes(isoLang)) { 191 isoLang = 'en_EN'; 192 } 193 194 urlObj.searchParams.set('lang', isoLang); 195 // Add existing parameters from the URL if they start with 'utm_' or 'reserved_' and are not visitor identifiers 196 const identifierPatterns = [ 197 /^(\d{8,}|[a-fA-F0-9]{16,})$/, // Numeric or hex identifiers 198 /^G-[A-Z0-9]{10}$/, // Google Analytics G-XXXXXXXXXX 199 /^fb\.\d+$/, // Facebook Analytics (fb.<number>) 200 /^ABTastyID$/, // AB Tasty identifier pattern 201 /^GA\d+\.\d+\.\d+$/, // Google Analytics specific identifier pattern 202 ]; 203 204 const allowedKeys = [ 205 'cid', 'country', 'amount', 'currency', 'frequency', 'free_amount', 'once_grid', 'regular_grid', 'affectation', 'once_grid[]', 'regular_grid[]' 206 // utm_*, reserved_* 207 ]; 208 209 originUrlObj.searchParams.forEach((value, key) => { 210 if ((key.startsWith('utm_') || key.startsWith('reserved_')|| allowedKeys.includes(key)) 211 && !identifierPatterns.some(pattern => pattern.test(value))) { 212 if (key.endsWith('[]')) { 213 urlObj.searchParams.append(key, value); 214 } else { 215 urlObj.searchParams.set(key, value); 216 } 217 } 218 }); 219 220 // Set query parameters if element has corresponding data- attributes 221 allowedKeys.forEach((key) => { 222 let dataAttr = `data-${key}`; 223 if (element.hasAttribute(dataAttr)) { 224 urlObj.searchParams.set(key, element.getAttribute(dataAttr)); 225 } 226 }); 227 228 const scriptUrl = IRaiserFrame.currentScript.src; 229 const version = scriptUrl.split('/').slice(-2, -1)[0]; 230 const versionParam = 'private_embed_form_version=' + version; 231 const busParam = 'private_embed_form_bus=' + busId; 232 233 // Add 'iframe' and 'private_embed_form_bus' and 'private_embed_form_version' to the hash if not already present 234 if (urlObj.hash) { 235 if (!urlObj.hash.includes('iframe')) { 236 urlObj.hash += '&iframe'; 237 } 238 if (!urlObj.hash.includes('private_embed_form_bus')) { 239 urlObj.hash += '&' + busParam; 240 } 241 if (!urlObj.hash.includes('private_embed_form_version')) { 242 urlObj.hash += '&' + versionParam; 243 } 244 } else { 245 urlObj.hash = `#iframe&${busParam}&${versionParam}`; 246 } 247 248 // #step2 / data-step="2": embed.js renders the form directly on step 2 (no step-1 flash) 249 if (element.getAttribute('data-step') === '2' && !urlObj.hash.includes('private_embed_form_step')) { 250 urlObj.hash += '&private_embed_form_step=2';
251 } 252 253 return urlObj.toString(); 254 }, 255 256 // Transforms an element into an iframe if it is not already one 257 transformToIframe(element, type='native') { 258 const iframe = document.createElement('iframe'); 259 iframe.classList.add('iraiser'); 260 261 // Assign a random data-bus ID if not present 262 if (!iframe.getAttribute('data-bus')) { 263 iframe.setAttribute('data-bus', 'bus_' + Math.floor(Math.random() * 10 ** 16).toString()); 264 } 265 266 // Copy attributes from the original element to the iframe 267 Array.from(element.attributes).forEach(attr => { 268 iframe.setAttribute(attr.name, attr.value); 269 }); 270 271 // We MUST set the `allow` attribute before the src below, for the payment Permissions 272 // Policy to take effect: it's evaluated at load time, so setting it after has no effect 273 // until a reload. Otherwise Apple/Google Pay (PaymentRequest) get blocked on native 274 // iframes ("payment is not allowed"). 275 iframe.setAttribute('allow', 'payment *'); 276 277 // Set iframe source using corrected src or original element's src/href 278 const src = element.getAttribute('src') || element.getAttribute('href'); 279 let correctedSrc; 280 try { 281 correctedSrc = IRaiserFrame.correctIframeSrc(src, type, iframe.getAttribute('data-bus'), element); 282 } catch (e) { 283 console.warn('IRaiserFrame: invalid or missing src/href, skipping element', element, e); 284 return null; 285 } 286 iframe.setAttribute('src', correctedSrc); 287 288 // Ensure a title is set for accessibility (screen readers) 289 if (!iframe.hasAttribute('title')) { 290 iframe.setAttribute('title', IRaiserFrame.getIframeTitle('donation', iframe.getAttribute('src'))); 291 } 292 293 // Replace original element with the new iframe 294 element.parentNode.replaceChild(iframe, element); 295 296 // Allow the parent page to communicate dynamically the amounts to the iframe 297 // Add listener to detect changes to data- attributes 298 const observer = new MutationObserver((mutations) => { 299 mutations.forEach((mutation) => { 300 const element = mutation.target; 301 if (mutation.type === 'attributes' && mutation.attributeName.startsWith('data-') && !mutation.attributeName.startsWith('data-donation')) { 302 const attributeName = mutation.attributeName; 303 const attributeValue = element.getAttribute(attributeName); 304 if (attributeValue) { 305 if (['data-amount', 'data-frequency', 'data-currency'].includes(attributeName)) { 306 // Collect all relevant values if they exist 307 const amount = element.getAttribute('data-amount'); 308 const frequency = element.getAttribute('data-frequency'); 309 const currency = element.getAttribute('data-currency'); 310 311 let message = {}; 312 if (amount) message['amount'] = amount; 313 if (frequency) message['frequency'] = frequency; 314 if (currency) message['currency'] = currency; 315 316 element.contentWindow.postMessage({'set_donation':message}, '*'); 317 } else { 318 element.contentWindow.postMessage( 319 { [`set_${attributeName.replace('data-', '')}`]: attributeValue }, 320 '*' 321 ); 322 } 323 } 324 } 325 }); 326 }); 327 328 // Observe the iframe for changes to attributes starting with data-donation_ 329 observer.observe(iframe, { 330 attributes: true, 331 }); 332 333 return iframe; 334 }, 335 336 setupNativeIframe(element, type='native') { 337 const iframe = IRaiserFrame.transformToIframe(element, type); 338 if (!iframe) { 339 return null; 340 } 341 342 iframe.style.margin = '0 auto'; 343 iframe.style.display = 'block'; 344 iframe.style.maxWidth = 'min(100%, 100vw, 580px)'; 345 iframe.style.height = '550px'; 346 347 if (!iframe.hasAttribute('width') && type=='native') { 348 iframe.setAttribute('width', '480px'); 349 } 350 351 iframe.setAttribute('scrolling', 'no'); 352 iframe.setAttribute('frameborder', '0'); 353 iframe.setAttribute('marginheight', '0'); 354 iframe.setAttribute('marginwidth', '0'); 355 356 return iframe; 357 }, 358 359 // Configures all iframes with the .iraiser_native class 360 setupNativeIframes() { 361 const iframes = document.querySelectorAll('.iraiser_native'); 362
363 iframes.forEach((element) => { 364 try { 365 IRaiserFrame.setupNativeIframe(element); 366 } catch (e) { 367 console.warn('IRaiserFrame: failed to set up native iframe, skipping element', element, e); 368 } 369 }); 370 }, 371 372 setupPopinIframes() { 373 document.querySelectorAll('.iraiser_popin').forEach(element => { 374 element.addEventListener('click', function (event) { 375 if (!(this.getAttribute('data-scroll') === 'true')) { 376 event.preventDefault(); // Prevent default behaviour (scroll to the top at popin opening) except if data-scroll is true 377 } 378 let href = this.getAttribute('data-href') || this.getAttribute('href'); 379 if (!href || href === '#') return; 380 381 this.setAttribute('href', '#'); 382 this.setAttribute('data-href', href); 383 384 if (!this.getAttribute('data-bus')) { 385 this.setAttribute('data-bus', 'bus_' + Math.floor(Math.random() * 10 ** 16).toString()); 386 } 387 388 const hideLeftside = this.getAttribute('data-noleftside') === 'true'; 389 390 let leftSrc, rightSrc; 391 try { 392 if (!hideLeftside) { 393 leftSrc = IRaiserFrame.correctIframeSrc(href, 'head', this.getAttribute('data-bus'), this); 394 } 395 rightSrc = IRaiserFrame.correctIframeSrc(href, 'popin', this.getAttribute('data-bus'), this); 396 } catch (e) { 397 console.warn('IRaiserFrame: invalid popin href, aborting', href, e); 398 return; 399 } 400 401 let checkElement = document.getElementById('iraiser-modal-overlay'); 402 if (checkElement) { 403 checkElement.remove(); 404 } 405 406 // Modal overlay creation 407 const modalOverlay = document.createElement('div'); 408 modalOverlay.id = 'iraiser-modal-overlay'; 409 modalOverlay.classList.add('iraiser'); 410 411 // Modal content creation 412 const modalContent = document.createElement('div'); 413 modalContent.classList.add('iraiser', 'modal-content'); 414 if (hideLeftside) { 415 modalContent.classList.add('no-leftside'); 416 } 417 418 // Close button creation 419 const closeButton = document.createElement('div'); 420 closeButton.classList.add('iraiser', 'close-button'); 421 422 const closeSvg = ` 423 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"> 424 <circle cx="12" cy="12" r="12" fill="white" opacity="0.8"/> 425 <line x1="6" y1="6" x2="18" y2="18" stroke="black" stroke-width="2"/> 426 <line x1="6" y1="18" x2="18" y2="6" stroke="black" stroke-width="2"/> 427 </svg>`; 428 closeButton.innerHTML = closeSvg; 429 430 // Iframe container creation 431 const iframeContainer = document.createElement('div'); 432 iframeContainer.classList.add('iraiser', 'iframe-container'); 433 iframeContainer.style.zIndex = '1001'; 434 435 // Left iframe creation with corrected ID (skipped when data-noleftside="true") 436 if (!hideLeftside) { 437 const iframeLeft = document.createElement('iframe'); 438 iframeLeft.id = 'iraiser-iframe-left'; 439 iframeLeft.classList.add('iraiser'); 440 iframeLeft.style.border = 'none'; 441 iframeLeft.src = leftSrc; 442 iframeLeft.setAttribute('title', IRaiserFrame.getIframeTitle('presentation', iframeLeft.src));
443 iframeContainer.appendChild(iframeLeft); 444 } 445 446 // Right iframe creation with corrected ID 447 const iframeRight = document.createElement('iframe'); 448 iframeRight.id = 'iraiser-iframe-right'; 449 iframeRight.classList.add('iraiser', 'iraiser_popin'); 450 iframeRight.setAttribute('allow', 'payment *'); 451 iframeRight.setAttribute('frameborder', '0'); 452 iframeRight.setAttribute('marginheight', '0'); 453 iframeRight.setAttribute('marginwidth', '0'); 454 iframeRight.src = rightSrc; 455 iframeRight.setAttribute('title', IRaiserFrame.getIframeTitle('donation', iframeRight.src)); 456 iframeRight.style.border = 'none'; 457 iframeContainer.appendChild(iframeRight); 458 459 modalContent.appendChild(closeButton); 460 modalContent.appendChild(iframeContainer); 461 modalOverlay.appendChild(modalContent); 462 document.body.appendChild(modalOverlay); 463 464 // Modal close functionality 465 closeButton.addEventListener('click', function () { 466 modalOverlay.remove(); 467 const styleElement = document.getElementById('hide-scrollbar-style'); 468 if (styleElement) styleElement.remove(); 469 }); 470 471 // Inject CSS styles as a <style> element 472 const style = document.createElement('style'); 473 style.id = 'iraiser-dynamic-styles'; 474 style.textContent = ` 475 /* Modal overlay styling */ 476 #iraiser-modal-overlay.iraiser { 477 display: flex; 478 position: fixed; 479 top: 0; 480 left: 0; 481 width: 100%; 482 height: 100%; 483 z-index: 99999999999999; 484 background-color: rgba(0, 0, 0, 0.7); 485 overflow-y: auto; 486 overflow-x: hidden; 487 } 488 489 /* Modal content styling */ 490 .iraiser .modal-content { 491 display: flex; 492 flex-direction: row; 493 align-items: center; 494 position: relative; 495 width: 100%; 496 height: 100%; 497 box-sizing: border-box; 498 background-color: transparent; 499 } 500 501 /* Close button styling */ 502 .iraiser .close-button { 503 position: absolute; 504 top: 20px; 505 right: 30px; 506 cursor: pointer; 507 z-index: 1002; 508 } 509 510 /* Iframe container styling */ 511 .iraiser .iframe-container { 512 display: flex; 513 gap: 20px; 514 z-index: 1001; 515 } 516 517 /* Iframe styling for large screens */ 518 @media (min-width: 930px) { 519 .iraiser .modal-content { 520 justify-content: center; 521 padding-top: calc((100vh - 700px) / 2); 522 align-items: flex-start; 523 } 524 .iraiser .iframe-container { 525 flex-direction: row; 526 gap: 20px; 527 } 528 .iraiser iframe { 529 border-radius: 12px; 530 } 531 .iraiser iframe#iraiser-iframe-left { 532 width: 500px; 533 height: 640px; 534 min-height: min(98vh,640px); 535 max-height: calc(47vh + 340px); 536 } 537 .iraiser iframe#iraiser-iframe-right { 538 width: 380px; 539 height: 640px; 540 max-height: calc(47vh + 340px); 541 } 542 } 543 544 /* Iframe styling for medium screens */ 545 @media (max-width: 929px) and (min-width: 500px) { 546 .iraiser .iframe-container { 547 flex-direction: column; 548 gap: 0; 549 } 550 .iraiser iframe { 551 width: 500px; 552 max-width: 500px; 553 height: 640px; 554 } 555 .iraiser iframe#iraiser-iframe-left { 556 border-radius: 12px 12px 0 0; 557 } 558 .iraiser iframe#iraiser-iframe-right { 559 border-radius: 0 0 12px 12px; 560 } 561 } 562 563 /* Iframe styling for small screens */ 564 @media (max-width: 929px) { 565 .iraiser .modal-content{ 566 align-items: center; 567 flex-direction: column; 568 justify-content: flex-start; 569 } 570 571 .iraiser iframe { 572 width: 100vw; 573 max-width: 500px; 574 height: 640px; 575 } 576 .iraiser .iframe-container { 577 flex-direction: column; 578 gap: 0; 579 } 580 #iraiser-iframe-left{ 581 max-height:200px; 582 } 583 } 584 585 html:has(#iraiser-modal-overlay) { 586 overflow: hidden; 587 } 588 589 /* When the left side is hidden, center the donation iframe alone */ 590 .iraiser .modal-content.no-leftside .iframe-container { 591 gap: 0; 592 justify-content: center; 593 } 594 @media (min-width: 930px) { 595 .iraiser .modal-content.no-leftside iframe#iraiser-iframe-right { 596 width: 480px; 597 border-radius: 12px; 598 } 599 } 600 @media (max-width: 929px) and (min-width: 500px) { 601 .iraiser .modal-content.no-leftside iframe#iraiser-iframe-right { 602 border-radius: 12px; 603 } 604 } 605 `; 606 document.head.appendChild(style); 607 }); 608 }); 609 }, 610 611 // Fonction pour scroller jusqu'à p pixels en dessous de l'iframe dans la page parente 612 scrollToPixelsBelowIframe(iframe, p) { 613 var reference = window; 614 615 const iframeRect = iframe.getBoundingClientRect(); 616 const absoluteTop = iframeRect.top + reference.scrollY; 617 const targetScrollPosition = absoluteTop + p - 80; // au cas ou un menu de 80px serait présent en overlay 618 619 reference.scrollTo({ 620 top: targetScrollPosition, 621 behavior: 'smooth', 622 }); 623 }, 624 625 // Fonction pour vérifier si le pixel situé à p pixels du haut de l'iframe appartient à l'iframe 626 isPixelBelongingToIframe(iframe, p) { 627 const iframeRect = iframe.getBoundingClientRect(); 628 const pixelX = iframeRect.left + 1; 629 const pixelY = iframeRect.top + p; 630 const elementAtPixel = document.elementFromPoint(pixel
630X, pixelY); 631 632 return elementAtPixel === iframe || iframe.contains(elementAtPixel); 633 }, 634 635 // Fonction pour scroller si l'iframe est recouverte 636 scrollToIframeIfNotVisible(iframe, p = 0) { 637 638 if (iframe.classList.contains('iraiser_popin')) { 639 return; // don't scroll in parent if popin 640 } 641 642 if (!IRaiserFrame.isPixelBelongingToIframe(iframe, p)) { 643 IRaiserFrame.scrollToPixelsBelowIframe(iframe, p); 644 } 645 }, 646 647 beforeunload: false, 648 649 // Handles incoming messages from iframes 650 handleIframeMessage(event) { 651 let iframe = null; 652 const iframes = document.getElementsByTagName('iframe'); 653 654 for (let i = 0; i < iframes.length; i++) { 655 if (iframes[i].contentWindow === event.source) { 656 iframe = iframes[i]; 657 break; 658 } 659 } 660 if (iframe) { 661 const data = event.data; 662 663 if (data) { 664 if (!!data.height && !isNaN(data.height)) { 665 iframe.style.height = Math.max(data.height+1, 200) + 'px'; 666 } 667 668 if(data.interaction_init){ 669 IRaiserFrame.interactionInit = true; 670 } 671 672 if (!!data.current_step && IRaiserFrame.interactionInit) { 673 if (!IRaiserFrame.beforeUnloadFlag) { 674 IRaiserFrame.beforeUnloadFlag = true; 675 window.addEventListener('beforeunload', (e) => { 676 let message; 677 if (iframe.hasAttribute('data-message')) { 678 message = iframe.getAttribute('data-message'); 679 } else { 680 const translations = { 681 fr: "Ãtes-vous sûrs de vouloir abandonner votre don ? Pour revenir à l'étape précédente, cliquez sur la flèche située en haut à gauche du formulaire de don. Cliquez sur 'annuler' pour poursuivre votre don et sur 'confirmer' pour quitter ou changer de page.", 682 en: "Are you sure you want to abandon your donation? To return to the previous step, click the arrow at the top left of the donation form. Click 'cancel' to continue your donation and 'confirm' to leave or change the page.", 683 it: "Sei sicuro di voler abbandonare la tua donazione? Per tornare alla fase precedente, fai clic sulla freccia in alto a sinistra del modulo di donazione. Clicca su 'annulla' per continuare la tua donazione e su 'conferma' per lasciare o cambiare pagina.", 684 nl: "Weet u zeker dat u uw donatie wilt annuleren? Om terug te keren naar de vorige stap, klikt u op de pijl linksboven in het donatieformulier. Klik op 'annuleren' om door te gaan met uw donatie en op 'bevestigen' om de pagina te verlaten of te wijzigen." 685 }; 686 const lang = document.documentElement.lang || 'en'; 687 message = translations[lang] || translations['fr']; 688 } 689 (e || window.event).returnValue = message; 690 return message; 691 }); 692 } 693 iframe.setAttribute('data-donation_step', data.current_step); 694 let closestParent = iframe.closest('.iraiser.modal-content'); 695 if(closestParent){ 696 closestParent.setAttribute('data-donation_step', data.current_step); 697 } 698 } 699 700 if (!!data.amount && !!data.frequency && !!data.currency) { 701 iframe.setAttribute('data-donation_amount', data.amount); 702 iframe.setAttribute('data-donation_frequency', data.frequency); 703 iframe.setAttribute('data-donation_currency', data.currency); 704 } 705 706 if (!!data.scroll && IRaiserFrame.interactionInit) { 707 IRaiserFrame.scrollToIframeIfNotVisible(iframe, data.scroll); 708 } 709 710 if(!!data.ack){ 711 if(data.ack.cid) { 712 iframe.setAttribute('data-cid',data.ack.cid); 713 } 714 if(data.ack.lang) { 715 iframe.setAttribute('data-lang',data.ack.lang); 716 } 717 } 718 } 719 } 720 }, 721 722 ensureMessageListener() { 723 window.addEventListener('message', IRaiserFrame.handleIframeMessage); 724 }, 725 726 727 // Initialization function 728 init() { 729 IRaiserFrame.processAnchors(); 730 IRaiserFrame.setupNativeIframes(); 731 IRaiserFrame.setupPopinIframes(); 732 IRaiserFrame.ensureMessageListener(); 733 }, 734 }; 735 736 window.addEventListener('load', function() { 737 IRaiserFrame.init(); 738 }); 739}
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.