vendor: 8,776 bytes, lines 1-305
1/*! 2 * @preserve 3 * jquery.scrolldepth.js | v0.9 4 * Copyright (c) 2015 Rob Flaherty (@robflaherty) 5 * Licensed under the MIT and GPL licenses. 6 */ 7;(function ( $, window, document, undefined ) { 8 9 "use strict"; 10 11 var defaults = { 12 minHeight: 0, 13 elements: [], 14 percentage: true, 15 userTiming: true, 16 pixelDepth: true, 17 nonInteraction: true, 18 gaGlobal: false, 19 gtmOverride: false 20 }; 21 22 var $window = $(window), 23 cache = [], 24 scrollEventBound = false, 25 lastPixelDepth = 0, 26 universalGA, 27 classicGA, 28 gaGlobal, 29 standardEventHandler; 30 31 /* 32 * Plugin 33 */ 34 35 $.scrollDepth = function(options) { 36 37 var startTime = +new Date; 38 39 options = $.extend({}, defaults, options); 40 41 // Return early if document height is too small 42 if ( $(document).height() < options.minHeight ) { 43 return; 44 } 45 46 /* 47 * Determine which version of GA is being used 48 * "ga", "__gaTracker", _gaq", and "dataLayer" are the possible globals 49 */ 50 51 if (options.gaGlobal) { 52 universalGA = true; 53 gaGlobal = options.gaGlobal; 54 } else if (typeof ga === "function") { 55 universalGA = true; 56 gaGlobal = 'ga'; 57 } else if (typeof __gaTracker === "function") { 58 universalGA = true; 59 gaGlobal = '__gaTracker'; 60 } 61 62 if (typeof _gaq !== "undefined" && typeof _gaq.push === "function") { 63 classicGA = true; 64 } 65 66 if (typeof options.eventHandler === "function") { 67 standardEventHandler = options.eventHandler; 68 } else if (typeof dataLayer !== "undefined" && typeof dataLayer.push === "function" && !options.gtmOverride) { 69 70 standardEventHandler = function(data) { 71 dataLayer.push(data); 72 }; 73 } 74 75 /* 76 * Functions 77 */ 78 79 function sendEvent(action, label, scrollDistance, timing) { 80 81 if (standardEventHandler) { 82 83 standardEventHandler({'event': 'ScrollDistance', 'eventCategory': 'Scroll Depth', 'eventAction': action, 'eventLabel': label, 'eventValue': 1, 'eventNonInteraction': options.nonInteraction}); 84 85 if (options.pixelDepth && arguments.length > 2 && scrollDistance > lastPixelDepth) { 86 lastPixelDepth = scrollDistance; 87 standardEventHandler({'event': 'ScrollDistance', 'eventCategory': 'Scroll Depth', 'eventAction': 'Pixel Depth', 'eventLabel': rounded(scrollDistance), 'eventValue': 1, 'eventNonInteraction': options.nonInteraction}); 88 } 89 90 if (options.userTiming && arguments.length > 3) { 91 standardEventHandler({'event': 'ScrollTiming', 'eventCategory': 'Scroll Depth', 'eventAction': action, 'eventLabel': label, 'eventTiming': timing}); 92 } 93 94 } else { 95 96 if (universalGA) { 97 98 window[gaGlobal]('send', 'event', 'Scroll Depth', action, label, 1, {'nonInteraction': options.nonInteraction}); 99 100 if (options.pixelDepth && arguments.length > 2 && scrollDistance > lastPixelDepth) { 101 lastPixelDepth = scrollDistance; 102 window[gaGlobal]('send', 'event', 'Scroll Depth', 'Pixel Depth', rounded(scrollDistance), 1, {'nonInteraction': options.nonInteraction}); 103 } 104 105 if (options.userTiming && arguments.length > 3) { 106 window[gaGlobal]('send', 'timing', 'Scroll Depth', action, timing, label); 107 } 108 109 } 110 111 if (classicGA) { 112 113 _gaq.push(['_trackEvent', 'Scroll Depth', action, label, 1, options.nonInteraction]); 114 115 if (options.pixelDepth && arguments.length > 2 && scrollDistance > lastPixelDepth) { 116 lastPixelDepth = scrollDistance; 117 _gaq.push(['_trackEvent', 'Scroll Depth', 'Pixel Depth', rounded(scrollDistance), 1, options.nonInteraction]); 118 } 119 120 if (options.userTiming && arguments.length > 3) { 121 _gaq.push(['_trackTiming', 'Scroll Depth', action, timing, label, 100]); 122 } 123 124 } 125 126 } 127 128 } 129 130 function calculateMarks(docHeight) { 131 return { 132 '25%' : parseInt(docHeight * 0.25, 10), 133 '50%' : parseInt(docHeight * 0.50, 10), 134 '75%' : parseInt(docHeight * 0.75, 10), 135 // Cushion to trigger 100% event in iOS 136 '100%': docHeight - 5 137 }; 138 } 139 140 function checkMarks(marks, scrollDistance, timing) { 141 // Check each active mark 142 $.each(marks, function(key, val) { 143 if ( $.inArray(key, cache) === -1 && scrollDistance >= val ) { 144 sendEvent('Percentage', key, scrollDistance, timing); 145 cache.push(key); 146 } 147 }); 148 } 149 150 function checkElements(elements, scrollDistance, timing) { 151 $.each(elements, function(index, elem) { 152 if ( $.inArray(elem, cache) === -1 && $(elem).length ) { 153 if ( scrollDistance >= $(elem).offset().top ) { 154 sendEvent('Elements', elem, scrollDistance, timing); 155 cache.push(elem); 156 } 157 } 158 }); 159 } 160 161 function rounded(scrollDistance) { 162 // Returns String 163 return (Math.floor(scrollDistance/250) * 250).toString(); 164 } 165 166 function init() { 167 bindScrollDepth(); 168 } 169 170 /* 171 * Public Methods 172 */ 173 174 // Reset Scroll Depth with the originally initialized options 175 $.scrollDepth.reset = function() { 176 cache = []; 177 lastPixelDepth = 0; 178 $window.off('scroll.scrollDepth'); 179 bindScrollDepth(); 180 }; 181 182 // Add DOM elements to be tracked 183 $.scrollDepth.addElements = function(elems) { 184 185 if (typeof elems == "undefined" || !$.isArray(elems)) { 186 return; 187 } 188 189 $.merge(options.elements, elems); 190 191 // If scroll event has been unbound from window, rebind 192 if (!scrollEventBound) { 193 bindScrollDepth(); 194 } 195 196 }; 197 198 // Remove DOM elements currently tracked 199 $.scrollDepth.removeElements = function(elems) { 200 201 if (typeof elems == "undefined" || !$.isArray(elems)) { 202 return; 203 } 204 205 $.each(elems, function(index, elem) { 206 207 var inElementsArray = $.inArray(elem, options.elements); 208 var inCacheArray = $.inArray(elem, cache); 209 210 if (inElementsArray != -1) { 211 options.elements.splice(inElementsArray, 1); 212 } 213 214 if (inCacheArray != -1) { 215 cache.splice(inCacheArray, 1); 216 } 217 218 }); 219 220 }; 221 222 /* 223 * Throttle function borrowed from: 224 * Underscore.js 1.5.2 225 * http://underscorejs.org 226 * (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 227 * Underscore may be freely distributed under the MIT license. 228 */ 229 230 function throttle(func, wait) { 231 var context, args, result; 232 var timeout = null; 233 var previous = 0; 234 var later = function() { 235 previous = new Date; 236 timeout = null; 237 result = func.apply(context, args); 238 }; 239 return function() { 240 var now = new Date; 241 if (!previous) previous = now; 242 var remaining = wait - (now - previous); 243 context = this; 244 args = arguments; 245 if (remaining <= 0) { 246 clearTimeout(timeout); 247 timeout = null; 248 previous = now; 249 result = func.apply(context, args); 250 } else if (!timeout) { 251 timeout = setTimeout(later, remaining); 252 } 253 return result; 254 }; 255 } 256 257 /* 258 * Scroll Event 259 */ 260 261 function bindScrollDepth() { 262 263 scrollEventBound = true; 264 265 $window.on('scroll.scrollDepth', throttle(function() { 266 /* 267 * We calculate document and window height on each scroll event to 268 * account for dynamic DOM changes. 269 */ 270 271 var docHeight = $(document).height(), 272 winHeight = window.innerHeight ? window.innerHeight : $window.height(), 273 scrollDistance = $window.scrollTop() + winHeight, 274 275 // Recalculate percentage marks 276 marks = calculateMarks(docHeight), 277 278 // Timing 279 timing = +new Date - startTime; 280 281 // If all marks already hit, unbind scroll event 282 if (cache.length >= options.elements.length + (options.percentage ? 4:0)) { 283 $window.off('scroll.scrollDepth'); 284 scrollEventBound = false; 285 return; 286 } 287 288 // Check specified DOM elements 289 if (options.elements) { 290 checkElements(options.elements, scrollDistance, timing); 291 } 292 293 // Check standard marks 294 if (options.percentage) { 295 checkMarks(marks, scrollDistance, timing); 296 } 297 }, 500)); 298 299 } 300 301 init(); 302 303 }; 304 305})( jQuery, window, document );
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.