Instrukcja użycia→ Utwórz tag w GTM – Niestandardowy kod HTML
→ Wklej kod listenera do nasłuchiwania zdarzeń
→ Ustaw regułę – Wszystkie strony
→ Sprawdź w trybie podglądu GTM poprawność działania listenera
Kod listenera
<script>
;(function(){
'use strict';
var cfg = {
dataLayerName: 'dataLayer',
eventName: 'Film w Vimeo',
progressThresholds: [0,10,25,50,75,90], // 100% wyłącznie na "complete"
sendPause: true,
endNearPercent: 98 // pauzy przy >=98% ignorujemy (autopauza na finish)
};
var dl = window[cfg.dataLayerName] = window[cfg.dataLayerName] || [];
// deduplikacja wg status|percent|time
var sentCache = {};
function emitOnce(key, evt){
if (sentCache[key]) return;
sentCache[key] = 1;
dl.push(evt);
var keys = Object.keys(sentCache);
if (keys.length > 200) { for (var i=0;i<100;i++) delete sentCache[keys[i]]; }
}
function pushEvent(ctx){
var evt = {
event: cfg.eventName,
video_provider: 'vimeo',
video_title: (ctx.title||'').toLowerCase(),
video_url: ctx.url || undefined,
video_duration: typeof ctx.duration==='number' ? ctx.duration : undefined,
video_status: ctx.status
};
if (typeof ctx.percent === 'number'){
evt.video_progress = ctx.percent;
evt.video_percent = ctx.percent + '%';
}
if (typeof ctx.currentTime === 'number'){
evt.video_current_time = Math.round(ctx.currentTime);
}
if (typeof ctx.seek_from === 'number') evt.video_seek_from = Math.round(ctx.seek_from);
if (typeof ctx.seek_to === 'number') evt.video_seek_to = Math.round(ctx.seek_to);
var k = [evt.video_status, evt.video_progress || '', evt.video_current_time || ''].join('|');
emitOnce(k, evt);
}
function uniqSortThresholds(list){
var map = {}, out = [];
for (var i=0;i<list.length;i++){
var v = Number(list[i]); if (isNaN(v)) continue;
if (v>=0 && v<=100) map[v]=true;
}
for (var k in map) if (map.hasOwnProperty(k)) out.push(Number(k));
out.sort(function(a,b){return a-b;});
return out;
}
var THRESHOLDS = uniqSortThresholds(cfg.progressThresholds);
function loadVimeoSdk(cb){
if (window.Vimeo && window.Vimeo.Player) { cb(); return; }
var s = document.createElement('script');
s.src = 'https://player.vimeo.com/api/player.js';
s.async = true;
s.onload = function(){ cb(); };
(document.head||document.documentElement).appendChild(s);
}
function isVimeoIframe(el){
return el && el.tagName === 'IFRAME' && /player\.vimeo\.com\/video\/\d+/.test(el.src);
}
function trackIframe(iframe){
if (iframe.__pp_vimeoTracked) return;
iframe.__pp_vimeoTracked = true;
var player = new window.Vimeo.Player(iframe);
var meta = { title:'', duration:undefined, url:iframe.src };
player.getVideoTitle().then(function(t){ meta.title = t || ''; }).catch(function(){});
player.getDuration().then(function(d){ if (typeof d==='number') meta.duration = d; }).catch(function(){});
player.getVideoUrl().then(function(u){ if (u) meta.url = u; }).catch(function(){
player.getVideoId().then(function(id){ if (id) meta.url = 'https://vimeo.com/'+id; }).catch(function(){});
});
// stan sesji odtwarzania
var fired = {}; // progi progress – raz na sesję; NIE resetujemy przy seek do początku
var started = false; // czy trwa sesja
var endSeq = false; // po complete blokujemy pause/seek
var lastPercent = 0; // z timeupdate
var lastSeconds = 0; // z timeupdate (użyte jako seek_from)
player.on('play', function(){
// nowa sesja startuje dopiero po "play", więc tu odblokuj i zresetuj liczniki
if (!started){
started = true;
endSeq = false;
fired = {'0': true}; // progu 0 nie raportujemy
lastSeconds = 0;
pushEvent({ status:'start', title:meta.title, duration:meta.duration, url:meta.url, percent:0, currentTime:0 });
}
});
if (THRESHOLDS.length){
player.on('timeupdate', function(ev){
var p = Math.floor(((ev && ev.percent) ? ev.percent : 0) * 100);
lastPercent = p;
if (ev && typeof ev.seconds === 'number') lastSeconds = ev.seconds;
// PROGRESS – każdy próg tylko raz na sesję (niezależnie od seeków wstecz/początek)
for (var i=0;i<THRESHOLDS.length;i++){
var th = THRESHOLDS[i];
if (p >= th && !fired[String(th)]){
fired[String(th)] = true;
if (th === 0) continue; // nigdy nie wysyłamy 0
pushEvent({
status:'progress',
title:meta.title,
duration:meta.duration,
url:meta.url,
percent:th,
currentTime:ev.seconds
});
}
}
});
}
// SEEK – nie wysyłaj po complete (Vimeo resetuje do 0 i emituje seeked)
player.on('seeked', function(ev){
if (endSeq) return; // po zakończeniu ignoruj wszelkie seeki
var to = (ev && typeof ev.seconds === 'number') ? ev.seconds : undefined;
if (!started && (to === 0 || to === undefined)) return; // reset playera → pomiń
var from = (typeof lastSeconds === 'number') ? lastSeconds : undefined;
var percent = (typeof meta.duration === 'number' && typeof to === 'number')
? Math.floor((to / meta.duration) * 100)
: undefined;
pushEvent({
status:'seek',
title:meta.title,
duration:meta.duration,
url:meta.url,
percent: percent,
currentTime: to,
seek_from: from,
seek_to: to
});
if (typeof to === 'number') lastSeconds = to;
});
// PAUSE – filtruj „autopauzę” przy finiszu
if (cfg.sendPause){
player.on('pause', function(ev){
if (endSeq) return;
if (lastPercent >= cfg.endNearPercent) return; // blisko końca → zwykle autopauza
pushEvent({ status:'pause', title:meta.title, duration:meta.duration, url:meta.url, currentTime: ev && ev.seconds });
});
}
// COMPLETE – jedyny moment na 100%
player.on('ended', function(){
if (endSeq) return;
endSeq = true;
lastPercent = 100;
var d = (typeof meta.duration === 'number' ? meta.duration : undefined);
pushEvent({ status:'complete', title:meta.title, duration:meta.duration, url:meta.url, percent:100, currentTime:d });
// zamykamy sesję – kolejne progi/seek do zera nie policzą się
started = false;
// fired zostawiamy – i tak nowa sesja zresetuje je w on('play')
});
}
function init(){
var iframes = document.getElementsByTagName('iframe');
var list = [];
for (var i=0;i<iframes.length;i++) if (isVimeoIframe(iframes[i])) list.push(iframes[i]);
if (!list.length) return;
loadVimeoSdk(function(){ for (var j=0;j<list.length;j++) trackIframe(list[j]); });
}
if (document.readyState === 'loading'){
document.addEventListener('DOMContentLoaded', init, false);
window.addEventListener('load', init, false);
} else { init(); }
if (window.MutationObserver){
try{
var mo = new MutationObserver(function(muts){
for (var i=0;i<muts.length;i++){
var nodes = muts[i].addedNodes || [];
for (var j=0;j<nodes.length;j++){
var n = nodes[j];
if (n && n.nodeType===1){
if (isVimeoIframe(n)) { init(); continue; }
var q = n.getElementsByTagName ? n.getElementsByTagName('iframe') : [];
for (var k=0;k<q.length;k++){ if (isVimeoIframe(q[k])) { init(); break; } }
}
}
}
});
mo.observe(document.documentElement, { childList:true, subtree:true });
}catch(e){}
}
})();
</script>