1import './modal.js'; 2import '../../styles/components/home/charities.css' 3import * as youtube from '../utils/youtube.js' 4 5const YOUTUBE_PLAYER_CONTAINER_ID = 'charity-hero-video'; 6 7class HomeCharities { 8 #youtubeApiPromise 9 #currentHeroPlayer = null 10 11 start() { 12 document.querySelectorAll('.see-more-button').forEach((button) => { 13 button.addEventListener('click', async (event) => { 14 event.preventDefault(); 15 await this.#openCharityDetails(button); 16 }); 17 }); 18 } 19 20 async #openCharityDetails(button) { 21 const modal = document.querySelector('.charity-details-modal'); 22 if (!modal) { 23 return; 24 } 25 26 const heroTitle = modal.querySelector('.charity-details-modal__hero-title'); 27 const heroImage = modal.querySelector('.charity-details-modal__hero-image'); 28 const heroVideo = modal.querySelector('.charity-details-modal__hero-video'); 29 const description = modal.querySelector('.modal__content__body__description'); 30 const charityName = button.getAttribute('data-name') ?? ''; 31 const videoSrc = button.getAttribute('data-video') ?? ''; 32 33 if (!heroTitle || !heroImage || !heroVideo || !description) { 34 return; 35 } 36 37 heroTitle.textContent = charityName; 38 this.#toggleHeroTitle(heroTitle, false); 39 heroImage.src = button.getAttribute('data-image-path') ?? ''; 40 heroImage.alt = charityName; 41 description.innerHTML = button.getAttribute('data-description-html') || ''; 42 43 await this.#populateHeroMedia(heroVideo, heroImage, videoSrc, charityName, heroTitle); 44 this.#populateTaxReduction(modal, button); 45 } 46 47 async #populateHeroMedia(heroVideo, heroImage, videoSrc, charityName, heroTitle) { 48 this.#resetHeroPlayer(); 49 heroVideo.innerHTML = ''; 50 51 if (videoSrc === '') { 52 this.#toggleHeroTitle(heroTitle, false); 53 heroVideo.hidden = true; 54 heroImage.hidden = false; 55 return; 56 } 57 58 heroVideo.hidden = false; 59 heroImage.hidden = true; 60 61 if (!youtube.isYoutubeUrl(videoSrc)) { 62 this.#toggleHeroTitle(heroTitle, true); 63 heroVideo.appendChild(this.#createVideoIframe(videoSrc, charityName)); 64 return; 65 } 66 67 const videoId = youtube.getYoutubeVideoId(videoSrc); 68 if (videoId === null) { 69 this.#toggleHeroTitle(heroTitle, true); 70 heroVideo.appendChild(this.#createVideoIframe(videoSrc, charityName)); 71 return; 72 } 73 74 if (youtube.shouldUseClassicYoutubeIframe()) { 75 this.#toggleHeroTitle(heroTitle, true); 76 heroVideo.appendChild(this.#createVideoIframe(videoSrc, charityName)); 77 return; 78 } 79 80 this.#toggleHeroTitle(heroTitle, false); 81 const youtubeContainer = document.createElement('div'); 82 youtubeContainer.id = YOUTUBE_PLAYER_CONTAINER_ID; 83 heroVideo.appendChild(youtubeContainer); 84 await this.#attachYoutubeApiPlayer(youtubeContainer, videoId, heroTitle); 85 } 86 87 #populateTaxReduction(modal, button) { 88 const taxReduction = modal.querySelector('.modal__content__body__tax-reduction'); 89 const taxReductionEnabled = button.getAttribute('data-tax-enabled'); 90 91 if (!taxReduction) { 92 return; 93 } 94 95 if (taxReductionEnabled != 1) { 96 taxReduction.innerHTML = button.getAttribute('data-no-tax-reduction-content') || ''; 97 return; 98 } 99 100 taxReduction.innerHTML = button.getAttribute('data-tax-reduction-content') || ''; 101 const amount = taxReduction.querySelector('span.amount'); 102 if (amount) { 103 amount.innerHTML = button.getAttribute('data-tax-min') || ''; 104 } 105 } 106 107 async #attachYoutubeApiPlayer(container, videoId, heroTitle) { 108 const YT = await this.#ensureYoutubeApi(); 109 this.#resetHeroPlayer(); 110 111 this.#currentHeroPlayer = new YT.Player(container, { 112 videoId, 113 host: 'https://www.youtube.com', 114 playerVars: { 115 playsinline: 1, 116 rel: 0, 117 }, 118 events: { 119 onStateChange: (event) => { 120 if (event.data === YT.PlayerState.PLAYING) { 121 this.#toggleHeroTitle(heroTitle, true); 122 return; 123 } 124 125 if ( 126 event.data === YT.PlayerState.PAUSED 127 || event.data === YT.PlayerState.ENDED 128 || event.data === YT.PlayerState.CUED 129 ) { 130 this.#toggleHeroTitle(heroTitle, false); 131 } 132 }, 133 }, 134 }); 135 } 136 137 #resetHeroPlayer() { 138 if (this.#currentHeroPlayer?.destroy) { 139 this.#currentHeroPlayer.destroy(); 140 this.#currentHeroPlayer = null; 141 } 142 } 143 144 async #ensureYoutubeApi() { 145 if (window.YT && window.YT.Player) { 146 return window.YT; 147 } 148 149 if (this.#youtubeApiPromise) { 150 return this.#youtubeApiPromise; 151 } 152 153 this.#youtubeApiPromise = new Promise((resolve) => { 154 const previousReady = window.onYouTubeIframeAPIReady; 155 156 window.onYouTubeIframeAPIReady = () => { 157 if (typeof previousReady === 'function') { 158 previousReady(); 159 } 160 161 resolve(window.YT); 162 }; 163 164 const existingScript = document.querySelector('script[src="https://www.youtube.com/iframe_api"]'); 165 if (existingScript) { 166 return; 167 } 168 169 const script = document.createElement('script'); 170 script.src = 'https://www.youtube.com/iframe_api'; 171 script.async = true; 172 document.head.appendChild(script); 173 }); 174 175 return this.#youtubeApiPromise; 176 } 177 178 #toggleHeroTitle(heroTitle, shouldHide) { 179 const heroCaption = heroTitle.closest('.charity-details-modal__hero-caption'); 180 181 heroTitle.hidden = shouldHide; 182 heroTitle.style.display = shouldHide ? 'none' : ''; 183 184 if (heroCaption) { 185 heroCaption.style.display = shouldHide ? 'none' : ''; 186 } 187 } 188 189 #createVideoIframe(videoSrc, charityName) { 190 const video = document.createElement('iframe'); 191 video.referrerPolicy = 'strict-origin-when-cross-origin'; 192 video.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share'; 193 video.title = charityName; 194 video.src = youtube.toClassicYoutubeIframeUrl(videoSrc); 195 196 return video; 197 } 198} 199 200new HomeCharities().start();
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.