Advertisement
GiacomoGalanti

youtube listener

Oct 12th, 2023
43
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.29 KB | None | 0 0
  1. <script type="text/javascript" id="gtm-youtube-tracking">
  2. (function(document, window, config) {
  3.  
  4. 'use strict';
  5.  
  6. var _config = config || {};
  7. var forceSyntax = _config.forceSyntax || 0;
  8. var dataLayerName = _config.dataLayerName || 'dataLayer';
  9. // Default configuration for events
  10. var eventsFired = {
  11. 'Play': true,
  12. 'Pause': true,
  13. 'Watch to End': true
  14. };
  15. var key;
  16.  
  17. // Fetches YouTube JS API
  18. var tag = document.createElement('script');
  19. tag.src = '//www.youtube.com/iframe_api';
  20. var firstScriptTag = document.getElementsByTagName('script')[0];
  21. firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
  22.  
  23. for (key in _config.events) {
  24.  
  25. if (_config.events.hasOwnProperty(key)) {
  26.  
  27. eventsFired[key] = _config.events[key];
  28.  
  29. }
  30.  
  31. }
  32.  
  33. window.onYouTubeIframeAPIReady = (function() {
  34.  
  35. var cached = window.onYouTubeIframeAPIReady;
  36.  
  37. return function() {
  38.  
  39. if (cached) {
  40.  
  41. cached.apply(this, arguments);
  42.  
  43. }
  44.  
  45. // This script won't work on IE 6 or 7, so we bail at this point if we detect that UA
  46. if (navigator.userAgent.match(/MSIE [67]\./gi)) return;
  47.  
  48. if (document.readyState !== 'loading') {
  49.  
  50. init();
  51.  
  52. } else {
  53.  
  54. // On IE8 this fires on window.load, all other browsers will fire when DOM ready
  55. if (document.addEventListener) {
  56.  
  57. addEvent(document, 'DOMContentLoaded', init);
  58.  
  59. } else {
  60.  
  61. addEvent(window, 'load', init);
  62.  
  63. }
  64.  
  65. }
  66.  
  67. };
  68.  
  69. })();
  70.  
  71. // Invoked by the YouTube API when it's ready
  72. function init() {
  73.  
  74. var potentialVideos = getTagsAsArr_('iframe').concat(getTagsAsArr_('embed'));
  75. digestPotentialVideos(potentialVideos);
  76.  
  77. // Will bind to dynamically added videos. CAPTURE NOT SUPPORTED BY IE8
  78. if ('addEventListener' in document) {
  79. document.addEventListener('load', bindToNewVideos_, true);
  80. }
  81.  
  82.  
  83. }
  84.  
  85. // Take our videos and turn them into trackable videos with events
  86. function digestPotentialVideos(potentialVideos) {
  87.  
  88. var i;
  89.  
  90. for (i = 0; i < potentialVideos.length; i++) {
  91.  
  92. var isYouTubeVideo = checkIfYouTubeVideo(potentialVideos[i]);
  93.  
  94. if (isYouTubeVideo) {
  95.  
  96. var normalizedYouTubeIframe = normalizeYouTubeIframe(potentialVideos[i]);
  97. addYouTubeEvents(normalizedYouTubeIframe);
  98.  
  99. }
  100.  
  101. }
  102.  
  103. }
  104.  
  105. // Determine if the element is a YouTube video or not
  106. function checkIfYouTubeVideo(potentialYouTubeVideo) {
  107.  
  108. var potentialYouTubeVideoSrc = potentialYouTubeVideo.src || '';
  109.  
  110. if (potentialYouTubeVideoSrc.indexOf('youtube.com/embed/') > -1 ||
  111. potentialYouTubeVideoSrc.indexOf('youtube.com/v/') > -1) {
  112.  
  113. return true;
  114.  
  115. }
  116.  
  117. return false;
  118.  
  119. }
  120.  
  121. function jsApiEnabled(url) {
  122.  
  123. return url.indexOf('enablejsapi') > -1;
  124.  
  125. }
  126.  
  127. function originEnabled(url) {
  128.  
  129. return url.indexOf('origin') > -1;
  130.  
  131. }
  132.  
  133. // Turn embed objects into iframe objects and ensure they have the right parameters
  134. function normalizeYouTubeIframe(youTubeVideo) {
  135.  
  136. var loc = window.location;
  137. var a = document.createElement('a');
  138. a.href = youTubeVideo.src;
  139. a.hostname = 'www.youtube.com';
  140. a.protocol = loc.protocol;
  141. var tmpPathname = a.pathname.charAt(0) === '/' ? a.pathname : '/' + a.pathname; // IE10 shim
  142.  
  143. // For security reasons, YouTube wants an origin parameter set that matches our hostname
  144.  
  145. if (!jsApiEnabled(a.search)) {
  146.  
  147. a.search = (a.search.length > 0 ? a.search + '&' : '') + 'enablejsapi=1';
  148.  
  149. }
  150.  
  151. if (!originEnabled(a.search) && loc.hostname.indexOf('localhost') === -1) {
  152.  
  153. var port = loc.port ? ':' + loc.port : '';
  154. var origin = loc.protocol + '%2F%2F' + loc.hostname + port;
  155.  
  156. a.search = a.search + '&origin=' + origin;
  157.  
  158. }
  159.  
  160. if (youTubeVideo.type === 'application/x-shockwave-flash') {
  161.  
  162. var newIframe = document.createElement('iframe');
  163. newIframe.height = youTubeVideo.height;
  164. newIframe.width = youTubeVideo.width;
  165. tmpPathname = tmpPathname.replace('/v/', '/embed/');
  166.  
  167. youTubeVideo.parentNode.parentNode.replaceChild(newIframe, youTubeVideo.parentNode);
  168.  
  169. youTubeVideo = newIframe;
  170.  
  171. }
  172.  
  173. a.pathname = tmpPathname;
  174.  
  175. if (youTubeVideo.src !== a.href + a.hash) {
  176.  
  177. youTubeVideo.src = a.href + a.hash;
  178.  
  179. }
  180.  
  181. return youTubeVideo;
  182.  
  183. }
  184.  
  185. // Add event handlers for events emitted by the YouTube API
  186. function addYouTubeEvents(youTubeIframe) {
  187.  
  188. var player = YT.get(youTubeIframe.id);
  189.  
  190. if (!player) {
  191.  
  192. player = new YT.Player(youTubeIframe, {});
  193.  
  194. }
  195.  
  196. if (typeof youTubeIframe.pauseFlag === 'undefined') {
  197.  
  198. youTubeIframe.pauseFlag = false;
  199. player.addEventListener('onStateChange', function(evt) {
  200.  
  201. onStateChangeHandler(evt, youTubeIframe);
  202.  
  203. });
  204.  
  205. }
  206.  
  207. }
  208.  
  209. // Returns key/value pairs of percentages: number of seconds to achieve
  210. function getMarks(duration) {
  211.  
  212. var marks = {};
  213.  
  214. // For full support, we're handling Watch to End with percentage viewed
  215. if (_config.events['Watch to End']) {
  216.  
  217. marks['Watch to End'] = Math.min(duration - 3, Math.floor(duration * 0.99));
  218.  
  219. }
  220.  
  221. if (_config.percentageTracking) {
  222.  
  223. var points = [];
  224. var i;
  225.  
  226. if (_config.percentageTracking.each) {
  227.  
  228. points = points.concat(_config.percentageTracking.each);
  229.  
  230. }
  231.  
  232. if (_config.percentageTracking.every) {
  233.  
  234. var every = parseInt(_config.percentageTracking.every, 10);
  235. var num = 100 / every;
  236.  
  237. for (i = 1; i < num; i++) {
  238.  
  239. points.push(i * every);
  240.  
  241. }
  242.  
  243. }
  244.  
  245. for (i = 0; i < points.length; i++) {
  246.  
  247. var _point = points[i];
  248. var _mark = _point + '%';
  249. var _time = duration * _point / 100;
  250.  
  251. marks[_mark] = Math.floor(_time);
  252.  
  253. }
  254.  
  255. }
  256.  
  257. return marks;
  258.  
  259. }
  260.  
  261. function checkCompletion(player, marks, videoId) {
  262.  
  263. var currentTime = player.getCurrentTime();
  264. var key;
  265.  
  266. player[videoId] = player[videoId] || {};
  267.  
  268. for (key in marks) {
  269.  
  270. if (marks[key] <= currentTime && !player[videoId][key]) {
  271.  
  272. player[videoId][key] = true;
  273. fireAnalyticsEvent(videoId, key);
  274.  
  275. }
  276.  
  277. }
  278.  
  279. }
  280.  
  281. // Event handler for events emitted from the YouTube API
  282. function onStateChangeHandler(evt, youTubeIframe) {
  283.  
  284. var stateIndex = evt.data;
  285. var player = evt.target;
  286. var targetVideoUrl = player.getVideoUrl();
  287. var targetVideoId = targetVideoUrl.match(/[?&]v=([^&#]*)/)[1]; // Extract the ID
  288. var playerState = player.getPlayerState();
  289. var duration = Math.floor(player.getDuration());
  290. var marks = getMarks(duration);
  291. var playerStatesIndex = {
  292. '1': 'Play',
  293. '2': 'Pause'
  294. };
  295. var state = playerStatesIndex[stateIndex];
  296.  
  297. youTubeIframe.playTracker = youTubeIframe.playTracker || {};
  298.  
  299. if (playerState === 1 && !youTubeIframe.timer) {
  300.  
  301. clearInterval(youTubeIframe.timer);
  302.  
  303. youTubeIframe.timer = setInterval(function() {
  304.  
  305. // Check every second to see if we've hit any of our percentage viewed marks
  306. checkCompletion(player, marks, youTubeIframe.videoId);
  307.  
  308. }, 1000);
  309.  
  310. } else {
  311.  
  312. clearInterval(youTubeIframe.timer);
  313. youTubeIframe.timer = false;
  314.  
  315. }
  316.  
  317. // Playlist edge-case handler
  318. if (stateIndex === 1) {
  319.  
  320. youTubeIframe.playTracker[targetVideoId] = true;
  321. youTubeIframe.videoId = targetVideoId;
  322. youTubeIframe.pauseFlag = false;
  323.  
  324. }
  325.  
  326. if (!youTubeIframe.playTracker[youTubeIframe.videoId]) {
  327.  
  328. // This video hasn't started yet, so this is spam
  329. return false;
  330.  
  331. }
  332.  
  333. if (stateIndex === 2) {
  334.  
  335. if (!youTubeIframe.pauseFlag) {
  336.  
  337. youTubeIframe.pauseFlag = true;
  338.  
  339. } else {
  340.  
  341. // We don't want to fire consecutive pause events
  342. return false;
  343.  
  344. }
  345.  
  346. }
  347.  
  348. // If we're meant to track this event, fire it
  349. if (eventsFired[state]) {
  350.  
  351. fireAnalyticsEvent(youTubeIframe.videoId, state);
  352.  
  353. }
  354.  
  355. }
  356.  
  357. // Fire an event to Google Analytics or Google Tag Manager
  358. function fireAnalyticsEvent(videoId, state) {
  359.  
  360. var videoUrl = 'https://www.youtube.com/watch?v=' + videoId;
  361. var _ga = window.GoogleAnalyticsObject;
  362.  
  363. if (typeof window[dataLayerName] !== 'undefined' && !_config.forceSyntax) {
  364.  
  365. window[dataLayerName].push({
  366.  
  367. 'event': 'youTubeTrack',
  368. 'attributes': {
  369.  
  370. 'videoUrl': videoUrl,
  371. 'videoAction': state
  372.  
  373. }
  374.  
  375. });
  376.  
  377. } else if (typeof window[_ga] === 'function' &&
  378. typeof window[_ga].getAll === 'function' &&
  379. _config.forceSyntax !== 2) {
  380.  
  381. window[_ga]('send', 'event', 'Videos', state, videoUrl);
  382.  
  383. } else if (typeof window._gaq !== 'undefined' && forceSyntax !== 1) {
  384.  
  385. window._gaq.push(['_trackEvent', 'Videos', state, videoUrl]);
  386.  
  387. }
  388.  
  389. }
  390.  
  391. // Simple cross-browser event listener
  392. function addEvent(el, name, fn) {
  393.  
  394. if (el.addEventListener) {
  395.  
  396. el.addEventListener(name, fn);
  397.  
  398. } else if (el.attachEvent) {
  399.  
  400. el.attachEvent('on' + name, function(evt) {
  401.  
  402. evt.target = evt.target || evt.srcElement;
  403. // Call the event to ensure uniform 'this' handling, pass it event
  404. fn.call(el, evt);
  405.  
  406. });
  407.  
  408. } else if (typeof el['on' + name] === 'undefined' || el['on' + name] === null) {
  409.  
  410.  
  411. el['on' + name] = function(evt) {
  412.  
  413. evt.target = evt.target || evt.srcElement;
  414. // Call the event to ensure uniform 'this' handling, pass it event
  415. fn.call(el, evt);
  416.  
  417. };
  418.  
  419. }
  420.  
  421. }
  422.  
  423. // Returns array of elements with given tag name
  424. function getTagsAsArr_(tagName) {
  425.  
  426. return [].slice.call(document.getElementsByTagName(tagName));
  427.  
  428. }
  429.  
  430. function bindToNewVideos_(evt) {
  431.  
  432. var el = evt.target || evt.srcElement;
  433. var isYT = checkIfYouTubeVideo(el);
  434.  
  435. // We only bind to iframes with a YouTube URL with the enablejsapi=1 and
  436. // origin=<<hostname>> parameters
  437. if (el.tagName === 'IFRAME' && isYT && jsApiEnabled(el.src) && originEnabled(el.src)) {
  438.  
  439. addYouTubeEvents(el);
  440.  
  441. }
  442.  
  443. }
  444.  
  445. })(document, window, {
  446. 'events': {
  447. 'Play': true,
  448. 'Pause': true,
  449. 'Watch to End': true
  450. },
  451. 'percentageTracking': {
  452. 'every': 25,
  453. 'each': [10, 90]
  454. }
  455. });
  456. /*
  457. * Configuration Details
  458. *
  459. * @property events object
  460. * Defines which events emitted by YouTube API
  461. * will be turned into Google Analytics or GTM events
  462. *
  463. * @property percentageTracking object
  464. * Object with configurations for percentage viewed events
  465. *
  466. * @property each array
  467. * Fires an event once each percentage ahs been reached
  468. *
  469. * @property every number
  470. * Fires an event for every n% viewed
  471. *
  472. * @property forceSyntax int 0, 1, or 2
  473. * Forces script to use Classic (2) or Universal(1)
  474. *
  475. * @property dataLayerName string
  476. * Tells script to use custom dataLayer name instead of default
  477. */
  478. /*
  479. * v8.1.3
  480. * Created by the Google Analytics consultants at http://www.lunametrics.com
  481. * Written by @SayfSharif and @notdanwilkerson
  482. * Documentation: https://github.com/lunametrics/youtube-google-analytics/
  483. * Licensed under the Creative Commons 4.0 Attribution Public License
  484. */
  485. </script>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement