1// Variables to change 2var contentContainer = 'main';//'#content'; 3var endOfContent = '#site-footer'; 4var wordsPerMinute = 250; 5var kp_cc_debug = 1; // optional, change to 1 if you wish to see debug messages in the console 6 7// DON'T CHANGE ANYTHING BELOW 8// Set Variables 9var contentStarted = false; 10var pageViewed = false; 11var pageRead = false; 12var kp_cc_error = false; 13 14var event_end_of_content = 'content_scroll'; 15var event_timer_reached = 'content_dwell'; 16var event_viewed_read = 'content_consumed'; 17 18// Calculated Variables 19var contentEl = document.querySelector(contentContainer); 20var endOfContentEl = document.querySelector(endOfContent); 21 22if (contentEl == null) { 23 console.error('KP Content Consumption: Content Container not found. Please check your settings and ensure a valid selector is provided.'); 24 kp_cc_error = true; 25} 26 27if (endOfContentEl == null) { 28 console.error('KP Content Consumption: Content End Element not found. Please check your settings and ensure a valid selector is provided.'); 29 kp_cc_error = true; 30} 31 32/** 33 * Start content consumption plugin 34 */ 35function initContentConsumption() { 36 37 var contentEl = document.querySelectorAll(contentContainer); 38 var wordContentCount = wordCount(contentEl); 39 40 // Count all words in content 41 if (kp_cc_debug) { 42 console.log('Content Container: ' + contentContainer); 43 console.log('Content End Element: ' + endOfContent); 44 console.log('Word Count: ' + wordContentCount); 45 } 46 47 if (contentEl && wordContentCount) { 48 // Time to read by wordsPerMinute 49 var timeToRead = wordContentCount / wordsPerMinute; 50 var interval = timeToRead * 60 * 1000; 51 var intervalFast = interval * .75; 52 53 if (kp_cc_debug) { 54 console.log('Time to read: ' + timeToRead + ' mins, Interval:' + interval + ' seconds, Interval fast (not used):' + intervalFast + ' seconds' ); 55 } 56 57 pageReadTimer(interval, intervalFast); 58 } 59} 60 61function pageReadTimer(interval, intervalFast) { 62 window.setTimeout(function() { 63 if (kp_cc_debug) 64 console.log('Content Timer Completed'); 65 66 window.dataLayer.push({ 'event': event_timer_reached }); 67 pageRead = true; 68 contentConsumptionPushUpdate(); 69 }, interval); 70} 71 72function hasReachedEndofContent(direction) { 73 // Detect Content Start 74 if (contentStarted === false) { 75 startContentObserver.observe(contentEl) 76 } 77 78 if (contentStarted) { 79 // scrolled to end of content 80 if (pageViewed === false) { 81 endContentObserver.observe(endOfContentEl) 82 } 83 } 84} 85 86function checkContentReader() { 87 if (pageRead === true && pageViewed === true) { 88 // consumers 89 window.dataLayer.push({ 'event': event_viewed_read }); 90 } 91} 92 93function wordCount(words) { 94 var count = 0; 95 for (var i = 0; i < words.length; i++) { 96 var cleanWords = words[i].innerText.replace(/\n\n/g, " "); 97 cleanWords = cleanWords.replace(/\n|\t/g, " "); 98 count += cleanWords.split(" ").length; 99 } 100 return count; 101} 102 103// Scroll Function 104var scroll_pos = 0; 105var last_scroll_pos = 0; 106var ticking = false; 107 108var startContentObserver = new IntersectionObserver(function (entries, observer) { 109 entries.forEach(function (entry) { 110 if (entry.isIntersecting) { 111 if (kp_cc_debug) 112 console.log('Content Started'); 113 114 contentStarted = true; 115 116 observer.unobserve(entry.target) 117 } 118 }) 119}, { 120 threshold: .1 121}) 122 123var endContentObserver = new IntersectionObserver(function (entries, observer) { 124 entries.forEach(function (entry) { 125 if (entry.isIntersecting) { 126 if (kp_cc_debug) 127 console.log('Content Ended'); 128 129 window.dataLayer.push({ 'event': event_end_of_content }); 130 pageViewed = true; 131 132 observer.unobserve(entry.target) 133 } 134 }) 135}, { 136 threshold: .1 137}) 138 139function getDirection(scroll_pos, last_scroll_pos) { 140 if (scroll_pos > last_scroll_pos) { 141 return 'down'; 142 } else if (scroll_pos < last_scroll_pos) { 143 return 'up'; 144 } 145} 146 147initContentConsumption(); 148
149if (contentContainer && endOfContent && kp_cc_error !== true) { 150 window.addEventListener('scroll', function (e) { 151 scroll_pos = window.scrollY; 152 if (!ticking) { 153 window.requestAnimationFrame(function () { 154 const direction = getDirection(scroll_pos, last_scroll_pos); 155 hasReachedEndofContent(direction); 156 157 ticking = false; 158 last_scroll_pos = scroll_pos; 159 }) 160 161 ticking = true; 162 } 163 }) 164} 165 166function contentConsumptionPushUpdate() { 167 if (pageRead === false && pageViewed === false) { 168 // bouncers 169 } 170 171 if (pageRead === false && pageViewed === true) { 172 // skimmers 173 } 174 175 if (pageRead === true && pageViewed === false) { 176 // tab hoarders 177 } 178 179 if (pageRead === true && pageViewed === true) { 180 // consumers 181 window.dataLayer.push({ 'event': event_viewed_read }); 182 } 183}
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.