1/** 2 * youtube-embed.js â utilidades compartilhadas dos embeds de YouTube. 3 * 4 * Consumido por video-block.js, que só cria o iframe no clique do play (lazy-load). 5 */ 6(function (w) { 7 /** 8 * Garante enablejsapi=1 (e origin) na URL do embed. 9 * Sem enablejsapi o player nao expõe a IFrame API e o GTM nao consegue 10 * rastrear play/pause/progresso/conclusao do video. 11 */ 12 function withJsApi(src) { 13 const sep = src.indexOf('?') === -1 ? '?' : '&'; 14 let url = src; 15 16 if (!/[?&]enablejsapi=/.test(url)) { 17 url += sep + 'enablejsapi=1'; 18 } 19 20 if (!/[?&]origin=/.test(url) && w.location.origin) { 21 url += '&origin=' + encodeURIComponent(w.location.origin); 22 } 23 24 return url; 25 } 26 27 let iframeApiPromise = null; 28 29 function loadYouTubeIframeApi() { 30 if (iframeApiPromise) return iframeApiPromise; 31 32 iframeApiPromise = new Promise(function (resolve) { 33 if (w.YT && w.YT.Player) { 34 resolve(w.YT); 35 return; 36 } 37 38 const previousReady = w.onYouTubeIframeAPIReady; 39 w.onYouTubeIframeAPIReady = function () { 40 if (typeof previousReady === 'function') previousReady(); 41 resolve(w.YT); 42 }; 43 44 const tag = document.createElement('script'); 45 tag.src = 'https://www.youtube.com/iframe_api'; 46 document.head.appendChild(tag); 47 }); 48 49 return iframeApiPromise; 50 } 51 52 /** 53 * Empurra play/pause/progresso/conclusao para o dataLayer. 54 * 55 * O gatilho nativo de video do GTM so reconhece youtube.com; o embed usa 56 * youtube-nocookie.com (para nao gravar cookie antes do consentimento), entao 57 * os eventos precisam ser enviados na mao mesmo com enablejsapi=1. 58 */ 59 function trackVideo(iframe, videoLabel) { 60 w.dataLayer = w.dataLayer || []; 61 62 const PROGRESS_MARKS = [25, 50, 75, 90]; 63 let sentMarks = []; 64 let progressTimer = null; 65 let started = false; 66 let completed = false; 67 68 function pushEvent(eventName, extra) { 69 w.dataLayer.push(Object.assign({ 70 event: eventName, 71 video_title: videoLabel 72 }, extra || {})); 73 } 74 75 function stopProgressPolling() { 76 if (progressTimer) { 77 clearInterval(progressTimer); 78 progressTimer = null; 79 } 80 } 81 82 function checkProgress(player) { 83 const duration = player.getDuration(); 84 if (!duration) return; 85 86 const percent = (player.getCurrentTime() / duration) * 100; 87 PROGRESS_MARKS.forEach(function (mark) { 88 if (percent >= mark && sentMarks.indexOf(mark) === -1) { 89 sentMarks.push(mark); 90 pushEvent('video_progress', { 91 video_percent: mark, 92 video_current_time: Math.round(player.getCurrentTime()), 93 video_duration: Math.round(duration) 94 }); 95 } 96 }); 97 } 98 99 loadYouTubeIframeApi().then(function (YT) { 100 new YT.Player(iframe, { 101 events: { 102 onStateChange: function (e) { 103 const player = e.target; 104 105 if (e.data === YT.PlayerState.PLAYING) { 106 if (!started) { 107 started = true; 108 pushEvent('video_start'); 109 } 110 stopProgressPolling(); 111 progressTimer = setInterval(function () { checkProgress(player); }, 1000); 112 } 113 114 if (e.data === YT.PlayerState.PAUSED) { 115 stopProgressPolling(); 116 pushEvent('video_pause', { 117 video_current_time: Math.round(player.getCurrentTime()), 118 video_duration: Math.round(player.getDuration()) 119 }); 120 } 121 122 if (e.data === YT.PlayerState.ENDED) { 123 stopProgressPolling(); 124 if (!completed) { 125 completed = true; 126 pushEvent('video_complete', { 127 video_duration: Math.round(player.getDuration()) 128 }); 129 } 130 } 131 } 132 } 133 }); 134 }); 135 } 136 137 w.tecidoskiteYouTube = w.tecidoskiteYouTube || {}; 138 w.tecidoskiteYouTube.withJsApi = withJsApi; 139 w.tecidoskiteYouTube.trackVideo = trackVideo; 140})(window);
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.